Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/binary-gems.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,26 @@ jobs:
cp -v pg-*.gem misc/yugabyte/
cd misc/yugabyte
docker-compose up --abort-on-container-exit --exit-code-from pg

job_binary_too_old_glibc:
name: GLIBC
needs: rcd_build
strategy:
fail-fast: false
matrix:
include:
- gem_platform: x86_64-linux

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download gem-${{ matrix.gem_platform }}
uses: actions/download-artifact@v4
with:
name: binary-gem-${{ matrix.gem_platform }}
- name: Build image and Run tests
run: |
sudo apt-get install -y docker-compose
cp -v pg-*.gem misc/glibc/
cd misc/glibc
docker-compose up --abort-on-container-exit --exit-code-from pg
292 changes: 156 additions & 136 deletions lib/pg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,141 +5,161 @@
# The top-level PG namespace.
module PG

# Is this file part of a fat binary gem with bundled libpq?
# This path must be enabled by add_dll_directory on Windows.
gplat = Gem::Platform.local
bundled_libpq_path = Dir[File.expand_path("../ports/#{gplat.cpu}-#{gplat.os}*/lib", __dir__)].first
if bundled_libpq_path
POSTGRESQL_LIB_PATH = bundled_libpq_path
else
# Try to load libpq path as found by extconf.rb
begin
require "pg/postgresql_lib_path"
rescue LoadError
# rake-compiler doesn't use regular "make install", but uses it's own install tasks.
# It therefore doesn't copy pg/postgresql_lib_path.rb in case of "rake compile".
POSTGRESQL_LIB_PATH = false
end
end
POSTGRESQL_LIB_PATH.freeze

add_dll_path = proc do |path, &block|
if RUBY_PLATFORM =~/(mswin|mingw)/i && path
BUNDLED_LIBPQ_WITH_UNIXSOCKET = false
begin
require 'ruby_installer/runtime'
RubyInstaller::Runtime.add_dll_directory(path, &block)
rescue LoadError
old_path = ENV['PATH']
ENV['PATH'] = "#{path};#{old_path}"
block.call
ENV['PATH'] = old_path
end
else
# libpq is found by a relative rpath in the cross compiled extension dll
# or by the system library loader
block.call
BUNDLED_LIBPQ_WITH_UNIXSOCKET = RUBY_PLATFORM=~/linux/i && PG::IS_BINARY_GEM
end
end

# Add a load path to the one retrieved from pg_config
add_dll_path.call(POSTGRESQL_LIB_PATH) do
begin
# Try the <major>.<minor> subdirectory for fat binary gems
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
require "#{major_minor}/pg_ext"
rescue LoadError
require 'pg_ext'
end
end

# Get the PG library version.
#
# +include_buildnum+ is no longer used and any value passed will be ignored.
def self.version_string( include_buildnum=nil )
"%s %s" % [ self.name, VERSION ]
end


### Convenience alias for PG::Connection.new.
def self.connect( *args, &block )
Connection.new( *args, &block )
end

if defined?(Ractor.make_shareable)
def self.make_shareable(obj)
Ractor.make_shareable(obj)
end
else
def self.make_shareable(obj)
obj.freeze
end
end

module BinaryDecoder
%i[ TimestampUtc TimestampUtcToLocal TimestampLocal ].each do |klass|
autoload klass, 'pg/binary_decoder/timestamp'
end
autoload :Date, 'pg/binary_decoder/date'
end
module BinaryEncoder
%i[ TimestampUtc TimestampLocal ].each do |klass|
autoload klass, 'pg/binary_encoder/timestamp'
end
end
module TextDecoder
%i[ TimestampUtc TimestampUtcToLocal TimestampLocal TimestampWithoutTimeZone TimestampWithTimeZone ].each do |klass|
autoload klass, 'pg/text_decoder/timestamp'
end
autoload :Date, 'pg/text_decoder/date'
autoload :Inet, 'pg/text_decoder/inet'
autoload :JSON, 'pg/text_decoder/json'
autoload :Numeric, 'pg/text_decoder/numeric'
end
module TextEncoder
%i[ TimestampUtc TimestampWithoutTimeZone TimestampWithTimeZone ].each do |klass|
autoload klass, 'pg/text_encoder/timestamp'
end
autoload :Date, 'pg/text_encoder/date'
autoload :Inet, 'pg/text_encoder/inet'
autoload :JSON, 'pg/text_encoder/json'
autoload :Numeric, 'pg/text_encoder/numeric'
end

autoload :BasicTypeMapBasedOnResult, 'pg/basic_type_map_based_on_result'
autoload :BasicTypeMapForQueries, 'pg/basic_type_map_for_queries'
autoload :BasicTypeMapForResults, 'pg/basic_type_map_for_results'
autoload :BasicTypeRegistry, 'pg/basic_type_registry'
require 'pg/exceptions'
require 'pg/coder'
require 'pg/type_map_by_column'
require 'pg/connection'
require 'pg/cancel_connection'
require 'pg/result'
require 'pg/tuple'
autoload :VERSION, 'pg/version'


# Avoid "uninitialized constant Truffle::WarningOperations" on Truffleruby up to 22.3.1
if RUBY_ENGINE=="truffleruby" && !defined?(Truffle::WarningOperations)
module TruffleFixWarn
def warn(str, category=nil)
super(str)
end
end
Warning.extend(TruffleFixWarn)
end

# Ruby-3.4+ prints a warning, if bigdecimal is required but not in the Gemfile.
# But it's a false positive, since we enable bigdecimal depending features only if it's available.
# And most people don't need these features.
def self.require_bigdecimal_without_warning
oldverb, $VERBOSE = $VERBOSE, nil
require "bigdecimal"
ensure
$VERBOSE = oldverb
end
# Is this file part of a fat binary gem with bundled libpq?
# This path must be enabled by add_dll_directory on Windows.
gplat = Gem::Platform.local
bundled_libpq_path = Dir[File.expand_path("../ports/#{gplat.cpu}-#{gplat.os}*/lib", __dir__)].first
if bundled_libpq_path
POSTGRESQL_LIB_PATH = bundled_libpq_path
else
# Try to load libpq path as found by extconf.rb
begin
require "pg/postgresql_lib_path"
rescue LoadError
# rake-compiler doesn't use regular "make install", but uses it's own install tasks.
# It therefore doesn't copy pg/postgresql_lib_path.rb in case of "rake compile".
POSTGRESQL_LIB_PATH = false
end
end
POSTGRESQL_LIB_PATH.freeze

add_dll_path = proc do |path, &block|
if RUBY_PLATFORM =~/(mswin|mingw)/i && path
BUNDLED_LIBPQ_WITH_UNIXSOCKET = false
begin
require 'ruby_installer/runtime'
RubyInstaller::Runtime.add_dll_directory(path, &block)
rescue LoadError
old_path = ENV['PATH']
ENV['PATH'] = "#{path};#{old_path}"
block.call
ENV['PATH'] = old_path
end
else
# libpq is found by a relative rpath in the cross compiled extension dll
# or by the system library loader
block.call
BUNDLED_LIBPQ_WITH_UNIXSOCKET = RUBY_PLATFORM=~/linux/i && PG::IS_BINARY_GEM
end
end

# Add a load path to the one retrieved from pg_config
add_dll_path.call(POSTGRESQL_LIB_PATH) do
begin
# Try the <major>.<minor> subdirectory for fat binary gems
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
require "#{major_minor}/pg_ext"
rescue LoadError => error1
begin
require 'pg_ext'
rescue LoadError => error2
msg = <<~EOT
pg's C extension failed to load:
#{error1}
#{error2}
EOT
if msg =~ /GLIBC/
msg += <<~EOT

The GLIBC version of this system seems too old. Please use the source version of pg:
gem uninstall pg --all
gem install pg --platform ruby
or in your Gemfile:
gem "pg", force_ruby_platform: true
See also: https://deveiate.org/code/pg/README_md.html#label-Source+gem
EOT
end
raise error2, msg
end
end
end

# Get the PG library version.
#
# +include_buildnum+ is no longer used and any value passed will be ignored.
def self.version_string( include_buildnum=nil )
"%s %s" % [ self.name, VERSION ]
end


### Convenience alias for PG::Connection.new.
def self.connect( *args, &block )
Connection.new( *args, &block )
end

if defined?(Ractor.make_shareable)
def self.make_shareable(obj)
Ractor.make_shareable(obj)
end
else
def self.make_shareable(obj)
obj.freeze
end
end

module BinaryDecoder
%i[ TimestampUtc TimestampUtcToLocal TimestampLocal ].each do |klass|
autoload klass, 'pg/binary_decoder/timestamp'
end
autoload :Date, 'pg/binary_decoder/date'
end
module BinaryEncoder
%i[ TimestampUtc TimestampLocal ].each do |klass|
autoload klass, 'pg/binary_encoder/timestamp'
end
end
module TextDecoder
%i[ TimestampUtc TimestampUtcToLocal TimestampLocal TimestampWithoutTimeZone TimestampWithTimeZone ].each do |klass|
autoload klass, 'pg/text_decoder/timestamp'
end
autoload :Date, 'pg/text_decoder/date'
autoload :Inet, 'pg/text_decoder/inet'
autoload :JSON, 'pg/text_decoder/json'
autoload :Numeric, 'pg/text_decoder/numeric'
end
module TextEncoder
%i[ TimestampUtc TimestampWithoutTimeZone TimestampWithTimeZone ].each do |klass|
autoload klass, 'pg/text_encoder/timestamp'
end
autoload :Date, 'pg/text_encoder/date'
autoload :Inet, 'pg/text_encoder/inet'
autoload :JSON, 'pg/text_encoder/json'
autoload :Numeric, 'pg/text_encoder/numeric'
end

autoload :BasicTypeMapBasedOnResult, 'pg/basic_type_map_based_on_result'
autoload :BasicTypeMapForQueries, 'pg/basic_type_map_for_queries'
autoload :BasicTypeMapForResults, 'pg/basic_type_map_for_results'
autoload :BasicTypeRegistry, 'pg/basic_type_registry'
require 'pg/exceptions'
require 'pg/coder'
require 'pg/type_map_by_column'
require 'pg/connection'
require 'pg/cancel_connection'
require 'pg/result'
require 'pg/tuple'
autoload :VERSION, 'pg/version'


# Avoid "uninitialized constant Truffle::WarningOperations" on Truffleruby up to 22.3.1
if RUBY_ENGINE=="truffleruby" && !defined?(Truffle::WarningOperations)
module TruffleFixWarn
def warn(str, category=nil)
super(str)
end
end
Warning.extend(TruffleFixWarn)
end

# Ruby-3.4+ prints a warning, if bigdecimal is required but not in the Gemfile.
# But it's a false positive, since we enable bigdecimal depending features only if it's available.
# And most people don't need these features.
def self.require_bigdecimal_without_warning
oldverb, $VERBOSE = $VERBOSE, nil
require "bigdecimal"
ensure
$VERBOSE = oldverb
end

end # module PG
20 changes: 20 additions & 0 deletions misc/glibc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM debian:10.13

WORKDIR /pg

# Debian 10.13 is EOL now:
RUN sed -i s/deb.debian.org/archive.debian.org/g /etc/apt/sources.list

RUN apt-get update && apt-get install ruby git wget gcc make libz-dev libffi-dev libreadline-dev libyaml-dev libssl-dev -y

ENV RBENV_ROOT=/usr/local/rbenv

RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
$RBENV_ROOT/bin/rbenv init

RUN $RBENV_ROOT/bin/rbenv install 3.3.9 -- --disable-install-doc
RUN /usr/local/rbenv/versions/3.3.9/bin/gem inst rspec

CMD /usr/local/rbenv/versions/3.3.9/bin/gem inst --local pg-*.gem && \
/usr/local/rbenv/versions/3.3.9/bin/rspec glibc_spec.rb
9 changes: 9 additions & 0 deletions misc/glibc/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
pg:
build:
context: .
args:
- http_proxy
- https_proxy
volumes:
- .:/pg
5 changes: 5 additions & 0 deletions misc/glibc/glibc_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RSpec.describe "require 'pg'" do
it "gives a descriptive error message when GLIBC is too old" do
expect { require "pg" }.to raise_error(/GLIBC.*gem install pg --platform ruby/m)
end
end
Loading