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/Rakefile CHANGED
@@ -11,6 +11,7 @@ require 'rake/clean'
11
11
  require 'rspec/core/rake_task'
12
12
  require 'bundler'
13
13
  require 'bundler/gem_helper'
14
+ require_relative "rakelib/pg_gem_helper"
14
15
 
15
16
  # Build directory constants
16
17
  BASEDIR = Pathname( __FILE__ ).dirname
@@ -31,10 +32,10 @@ CLEAN.include( PKGDIR.to_s, TMPDIR.to_s )
31
32
  CLEAN.include "lib/*/libpq.dll"
32
33
  CLEAN.include "lib/pg_ext.*"
33
34
  CLEAN.include "lib/pg/postgresql_lib_path.rb"
35
+ CLEAN.include "ports/*.installed"
36
+ CLEAN.include "ports/*mingw*", "ports/*linux*", "ports/*darwin*"
34
37
 
35
- load 'Rakefile.cross'
36
-
37
- Bundler::GemHelper.install_tasks
38
+ PgGemHelper.install_tasks
38
39
  $gem_spec = Bundler.load_gemspec(GEMSPEC)
39
40
 
40
41
  desc "Turn on warnings and debugging in the build."
@@ -42,6 +43,23 @@ task :maint do
42
43
  ENV['MAINTAINER_MODE'] = 'yes'
43
44
  end
44
45
 
46
+ CrossLibrary = Struct.new :platform, :openssl_config, :toolchain
47
+ CrossLibraries = [
48
+ ['aarch64-mingw-ucrt', 'mingwarm64', 'aarch64-w64-mingw32'],
49
+ ['x64-mingw-ucrt', 'mingw64', 'x86_64-w64-mingw32'],
50
+ ['x86_64-linux', 'linux-x86_64', 'x86_64-linux-gnu'],
51
+ ['x86_64-linux-musl', 'linux-x86_64', 'x86_64-unknown-linux-musl'],
52
+ ['aarch64-linux', 'linux-aarch64', 'aarch64-linux-gnu'],
53
+ ['aarch64-linux-musl', 'linux-aarch64', 'aarch64-linux-musl'],
54
+ ['x86_64-darwin', 'darwin64-x86_64', 'x86_64-apple-darwin'],
55
+ ['arm64-darwin', 'darwin64-arm64', 'arm64-apple-darwin'],
56
+ ].map do |platform, openssl_config, toolchain|
57
+ CrossLibrary.new platform, openssl_config, toolchain
58
+ end
59
+
60
+ # Register binary gems to be pushed to rubygems.org
61
+ Bundler::GemHelper.instance.cross_platforms = CrossLibraries.map(&:platform)
62
+
45
63
  # Rake-compiler task
46
64
  Rake::ExtensionTask.new do |ext|
47
65
  ext.name = 'pg_ext'
@@ -50,26 +68,70 @@ Rake::ExtensionTask.new do |ext|
50
68
  ext.lib_dir = 'lib'
51
69
  ext.source_pattern = "*.{c,h}"
52
70
  ext.cross_compile = true
53
- ext.cross_platform = CrossLibraries.map(&:for_platform)
54
71
 
55
- ext.cross_config_options += CrossLibraries.map do |lib|
72
+ # Activate current cross compiled platform only.
73
+ # This is to work around the issue that `linux` platform is selected in `linux-musl` image.
74
+ ext.cross_platform = CrossLibraries.map(&:platform).select do |pl|
75
+ m = ENV["RCD_IMAGE"]&.match(/:(?<ruby_ver>[\w\.]+)-mri-(?<platform>[-\w]+)$/)
76
+ m && m[:platform] == pl
77
+ end
78
+
79
+ ext.cross_config_options += CrossLibraries.map do |xlib|
56
80
  {
57
- lib.for_platform => [
58
- "--enable-windows-cross",
59
- "--with-pg-include=#{lib.static_postgresql_incdir}",
60
- "--with-pg-lib=#{lib.static_postgresql_libdir}",
61
- # libpq-fe.h resides in src/interfaces/libpq/ before make install
62
- "--with-opt-include=#{lib.static_postgresql_libdir}",
81
+ xlib.platform => [
82
+ "--with-cross-build=#{xlib.platform}",
83
+ "--with-openssl-platform=#{xlib.openssl_config}",
84
+ "--with-toolchain=#{xlib.toolchain}",
63
85
  ]
64
86
  }
65
87
  end
66
88
 
67
- # Add libpq.dll to windows binary gemspec
89
+ # Add libpq.dll/.so to fat binary gemspecs
68
90
  ext.cross_compiling do |spec|
69
- spec.files << "lib/#{spec.platform}/libpq.dll"
91
+ spec.files << "ports/#{spec.platform.to_s}/lib/libpq-ruby-pg.so.1" if spec.platform.to_s =~ /linux/
92
+ spec.files << "ports/#{spec.platform.to_s}/lib/libpq-ruby-pg.1.dylib" if spec.platform.to_s =~ /darwin/
93
+ spec.files << "ports/#{spec.platform.to_s}/lib/libpq.dll" if spec.platform.to_s =~ /mingw|mswin/
94
+
95
+ # Binary gems don't need postgresql header+lib files
96
+ spec.metadata.delete("msys2_mingw_dependencies")
70
97
  end
71
98
  end
72
99
 
100
+ task 'gem:native:prepare' do
101
+ require 'io/console'
102
+ require 'rake_compiler_dock'
103
+
104
+ # Copy gem signing key and certs to be accessible from the docker container
105
+ mkdir_p 'build/gem'
106
+ sh "cp ~/.gem/gem-*.pem build/gem/ || true"
107
+ sh "bundle config set cache_all false"
108
+ sh "bundle package"
109
+ begin
110
+ OpenSSL::PKey.read(File.read(File.expand_path("~/.gem/gem-private_key.pem")), ENV["GEM_PRIVATE_KEY_PASSPHRASE"] || "")
111
+ rescue OpenSSL::PKey::PKeyError
112
+ ENV["GEM_PRIVATE_KEY_PASSPHRASE"] = STDIN.getpass("Enter passphrase of gem signature key: ")
113
+ retry
114
+ end
115
+ end
116
+
117
+ CrossLibraries.each do |xlib|
118
+ platform = xlib.platform
119
+ desc "Build fat binary gem for platform #{platform}"
120
+ task "gem:native:#{platform}" => ['gem:native:prepare'] do
121
+ RakeCompilerDock.sh <<-EOT, platform: platform
122
+ #{ "sudo apt-get update && sudo apt-get install -y bison flex &&" if platform =~ /darwin/ }
123
+ #{ # remove nm on Linux to suppress PostgreSQL's check for exit which raises thread_exit as a false positive:
124
+ "sudo mv `which nm` `which nm`.bak &&" if platform =~ /linux/ }
125
+ sudo apt-get update && sudo apt-get install -y bison flex &&
126
+ (cp build/gem/gem-*.pem ~/.gem/ || true) &&
127
+ bundle install --local &&
128
+ rake native:#{platform} pkg/#{$gem_spec.full_name}-#{platform}.gem MAKEFLAGS="-j`nproc` V=1" RUBY_CC_VERSION=#{RakeCompilerDock.ruby_cc_version("~>4.0", "~>3.1")}
129
+ EOT
130
+ end
131
+ desc "Build the native binary gems"
132
+ multitask 'gem:native' => "gem:native:#{platform}"
133
+ end
134
+
73
135
  RSpec::Core::RakeTask.new(:spec).rspec_opts = "--profile -cfdoc"
74
136
  task :test => :spec
75
137
 
@@ -94,7 +156,8 @@ end
94
156
 
95
157
  desc "Update list of server error codes"
96
158
  task :update_error_codes do
97
- URL_ERRORCODES_TXT = "http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;f=src/backend/utils/errcodes.txt;hb=refs/tags/REL_17_0"
159
+ # URL_ERRORCODES_TXT = "https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;f=src/backend/utils/errcodes.txt;hb=refs/tags/REL_18_0"
160
+ URL_ERRORCODES_TXT = "https://raw.githubusercontent.com/postgres/postgres/refs/tags/REL_18_0/src/backend/utils/errcodes.txt"
98
161
 
99
162
  ERRORCODES_TXT = "ext/errorcodes.txt"
100
163
  sh "wget #{URL_ERRORCODES_TXT.inspect} -O #{ERRORCODES_TXT.inspect} || curl #{URL_ERRORCODES_TXT.inspect} -o #{ERRORCODES_TXT.inspect}"
@@ -109,10 +172,12 @@ end
109
172
 
110
173
  desc "Translate readme"
111
174
  task :translate do
112
- cd "translation" do
113
- # po4a's lexer might change, so record its version for reference
114
- sh "LANG=C po4a --version > .po4a-version"
175
+ cd "translation" do
176
+ # po4a's lexer might change, so record its version for reference
177
+ sh "LANG=C po4a --version > .po4a-version"
115
178
 
116
- sh "po4a po4a.cfg"
117
- end
179
+ pot = 'po/en.pot'
180
+ File.file?(pot) or FileUtils.touch(pot)
181
+ sh "po4a po4a.cfg"
182
+ end
118
183
  end
@@ -0,0 +1,24 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEBDCCAmygAwIBAgIBBDANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1sYXJz
3
+ L0RDPWdyZWl6LXJlaW5zZG9yZi9EQz1kZTAeFw0yNjAyMTQyMTU3MTlaFw0yNzAy
4
+ MTQyMTU3MTlaMCgxJjAkBgNVBAMMHWxhcnMvREM9Z3JlaXotcmVpbnNkb3JmL0RD
5
+ PWRlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwum6Y1KznfpzXOT/
6
+ mZgJTBbxZuuZF49Fq3K0WA67YBzNlDv95qzSp7V/7Ek3NCcnT7G+2kSuhNo1FhdN
7
+ eSDO/moYebZNAcu3iqLsuzuULXPLuoU0GsMnVMqV9DZPh7cQHE5EBZ7hlzDBK7k/
8
+ 8nBMvR0mHo77kIkapHc26UzVq/G0nKLfDsIHXVylto3PjzOumjG6GhmFN4r3cP6e
9
+ SDfl1FSeRYVpt4kmQULz/zdSaOH3AjAq7PM2Z91iGwQvoUXMANH2v89OWjQO/NHe
10
+ JMNDFsmHK/6Ji4Kk48Z3TyscHQnipAID5GhS1oD21/WePdj7GhmbF5gBzkV5uepd
11
+ eJQPgWGwrQW/Z2oPjRuJrRofzWfrMWqbOahj9uth6WSxhNexUtbjk6P8emmXOJi5
12
+ chQPnWX+N3Gj+jjYxqTFdwT7Mj3pv1VHa+aNUbqSPpvJeDyxRIuo9hvzDaBHb/Cg
13
+ 9qRVcm8a96n4t7y2lrX1oookY6bkBaxWOMtWlqIprq8JZXM9AgMBAAGjOTA3MAkG
14
+ A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQ4h1tIyvdUWtMI739xMzTR
15
+ 7EfMFzANBgkqhkiG9w0BAQsFAAOCAYEAWuY62AlTOhipB443sEkhK0G09vjB3v0j
16
+ 9A2HlHXMOcgilbfbMR7ejT+/ynwdqMkrYCQCvoQH06NgIzFi2u6cKKmb2rWXMAF1
17
+ g0NLTs1h/KFsfwuX7IOQ9LxkMl9jlYWdFptFi9oQhlfgmFAFLiZJzNA0+QZMgW6A
18
+ e3usJh0/pT8E0hLWnJdomEF7pNsFyfKjbp5KWRd585z3dePi0iPcpzdByCnJsO36
19
+ Hyl/JsJZyHwpR6LlgfpXmYFwxeFZvpJg2S9O7Yo+qlgiaIUbnkVxOqfEXhCRZm8d
20
+ vfBZ6NSBwcdh7tXk01Yeqap0OtIE7Ey6C/so7uc3vZE6lPZ8xumMYyZ1lJTk5WwS
21
+ qgNbJXZWC/Q9RqPbOZXj4Q2Rq0iFfGNPlkKVeDabXfAASAVtrUb1gByb2b53Kq6N
22
+ OZ/RmpA4/XcoJIE2pnoUq8j6WCeNJ9m11JS2NmgUvYtdMFgMJWoH/hiF71l8dH3v
23
+ 76Q140MRgXCuR7RMxztucM6XZQbk1VSO
24
+ -----END CERTIFICATE-----
data/ext/errorcodes.def CHANGED
@@ -87,6 +87,11 @@
87
87
  VALUE klass = define_error_class( "StackedDiagnosticsAccessedWithoutActiveHandler", "0Z" );
88
88
  register_error_class( "0Z002", klass );
89
89
  }
90
+ {
91
+ VALUE klass = define_error_class( "InvalidArgumentForXquery", NULL );
92
+ register_error_class( "10608", klass );
93
+ register_error_class( "10", klass );
94
+ }
90
95
  {
91
96
  VALUE klass = define_error_class( "CaseNotFound", NULL );
92
97
  register_error_class( "20000", klass );
@@ -889,6 +894,10 @@
889
894
  VALUE klass = define_error_class( "DuplicateFile", "58" );
890
895
  register_error_class( "58P02", klass );
891
896
  }
897
+ {
898
+ VALUE klass = define_error_class( "FileNameTooLong", "58" );
899
+ register_error_class( "58P03", klass );
900
+ }
892
901
  {
893
902
  VALUE klass = define_error_class( "ConfigFileError", NULL );
894
903
  register_error_class( "F0000", klass );
data/ext/errorcodes.rb CHANGED
@@ -28,7 +28,7 @@ EOCODE
28
28
  sqlstate, ews, errcode_macro_name = $1, $2, $3
29
29
  next unless ews=='E'
30
30
 
31
- is_sqlclass = sqlstate[2..-1] == '000'
31
+ is_sqlclass = sqlstate[2..-1] == '000' || sqlstate == '10608'
32
32
  class_code = sqlstate[0,2]
33
33
  baseclass_code = is_sqlclass ? 'NULL' : class_code.inspect
34
34
  class_name = camelize(errcode_macro_name.sub('ERRCODE_', '').downcase)
data/ext/errorcodes.txt CHANGED
@@ -2,7 +2,7 @@
2
2
  # errcodes.txt
3
3
  # PostgreSQL error codes
4
4
  #
5
- # Copyright (c) 2003-2024, PostgreSQL Global Development Group
5
+ # Copyright (c) 2003-2025, PostgreSQL Global Development Group
6
6
  #
7
7
  # This list serves as the basis for generating source files containing error
8
8
  # codes. It is kept in a common format to make sure all these source files have
@@ -141,6 +141,12 @@ Section: Class 0Z - Diagnostics Exception
141
141
  0Z000 E ERRCODE_DIAGNOSTICS_EXCEPTION diagnostics_exception
142
142
  0Z002 E ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER stacked_diagnostics_accessed_without_active_handler
143
143
 
144
+ Section: Class 10 - XQuery Error
145
+
146
+ # recent SQL versions define quite a few codes in this class, but for now
147
+ # we are only using this generic one
148
+ 10608 E ERRCODE_INVALID_ARGUMENT_FOR_XQUERY invalid_argument_for_xquery
149
+
144
150
  Section: Class 20 - Case Not Found
145
151
 
146
152
  20000 E ERRCODE_CASE_NOT_FOUND case_not_found
@@ -439,6 +445,7 @@ Section: Class 58 - System Error (errors external to PostgreSQL itself)
439
445
  58030 E ERRCODE_IO_ERROR io_error
440
446
  58P01 E ERRCODE_UNDEFINED_FILE undefined_file
441
447
  58P02 E ERRCODE_DUPLICATE_FILE duplicate_file
448
+ 58P03 E ERRCODE_FILE_NAME_TOO_LONG file_name_too_long
442
449
 
443
450
  Section: Class F0 - Configuration File Error
444
451
 
data/ext/extconf.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'pp'
2
2
  require 'mkmf'
3
3
 
4
-
5
4
  if ENV['MAINTAINER_MODE']
6
5
  $stderr.puts "Maintainer mode enabled."
7
6
  $CFLAGS <<
@@ -24,12 +23,179 @@ else
24
23
  $stderr.puts "Calling libpq with GVL locked"
25
24
  end
26
25
 
27
- if enable_config("windows-cross")
26
+ if gem_platform=with_config("cross-build")
27
+ gem 'mini_portile2', '~>2.1'
28
+ require 'mini_portile2'
29
+
30
+ OPENSSL_VERSION = ENV['OPENSSL_VERSION'] || '3.6.5'
31
+ OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
32
+
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
+
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
+
39
+ class BuildRecipe < MiniPortile
40
+ def initialize(name, version, files)
41
+ super(name, version)
42
+ self.files = files
43
+ rootdir = File.expand_path('../..', __FILE__)
44
+ self.target = File.join(rootdir, "ports")
45
+ self.patch_files = Dir[File.join(target, "patches", self.name, self.version, "*.patch")].sort
46
+ end
47
+
48
+ def port_path
49
+ "#{target}/#{RUBY_PLATFORM}"
50
+ end
51
+
52
+ # Add "--prefix=/", to avoid our actual build install path compiled into the binary.
53
+ # Instead use DESTDIR variable of make to set our install path.
54
+ def configure_prefix
55
+ "--prefix="
56
+ end
57
+
58
+ def cook_and_activate
59
+ checkpoint = File.join(self.target, "#{self.name}-#{self.version}-#{RUBY_PLATFORM}.installed")
60
+ unless File.exist?(checkpoint)
61
+ self.cook
62
+ FileUtils.touch checkpoint
63
+ end
64
+ self.activate
65
+ self
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
73
+ end
74
+
75
+ openssl_platform = with_config("openssl-platform")
76
+ toolchain = with_config("toolchain")
77
+
78
+ openssl_recipe = BuildRecipe.new("openssl", OPENSSL_VERSION, [OPENSSL_SOURCE_URI]).tap do |recipe|
79
+ class << recipe
80
+ attr_accessor :openssl_platform
81
+ def configure
82
+ envs = []
83
+ envs << "CFLAGS=-DDSO_WIN32 -DOPENSSL_THREADS" if RUBY_PLATFORM =~ /mingw|mswin/
84
+ envs << "CFLAGS=-fPIC -DOPENSSL_THREADS" if RUBY_PLATFORM =~ /linux|darwin/
85
+ execute('configure', ['env', *envs, "./Configure", openssl_platform, "threads", "-static", "CROSS_COMPILE=#{host}-", "--prefix=/", "--libdir=lib"], altlog: "config.log")
86
+ end
87
+ def compile
88
+ execute('compile', "#{make_cmd} build_libs")
89
+ end
90
+ def install
91
+ execute('install', "#{make_cmd} install_dev DESTDIR=#{path}")
92
+ end
93
+ end
94
+
95
+ recipe.openssl_platform = openssl_platform
96
+ recipe.host = toolchain
97
+ recipe.cook_and_activate
98
+ end
99
+
100
+ if RUBY_PLATFORM =~ /linux|darwin/
101
+ BuildRecipe.new("krb5", KRB5_VERSION, [KRB5_SOURCE_URI]).tap do |recipe|
102
+ class << recipe
103
+ def work_path
104
+ File.join(super, "src")
105
+ end
106
+ def configure
107
+ if RUBY_PLATFORM=~/darwin/
108
+ ENV["CC"] = host[/^.*[^\.\d]/] + "-clang"
109
+ ENV["CXX"] = host[/^.*[^\.\d]/] + "-c++"
110
+
111
+ # Manually set the correct values for configure checks that libkrb5 won't be
112
+ # able to perform because we're cross-compiling.
113
+ ENV["krb5_cv_attr_constructor_destructor"] = "yes"
114
+ ENV["ac_cv_func_regcomp"] = "yes"
115
+ ENV["ac_cv_printf_positional"] = "yes"
116
+ end
117
+ super
118
+ end
119
+ def install
120
+ execute('install', "#{make_cmd} install DESTDIR=#{path}")
121
+ end
122
+ end
123
+ # We specify -fcommon to get around duplicate definition errors in recent gcc.
124
+ # See https://github.com/cockroachdb/cockroach/issues/49734
125
+ recipe.configure_options << "CFLAGS=-fcommon#{" -fPIC" if RUBY_PLATFORM =~ /linux/}"
126
+ recipe.configure_options << "LDFLAGS=-framework Kerberos" if RUBY_PLATFORM =~ /darwin/
127
+ recipe.configure_options << "--without-keyutils"
128
+ recipe.configure_options << "--disable-nls"
129
+ recipe.configure_options << "--disable-silent-rules"
130
+ recipe.configure_options << "--disable-rpath"
131
+ recipe.configure_options << "--without-system-verto"
132
+ recipe.configure_options << "krb5_cv_attr_constructor_destructor=yes"
133
+ recipe.configure_options << "ac_cv_func_regcomp=yes"
134
+ recipe.configure_options << "ac_cv_printf_positional=yes"
135
+ recipe.host = toolchain
136
+ recipe.cook_and_activate
137
+ end
138
+ end
139
+
140
+ # We build a libpq library file which static links OpenSSL and krb5.
141
+ # Our builtin libpq is referenced in different ways depending on the OS:
142
+ # - Window: Add the ports directory at runtime per RubyInstaller::Runtime.add_dll_directory
143
+ # The file is called "libpq.dll"
144
+ # - Linux: Add a rpath to pg_ext.so which references the ports directory.
145
+ # The file is called "libpq-ruby-pg.so.1" to avoid loading of system libpq by accident.
146
+ # - Macos: Add a reference with relative path in pg_ext.so to the ports directory.
147
+ # The file is called "libpq-ruby-pg.1.dylib" to avoid loading of other libpq by accident.
148
+ libpq_orig, libpq_rubypg = case RUBY_PLATFORM
149
+ when /linux/ then ["libpq.so.5", "libpq-ruby-pg.so.1"]
150
+ when /darwin/ then ["libpq.5.dylib", "libpq-ruby-pg.1.dylib"]
151
+ # when /mingw/ then ["libpq.dll", "libpq.dll"] # renaming not needed
152
+ end
153
+
154
+ postgresql_recipe = BuildRecipe.new("postgresql", POSTGRESQL_VERSION, [POSTGRESQL_SOURCE_URI]).tap do |recipe|
155
+ class << recipe
156
+ def configure_defaults
157
+ [
158
+ "--target=#{host}",
159
+ "--host=#{host}",
160
+ '--with-openssl',
161
+ *(RUBY_PLATFORM=~/linux|darwin/ ? ['--with-gssapi'] : []),
162
+ '--without-zlib',
163
+ '--without-icu',
164
+ '--without-readline',
165
+ '--disable-rpath',
166
+ 'ac_cv_search_gss_store_cred_into=',
167
+ ]
168
+ end
169
+ def compile
170
+ execute 'compile include', "#{make_cmd} -C src/include install DESTDIR=#{path}"
171
+ execute 'compile interfaces', "#{make_cmd} -C src/interfaces install DESTDIR=#{path}"
172
+ end
173
+ def install
174
+ end
175
+ end
176
+
177
+ recipe.host = toolchain
178
+ recipe.configure_options << "CFLAGS=#{" -fPIC" if RUBY_PLATFORM =~ /linux|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/}"
180
+ recipe.configure_options << "LIBS=-lkrb5 -lcom_err -lk5crypto -lkrb5support -lresolv" if RUBY_PLATFORM =~ /linux/
181
+ recipe.configure_options << "LIBS=-lssl -lwsock32 -lgdi32 -lws2_32 -lcrypt32" if RUBY_PLATFORM =~ /mingw|mswin/
182
+ recipe.configure_options << "CPPFLAGS=-I#{openssl_recipe.path}/include"
183
+ recipe.cook_and_activate
184
+ end
185
+
186
+ # Use our own library name for libpq to avoid loading of system libpq by accident.
187
+ FileUtils.ln_sf File.join(postgresql_recipe.port_path, "lib/#{libpq_orig}"),
188
+ File.join(postgresql_recipe.port_path, "lib/#{libpq_rubypg}")
189
+ # Link to libpq_rubypg in our ports directory without adding it as rpath (like dir_config does)
190
+ $CFLAGS << " -I#{postgresql_recipe.path}/include"
191
+ $LDFLAGS << " -L#{postgresql_recipe.path}/lib"
28
192
  # Avoid dependency to external libgcc.dll on x86-mingw32
29
- $LDFLAGS << " -static-libgcc"
30
- # Don't use pg_config for cross build, but --with-pg-* path options
31
- dir_config 'pg'
193
+ $LDFLAGS << " -static-libgcc" if RUBY_PLATFORM =~ /mingw|mswin/
194
+ # Find libpq in the ports directory coming from lib/3.x
195
+ # It is shared between all compiled ruby versions.
196
+ $LDFLAGS << " '-Wl,-rpath=$$ORIGIN/../../ports/#{gem_platform}/lib'" if RUBY_PLATFORM =~ /linux/
32
197
 
198
+ $defs.push( "-DPG_IS_BINARY_GEM")
33
199
  else
34
200
  # Native build
35
201
 
@@ -56,13 +222,6 @@ else
56
222
  dlldir = libdir
57
223
  end
58
224
 
59
- # Try to use runtime path linker option, even if RbConfig doesn't know about it.
60
- # The rpath option is usually set implicit by dir_config(), but so far not
61
- # on MacOS-X.
62
- if dlldir && RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
63
- append_ldflags "-Wl,-rpath,#{dlldir.quote}"
64
- end
65
-
66
225
  if /mswin/ =~ RUBY_PLATFORM
67
226
  $libs = append_library($libs, 'ws2_32')
68
227
  end
@@ -79,7 +238,7 @@ $INSTALLFILES = {
79
238
  "./postgresql_lib_path.rb" => "$(RUBYLIBDIR)/pg/"
80
239
  }
81
240
 
82
- if RUBY_VERSION >= '2.3.0' && /solaris/ =~ RUBY_PLATFORM
241
+ if /solaris/ =~ RUBY_PLATFORM
83
242
  append_cppflags( '-D__EXTENSIONS__' )
84
243
  end
85
244
 
@@ -144,28 +303,16 @@ if /mingw/ =~ RUBY_PLATFORM && RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
144
303
  end
145
304
  end
146
305
 
147
- have_func 'PQconninfo', 'libpq-fe.h' or
148
- abort "Your PostgreSQL is too old. Either install an older version " +
149
- "of this gem or upgrade your database to at least PostgreSQL-9.3."
306
+ have_func 'PQencryptPasswordConn', 'libpq-fe.h' or # since 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."
150
310
  # optional headers/functions
151
- have_func 'PQsslAttribute', 'libpq-fe.h' # since PostgreSQL-9.5
152
- have_func 'PQresultVerboseErrorMessage', 'libpq-fe.h' # since PostgreSQL-9.6
153
- have_func 'PQencryptPasswordConn', 'libpq-fe.h' # since PostgreSQL-10
154
311
  have_func 'PQresultMemorySize', 'libpq-fe.h' # since PostgreSQL-12
155
- have_func 'PQenterPipelineMode', 'libpq-fe.h' do |src| # since PostgreSQL-14
156
- # Ensure header files fit as well
157
- src + " int con(){ return PGRES_PIPELINE_SYNC; }"
158
- end
159
312
  have_func 'timegm'
160
- have_func 'rb_gc_adjust_memory_usage' # since ruby-2.4
161
- have_func 'rb_gc_mark_movable' # since ruby-2.7
162
- have_func 'rb_io_wait' # since ruby-3.0
163
- have_func 'rb_io_descriptor' # since ruby-3.1
313
+ have_func 'rb_hash_new_capa' # since ruby-3.2
164
314
 
165
- # unistd.h confilicts with ruby/win32.h when cross compiling for win32 and ruby 1.9.1
166
- have_header 'unistd.h'
167
315
  have_header 'inttypes.h'
168
- have_header('ruby/fiber/scheduler.h') if RUBY_PLATFORM=~/mingw|mswin/
169
316
 
170
317
  checking_for "C99 variable length arrays" do
171
318
  $defs.push( "-DHAVE_VARIABLE_LENGTH_ARRAYS" ) if try_compile('void test_vla(int l){ int vla[l]; }')
@@ -174,3 +321,20 @@ end
174
321
  create_header()
175
322
  create_makefile( "pg_ext" )
176
323
 
324
+ if gem_platform
325
+ # exercise the strip command on native binary gems
326
+ # This approach borrowed from
327
+ # https://github.com/rake-compiler/rake-compiler-dock/blob/38066d479050f4fdb3956469255b35a05e5949ef/test/rcd_test/ext/mri/extconf.rb#L97C1-L110C42
328
+ strip_tool = RbConfig::CONFIG['STRIP']
329
+ strip_tool += ' -x' if RUBY_PLATFORM =~ /darwin/
330
+ File.open('Makefile.new', 'w') do |o|
331
+ o.puts 'hijack: all strip'
332
+ o.puts
333
+ o.write(File.read('Makefile'))
334
+ o.puts
335
+ o.puts 'strip: $(DLLIB)'
336
+ o.puts "\t$(ECHO) Stripping $(DLLIB)"
337
+ o.puts "\t$(Q) #{strip_tool} $(DLLIB)"
338
+ end
339
+ File.rename('Makefile.new', 'Makefile')
340
+ end
data/ext/gvl_wrappers.c CHANGED
@@ -5,8 +5,19 @@
5
5
 
6
6
  #include "pg.h"
7
7
 
8
- #ifndef HAVE_PQENCRYPTPASSWORDCONN
9
- char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm){return NULL;}
8
+
9
+ #ifndef LIBPQ_HAS_CHUNK_MODE
10
+ PGresult *PQclosePrepared(PGconn *conn, const char *stmtName){return NULL;}
11
+ PGresult *PQclosePortal(PGconn *conn, const char *portalName){return NULL;}
12
+ int PQsendClosePrepared(PGconn *conn, const char *stmtName){return 0;}
13
+ int PQsendClosePortal(PGconn *conn, const char *portalName){return 0;}
14
+ int PQsendPipelineSync(PGconn *conn){return 0;}
15
+ int PQcancelBlocking(PGcancelConn *cancelConn){return 0;}
16
+ int PQcancelStart(PGcancelConn *cancelConn){return 0;}
17
+ PostgresPollingStatusType PQcancelPoll(PGcancelConn *cancelConn){return PGRES_POLLING_FAILED;}
18
+ #endif
19
+ #ifndef LIBPQ_HAS_PIPELINING
20
+ int PQpipelineSync(PGconn *conn){return 0;}
10
21
  #endif
11
22
 
12
23
  #ifdef ENABLE_GVL_UNLOCK