pg 1.6.2 → 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 (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +61 -0
  4. data/Gemfile +5 -4
  5. data/README.ja.md +81 -30
  6. data/README.md +20 -10
  7. data/Rakefile +13 -26
  8. data/certs/larskanis-2026.pem +24 -0
  9. data/ext/errorcodes.def +9 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +8 -1
  12. data/ext/extconf.rb +19 -28
  13. data/ext/gvl_wrappers.c +2 -2
  14. data/ext/gvl_wrappers.h +40 -160
  15. data/ext/pg.c +8 -12
  16. data/ext/pg.h +14 -15
  17. data/ext/pg_binary_decoder.c +6 -2
  18. data/ext/pg_binary_encoder.c +7 -0
  19. data/ext/pg_cancel_connection.c +6 -6
  20. data/ext/pg_coder.c +4 -4
  21. data/ext/pg_connection.c +218 -161
  22. data/ext/pg_copy_coder.c +34 -21
  23. data/ext/pg_record_coder.c +10 -6
  24. data/ext/pg_result.c +131 -61
  25. data/ext/pg_text_decoder.c +6 -6
  26. data/ext/pg_text_encoder.c +7 -2
  27. data/ext/pg_tuple.c +2 -2
  28. data/ext/pg_type_map.c +41 -1
  29. data/ext/pg_type_map_all_strings.c +2 -2
  30. data/ext/pg_type_map_by_class.c +1 -1
  31. data/ext/pg_type_map_by_column.c +2 -2
  32. data/ext/pg_type_map_by_oid.c +1 -1
  33. data/ext/pg_type_map_in_ruby.c +1 -1
  34. data/lib/pg/basic_type_map_based_on_result.rb +5 -0
  35. data/lib/pg/basic_type_map_for_queries.rb +9 -2
  36. data/lib/pg/basic_type_map_for_results.rb +5 -0
  37. data/lib/pg/connection.rb +166 -76
  38. data/lib/pg/text_decoder/date.rb +1 -1
  39. data/lib/pg/text_decoder/json.rb +1 -1
  40. data/lib/pg/text_encoder/date.rb +7 -1
  41. data/lib/pg/text_encoder/json.rb +1 -1
  42. data/lib/pg/version.rb +1 -1
  43. data/misc/yugabyte/docker-compose.yml +1 -1
  44. data/pg.gemspec +1 -1
  45. data/sample/async_mixed.rb +1 -1
  46. data.tar.gz.sig +0 -0
  47. metadata +19 -20
  48. metadata.gz.sig +0 -0
  49. data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +0 -21
  50. data/ports/patches/postgresql/17.6/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +0 -52
  51. /data/ports/patches/krb5/{1.22.1 → 1.22.2}/0001-Allow-static-linking-krb5-library.patch +0 -0
  52. /data/ports/patches/krb5/{1.22.1 → 1.22.2}/0002-unknown-command-line-option-on-clang.patch +0 -0
  53. /data/ports/patches/postgresql/{17.6 → 18.6}/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +0 -0
data/ext/extconf.rb CHANGED
@@ -27,14 +27,14 @@ if gem_platform=with_config("cross-build")
27
27
  gem 'mini_portile2', '~>2.1'
28
28
  require 'mini_portile2'
29
29
 
30
- OPENSSL_VERSION = ENV['OPENSSL_VERSION'] || '3.5.2'
31
- OPENSSL_SOURCE_URI = "http://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
30
+ OPENSSL_VERSION = ENV['OPENSSL_VERSION'] || '3.6.5'
31
+ OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
32
32
 
33
- KRB5_VERSION = ENV['KRB5_VERSION'] || '1.22.1'
34
- KRB5_SOURCE_URI = "http://kerberos.org/dist/krb5/#{KRB5_VERSION[/^(\d+\.\d+)/]}/krb5-#{KRB5_VERSION}.tar.gz"
33
+ KRB5_VERSION = ENV['KRB5_VERSION'] || '1.22.2'
34
+ KRB5_SOURCE_URI = "https://kerberos.org/dist/krb5/#{KRB5_VERSION[/^(\d+\.\d+)/]}/krb5-#{KRB5_VERSION}.tar.gz"
35
35
 
36
- POSTGRESQL_VERSION = ENV['POSTGRESQL_VERSION'] || '17.6'
37
- POSTGRESQL_SOURCE_URI = "http://ftp.postgresql.org/pub/source/v#{POSTGRESQL_VERSION}/postgresql-#{POSTGRESQL_VERSION}.tar.bz2"
36
+ POSTGRESQL_VERSION = ENV['POSTGRESQL_VERSION'] || '18.6'
37
+ POSTGRESQL_SOURCE_URI = "https://ftp.postgresql.org/pub/source/v#{POSTGRESQL_VERSION}/postgresql-#{POSTGRESQL_VERSION}.tar.bz2"
38
38
 
39
39
  class BuildRecipe < MiniPortile
40
40
  def initialize(name, version, files)
@@ -64,6 +64,12 @@ if gem_platform=with_config("cross-build")
64
64
  self.activate
65
65
  self
66
66
  end
67
+
68
+ # Increase number of download retries; otherwise it fails often in CI
69
+ alias download_file_count_3 download_file
70
+ def download_file(url, full_path, count = 60)
71
+ download_file_count_3(url, full_path, count)
72
+ end
67
73
  end
68
74
 
69
75
  openssl_platform = with_config("openssl-platform")
@@ -76,7 +82,7 @@ if gem_platform=with_config("cross-build")
76
82
  envs = []
77
83
  envs << "CFLAGS=-DDSO_WIN32 -DOPENSSL_THREADS" if RUBY_PLATFORM =~ /mingw|mswin/
78
84
  envs << "CFLAGS=-fPIC -DOPENSSL_THREADS" if RUBY_PLATFORM =~ /linux|darwin/
79
- execute('configure', ['env', *envs, "./Configure", openssl_platform, "threads", "-static", "CROSS_COMPILE=#{host}-", "--prefix=/"], altlog: "config.log")
85
+ execute('configure', ['env', *envs, "./Configure", openssl_platform, "threads", "-static", "CROSS_COMPILE=#{host}-", "--prefix=/", "--libdir=lib"], altlog: "config.log")
80
86
  end
81
87
  def compile
82
88
  execute('compile', "#{make_cmd} build_libs")
@@ -92,7 +98,7 @@ if gem_platform=with_config("cross-build")
92
98
  end
93
99
 
94
100
  if RUBY_PLATFORM =~ /linux|darwin/
95
- krb5_recipe = BuildRecipe.new("krb5", KRB5_VERSION, [KRB5_SOURCE_URI]).tap do |recipe|
101
+ BuildRecipe.new("krb5", KRB5_VERSION, [KRB5_SOURCE_URI]).tap do |recipe|
96
102
  class << recipe
97
103
  def work_path
98
104
  File.join(super, "src")
@@ -170,7 +176,7 @@ if gem_platform=with_config("cross-build")
170
176
 
171
177
  recipe.host = toolchain
172
178
  recipe.configure_options << "CFLAGS=#{" -fPIC" if RUBY_PLATFORM =~ /linux|darwin/}"
173
- recipe.configure_options << "LDFLAGS=-L#{openssl_recipe.path}/lib -L#{openssl_recipe.path}/lib64 -L#{openssl_recipe.path}/lib-arm64 #{"-Wl,-soname,#{libpq_rubypg} -lgssapi_krb5 -lkrb5 -lk5crypto -lkrb5support -ldl" if RUBY_PLATFORM =~ /linux/} #{"-Wl,-install_name,@loader_path/../../ports/#{gem_platform}/lib/#{libpq_rubypg} -lgssapi_krb5 -lkrb5 -lk5crypto -lkrb5support -lresolv -framework Kerberos" if RUBY_PLATFORM =~ /darwin/}"
179
+ recipe.configure_options << "LDFLAGS=-L#{openssl_recipe.path}/lib #{"-Wl,-soname,#{libpq_rubypg} -lgssapi_krb5 -lkrb5 -lk5crypto -lkrb5support -ldl" if RUBY_PLATFORM =~ /linux/} #{"-Wl,-install_name,@loader_path/../../ports/#{gem_platform}/lib/#{libpq_rubypg} -lgssapi_krb5 -lkrb5 -lk5crypto -lkrb5support -lresolv -framework Kerberos" if RUBY_PLATFORM =~ /darwin/}"
174
180
  recipe.configure_options << "LIBS=-lkrb5 -lcom_err -lk5crypto -lkrb5support -lresolv" if RUBY_PLATFORM =~ /linux/
175
181
  recipe.configure_options << "LIBS=-lssl -lwsock32 -lgdi32 -lws2_32 -lcrypt32" if RUBY_PLATFORM =~ /mingw|mswin/
176
182
  recipe.configure_options << "CPPFLAGS=-I#{openssl_recipe.path}/include"
@@ -185,8 +191,6 @@ if gem_platform=with_config("cross-build")
185
191
  $LDFLAGS << " -L#{postgresql_recipe.path}/lib"
186
192
  # Avoid dependency to external libgcc.dll on x86-mingw32
187
193
  $LDFLAGS << " -static-libgcc" if RUBY_PLATFORM =~ /mingw|mswin/
188
- # Avoid: "libpq.so: undefined reference to `dlopen'" in cross-ruby-2.7.8
189
- $LDFLAGS << " -Wl,--no-as-needed" if RUBY_PLATFORM !~ /aarch64|arm64|darwin/
190
194
  # Find libpq in the ports directory coming from lib/3.x
191
195
  # It is shared between all compiled ruby versions.
192
196
  $LDFLAGS << " '-Wl,-rpath=$$ORIGIN/../../ports/#{gem_platform}/lib'" if RUBY_PLATFORM =~ /linux/
@@ -218,13 +222,6 @@ else
218
222
  dlldir = libdir
219
223
  end
220
224
 
221
- # Try to use runtime path linker option, even if RbConfig doesn't know about it.
222
- # The rpath option is usually set implicit by dir_config(), but so far not
223
- # on MacOS-X.
224
- if dlldir && RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
225
- append_ldflags "-Wl,-rpath,#{dlldir.quote}"
226
- end
227
-
228
225
  if /mswin/ =~ RUBY_PLATFORM
229
226
  $libs = append_library($libs, 'ws2_32')
230
227
  end
@@ -307,21 +304,15 @@ if /mingw/ =~ RUBY_PLATFORM && RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
307
304
  end
308
305
 
309
306
  have_func 'PQencryptPasswordConn', 'libpq-fe.h' or # since PostgreSQL-10
310
- abort "Your PostgreSQL is too old. Either install an older version " +
311
- "of this gem or upgrade your database to at least PostgreSQL-10."
307
+ abort "Your PostgreSQL client library (libpq) is too old. " +
308
+ "Either install an older version of this gem " +
309
+ "or upgrade your database to at least PostgreSQL-10."
312
310
  # optional headers/functions
313
311
  have_func 'PQresultMemorySize', 'libpq-fe.h' # since PostgreSQL-12
314
- have_func 'PQenterPipelineMode', 'libpq-fe.h' do |src| # since PostgreSQL-14
315
- # Ensure header files fit as well
316
- src + " int con(){ return PGRES_PIPELINE_SYNC; }"
317
- end
318
- have_func 'PQsetChunkedRowsMode', 'libpq-fe.h' # since PostgreSQL-17
319
312
  have_func 'timegm'
320
- have_func 'rb_io_wait' # since ruby-3.0
321
- have_func 'rb_io_descriptor' # since ruby-3.1
313
+ have_func 'rb_hash_new_capa' # since ruby-3.2
322
314
 
323
315
  have_header 'inttypes.h'
324
- have_header('ruby/fiber/scheduler.h') if RUBY_PLATFORM=~/mingw|mswin/
325
316
 
326
317
  checking_for "C99 variable length arrays" do
327
318
  $defs.push( "-DHAVE_VARIABLE_LENGTH_ARRAYS" ) if try_compile('void test_vla(int l){ int vla[l]; }')
data/ext/gvl_wrappers.c CHANGED
@@ -6,7 +6,7 @@
6
6
  #include "pg.h"
7
7
 
8
8
 
9
- #ifndef HAVE_PQSETCHUNKEDROWSMODE
9
+ #ifndef LIBPQ_HAS_CHUNK_MODE
10
10
  PGresult *PQclosePrepared(PGconn *conn, const char *stmtName){return NULL;}
11
11
  PGresult *PQclosePortal(PGconn *conn, const char *portalName){return NULL;}
12
12
  int PQsendClosePrepared(PGconn *conn, const char *stmtName){return 0;}
@@ -16,7 +16,7 @@ int PQcancelBlocking(PGcancelConn *cancelConn){return 0;}
16
16
  int PQcancelStart(PGcancelConn *cancelConn){return 0;}
17
17
  PostgresPollingStatusType PQcancelPoll(PGcancelConn *cancelConn){return PGRES_POLLING_FAILED;}
18
18
  #endif
19
- #ifndef HAVE_PQENTERPIPELINEMODE
19
+ #ifndef LIBPQ_HAS_PIPELINING
20
20
  int PQpipelineSync(PGconn *conn){return 0;}
21
21
  #endif
22
22
 
data/ext/gvl_wrappers.h CHANGED
@@ -15,13 +15,18 @@
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
 
24
- #ifndef HAVE_PQSETCHUNKEDROWSMODE
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
25
30
  typedef struct pg_cancel_conn PGcancelConn;
26
31
  #endif
27
32
 
@@ -83,20 +88,35 @@ typedef struct pg_cancel_conn PGcancelConn;
83
88
  }
84
89
 
85
90
  #ifdef ENABLE_GVL_UNLOCK
86
- #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
87
- rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
88
- struct gvl_wrapper_##name##_params params = { \
89
- {FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
90
- }; \
91
- rb_thread_call_with_gvl(gvl_##name##_skeleton, &params); \
92
- when_non_void( return params.retval; ) \
93
- }
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
94
114
  #else
95
- #define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
96
- rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
97
- when_non_void( return ) \
98
- name( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
99
- }
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
+ }
100
120
  #endif
101
121
 
102
122
  #define GVL_TYPE_VOID(string)
@@ -105,174 +125,34 @@ typedef struct pg_cancel_conn PGcancelConn;
105
125
 
106
126
  /*
107
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 .
108
134
  */
109
135
 
110
- #define FOR_EACH_PARAM_OF_PQconnectdb(param)
111
-
112
136
  #define FOR_EACH_PARAM_OF_PQconnectStart(param)
113
-
114
137
  #define FOR_EACH_PARAM_OF_PQconnectPoll(param)
115
138
 
116
- #define FOR_EACH_PARAM_OF_PQreset(param)
117
-
118
139
  #define FOR_EACH_PARAM_OF_PQresetStart(param)
119
-
120
140
  #define FOR_EACH_PARAM_OF_PQresetPoll(param)
121
141
 
122
142
  #define FOR_EACH_PARAM_OF_PQping(param)
123
143
 
124
- #define FOR_EACH_PARAM_OF_PQexec(param) \
125
- param(PGconn *, conn)
126
-
127
- #define FOR_EACH_PARAM_OF_PQexecParams(param) \
128
- param(PGconn *, conn) \
129
- param(const char *, command) \
130
- param(int, nParams) \
131
- param(const Oid *, paramTypes) \
132
- param(const char * const *, paramValues) \
133
- param(const int *, paramLengths) \
134
- param(const int *, paramFormats)
135
-
136
- #define FOR_EACH_PARAM_OF_PQexecPrepared(param) \
137
- param(PGconn *, conn) \
138
- param(const char *, stmtName) \
139
- param(int, nParams) \
140
- param(const char * const *, paramValues) \
141
- param(const int *, paramLengths) \
142
- param(const int *, paramFormats)
143
-
144
- #define FOR_EACH_PARAM_OF_PQprepare(param) \
145
- param(PGconn *, conn) \
146
- param(const char *, stmtName) \
147
- param(const char *, query) \
148
- param(int, nParams)
149
-
150
- #define FOR_EACH_PARAM_OF_PQdescribePrepared(param) \
151
- param(PGconn *, conn)
152
-
153
- #define FOR_EACH_PARAM_OF_PQdescribePortal(param) \
154
- param(PGconn *, conn)
155
-
156
- #define FOR_EACH_PARAM_OF_PQclosePrepared(param) \
157
- param(PGconn *, conn)
158
-
159
- #define FOR_EACH_PARAM_OF_PQclosePortal(param) \
160
- param(PGconn *, conn)
161
-
162
- #define FOR_EACH_PARAM_OF_PQgetResult(param)
163
-
164
- #define FOR_EACH_PARAM_OF_PQputCopyData(param) \
165
- param(PGconn *, conn) \
166
- param(const char *, buffer)
167
-
168
- #define FOR_EACH_PARAM_OF_PQputCopyEnd(param) \
169
- param(PGconn *, conn)
170
-
171
- #define FOR_EACH_PARAM_OF_PQgetCopyData(param) \
172
- param(PGconn *, conn) \
173
- param(char **, buffer)
174
-
175
- #define FOR_EACH_PARAM_OF_PQnotifies(param)
176
-
177
- #define FOR_EACH_PARAM_OF_PQsendQuery(param) \
178
- param(PGconn *, conn)
179
-
180
- #define FOR_EACH_PARAM_OF_PQsendQueryParams(param) \
181
- param(PGconn *, conn) \
182
- param(const char *, command) \
183
- param(int, nParams) \
184
- param(const Oid *, paramTypes) \
185
- param(const char *const *, paramValues) \
186
- param(const int *, paramLengths) \
187
- param(const int *, paramFormats)
188
-
189
- #define FOR_EACH_PARAM_OF_PQsendPrepare(param) \
190
- param(PGconn *, conn) \
191
- param(const char *, stmtName) \
192
- param(const char *, query) \
193
- param(int, nParams)
194
-
195
- #define FOR_EACH_PARAM_OF_PQsendQueryPrepared(param) \
196
- param(PGconn *, conn) \
197
- param(const char *, stmtName) \
198
- param(int, nParams) \
199
- param(const char *const *, paramValues) \
200
- param(const int *, paramLengths) \
201
- param(const int *, paramFormats)
202
-
203
- #define FOR_EACH_PARAM_OF_PQsendDescribePrepared(param) \
204
- param(PGconn *, conn)
205
-
206
- #define FOR_EACH_PARAM_OF_PQsendDescribePortal(param) \
207
- param(PGconn *, conn)
208
-
209
- #define FOR_EACH_PARAM_OF_PQsendClosePrepared(param) \
210
- param(PGconn *, conn)
211
-
212
- #define FOR_EACH_PARAM_OF_PQsendClosePortal(param) \
213
- param(PGconn *, conn)
214
-
215
- #define FOR_EACH_PARAM_OF_PQpipelineSync(param)
216
-
217
- #define FOR_EACH_PARAM_OF_PQsendPipelineSync(param)
218
-
219
- #define FOR_EACH_PARAM_OF_PQsetClientEncoding(param) \
220
- param(PGconn *, conn)
221
-
222
- #define FOR_EACH_PARAM_OF_PQisBusy(param)
223
-
224
- #define FOR_EACH_PARAM_OF_PQcancelBlocking(param)
225
144
  #define FOR_EACH_PARAM_OF_PQcancelStart(param)
226
145
  #define FOR_EACH_PARAM_OF_PQcancelPoll(param)
227
146
 
228
- #define FOR_EACH_PARAM_OF_PQencryptPasswordConn(param) \
229
- param(PGconn *, conn) \
230
- param(const char *, passwd) \
231
- param(const char *, user)
232
-
233
- #define FOR_EACH_PARAM_OF_PQcancel(param) \
234
- param(PGcancel *, cancel) \
235
- param(char *, errbuf)
236
-
237
147
  /* function( name, void_or_nonvoid, returntype, lastparamtype, lastparamname ) */
238
148
  #define FOR_EACH_BLOCKING_FUNCTION(function) \
239
- function(PQconnectdb, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
240
149
  function(PQconnectStart, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
241
150
  function(PQconnectPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
242
- function(PQreset, GVL_TYPE_VOID, void, PGconn *, conn) \
243
151
  function(PQresetStart, GVL_TYPE_NONVOID, int, PGconn *, conn) \
244
152
  function(PQresetPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
245
153
  function(PQping, GVL_TYPE_NONVOID, PGPing, const char *, conninfo) \
246
- function(PQexec, GVL_TYPE_NONVOID, PGresult *, const char *, command) \
247
- function(PQexecParams, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
248
- function(PQexecPrepared, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
249
- function(PQprepare, GVL_TYPE_NONVOID, PGresult *, const Oid *, paramTypes) \
250
- function(PQdescribePrepared, GVL_TYPE_NONVOID, PGresult *, const char *, stmtName) \
251
- function(PQdescribePortal, GVL_TYPE_NONVOID, PGresult *, const char *, portalName) \
252
- function(PQclosePrepared, GVL_TYPE_NONVOID, PGresult *, const char *, stmtName) \
253
- function(PQclosePortal, GVL_TYPE_NONVOID, PGresult *, const char *, portalName) \
254
- function(PQgetResult, GVL_TYPE_NONVOID, PGresult *, PGconn *, conn) \
255
- function(PQputCopyData, GVL_TYPE_NONVOID, int, int, nbytes) \
256
- function(PQputCopyEnd, GVL_TYPE_NONVOID, int, const char *, errormsg) \
257
- function(PQgetCopyData, GVL_TYPE_NONVOID, int, int, async) \
258
- function(PQnotifies, GVL_TYPE_NONVOID, PGnotify *, PGconn *, conn) \
259
- function(PQsendQuery, GVL_TYPE_NONVOID, int, const char *, query) \
260
- function(PQsendQueryParams, GVL_TYPE_NONVOID, int, int, resultFormat) \
261
- function(PQsendPrepare, GVL_TYPE_NONVOID, int, const Oid *, paramTypes) \
262
- function(PQsendQueryPrepared, GVL_TYPE_NONVOID, int, int, resultFormat) \
263
- function(PQsendDescribePrepared, GVL_TYPE_NONVOID, int, const char *, stmt) \
264
- function(PQsendDescribePortal, GVL_TYPE_NONVOID, int, const char *, portal) \
265
- function(PQsendClosePrepared, GVL_TYPE_NONVOID, int, const char *, stmt) \
266
- function(PQsendClosePortal, GVL_TYPE_NONVOID, int, const char *, portal) \
267
- function(PQpipelineSync, GVL_TYPE_NONVOID, int, PGconn *, conn) \
268
- function(PQsendPipelineSync, GVL_TYPE_NONVOID, int, PGconn *, conn) \
269
- function(PQsetClientEncoding, GVL_TYPE_NONVOID, int, const char *, encoding) \
270
- function(PQisBusy, GVL_TYPE_NONVOID, int, PGconn *, conn) \
271
- function(PQcancelBlocking, GVL_TYPE_NONVOID, int, PGcancelConn *, conn) \
272
154
  function(PQcancelStart, GVL_TYPE_NONVOID, int, PGcancelConn *, conn) \
273
155
  function(PQcancelPoll, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGcancelConn *, conn) \
274
- function(PQencryptPasswordConn, GVL_TYPE_NONVOID, char *, const char *, algorithm) \
275
- function(PQcancel, GVL_TYPE_NONVOID, int, int, errbufsize);
276
156
 
277
157
  FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB_DECL );
278
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));
@@ -508,12 +504,12 @@ Init_pg_ext(void)
508
504
  rb_define_const(rb_mPGconstants, "PGRES_COPY_BOTH", INT2FIX(PGRES_COPY_BOTH));
509
505
  /* Result#result_status constant - Single tuple from larger resultset. */
510
506
  rb_define_const(rb_mPGconstants, "PGRES_SINGLE_TUPLE", INT2FIX(PGRES_SINGLE_TUPLE));
511
- #ifdef HAVE_PQSETCHUNKEDROWSMODE
507
+ #ifdef LIBPQ_HAS_CHUNK_MODE
512
508
  /* Result#result_status constant - tuple chunk from larger resultset. */
513
509
  rb_define_const(rb_mPGconstants, "PGRES_TUPLES_CHUNK", INT2FIX(PGRES_TUPLES_CHUNK));
514
510
  #endif
515
511
 
516
- #ifdef HAVE_PQENTERPIPELINEMODE
512
+ #ifdef LIBPQ_HAS_PIPELINING
517
513
  /* Result#result_status constant - The PG::Result represents a synchronization point in pipeline mode, requested by Connection#pipeline_sync.
518
514
  *
519
515
  * This status occurs only when pipeline mode has been selected. */
@@ -643,7 +639,7 @@ Init_pg_ext(void)
643
639
  rb_define_const(rb_mPGconstants, "PG_DIAG_CONSTRAINT_NAME", INT2FIX(PG_DIAG_CONSTRAINT_NAME));
644
640
  #endif
645
641
 
646
- #ifdef HAVE_PQENTERPIPELINEMODE
642
+ #ifdef LIBPQ_HAS_PIPELINING
647
643
  /* Connection#pipeline_status constant
648
644
  *
649
645
  * The libpq connection is in pipeline mode.
data/ext/pg.h CHANGED
@@ -76,14 +76,14 @@ typedef long suseconds_t;
76
76
  #define PG_MAX_COLUMNS 4000
77
77
  #endif
78
78
 
79
+ #ifndef HAVE_RB_HASH_NEW_CAPA
80
+ #define rb_hash_new_capa(x) rb_hash_new()
81
+ #endif
82
+
79
83
  #define pg_gc_location(x) x = rb_gc_location(x)
80
84
 
81
- /* For compatibility with ruby < 3.0 */
82
- #ifndef RUBY_TYPED_FROZEN_SHAREABLE
83
- #define PG_RUBY_TYPED_FROZEN_SHAREABLE 0
84
- #else
85
- #define PG_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
86
- #endif
85
+ extern int ruby_native_thread_p(void);
86
+
87
87
  #define PG_ENC_IDX_BITS 28
88
88
 
89
89
  /* The data behind each PG::Connection object */
@@ -99,6 +99,8 @@ typedef struct {
99
99
  VALUE notice_receiver;
100
100
  /* Proc object that receives notices as String objects */
101
101
  VALUE notice_processor;
102
+ /* Corresponding PG::Connection ruby object. Only used for the notice_receiver */
103
+ VALUE self;
102
104
  /* Kind of PG::TypeMap object for casting query params */
103
105
  VALUE type_map_for_queries;
104
106
  /* Kind of PG::TypeMap object for casting result values */
@@ -112,7 +114,7 @@ typedef struct {
112
114
  /* Ruby encoding index of the client/internal encoding */
113
115
  int enc_idx : PG_ENC_IDX_BITS;
114
116
  /* flags controlling Symbol/String field names */
115
- unsigned int flags : 2;
117
+ unsigned int flags : 1;
116
118
  /* enable automatic flushing of send data at the end of send_query calls */
117
119
  unsigned int flush_data : 1;
118
120
 
@@ -147,7 +149,7 @@ typedef struct {
147
149
  unsigned int autoclear : 1;
148
150
 
149
151
  /* flags controlling Symbol/String field names */
150
- unsigned int flags : 2;
152
+ unsigned int flags : 1;
151
153
 
152
154
  /* Number of fields in fnames[] .
153
155
  * Set to -1 if fnames[] is not yet initialized.
@@ -157,10 +159,8 @@ typedef struct {
157
159
  /* Size of PGresult as published to ruby memory management. */
158
160
  ssize_t result_size;
159
161
 
160
- /* Prefilled tuple Hash with fnames[] as keys. */
161
- VALUE tuple_hash;
162
-
163
- /* 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. */
164
164
  VALUE field_map;
165
165
 
166
166
  /* List of field names as frozen String or Symbol objects.
@@ -179,9 +179,8 @@ typedef VALUE (* t_pg_typecast_result)(t_typemap *, VALUE, int, int);
179
179
  typedef t_pg_coder *(* t_pg_typecast_query_param)(t_typemap *, VALUE, int);
180
180
  typedef VALUE (* t_pg_typecast_copy_get)( t_typemap *, VALUE, int, int, int );
181
181
 
182
- #define PG_RESULT_FIELD_NAMES_MASK 0x03
182
+ #define PG_RESULT_FIELD_NAMES_MASK 0x01
183
183
  #define PG_RESULT_FIELD_NAMES_SYMBOL 0x01
184
- #define PG_RESULT_FIELD_NAMES_STATIC_SYMBOL 0x02
185
184
 
186
185
  #define PG_CODER_TIMESTAMP_DB_UTC 0x0
187
186
  #define PG_CODER_TIMESTAMP_DB_LOCAL 0x1
@@ -318,7 +317,7 @@ VALUE pg_tmbc_allocate _(( void ));
318
317
  void pg_coder_init_encoder _(( VALUE ));
319
318
  void pg_coder_init_decoder _(( VALUE ));
320
319
  void pg_coder_compact _(( void * ));
321
- char *pg_rb_str_ensure_capa _(( VALUE, long, char *, char ** ));
320
+ char *pg_rb_str_ensure_capa _(( VALUE, size_t, char *, char ** ));
322
321
 
323
322
  #define PG_RB_STR_ENSURE_CAPA( str, expand_len, curr_ptr, end_ptr ) \
324
323
  do { \
@@ -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
 
@@ -218,6 +219,7 @@ pg_bin_dec_array(t_pg_coder *conv, const char *input_line, int len, int tuple, i
218
219
  cur_ptr += 8;
219
220
  }
220
221
  nitems = (int)nitems32;
222
+ if (nitems > (line_end_ptr - cur_ptr) / 4) goto length_error;
221
223
 
222
224
  dim = 0;
223
225
  arrays[dim] = rb_ary_new2(ndim == 0 ? 0 : dim_sizes[dim]);
@@ -403,7 +405,7 @@ pg_bin_dec_date(t_pg_coder *conv, const char *val, int len, int tuple, int field
403
405
  default:
404
406
  j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
405
407
 
406
- 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);
407
409
  }
408
410
  }
409
411
 
@@ -412,8 +414,10 @@ static VALUE
412
414
  init_pg_bin_decoder_date(VALUE rb_mPG_BinaryDecoder)
413
415
  {
414
416
  rb_require("date");
417
+ rb_gc_register_address(&s_Date);
418
+ rb_gc_register_address(&s_Date_GREGORIAN);
415
419
  s_Date = rb_const_get(rb_cObject, rb_intern("Date"));
416
- rb_gc_register_mark_object(s_Date);
420
+ s_Date_GREGORIAN = rb_const_get(s_Date, rb_intern("GREGORIAN"));
417
421
  s_id_new = rb_intern("new");
418
422
 
419
423
  /* dummy = rb_define_class_under( rb_mPG_BinaryDecoder, "Date", rb_cPG_SimpleDecoder ); */
@@ -11,6 +11,8 @@
11
11
  #endif
12
12
 
13
13
  VALUE rb_mPG_BinaryEncoder;
14
+ static ID s_id_gregorianP;
15
+ static ID s_id_gregorian;
14
16
  static ID s_id_year;
15
17
  static ID s_id_month;
16
18
  static ID s_id_day;
@@ -272,6 +274,9 @@ pg_bin_enc_date(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, i
272
274
  write_nbo32(PG_INT32_MIN, out);
273
275
  return 4;
274
276
  } {
277
+ /* Only create a new gregorian Date object if necessary */
278
+ if( rb_funcall(value, s_id_gregorianP, 0) != Qtrue )
279
+ value = rb_funcall(value, s_id_gregorian, 0);
275
280
  VALUE year = rb_funcall(value, s_id_year, 0);
276
281
  VALUE month = rb_funcall(value, s_id_month, 0);
277
282
  VALUE day = rb_funcall(value, s_id_day, 0);
@@ -554,6 +559,8 @@ pg_bin_enc_from_base64(t_pg_coder *conv, VALUE value, char *out, VALUE *intermed
554
559
  void
555
560
  init_pg_binary_encoder(void)
556
561
  {
562
+ s_id_gregorianP = rb_intern("gregorian?");
563
+ s_id_gregorian = rb_intern("gregorian");
557
564
  s_id_year = rb_intern("year");
558
565
  s_id_month = rb_intern("month");
559
566
  s_id_day = rb_intern("day");
@@ -6,14 +6,14 @@
6
6
  *
7
7
  * The class to represent a connection to cancel a query.
8
8
  *
9
- * On PostgreSQL-17+ client libaray this class is used to implement PG::Connection#cancel .
9
+ * On PostgreSQL-17+ client library this class is used to implement PG::Connection#cancel .
10
10
  * It works on older PostgreSQL server versions too.
11
11
  *
12
12
  * Available since PostgreSQL-17
13
13
  *
14
14
  */
15
15
 
16
- #ifdef HAVE_PQSETCHUNKEDROWSMODE
16
+ #ifdef LIBPQ_HAS_CHUNK_MODE
17
17
 
18
18
  static VALUE rb_cPG_Cancon;
19
19
  static ID s_id_autoclose_set;
@@ -75,7 +75,7 @@ static const rb_data_type_t pg_cancon_type = {
75
75
  pg_cancon_gc_compact,
76
76
  },
77
77
  0, 0,
78
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
78
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
79
79
  };
80
80
 
81
81
  /*
@@ -155,7 +155,7 @@ pg_cancon_initialize(VALUE self, VALUE rb_conn)
155
155
  *
156
156
  * Requests that the server abandons processing of the current command in a blocking manner.
157
157
  *
158
- * This method directly calls +PQcancelBlocking+ of libpq, so that it doesn't respond to ruby interrupts and doesn't trigger the +Thread.scheduler+ .
158
+ * This method directly calls +PQcancelBlocking+ of libpq, so that it doesn't allow parallel threads, doesn't respond to ruby interrupts and doesn't trigger the +Thread.scheduler+ .
159
159
  * It is threrfore recommended to call #cancel instead.
160
160
  *
161
161
  */
@@ -165,7 +165,7 @@ pg_cancon_sync_cancel(VALUE self)
165
165
  PGcancelConn *conn = pg_cancon_get_conn(self);
166
166
 
167
167
  pg_cancon_close_socket_io( self );
168
- if(gvl_PQcancelBlocking(conn) == 0)
168
+ if(PQcancelBlocking(conn) == 0)
169
169
  pg_raise_conn_error( rb_eConnectionBad, self, "PQcancelBlocking %s", PQcancelErrorMessage(conn));
170
170
  return Qnil;
171
171
  }
@@ -340,7 +340,7 @@ pg_cancon_finish(VALUE self)
340
340
  void
341
341
  init_pg_cancon(void)
342
342
  {
343
- #ifdef HAVE_PQSETCHUNKEDROWSMODE
343
+ #ifdef LIBPQ_HAS_CHUNK_MODE
344
344
  s_id_autoclose_set = rb_intern("autoclose=");
345
345
 
346
346
  rb_cPG_Cancon = rb_define_class_under( rb_mPG, "CancelConnection", rb_cObject );
data/ext/pg_coder.c CHANGED
@@ -101,7 +101,7 @@ const rb_data_type_t pg_coder_type = {
101
101
  0,
102
102
  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
103
103
  // macro to update VALUE references, as to trigger write barriers.
104
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
104
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
105
105
  };
106
106
 
107
107
  static VALUE
@@ -123,7 +123,7 @@ static const rb_data_type_t pg_composite_coder_type = {
123
123
  },
124
124
  &pg_coder_type,
125
125
  0,
126
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
126
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
127
127
  };
128
128
 
129
129
  static VALUE
@@ -505,7 +505,7 @@ static const rb_data_type_t pg_coder_cfunc_type = {
505
505
  },
506
506
  0,
507
507
  0,
508
- RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
508
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
509
509
  };
510
510
 
511
511
  VALUE
@@ -631,7 +631,7 @@ init_pg_coder(void)
631
631
  /*
632
632
  * Name of the coder or the corresponding data type.
633
633
  *
634
- * This accessor is only used in PG::Coder#inspect .
634
+ * This accessor is used by PG::Coder#inspect and PG::Connection#embed_params .
635
635
  */
636
636
  rb_define_attr( rb_cPG_Coder, "name", 1, 1 );
637
637