@@ -21,7 +21,6 @@ mod cursor;
21
21
mod query;
22
22
mod tuple;
23
23
pub use client:: SpiClient ;
24
- use client:: SpiConnection ;
25
24
pub use cursor:: SpiCursor ;
26
25
pub use query:: { OwnedPreparedStatement , PreparedStatement , Query } ;
27
26
pub use tuple:: { SpiHeapTupleData , SpiHeapTupleDataEntry , SpiTupleTable } ;
@@ -237,13 +236,13 @@ impl Spi {
237
236
}
238
237
239
238
pub fn get_one < A : FromDatum + IntoDatum > ( query : & str ) -> Result < Option < A > > {
240
- Spi :: connect ( | mut client| client. update ( query, Some ( 1 ) , & [ ] ) ?. first ( ) . get_one ( ) )
239
+ Spi :: connect_mut ( | client| client. update ( query, Some ( 1 ) , & [ ] ) ?. first ( ) . get_one ( ) )
241
240
}
242
241
243
242
pub fn get_two < A : FromDatum + IntoDatum , B : FromDatum + IntoDatum > (
244
243
query : & str ,
245
244
) -> Result < ( Option < A > , Option < B > ) > {
246
- Spi :: connect ( | mut client| client. update ( query, Some ( 1 ) , & [ ] ) ?. first ( ) . get_two :: < A , B > ( ) )
245
+ Spi :: connect_mut ( | client| client. update ( query, Some ( 1 ) , & [ ] ) ?. first ( ) . get_two :: < A , B > ( ) )
247
246
}
248
247
249
248
pub fn get_three <
@@ -253,7 +252,7 @@ impl Spi {
253
252
> (
254
253
query : & str ,
255
254
) -> Result < ( Option < A > , Option < B > , Option < C > ) > {
256
- Spi :: connect ( | mut client| {
255
+ Spi :: connect_mut ( | client| {
257
256
client. update ( query, Some ( 1 ) , & [ ] ) ?. first ( ) . get_three :: < A , B , C > ( )
258
257
} )
259
258
}
@@ -262,14 +261,14 @@ impl Spi {
262
261
query : & str ,
263
262
args : & [ DatumWithOid < ' mcx > ] ,
264
263
) -> Result < Option < A > > {
265
- Spi :: connect ( | mut client| client. update ( query, Some ( 1 ) , args) ?. first ( ) . get_one ( ) )
264
+ Spi :: connect_mut ( | client| client. update ( query, Some ( 1 ) , args) ?. first ( ) . get_one ( ) )
266
265
}
267
266
268
267
pub fn get_two_with_args < ' mcx , A : FromDatum + IntoDatum , B : FromDatum + IntoDatum > (
269
268
query : & str ,
270
269
args : & [ DatumWithOid < ' mcx > ] ,
271
270
) -> Result < ( Option < A > , Option < B > ) > {
272
- Spi :: connect ( | mut client| client. update ( query, Some ( 1 ) , args) ?. first ( ) . get_two :: < A , B > ( ) )
271
+ Spi :: connect_mut ( | client| client. update ( query, Some ( 1 ) , args) ?. first ( ) . get_two :: < A , B > ( ) )
273
272
}
274
273
275
274
pub fn get_three_with_args <
@@ -281,12 +280,12 @@ impl Spi {
281
280
query : & str ,
282
281
args : & [ DatumWithOid < ' mcx > ] ,
283
282
) -> Result < ( Option < A > , Option < B > , Option < C > ) > {
284
- Spi :: connect ( | mut client| {
283
+ Spi :: connect_mut ( | client| {
285
284
client. update ( query, Some ( 1 ) , args) ?. first ( ) . get_three :: < A , B , C > ( )
286
285
} )
287
286
}
288
287
289
- /// just run an arbitrary SQL statement.
288
+ /// Just run an arbitrary SQL statement.
290
289
///
291
290
/// ## Safety
292
291
///
@@ -304,7 +303,7 @@ impl Spi {
304
303
query : & str ,
305
304
args : & [ DatumWithOid < ' mcx > ] ,
306
305
) -> std:: result:: Result < ( ) , Error > {
307
- Spi :: connect ( | mut client| client. update ( query, None , args) . map ( |_| ( ) ) )
306
+ Spi :: connect_mut ( | client| client. update ( query, None , args) . map ( |_| ( ) ) )
308
307
}
309
308
310
309
/// explain a query, returning its result in json form
@@ -314,7 +313,7 @@ impl Spi {
314
313
315
314
/// explain a query with args, returning its result in json form
316
315
pub fn explain_with_args < ' mcx > ( query : & str , args : & [ DatumWithOid < ' mcx > ] ) -> Result < Json > {
317
- Ok ( Spi :: connect ( | mut client| {
316
+ Ok ( Spi :: connect_mut ( | client| {
318
317
client
319
318
. update ( & format ! ( "EXPLAIN (format json) {query}" ) , None , args) ?
320
319
. first ( )
@@ -323,7 +322,7 @@ impl Spi {
323
322
. unwrap ( ) )
324
323
}
325
324
326
- /// Execute SPI commands via the provided `SpiClient`.
325
+ /// Execute SPI read-only commands via the provided `SpiClient`.
327
326
///
328
327
/// While inside the provided closure, code executes under a short-lived "SPI Memory Context",
329
328
/// and Postgres will completely free that context when this function is finished.
@@ -360,10 +359,51 @@ impl Spi {
360
359
/// ([`pg_sys::SPI_connect()`]) **always** returns a successful response.
361
360
pub fn connect < R , F > ( f : F ) -> R
362
361
where
363
- F : FnOnce ( SpiClient < ' _ > ) -> R , /* TODO: redesign this with 2 lifetimes:
364
- - 'conn ~= CurrentMemoryContext after connection
365
- - 'ret ~= SPI_palloc's context
366
- */
362
+ F : FnOnce ( & SpiClient < ' _ > ) -> R ,
363
+ {
364
+ Self :: connect_mut ( |client| f ( client) )
365
+ }
366
+
367
+ /// Execute SPI mutating commands via the provided `SpiClient`.
368
+ ///
369
+ /// While inside the provided closure, code executes under a short-lived "SPI Memory Context",
370
+ /// and Postgres will completely free that context when this function is finished.
371
+ ///
372
+ /// pgrx' SPI API endeavors to return Datum values from functions like `::get_one()` that are
373
+ /// automatically copied into the into the `CurrentMemoryContext` at the time of this
374
+ /// function call.
375
+ ///
376
+ /// # Examples
377
+ ///
378
+ /// ```rust,no_run
379
+ /// use pgrx::prelude::*;
380
+ /// # fn foo() -> spi::Result<()> {
381
+ /// Spi::connect_mut(|client| {
382
+ /// client.update("INSERT INTO users VALUES ('Bob')", None, &[])?;
383
+ /// Ok(())
384
+ /// })
385
+ /// # }
386
+ /// ```
387
+ ///
388
+ /// Note that `SpiClient` is scoped to the connection lifetime and cannot be returned. The
389
+ /// following code will not compile:
390
+ ///
391
+ /// ```rust,compile_fail
392
+ /// use pgrx::prelude::*;
393
+ /// let cant_return_client = Spi::connect(|client| client);
394
+ /// ```
395
+ ///
396
+ /// # Panics
397
+ ///
398
+ /// This function will panic if for some reason it's unable to "connect" to Postgres' SPI
399
+ /// system. At the time of this writing, that's actually impossible as the underlying function
400
+ /// ([`pg_sys::SPI_connect()`]) **always** returns a successful response.
401
+ pub fn connect_mut < R , F > ( f : F ) -> R
402
+ where
403
+ F : FnOnce ( & mut SpiClient < ' _ > ) -> R , /* TODO: redesign this with 2 lifetimes:
404
+ - 'conn ~= CurrentMemoryContext after connection
405
+ - 'ret ~= SPI_palloc's context
406
+ */
367
407
{
368
408
// connect to SPI
369
409
//
@@ -379,14 +419,13 @@ impl Spi {
379
419
// otherwise this function would need to return a `Result<R, spi::Error>` and that's a
380
420
// fucking nightmare for users to deal with. There's ample discussion around coming to
381
421
// this decision at https://github.com/pgcentralfoundation/pgrx/pull/977
382
- let connection =
383
- SpiConnection :: connect ( ) . expect ( "SPI_connect indicated an unexpected failure" ) ;
422
+ let mut client = SpiClient :: connect ( ) . expect ( "SPI_connect indicated an unexpected failure" ) ;
384
423
385
424
// run the provided closure within the memory context that SPI_connect()
386
425
// just put us un. We'll disconnect from SPI when the closure is finished.
387
426
// If there's a panic or elog(ERROR), we don't care about also disconnecting from
388
427
// SPI b/c Postgres will do that for us automatically
389
- f ( connection . client ( ) )
428
+ f ( & mut client)
390
429
}
391
430
392
431
#[ track_caller]
0 commit comments