pg 1.2.3 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +986 -0
  4. data/Gemfile +23 -0
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +300 -0
  7. data/README.md +327 -0
  8. data/Rakefile +123 -144
  9. data/certs/ged.pem +24 -0
  10. data/certs/kanis@comcard.de.pem +20 -0
  11. data/certs/larskanis-2022.pem +26 -0
  12. data/certs/larskanis-2023.pem +24 -0
  13. data/certs/larskanis-2024.pem +24 -0
  14. data/ext/errorcodes.def +16 -5
  15. data/ext/errorcodes.rb +0 -0
  16. data/ext/errorcodes.txt +5 -5
  17. data/ext/extconf.rb +259 -33
  18. data/ext/gvl_wrappers.c +17 -2
  19. data/ext/gvl_wrappers.h +56 -0
  20. data/ext/pg.c +89 -63
  21. data/ext/pg.h +31 -8
  22. data/ext/pg_binary_decoder.c +232 -1
  23. data/ext/pg_binary_encoder.c +428 -1
  24. data/ext/pg_cancel_connection.c +360 -0
  25. data/ext/pg_coder.c +148 -36
  26. data/ext/pg_connection.c +1365 -817
  27. data/ext/pg_copy_coder.c +360 -38
  28. data/ext/pg_errors.c +1 -1
  29. data/ext/pg_record_coder.c +56 -25
  30. data/ext/pg_result.c +187 -76
  31. data/ext/pg_text_decoder.c +32 -11
  32. data/ext/pg_text_encoder.c +65 -33
  33. data/ext/pg_tuple.c +84 -61
  34. data/ext/pg_type_map.c +44 -10
  35. data/ext/pg_type_map_all_strings.c +17 -3
  36. data/ext/pg_type_map_by_class.c +54 -27
  37. data/ext/pg_type_map_by_column.c +74 -31
  38. data/ext/pg_type_map_by_mri_type.c +48 -19
  39. data/ext/pg_type_map_by_oid.c +61 -27
  40. data/ext/pg_type_map_in_ruby.c +55 -21
  41. data/ext/pg_util.c +2 -2
  42. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  43. data/lib/pg/basic_type_map_for_queries.rb +206 -0
  44. data/lib/pg/basic_type_map_for_results.rb +104 -0
  45. data/lib/pg/basic_type_registry.rb +311 -0
  46. data/lib/pg/binary_decoder/date.rb +9 -0
  47. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  48. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  49. data/lib/pg/cancel_connection.rb +53 -0
  50. data/lib/pg/coder.rb +18 -14
  51. data/lib/pg/connection.rb +894 -91
  52. data/lib/pg/exceptions.rb +20 -1
  53. data/lib/pg/text_decoder/date.rb +21 -0
  54. data/lib/pg/text_decoder/inet.rb +9 -0
  55. data/lib/pg/text_decoder/json.rb +17 -0
  56. data/lib/pg/text_decoder/numeric.rb +9 -0
  57. data/lib/pg/text_decoder/timestamp.rb +30 -0
  58. data/lib/pg/text_encoder/date.rb +13 -0
  59. data/lib/pg/text_encoder/inet.rb +31 -0
  60. data/lib/pg/text_encoder/json.rb +17 -0
  61. data/lib/pg/text_encoder/numeric.rb +9 -0
  62. data/lib/pg/text_encoder/timestamp.rb +24 -0
  63. data/lib/pg/version.rb +4 -0
  64. data/lib/pg.rb +109 -39
  65. data/misc/openssl-pg-segfault.rb +31 -0
  66. data/misc/postgres/History.txt +9 -0
  67. data/misc/postgres/Manifest.txt +5 -0
  68. data/misc/postgres/README.txt +21 -0
  69. data/misc/postgres/Rakefile +21 -0
  70. data/misc/postgres/lib/postgres.rb +16 -0
  71. data/misc/ruby-pg/History.txt +9 -0
  72. data/misc/ruby-pg/Manifest.txt +5 -0
  73. data/misc/ruby-pg/README.txt +21 -0
  74. data/misc/ruby-pg/Rakefile +21 -0
  75. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  76. data/misc/yugabyte/Dockerfile +9 -0
  77. data/misc/yugabyte/docker-compose.yml +28 -0
  78. data/misc/yugabyte/pg-test.rb +45 -0
  79. data/pg.gemspec +38 -0
  80. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  81. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  82. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  83. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  84. data/rakelib/pg_gem_helper.rb +64 -0
  85. data/rakelib/task_extension.rb +46 -0
  86. data/sample/array_insert.rb +20 -0
  87. data/sample/async_api.rb +102 -0
  88. data/sample/async_copyto.rb +39 -0
  89. data/sample/async_mixed.rb +56 -0
  90. data/sample/check_conn.rb +21 -0
  91. data/sample/copydata.rb +71 -0
  92. data/sample/copyfrom.rb +81 -0
  93. data/sample/copyto.rb +19 -0
  94. data/sample/cursor.rb +21 -0
  95. data/sample/disk_usage_report.rb +177 -0
  96. data/sample/issue-119.rb +94 -0
  97. data/sample/losample.rb +69 -0
  98. data/sample/minimal-testcase.rb +17 -0
  99. data/sample/notify_wait.rb +72 -0
  100. data/sample/pg_statistics.rb +285 -0
  101. data/sample/replication_monitor.rb +222 -0
  102. data/sample/test_binary_values.rb +33 -0
  103. data/sample/wal_shipper.rb +434 -0
  104. data/sample/warehouse_partitions.rb +311 -0
  105. data.tar.gz.sig +0 -0
  106. metadata +139 -213
  107. metadata.gz.sig +0 -0
  108. data/.gemtest +0 -0
  109. data/ChangeLog +0 -0
  110. data/History.rdoc +0 -578
  111. data/Manifest.txt +0 -73
  112. data/README.ja.rdoc +0 -13
  113. data/README.rdoc +0 -213
  114. data/Rakefile.cross +0 -299
  115. data/lib/pg/basic_type_mapping.rb +0 -522
  116. data/lib/pg/binary_decoder.rb +0 -23
  117. data/lib/pg/constants.rb +0 -12
  118. data/lib/pg/text_decoder.rb +0 -46
  119. data/lib/pg/text_encoder.rb +0 -59
  120. data/spec/data/expected_trace.out +0 -26
  121. data/spec/data/random_binary_data +0 -0
  122. data/spec/helpers.rb +0 -380
  123. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  124. data/spec/pg/connection_spec.rb +0 -1949
  125. data/spec/pg/connection_sync_spec.rb +0 -41
  126. data/spec/pg/result_spec.rb +0 -681
  127. data/spec/pg/tuple_spec.rb +0 -333
  128. data/spec/pg/type_map_by_class_spec.rb +0 -138
  129. data/spec/pg/type_map_by_column_spec.rb +0 -226
  130. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  131. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  132. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  133. data/spec/pg/type_map_spec.rb +0 -22
  134. data/spec/pg/type_spec.rb +0 -1123
  135. data/spec/pg_spec.rb +0 -50
data/ext/pg_connection.c CHANGED
@@ -12,20 +12,38 @@
12
12
 
13
13
  VALUE rb_cPGconn;
14
14
  static ID s_id_encode;
15
+ static ID s_id_autoclose_set;
15
16
  static VALUE sym_type, sym_format, sym_value;
16
17
  static VALUE sym_symbol, sym_string, sym_static_symbol;
17
18
 
18
- static PQnoticeReceiver default_notice_receiver = NULL;
19
- static PQnoticeProcessor default_notice_processor = NULL;
20
-
21
19
  static VALUE pgconn_finish( VALUE );
22
20
  static VALUE pgconn_set_default_encoding( VALUE self );
21
+ static VALUE pgconn_wait_for_flush( VALUE self );
23
22
  static void pgconn_set_internal_encoding_index( VALUE );
23
+ static const rb_data_type_t pg_connection_type;
24
+ static VALUE pgconn_async_flush(VALUE self);
24
25
 
25
26
  /*
26
27
  * Global functions
27
28
  */
28
29
 
30
+ /*
31
+ * Convenience function to raise connection errors
32
+ */
33
+ void
34
+ pg_raise_conn_error( VALUE klass, VALUE self, const char *format, ...)
35
+ {
36
+ VALUE msg, error;
37
+ va_list ap;
38
+
39
+ va_start(ap, format);
40
+ msg = rb_vsprintf(format, ap);
41
+ va_end(ap);
42
+ error = rb_exc_new_str(klass, msg);
43
+ rb_iv_set(error, "@connection", self);
44
+ rb_exc_raise(error);
45
+ }
46
+
29
47
  /*
30
48
  * Fetch the PG::Connection object data pointer.
31
49
  */
@@ -33,7 +51,7 @@ t_pg_connection *
33
51
  pg_get_connection( VALUE self )
34
52
  {
35
53
  t_pg_connection *this;
36
- Data_Get_Struct( self, t_pg_connection, this);
54
+ TypedData_Get_Struct( self, t_pg_connection, &pg_connection_type, this);
37
55
 
38
56
  return this;
39
57
  }
@@ -46,10 +64,10 @@ static t_pg_connection *
46
64
  pg_get_connection_safe( VALUE self )
47
65
  {
48
66
  t_pg_connection *this;
49
- Data_Get_Struct( self, t_pg_connection, this);
67
+ TypedData_Get_Struct( self, t_pg_connection, &pg_connection_type, this);
50
68
 
51
69
  if ( !this->pgconn )
52
- rb_raise( rb_eConnectionBad, "connection is closed" );
70
+ pg_raise_conn_error( rb_eConnectionBad, self, "connection is closed");
53
71
 
54
72
  return this;
55
73
  }
@@ -65,15 +83,30 @@ PGconn *
65
83
  pg_get_pgconn( VALUE self )
66
84
  {
67
85
  t_pg_connection *this;
68
- Data_Get_Struct( self, t_pg_connection, this);
86
+ TypedData_Get_Struct( self, t_pg_connection, &pg_connection_type, this);
69
87
 
70
- if ( !this->pgconn )
71
- rb_raise( rb_eConnectionBad, "connection is closed" );
88
+ if ( !this->pgconn ){
89
+ pg_raise_conn_error( rb_eConnectionBad, self, "connection is closed");
90
+ }
72
91
 
73
92
  return this->pgconn;
74
93
  }
75
94
 
76
95
 
96
+ void
97
+ pg_unwrap_socket_io( VALUE self, VALUE *p_socket_io, int ruby_sd )
98
+ {
99
+ if ( RTEST(*p_socket_io) ) {
100
+ #if defined(_WIN32)
101
+ if( rb_w32_unwrap_io_handle(ruby_sd) )
102
+ pg_raise_conn_error( rb_eConnectionBad, self, "Could not unwrap win32 socket handle");
103
+ #endif
104
+ rb_funcall( *p_socket_io, rb_intern("close"), 0 );
105
+ }
106
+
107
+ RB_OBJ_WRITE(self, p_socket_io, Qnil);
108
+ }
109
+
77
110
 
78
111
  /*
79
112
  * Close the associated socket IO object if there is one.
@@ -82,18 +115,7 @@ static void
82
115
  pgconn_close_socket_io( VALUE self )
83
116
  {
84
117
  t_pg_connection *this = pg_get_connection( self );
85
- VALUE socket_io = this->socket_io;
86
-
87
- if ( RTEST(socket_io) ) {
88
- #if defined(_WIN32)
89
- if( rb_w32_unwrap_io_handle(this->ruby_sd) ){
90
- rb_raise(rb_eConnectionBad, "Could not unwrap win32 socket handle");
91
- }
92
- #endif
93
- rb_funcall( socket_io, rb_intern("close"), 0 );
94
- }
95
-
96
- this->socket_io = Qnil;
118
+ pg_unwrap_socket_io( self, &this->socket_io, this->ruby_sd);
97
119
  }
98
120
 
99
121
 
@@ -145,16 +167,31 @@ static const char *pg_cstr_enc(VALUE str, int enc_idx){
145
167
  * GC Mark function
146
168
  */
147
169
  static void
148
- pgconn_gc_mark( t_pg_connection *this )
170
+ pgconn_gc_mark( void *_this )
171
+ {
172
+ t_pg_connection *this = (t_pg_connection *)_this;
173
+ rb_gc_mark_movable( this->socket_io );
174
+ rb_gc_mark_movable( this->notice_receiver );
175
+ rb_gc_mark_movable( this->notice_processor );
176
+ rb_gc_mark_movable( this->type_map_for_queries );
177
+ rb_gc_mark_movable( this->type_map_for_results );
178
+ rb_gc_mark_movable( this->trace_stream );
179
+ rb_gc_mark_movable( this->encoder_for_put_copy_data );
180
+ rb_gc_mark_movable( this->decoder_for_get_copy_data );
181
+ }
182
+
183
+ static void
184
+ pgconn_gc_compact( void *_this )
149
185
  {
150
- rb_gc_mark( this->socket_io );
151
- rb_gc_mark( this->notice_receiver );
152
- rb_gc_mark( this->notice_processor );
153
- rb_gc_mark( this->type_map_for_queries );
154
- rb_gc_mark( this->type_map_for_results );
155
- rb_gc_mark( this->trace_stream );
156
- rb_gc_mark( this->encoder_for_put_copy_data );
157
- rb_gc_mark( this->decoder_for_get_copy_data );
186
+ t_pg_connection *this = (t_pg_connection *)_this;
187
+ pg_gc_location( this->socket_io );
188
+ pg_gc_location( this->notice_receiver );
189
+ pg_gc_location( this->notice_processor );
190
+ pg_gc_location( this->type_map_for_queries );
191
+ pg_gc_location( this->type_map_for_results );
192
+ pg_gc_location( this->trace_stream );
193
+ pg_gc_location( this->encoder_for_put_copy_data );
194
+ pg_gc_location( this->decoder_for_get_copy_data );
158
195
  }
159
196
 
160
197
 
@@ -162,11 +199,15 @@ pgconn_gc_mark( t_pg_connection *this )
162
199
  * GC Free function
163
200
  */
164
201
  static void
165
- pgconn_gc_free( t_pg_connection *this )
202
+ pgconn_gc_free( void *_this )
166
203
  {
204
+ t_pg_connection *this = (t_pg_connection *)_this;
167
205
  #if defined(_WIN32)
168
- if ( RTEST(this->socket_io) )
169
- rb_w32_unwrap_io_handle( this->ruby_sd );
206
+ if ( RTEST(this->socket_io) ) {
207
+ if( rb_w32_unwrap_io_handle(this->ruby_sd) ){
208
+ rb_warn("pg: Could not unwrap win32 socket handle by garbage collector");
209
+ }
210
+ }
170
211
  #endif
171
212
  if (this->pgconn != NULL)
172
213
  PQfinish( this->pgconn );
@@ -174,6 +215,29 @@ pgconn_gc_free( t_pg_connection *this )
174
215
  xfree(this);
175
216
  }
176
217
 
218
+ /*
219
+ * Object Size function
220
+ */
221
+ static size_t
222
+ pgconn_memsize( const void *_this )
223
+ {
224
+ const t_pg_connection *this = (const t_pg_connection *)_this;
225
+ return sizeof(*this);
226
+ }
227
+
228
+ static const rb_data_type_t pg_connection_type = {
229
+ "PG::Connection",
230
+ {
231
+ pgconn_gc_mark,
232
+ pgconn_gc_free,
233
+ pgconn_memsize,
234
+ pgconn_gc_compact,
235
+ },
236
+ 0,
237
+ 0,
238
+ RUBY_TYPED_WB_PROTECTED,
239
+ };
240
+
177
241
 
178
242
  /**************************************************************************
179
243
  * Class Methods
@@ -189,93 +253,39 @@ static VALUE
189
253
  pgconn_s_allocate( VALUE klass )
190
254
  {
191
255
  t_pg_connection *this;
192
- VALUE self = Data_Make_Struct( klass, t_pg_connection, pgconn_gc_mark, pgconn_gc_free, this );
256
+ VALUE self = TypedData_Make_Struct( klass, t_pg_connection, &pg_connection_type, this );
193
257
 
194
258
  this->pgconn = NULL;
195
- this->socket_io = Qnil;
196
- this->notice_receiver = Qnil;
197
- this->notice_processor = Qnil;
198
- this->type_map_for_queries = pg_typemap_all_strings;
199
- this->type_map_for_results = pg_typemap_all_strings;
200
- this->encoder_for_put_copy_data = Qnil;
201
- this->decoder_for_get_copy_data = Qnil;
202
- this->trace_stream = Qnil;
259
+ RB_OBJ_WRITE(self, &this->socket_io, Qnil);
260
+ RB_OBJ_WRITE(self, &this->notice_receiver, Qnil);
261
+ RB_OBJ_WRITE(self, &this->notice_processor, Qnil);
262
+ RB_OBJ_WRITE(self, &this->type_map_for_queries, pg_typemap_all_strings);
263
+ RB_OBJ_WRITE(self, &this->type_map_for_results, pg_typemap_all_strings);
264
+ RB_OBJ_WRITE(self, &this->encoder_for_put_copy_data, Qnil);
265
+ RB_OBJ_WRITE(self, &this->decoder_for_get_copy_data, Qnil);
266
+ RB_OBJ_WRITE(self, &this->trace_stream, Qnil);
267
+ rb_ivar_set(self, rb_intern("@calls_to_put_copy_data"), INT2FIX(0));
268
+ rb_ivar_set(self, rb_intern("@iopts_for_reset"), Qnil);
203
269
 
204
270
  return self;
205
271
  }
206
272
 
207
-
208
- /*
209
- * Document-method: new
210
- *
211
- * call-seq:
212
- * PG::Connection.new -> conn
213
- * PG::Connection.new(connection_hash) -> conn
214
- * PG::Connection.new(connection_string) -> conn
215
- * PG::Connection.new(host, port, options, tty, dbname, user, password) -> conn
216
- *
217
- * Create a connection to the specified server.
218
- *
219
- * +connection_hash+ must be a ruby Hash with connection parameters.
220
- * See the {list of valid parameters}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS] in the PostgreSQL documentation.
221
- *
222
- * There are two accepted formats for +connection_string+: plain <code>keyword = value</code> strings and URIs.
223
- * See the documentation of {connection strings}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING].
224
- *
225
- * The positional parameter form has the same functionality except that the missing parameters will always take on default values. The parameters are:
226
- * [+host+]
227
- * server hostname
228
- * [+port+]
229
- * server port number
230
- * [+options+]
231
- * backend options
232
- * [+tty+]
233
- * (ignored in newer versions of PostgreSQL)
234
- * [+dbname+]
235
- * connecting database name
236
- * [+user+]
237
- * login user name
238
- * [+password+]
239
- * login password
240
- *
241
- * Examples:
242
- *
243
- * # Connect using all defaults
244
- * PG::Connection.new
245
- *
246
- * # As a Hash
247
- * PG::Connection.new( :dbname => 'test', :port => 5432 )
248
- *
249
- * # As a String
250
- * PG::Connection.new( "dbname=test port=5432" )
251
- *
252
- * # As an Array
253
- * PG::Connection.new( nil, 5432, nil, nil, 'test', nil, nil )
254
- *
255
- * If the Ruby default internal encoding is set (i.e., <code>Encoding.default_internal != nil</code>), the
256
- * connection will have its +client_encoding+ set accordingly.
257
- *
258
- * Raises a PG::Error if the connection fails.
259
- */
260
273
  static VALUE
261
- pgconn_init(int argc, VALUE *argv, VALUE self)
274
+ pgconn_s_sync_connect(int argc, VALUE *argv, VALUE klass)
262
275
  {
263
276
  t_pg_connection *this;
264
277
  VALUE conninfo;
265
- VALUE error;
278
+ VALUE self = pgconn_s_allocate( klass );
266
279
 
267
280
  this = pg_get_connection( self );
268
281
  conninfo = rb_funcall2( rb_cPGconn, rb_intern("parse_connect_args"), argc, argv );
269
282
  this->pgconn = gvl_PQconnectdb(StringValueCStr(conninfo));
270
283
 
271
284
  if(this->pgconn == NULL)
272
- rb_raise(rb_ePGerror, "PQconnectdb() unable to allocate structure");
285
+ rb_raise(rb_ePGerror, "PQconnectdb() unable to allocate PGconn structure");
273
286
 
274
- if (PQstatus(this->pgconn) == CONNECTION_BAD) {
275
- error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(this->pgconn));
276
- rb_iv_set(error, "@connection", self);
277
- rb_exc_raise(error);
278
- }
287
+ if (PQstatus(this->pgconn) == CONNECTION_BAD)
288
+ pg_raise_conn_error( rb_eConnectionBad, self, "%s", PQerrorMessage(this->pgconn));
279
289
 
280
290
  pgconn_set_default_encoding( self );
281
291
 
@@ -308,7 +318,6 @@ pgconn_s_connect_start( int argc, VALUE *argv, VALUE klass )
308
318
  {
309
319
  VALUE rb_conn;
310
320
  VALUE conninfo;
311
- VALUE error;
312
321
  t_pg_connection *this;
313
322
 
314
323
  /*
@@ -321,13 +330,10 @@ pgconn_s_connect_start( int argc, VALUE *argv, VALUE klass )
321
330
  this->pgconn = gvl_PQconnectStart( StringValueCStr(conninfo) );
322
331
 
323
332
  if( this->pgconn == NULL )
324
- rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate structure");
333
+ rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate PGconn structure");
325
334
 
326
- if ( PQstatus(this->pgconn) == CONNECTION_BAD ) {
327
- error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(this->pgconn));
328
- rb_iv_set(error, "@connection", rb_conn);
329
- rb_exc_raise(error);
330
- }
335
+ if ( PQstatus(this->pgconn) == CONNECTION_BAD )
336
+ pg_raise_conn_error( rb_eConnectionBad, rb_conn, "%s", PQerrorMessage(this->pgconn));
331
337
 
332
338
  if ( rb_block_given_p() ) {
333
339
  return rb_ensure( rb_yield, rb_conn, pgconn_finish, rb_conn );
@@ -335,34 +341,14 @@ pgconn_s_connect_start( int argc, VALUE *argv, VALUE klass )
335
341
  return rb_conn;
336
342
  }
337
343
 
338
- /*
339
- * call-seq:
340
- * PG::Connection.ping(connection_hash) -> Integer
341
- * PG::Connection.ping(connection_string) -> Integer
342
- * PG::Connection.ping(host, port, options, tty, dbname, login, password) -> Integer
343
- *
344
- * Check server status.
345
- *
346
- * See PG::Connection.new for a description of the parameters.
347
- *
348
- * Returns one of:
349
- * [+PQPING_OK+]
350
- * server is accepting connections
351
- * [+PQPING_REJECT+]
352
- * server is alive but rejecting connections
353
- * [+PQPING_NO_RESPONSE+]
354
- * could not establish connection
355
- * [+PQPING_NO_ATTEMPT+]
356
- * connection not attempted (bad params)
357
- */
358
344
  static VALUE
359
- pgconn_s_ping( int argc, VALUE *argv, VALUE klass )
345
+ pgconn_s_sync_ping( int argc, VALUE *argv, VALUE klass )
360
346
  {
361
347
  PGPing ping;
362
348
  VALUE conninfo;
363
349
 
364
350
  conninfo = rb_funcall2( klass, rb_intern("parse_connect_args"), argc, argv );
365
- ping = PQping( StringValueCStr(conninfo) );
351
+ ping = gvl_PQping( StringValueCStr(conninfo) );
366
352
 
367
353
  return INT2FIX((int)ping);
368
354
  }
@@ -403,32 +389,39 @@ pgconn_s_conndefaults(VALUE self)
403
389
  return array;
404
390
  }
405
391
 
406
-
407
- #ifdef HAVE_PQENCRYPTPASSWORDCONN
408
392
  /*
409
- * call-seq:
410
- * conn.encrypt_password( password, username, algorithm=nil ) -> String
411
- *
412
- * This function is intended to be used by client applications that wish to send commands like <tt>ALTER USER joe PASSWORD 'pwd'</tt>.
413
- * It is good practice not to send the original cleartext password in such a command, because it might be exposed in command logs, activity displays, and so on.
414
- * Instead, use this function to convert the password to encrypted form before it is sent.
393
+ * Document-method: PG::Connection.conninfo_parse
415
394
  *
416
- * The +password+ and +username+ arguments are the cleartext password, and the SQL name of the user it is for.
417
- * +algorithm+ specifies the encryption algorithm to use to encrypt the password.
418
- * Currently supported algorithms are +md5+ and +scram-sha-256+ (+on+ and +off+ are also accepted as aliases for +md5+, for compatibility with older server versions).
419
- * Note that support for +scram-sha-256+ was introduced in PostgreSQL version 10, and will not work correctly with older server versions.
420
- * If algorithm is omitted or +nil+, this function will query the server for the current value of the +password_encryption+ setting.
421
- * That can block, and will fail if the current transaction is aborted, or if the connection is busy executing another query.
422
- * If you wish to use the default algorithm for the server but want to avoid blocking, query +password_encryption+ yourself before calling #encrypt_password, and pass that value as the algorithm.
423
- *
424
- * Return value is the encrypted password.
425
- * The caller can assume the string doesn't contain any special characters that would require escaping.
395
+ * call-seq:
396
+ * PG::Connection.conninfo_parse(conninfo_string) -> Array
426
397
  *
427
- * Available since PostgreSQL-10.
428
- * See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-misc.html#LIBPQ-PQENCRYPTPASSWORDCONN].
398
+ * Returns parsed connection options from the provided connection string as an array of hashes.
399
+ * Each hash has the same keys as PG::Connection.conndefaults() .
400
+ * The values from the +conninfo_string+ are stored in the +:val+ key.
429
401
  */
430
402
  static VALUE
431
- pgconn_encrypt_password(int argc, VALUE *argv, VALUE self)
403
+ pgconn_s_conninfo_parse(VALUE self, VALUE conninfo)
404
+ {
405
+ VALUE array;
406
+ char *errmsg = NULL;
407
+ PQconninfoOption *options = PQconninfoParse(StringValueCStr(conninfo), &errmsg);
408
+ if(errmsg){
409
+ VALUE error = rb_str_new_cstr(errmsg);
410
+ PQfreemem(errmsg);
411
+ rb_raise(rb_ePGerror, "%"PRIsVALUE, error);
412
+ }
413
+ array = pgconn_make_conninfo_array( options );
414
+
415
+ PQconninfoFree(options);
416
+
417
+ UNUSED( self );
418
+
419
+ return array;
420
+ }
421
+
422
+
423
+ static VALUE
424
+ pgconn_sync_encrypt_password(int argc, VALUE *argv, VALUE self)
432
425
  {
433
426
  char *encrypted = NULL;
434
427
  VALUE rval = Qnil;
@@ -445,12 +438,11 @@ pgconn_encrypt_password(int argc, VALUE *argv, VALUE self)
445
438
  rval = rb_str_new2( encrypted );
446
439
  PQfreemem( encrypted );
447
440
  } else {
448
- rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
441
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
449
442
  }
450
443
 
451
444
  return rval;
452
445
  }
453
- #endif
454
446
 
455
447
 
456
448
  /*
@@ -499,17 +491,18 @@ pgconn_s_encrypt_password(VALUE self, VALUE password, VALUE username)
499
491
  * the asynchronous connection is ready
500
492
  *
501
493
  * Example:
502
- * conn = PG::Connection.connect_start("dbname=mydatabase")
503
- * socket = conn.socket_io
494
+ * require "io/wait"
495
+ *
496
+ * conn = PG::Connection.connect_start(dbname: 'mydatabase')
504
497
  * status = conn.connect_poll
505
498
  * while(status != PG::PGRES_POLLING_OK) do
506
499
  * # do some work while waiting for the connection to complete
507
500
  * if(status == PG::PGRES_POLLING_READING)
508
- * if(not select([socket], [], [], 10.0))
501
+ * unless conn.socket_io.wait_readable(10.0)
509
502
  * raise "Asynchronous connection timed out!"
510
503
  * end
511
504
  * elsif(status == PG::PGRES_POLLING_WRITING)
512
- * if(not select([], [socket], [], 10.0))
505
+ * unless conn.socket_io.wait_writable(10.0)
513
506
  * raise "Asynchronous connection timed out!"
514
507
  * end
515
508
  * end
@@ -522,7 +515,10 @@ static VALUE
522
515
  pgconn_connect_poll(VALUE self)
523
516
  {
524
517
  PostgresPollingStatusType status;
518
+
519
+ pgconn_close_socket_io(self);
525
520
  status = gvl_PQconnectPoll(pg_get_pgconn(self));
521
+
526
522
  return INT2FIX((int)status);
527
523
  }
528
524
 
@@ -559,21 +555,35 @@ pgconn_finished_p( VALUE self )
559
555
  }
560
556
 
561
557
 
562
- /*
563
- * call-seq:
564
- * conn.reset()
565
- *
566
- * Resets the backend connection. This method closes the
567
- * backend connection and tries to re-connect.
568
- */
569
558
  static VALUE
570
- pgconn_reset( VALUE self )
559
+ pgconn_sync_reset( VALUE self )
571
560
  {
572
561
  pgconn_close_socket_io( self );
573
562
  gvl_PQreset( pg_get_pgconn(self) );
574
563
  return self;
575
564
  }
576
565
 
566
+ static VALUE
567
+ pgconn_reset_start2( VALUE self, VALUE conninfo )
568
+ {
569
+ t_pg_connection *this = pg_get_connection( self );
570
+
571
+ /* Close old connection */
572
+ pgconn_close_socket_io( self );
573
+ PQfinish( this->pgconn );
574
+
575
+ /* Start new connection */
576
+ this->pgconn = gvl_PQconnectStart( StringValueCStr(conninfo) );
577
+
578
+ if( this->pgconn == NULL )
579
+ rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate PGconn structure");
580
+
581
+ if ( PQstatus(this->pgconn) == CONNECTION_BAD )
582
+ pg_raise_conn_error( rb_eConnectionBad, self, "%s", PQerrorMessage(this->pgconn));
583
+
584
+ return Qnil;
585
+ }
586
+
577
587
  /*
578
588
  * call-seq:
579
589
  * conn.reset_start() -> nil
@@ -589,7 +599,7 @@ pgconn_reset_start(VALUE self)
589
599
  {
590
600
  pgconn_close_socket_io( self );
591
601
  if(gvl_PQresetStart(pg_get_pgconn(self)) == 0)
592
- rb_raise(rb_eUnableToSend, "reset has failed");
602
+ pg_raise_conn_error( rb_eUnableToSend, self, "reset has failed");
593
603
  return Qnil;
594
604
  }
595
605
 
@@ -598,14 +608,17 @@ pgconn_reset_start(VALUE self)
598
608
  * conn.reset_poll -> Integer
599
609
  *
600
610
  * Checks the status of a connection reset operation.
601
- * See #connect_start and #connect_poll for
611
+ * See Connection.connect_start and #connect_poll for
602
612
  * usage information and return values.
603
613
  */
604
614
  static VALUE
605
615
  pgconn_reset_poll(VALUE self)
606
616
  {
607
617
  PostgresPollingStatusType status;
618
+
619
+ pgconn_close_socket_io(self);
608
620
  status = gvl_PQresetPoll(pg_get_pgconn(self));
621
+
609
622
  return INT2FIX((int)status);
610
623
  }
611
624
 
@@ -656,7 +669,18 @@ pgconn_pass(VALUE self)
656
669
  * call-seq:
657
670
  * conn.host()
658
671
  *
659
- * Returns the connected server name.
672
+ * Returns the server host name of the active connection.
673
+ * This can be a host name, an IP address, or a directory path if the connection is via Unix socket.
674
+ * (The path case can be distinguished because it will always be an absolute path, beginning with +/+ .)
675
+ *
676
+ * If the connection parameters specified both host and hostaddr, then +host+ will return the host information.
677
+ * If only hostaddr was specified, then that is returned.
678
+ * If multiple hosts were specified in the connection parameters, +host+ returns the host actually connected to.
679
+ *
680
+ * If there is an error producing the host information (perhaps if the connection has not been fully established or there was an error), it returns an empty string.
681
+ *
682
+ * If multiple hosts were specified in the connection parameters, it is not possible to rely on the result of +host+ until the connection is established.
683
+ * The status of the connection can be checked using the function Connection#status .
660
684
  */
661
685
  static VALUE
662
686
  pgconn_host(VALUE self)
@@ -666,6 +690,26 @@ pgconn_host(VALUE self)
666
690
  return rb_str_new2(host);
667
691
  }
668
692
 
693
+ /* PQhostaddr() appeared in PostgreSQL-12 together with PQresultMemorySize() */
694
+ #if defined(HAVE_PQRESULTMEMORYSIZE)
695
+ /*
696
+ * call-seq:
697
+ * conn.hostaddr()
698
+ *
699
+ * Returns the server IP address of the active connection.
700
+ * This can be the address that a host name resolved to, or an IP address provided through the hostaddr parameter.
701
+ * If there is an error producing the host information (perhaps if the connection has not been fully established or there was an error), it returns an empty string.
702
+ *
703
+ */
704
+ static VALUE
705
+ pgconn_hostaddr(VALUE self)
706
+ {
707
+ char *host = PQhostaddr(pg_get_pgconn(self));
708
+ if (!host) return Qnil;
709
+ return rb_str_new2(host);
710
+ }
711
+ #endif
712
+
669
713
  /*
670
714
  * call-seq:
671
715
  * conn.port()
@@ -676,21 +720,22 @@ static VALUE
676
720
  pgconn_port(VALUE self)
677
721
  {
678
722
  char* port = PQport(pg_get_pgconn(self));
679
- return INT2NUM(atol(port));
723
+ if (!port || port[0] == '\0')
724
+ return INT2NUM(DEF_PGPORT);
725
+ else
726
+ return INT2NUM(atoi(port));
680
727
  }
681
728
 
682
729
  /*
683
730
  * call-seq:
684
731
  * conn.tty()
685
732
  *
686
- * Returns the connected pgtty. (Obsolete)
733
+ * Obsolete function.
687
734
  */
688
735
  static VALUE
689
736
  pgconn_tty(VALUE self)
690
737
  {
691
- char *tty = PQtty(pg_get_pgconn(self));
692
- if (!tty) return Qnil;
693
- return rb_str_new2(tty);
738
+ return rb_str_new2("");
694
739
  }
695
740
 
696
741
  /*
@@ -708,14 +753,12 @@ pgconn_options(VALUE self)
708
753
  }
709
754
 
710
755
 
711
- #ifdef HAVE_PQCONNINFO
712
756
  /*
713
757
  * call-seq:
714
758
  * conn.conninfo -> hash
715
759
  *
716
760
  * Returns the connection options used by a live connection.
717
761
  *
718
- * Available since PostgreSQL-9.3
719
762
  */
720
763
  static VALUE
721
764
  pgconn_conninfo( VALUE self )
@@ -728,14 +771,24 @@ pgconn_conninfo( VALUE self )
728
771
 
729
772
  return array;
730
773
  }
731
- #endif
732
774
 
733
775
 
734
776
  /*
735
777
  * call-seq:
736
778
  * conn.status()
737
779
  *
738
- * Returns status of connection : CONNECTION_OK or CONNECTION_BAD
780
+ * Returns the status of the connection, which is one:
781
+ * PG::Constants::CONNECTION_OK
782
+ * PG::Constants::CONNECTION_BAD
783
+ *
784
+ * ... and other constants of kind PG::Constants::CONNECTION_*
785
+ *
786
+ * This method returns the status of the last command from memory.
787
+ * It doesn't do any socket access hence is not suitable to test the connectivity.
788
+ * See check_socket for a way to verify the socket state.
789
+ *
790
+ * Example:
791
+ * PG.constants.grep(/CONNECTION_/).find{|c| PG.const_get(c) == conn.status} # => :CONNECTION_OK
739
792
  */
740
793
  static VALUE
741
794
  pgconn_status(VALUE self)
@@ -792,38 +845,62 @@ pgconn_parameter_status(VALUE self, VALUE param_name)
792
845
  * call-seq:
793
846
  * conn.protocol_version -> Integer
794
847
  *
795
- * The 3.0 protocol will normally be used when communicating with PostgreSQL 7.4
796
- * or later servers; pre-7.4 servers support only protocol 2.0. (Protocol 1.0 is
797
- * obsolete and not supported by libpq.)
848
+ * Interrogates the frontend/backend protocol being used.
849
+ *
850
+ * Applications might wish to use this function to determine whether certain features are supported.
851
+ * Currently, the only value is 3 (3.0 protocol).
852
+ * The protocol version will not change after connection startup is complete, but it could theoretically change during a connection reset.
853
+ * The 3.0 protocol is supported by PostgreSQL server versions 7.4 and above.
854
+ *
855
+ * PG::ConnectionBad is raised if the connection is bad.
798
856
  */
799
857
  static VALUE
800
858
  pgconn_protocol_version(VALUE self)
801
859
  {
802
- return INT2NUM(PQprotocolVersion(pg_get_pgconn(self)));
860
+ int protocol_version = PQprotocolVersion(pg_get_pgconn(self));
861
+ if (protocol_version == 0) {
862
+ pg_raise_conn_error( rb_eConnectionBad, self, "PQprotocolVersion() can't get protocol version");
863
+ }
864
+ return INT2NUM(protocol_version);
803
865
  }
804
866
 
805
867
  /*
806
868
  * call-seq:
807
869
  * conn.server_version -> Integer
808
870
  *
809
- * The number is formed by converting the major, minor, and revision
810
- * numbers into two-decimal-digit numbers and appending them together.
811
- * For example, version 7.4.2 will be returned as 70402, and version
812
- * 8.1 will be returned as 80100 (leading zeroes are not shown). Zero
813
- * is returned if the connection is bad.
871
+ * Returns an integer representing the server version.
872
+ *
873
+ * Applications might use this function to determine the version of the database server they are connected to.
874
+ * The result is formed by multiplying the server's major version number by 10000 and adding the minor version number.
875
+ * For example, version 10.1 will be returned as 100001, and version 11.0 will be returned as 110000.
876
+ *
877
+ * PG::ConnectionBad is raised if the connection is bad.
878
+ *
879
+ * Prior to major version 10, PostgreSQL used three-part version numbers in which the first two parts together represented the major version.
880
+ * For those versions, PQserverVersion uses two digits for each part; for example version 9.1.5 will be returned as 90105, and version 9.2.0 will be returned as 90200.
881
+ *
882
+ * Therefore, for purposes of determining feature compatibility, applications should divide the result of PQserverVersion by 100 not 10000 to determine a logical major version number.
883
+ * In all release series, only the last two digits differ between minor releases (bug-fix releases).
814
884
  *
815
885
  */
816
886
  static VALUE
817
887
  pgconn_server_version(VALUE self)
818
888
  {
819
- return INT2NUM(PQserverVersion(pg_get_pgconn(self)));
889
+ int server_version = PQserverVersion(pg_get_pgconn(self));
890
+ if (server_version == 0) {
891
+ pg_raise_conn_error( rb_eConnectionBad, self, "PQserverVersion() can't get server version");
892
+ }
893
+ return INT2NUM(server_version);
820
894
  }
821
895
 
822
896
  /*
823
897
  * call-seq:
824
898
  * conn.error_message -> String
825
899
  *
826
- * Returns the error message about connection.
900
+ * Returns the error message most recently generated by an operation on the connection.
901
+ *
902
+ * Nearly all libpq functions will set a message for conn.error_message if they fail.
903
+ * Note that by libpq convention, a nonempty error_message result can consist of multiple lines, and will include a trailing newline.
827
904
  */
828
905
  static VALUE
829
906
  pgconn_error_message(VALUE self)
@@ -857,51 +934,68 @@ pgconn_socket(VALUE self)
857
934
  pg_deprecated(4, ("conn.socket is deprecated and should be replaced by conn.socket_io"));
858
935
 
859
936
  if( (sd = PQsocket(pg_get_pgconn(self))) < 0)
860
- rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
937
+ pg_raise_conn_error( rb_eConnectionBad, self, "PQsocket() can't get socket descriptor");
938
+
861
939
  return INT2NUM(sd);
862
940
  }
863
941
 
942
+
943
+ VALUE
944
+ pg_wrap_socket_io(int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd)
945
+ {
946
+ int ruby_sd;
947
+ VALUE cSocket;
948
+ VALUE socket_io = *p_socket_io;
949
+
950
+ #ifdef _WIN32
951
+ ruby_sd = rb_w32_wrap_io_handle((HANDLE)(intptr_t)sd, O_RDWR|O_BINARY|O_NOINHERIT);
952
+ if( ruby_sd == -1 )
953
+ pg_raise_conn_error( rb_eConnectionBad, self, "Could not wrap win32 socket handle");
954
+
955
+ *p_ruby_sd = ruby_sd;
956
+ #else
957
+ *p_ruby_sd = ruby_sd = sd;
958
+ #endif
959
+
960
+ cSocket = rb_const_get(rb_cObject, rb_intern("BasicSocket"));
961
+ socket_io = rb_funcall( cSocket, rb_intern("for_fd"), 1, INT2NUM(ruby_sd));
962
+
963
+ /* Disable autoclose feature */
964
+ rb_funcall( socket_io, s_id_autoclose_set, 1, Qfalse );
965
+
966
+ RB_OBJ_WRITE(self, p_socket_io, socket_io);
967
+
968
+ return socket_io;
969
+ }
970
+
864
971
  /*
865
972
  * call-seq:
866
973
  * conn.socket_io() -> IO
867
974
  *
868
- * Fetch a memorized IO object created from the Connection's underlying socket.
869
- * This object can be used for IO.select to wait for events while running
870
- * asynchronous API calls.
975
+ * Fetch an IO object created from the Connection's underlying socket.
976
+ * This object can be used per <tt>socket_io.wait_readable</tt>, <tt>socket_io.wait_writable</tt> or for <tt>IO.select</tt> to wait for events while running asynchronous API calls.
977
+ * <tt>IO#wait_*able</tt> is <tt>Fiber.scheduler</tt> compatible in contrast to <tt>IO.select</tt>.
978
+ *
979
+ * The IO object can change while the connection is established, but is memorized afterwards.
980
+ * So be sure not to cache the IO object, but repeat calling <tt>conn.socket_io</tt> instead.
871
981
  *
872
- * Using this instead of #socket avoids the problem of the underlying connection
873
- * being closed by Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt>
874
- * goes out of scope. In contrast to #socket, it also works on Windows.
982
+ * Using this method also works on Windows in contrast to using #socket .
983
+ * It also avoids the problem of the underlying connection being closed by Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt> goes out of scope.
875
984
  */
876
985
  static VALUE
877
986
  pgconn_socket_io(VALUE self)
878
987
  {
879
- int sd;
880
- int ruby_sd;
881
- ID id_autoclose = rb_intern("autoclose=");
882
988
  t_pg_connection *this = pg_get_connection_safe( self );
883
- VALUE socket_io = this->socket_io;
884
989
 
885
- if ( !RTEST(socket_io) ) {
886
- if( (sd = PQsocket(this->pgconn)) < 0)
887
- rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
888
-
889
- #ifdef _WIN32
890
- ruby_sd = rb_w32_wrap_io_handle((HANDLE)(intptr_t)sd, O_RDWR|O_BINARY|O_NOINHERIT);
891
- this->ruby_sd = ruby_sd;
892
- #else
893
- ruby_sd = sd;
894
- #endif
895
-
896
- socket_io = rb_funcall( rb_cIO, rb_intern("for_fd"), 1, INT2NUM(ruby_sd) );
897
-
898
- /* Disable autoclose feature */
899
- rb_funcall( socket_io, id_autoclose, 1, Qfalse );
900
-
901
- this->socket_io = socket_io;
990
+ if ( !RTEST(this->socket_io) ) {
991
+ int sd;
992
+ if( (sd = PQsocket(this->pgconn)) < 0){
993
+ pg_raise_conn_error( rb_eConnectionBad, self, "PQsocket() can't get socket descriptor");
994
+ }
995
+ return pg_wrap_socket_io( sd, self, &this->socket_io, &this->ruby_sd);
902
996
  }
903
997
 
904
- return socket_io;
998
+ return this->socket_io;
905
999
  }
906
1000
 
907
1001
  /*
@@ -918,6 +1012,53 @@ pgconn_backend_pid(VALUE self)
918
1012
  return INT2NUM(PQbackendPID(pg_get_pgconn(self)));
919
1013
  }
920
1014
 
1015
+ #ifndef HAVE_PQSETCHUNKEDROWSMODE
1016
+ typedef struct
1017
+ {
1018
+ struct sockaddr_storage addr;
1019
+ socklen_t salen;
1020
+ } SockAddr;
1021
+
1022
+ /* Copy of struct pg_cancel from libpq-int.h
1023
+ *
1024
+ * See https://github.com/postgres/postgres/blame/master/src/interfaces/libpq/libpq-int.h#L577-L586
1025
+ */
1026
+ struct pg_cancel
1027
+ {
1028
+ SockAddr raddr; /* Remote address */
1029
+ int be_pid; /* PID of backend --- needed for cancels */
1030
+ int be_key; /* key of backend --- needed for cancels */
1031
+ };
1032
+
1033
+ /*
1034
+ * call-seq:
1035
+ * conn.backend_key() -> Integer
1036
+ *
1037
+ * Returns the key of the backend server process for this connection.
1038
+ * This key can be used to cancel queries on the server.
1039
+ */
1040
+ static VALUE
1041
+ pgconn_backend_key(VALUE self)
1042
+ {
1043
+ int be_key;
1044
+ struct pg_cancel *cancel;
1045
+ PGconn *conn = pg_get_pgconn(self);
1046
+
1047
+ cancel = (struct pg_cancel*)PQgetCancel(conn);
1048
+ if(cancel == NULL)
1049
+ pg_raise_conn_error( rb_ePGerror, self, "Invalid connection!");
1050
+
1051
+ if( cancel->be_pid != PQbackendPID(conn) )
1052
+ rb_raise(rb_ePGerror,"Unexpected binary struct layout - please file a bug report at ruby-pg!");
1053
+
1054
+ be_key = cancel->be_key;
1055
+
1056
+ PQfreeCancel(cancel);
1057
+
1058
+ return INT2NUM(be_key);
1059
+ }
1060
+ #endif
1061
+
921
1062
  /*
922
1063
  * call-seq:
923
1064
  * conn.connection_needs_password() -> Boolean
@@ -948,7 +1089,7 @@ pgconn_connection_used_password(VALUE self)
948
1089
  /* :TODO: get_ssl */
949
1090
 
950
1091
 
951
- static VALUE pgconn_exec_params( int, VALUE *, VALUE );
1092
+ static VALUE pgconn_sync_exec_params( int, VALUE *, VALUE );
952
1093
 
953
1094
  /*
954
1095
  * call-seq:
@@ -962,11 +1103,11 @@ static VALUE pgconn_exec_params( int, VALUE *, VALUE );
962
1103
  * However #async_exec has two advantages:
963
1104
  *
964
1105
  * 1. #async_exec can be aborted by signals (like Ctrl-C), while #exec blocks signal processing until the query is answered.
965
- * 2. Ruby VM gets notified about IO blocked operations.
966
- * It can therefore schedule things like garbage collection, while queries are running like in this proposal: https://bugs.ruby-lang.org/issues/14723
1106
+ * 2. Ruby VM gets notified about IO blocked operations and can pass them through <tt>Fiber.scheduler</tt>.
1107
+ * So only <tt>async_*</tt> methods are compatible to event based schedulers like the async gem.
967
1108
  */
968
1109
  static VALUE
969
- pgconn_exec(int argc, VALUE *argv, VALUE self)
1110
+ pgconn_sync_exec(int argc, VALUE *argv, VALUE self)
970
1111
  {
971
1112
  t_pg_connection *this = pg_get_connection_safe( self );
972
1113
  PGresult *result = NULL;
@@ -987,7 +1128,7 @@ pgconn_exec(int argc, VALUE *argv, VALUE self)
987
1128
  pg_deprecated(0, ("forwarding exec to exec_params is deprecated"));
988
1129
 
989
1130
  /* Otherwise, just call #exec_params instead for backward-compatibility */
990
- return pgconn_exec_params( argc, argv, self );
1131
+ return pgconn_sync_exec_params( argc, argv, self );
991
1132
 
992
1133
  }
993
1134
 
@@ -1019,7 +1160,7 @@ struct query_params_data {
1019
1160
  * Filled by alloc_query_params()
1020
1161
  */
1021
1162
 
1022
- /* Wraps the pointer of allocated memory, if function parameters dont't
1163
+ /* Wraps the pointer of allocated memory, if function parameters don't
1023
1164
  * fit in the memory_pool below.
1024
1165
  */
1025
1166
  VALUE heap_pool;
@@ -1037,7 +1178,7 @@ struct query_params_data {
1037
1178
  Oid *types;
1038
1179
 
1039
1180
  /* This array takes the string values for the timeframe of the query,
1040
- * if param value convertion is required
1181
+ * if param value conversion is required
1041
1182
  */
1042
1183
  VALUE gc_array;
1043
1184
 
@@ -1051,8 +1192,9 @@ struct query_params_data {
1051
1192
  };
1052
1193
 
1053
1194
  static void
1054
- free_typecast_heap_chain(struct linked_typecast_data *chain_entry)
1195
+ free_typecast_heap_chain(void *_chain_entry)
1055
1196
  {
1197
+ struct linked_typecast_data *chain_entry = (struct linked_typecast_data *)_chain_entry;
1056
1198
  while(chain_entry){
1057
1199
  struct linked_typecast_data *next = chain_entry->next;
1058
1200
  xfree(chain_entry);
@@ -1060,6 +1202,18 @@ free_typecast_heap_chain(struct linked_typecast_data *chain_entry)
1060
1202
  }
1061
1203
  }
1062
1204
 
1205
+ static const rb_data_type_t pg_typecast_buffer_type = {
1206
+ "PG::Connection typecast buffer chain",
1207
+ {
1208
+ (RUBY_DATA_FUNC) NULL,
1209
+ free_typecast_heap_chain,
1210
+ (size_t (*)(const void *))NULL,
1211
+ },
1212
+ 0,
1213
+ 0,
1214
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1215
+ };
1216
+
1063
1217
  static char *
1064
1218
  alloc_typecast_buf( VALUE *typecast_heap_chain, int len )
1065
1219
  {
@@ -1070,17 +1224,28 @@ alloc_typecast_buf( VALUE *typecast_heap_chain, int len )
1070
1224
  /* Did we already wrap a memory chain per T_DATA object? */
1071
1225
  if( NIL_P( *typecast_heap_chain ) ){
1072
1226
  /* Leave free'ing of the buffer chain to the GC, when paramsData has left the stack */
1073
- *typecast_heap_chain = Data_Wrap_Struct( rb_cObject, NULL, free_typecast_heap_chain, allocated );
1227
+ *typecast_heap_chain = TypedData_Wrap_Struct( rb_cObject, &pg_typecast_buffer_type, allocated );
1074
1228
  allocated->next = NULL;
1075
1229
  } else {
1076
1230
  /* Append to the chain */
1077
- allocated->next = DATA_PTR( *typecast_heap_chain );
1078
- DATA_PTR( *typecast_heap_chain ) = allocated;
1231
+ allocated->next = RTYPEDDATA_DATA( *typecast_heap_chain );
1232
+ RTYPEDDATA_DATA( *typecast_heap_chain ) = allocated;
1079
1233
  }
1080
1234
 
1081
1235
  return &allocated->data[0];
1082
1236
  }
1083
1237
 
1238
+ static const rb_data_type_t pg_query_heap_pool_type = {
1239
+ "PG::Connection query heap pool",
1240
+ {
1241
+ (RUBY_DATA_FUNC) NULL,
1242
+ RUBY_TYPED_DEFAULT_FREE,
1243
+ (size_t (*)(const void *))NULL,
1244
+ },
1245
+ 0,
1246
+ 0,
1247
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1248
+ };
1084
1249
 
1085
1250
  static int
1086
1251
  alloc_query_params(struct query_params_data *paramsData)
@@ -1095,7 +1260,7 @@ alloc_query_params(struct query_params_data *paramsData)
1095
1260
 
1096
1261
  Check_Type(paramsData->params, T_ARRAY);
1097
1262
 
1098
- p_typemap = DATA_PTR( paramsData->typemap );
1263
+ p_typemap = RTYPEDDATA_DATA( paramsData->typemap );
1099
1264
  p_typemap->funcs.fit_to_query( paramsData->typemap, paramsData->params );
1100
1265
 
1101
1266
  paramsData->heap_pool = Qnil;
@@ -1114,7 +1279,7 @@ alloc_query_params(struct query_params_data *paramsData)
1114
1279
  /* Allocate one combined memory pool for all possible function parameters */
1115
1280
  memory_pool = (char*)xmalloc( required_pool_size );
1116
1281
  /* Leave free'ing of the buffer to the GC, when paramsData has left the stack */
1117
- paramsData->heap_pool = Data_Wrap_Struct( rb_cObject, NULL, -1, memory_pool );
1282
+ paramsData->heap_pool = TypedData_Wrap_Struct( rb_cObject, &pg_query_heap_pool_type, memory_pool );
1118
1283
  required_pool_size = 0;
1119
1284
  }else{
1120
1285
  /* Use stack memory for function parameters */
@@ -1165,7 +1330,7 @@ alloc_query_params(struct query_params_data *paramsData)
1165
1330
  paramsData->lengths[i] = 0;
1166
1331
  } else {
1167
1332
  t_pg_coder_enc_func enc_func = pg_coder_enc_func( conv );
1168
- VALUE intermediate;
1333
+ VALUE intermediate = Qnil;
1169
1334
 
1170
1335
  /* 1st pass for retiving the required memory space */
1171
1336
  int len = enc_func(conv, param_value, NULL, &intermediate, paramsData->enc_idx);
@@ -1205,8 +1370,6 @@ alloc_query_params(struct query_params_data *paramsData)
1205
1370
  required_pool_size += len;
1206
1371
  }
1207
1372
  }
1208
-
1209
- RB_GC_GUARD(intermediate);
1210
1373
  }
1211
1374
  }
1212
1375
  }
@@ -1227,12 +1390,11 @@ pgconn_query_assign_typemap( VALUE self, struct query_params_data *paramsData )
1227
1390
  /* Use default typemap for queries. It's type is checked when assigned. */
1228
1391
  paramsData->typemap = pg_get_connection(self)->type_map_for_queries;
1229
1392
  }else{
1393
+ t_typemap *tm;
1394
+ UNUSED(tm);
1395
+
1230
1396
  /* Check type of method param */
1231
- if ( !rb_obj_is_kind_of(paramsData->typemap, rb_cTypeMap) ) {
1232
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
1233
- rb_obj_classname( paramsData->typemap ) );
1234
- }
1235
- Check_Type( paramsData->typemap, T_DATA );
1397
+ TypedData_Get_Struct(paramsData->typemap, t_typemap, &pg_typemap_type, tm);
1236
1398
  }
1237
1399
  }
1238
1400
 
@@ -1246,7 +1408,7 @@ pgconn_query_assign_typemap( VALUE self, struct query_params_data *paramsData )
1246
1408
  * It's not recommended to use explicit sync or async variants but #exec_params instead, unless you have a good reason to do so.
1247
1409
  */
1248
1410
  static VALUE
1249
- pgconn_exec_params( int argc, VALUE *argv, VALUE self )
1411
+ pgconn_sync_exec_params( int argc, VALUE *argv, VALUE self )
1250
1412
  {
1251
1413
  t_pg_connection *this = pg_get_connection_safe( self );
1252
1414
  PGresult *result = NULL;
@@ -1266,7 +1428,7 @@ pgconn_exec_params( int argc, VALUE *argv, VALUE self )
1266
1428
  */
1267
1429
  if ( NIL_P(paramsData.params) ) {
1268
1430
  pg_deprecated(1, ("forwarding exec_params to exec is deprecated"));
1269
- return pgconn_exec( 1, argv, self );
1431
+ return pgconn_sync_exec( 1, argv, self );
1270
1432
  }
1271
1433
  pgconn_query_assign_typemap( self, &paramsData );
1272
1434
 
@@ -1297,7 +1459,7 @@ pgconn_exec_params( int argc, VALUE *argv, VALUE self )
1297
1459
  * It's not recommended to use explicit sync or async variants but #prepare instead, unless you have a good reason to do so.
1298
1460
  */
1299
1461
  static VALUE
1300
- pgconn_prepare(int argc, VALUE *argv, VALUE self)
1462
+ pgconn_sync_prepare(int argc, VALUE *argv, VALUE self)
1301
1463
  {
1302
1464
  t_pg_connection *this = pg_get_connection_safe( self );
1303
1465
  PGresult *result = NULL;
@@ -1346,7 +1508,7 @@ pgconn_prepare(int argc, VALUE *argv, VALUE self)
1346
1508
  * It's not recommended to use explicit sync or async variants but #exec_prepared instead, unless you have a good reason to do so.
1347
1509
  */
1348
1510
  static VALUE
1349
- pgconn_exec_prepared(int argc, VALUE *argv, VALUE self)
1511
+ pgconn_sync_exec_prepared(int argc, VALUE *argv, VALUE self)
1350
1512
  {
1351
1513
  t_pg_connection *this = pg_get_connection_safe( self );
1352
1514
  PGresult *result = NULL;
@@ -1382,6 +1544,19 @@ pgconn_exec_prepared(int argc, VALUE *argv, VALUE self)
1382
1544
  return rb_pgresult;
1383
1545
  }
1384
1546
 
1547
+ static VALUE
1548
+ pgconn_sync_describe_close_prepared_portal(VALUE self, VALUE name, PGresult *(*func)(PGconn *, const char *))
1549
+ {
1550
+ PGresult *result;
1551
+ VALUE rb_pgresult;
1552
+ t_pg_connection *this = pg_get_connection_safe( self );
1553
+ const char *stmt = NIL_P(name) ? NULL : pg_cstr_enc(name, this->enc_idx);
1554
+ result = func(this->pgconn, stmt);
1555
+ rb_pgresult = pg_new_result(result, self);
1556
+ pg_result_check(rb_pgresult);
1557
+ return rb_pgresult;
1558
+ }
1559
+
1385
1560
  /*
1386
1561
  * call-seq:
1387
1562
  * conn.sync_describe_prepared( statement_name ) -> PG::Result
@@ -1391,22 +1566,9 @@ pgconn_exec_prepared(int argc, VALUE *argv, VALUE self)
1391
1566
  * It's not recommended to use explicit sync or async variants but #describe_prepared instead, unless you have a good reason to do so.
1392
1567
  */
1393
1568
  static VALUE
1394
- pgconn_describe_prepared(VALUE self, VALUE stmt_name)
1569
+ pgconn_sync_describe_prepared(VALUE self, VALUE stmt_name)
1395
1570
  {
1396
- PGresult *result;
1397
- VALUE rb_pgresult;
1398
- t_pg_connection *this = pg_get_connection_safe( self );
1399
- const char *stmt;
1400
- if(NIL_P(stmt_name)) {
1401
- stmt = NULL;
1402
- }
1403
- else {
1404
- stmt = pg_cstr_enc(stmt_name, this->enc_idx);
1405
- }
1406
- result = gvl_PQdescribePrepared(this->pgconn, stmt);
1407
- rb_pgresult = pg_new_result(result, self);
1408
- pg_result_check(rb_pgresult);
1409
- return rb_pgresult;
1571
+ return pgconn_sync_describe_close_prepared_portal(self, stmt_name, gvl_PQdescribePrepared);
1410
1572
  }
1411
1573
 
1412
1574
 
@@ -1419,25 +1581,45 @@ pgconn_describe_prepared(VALUE self, VALUE stmt_name)
1419
1581
  * It's not recommended to use explicit sync or async variants but #describe_portal instead, unless you have a good reason to do so.
1420
1582
  */
1421
1583
  static VALUE
1422
- pgconn_describe_portal(self, stmt_name)
1423
- VALUE self, stmt_name;
1584
+ pgconn_sync_describe_portal(VALUE self, VALUE stmt_name)
1424
1585
  {
1425
- PGresult *result;
1426
- VALUE rb_pgresult;
1427
- t_pg_connection *this = pg_get_connection_safe( self );
1428
- const char *stmt;
1429
- if(NIL_P(stmt_name)) {
1430
- stmt = NULL;
1431
- }
1432
- else {
1433
- stmt = pg_cstr_enc(stmt_name, this->enc_idx);
1434
- }
1435
- result = gvl_PQdescribePortal(this->pgconn, stmt);
1436
- rb_pgresult = pg_new_result(result, self);
1437
- pg_result_check(rb_pgresult);
1438
- return rb_pgresult;
1586
+ return pgconn_sync_describe_close_prepared_portal(self, stmt_name, gvl_PQdescribePortal);
1587
+ }
1588
+
1589
+
1590
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
1591
+ /*
1592
+ * call-seq:
1593
+ * conn.sync_close_prepared( stmt_name ) -> PG::Result
1594
+ *
1595
+ * This function has the same behavior as #async_close_prepared, but is implemented using the synchronous command processing API of libpq.
1596
+ * See #async_exec for the differences between the two API variants.
1597
+ * It's not recommended to use explicit sync or async variants but #close_prepared instead, unless you have a good reason to do so.
1598
+ *
1599
+ * Available since PostgreSQL-17.
1600
+ */
1601
+ static VALUE
1602
+ pgconn_sync_close_prepared(VALUE self, VALUE stmt_name)
1603
+ {
1604
+ return pgconn_sync_describe_close_prepared_portal(self, stmt_name, gvl_PQclosePrepared);
1439
1605
  }
1440
1606
 
1607
+ /*
1608
+ * call-seq:
1609
+ * conn.sync_close_portal( portal_name ) -> PG::Result
1610
+ *
1611
+ * This function has the same behavior as #async_close_portal, but is implemented using the synchronous command processing API of libpq.
1612
+ * See #async_exec for the differences between the two API variants.
1613
+ * It's not recommended to use explicit sync or async variants but #close_portal instead, unless you have a good reason to do so.
1614
+ *
1615
+ * Available since PostgreSQL-17.
1616
+ */
1617
+ static VALUE
1618
+ pgconn_sync_close_portal(VALUE self, VALUE stmt_name)
1619
+ {
1620
+ return pgconn_sync_describe_close_prepared_portal(self, stmt_name, gvl_PQclosePortal);
1621
+ }
1622
+ #endif
1441
1623
 
1442
1624
  /*
1443
1625
  * call-seq:
@@ -1454,6 +1636,10 @@ pgconn_describe_portal(self, stmt_name)
1454
1636
  * * +PGRES_NONFATAL_ERROR+
1455
1637
  * * +PGRES_FATAL_ERROR+
1456
1638
  * * +PGRES_COPY_BOTH+
1639
+ * * +PGRES_SINGLE_TUPLE+
1640
+ * * +PGRES_TUPLES_CHUNK+
1641
+ * * +PGRES_PIPELINE_SYNC+
1642
+ * * +PGRES_PIPELINE_ABORTED+
1457
1643
  */
1458
1644
  static VALUE
1459
1645
  pgconn_make_empty_pgresult(VALUE self, VALUE status)
@@ -1509,9 +1695,9 @@ pgconn_s_escape(VALUE self, VALUE string)
1509
1695
  if( !singleton ) {
1510
1696
  size = PQescapeStringConn(pg_get_pgconn(self), RSTRING_PTR(result),
1511
1697
  RSTRING_PTR(string), RSTRING_LEN(string), &error);
1512
- if(error) {
1513
- rb_raise(rb_ePGerror, "%s", PQerrorMessage(pg_get_pgconn(self)));
1514
- }
1698
+ if(error)
1699
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(pg_get_pgconn(self)));
1700
+
1515
1701
  } else {
1516
1702
  size = PQescapeString(RSTRING_PTR(result), RSTRING_PTR(string), RSTRING_LEN(string));
1517
1703
  }
@@ -1607,7 +1793,6 @@ pgconn_escape_literal(VALUE self, VALUE string)
1607
1793
  {
1608
1794
  t_pg_connection *this = pg_get_connection_safe( self );
1609
1795
  char *escaped = NULL;
1610
- VALUE error;
1611
1796
  VALUE result = Qnil;
1612
1797
  int enc_idx = this->enc_idx;
1613
1798
 
@@ -1618,12 +1803,8 @@ pgconn_escape_literal(VALUE self, VALUE string)
1618
1803
 
1619
1804
  escaped = PQescapeLiteral(this->pgconn, RSTRING_PTR(string), RSTRING_LEN(string));
1620
1805
  if (escaped == NULL)
1621
- {
1622
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
1623
- rb_iv_set(error, "@connection", self);
1624
- rb_exc_raise(error);
1625
- return Qnil;
1626
- }
1806
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(this->pgconn));
1807
+
1627
1808
  result = rb_str_new2(escaped);
1628
1809
  PQfreemem(escaped);
1629
1810
  PG_ENCODING_SET_NOCHECK(result, enc_idx);
@@ -1646,7 +1827,6 @@ pgconn_escape_identifier(VALUE self, VALUE string)
1646
1827
  {
1647
1828
  t_pg_connection *this = pg_get_connection_safe( self );
1648
1829
  char *escaped = NULL;
1649
- VALUE error;
1650
1830
  VALUE result = Qnil;
1651
1831
  int enc_idx = this->enc_idx;
1652
1832
 
@@ -1657,12 +1837,8 @@ pgconn_escape_identifier(VALUE self, VALUE string)
1657
1837
 
1658
1838
  escaped = PQescapeIdentifier(this->pgconn, RSTRING_PTR(string), RSTRING_LEN(string));
1659
1839
  if (escaped == NULL)
1660
- {
1661
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
1662
- rb_iv_set(error, "@connection", self);
1663
- rb_exc_raise(error);
1664
- return Qnil;
1665
- }
1840
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(this->pgconn));
1841
+
1666
1842
  result = rb_str_new2(escaped);
1667
1843
  PQfreemem(escaped);
1668
1844
  PG_ENCODING_SET_NOCHECK(result, enc_idx);
@@ -1686,14 +1862,11 @@ pgconn_escape_identifier(VALUE self, VALUE string)
1686
1862
  * (column names, types, etc) that an ordinary Result object for the query
1687
1863
  * would have.
1688
1864
  *
1689
- * *Caution:* While processing a query, the server may return some rows and
1690
- * then encounter an error, causing the query to be aborted. Ordinarily, pg
1691
- * discards any such rows and reports only the error. But in single-row mode,
1692
- * those rows will have already been returned to the application. Hence, the
1693
- * application will see some Result objects followed by an Error raised in get_result.
1694
- * For proper transactional behavior, the application must be designed to discard
1695
- * or undo whatever has been done with the previously-processed rows, if the query
1696
- * ultimately fails.
1865
+ * *Caution:* While processing a query, the server may return some rows and then encounter an error, causing the query to be aborted.
1866
+ * Ordinarily, pg discards any such rows and reports only the error.
1867
+ * But in single-row or chunked mode, some rows may have already been returned to the application.
1868
+ * Hence, the application will see some PGRES_SINGLE_TUPLE or PGRES_TUPLES_CHUNK PG::Result objects followed by a PG::Error raised in get_result.
1869
+ * For proper transactional behavior, the application must be designed to discard or undo whatever has been done with the previously-processed rows, if the query ultimately fails.
1697
1870
  *
1698
1871
  * Example:
1699
1872
  * conn.send_query( "your SQL command" )
@@ -1710,26 +1883,61 @@ static VALUE
1710
1883
  pgconn_set_single_row_mode(VALUE self)
1711
1884
  {
1712
1885
  PGconn *conn = pg_get_pgconn(self);
1713
- VALUE error;
1714
1886
 
1887
+ rb_check_frozen(self);
1715
1888
  if( PQsetSingleRowMode(conn) == 0 )
1716
- {
1717
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
1718
- rb_iv_set(error, "@connection", self);
1719
- rb_exc_raise(error);
1720
- }
1889
+ pg_raise_conn_error( rb_ePGerror, self, "PQsetSingleRowMode %s", PQerrorMessage(conn));
1721
1890
 
1722
1891
  return self;
1723
1892
  }
1724
1893
 
1725
- static VALUE pgconn_send_query_params(int argc, VALUE *argv, VALUE self);
1726
-
1894
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
1727
1895
  /*
1728
1896
  * call-seq:
1729
- * conn.send_query(sql) -> nil
1897
+ * conn.set_chunked_rows_mode -> self
1730
1898
  *
1731
- * Sends SQL query request specified by _sql_ to PostgreSQL for
1732
- * asynchronous processing, and immediately returns.
1899
+ * Select chunked mode for the currently-executing query.
1900
+ *
1901
+ * This function is similar to set_single_row_mode, except that it specifies retrieval of up to +chunk_size+ rows per PGresult, not necessarily just one row.
1902
+ * This function can only be called immediately after send_query or one of its sibling functions, before any other operation on the connection such as consume_input or get_result.
1903
+ * If called at the correct time, the function activates chunked mode for the current query.
1904
+ * Otherwise the mode stays unchanged and the function raises an error.
1905
+ * In any case, the mode reverts to normal after completion of the current query.
1906
+ *
1907
+ * Example:
1908
+ * conn.send_query( "your SQL command" )
1909
+ * conn.set_chunked_rows_mode(10)
1910
+ * loop do
1911
+ * res = conn.get_result or break
1912
+ * res.check
1913
+ * res.each do |row|
1914
+ * # do something with the received max. 10 rows
1915
+ * end
1916
+ * end
1917
+ *
1918
+ * Available since PostgreSQL-17
1919
+ */
1920
+ static VALUE
1921
+ pgconn_set_chunked_rows_mode(VALUE self, VALUE chunk_size)
1922
+ {
1923
+ PGconn *conn = pg_get_pgconn(self);
1924
+
1925
+ rb_check_frozen(self);
1926
+ if( PQsetChunkedRowsMode(conn, NUM2INT(chunk_size)) == 0 )
1927
+ pg_raise_conn_error( rb_ePGerror, self, "PQsetChunkedRowsMode %s", PQerrorMessage(conn));
1928
+
1929
+ return self;
1930
+ }
1931
+ #endif
1932
+
1933
+ static VALUE pgconn_send_query_params(int argc, VALUE *argv, VALUE self);
1934
+
1935
+ /*
1936
+ * call-seq:
1937
+ * conn.send_query(sql) -> nil
1938
+ *
1939
+ * Sends SQL query request specified by _sql_ to PostgreSQL for
1940
+ * asynchronous processing, and immediately returns.
1733
1941
  * On failure, it raises a PG::Error.
1734
1942
  *
1735
1943
  * For backward compatibility, if you pass more than one parameter to this method,
@@ -1741,15 +1949,13 @@ static VALUE
1741
1949
  pgconn_send_query(int argc, VALUE *argv, VALUE self)
1742
1950
  {
1743
1951
  t_pg_connection *this = pg_get_connection_safe( self );
1744
- VALUE error;
1745
1952
 
1746
1953
  /* If called with no or nil parameters, use PQexec for compatibility */
1747
1954
  if ( argc == 1 || (argc >= 2 && argc <= 4 && NIL_P(argv[1]) )) {
1748
- if(gvl_PQsendQuery(this->pgconn, pg_cstr_enc(argv[0], this->enc_idx)) == 0) {
1749
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
1750
- rb_iv_set(error, "@connection", self);
1751
- rb_exc_raise(error);
1752
- }
1955
+ if(gvl_PQsendQuery(this->pgconn, pg_cstr_enc(argv[0], this->enc_idx)) == 0)
1956
+ pg_raise_conn_error( rb_eUnableToSend, self, "PQsendQuery %s", PQerrorMessage(this->pgconn));
1957
+
1958
+ pgconn_wait_for_flush( self );
1753
1959
  return Qnil;
1754
1960
  }
1755
1961
 
@@ -1779,7 +1985,7 @@ pgconn_send_query(int argc, VALUE *argv, VALUE self)
1779
1985
  * or, it may be a String. If it is a string, that is equivalent to the hash:
1780
1986
  * { :value => <string value>, :type => 0, :format => 0 }
1781
1987
  *
1782
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
1988
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
1783
1989
  * inside the SQL query. The 0th element of the +params+ array is bound
1784
1990
  * to $1, the 1st element is bound to $2, etc. +nil+ is treated as +NULL+.
1785
1991
  *
@@ -1805,7 +2011,6 @@ pgconn_send_query_params(int argc, VALUE *argv, VALUE self)
1805
2011
  t_pg_connection *this = pg_get_connection_safe( self );
1806
2012
  int result;
1807
2013
  VALUE command, in_res_fmt;
1808
- VALUE error;
1809
2014
  int nParams;
1810
2015
  int resultFormat;
1811
2016
  struct query_params_data paramsData = { this->enc_idx };
@@ -1822,11 +2027,10 @@ pgconn_send_query_params(int argc, VALUE *argv, VALUE self)
1822
2027
 
1823
2028
  free_query_params( &paramsData );
1824
2029
 
1825
- if(result == 0) {
1826
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
1827
- rb_iv_set(error, "@connection", self);
1828
- rb_exc_raise(error);
1829
- }
2030
+ if(result == 0)
2031
+ pg_raise_conn_error( rb_eUnableToSend, self, "PQsendQueryParams %s", PQerrorMessage(this->pgconn));
2032
+
2033
+ pgconn_wait_for_flush( self );
1830
2034
  return Qnil;
1831
2035
  }
1832
2036
 
@@ -1847,7 +2051,7 @@ pgconn_send_query_params(int argc, VALUE *argv, VALUE self)
1847
2051
  *
1848
2052
  * For example: "SELECT $1::int"
1849
2053
  *
1850
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
2054
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
1851
2055
  * inside the SQL query.
1852
2056
  */
1853
2057
  static VALUE
@@ -1857,7 +2061,6 @@ pgconn_send_prepare(int argc, VALUE *argv, VALUE self)
1857
2061
  int result;
1858
2062
  VALUE name, command, in_paramtypes;
1859
2063
  VALUE param;
1860
- VALUE error;
1861
2064
  int i = 0;
1862
2065
  int nParams = 0;
1863
2066
  Oid *paramTypes = NULL;
@@ -1886,10 +2089,9 @@ pgconn_send_prepare(int argc, VALUE *argv, VALUE self)
1886
2089
  xfree(paramTypes);
1887
2090
 
1888
2091
  if(result == 0) {
1889
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
1890
- rb_iv_set(error, "@connection", self);
1891
- rb_exc_raise(error);
2092
+ pg_raise_conn_error( rb_eUnableToSend, self, "PQsendPrepare %s", PQerrorMessage(this->pgconn));
1892
2093
  }
2094
+ pgconn_wait_for_flush( self );
1893
2095
  return Qnil;
1894
2096
  }
1895
2097
 
@@ -1911,7 +2113,7 @@ pgconn_send_prepare(int argc, VALUE *argv, VALUE self)
1911
2113
  * or, it may be a String. If it is a string, that is equivalent to the hash:
1912
2114
  * { :value => <string value>, :format => 0 }
1913
2115
  *
1914
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
2116
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
1915
2117
  * inside the SQL query. The 0th element of the +params+ array is bound
1916
2118
  * to $1, the 1st element is bound to $2, etc. +nil+ is treated as +NULL+.
1917
2119
  *
@@ -1931,7 +2133,6 @@ pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
1931
2133
  t_pg_connection *this = pg_get_connection_safe( self );
1932
2134
  int result;
1933
2135
  VALUE name, in_res_fmt;
1934
- VALUE error;
1935
2136
  int nParams;
1936
2137
  int resultFormat;
1937
2138
  struct query_params_data paramsData = { this->enc_idx };
@@ -1941,7 +2142,6 @@ pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
1941
2142
 
1942
2143
  if(NIL_P(paramsData.params)) {
1943
2144
  paramsData.params = rb_ary_new2(0);
1944
- resultFormat = 0;
1945
2145
  }
1946
2146
  pgconn_query_assign_typemap( self, &paramsData );
1947
2147
 
@@ -1954,11 +2154,24 @@ pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
1954
2154
 
1955
2155
  free_query_params( &paramsData );
1956
2156
 
1957
- if(result == 0) {
1958
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
1959
- rb_iv_set(error, "@connection", self);
1960
- rb_exc_raise(error);
1961
- }
2157
+ if(result == 0)
2158
+ pg_raise_conn_error( rb_eUnableToSend, self, "PQsendQueryPrepared %s", PQerrorMessage(this->pgconn));
2159
+
2160
+ pgconn_wait_for_flush( self );
2161
+ return Qnil;
2162
+ }
2163
+
2164
+
2165
+ static VALUE
2166
+ pgconn_send_describe_close_prepared_portal(VALUE self, VALUE name, int (*func)(PGconn *, const char *), const char *funame)
2167
+ {
2168
+ t_pg_connection *this = pg_get_connection_safe( self );
2169
+ const char *stmt = NIL_P(name) ? NULL : pg_cstr_enc(name, this->enc_idx);
2170
+ /* returns 0 on failure */
2171
+ if(func(this->pgconn, stmt) == 0)
2172
+ pg_raise_conn_error( rb_eUnableToSend, self, "%s %s", funame, PQerrorMessage(this->pgconn));
2173
+
2174
+ pgconn_wait_for_flush( self );
1962
2175
  return Qnil;
1963
2176
  }
1964
2177
 
@@ -1972,15 +2185,9 @@ pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
1972
2185
  static VALUE
1973
2186
  pgconn_send_describe_prepared(VALUE self, VALUE stmt_name)
1974
2187
  {
1975
- VALUE error;
1976
- t_pg_connection *this = pg_get_connection_safe( self );
1977
- /* returns 0 on failure */
1978
- if(gvl_PQsendDescribePrepared(this->pgconn, pg_cstr_enc(stmt_name, this->enc_idx)) == 0) {
1979
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
1980
- rb_iv_set(error, "@connection", self);
1981
- rb_exc_raise(error);
1982
- }
1983
- return Qnil;
2188
+ return pgconn_send_describe_close_prepared_portal(
2189
+ self, stmt_name, gvl_PQsendDescribePrepared,
2190
+ "PQsendDescribePrepared");
1984
2191
  }
1985
2192
 
1986
2193
 
@@ -1994,36 +2201,50 @@ pgconn_send_describe_prepared(VALUE self, VALUE stmt_name)
1994
2201
  static VALUE
1995
2202
  pgconn_send_describe_portal(VALUE self, VALUE portal)
1996
2203
  {
1997
- VALUE error;
1998
- t_pg_connection *this = pg_get_connection_safe( self );
1999
- /* returns 0 on failure */
2000
- if(gvl_PQsendDescribePortal(this->pgconn, pg_cstr_enc(portal, this->enc_idx)) == 0) {
2001
- error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(this->pgconn));
2002
- rb_iv_set(error, "@connection", self);
2003
- rb_exc_raise(error);
2004
- }
2005
- return Qnil;
2204
+ return pgconn_send_describe_close_prepared_portal(
2205
+ self, portal, gvl_PQsendDescribePortal,
2206
+ "PQsendDescribePortal");
2006
2207
  }
2007
2208
 
2008
-
2209
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
2009
2210
  /*
2010
2211
  * call-seq:
2011
- * conn.get_result() -> PG::Result
2012
- * conn.get_result() {|pg_result| block }
2212
+ * conn.send_close_prepared( statement_name ) -> nil
2213
+ *
2214
+ * Asynchronously send _command_ to the server. Does not block.
2215
+ * Use in combination with +conn.get_result+.
2013
2216
  *
2014
- * Blocks waiting for the next result from a call to
2015
- * #send_query (or another asynchronous command), and returns
2016
- * it. Returns +nil+ if no more results are available.
2217
+ * Available since PostgreSQL-17.
2218
+ */
2219
+ static VALUE
2220
+ pgconn_send_close_prepared(VALUE self, VALUE stmt_name)
2221
+ {
2222
+ return pgconn_send_describe_close_prepared_portal(
2223
+ self, stmt_name, gvl_PQsendClosePrepared,
2224
+ "PQsendClosePrepared");
2225
+ }
2226
+
2227
+
2228
+ /*
2229
+ * call-seq:
2230
+ * conn.send_close_portal( portal_name ) -> nil
2017
2231
  *
2018
- * Note: call this function repeatedly until it returns +nil+, or else
2019
- * you will not be able to issue further commands.
2232
+ * Asynchronously send _command_ to the server. Does not block.
2233
+ * Use in combination with +conn.get_result+.
2020
2234
  *
2021
- * If the optional code block is given, it will be passed <i>result</i> as an argument,
2022
- * and the PG::Result object will automatically be cleared when the block terminates.
2023
- * In this instance, <code>conn.exec</code> returns the value of the block.
2235
+ * Available since PostgreSQL-17.
2024
2236
  */
2025
2237
  static VALUE
2026
- pgconn_get_result(VALUE self)
2238
+ pgconn_send_close_portal(VALUE self, VALUE portal)
2239
+ {
2240
+ return pgconn_send_describe_close_prepared_portal(
2241
+ self, portal, gvl_PQsendClosePortal,
2242
+ "PQsendClosePortal");
2243
+ }
2244
+ #endif
2245
+
2246
+ static VALUE
2247
+ pgconn_sync_get_result(VALUE self)
2027
2248
  {
2028
2249
  PGconn *conn = pg_get_pgconn(self);
2029
2250
  PGresult *result;
@@ -2049,17 +2270,15 @@ pgconn_get_result(VALUE self)
2049
2270
  * or *notifies* to see if the state has changed.
2050
2271
  */
2051
2272
  static VALUE
2052
- pgconn_consume_input(self)
2053
- VALUE self;
2273
+ pgconn_consume_input(VALUE self)
2054
2274
  {
2055
- VALUE error;
2056
2275
  PGconn *conn = pg_get_pgconn(self);
2057
2276
  /* returns 0 on error */
2058
2277
  if(PQconsumeInput(conn) == 0) {
2059
- error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(conn));
2060
- rb_iv_set(error, "@connection", self);
2061
- rb_exc_raise(error);
2278
+ pgconn_close_socket_io(self);
2279
+ pg_raise_conn_error( rb_eConnectionBad, self, "%s", PQerrorMessage(conn));
2062
2280
  }
2281
+
2063
2282
  return Qnil;
2064
2283
  }
2065
2284
 
@@ -2068,38 +2287,20 @@ pgconn_consume_input(self)
2068
2287
  * conn.is_busy() -> Boolean
2069
2288
  *
2070
2289
  * Returns +true+ if a command is busy, that is, if
2071
- * PQgetResult would block. Otherwise returns +false+.
2290
+ * #get_result would block. Otherwise returns +false+.
2072
2291
  */
2073
2292
  static VALUE
2074
- pgconn_is_busy(self)
2075
- VALUE self;
2293
+ pgconn_is_busy(VALUE self)
2076
2294
  {
2077
2295
  return gvl_PQisBusy(pg_get_pgconn(self)) ? Qtrue : Qfalse;
2078
2296
  }
2079
2297
 
2080
- /*
2081
- * call-seq:
2082
- * conn.setnonblocking(Boolean) -> nil
2083
- *
2084
- * Sets the nonblocking status of the connection.
2085
- * In the blocking state, calls to #send_query
2086
- * will block until the message is sent to the server,
2087
- * but will not wait for the query results.
2088
- * In the nonblocking state, calls to #send_query
2089
- * will return an error if the socket is not ready for
2090
- * writing.
2091
- * Note: This function does not affect #exec, because
2092
- * that function doesn't return until the server has
2093
- * processed the query and returned the results.
2094
- * Returns +nil+.
2095
- */
2096
2298
  static VALUE
2097
- pgconn_setnonblocking(self, state)
2098
- VALUE self, state;
2299
+ pgconn_sync_setnonblocking(VALUE self, VALUE state)
2099
2300
  {
2100
2301
  int arg;
2101
- VALUE error;
2102
2302
  PGconn *conn = pg_get_pgconn(self);
2303
+ rb_check_frozen(self);
2103
2304
  if(state == Qtrue)
2104
2305
  arg = 1;
2105
2306
  else if (state == Qfalse)
@@ -2107,67 +2308,33 @@ pgconn_setnonblocking(self, state)
2107
2308
  else
2108
2309
  rb_raise(rb_eArgError, "Boolean value expected");
2109
2310
 
2110
- if(PQsetnonblocking(conn, arg) == -1) {
2111
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
2112
- rb_iv_set(error, "@connection", self);
2113
- rb_exc_raise(error);
2114
- }
2311
+ if(PQsetnonblocking(conn, arg) == -1)
2312
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
2313
+
2115
2314
  return Qnil;
2116
2315
  }
2117
2316
 
2118
2317
 
2119
- /*
2120
- * call-seq:
2121
- * conn.isnonblocking() -> Boolean
2122
- *
2123
- * Returns +true+ if a command is busy, that is, if
2124
- * PQgetResult would block. Otherwise returns +false+.
2125
- */
2126
2318
  static VALUE
2127
- pgconn_isnonblocking(self)
2128
- VALUE self;
2319
+ pgconn_sync_isnonblocking(VALUE self)
2129
2320
  {
2130
2321
  return PQisnonblocking(pg_get_pgconn(self)) ? Qtrue : Qfalse;
2131
2322
  }
2132
2323
 
2133
- /*
2134
- * call-seq:
2135
- * conn.flush() -> Boolean
2136
- *
2137
- * Attempts to flush any queued output data to the server.
2138
- * Returns +true+ if data is successfully flushed, +false+
2139
- * if not (can only return +false+ if connection is
2140
- * nonblocking.
2141
- * Raises PG::Error if some other failure occurred.
2142
- */
2143
2324
  static VALUE
2144
- pgconn_flush(self)
2145
- VALUE self;
2325
+ pgconn_sync_flush(VALUE self)
2146
2326
  {
2147
2327
  PGconn *conn = pg_get_pgconn(self);
2148
- int ret;
2149
- VALUE error;
2150
- ret = PQflush(conn);
2151
- if(ret == -1) {
2152
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
2153
- rb_iv_set(error, "@connection", self);
2154
- rb_exc_raise(error);
2155
- }
2328
+ int ret = PQflush(conn);
2329
+ if(ret == -1)
2330
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
2331
+
2156
2332
  return (ret) ? Qfalse : Qtrue;
2157
2333
  }
2158
2334
 
2159
- /*
2160
- * call-seq:
2161
- * conn.cancel() -> String
2162
- *
2163
- * Requests cancellation of the command currently being
2164
- * processed. (Only implemented in PostgreSQL >= 8.0)
2165
- *
2166
- * Returns +nil+ on success, or a string containing the
2167
- * error message if a failure occurs.
2168
- */
2335
+ #ifndef HAVE_PQSETCHUNKEDROWSMODE
2169
2336
  static VALUE
2170
- pgconn_cancel(VALUE self)
2337
+ pgconn_sync_cancel(VALUE self)
2171
2338
  {
2172
2339
  char errbuf[256];
2173
2340
  PGcancel *cancel;
@@ -2176,9 +2343,9 @@ pgconn_cancel(VALUE self)
2176
2343
 
2177
2344
  cancel = PQgetCancel(pg_get_pgconn(self));
2178
2345
  if(cancel == NULL)
2179
- rb_raise(rb_ePGerror,"Invalid connection!");
2346
+ pg_raise_conn_error( rb_ePGerror, self, "Invalid connection!");
2180
2347
 
2181
- ret = gvl_PQcancel(cancel, errbuf, 256);
2348
+ ret = gvl_PQcancel(cancel, errbuf, sizeof(errbuf));
2182
2349
  if(ret == 1)
2183
2350
  retval = Qnil;
2184
2351
  else
@@ -2187,6 +2354,7 @@ pgconn_cancel(VALUE self)
2187
2354
  PQfreeCancel(cancel);
2188
2355
  return retval;
2189
2356
  }
2357
+ #endif
2190
2358
 
2191
2359
 
2192
2360
  /*
@@ -2229,55 +2397,73 @@ pgconn_notifies(VALUE self)
2229
2397
  return hash;
2230
2398
  }
2231
2399
 
2232
- /* Win32 + Ruby 1.9+ */
2233
- #if defined( _WIN32 )
2234
- /*
2235
- * On Windows, use platform-specific strategies to wait for the socket
2236
- * instead of rb_wait_for_single_fd().
2400
+ #ifndef HAVE_RB_IO_DESCRIPTOR
2401
+ static int
2402
+ rb_io_descriptor(VALUE io)
2403
+ {
2404
+ rb_io_t *fptr;
2405
+ Check_Type(io, T_FILE);
2406
+ fptr = RFILE(io)->fptr;
2407
+ rb_io_check_closed(fptr);
2408
+ return fptr->fd;
2409
+ }
2410
+ #endif
2411
+
2412
+ #if defined(_WIN32)
2413
+
2414
+ /* We use a specialized implementation of rb_io_wait() on Windows.
2415
+ * This is because rb_io_wait() and rb_wait_for_single_fd() are very slow on Windows.
2237
2416
  */
2238
2417
 
2418
+ #if defined(HAVE_RUBY_FIBER_SCHEDULER_H)
2419
+ #include <ruby/fiber/scheduler.h>
2420
+ #endif
2421
+
2422
+ typedef enum {
2423
+ PG_RUBY_IO_READABLE = RB_WAITFD_IN,
2424
+ PG_RUBY_IO_WRITABLE = RB_WAITFD_OUT,
2425
+ PG_RUBY_IO_PRIORITY = RB_WAITFD_PRI,
2426
+ } pg_rb_io_event_t;
2427
+
2239
2428
  int rb_w32_wait_events( HANDLE *events, int num, DWORD timeout );
2240
2429
 
2241
- static void *
2242
- wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readable)(PGconn *) )
2243
- {
2244
- int sd = PQsocket( conn );
2245
- void *retval;
2430
+ static VALUE
2431
+ pg_rb_thread_io_wait(VALUE io, VALUE events, VALUE timeout) {
2432
+ struct timeval ptimeout;
2433
+
2246
2434
  struct timeval aborttime={0,0}, currtime, waittime;
2247
2435
  DWORD timeout_milisec = INFINITE;
2248
- DWORD wait_ret;
2249
- WSAEVENT hEvent;
2436
+ HANDLE hEvent = WSACreateEvent();
2250
2437
 
2251
- if ( sd < 0 )
2252
- rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
2253
-
2254
- hEvent = WSACreateEvent();
2438
+ long rb_events = NUM2UINT(events);
2439
+ long w32_events = 0;
2440
+ DWORD wait_ret;
2255
2441
 
2256
- /* Check for connection errors (PQisBusy is true on connection errors) */
2257
- if( PQconsumeInput(conn) == 0 ) {
2258
- WSACloseEvent( hEvent );
2259
- rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
2260
- }
2442
+ if( !NIL_P(timeout) ){
2443
+ ptimeout.tv_sec = (time_t)(NUM2DBL(timeout));
2444
+ ptimeout.tv_usec = (time_t)((NUM2DBL(timeout) - (double)ptimeout.tv_sec) * 1e6);
2261
2445
 
2262
- if ( ptimeout ) {
2263
2446
  gettimeofday(&currtime, NULL);
2264
- timeradd(&currtime, ptimeout, &aborttime);
2447
+ timeradd(&currtime, &ptimeout, &aborttime);
2265
2448
  }
2266
2449
 
2267
- while ( !(retval=is_readable(conn)) ) {
2268
- if ( WSAEventSelect(sd, hEvent, FD_READ|FD_CLOSE) == SOCKET_ERROR ) {
2450
+ if(rb_events & PG_RUBY_IO_READABLE) w32_events |= FD_READ | FD_ACCEPT | FD_CLOSE;
2451
+ if(rb_events & PG_RUBY_IO_WRITABLE) w32_events |= FD_WRITE | FD_CONNECT;
2452
+ if(rb_events & PG_RUBY_IO_PRIORITY) w32_events |= FD_OOB;
2453
+
2454
+ for(;;) {
2455
+ if ( WSAEventSelect(_get_osfhandle(rb_io_descriptor(io)), hEvent, w32_events) == SOCKET_ERROR ) {
2269
2456
  WSACloseEvent( hEvent );
2270
2457
  rb_raise( rb_eConnectionBad, "WSAEventSelect socket error: %d", WSAGetLastError() );
2271
2458
  }
2272
2459
 
2273
- if ( ptimeout ) {
2460
+ if ( !NIL_P(timeout) ) {
2274
2461
  gettimeofday(&currtime, NULL);
2275
2462
  timersub(&aborttime, &currtime, &waittime);
2276
2463
  timeout_milisec = (DWORD)( waittime.tv_sec * 1e3 + waittime.tv_usec / 1e3 );
2277
2464
  }
2278
2465
 
2279
- /* Is the given timeout valid? */
2280
- if( !ptimeout || (waittime.tv_sec >= 0 && waittime.tv_usec >= 0) ){
2466
+ if( NIL_P(timeout) || (waittime.tv_sec >= 0 && waittime.tv_usec >= 0) ){
2281
2467
  /* Wait for the socket to become readable before checking again */
2282
2468
  wait_ret = rb_w32_wait_events( &hEvent, 1, timeout_milisec );
2283
2469
  } else {
@@ -2286,9 +2472,11 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
2286
2472
 
2287
2473
  if ( wait_ret == WAIT_TIMEOUT ) {
2288
2474
  WSACloseEvent( hEvent );
2289
- return NULL;
2475
+ return UINT2NUM(0);
2290
2476
  } else if ( wait_ret == WAIT_OBJECT_0 ) {
2477
+ WSACloseEvent( hEvent );
2291
2478
  /* The event we were waiting for. */
2479
+ return UINT2NUM(rb_events);
2292
2480
  } else if ( wait_ret == WAIT_OBJECT_0 + 1) {
2293
2481
  /* This indicates interruption from timer thread, GC, exception
2294
2482
  * from other threads etc... */
@@ -2300,36 +2488,64 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
2300
2488
  WSACloseEvent( hEvent );
2301
2489
  rb_raise( rb_eConnectionBad, "Wait on socket abandoned (WaitForMultipleObjects)" );
2302
2490
  }
2303
-
2304
- /* Check for connection errors (PQisBusy is true on connection errors) */
2305
- if ( PQconsumeInput(conn) == 0 ) {
2306
- WSACloseEvent( hEvent );
2307
- rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
2308
- }
2309
2491
  }
2492
+ }
2310
2493
 
2311
- WSACloseEvent( hEvent );
2312
- return retval;
2494
+ static VALUE
2495
+ pg_rb_io_wait(VALUE io, VALUE events, VALUE timeout) {
2496
+ #if defined(HAVE_RUBY_FIBER_SCHEDULER_H)
2497
+ /* We don't support Fiber.scheduler on Windows ruby-3.0 because there is no fast way to check whether a scheduler is active.
2498
+ * Fortunately ruby-3.1 offers a C-API for it.
2499
+ */
2500
+ VALUE scheduler = rb_fiber_scheduler_current();
2501
+
2502
+ if (!NIL_P(scheduler)) {
2503
+ return rb_io_wait(io, events, timeout);
2504
+ }
2505
+ #endif
2506
+ return pg_rb_thread_io_wait(io, events, timeout);
2313
2507
  }
2314
2508
 
2509
+ #elif defined(HAVE_RB_IO_WAIT)
2510
+
2511
+ /* Use our own function and constants names, to avoid conflicts with truffleruby-head on its road to ruby-3.0 compatibility. */
2512
+ #define pg_rb_io_wait rb_io_wait
2513
+ #define PG_RUBY_IO_READABLE RUBY_IO_READABLE
2514
+ #define PG_RUBY_IO_WRITABLE RUBY_IO_WRITABLE
2515
+ #define PG_RUBY_IO_PRIORITY RUBY_IO_PRIORITY
2516
+
2315
2517
  #else
2518
+ /* For compat with ruby < 3.0 */
2519
+
2520
+ typedef enum {
2521
+ PG_RUBY_IO_READABLE = RB_WAITFD_IN,
2522
+ PG_RUBY_IO_WRITABLE = RB_WAITFD_OUT,
2523
+ PG_RUBY_IO_PRIORITY = RB_WAITFD_PRI,
2524
+ } pg_rb_io_event_t;
2316
2525
 
2317
- /* non Win32 */
2526
+ static VALUE
2527
+ pg_rb_io_wait(VALUE io, VALUE events, VALUE timeout) {
2528
+ struct timeval waittime;
2529
+ int res;
2530
+
2531
+ if( !NIL_P(timeout) ){
2532
+ waittime.tv_sec = (time_t)(NUM2DBL(timeout));
2533
+ waittime.tv_usec = (time_t)((NUM2DBL(timeout) - (double)waittime.tv_sec) * 1e6);
2534
+ }
2535
+ res = rb_wait_for_single_fd(rb_io_descriptor(io), NUM2UINT(events), NIL_P(timeout) ? NULL : &waittime);
2536
+
2537
+ return UINT2NUM(res);
2538
+ }
2539
+ #endif
2318
2540
 
2319
2541
  static void *
2320
- wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readable)(PGconn *))
2542
+ wait_socket_readable( VALUE self, struct timeval *ptimeout, void *(*is_readable)(PGconn *))
2321
2543
  {
2322
- int sd = PQsocket( conn );
2323
- int ret;
2544
+ VALUE ret;
2324
2545
  void *retval;
2325
2546
  struct timeval aborttime={0,0}, currtime, waittime;
2326
-
2327
- if ( sd < 0 )
2328
- rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
2329
-
2330
- /* Check for connection errors (PQisBusy is true on connection errors) */
2331
- if ( PQconsumeInput(conn) == 0 )
2332
- rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
2547
+ VALUE wait_timeout = Qnil;
2548
+ PGconn *conn = pg_get_pgconn(self);
2333
2549
 
2334
2550
  if ( ptimeout ) {
2335
2551
  gettimeofday(&currtime, NULL);
@@ -2340,36 +2556,81 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
2340
2556
  if ( ptimeout ) {
2341
2557
  gettimeofday(&currtime, NULL);
2342
2558
  timersub(&aborttime, &currtime, &waittime);
2559
+ wait_timeout = DBL2NUM((double)(waittime.tv_sec) + (double)(waittime.tv_usec) / 1000000.0);
2343
2560
  }
2344
2561
 
2345
2562
  /* Is the given timeout valid? */
2346
2563
  if( !ptimeout || (waittime.tv_sec >= 0 && waittime.tv_usec >= 0) ){
2564
+ VALUE socket_io;
2565
+
2566
+ /* before we wait for data, make sure everything has been sent */
2567
+ pgconn_async_flush(self);
2568
+ if ((retval=is_readable(conn)))
2569
+ return retval;
2570
+
2571
+ socket_io = pgconn_socket_io(self);
2347
2572
  /* Wait for the socket to become readable before checking again */
2348
- ret = rb_wait_for_single_fd( sd, RB_WAITFD_IN, ptimeout ? &waittime : NULL );
2573
+ ret = pg_rb_io_wait(socket_io, RB_INT2NUM(PG_RUBY_IO_READABLE), wait_timeout);
2349
2574
  } else {
2350
- ret = 0;
2351
- }
2352
-
2353
- if ( ret < 0 ){
2354
- rb_sys_fail( "rb_wait_for_single_fd()" );
2575
+ ret = Qfalse;
2355
2576
  }
2356
2577
 
2357
2578
  /* Return false if the select() timed out */
2358
- if ( ret == 0 ){
2579
+ if ( ret == Qfalse ){
2359
2580
  return NULL;
2360
2581
  }
2361
2582
 
2362
2583
  /* Check for connection errors (PQisBusy is true on connection errors) */
2363
2584
  if ( PQconsumeInput(conn) == 0 ){
2364
- rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
2585
+ pgconn_close_socket_io(self);
2586
+ pg_raise_conn_error(rb_eConnectionBad, self, "PQconsumeInput() %s", PQerrorMessage(conn));
2365
2587
  }
2366
2588
  }
2367
2589
 
2368
2590
  return retval;
2369
2591
  }
2370
2592
 
2593
+ /*
2594
+ * call-seq:
2595
+ * conn.flush() -> Boolean
2596
+ *
2597
+ * Attempts to flush any queued output data to the server.
2598
+ * Returns +true+ if data is successfully flushed, +false+
2599
+ * if not. It can only return +false+ if connection is
2600
+ * in nonblocking mode.
2601
+ * Raises PG::Error if some other failure occurred.
2602
+ */
2603
+ static VALUE
2604
+ pgconn_async_flush(VALUE self)
2605
+ {
2606
+ while( pgconn_sync_flush(self) == Qfalse ){
2607
+ /* wait for the socket to become read- or write-ready */
2608
+ int events;
2609
+ VALUE socket_io = pgconn_socket_io(self);
2610
+ events = RB_NUM2INT(pg_rb_io_wait(socket_io, RB_INT2NUM(PG_RUBY_IO_READABLE | PG_RUBY_IO_WRITABLE), Qnil));
2611
+
2612
+ if (events & PG_RUBY_IO_READABLE){
2613
+ pgconn_consume_input(self);
2614
+ }
2615
+ }
2616
+ return Qtrue;
2617
+ }
2618
+
2619
+ static VALUE
2620
+ pgconn_wait_for_flush( VALUE self ){
2621
+ if( !pg_get_connection_safe(self)->flush_data )
2622
+ return Qnil;
2371
2623
 
2372
- #endif
2624
+ return pgconn_async_flush(self);
2625
+ }
2626
+
2627
+ static VALUE
2628
+ pgconn_flush_data_set( VALUE self, VALUE enabled ){
2629
+ t_pg_connection *conn = pg_get_connection(self);
2630
+ rb_check_frozen(self);
2631
+ conn->flush_data = RTEST(enabled);
2632
+ return enabled;
2633
+ }
2373
2634
 
2374
2635
  static void *
2375
2636
  notify_readable(PGconn *conn)
@@ -2408,7 +2669,7 @@ pgconn_wait_for_notify(int argc, VALUE *argv, VALUE self)
2408
2669
  ptimeout = &timeout;
2409
2670
  }
2410
2671
 
2411
- pnotification = (PGnotify*) wait_socket_readable( this->pgconn, ptimeout, notify_readable);
2672
+ pnotification = (PGnotify*) wait_socket_readable( self, ptimeout, notify_readable);
2412
2673
 
2413
2674
  /* Return nil if the select timed out */
2414
2675
  if ( !pnotification ) return Qnil;
@@ -2429,28 +2690,8 @@ pgconn_wait_for_notify(int argc, VALUE *argv, VALUE self)
2429
2690
  }
2430
2691
 
2431
2692
 
2432
- /*
2433
- * call-seq:
2434
- * conn.put_copy_data( buffer [, encoder] ) -> Boolean
2435
- *
2436
- * Transmits _buffer_ as copy data to the server.
2437
- * Returns true if the data was sent, false if it was
2438
- * not sent (false is only possible if the connection
2439
- * is in nonblocking mode, and this command would block).
2440
- *
2441
- * _encoder_ can be a PG::Coder derivation (typically PG::TextEncoder::CopyRow).
2442
- * This encodes the data fields given as _buffer_ from an Array of Strings to
2443
- * PostgreSQL's COPY text format inclusive proper escaping. Optionally
2444
- * the encoder can type cast the fields from various Ruby types in one step,
2445
- * if PG::TextEncoder::CopyRow#type_map is set accordingly.
2446
- *
2447
- * Raises an exception if an error occurs.
2448
- *
2449
- * See also #copy_data.
2450
- *
2451
- */
2452
2693
  static VALUE
2453
- pgconn_put_copy_data(int argc, VALUE *argv, VALUE self)
2694
+ pgconn_sync_put_copy_data(int argc, VALUE *argv, VALUE self)
2454
2695
  {
2455
2696
  int ret;
2456
2697
  int len;
@@ -2458,7 +2699,7 @@ pgconn_put_copy_data(int argc, VALUE *argv, VALUE self)
2458
2699
  VALUE value;
2459
2700
  VALUE buffer = Qnil;
2460
2701
  VALUE encoder;
2461
- VALUE intermediate;
2702
+ VALUE intermediate = Qnil;
2462
2703
  t_pg_coder *p_coder = NULL;
2463
2704
 
2464
2705
  rb_scan_args( argc, argv, "11", &value, &encoder );
@@ -2467,13 +2708,11 @@ pgconn_put_copy_data(int argc, VALUE *argv, VALUE self)
2467
2708
  if( NIL_P(this->encoder_for_put_copy_data) ){
2468
2709
  buffer = value;
2469
2710
  } else {
2470
- p_coder = DATA_PTR( this->encoder_for_put_copy_data );
2711
+ p_coder = RTYPEDDATA_DATA( this->encoder_for_put_copy_data );
2471
2712
  }
2472
- } else if( rb_obj_is_kind_of(encoder, rb_cPG_Coder) ) {
2473
- Data_Get_Struct( encoder, t_pg_coder, p_coder );
2474
2713
  } else {
2475
- rb_raise( rb_eTypeError, "wrong encoder type %s (expected some kind of PG::Coder)",
2476
- rb_obj_classname( encoder ) );
2714
+ /* Check argument type and use argument encoder */
2715
+ TypedData_Get_Struct(encoder, t_pg_coder, &pg_coder_type, p_coder);
2477
2716
  }
2478
2717
 
2479
2718
  if( p_coder ){
@@ -2496,36 +2735,18 @@ pgconn_put_copy_data(int argc, VALUE *argv, VALUE self)
2496
2735
  Check_Type(buffer, T_STRING);
2497
2736
 
2498
2737
  ret = gvl_PQputCopyData(this->pgconn, RSTRING_PTR(buffer), RSTRING_LENINT(buffer));
2499
- if(ret == -1) {
2500
- VALUE error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
2501
- rb_iv_set(error, "@connection", self);
2502
- rb_exc_raise(error);
2503
- }
2504
- RB_GC_GUARD(intermediate);
2738
+ if(ret == -1)
2739
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(this->pgconn));
2740
+
2505
2741
  RB_GC_GUARD(buffer);
2506
2742
 
2507
2743
  return (ret) ? Qtrue : Qfalse;
2508
2744
  }
2509
2745
 
2510
- /*
2511
- * call-seq:
2512
- * conn.put_copy_end( [ error_message ] ) -> Boolean
2513
- *
2514
- * Sends end-of-data indication to the server.
2515
- *
2516
- * _error_message_ is an optional parameter, and if set,
2517
- * forces the COPY command to fail with the string
2518
- * _error_message_.
2519
- *
2520
- * Returns true if the end-of-data was sent, false if it was
2521
- * not sent (false is only possible if the connection
2522
- * is in nonblocking mode, and this command would block).
2523
- */
2524
2746
  static VALUE
2525
- pgconn_put_copy_end(int argc, VALUE *argv, VALUE self)
2747
+ pgconn_sync_put_copy_end(int argc, VALUE *argv, VALUE self)
2526
2748
  {
2527
2749
  VALUE str;
2528
- VALUE error;
2529
2750
  int ret;
2530
2751
  const char *error_message = NULL;
2531
2752
  t_pg_connection *this = pg_get_connection_safe( self );
@@ -2536,38 +2757,16 @@ pgconn_put_copy_end(int argc, VALUE *argv, VALUE self)
2536
2757
  error_message = pg_cstr_enc(str, this->enc_idx);
2537
2758
 
2538
2759
  ret = gvl_PQputCopyEnd(this->pgconn, error_message);
2539
- if(ret == -1) {
2540
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
2541
- rb_iv_set(error, "@connection", self);
2542
- rb_exc_raise(error);
2543
- }
2760
+ if(ret == -1)
2761
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(this->pgconn));
2762
+
2544
2763
  return (ret) ? Qtrue : Qfalse;
2545
2764
  }
2546
2765
 
2547
- /*
2548
- * call-seq:
2549
- * conn.get_copy_data( [ async = false [, decoder = nil ]] ) -> Object
2550
- *
2551
- * Return one row of data, +nil+
2552
- * if the copy is done, or +false+ if the call would
2553
- * block (only possible if _async_ is true).
2554
- *
2555
- * If _decoder_ is not set or +nil+, data is returned as binary string.
2556
- *
2557
- * If _decoder_ is set to a PG::Coder derivation, the return type depends on this decoder.
2558
- * PG::TextDecoder::CopyRow decodes the received data fields from one row of PostgreSQL's
2559
- * COPY text format to an Array of Strings.
2560
- * Optionally the decoder can type cast the single fields to various Ruby types in one step,
2561
- * if PG::TextDecoder::CopyRow#type_map is set accordingly.
2562
- *
2563
- * See also #copy_data.
2564
- *
2565
- */
2566
2766
  static VALUE
2567
- pgconn_get_copy_data(int argc, VALUE *argv, VALUE self )
2767
+ pgconn_sync_get_copy_data(int argc, VALUE *argv, VALUE self )
2568
2768
  {
2569
2769
  VALUE async_in;
2570
- VALUE error;
2571
2770
  VALUE result;
2572
2771
  int ret;
2573
2772
  char *buffer;
@@ -2579,20 +2778,16 @@ pgconn_get_copy_data(int argc, VALUE *argv, VALUE self )
2579
2778
 
2580
2779
  if( NIL_P(decoder) ){
2581
2780
  if( !NIL_P(this->decoder_for_get_copy_data) ){
2582
- p_coder = DATA_PTR( this->decoder_for_get_copy_data );
2781
+ p_coder = RTYPEDDATA_DATA( this->decoder_for_get_copy_data );
2583
2782
  }
2584
- } else if( rb_obj_is_kind_of(decoder, rb_cPG_Coder) ) {
2585
- Data_Get_Struct( decoder, t_pg_coder, p_coder );
2586
2783
  } else {
2587
- rb_raise( rb_eTypeError, "wrong decoder type %s (expected some kind of PG::Coder)",
2588
- rb_obj_classname( decoder ) );
2784
+ /* Check argument type and use argument decoder */
2785
+ TypedData_Get_Struct(decoder, t_pg_coder, &pg_coder_type, p_coder);
2589
2786
  }
2590
2787
 
2591
2788
  ret = gvl_PQgetCopyData(this->pgconn, &buffer, RTEST(async_in));
2592
- if(ret == -2) { /* error */
2593
- error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
2594
- rb_iv_set(error, "@connection", self);
2595
- rb_exc_raise(error);
2789
+ if(ret == -2){ /* error */
2790
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(this->pgconn));
2596
2791
  }
2597
2792
  if(ret == -1) { /* No data left */
2598
2793
  return Qnil;
@@ -2637,7 +2832,6 @@ pgconn_set_error_verbosity(VALUE self, VALUE in_verbosity)
2637
2832
  return INT2FIX(PQsetErrorVerbosity(conn, verbosity));
2638
2833
  }
2639
2834
 
2640
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
2641
2835
  /*
2642
2836
  * call-seq:
2643
2837
  * conn.set_error_context_visibility( context_visibility ) -> Integer
@@ -2657,7 +2851,6 @@ pgconn_set_error_verbosity(VALUE self, VALUE in_verbosity)
2657
2851
  *
2658
2852
  * See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-control.html#LIBPQ-PQSETERRORCONTEXTVISIBILITY].
2659
2853
  *
2660
- * Available since PostgreSQL-9.6
2661
2854
  */
2662
2855
  static VALUE
2663
2856
  pgconn_set_error_context_visibility(VALUE self, VALUE in_context_visibility)
@@ -2666,7 +2859,6 @@ pgconn_set_error_context_visibility(VALUE self, VALUE in_context_visibility)
2666
2859
  PGContextVisibility context_visibility = NUM2INT(in_context_visibility);
2667
2860
  return INT2FIX(PQsetErrorContextVisibility(conn, context_visibility));
2668
2861
  }
2669
- #endif
2670
2862
 
2671
2863
  /*
2672
2864
  * call-seq:
@@ -2686,6 +2878,7 @@ pgconn_trace(VALUE self, VALUE stream)
2686
2878
  VALUE new_file;
2687
2879
  t_pg_connection *this = pg_get_connection_safe( self );
2688
2880
 
2881
+ rb_check_frozen(self);
2689
2882
  if(!rb_respond_to(stream,rb_intern("fileno")))
2690
2883
  rb_raise(rb_eArgError, "stream does not respond to method: fileno");
2691
2884
 
@@ -2707,7 +2900,7 @@ pgconn_trace(VALUE self, VALUE stream)
2707
2900
  rb_raise(rb_eArgError, "stream is not writable");
2708
2901
 
2709
2902
  new_file = rb_funcall(rb_cIO, rb_intern("new"), 1, INT2NUM(new_fd));
2710
- this->trace_stream = new_file;
2903
+ RB_OBJ_WRITE(self, &this->trace_stream, new_file);
2711
2904
 
2712
2905
  PQtrace(this->pgconn, new_fp);
2713
2906
  return Qnil;
@@ -2726,7 +2919,7 @@ pgconn_untrace(VALUE self)
2726
2919
 
2727
2920
  PQuntrace(this->pgconn);
2728
2921
  rb_funcall(this->trace_stream, rb_intern("close"), 0);
2729
- this->trace_stream = Qnil;
2922
+ RB_OBJ_WRITE(self, &this->trace_stream, Qnil);
2730
2923
  return Qnil;
2731
2924
  }
2732
2925
 
@@ -2785,13 +2978,14 @@ pgconn_set_notice_receiver(VALUE self)
2785
2978
  VALUE proc, old_proc;
2786
2979
  t_pg_connection *this = pg_get_connection_safe( self );
2787
2980
 
2981
+ rb_check_frozen(self);
2788
2982
  /* If default_notice_receiver is unset, assume that the current
2789
2983
  * notice receiver is the default, and save it to a global variable.
2790
2984
  * This should not be a problem because the default receiver is
2791
2985
  * always the same, so won't vary among connections.
2792
2986
  */
2793
- if(default_notice_receiver == NULL)
2794
- default_notice_receiver = PQsetNoticeReceiver(this->pgconn, NULL, NULL);
2987
+ if(this->default_notice_receiver == NULL)
2988
+ this->default_notice_receiver = PQsetNoticeReceiver(this->pgconn, NULL, NULL);
2795
2989
 
2796
2990
  old_proc = this->notice_receiver;
2797
2991
  if( rb_block_given_p() ) {
@@ -2800,10 +2994,10 @@ pgconn_set_notice_receiver(VALUE self)
2800
2994
  } else {
2801
2995
  /* if no block is given, set back to default */
2802
2996
  proc = Qnil;
2803
- PQsetNoticeReceiver(this->pgconn, default_notice_receiver, NULL);
2997
+ PQsetNoticeReceiver(this->pgconn, this->default_notice_receiver, NULL);
2804
2998
  }
2805
2999
 
2806
- this->notice_receiver = proc;
3000
+ RB_OBJ_WRITE(self, &this->notice_receiver, proc);
2807
3001
  return old_proc;
2808
3002
  }
2809
3003
 
@@ -2818,10 +3012,10 @@ notice_processor_proxy(void *arg, const char *message)
2818
3012
  VALUE self = (VALUE)arg;
2819
3013
  t_pg_connection *this = pg_get_connection( self );
2820
3014
 
2821
- if (this->notice_receiver != Qnil) {
3015
+ if (this->notice_processor != Qnil) {
2822
3016
  VALUE message_str = rb_str_new2(message);
2823
3017
  PG_ENCODING_SET_NOCHECK( message_str, this->enc_idx );
2824
- rb_funcall(this->notice_receiver, rb_intern("call"), 1, message_str);
3018
+ rb_funcall(this->notice_processor, rb_intern("call"), 1, message_str);
2825
3019
  }
2826
3020
  return;
2827
3021
  }
@@ -2830,7 +3024,7 @@ notice_processor_proxy(void *arg, const char *message)
2830
3024
  * call-seq:
2831
3025
  * conn.set_notice_processor {|message| ... } -> Proc
2832
3026
  *
2833
- * See #set_notice_receiver for the desription of what this and the
3027
+ * See #set_notice_receiver for the description of what this and the
2834
3028
  * notice_processor methods do.
2835
3029
  *
2836
3030
  * This function takes a new block to act as the notice processor and returns
@@ -2845,25 +3039,26 @@ pgconn_set_notice_processor(VALUE self)
2845
3039
  VALUE proc, old_proc;
2846
3040
  t_pg_connection *this = pg_get_connection_safe( self );
2847
3041
 
3042
+ rb_check_frozen(self);
2848
3043
  /* If default_notice_processor is unset, assume that the current
2849
3044
  * notice processor is the default, and save it to a global variable.
2850
3045
  * This should not be a problem because the default processor is
2851
3046
  * always the same, so won't vary among connections.
2852
3047
  */
2853
- if(default_notice_processor == NULL)
2854
- default_notice_processor = PQsetNoticeProcessor(this->pgconn, NULL, NULL);
3048
+ if(this->default_notice_processor == NULL)
3049
+ this->default_notice_processor = PQsetNoticeProcessor(this->pgconn, NULL, NULL);
2855
3050
 
2856
- old_proc = this->notice_receiver;
3051
+ old_proc = this->notice_processor;
2857
3052
  if( rb_block_given_p() ) {
2858
3053
  proc = rb_block_proc();
2859
3054
  PQsetNoticeProcessor(this->pgconn, gvl_notice_processor_proxy, (void *)self);
2860
3055
  } else {
2861
3056
  /* if no block is given, set back to default */
2862
3057
  proc = Qnil;
2863
- PQsetNoticeProcessor(this->pgconn, default_notice_processor, NULL);
3058
+ PQsetNoticeProcessor(this->pgconn, this->default_notice_processor, NULL);
2864
3059
  }
2865
3060
 
2866
- this->notice_receiver = proc;
3061
+ RB_OBJ_WRITE(self, &this->notice_processor, proc);
2867
3062
  return old_proc;
2868
3063
  }
2869
3064
 
@@ -2884,68 +3079,28 @@ pgconn_get_client_encoding(VALUE self)
2884
3079
 
2885
3080
  /*
2886
3081
  * call-seq:
2887
- * conn.set_client_encoding( encoding )
3082
+ * conn.sync_set_client_encoding( encoding )
2888
3083
  *
2889
- * Sets the client encoding to the _encoding_ String.
3084
+ * This function has the same behavior as #async_set_client_encoding, but is implemented using the synchronous command processing API of libpq.
3085
+ * See #async_exec for the differences between the two API variants.
3086
+ * It's not recommended to use explicit sync or async variants but #set_client_encoding instead, unless you have a good reason to do so.
2890
3087
  */
2891
3088
  static VALUE
2892
- pgconn_set_client_encoding(VALUE self, VALUE str)
3089
+ pgconn_sync_set_client_encoding(VALUE self, VALUE str)
2893
3090
  {
2894
3091
  PGconn *conn = pg_get_pgconn( self );
2895
3092
 
3093
+ rb_check_frozen(self);
2896
3094
  Check_Type(str, T_STRING);
2897
3095
 
2898
- if ( (gvl_PQsetClientEncoding(conn, StringValueCStr(str))) == -1 ) {
2899
- rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
2900
- }
3096
+ if ( (gvl_PQsetClientEncoding(conn, StringValueCStr(str))) == -1 )
3097
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3098
+
2901
3099
  pgconn_set_internal_encoding_index( self );
2902
3100
 
2903
3101
  return Qnil;
2904
3102
  }
2905
3103
 
2906
- /*
2907
- * call-seq:
2908
- * conn.transaction { |conn| ... } -> result of the block
2909
- *
2910
- * Executes a +BEGIN+ at the start of the block,
2911
- * and a +COMMIT+ at the end of the block, or
2912
- * +ROLLBACK+ if any exception occurs.
2913
- */
2914
- static VALUE
2915
- pgconn_transaction(VALUE self)
2916
- {
2917
- PGconn *conn = pg_get_pgconn(self);
2918
- PGresult *result;
2919
- VALUE rb_pgresult;
2920
- VALUE block_result = Qnil;
2921
- int status;
2922
-
2923
- if (rb_block_given_p()) {
2924
- result = gvl_PQexec(conn, "BEGIN");
2925
- rb_pgresult = pg_new_result(result, self);
2926
- pg_result_check(rb_pgresult);
2927
- block_result = rb_protect(rb_yield, self, &status);
2928
- if(status == 0) {
2929
- result = gvl_PQexec(conn, "COMMIT");
2930
- rb_pgresult = pg_new_result(result, self);
2931
- pg_result_check(rb_pgresult);
2932
- }
2933
- else {
2934
- /* exception occurred, ROLLBACK and re-raise */
2935
- result = gvl_PQexec(conn, "ROLLBACK");
2936
- rb_pgresult = pg_new_result(result, self);
2937
- pg_result_check(rb_pgresult);
2938
- rb_jump_tag(status);
2939
- }
2940
-
2941
- }
2942
- else {
2943
- /* no block supplied? */
2944
- rb_raise(rb_eArgError, "Must supply block for PG::Connection#transaction");
2945
- }
2946
- return block_result;
2947
- }
2948
-
2949
3104
 
2950
3105
  /*
2951
3106
  * call-seq:
@@ -3020,10 +3175,8 @@ get_result_readable(PGconn *conn)
3020
3175
  * If +true+ is returned, +conn.is_busy+ will return +false+
3021
3176
  * and +conn.get_result+ will not block.
3022
3177
  */
3023
- static VALUE
3178
+ VALUE
3024
3179
  pgconn_block( int argc, VALUE *argv, VALUE self ) {
3025
- PGconn *conn = pg_get_pgconn( self );
3026
-
3027
3180
  struct timeval timeout;
3028
3181
  struct timeval *ptimeout = NULL;
3029
3182
  VALUE timeout_in;
@@ -3037,7 +3190,7 @@ pgconn_block( int argc, VALUE *argv, VALUE self ) {
3037
3190
  ptimeout = &timeout;
3038
3191
  }
3039
3192
 
3040
- ret = wait_socket_readable( conn, ptimeout, get_result_readable);
3193
+ ret = wait_socket_readable( self, ptimeout, get_result_readable);
3041
3194
 
3042
3195
  if( !ret )
3043
3196
  return Qfalse;
@@ -3046,6 +3199,42 @@ pgconn_block( int argc, VALUE *argv, VALUE self ) {
3046
3199
  }
3047
3200
 
3048
3201
 
3202
+ /*
3203
+ * call-seq:
3204
+ * conn.sync_get_last_result( ) -> PG::Result
3205
+ *
3206
+ * This function has the same behavior as #async_get_last_result, but is implemented using the synchronous command processing API of libpq.
3207
+ * See #async_exec for the differences between the two API variants.
3208
+ * It's not recommended to use explicit sync or async variants but #get_last_result instead, unless you have a good reason to do so.
3209
+ */
3210
+ static VALUE
3211
+ pgconn_sync_get_last_result(VALUE self)
3212
+ {
3213
+ PGconn *conn = pg_get_pgconn(self);
3214
+ VALUE rb_pgresult = Qnil;
3215
+ PGresult *cur, *prev;
3216
+
3217
+
3218
+ cur = prev = NULL;
3219
+ while ((cur = gvl_PQgetResult(conn)) != NULL) {
3220
+ int status;
3221
+
3222
+ if (prev) PQclear(prev);
3223
+ prev = cur;
3224
+
3225
+ status = PQresultStatus(cur);
3226
+ if (status == PGRES_COPY_OUT || status == PGRES_COPY_IN || status == PGRES_COPY_BOTH)
3227
+ break;
3228
+ }
3229
+
3230
+ if (prev) {
3231
+ rb_pgresult = pg_new_result( prev, self );
3232
+ pg_result_check(rb_pgresult);
3233
+ }
3234
+
3235
+ return rb_pgresult;
3236
+ }
3237
+
3049
3238
  /*
3050
3239
  * call-seq:
3051
3240
  * conn.get_last_result( ) -> PG::Result
@@ -3056,27 +3245,38 @@ pgconn_block( int argc, VALUE *argv, VALUE self ) {
3056
3245
  * returns the last non-NULL result, or +nil+ if no
3057
3246
  * results are available.
3058
3247
  *
3248
+ * If the last result contains a bad result_status, an
3249
+ * appropriate exception is raised.
3250
+ *
3059
3251
  * This function is similar to #get_result
3060
3252
  * except that it is designed to get one and only
3061
- * one result.
3253
+ * one result and that it checks the result state.
3062
3254
  */
3063
3255
  static VALUE
3064
- pgconn_get_last_result(VALUE self)
3256
+ pgconn_async_get_last_result(VALUE self)
3065
3257
  {
3066
3258
  PGconn *conn = pg_get_pgconn(self);
3067
3259
  VALUE rb_pgresult = Qnil;
3068
3260
  PGresult *cur, *prev;
3069
3261
 
3070
-
3071
3262
  cur = prev = NULL;
3072
- while ((cur = gvl_PQgetResult(conn)) != NULL) {
3263
+ for(;;) {
3073
3264
  int status;
3074
3265
 
3266
+ /* Wait for input before reading each result.
3267
+ * That way we support the ruby-3.x IO scheduler and don't block other ruby threads.
3268
+ */
3269
+ wait_socket_readable(self, NULL, get_result_readable);
3270
+
3271
+ cur = gvl_PQgetResult(conn);
3272
+ if (cur == NULL)
3273
+ break;
3274
+
3075
3275
  if (prev) PQclear(prev);
3076
3276
  prev = cur;
3077
3277
 
3078
3278
  status = PQresultStatus(cur);
3079
- if (status == PGRES_COPY_OUT || status == PGRES_COPY_IN)
3279
+ if (status == PGRES_COPY_OUT || status == PGRES_COPY_IN || status == PGRES_COPY_BOTH)
3080
3280
  break;
3081
3281
  }
3082
3282
 
@@ -3093,29 +3293,91 @@ pgconn_get_last_result(VALUE self)
3093
3293
  * conn.discard_results()
3094
3294
  *
3095
3295
  * Silently discard any prior query result that application didn't eat.
3096
- * This is done prior of Connection#exec and sibling methods and can
3097
- * be called explicitly when using the async API.
3296
+ * This is internally used prior to Connection#exec and sibling methods.
3297
+ * It doesn't raise an exception on connection errors, but returns +false+ instead.
3298
+ *
3299
+ * Returns:
3300
+ * * +nil+ when the connection is already idle
3301
+ * * +true+ when some results have been discarded
3302
+ * * +false+ when a failure occurred and the connection was closed
3303
+ *
3098
3304
  */
3099
3305
  static VALUE
3100
3306
  pgconn_discard_results(VALUE self)
3101
3307
  {
3102
3308
  PGconn *conn = pg_get_pgconn(self);
3309
+ VALUE socket_io;
3310
+
3311
+ switch( PQtransactionStatus(conn) ) {
3312
+ case PQTRANS_IDLE:
3313
+ case PQTRANS_INTRANS:
3314
+ case PQTRANS_INERROR:
3315
+ return Qnil;
3316
+ default:;
3317
+ }
3103
3318
 
3104
- PGresult *cur;
3105
- while ((cur = gvl_PQgetResult(conn)) != NULL) {
3106
- int status = PQresultStatus(cur);
3319
+ socket_io = pgconn_socket_io(self);
3320
+
3321
+ for(;;) {
3322
+ PGresult *cur;
3323
+ int status;
3324
+
3325
+ /* pgconn_block() raises an exception in case of errors.
3326
+ * To avoid this call pg_rb_io_wait() and PQconsumeInput() without rb_raise().
3327
+ */
3328
+ while( gvl_PQisBusy(conn) ){
3329
+ int events;
3330
+
3331
+ switch( PQflush(conn) ) {
3332
+ case 1:
3333
+ events = RB_NUM2INT(pg_rb_io_wait(socket_io, RB_INT2NUM(PG_RUBY_IO_READABLE | PG_RUBY_IO_WRITABLE), Qnil));
3334
+ if (events & PG_RUBY_IO_READABLE){
3335
+ if ( PQconsumeInput(conn) == 0 ) goto error;
3336
+ }
3337
+ break;
3338
+ case 0:
3339
+ pg_rb_io_wait(socket_io, RB_INT2NUM(PG_RUBY_IO_READABLE), Qnil);
3340
+ if ( PQconsumeInput(conn) == 0 ) goto error;
3341
+ break;
3342
+ default:
3343
+ goto error;
3344
+ }
3345
+ }
3346
+
3347
+ cur = gvl_PQgetResult(conn);
3348
+ if( cur == NULL) break;
3349
+
3350
+ status = PQresultStatus(cur);
3107
3351
  PQclear(cur);
3108
3352
  if (status == PGRES_COPY_IN){
3109
- gvl_PQputCopyEnd(conn, "COPY terminated by new PQexec");
3353
+ while( gvl_PQputCopyEnd(conn, "COPY terminated by new query or discard_results") == 0 ){
3354
+ pgconn_async_flush(self);
3355
+ }
3110
3356
  }
3111
3357
  if (status == PGRES_COPY_OUT){
3112
- char *buffer = NULL;
3113
- while( gvl_PQgetCopyData(conn, &buffer, 0) > 0)
3114
- PQfreemem(buffer);
3358
+ for(;;) {
3359
+ char *buffer = NULL;
3360
+ int st = gvl_PQgetCopyData(conn, &buffer, 1);
3361
+ if( st == 0 ) {
3362
+ /* would block -> wait for readable data */
3363
+ pg_rb_io_wait(socket_io, RB_INT2NUM(PG_RUBY_IO_READABLE), Qnil);
3364
+ if ( PQconsumeInput(conn) == 0 ) goto error;
3365
+ } else if( st > 0 ) {
3366
+ /* some data retrieved -> discard it */
3367
+ PQfreemem(buffer);
3368
+ } else {
3369
+ /* no more data */
3370
+ break;
3371
+ }
3372
+ }
3115
3373
  }
3116
3374
  }
3117
3375
 
3118
- return Qnil;
3376
+ return Qtrue;
3377
+
3378
+ error:
3379
+ pgconn_close_socket_io(self);
3380
+ return Qfalse;
3119
3381
  }
3120
3382
 
3121
3383
  /*
@@ -3138,6 +3400,7 @@ pgconn_discard_results(VALUE self)
3138
3400
  * #exec is an alias for #async_exec which is almost identical to #sync_exec .
3139
3401
  * #sync_exec is implemented on the simpler synchronous command processing API of libpq, whereas
3140
3402
  * #async_exec is implemented on the asynchronous API and on ruby's IO mechanisms.
3403
+ * Only #async_exec is compatible to <tt>Fiber.scheduler</tt> based asynchronous IO processing introduced in ruby-3.0.
3141
3404
  * Both methods ensure that other threads can process while waiting for the server to
3142
3405
  * complete the request, but #sync_exec blocks all signals to be processed until the query is finished.
3143
3406
  * This is most notably visible by a delayed reaction to Control+C.
@@ -3152,8 +3415,7 @@ pgconn_async_exec(int argc, VALUE *argv, VALUE self)
3152
3415
 
3153
3416
  pgconn_discard_results( self );
3154
3417
  pgconn_send_query( argc, argv, self );
3155
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3156
- rb_pgresult = pgconn_get_last_result( self );
3418
+ rb_pgresult = pgconn_async_get_last_result( self );
3157
3419
 
3158
3420
  if ( rb_block_given_p() ) {
3159
3421
  return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
@@ -3182,7 +3444,7 @@ pgconn_async_exec(int argc, VALUE *argv, VALUE self)
3182
3444
  * or, it may be a String. If it is a string, that is equivalent to the hash:
3183
3445
  * { :value => <string value>, :type => 0, :format => 0 }
3184
3446
  *
3185
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
3447
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
3186
3448
  * inside the SQL query. The 0th element of the +params+ array is bound
3187
3449
  * to $1, the 1st element is bound to $2, etc. +nil+ is treated as +NULL+.
3188
3450
  *
@@ -3225,8 +3487,7 @@ pgconn_async_exec_params(int argc, VALUE *argv, VALUE self)
3225
3487
  } else {
3226
3488
  pgconn_send_query_params( argc, argv, self );
3227
3489
  }
3228
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3229
- rb_pgresult = pgconn_get_last_result( self );
3490
+ rb_pgresult = pgconn_async_get_last_result( self );
3230
3491
 
3231
3492
  if ( rb_block_given_p() ) {
3232
3493
  return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
@@ -3252,7 +3513,7 @@ pgconn_async_exec_params(int argc, VALUE *argv, VALUE self)
3252
3513
  *
3253
3514
  * For example: "SELECT $1::int"
3254
3515
  *
3255
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
3516
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
3256
3517
  * inside the SQL query.
3257
3518
  *
3258
3519
  * See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-exec.html#LIBPQ-PQPREPARE].
@@ -3264,8 +3525,7 @@ pgconn_async_prepare(int argc, VALUE *argv, VALUE self)
3264
3525
 
3265
3526
  pgconn_discard_results( self );
3266
3527
  pgconn_send_prepare( argc, argv, self );
3267
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3268
- rb_pgresult = pgconn_get_last_result( self );
3528
+ rb_pgresult = pgconn_async_get_last_result( self );
3269
3529
 
3270
3530
  if ( rb_block_given_p() ) {
3271
3531
  return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
@@ -3292,7 +3552,7 @@ pgconn_async_prepare(int argc, VALUE *argv, VALUE self)
3292
3552
  * or, it may be a String. If it is a string, that is equivalent to the hash:
3293
3553
  * { :value => <string value>, :format => 0 }
3294
3554
  *
3295
- * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
3555
+ * PostgreSQL bind parameters are represented as $1, $2, $3, etc.,
3296
3556
  * inside the SQL query. The 0th element of the +params+ array is bound
3297
3557
  * to $1, the 1st element is bound to $2, etc. +nil+ is treated as +NULL+.
3298
3558
  *
@@ -3318,8 +3578,7 @@ pgconn_async_exec_prepared(int argc, VALUE *argv, VALUE self)
3318
3578
 
3319
3579
  pgconn_discard_results( self );
3320
3580
  pgconn_send_query_prepared( argc, argv, self );
3321
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3322
- rb_pgresult = pgconn_get_last_result( self );
3581
+ rb_pgresult = pgconn_async_get_last_result( self );
3323
3582
 
3324
3583
  if ( rb_block_given_p() ) {
3325
3584
  return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
@@ -3327,6 +3586,21 @@ pgconn_async_exec_prepared(int argc, VALUE *argv, VALUE self)
3327
3586
  return rb_pgresult;
3328
3587
  }
3329
3588
 
3589
+ static VALUE
3590
+ pgconn_async_describe_close_prepared_potral(VALUE self, VALUE name, VALUE
3591
+ (*func)(VALUE, VALUE))
3592
+ {
3593
+ VALUE rb_pgresult = Qnil;
3594
+
3595
+ pgconn_discard_results( self );
3596
+ func( self, name );
3597
+ rb_pgresult = pgconn_async_get_last_result( self );
3598
+
3599
+ if ( rb_block_given_p() ) {
3600
+ return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
3601
+ }
3602
+ return rb_pgresult;
3603
+ }
3330
3604
 
3331
3605
  /*
3332
3606
  * call-seq:
@@ -3339,17 +3613,7 @@ pgconn_async_exec_prepared(int argc, VALUE *argv, VALUE self)
3339
3613
  static VALUE
3340
3614
  pgconn_async_describe_portal(VALUE self, VALUE portal)
3341
3615
  {
3342
- VALUE rb_pgresult = Qnil;
3343
-
3344
- pgconn_discard_results( self );
3345
- pgconn_send_describe_portal( self, portal );
3346
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3347
- rb_pgresult = pgconn_get_last_result( self );
3348
-
3349
- if ( rb_block_given_p() ) {
3350
- return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
3351
- }
3352
- return rb_pgresult;
3616
+ return pgconn_async_describe_close_prepared_potral(self, portal, pgconn_send_describe_portal);
3353
3617
  }
3354
3618
 
3355
3619
 
@@ -3364,28 +3628,64 @@ pgconn_async_describe_portal(VALUE self, VALUE portal)
3364
3628
  static VALUE
3365
3629
  pgconn_async_describe_prepared(VALUE self, VALUE stmt_name)
3366
3630
  {
3367
- VALUE rb_pgresult = Qnil;
3368
-
3369
- pgconn_discard_results( self );
3370
- pgconn_send_describe_prepared( self, stmt_name );
3371
- pgconn_block( 0, NULL, self ); /* wait for input (without blocking) before reading the last result */
3372
- rb_pgresult = pgconn_get_last_result( self );
3631
+ return pgconn_async_describe_close_prepared_potral(self, stmt_name, pgconn_send_describe_prepared);
3632
+ }
3373
3633
 
3374
- if ( rb_block_given_p() ) {
3375
- return rb_ensure( rb_yield, rb_pgresult, pg_result_clear, rb_pgresult );
3376
- }
3377
- return rb_pgresult;
3634
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
3635
+ /*
3636
+ * call-seq:
3637
+ * conn.close_prepared( statement_name ) -> PG::Result
3638
+ *
3639
+ * Submits a request to close the specified prepared statement, and waits for completion.
3640
+ * close_prepared allows an application to close a previously prepared statement.
3641
+ * Closing a statement releases all of its associated resources on the server and allows its name to be reused.
3642
+ * It's the same as using the +DEALLOCATE+ SQL statement, but on a lower protocol level.
3643
+ *
3644
+ * +statement_name+ can be "" or +nil+ to reference the unnamed statement.
3645
+ * It is fine if no statement exists with this name, in that case the operation is a no-op.
3646
+ * On success, a PG::Result with status PGRES_COMMAND_OK is returned.
3647
+ *
3648
+ * See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-exec.html#LIBPQ-PQCLOSEPREPARED].
3649
+ *
3650
+ * Available since PostgreSQL-17.
3651
+ */
3652
+ static VALUE
3653
+ pgconn_async_close_prepared(VALUE self, VALUE stmt_name)
3654
+ {
3655
+ return pgconn_async_describe_close_prepared_potral(self, stmt_name, pgconn_send_close_prepared);
3378
3656
  }
3379
3657
 
3658
+ /*
3659
+ * call-seq:
3660
+ * conn.close_portal( portal_name ) -> PG::Result
3661
+ *
3662
+ * Submits a request to close the specified portal, and waits for completion.
3663
+ *
3664
+ * close_portal allows an application to trigger a close of a previously created portal.
3665
+ * Closing a portal releases all of its associated resources on the server and allows its name to be reused.
3666
+ * (pg does not provide any direct access to portals, but you can use this function to close a cursor created with a DECLARE CURSOR SQL command.)
3667
+ *
3668
+ * +portal_name+ can be "" or +nil+ to reference the unnamed portal.
3669
+ * It is fine if no portal exists with this name, in that case the operation is a no-op.
3670
+ * On success, a PG::Result with status PGRES_COMMAND_OK is returned.
3671
+ *
3672
+ * See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-exec.html#LIBPQ-PQCLOSEPORTAL].
3673
+ *
3674
+ * Available since PostgreSQL-17.
3675
+ */
3676
+ static VALUE
3677
+ pgconn_async_close_portal(VALUE self, VALUE portal)
3678
+ {
3679
+ return pgconn_async_describe_close_prepared_potral(self, portal, pgconn_send_close_portal);
3680
+ }
3681
+ #endif
3380
3682
 
3381
- #ifdef HAVE_PQSSLATTRIBUTE
3382
3683
  /*
3383
3684
  * call-seq:
3384
3685
  * conn.ssl_in_use? -> Boolean
3385
3686
  *
3386
3687
  * Returns +true+ if the connection uses SSL/TLS, +false+ if not.
3387
3688
  *
3388
- * Available since PostgreSQL-9.5
3389
3689
  */
3390
3690
  static VALUE
3391
3691
  pgconn_ssl_in_use(VALUE self)
@@ -3419,7 +3719,6 @@ pgconn_ssl_in_use(VALUE self)
3419
3719
  *
3420
3720
  * See also #ssl_attribute_names and the {corresponding libpq function}[https://www.postgresql.org/docs/current/libpq-status.html#LIBPQ-PQSSLATTRIBUTE].
3421
3721
  *
3422
- * Available since PostgreSQL-9.5
3423
3722
  */
3424
3723
  static VALUE
3425
3724
  pgconn_ssl_attribute(VALUE self, VALUE attribute_name)
@@ -3438,7 +3737,6 @@ pgconn_ssl_attribute(VALUE self, VALUE attribute_name)
3438
3737
  *
3439
3738
  * See also #ssl_attribute
3440
3739
  *
3441
- * Available since PostgreSQL-9.5
3442
3740
  */
3443
3741
  static VALUE
3444
3742
  pgconn_ssl_attribute_names(VALUE self)
@@ -3454,13 +3752,163 @@ pgconn_ssl_attribute_names(VALUE self)
3454
3752
  }
3455
3753
 
3456
3754
 
3755
+
3756
+ #ifdef HAVE_PQENTERPIPELINEMODE
3757
+ /*
3758
+ * call-seq:
3759
+ * conn.pipeline_status -> Integer
3760
+ *
3761
+ * Returns the current pipeline mode status of the libpq connection.
3762
+ *
3763
+ * PQpipelineStatus can return one of the following values:
3764
+ *
3765
+ * * PQ_PIPELINE_ON - The libpq connection is in pipeline mode.
3766
+ * * PQ_PIPELINE_OFF - The libpq connection is not in pipeline mode.
3767
+ * * PQ_PIPELINE_ABORTED - The libpq connection is in pipeline mode and an error occurred while processing the current pipeline.
3768
+ * The aborted flag is cleared when PQgetResult returns a result of type PGRES_PIPELINE_SYNC.
3769
+ *
3770
+ * Available since PostgreSQL-14
3771
+ */
3772
+ static VALUE
3773
+ pgconn_pipeline_status(VALUE self)
3774
+ {
3775
+ int res = PQpipelineStatus(pg_get_pgconn(self));
3776
+ return INT2FIX(res);
3777
+ }
3778
+
3779
+
3780
+ /*
3781
+ * call-seq:
3782
+ * conn.enter_pipeline_mode -> nil
3783
+ *
3784
+ * Causes a connection to enter pipeline mode if it is currently idle or already in pipeline mode.
3785
+ *
3786
+ * Raises PG::Error and has no effect if the connection is not currently idle, i.e., it has a result ready, or it is waiting for more input from the server, etc.
3787
+ * This function does not actually send anything to the server, it just changes the libpq connection state.
3788
+ *
3789
+ * See the {PostgreSQL documentation}[https://www.postgresql.org/docs/17/libpq-pipeline-mode.html#LIBPQ-PIPELINE-MODE].
3790
+ *
3791
+ * Available since PostgreSQL-14
3792
+ */
3793
+ static VALUE
3794
+ pgconn_enter_pipeline_mode(VALUE self)
3795
+ {
3796
+ PGconn *conn = pg_get_pgconn(self);
3797
+ int res = PQenterPipelineMode(conn);
3798
+ if( res != 1 )
3799
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3800
+
3801
+ return Qnil;
3802
+ }
3803
+
3804
+ /*
3805
+ * call-seq:
3806
+ * conn.exit_pipeline_mode -> nil
3807
+ *
3808
+ * Causes a connection to exit pipeline mode if it is currently in pipeline mode with an empty queue and no pending results.
3809
+ *
3810
+ * Takes no action if not in pipeline mode.
3811
+ * Raises PG::Error if the current statement isn't finished processing, or PQgetResult has not been called to collect results from all previously sent query.
3812
+ *
3813
+ * Available since PostgreSQL-14
3814
+ */
3815
+ static VALUE
3816
+ pgconn_exit_pipeline_mode(VALUE self)
3817
+ {
3818
+ PGconn *conn = pg_get_pgconn(self);
3819
+ int res = PQexitPipelineMode(conn);
3820
+ if( res != 1 )
3821
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3822
+
3823
+ return Qnil;
3824
+ }
3825
+
3826
+
3827
+ /*
3828
+ * call-seq:
3829
+ * conn.sync_pipeline_sync -> nil
3830
+ *
3831
+ * This function has the same behavior as #async_pipeline_sync, but is implemented using the synchronous command processing API of libpq.
3832
+ * See #async_exec for the differences between the two API variants.
3833
+ * It's not recommended to use explicit sync or async variants but #pipeline_sync instead, unless you have a good reason to do so.
3834
+ *
3835
+ * Available since PostgreSQL-14
3836
+ */
3837
+ static VALUE
3838
+ pgconn_sync_pipeline_sync(VALUE self)
3839
+ {
3840
+ PGconn *conn = pg_get_pgconn(self);
3841
+ int res = gvl_PQpipelineSync(conn);
3842
+ if( res != 1 )
3843
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3844
+
3845
+ return Qnil;
3846
+ }
3847
+
3848
+
3849
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
3850
+ /*
3851
+ * call-seq:
3852
+ * conn.send_pipeline_sync -> nil
3853
+ *
3854
+ * Marks a synchronization point in a pipeline by sending a sync message without flushing the send buffer.
3855
+ *
3856
+ * This serves as the delimiter of an implicit transaction and an error recovery point.
3857
+ * Raises PG::Error if the connection is not in pipeline mode or sending a sync message failed.
3858
+ * Note that the message is not itself flushed to the server automatically; use flush if necessary.
3859
+ *
3860
+ * Available since PostgreSQL-17
3861
+ */
3862
+ static VALUE
3863
+ pgconn_send_pipeline_sync(VALUE self)
3864
+ {
3865
+ PGconn *conn = pg_get_pgconn(self);
3866
+ int res = gvl_PQsendPipelineSync(conn);
3867
+ if( res != 1 )
3868
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3869
+
3870
+ return Qnil;
3871
+ }
3457
3872
  #endif
3458
3873
 
3459
3874
 
3875
+ /*
3876
+ * call-seq:
3877
+ * conn.send_flush_request -> nil
3878
+ *
3879
+ * Sends a request for the server to flush its output buffer.
3880
+ *
3881
+ * The server flushes its output buffer automatically as a result of Connection#pipeline_sync being called, or on any request when not in pipeline mode.
3882
+ * This function is useful to cause the server to flush its output buffer in pipeline mode without establishing a synchronization point.
3883
+ * Note that the request is not itself flushed to the server automatically; use Connection#flush if necessary.
3884
+ *
3885
+ * Available since PostgreSQL-14
3886
+ */
3887
+ static VALUE
3888
+ pgconn_send_flush_request(VALUE self)
3889
+ {
3890
+ PGconn *conn = pg_get_pgconn(self);
3891
+ int res = PQsendFlushRequest(conn);
3892
+ if( res != 1 )
3893
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3894
+
3895
+ return Qnil;
3896
+ }
3897
+
3898
+ #endif
3899
+
3460
3900
  /**************************************************************************
3461
3901
  * LARGE OBJECT SUPPORT
3462
3902
  **************************************************************************/
3463
3903
 
3904
+ #define BLOCKING_BEGIN(conn) do { \
3905
+ int old_nonblocking = PQisnonblocking(conn); \
3906
+ PQsetnonblocking(conn, 0);
3907
+
3908
+ #define BLOCKING_END(th) \
3909
+ PQsetnonblocking(conn, old_nonblocking); \
3910
+ } while(0);
3911
+
3464
3912
  /*
3465
3913
  * call-seq:
3466
3914
  * conn.lo_creat( [mode] ) -> Integer
@@ -3481,9 +3929,12 @@ pgconn_locreat(int argc, VALUE *argv, VALUE self)
3481
3929
  else
3482
3930
  mode = NUM2INT(nmode);
3483
3931
 
3484
- lo_oid = lo_creat(conn, mode);
3932
+ BLOCKING_BEGIN(conn)
3933
+ lo_oid = lo_creat(conn, mode);
3934
+ BLOCKING_END(conn)
3935
+
3485
3936
  if (lo_oid == 0)
3486
- rb_raise(rb_ePGerror, "lo_creat failed");
3937
+ pg_raise_conn_error( rb_ePGerror, self, "lo_creat failed");
3487
3938
 
3488
3939
  return UINT2NUM(lo_oid);
3489
3940
  }
@@ -3504,7 +3955,7 @@ pgconn_locreate(VALUE self, VALUE in_lo_oid)
3504
3955
 
3505
3956
  ret = lo_create(conn, lo_oid);
3506
3957
  if (ret == InvalidOid)
3507
- rb_raise(rb_ePGerror, "lo_create failed");
3958
+ pg_raise_conn_error( rb_ePGerror, self, "lo_create failed");
3508
3959
 
3509
3960
  return UINT2NUM(ret);
3510
3961
  }
@@ -3526,9 +3977,12 @@ pgconn_loimport(VALUE self, VALUE filename)
3526
3977
 
3527
3978
  Check_Type(filename, T_STRING);
3528
3979
 
3529
- lo_oid = lo_import(conn, StringValueCStr(filename));
3980
+ BLOCKING_BEGIN(conn)
3981
+ lo_oid = lo_import(conn, StringValueCStr(filename));
3982
+ BLOCKING_END(conn)
3983
+
3530
3984
  if (lo_oid == 0) {
3531
- rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
3985
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3532
3986
  }
3533
3987
  return UINT2NUM(lo_oid);
3534
3988
  }
@@ -3544,12 +3998,17 @@ pgconn_loexport(VALUE self, VALUE lo_oid, VALUE filename)
3544
3998
  {
3545
3999
  PGconn *conn = pg_get_pgconn(self);
3546
4000
  Oid oid;
4001
+ int ret;
3547
4002
  Check_Type(filename, T_STRING);
3548
4003
 
3549
4004
  oid = NUM2UINT(lo_oid);
3550
4005
 
3551
- if (lo_export(conn, oid, StringValueCStr(filename)) < 0) {
3552
- rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
4006
+ BLOCKING_BEGIN(conn)
4007
+ ret = lo_export(conn, oid, StringValueCStr(filename));
4008
+ BLOCKING_END(conn)
4009
+
4010
+ if (ret < 0) {
4011
+ pg_raise_conn_error( rb_ePGerror, self, "%s", PQerrorMessage(conn));
3553
4012
  }
3554
4013
  return Qnil;
3555
4014
  }
@@ -3579,8 +4038,12 @@ pgconn_loopen(int argc, VALUE *argv, VALUE self)
3579
4038
  else
3580
4039
  mode = NUM2INT(nmode);
3581
4040
 
3582
- if((fd = lo_open(conn, lo_oid, mode)) < 0) {
3583
- rb_raise(rb_ePGerror, "can't open large object: %s", PQerrorMessage(conn));
4041
+ BLOCKING_BEGIN(conn)
4042
+ fd = lo_open(conn, lo_oid, mode);
4043
+ BLOCKING_END(conn)
4044
+
4045
+ if(fd < 0) {
4046
+ pg_raise_conn_error( rb_ePGerror, self, "can't open large object: %s", PQerrorMessage(conn));
3584
4047
  }
3585
4048
  return INT2FIX(fd);
3586
4049
  }
@@ -3602,11 +4065,15 @@ pgconn_lowrite(VALUE self, VALUE in_lo_desc, VALUE buffer)
3602
4065
  Check_Type(buffer, T_STRING);
3603
4066
 
3604
4067
  if( RSTRING_LEN(buffer) < 0) {
3605
- rb_raise(rb_ePGerror, "write buffer zero string");
4068
+ pg_raise_conn_error( rb_ePGerror, self, "write buffer zero string");
3606
4069
  }
3607
- if((n = lo_write(conn, fd, StringValuePtr(buffer),
3608
- RSTRING_LEN(buffer))) < 0) {
3609
- rb_raise(rb_ePGerror, "lo_write failed: %s", PQerrorMessage(conn));
4070
+ BLOCKING_BEGIN(conn)
4071
+ n = lo_write(conn, fd, StringValuePtr(buffer),
4072
+ RSTRING_LEN(buffer));
4073
+ BLOCKING_END(conn)
4074
+
4075
+ if(n < 0) {
4076
+ pg_raise_conn_error( rb_ePGerror, self, "lo_write failed: %s", PQerrorMessage(conn));
3610
4077
  }
3611
4078
 
3612
4079
  return INT2FIX(n);
@@ -3629,16 +4096,17 @@ pgconn_loread(VALUE self, VALUE in_lo_desc, VALUE in_len)
3629
4096
  VALUE str;
3630
4097
  char *buffer;
3631
4098
 
3632
- buffer = ALLOC_N(char, len);
3633
- if(buffer == NULL)
3634
- rb_raise(rb_eNoMemError, "ALLOC failed!");
4099
+ if (len < 0)
4100
+ pg_raise_conn_error( rb_ePGerror, self, "negative length %d given", len);
3635
4101
 
3636
- if (len < 0){
3637
- rb_raise(rb_ePGerror,"nagative length %d given", len);
3638
- }
4102
+ buffer = ALLOC_N(char, len);
3639
4103
 
3640
- if((ret = lo_read(conn, lo_desc, buffer, len)) < 0)
3641
- rb_raise(rb_ePGerror, "lo_read failed");
4104
+ BLOCKING_BEGIN(conn)
4105
+ ret = lo_read(conn, lo_desc, buffer, len);
4106
+ BLOCKING_END(conn)
4107
+
4108
+ if(ret < 0)
4109
+ pg_raise_conn_error( rb_ePGerror, self, "lo_read failed");
3642
4110
 
3643
4111
  if(ret == 0) {
3644
4112
  xfree(buffer);
@@ -3667,8 +4135,12 @@ pgconn_lolseek(VALUE self, VALUE in_lo_desc, VALUE offset, VALUE whence)
3667
4135
  int lo_desc = NUM2INT(in_lo_desc);
3668
4136
  int ret;
3669
4137
 
3670
- if((ret = lo_lseek(conn, lo_desc, NUM2INT(offset), NUM2INT(whence))) < 0) {
3671
- rb_raise(rb_ePGerror, "lo_lseek failed");
4138
+ BLOCKING_BEGIN(conn)
4139
+ ret = lo_lseek(conn, lo_desc, NUM2INT(offset), NUM2INT(whence));
4140
+ BLOCKING_END(conn)
4141
+
4142
+ if(ret < 0) {
4143
+ pg_raise_conn_error( rb_ePGerror, self, "lo_lseek failed");
3672
4144
  }
3673
4145
 
3674
4146
  return INT2FIX(ret);
@@ -3687,8 +4159,12 @@ pgconn_lotell(VALUE self, VALUE in_lo_desc)
3687
4159
  PGconn *conn = pg_get_pgconn(self);
3688
4160
  int lo_desc = NUM2INT(in_lo_desc);
3689
4161
 
3690
- if((position = lo_tell(conn, lo_desc)) < 0)
3691
- rb_raise(rb_ePGerror,"lo_tell failed");
4162
+ BLOCKING_BEGIN(conn)
4163
+ position = lo_tell(conn, lo_desc);
4164
+ BLOCKING_END(conn)
4165
+
4166
+ if(position < 0)
4167
+ pg_raise_conn_error( rb_ePGerror, self, "lo_tell failed");
3692
4168
 
3693
4169
  return INT2FIX(position);
3694
4170
  }
@@ -3705,9 +4181,14 @@ pgconn_lotruncate(VALUE self, VALUE in_lo_desc, VALUE in_len)
3705
4181
  PGconn *conn = pg_get_pgconn(self);
3706
4182
  int lo_desc = NUM2INT(in_lo_desc);
3707
4183
  size_t len = NUM2INT(in_len);
4184
+ int ret;
3708
4185
 
3709
- if(lo_truncate(conn,lo_desc,len) < 0)
3710
- rb_raise(rb_ePGerror,"lo_truncate failed");
4186
+ BLOCKING_BEGIN(conn)
4187
+ ret = lo_truncate(conn,lo_desc,len);
4188
+ BLOCKING_END(conn)
4189
+
4190
+ if(ret < 0)
4191
+ pg_raise_conn_error( rb_ePGerror, self, "lo_truncate failed");
3711
4192
 
3712
4193
  return Qnil;
3713
4194
  }
@@ -3723,9 +4204,14 @@ pgconn_loclose(VALUE self, VALUE in_lo_desc)
3723
4204
  {
3724
4205
  PGconn *conn = pg_get_pgconn(self);
3725
4206
  int lo_desc = NUM2INT(in_lo_desc);
4207
+ int ret;
3726
4208
 
3727
- if(lo_close(conn,lo_desc) < 0)
3728
- rb_raise(rb_ePGerror,"lo_close failed");
4209
+ BLOCKING_BEGIN(conn)
4210
+ ret = lo_close(conn,lo_desc);
4211
+ BLOCKING_END(conn)
4212
+
4213
+ if(ret < 0)
4214
+ pg_raise_conn_error( rb_ePGerror, self, "lo_close failed");
3729
4215
 
3730
4216
  return Qnil;
3731
4217
  }
@@ -3741,9 +4227,14 @@ pgconn_lounlink(VALUE self, VALUE in_oid)
3741
4227
  {
3742
4228
  PGconn *conn = pg_get_pgconn(self);
3743
4229
  Oid oid = NUM2UINT(in_oid);
4230
+ int ret;
3744
4231
 
3745
- if(lo_unlink(conn,oid) < 0)
3746
- rb_raise(rb_ePGerror,"lo_unlink failed");
4232
+ BLOCKING_BEGIN(conn)
4233
+ ret = lo_unlink(conn,oid);
4234
+ BLOCKING_END(conn)
4235
+
4236
+ if(ret < 0)
4237
+ pg_raise_conn_error( rb_ePGerror, self, "lo_unlink failed");
3747
4238
 
3748
4239
  return Qnil;
3749
4240
  }
@@ -3800,12 +4291,13 @@ static VALUE pgconn_external_encoding(VALUE self);
3800
4291
  static VALUE
3801
4292
  pgconn_internal_encoding_set(VALUE self, VALUE enc)
3802
4293
  {
4294
+ rb_check_frozen(self);
3803
4295
  if (NIL_P(enc)) {
3804
- pgconn_set_client_encoding( self, rb_usascii_str_new_cstr("SQL_ASCII") );
4296
+ pgconn_sync_set_client_encoding( self, rb_usascii_str_new_cstr("SQL_ASCII") );
3805
4297
  return enc;
3806
4298
  }
3807
4299
  else if ( TYPE(enc) == T_STRING && strcasecmp("JOHAB", StringValueCStr(enc)) == 0 ) {
3808
- pgconn_set_client_encoding(self, rb_usascii_str_new_cstr("JOHAB"));
4300
+ pgconn_sync_set_client_encoding(self, rb_usascii_str_new_cstr("JOHAB"));
3809
4301
  return enc;
3810
4302
  }
3811
4303
  else {
@@ -3843,16 +4335,34 @@ pgconn_external_encoding(VALUE self)
3843
4335
  return rb_enc_from_encoding( enc );
3844
4336
  }
3845
4337
 
4338
+ /*
4339
+ * call-seq:
4340
+ * conn.set_client_encoding( encoding )
4341
+ *
4342
+ * Sets the client encoding to the _encoding_ String.
4343
+ */
4344
+ static VALUE
4345
+ pgconn_async_set_client_encoding(VALUE self, VALUE encname)
4346
+ {
4347
+ VALUE query_format, query;
4348
+
4349
+ rb_check_frozen(self);
4350
+ Check_Type(encname, T_STRING);
4351
+ query_format = rb_str_new_cstr("set client_encoding to '%s'");
4352
+ query = rb_funcall(query_format, rb_intern("%"), 1, encname);
4353
+
4354
+ pgconn_async_exec(1, &query, self);
4355
+ pgconn_set_internal_encoding_index( self );
4356
+
4357
+ return Qnil;
4358
+ }
3846
4359
 
3847
4360
  static VALUE
3848
4361
  pgconn_set_client_encoding_async1( VALUE args )
3849
4362
  {
3850
4363
  VALUE self = ((VALUE*)args)[0];
3851
4364
  VALUE encname = ((VALUE*)args)[1];
3852
- VALUE query_format = rb_str_new_cstr("set client_encoding to '%s'");
3853
- VALUE query = rb_funcall(query_format, rb_intern("%"), 1, encname);
3854
-
3855
- pgconn_async_exec(1, &query, self);
4365
+ pgconn_async_set_client_encoding(self, encname);
3856
4366
  return 0;
3857
4367
  }
3858
4368
 
@@ -3867,9 +4377,9 @@ pgconn_set_client_encoding_async2( VALUE arg, VALUE ex )
3867
4377
 
3868
4378
 
3869
4379
  static VALUE
3870
- pgconn_set_client_encoding_async( VALUE self, const char *encname )
4380
+ pgconn_set_client_encoding_async( VALUE self, VALUE encname )
3871
4381
  {
3872
- VALUE args[] = { self, rb_str_new_cstr(encname) };
4382
+ VALUE args[] = { self, encname };
3873
4383
  return rb_rescue(pgconn_set_client_encoding_async1, (VALUE)&args, pgconn_set_client_encoding_async2, Qnil);
3874
4384
  }
3875
4385
 
@@ -3886,16 +4396,23 @@ static VALUE
3886
4396
  pgconn_set_default_encoding( VALUE self )
3887
4397
  {
3888
4398
  PGconn *conn = pg_get_pgconn( self );
3889
- rb_encoding *enc;
3890
- const char *encname;
3891
-
3892
- if (( enc = rb_default_internal_encoding() )) {
3893
- encname = pg_get_rb_encoding_as_pg_encoding( enc );
3894
- if ( pgconn_set_client_encoding_async(self, encname) != 0 )
3895
- rb_warning( "Failed to set the default_internal encoding to %s: '%s'",
3896
- encname, PQerrorMessage(conn) );
4399
+ rb_encoding *rb_enc;
4400
+
4401
+ rb_check_frozen(self);
4402
+ if (( rb_enc = rb_default_internal_encoding() )) {
4403
+ rb_encoding * conn_encoding = pg_conn_enc_get( conn );
4404
+
4405
+ /* Don't set the server encoding, if it's unnecessary.
4406
+ * This is important for connection proxies, who disallow configuration settings.
4407
+ */
4408
+ if ( conn_encoding != rb_enc ) {
4409
+ const char *encname = pg_get_rb_encoding_as_pg_encoding( rb_enc );
4410
+ if ( pgconn_set_client_encoding_async(self, rb_str_new_cstr(encname)) != 0 )
4411
+ rb_warning( "Failed to set the default_internal encoding to %s: '%s'",
4412
+ encname, PQerrorMessage(conn) );
4413
+ }
3897
4414
  pgconn_set_internal_encoding_index( self );
3898
- return rb_enc_from_encoding( enc );
4415
+ return rb_enc_from_encoding( rb_enc );
3899
4416
  } else {
3900
4417
  pgconn_set_internal_encoding_index( self );
3901
4418
  return Qnil;
@@ -3916,13 +4433,14 @@ static VALUE
3916
4433
  pgconn_type_map_for_queries_set(VALUE self, VALUE typemap)
3917
4434
  {
3918
4435
  t_pg_connection *this = pg_get_connection( self );
4436
+ t_typemap *tm;
4437
+ UNUSED(tm);
3919
4438
 
3920
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
3921
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
3922
- rb_obj_classname( typemap ) );
3923
- }
3924
- Check_Type(typemap, T_DATA);
3925
- this->type_map_for_queries = typemap;
4439
+ rb_check_frozen(self);
4440
+ /* Check type of method param */
4441
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, tm);
4442
+
4443
+ RB_OBJ_WRITE(self, &this->type_map_for_queries, typemap);
3926
4444
 
3927
4445
  return typemap;
3928
4446
  }
@@ -3956,13 +4474,12 @@ static VALUE
3956
4474
  pgconn_type_map_for_results_set(VALUE self, VALUE typemap)
3957
4475
  {
3958
4476
  t_pg_connection *this = pg_get_connection( self );
4477
+ t_typemap *tm;
4478
+ UNUSED(tm);
3959
4479
 
3960
- if ( !rb_obj_is_kind_of(typemap, rb_cTypeMap) ) {
3961
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::TypeMap)",
3962
- rb_obj_classname( typemap ) );
3963
- }
3964
- Check_Type(typemap, T_DATA);
3965
- this->type_map_for_results = typemap;
4480
+ rb_check_frozen(self);
4481
+ TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, tm);
4482
+ RB_OBJ_WRITE(self, &this->type_map_for_results, typemap);
3966
4483
 
3967
4484
  return typemap;
3968
4485
  }
@@ -3996,20 +4513,20 @@ pgconn_type_map_for_results_get(VALUE self)
3996
4513
  *
3997
4514
  */
3998
4515
  static VALUE
3999
- pgconn_encoder_for_put_copy_data_set(VALUE self, VALUE typemap)
4516
+ pgconn_encoder_for_put_copy_data_set(VALUE self, VALUE encoder)
4000
4517
  {
4001
4518
  t_pg_connection *this = pg_get_connection( self );
4002
4519
 
4003
- if( typemap != Qnil ){
4004
- if ( !rb_obj_is_kind_of(typemap, rb_cPG_Coder) ) {
4005
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Coder)",
4006
- rb_obj_classname( typemap ) );
4007
- }
4008
- Check_Type(typemap, T_DATA);
4520
+ rb_check_frozen(self);
4521
+ if( encoder != Qnil ){
4522
+ t_pg_coder *co;
4523
+ UNUSED(co);
4524
+ /* Check argument type */
4525
+ TypedData_Get_Struct(encoder, t_pg_coder, &pg_coder_type, co);
4009
4526
  }
4010
- this->encoder_for_put_copy_data = typemap;
4527
+ RB_OBJ_WRITE(self, &this->encoder_for_put_copy_data, encoder);
4011
4528
 
4012
- return typemap;
4529
+ return encoder;
4013
4530
  }
4014
4531
 
4015
4532
  /*
@@ -4045,20 +4562,20 @@ pgconn_encoder_for_put_copy_data_get(VALUE self)
4045
4562
  *
4046
4563
  */
4047
4564
  static VALUE
4048
- pgconn_decoder_for_get_copy_data_set(VALUE self, VALUE typemap)
4565
+ pgconn_decoder_for_get_copy_data_set(VALUE self, VALUE decoder)
4049
4566
  {
4050
4567
  t_pg_connection *this = pg_get_connection( self );
4051
4568
 
4052
- if( typemap != Qnil ){
4053
- if ( !rb_obj_is_kind_of(typemap, rb_cPG_Coder) ) {
4054
- rb_raise( rb_eTypeError, "wrong argument type %s (expected kind of PG::Coder)",
4055
- rb_obj_classname( typemap ) );
4056
- }
4057
- Check_Type(typemap, T_DATA);
4569
+ rb_check_frozen(self);
4570
+ if( decoder != Qnil ){
4571
+ t_pg_coder *co;
4572
+ UNUSED(co);
4573
+ /* Check argument type */
4574
+ TypedData_Get_Struct(decoder, t_pg_coder, &pg_coder_type, co);
4058
4575
  }
4059
- this->decoder_for_get_copy_data = typemap;
4576
+ RB_OBJ_WRITE(self, &this->decoder_for_get_copy_data, decoder);
4060
4577
 
4061
- return typemap;
4578
+ return decoder;
4062
4579
  }
4063
4580
 
4064
4581
  /*
@@ -4102,6 +4619,7 @@ pgconn_field_name_type_set(VALUE self, VALUE sym)
4102
4619
  {
4103
4620
  t_pg_connection *this = pg_get_connection( self );
4104
4621
 
4622
+ rb_check_frozen(self);
4105
4623
  this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
4106
4624
  if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
4107
4625
  else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
@@ -4138,9 +4656,10 @@ pgconn_field_name_type_get(VALUE self)
4138
4656
  * Document-class: PG::Connection
4139
4657
  */
4140
4658
  void
4141
- init_pg_connection()
4659
+ init_pg_connection(void)
4142
4660
  {
4143
4661
  s_id_encode = rb_intern("encode");
4662
+ s_id_autoclose_set = rb_intern("autoclose=");
4144
4663
  sym_type = ID2SYM(rb_intern("type"));
4145
4664
  sym_format = ID2SYM(rb_intern("format"));
4146
4665
  sym_value = ID2SYM(rb_intern("value"));
@@ -4156,10 +4675,6 @@ init_pg_connection()
4156
4675
  /****** PG::Connection CLASS METHODS ******/
4157
4676
  rb_define_alloc_func( rb_cPGconn, pgconn_s_allocate );
4158
4677
 
4159
- SINGLETON_ALIAS(rb_cPGconn, "connect", "new");
4160
- SINGLETON_ALIAS(rb_cPGconn, "open", "new");
4161
- SINGLETON_ALIAS(rb_cPGconn, "setdb", "new");
4162
- SINGLETON_ALIAS(rb_cPGconn, "setdblogin", "new");
4163
4678
  rb_define_singleton_method(rb_cPGconn, "escape_string", pgconn_s_escape, 1);
4164
4679
  SINGLETON_ALIAS(rb_cPGconn, "escape", "escape_string");
4165
4680
  rb_define_singleton_method(rb_cPGconn, "escape_bytea", pgconn_s_escape_bytea, 1);
@@ -4168,15 +4683,17 @@ init_pg_connection()
4168
4683
  rb_define_singleton_method(rb_cPGconn, "quote_ident", pgconn_s_quote_ident, 1);
4169
4684
  rb_define_singleton_method(rb_cPGconn, "connect_start", pgconn_s_connect_start, -1);
4170
4685
  rb_define_singleton_method(rb_cPGconn, "conndefaults", pgconn_s_conndefaults, 0);
4171
- rb_define_singleton_method(rb_cPGconn, "ping", pgconn_s_ping, -1);
4686
+ rb_define_singleton_method(rb_cPGconn, "conninfo_parse", pgconn_s_conninfo_parse, 1);
4687
+ rb_define_singleton_method(rb_cPGconn, "sync_ping", pgconn_s_sync_ping, -1);
4688
+ rb_define_singleton_method(rb_cPGconn, "sync_connect", pgconn_s_sync_connect, -1);
4172
4689
 
4173
4690
  /****** PG::Connection INSTANCE METHODS: Connection Control ******/
4174
- rb_define_method(rb_cPGconn, "initialize", pgconn_init, -1);
4175
4691
  rb_define_method(rb_cPGconn, "connect_poll", pgconn_connect_poll, 0);
4176
4692
  rb_define_method(rb_cPGconn, "finish", pgconn_finish, 0);
4177
4693
  rb_define_method(rb_cPGconn, "finished?", pgconn_finished_p, 0);
4178
- rb_define_method(rb_cPGconn, "reset", pgconn_reset, 0);
4694
+ rb_define_method(rb_cPGconn, "sync_reset", pgconn_sync_reset, 0);
4179
4695
  rb_define_method(rb_cPGconn, "reset_start", pgconn_reset_start, 0);
4696
+ rb_define_private_method(rb_cPGconn, "reset_start2", pgconn_reset_start2, 1);
4180
4697
  rb_define_method(rb_cPGconn, "reset_poll", pgconn_reset_poll, 0);
4181
4698
  rb_define_alias(rb_cPGconn, "close", "finish");
4182
4699
 
@@ -4185,11 +4702,12 @@ init_pg_connection()
4185
4702
  rb_define_method(rb_cPGconn, "user", pgconn_user, 0);
4186
4703
  rb_define_method(rb_cPGconn, "pass", pgconn_pass, 0);
4187
4704
  rb_define_method(rb_cPGconn, "host", pgconn_host, 0);
4705
+ #if defined(HAVE_PQRESULTMEMORYSIZE)
4706
+ rb_define_method(rb_cPGconn, "hostaddr", pgconn_hostaddr, 0);
4707
+ #endif
4188
4708
  rb_define_method(rb_cPGconn, "port", pgconn_port, 0);
4189
4709
  rb_define_method(rb_cPGconn, "tty", pgconn_tty, 0);
4190
- #ifdef HAVE_PQCONNINFO
4191
4710
  rb_define_method(rb_cPGconn, "conninfo", pgconn_conninfo, 0);
4192
- #endif
4193
4711
  rb_define_method(rb_cPGconn, "options", pgconn_options, 0);
4194
4712
  rb_define_method(rb_cPGconn, "status", pgconn_status, 0);
4195
4713
  rb_define_method(rb_cPGconn, "transaction_status", pgconn_transaction_status, 0);
@@ -4200,17 +4718,24 @@ init_pg_connection()
4200
4718
  rb_define_method(rb_cPGconn, "socket", pgconn_socket, 0);
4201
4719
  rb_define_method(rb_cPGconn, "socket_io", pgconn_socket_io, 0);
4202
4720
  rb_define_method(rb_cPGconn, "backend_pid", pgconn_backend_pid, 0);
4721
+ #ifndef HAVE_PQSETCHUNKEDROWSMODE
4722
+ rb_define_method(rb_cPGconn, "backend_key", pgconn_backend_key, 0);
4723
+ #endif
4203
4724
  rb_define_method(rb_cPGconn, "connection_needs_password", pgconn_connection_needs_password, 0);
4204
4725
  rb_define_method(rb_cPGconn, "connection_used_password", pgconn_connection_used_password, 0);
4205
4726
  /* rb_define_method(rb_cPGconn, "getssl", pgconn_getssl, 0); */
4206
4727
 
4207
4728
  /****** PG::Connection INSTANCE METHODS: Command Execution ******/
4208
- rb_define_method(rb_cPGconn, "sync_exec", pgconn_exec, -1);
4209
- rb_define_method(rb_cPGconn, "sync_exec_params", pgconn_exec_params, -1);
4210
- rb_define_method(rb_cPGconn, "sync_prepare", pgconn_prepare, -1);
4211
- rb_define_method(rb_cPGconn, "sync_exec_prepared", pgconn_exec_prepared, -1);
4212
- rb_define_method(rb_cPGconn, "sync_describe_prepared", pgconn_describe_prepared, 1);
4213
- rb_define_method(rb_cPGconn, "sync_describe_portal", pgconn_describe_portal, 1);
4729
+ rb_define_method(rb_cPGconn, "sync_exec", pgconn_sync_exec, -1);
4730
+ rb_define_method(rb_cPGconn, "sync_exec_params", pgconn_sync_exec_params, -1);
4731
+ rb_define_method(rb_cPGconn, "sync_prepare", pgconn_sync_prepare, -1);
4732
+ rb_define_method(rb_cPGconn, "sync_exec_prepared", pgconn_sync_exec_prepared, -1);
4733
+ rb_define_method(rb_cPGconn, "sync_describe_prepared", pgconn_sync_describe_prepared, 1);
4734
+ rb_define_method(rb_cPGconn, "sync_describe_portal", pgconn_sync_describe_portal, 1);
4735
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
4736
+ rb_define_method(rb_cPGconn, "sync_close_prepared", pgconn_sync_close_prepared, 1);
4737
+ rb_define_method(rb_cPGconn, "sync_close_portal", pgconn_sync_close_portal, 1);
4738
+ #endif
4214
4739
 
4215
4740
  rb_define_method(rb_cPGconn, "exec", pgconn_async_exec, -1);
4216
4741
  rb_define_method(rb_cPGconn, "exec_params", pgconn_async_exec_params, -1);
@@ -4218,6 +4743,10 @@ init_pg_connection()
4218
4743
  rb_define_method(rb_cPGconn, "exec_prepared", pgconn_async_exec_prepared, -1);
4219
4744
  rb_define_method(rb_cPGconn, "describe_prepared", pgconn_async_describe_prepared, 1);
4220
4745
  rb_define_method(rb_cPGconn, "describe_portal", pgconn_async_describe_portal, 1);
4746
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
4747
+ rb_define_method(rb_cPGconn, "close_prepared", pgconn_async_close_prepared, 1);
4748
+ rb_define_method(rb_cPGconn, "close_portal", pgconn_async_close_portal, 1);
4749
+ #endif
4221
4750
 
4222
4751
  rb_define_alias(rb_cPGconn, "async_exec", "exec");
4223
4752
  rb_define_alias(rb_cPGconn, "async_query", "async_exec");
@@ -4226,6 +4755,10 @@ init_pg_connection()
4226
4755
  rb_define_alias(rb_cPGconn, "async_exec_prepared", "exec_prepared");
4227
4756
  rb_define_alias(rb_cPGconn, "async_describe_prepared", "describe_prepared");
4228
4757
  rb_define_alias(rb_cPGconn, "async_describe_portal", "describe_portal");
4758
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
4759
+ rb_define_alias(rb_cPGconn, "async_close_prepared", "close_prepared");
4760
+ rb_define_alias(rb_cPGconn, "async_close_portal", "close_portal");
4761
+ #endif
4229
4762
 
4230
4763
  rb_define_method(rb_cPGconn, "make_empty_pgresult", pgconn_make_empty_pgresult, 1);
4231
4764
  rb_define_method(rb_cPGconn, "escape_string", pgconn_s_escape, 1);
@@ -4235,6 +4768,9 @@ init_pg_connection()
4235
4768
  rb_define_method(rb_cPGconn, "escape_bytea", pgconn_s_escape_bytea, 1);
4236
4769
  rb_define_method(rb_cPGconn, "unescape_bytea", pgconn_s_unescape_bytea, 1);
4237
4770
  rb_define_method(rb_cPGconn, "set_single_row_mode", pgconn_set_single_row_mode, 0);
4771
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
4772
+ rb_define_method(rb_cPGconn, "set_chunked_rows_mode", pgconn_set_chunked_rows_mode, 1);
4773
+ #endif
4238
4774
 
4239
4775
  /****** PG::Connection INSTANCE METHODS: Asynchronous Command Processing ******/
4240
4776
  rb_define_method(rb_cPGconn, "send_query", pgconn_send_query, -1);
@@ -4243,31 +4779,32 @@ init_pg_connection()
4243
4779
  rb_define_method(rb_cPGconn, "send_query_prepared", pgconn_send_query_prepared, -1);
4244
4780
  rb_define_method(rb_cPGconn, "send_describe_prepared", pgconn_send_describe_prepared, 1);
4245
4781
  rb_define_method(rb_cPGconn, "send_describe_portal", pgconn_send_describe_portal, 1);
4246
- rb_define_method(rb_cPGconn, "get_result", pgconn_get_result, 0);
4782
+ rb_define_method(rb_cPGconn, "sync_get_result", pgconn_sync_get_result, 0);
4247
4783
  rb_define_method(rb_cPGconn, "consume_input", pgconn_consume_input, 0);
4248
4784
  rb_define_method(rb_cPGconn, "is_busy", pgconn_is_busy, 0);
4249
- rb_define_method(rb_cPGconn, "setnonblocking", pgconn_setnonblocking, 1);
4250
- rb_define_method(rb_cPGconn, "isnonblocking", pgconn_isnonblocking, 0);
4251
- rb_define_alias(rb_cPGconn, "nonblocking?", "isnonblocking");
4252
- rb_define_method(rb_cPGconn, "flush", pgconn_flush, 0);
4785
+ rb_define_method(rb_cPGconn, "sync_setnonblocking", pgconn_sync_setnonblocking, 1);
4786
+ rb_define_method(rb_cPGconn, "sync_isnonblocking", pgconn_sync_isnonblocking, 0);
4787
+ rb_define_method(rb_cPGconn, "sync_flush", pgconn_sync_flush, 0);
4788
+ rb_define_method(rb_cPGconn, "flush", pgconn_async_flush, 0);
4789
+ rb_define_alias(rb_cPGconn, "async_flush", "flush");
4253
4790
  rb_define_method(rb_cPGconn, "discard_results", pgconn_discard_results, 0);
4254
4791
 
4255
4792
  /****** PG::Connection INSTANCE METHODS: Cancelling Queries in Progress ******/
4256
- rb_define_method(rb_cPGconn, "cancel", pgconn_cancel, 0);
4793
+ #ifndef HAVE_PQSETCHUNKEDROWSMODE
4794
+ rb_define_method(rb_cPGconn, "sync_cancel", pgconn_sync_cancel, 0);
4795
+ #endif
4257
4796
 
4258
4797
  /****** PG::Connection INSTANCE METHODS: NOTIFY ******/
4259
4798
  rb_define_method(rb_cPGconn, "notifies", pgconn_notifies, 0);
4260
4799
 
4261
4800
  /****** PG::Connection INSTANCE METHODS: COPY ******/
4262
- rb_define_method(rb_cPGconn, "put_copy_data", pgconn_put_copy_data, -1);
4263
- rb_define_method(rb_cPGconn, "put_copy_end", pgconn_put_copy_end, -1);
4264
- rb_define_method(rb_cPGconn, "get_copy_data", pgconn_get_copy_data, -1);
4801
+ rb_define_method(rb_cPGconn, "sync_put_copy_data", pgconn_sync_put_copy_data, -1);
4802
+ rb_define_method(rb_cPGconn, "sync_put_copy_end", pgconn_sync_put_copy_end, -1);
4803
+ rb_define_method(rb_cPGconn, "sync_get_copy_data", pgconn_sync_get_copy_data, -1);
4265
4804
 
4266
4805
  /****** PG::Connection INSTANCE METHODS: Control Functions ******/
4267
4806
  rb_define_method(rb_cPGconn, "set_error_verbosity", pgconn_set_error_verbosity, 1);
4268
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
4269
4807
  rb_define_method(rb_cPGconn, "set_error_context_visibility", pgconn_set_error_context_visibility, 1 );
4270
- #endif
4271
4808
  rb_define_method(rb_cPGconn, "trace", pgconn_trace, 1);
4272
4809
  rb_define_method(rb_cPGconn, "untrace", pgconn_untrace, 0);
4273
4810
 
@@ -4277,22 +4814,33 @@ init_pg_connection()
4277
4814
 
4278
4815
  /****** PG::Connection INSTANCE METHODS: Other ******/
4279
4816
  rb_define_method(rb_cPGconn, "get_client_encoding", pgconn_get_client_encoding, 0);
4280
- rb_define_method(rb_cPGconn, "set_client_encoding", pgconn_set_client_encoding, 1);
4817
+ rb_define_method(rb_cPGconn, "sync_set_client_encoding", pgconn_sync_set_client_encoding, 1);
4818
+ rb_define_method(rb_cPGconn, "set_client_encoding", pgconn_async_set_client_encoding, 1);
4819
+ rb_define_alias(rb_cPGconn, "async_set_client_encoding", "set_client_encoding");
4281
4820
  rb_define_alias(rb_cPGconn, "client_encoding=", "set_client_encoding");
4282
- rb_define_method(rb_cPGconn, "transaction", pgconn_transaction, 0);
4283
4821
  rb_define_method(rb_cPGconn, "block", pgconn_block, -1);
4822
+ rb_define_private_method(rb_cPGconn, "flush_data=", pgconn_flush_data_set, 1);
4284
4823
  rb_define_method(rb_cPGconn, "wait_for_notify", pgconn_wait_for_notify, -1);
4285
4824
  rb_define_alias(rb_cPGconn, "notifies_wait", "wait_for_notify");
4286
4825
  rb_define_method(rb_cPGconn, "quote_ident", pgconn_s_quote_ident, 1);
4287
- rb_define_method(rb_cPGconn, "get_last_result", pgconn_get_last_result, 0);
4288
- #ifdef HAVE_PQENCRYPTPASSWORDCONN
4289
- rb_define_method(rb_cPGconn, "encrypt_password", pgconn_encrypt_password, -1);
4290
- #endif
4826
+ rb_define_method(rb_cPGconn, "sync_get_last_result", pgconn_sync_get_last_result, 0);
4827
+ rb_define_method(rb_cPGconn, "get_last_result", pgconn_async_get_last_result, 0);
4828
+ rb_define_alias(rb_cPGconn, "async_get_last_result", "get_last_result");
4829
+ rb_define_method(rb_cPGconn, "sync_encrypt_password", pgconn_sync_encrypt_password, -1);
4291
4830
 
4292
- #ifdef HAVE_PQSSLATTRIBUTE
4293
4831
  rb_define_method(rb_cPGconn, "ssl_in_use?", pgconn_ssl_in_use, 0);
4294
4832
  rb_define_method(rb_cPGconn, "ssl_attribute", pgconn_ssl_attribute, 1);
4295
4833
  rb_define_method(rb_cPGconn, "ssl_attribute_names", pgconn_ssl_attribute_names, 0);
4834
+
4835
+ #ifdef HAVE_PQENTERPIPELINEMODE
4836
+ rb_define_method(rb_cPGconn, "pipeline_status", pgconn_pipeline_status, 0);
4837
+ rb_define_method(rb_cPGconn, "enter_pipeline_mode", pgconn_enter_pipeline_mode, 0);
4838
+ rb_define_method(rb_cPGconn, "exit_pipeline_mode", pgconn_exit_pipeline_mode, 0);
4839
+ rb_define_method(rb_cPGconn, "sync_pipeline_sync", pgconn_sync_pipeline_sync, 0);
4840
+ rb_define_method(rb_cPGconn, "send_flush_request", pgconn_send_flush_request, 0);
4841
+ #ifdef HAVE_PQSETCHUNKEDROWSMODE
4842
+ rb_define_method(rb_cPGconn, "send_pipeline_sync", pgconn_send_pipeline_sync, 0);
4843
+ #endif
4296
4844
  #endif
4297
4845
 
4298
4846
  /****** PG::Connection INSTANCE METHODS: Large Object Support ******/