pg 1.5.9 → 1.7.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +126 -0
  4. data/Gemfile +11 -7
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +83 -32
  7. data/README.md +79 -29
  8. data/Rakefile +84 -19
  9. data/certs/larskanis-2026.pem +24 -0
  10. data/ext/errorcodes.def +9 -0
  11. data/ext/errorcodes.rb +1 -1
  12. data/ext/errorcodes.txt +8 -1
  13. data/ext/extconf.rb +194 -30
  14. data/ext/gvl_wrappers.c +13 -2
  15. data/ext/gvl_wrappers.h +47 -134
  16. data/ext/pg.c +23 -16
  17. data/ext/pg.h +23 -24
  18. data/ext/pg_binary_decoder.c +157 -3
  19. data/ext/pg_binary_encoder.c +219 -9
  20. data/ext/pg_cancel_connection.c +360 -0
  21. data/ext/pg_coder.c +58 -9
  22. data/ext/pg_connection.c +558 -291
  23. data/ext/pg_copy_coder.c +36 -23
  24. data/ext/pg_record_coder.c +11 -7
  25. data/ext/pg_result.c +144 -76
  26. data/ext/pg_text_decoder.c +7 -7
  27. data/ext/pg_text_encoder.c +29 -11
  28. data/ext/pg_tuple.c +9 -9
  29. data/ext/pg_type_map.c +45 -3
  30. data/ext/pg_type_map_all_strings.c +3 -3
  31. data/ext/pg_type_map_by_class.c +2 -2
  32. data/ext/pg_type_map_by_column.c +4 -3
  33. data/ext/pg_type_map_by_mri_type.c +1 -1
  34. data/ext/pg_type_map_by_oid.c +4 -2
  35. data/ext/pg_type_map_in_ruby.c +2 -2
  36. data/ext/pg_util.c +2 -2
  37. data/ext/pg_util.h +2 -2
  38. data/lib/pg/basic_type_map_based_on_result.rb +5 -0
  39. data/lib/pg/basic_type_map_for_queries.rb +16 -5
  40. data/lib/pg/basic_type_map_for_results.rb +5 -0
  41. data/lib/pg/basic_type_registry.rb +2 -2
  42. data/lib/pg/cancel_connection.rb +53 -0
  43. data/lib/pg/coder.rb +4 -2
  44. data/lib/pg/connection.rb +393 -180
  45. data/lib/pg/text_decoder/date.rb +1 -1
  46. data/lib/pg/text_decoder/json.rb +1 -1
  47. data/lib/pg/text_encoder/date.rb +7 -1
  48. data/lib/pg/text_encoder/json.rb +1 -1
  49. data/lib/pg/version.rb +2 -1
  50. data/lib/pg.rb +156 -130
  51. data/misc/glibc/Dockerfile +20 -0
  52. data/misc/glibc/docker-compose.yml +9 -0
  53. data/misc/glibc/glibc_spec.rb +5 -0
  54. data/misc/yugabyte/Dockerfile +9 -0
  55. data/misc/yugabyte/docker-compose.yml +28 -0
  56. data/misc/yugabyte/pg-test.rb +45 -0
  57. data/pg.gemspec +5 -3
  58. data/ports/patches/krb5/1.22.2/0001-Allow-static-linking-krb5-library.patch +30 -0
  59. data/ports/patches/krb5/1.22.2/0002-unknown-command-line-option-on-clang.patch +12 -0
  60. data/ports/patches/postgresql/18.6/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  61. data/rakelib/pg_gem_helper.rb +64 -0
  62. data/sample/async_mixed.rb +1 -1
  63. data.tar.gz.sig +0 -0
  64. metadata +36 -22
  65. metadata.gz.sig +0 -0
  66. data/Manifest.txt +0 -72
  67. data/Rakefile.cross +0 -303
data/ext/gvl_wrappers.h CHANGED
@@ -15,12 +15,21 @@
15
15
  #ifndef __gvl_wrappers_h
16
16
  #define __gvl_wrappers_h
17
17
 
18
+ #include <ruby/version.h>
18
19
  #include <ruby/thread.h>
19
20
 
20
21
  #ifdef RUBY_EXTCONF_H
21
22
  # include RUBY_EXTCONF_H
22
23
  #endif
23
24
 
25
+ #if RUBY_API_VERSION_MAJOR < 4
26
+ extern int ruby_thread_has_gvl_p(void);
27
+ #endif
28
+
29
+ #ifndef LIBPQ_HAS_CHUNK_MODE
30
+ typedef struct pg_cancel_conn PGcancelConn;
31
+ #endif
32
+
24
33
  #define DEFINE_PARAM_LIST1(type, name) \
25
34
  name,
26
35
 
@@ -79,20 +88,35 @@
79
88
  }
80
89
 
81
90
  #ifdef ENABLE_GVL_UNLOCK
82
- #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
83
- rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
84
- struct gvl_wrapper_##name##_params params = { \
85
- {FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
86
- }; \
87
- rb_thread_call_with_gvl(gvl_##name##_skeleton, &params); \
88
- when_non_void( return params.retval; ) \
89
- }
91
+ #if RUBY_API_VERSION_MAJOR >= 4 || defined(TRUFFLERUBY)
92
+ #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
93
+ rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
94
+ struct gvl_wrapper_##name##_params params = { \
95
+ {FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
96
+ }; \
97
+ rb_thread_call_with_gvl(gvl_##name##_skeleton, &params); \
98
+ when_non_void( return params.retval; ) \
99
+ }
100
+ #else
101
+ #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
102
+ rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
103
+ struct gvl_wrapper_##name##_params params = { \
104
+ {FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
105
+ }; \
106
+ if (ruby_thread_has_gvl_p()) { \
107
+ gvl_##name##_skeleton(&params); \
108
+ } else { \
109
+ rb_thread_call_with_gvl(gvl_##name##_skeleton, &params); \
110
+ } \
111
+ when_non_void( return params.retval; ) \
112
+ }
113
+ #endif
90
114
  #else
91
- #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
92
- rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
93
- when_non_void( return ) \
94
- name( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
95
- }
115
+ #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
116
+ rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
117
+ when_non_void( return ) \
118
+ name( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
119
+ }
96
120
  #endif
97
121
 
98
122
  #define GVL_TYPE_VOID(string)
@@ -101,145 +125,34 @@
101
125
 
102
126
  /*
103
127
  * Definitions of blocking functions and their parameters
128
+ *
129
+ * ATTENTION:
130
+ * Do not GVL-release functions that take pointers to ruby objects.
131
+ * If the ruby object is relocated by `GC.compact` from a second thread, before the pointer is used, it is no longer valid.
132
+ * That can lead to a crash or to wrong data.
133
+ * This issue has been raised in https://github.com/ged/ruby-pg/issues/738 and https://github.com/ged/ruby-pg/issues/721 .
104
134
  */
105
135
 
106
- #define FOR_EACH_PARAM_OF_PQconnectdb(param)
107
-
108
136
  #define FOR_EACH_PARAM_OF_PQconnectStart(param)
109
-
110
137
  #define FOR_EACH_PARAM_OF_PQconnectPoll(param)
111
138
 
112
- #define FOR_EACH_PARAM_OF_PQreset(param)
113
-
114
139
  #define FOR_EACH_PARAM_OF_PQresetStart(param)
115
-
116
140
  #define FOR_EACH_PARAM_OF_PQresetPoll(param)
117
141
 
118
142
  #define FOR_EACH_PARAM_OF_PQping(param)
119
143
 
120
- #define FOR_EACH_PARAM_OF_PQexec(param) \
121
- param(PGconn *, conn)
122
-
123
- #define FOR_EACH_PARAM_OF_PQexecParams(param) \
124
- param(PGconn *, conn) \
125
- param(const char *, command) \
126
- param(int, nParams) \
127
- param(const Oid *, paramTypes) \
128
- param(const char * const *, paramValues) \
129
- param(const int *, paramLengths) \
130
- param(const int *, paramFormats)
131
-
132
- #define FOR_EACH_PARAM_OF_PQexecPrepared(param) \
133
- param(PGconn *, conn) \
134
- param(const char *, stmtName) \
135
- param(int, nParams) \
136
- param(const char * const *, paramValues) \
137
- param(const int *, paramLengths) \
138
- param(const int *, paramFormats)
139
-
140
- #define FOR_EACH_PARAM_OF_PQprepare(param) \
141
- param(PGconn *, conn) \
142
- param(const char *, stmtName) \
143
- param(const char *, query) \
144
- param(int, nParams)
145
-
146
- #define FOR_EACH_PARAM_OF_PQdescribePrepared(param) \
147
- param(PGconn *, conn)
148
-
149
- #define FOR_EACH_PARAM_OF_PQdescribePortal(param) \
150
- param(PGconn *, conn)
151
-
152
- #define FOR_EACH_PARAM_OF_PQgetResult(param)
153
-
154
- #define FOR_EACH_PARAM_OF_PQputCopyData(param) \
155
- param(PGconn *, conn) \
156
- param(const char *, buffer)
157
-
158
- #define FOR_EACH_PARAM_OF_PQputCopyEnd(param) \
159
- param(PGconn *, conn)
160
-
161
- #define FOR_EACH_PARAM_OF_PQgetCopyData(param) \
162
- param(PGconn *, conn) \
163
- param(char **, buffer)
164
-
165
- #define FOR_EACH_PARAM_OF_PQnotifies(param)
166
-
167
- #define FOR_EACH_PARAM_OF_PQsendQuery(param) \
168
- param(PGconn *, conn)
169
-
170
- #define FOR_EACH_PARAM_OF_PQsendQueryParams(param) \
171
- param(PGconn *, conn) \
172
- param(const char *, command) \
173
- param(int, nParams) \
174
- param(const Oid *, paramTypes) \
175
- param(const char *const *, paramValues) \
176
- param(const int *, paramLengths) \
177
- param(const int *, paramFormats)
178
-
179
- #define FOR_EACH_PARAM_OF_PQsendPrepare(param) \
180
- param(PGconn *, conn) \
181
- param(const char *, stmtName) \
182
- param(const char *, query) \
183
- param(int, nParams)
184
-
185
- #define FOR_EACH_PARAM_OF_PQsendQueryPrepared(param) \
186
- param(PGconn *, conn) \
187
- param(const char *, stmtName) \
188
- param(int, nParams) \
189
- param(const char *const *, paramValues) \
190
- param(const int *, paramLengths) \
191
- param(const int *, paramFormats)
192
-
193
- #define FOR_EACH_PARAM_OF_PQsendDescribePrepared(param) \
194
- param(PGconn *, conn)
195
-
196
- #define FOR_EACH_PARAM_OF_PQsendDescribePortal(param) \
197
- param(PGconn *, conn)
198
-
199
- #define FOR_EACH_PARAM_OF_PQsetClientEncoding(param) \
200
- param(PGconn *, conn)
201
-
202
- #define FOR_EACH_PARAM_OF_PQisBusy(param)
203
-
204
- #define FOR_EACH_PARAM_OF_PQencryptPasswordConn(param) \
205
- param(PGconn *, conn) \
206
- param(const char *, passwd) \
207
- param(const char *, user)
208
-
209
- #define FOR_EACH_PARAM_OF_PQcancel(param) \
210
- param(PGcancel *, cancel) \
211
- param(char *, errbuf)
144
+ #define FOR_EACH_PARAM_OF_PQcancelStart(param)
145
+ #define FOR_EACH_PARAM_OF_PQcancelPoll(param)
212
146
 
213
147
  /* function( name, void_or_nonvoid, returntype, lastparamtype, lastparamname ) */
214
148
  #define FOR_EACH_BLOCKING_FUNCTION(function) \
215
- function(PQconnectdb, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
216
149
  function(PQconnectStart, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
217
150
  function(PQconnectPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
218
- function(PQreset, GVL_TYPE_VOID, void, PGconn *, conn) \
219
151
  function(PQresetStart, GVL_TYPE_NONVOID, int, PGconn *, conn) \
220
152
  function(PQresetPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
221
153
  function(PQping, GVL_TYPE_NONVOID, PGPing, const char *, conninfo) \
222
- function(PQexec, GVL_TYPE_NONVOID, PGresult *, const char *, command) \
223
- function(PQexecParams, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
224
- function(PQexecPrepared, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
225
- function(PQprepare, GVL_TYPE_NONVOID, PGresult *, const Oid *, paramTypes) \
226
- function(PQdescribePrepared, GVL_TYPE_NONVOID, PGresult *, const char *, stmtName) \
227
- function(PQdescribePortal, GVL_TYPE_NONVOID, PGresult *, const char *, portalName) \
228
- function(PQgetResult, GVL_TYPE_NONVOID, PGresult *, PGconn *, conn) \
229
- function(PQputCopyData, GVL_TYPE_NONVOID, int, int, nbytes) \
230
- function(PQputCopyEnd, GVL_TYPE_NONVOID, int, const char *, errormsg) \
231
- function(PQgetCopyData, GVL_TYPE_NONVOID, int, int, async) \
232
- function(PQnotifies, GVL_TYPE_NONVOID, PGnotify *, PGconn *, conn) \
233
- function(PQsendQuery, GVL_TYPE_NONVOID, int, const char *, query) \
234
- function(PQsendQueryParams, GVL_TYPE_NONVOID, int, int, resultFormat) \
235
- function(PQsendPrepare, GVL_TYPE_NONVOID, int, const Oid *, paramTypes) \
236
- function(PQsendQueryPrepared, GVL_TYPE_NONVOID, int, int, resultFormat) \
237
- function(PQsendDescribePrepared, GVL_TYPE_NONVOID, int, const char *, stmt) \
238
- function(PQsendDescribePortal, GVL_TYPE_NONVOID, int, const char *, portal) \
239
- function(PQsetClientEncoding, GVL_TYPE_NONVOID, int, const char *, encoding) \
240
- function(PQisBusy, GVL_TYPE_NONVOID, int, PGconn *, conn) \
241
- function(PQencryptPasswordConn, GVL_TYPE_NONVOID, char *, const char *, algorithm) \
242
- function(PQcancel, GVL_TYPE_NONVOID, int, int, errbufsize);
154
+ function(PQcancelStart, GVL_TYPE_NONVOID, int, PGcancelConn *, conn) \
155
+ function(PQcancelPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGcancelConn *, conn) \
243
156
 
244
157
  FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB_DECL );
245
158
 
data/ext/pg.c CHANGED
@@ -212,10 +212,10 @@ pg_get_rb_encoding_as_pg_encoding( rb_encoding *enc )
212
212
  *
213
213
  */
214
214
  char *
215
- pg_rb_str_ensure_capa( VALUE str, long expand_len, char *curr_ptr, char **end_ptr )
215
+ pg_rb_str_ensure_capa( VALUE str, size_t expand_len, char *curr_ptr, char **end_ptr )
216
216
  {
217
- long curr_len = curr_ptr - RSTRING_PTR(str);
218
- long curr_capa = rb_str_capacity( str );
217
+ size_t curr_len = curr_ptr - RSTRING_PTR(str);
218
+ size_t curr_capa = rb_str_capacity( str );
219
219
  if( curr_capa < curr_len + expand_len ){
220
220
  rb_str_set_len( str, curr_len );
221
221
  rb_str_modify_expand( str, (curr_len + expand_len) * 2 - curr_capa );
@@ -384,14 +384,10 @@ Init_pg_ext(void)
384
384
  rb_define_const(rb_mPGconstants, "CONNECTION_SSL_STARTUP", INT2FIX(CONNECTION_SSL_STARTUP));
385
385
  /* Internal state - PG.connect() needed. */
386
386
  rb_define_const(rb_mPGconstants, "CONNECTION_NEEDED", INT2FIX(CONNECTION_NEEDED));
387
- #if PG_MAJORVERSION_NUM >= 10
388
- /* Checking if session is read-write. Available since PostgreSQL-10. */
387
+ /* Checking if session is read-write. */
389
388
  rb_define_const(rb_mPGconstants, "CONNECTION_CHECK_WRITABLE", INT2FIX(CONNECTION_CHECK_WRITABLE));
390
- #endif
391
- #if PG_MAJORVERSION_NUM >= 10
392
- /* Consuming any extra messages. Available since PostgreSQL-10. */
389
+ /* Consuming any extra messages. */
393
390
  rb_define_const(rb_mPGconstants, "CONNECTION_CONSUME", INT2FIX(CONNECTION_CONSUME));
394
- #endif
395
391
  #if PG_MAJORVERSION_NUM >= 12
396
392
  /* Negotiating GSSAPI. Available since PostgreSQL-12. */
397
393
  rb_define_const(rb_mPGconstants, "CONNECTION_GSS_STARTUP", INT2FIX(CONNECTION_GSS_STARTUP));
@@ -404,6 +400,10 @@ Init_pg_ext(void)
404
400
  /* Checking if server is in standby mode. Available since PostgreSQL-14. */
405
401
  rb_define_const(rb_mPGconstants, "CONNECTION_CHECK_STANDBY", INT2FIX(CONNECTION_CHECK_STANDBY));
406
402
  #endif
403
+ #if PG_MAJORVERSION_NUM >= 17
404
+ /* Waiting for connection attempt to be started. Available since PostgreSQL-17. */
405
+ rb_define_const(rb_mPGconstants, "CONNECTION_ALLOCATED", INT2FIX(CONNECTION_ALLOCATED));
406
+ #endif
407
407
 
408
408
  /****** PG::Connection CLASS CONSTANTS: Nonblocking connection polling status ******/
409
409
 
@@ -451,14 +451,12 @@ Init_pg_ext(void)
451
451
  rb_define_const(rb_mPGconstants, "PQERRORS_SQLSTATE", INT2FIX(PQERRORS_SQLSTATE));
452
452
  #endif
453
453
 
454
- #ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
455
454
  /* See Connection#set_error_context_visibility */
456
455
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_NEVER", INT2FIX(PQSHOW_CONTEXT_NEVER));
457
456
  /* See Connection#set_error_context_visibility */
458
457
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_ERRORS", INT2FIX(PQSHOW_CONTEXT_ERRORS));
459
458
  /* See Connection#set_error_context_visibility */
460
459
  rb_define_const(rb_mPGconstants, "PQSHOW_CONTEXT_ALWAYS", INT2FIX(PQSHOW_CONTEXT_ALWAYS));
461
- #endif
462
460
 
463
461
  /****** PG::Connection CLASS CONSTANTS: Check Server Status ******/
464
462
 
@@ -506,8 +504,12 @@ Init_pg_ext(void)
506
504
  rb_define_const(rb_mPGconstants, "PGRES_COPY_BOTH", INT2FIX(PGRES_COPY_BOTH));
507
505
  /* Result#result_status constant - Single tuple from larger resultset. */
508
506
  rb_define_const(rb_mPGconstants, "PGRES_SINGLE_TUPLE", INT2FIX(PGRES_SINGLE_TUPLE));
507
+ #ifdef LIBPQ_HAS_CHUNK_MODE
508
+ /* Result#result_status constant - tuple chunk from larger resultset. */
509
+ rb_define_const(rb_mPGconstants, "PGRES_TUPLES_CHUNK", INT2FIX(PGRES_TUPLES_CHUNK));
510
+ #endif
509
511
 
510
- #ifdef HAVE_PQENTERPIPELINEMODE
512
+ #ifdef LIBPQ_HAS_PIPELINING
511
513
  /* Result#result_status constant - The PG::Result represents a synchronization point in pipeline mode, requested by Connection#pipeline_sync.
512
514
  *
513
515
  * This status occurs only when pipeline mode has been selected. */
@@ -530,16 +532,14 @@ Init_pg_ext(void)
530
532
  */
531
533
  rb_define_const(rb_mPGconstants, "PG_DIAG_SEVERITY", INT2FIX(PG_DIAG_SEVERITY));
532
534
 
533
- #ifdef PG_DIAG_SEVERITY_NONLOCALIZED
534
535
  /* Result#result_error_field argument constant
535
536
  *
536
537
  * The severity; the field contents are ERROR, FATAL, or PANIC (in an error message), or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message).
537
538
  * This is identical to the PG_DIAG_SEVERITY field except that the contents are never localized.
538
539
  *
539
- * Available since PostgreSQL-9.6
540
540
  */
541
541
  rb_define_const(rb_mPGconstants, "PG_DIAG_SEVERITY_NONLOCALIZED", INT2FIX(PG_DIAG_SEVERITY_NONLOCALIZED));
542
- #endif
542
+
543
543
  /* Result#result_error_field argument constant
544
544
  *
545
545
  * The SQLSTATE code for the error.
@@ -639,7 +639,7 @@ Init_pg_ext(void)
639
639
  rb_define_const(rb_mPGconstants, "PG_DIAG_CONSTRAINT_NAME", INT2FIX(PG_DIAG_CONSTRAINT_NAME));
640
640
  #endif
641
641
 
642
- #ifdef HAVE_PQENTERPIPELINEMODE
642
+ #ifdef LIBPQ_HAS_PIPELINING
643
643
  /* Connection#pipeline_status constant
644
644
  *
645
645
  * The libpq connection is in pipeline mode.
@@ -667,6 +667,12 @@ Init_pg_ext(void)
667
667
  /* PostgreSQL compiled in default port */
668
668
  rb_define_const(rb_mPGconstants, "DEF_PGPORT", INT2FIX(DEF_PGPORT));
669
669
 
670
+ #ifdef PG_IS_BINARY_GEM
671
+ rb_define_const(rb_mPG, "IS_BINARY_GEM", Qtrue);
672
+ #else
673
+ rb_define_const(rb_mPG, "IS_BINARY_GEM", Qfalse);
674
+ #endif
675
+
670
676
  /* Add the constants to the toplevel namespace */
671
677
  rb_include_module( rb_mPG, rb_mPGconstants );
672
678
 
@@ -689,4 +695,5 @@ Init_pg_ext(void)
689
695
  init_pg_copycoder();
690
696
  init_pg_recordcoder();
691
697
  init_pg_tuple();
698
+ init_pg_cancon();
692
699
  }
data/ext/pg.h CHANGED
@@ -76,21 +76,14 @@ typedef long suseconds_t;
76
76
  #define PG_MAX_COLUMNS 4000
77
77
  #endif
78
78
 
79
- #ifdef HAVE_RB_GC_MARK_MOVABLE
80
- #define pg_compact_callback(x) (x)
81
- #define pg_gc_location(x) x = rb_gc_location(x)
82
- #else
83
- #define rb_gc_mark_movable(x) rb_gc_mark(x)
84
- #define pg_compact_callback(x) {(x)}
85
- #define pg_gc_location(x) UNUSED(x)
79
+ #ifndef HAVE_RB_HASH_NEW_CAPA
80
+ #define rb_hash_new_capa(x) rb_hash_new()
86
81
  #endif
87
82
 
88
- /* For compatibility with ruby < 3.0 */
89
- #ifndef RUBY_TYPED_FROZEN_SHAREABLE
90
- #define PG_RUBY_TYPED_FROZEN_SHAREABLE 0
91
- #else
92
- #define PG_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
93
- #endif
83
+ #define pg_gc_location(x) x = rb_gc_location(x)
84
+
85
+ extern int ruby_native_thread_p(void);
86
+
94
87
  #define PG_ENC_IDX_BITS 28
95
88
 
96
89
  /* The data behind each PG::Connection object */
@@ -106,6 +99,8 @@ typedef struct {
106
99
  VALUE notice_receiver;
107
100
  /* Proc object that receives notices as String objects */
108
101
  VALUE notice_processor;
102
+ /* Corresponding PG::Connection ruby object. Only used for the notice_receiver */
103
+ VALUE self;
109
104
  /* Kind of PG::TypeMap object for casting query params */
110
105
  VALUE type_map_for_queries;
111
106
  /* Kind of PG::TypeMap object for casting result values */
@@ -119,14 +114,12 @@ typedef struct {
119
114
  /* Ruby encoding index of the client/internal encoding */
120
115
  int enc_idx : PG_ENC_IDX_BITS;
121
116
  /* flags controlling Symbol/String field names */
122
- unsigned int flags : 2;
117
+ unsigned int flags : 1;
123
118
  /* enable automatic flushing of send data at the end of send_query calls */
124
119
  unsigned int flush_data : 1;
125
120
 
126
- #if defined(_WIN32)
127
121
  /* File descriptor to be used for rb_w32_unwrap_io_handle() */
128
122
  int ruby_sd;
129
- #endif
130
123
  } t_pg_connection;
131
124
 
132
125
  typedef struct pg_coder t_pg_coder;
@@ -156,7 +149,7 @@ typedef struct {
156
149
  unsigned int autoclear : 1;
157
150
 
158
151
  /* flags controlling Symbol/String field names */
159
- unsigned int flags : 2;
152
+ unsigned int flags : 1;
160
153
 
161
154
  /* Number of fields in fnames[] .
162
155
  * Set to -1 if fnames[] is not yet initialized.
@@ -166,10 +159,8 @@ typedef struct {
166
159
  /* Size of PGresult as published to ruby memory management. */
167
160
  ssize_t result_size;
168
161
 
169
- /* Prefilled tuple Hash with fnames[] as keys. */
170
- VALUE tuple_hash;
171
-
172
- /* Hash with fnames[] to field number mapping. */
162
+ /* Hash with fnames[] to field number mapping.
163
+ * Init on-demand to create PG::Tuple objects, otherwise Qnil. */
173
164
  VALUE field_map;
174
165
 
175
166
  /* List of field names as frozen String or Symbol objects.
@@ -188,9 +179,8 @@ typedef VALUE (* t_pg_typecast_result)(t_typemap *, VALUE, int, int);
188
179
  typedef t_pg_coder *(* t_pg_typecast_query_param)(t_typemap *, VALUE, int);
189
180
  typedef VALUE (* t_pg_typecast_copy_get)( t_typemap *, VALUE, int, int, int );
190
181
 
191
- #define PG_RESULT_FIELD_NAMES_MASK 0x03
182
+ #define PG_RESULT_FIELD_NAMES_MASK 0x01
192
183
  #define PG_RESULT_FIELD_NAMES_SYMBOL 0x01
193
- #define PG_RESULT_FIELD_NAMES_STATIC_SYMBOL 0x02
194
184
 
195
185
  #define PG_CODER_TIMESTAMP_DB_UTC 0x0
196
186
  #define PG_CODER_TIMESTAMP_DB_LOCAL 0x1
@@ -215,6 +205,7 @@ typedef struct {
215
205
  t_pg_coder comp;
216
206
  t_pg_coder *elem;
217
207
  int needs_quotation;
208
+ int dimensions;
218
209
  char delimiter;
219
210
  } t_pg_composite_coder;
220
211
 
@@ -312,6 +303,7 @@ void init_pg_text_decoder _(( void ));
312
303
  void init_pg_binary_encoder _(( void ));
313
304
  void init_pg_binary_decoder _(( void ));
314
305
  void init_pg_tuple _(( void ));
306
+ void init_pg_cancon _(( void ));
315
307
  VALUE lookup_error_class _(( const char * ));
316
308
  VALUE pg_bin_dec_bytea _(( t_pg_coder*, const char *, int, int, int, int ));
317
309
  VALUE pg_text_dec_string _(( t_pg_coder*, const char *, int, int, int, int ));
@@ -325,7 +317,7 @@ VALUE pg_tmbc_allocate _(( void ));
325
317
  void pg_coder_init_encoder _(( VALUE ));
326
318
  void pg_coder_init_decoder _(( VALUE ));
327
319
  void pg_coder_compact _(( void * ));
328
- char *pg_rb_str_ensure_capa _(( VALUE, long, char *, char ** ));
320
+ char *pg_rb_str_ensure_capa _(( VALUE, size_t, char *, char ** ));
329
321
 
330
322
  #define PG_RB_STR_ENSURE_CAPA( str, expand_len, curr_ptr, end_ptr ) \
331
323
  do { \
@@ -351,6 +343,13 @@ void pg_typemap_compact _(( void * ));
351
343
  PGconn *pg_get_pgconn _(( VALUE ));
352
344
  t_pg_connection *pg_get_connection _(( VALUE ));
353
345
  VALUE pgconn_block _(( int, VALUE *, VALUE ));
346
+ #ifdef __GNUC__
347
+ __attribute__((format(printf, 3, 4)))
348
+ #endif
349
+ NORETURN(void pg_raise_conn_error _(( VALUE klass, VALUE self, const char *format, ...)));
350
+ VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
351
+ void pg_unwrap_socket_io _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
352
+
354
353
 
355
354
  VALUE pg_new_result _(( PGresult *, VALUE ));
356
355
  VALUE pg_new_result_autoclear _(( PGresult *, VALUE ));
@@ -13,6 +13,7 @@
13
13
 
14
14
  VALUE rb_mPG_BinaryDecoder;
15
15
  static VALUE s_Date;
16
+ static VALUE s_Date_GREGORIAN; /* Date::GREGORIAN */
16
17
  static ID s_id_new;
17
18
 
18
19
 
@@ -117,7 +118,7 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
117
118
  /* create a buffer of the encoded length */
118
119
  VALUE out_value = rb_str_new(NULL, encoded_len);
119
120
 
120
- base64_encode( RSTRING_PTR(out_value), val, len );
121
+ rbpg_base64_encode( RSTRING_PTR(out_value), val, len );
121
122
 
122
123
  /* Is it a pure String conversion? Then we can directly send out_value to the user. */
123
124
  if( this->comp.format == 0 && dec_func == pg_text_dec_string ){
@@ -133,6 +134,155 @@ pg_bin_dec_to_base64(t_pg_coder *conv, const char *val, int len, int tuple, int
133
134
  return out_value;
134
135
  }
135
136
 
137
+ /*
138
+ * Maximum number of array subscripts (arbitrary limit)
139
+ */
140
+ #define MAXDIM 6
141
+
142
+ /*
143
+ * Document-class: PG::BinaryDecoder::Array < PG::CompositeDecoder
144
+ *
145
+ * This is a decoder class for conversion of binary array types.
146
+ *
147
+ * It returns an Array with possibly an arbitrary number of sub-Arrays.
148
+ * All values are decoded according to the #elements_type accessor.
149
+ * Sub-arrays are decoded recursively.
150
+ *
151
+ * This decoder simply ignores any dimension decorations preceding the array values.
152
+ * It returns all array values as regular ruby Array with a zero based index, regardless of the index given in the dimension decoration.
153
+ *
154
+ * An array decoder which respects dimension decorations is waiting to be implemented.
155
+ *
156
+ */
157
+ static VALUE
158
+ pg_bin_dec_array(t_pg_coder *conv, const char *input_line, int len, int tuple, int field, int enc_idx)
159
+ {
160
+ t_pg_composite_coder *this = (t_pg_composite_coder *)conv;
161
+ t_pg_coder_dec_func dec_func = pg_coder_dec_func(this->elem, this->comp.format);
162
+
163
+ /* Current field */
164
+ VALUE field_str;
165
+
166
+ int32_t nitems32;
167
+ int i;
168
+ int ndim;
169
+ int nitems;
170
+ int flags;
171
+ int dim;
172
+ int dim_sizes[MAXDIM];
173
+ VALUE arrays[MAXDIM];
174
+ char *output_ptr;
175
+ const char *cur_ptr;
176
+ const char *line_end_ptr;
177
+ char *end_capa_ptr;
178
+
179
+ /* Allocate a new string with embedded capacity and realloc later with
180
+ * exponential growing size when needed. */
181
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
182
+
183
+ /* set pointer variables for loop */
184
+ cur_ptr = input_line;
185
+ line_end_ptr = input_line + len;
186
+
187
+ /* read number of dimensions */
188
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
189
+ ndim = read_nbo32(cur_ptr);
190
+ if (ndim < 0 || ndim > MAXDIM) {
191
+ rb_raise( rb_eArgError, "unsupported number of array dimensions: %d", ndim );
192
+ }
193
+ cur_ptr += 4;
194
+
195
+ /* read flags */
196
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
197
+ flags = read_nbo32(cur_ptr);
198
+ if (flags != 0 && flags != 1) {
199
+ rb_raise( rb_eArgError, "unsupported binary array flags: %d", flags );
200
+ }
201
+ cur_ptr += 4;
202
+
203
+ /* ignore element OID */
204
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
205
+ cur_ptr += 4;
206
+
207
+ nitems32 = ndim == 0 ? 0 : 1;
208
+ for (i = 0; i < ndim; i++) {
209
+ int64_t prod;
210
+
211
+ /* read size of dimensions and ignore lower bound */
212
+ if (line_end_ptr - cur_ptr < 8 ) goto length_error;
213
+ dim_sizes[i] = read_nbo32(cur_ptr);
214
+ prod = (int64_t) nitems32 * (int64_t) dim_sizes[i];
215
+ nitems32 = (int32_t) prod;
216
+ if (dim_sizes[i] < 0 || (int64_t) nitems32 != prod) {
217
+ rb_raise( rb_eArgError, "unsupported array size: %" PRId64, prod );
218
+ }
219
+ cur_ptr += 8;
220
+ }
221
+ nitems = (int)nitems32;
222
+ if (nitems > (line_end_ptr - cur_ptr) / 4) goto length_error;
223
+
224
+ dim = 0;
225
+ arrays[dim] = rb_ary_new2(ndim == 0 ? 0 : dim_sizes[dim]);
226
+ for (i = 0; i < nitems; i++) {
227
+ int input_len;
228
+
229
+ /* traverse dimensions down */
230
+ while (dim < ndim - 1) {
231
+ dim++;
232
+ arrays[dim] = rb_ary_new2(dim_sizes[dim]);
233
+ rb_ary_push(arrays[dim - 1], arrays[dim]);
234
+ }
235
+
236
+ /* read element length */
237
+ if (line_end_ptr - cur_ptr < 4 ) goto length_error;
238
+ input_len = read_nbo32(cur_ptr);
239
+ cur_ptr += 4;
240
+
241
+ /* convert and put element into array */
242
+ if (input_len < 0) {
243
+ if (input_len != -1) goto length_error;
244
+ /* NULL indicator */
245
+ rb_ary_push(arrays[dim], Qnil);
246
+ } else {
247
+ VALUE field_value;
248
+ if (line_end_ptr - cur_ptr < input_len ) goto length_error;
249
+
250
+ /* copy input data to field_str */
251
+ PG_RB_STR_ENSURE_CAPA( field_str, input_len, output_ptr, end_capa_ptr );
252
+ memcpy(output_ptr, cur_ptr, input_len);
253
+ cur_ptr += input_len;
254
+ output_ptr += input_len;
255
+ /* convert field_str through the type map */
256
+ rb_str_set_len( field_str, output_ptr - RSTRING_PTR(field_str) );
257
+ field_value = dec_func(this->elem, RSTRING_PTR(field_str), input_len, tuple, field, enc_idx);
258
+
259
+ rb_ary_push(arrays[dim], field_value);
260
+
261
+ if( field_value == field_str ){
262
+ /* Our output string will be send to the user, so we can not reuse
263
+ * it for the next field. */
264
+ PG_RB_STR_NEW( field_str, output_ptr, end_capa_ptr );
265
+ }
266
+ }
267
+
268
+ /* Reset the pointer to the start of the output/buffer string. */
269
+ output_ptr = RSTRING_PTR(field_str);
270
+
271
+ /* traverse dimensions up */
272
+ while (RARRAY_LEN(arrays[dim]) >= dim_sizes[dim] && dim > 0) {
273
+ dim--;
274
+ }
275
+ }
276
+
277
+ if (cur_ptr < line_end_ptr)
278
+ rb_raise( rb_eArgError, "trailing data after binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
279
+
280
+ return arrays[0];
281
+
282
+ length_error:
283
+ rb_raise( rb_eArgError, "premature end of binary array data at position: %ld", (long)(cur_ptr - input_line) + 1 );
284
+ }
285
+
136
286
  #define PG_INT64_MIN (-0x7FFFFFFFFFFFFFFFL - 1)
137
287
  #define PG_INT64_MAX 0x7FFFFFFFFFFFFFFFL
138
288
 
@@ -255,7 +405,7 @@ pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field
255
405
  default:
256
406
  j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
257
407
 
258
- return rb_funcall(s_Date, s_id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
408
+ return rb_funcall(s_Date, s_id_new, 4, INT2NUM(year), INT2NUM(month), INT2NUM(day), s_Date_GREGORIAN);
259
409
  }
260
410
  }
261
411
 
@@ -264,8 +414,10 @@ static VALUE
264
414
  init_pg_bin_decoder_date(VALUE rb_mPG_BinaryDecoder)
265
415
  {
266
416
  rb_require("date");
417
+ rb_gc_register_address(&s_Date);
418
+ rb_gc_register_address(&s_Date_GREGORIAN);
267
419
  s_Date = rb_const_get(rb_cObject, rb_intern("Date"));
268
- rb_gc_register_mark_object(s_Date);
420
+ s_Date_GREGORIAN = rb_const_get(s_Date, rb_intern("GREGORIAN"));
269
421
  s_id_new = rb_intern("new");
270
422
 
271
423
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Date", rb_cPG_SimpleDecoder ); */
@@ -305,6 +457,8 @@ init_pg_binary_decoder(void)
305
457
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
306
458
  pg_define_coder( "Timestamp", pg_bin_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_BinaryDecoder );
307
459
 
460
+ /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Array", rb_cPG_CompositeDecoder ); */
461
+ pg_define_coder( "Array", pg_bin_dec_array, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
308
462
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "ToBase64", rb_cPG_CompositeDecoder ); */
309
463
  pg_define_coder( "ToBase64", pg_bin_dec_to_base64, rb_cPG_CompositeDecoder, rb_mPG_BinaryDecoder );
310
464
  }