pg 1.2.3-x64-mingw32 → 1.3.0.rc1-x64-mingw32
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +80 -0
- data/.github/workflows/source-gem.yml +129 -0
- data/.gitignore +13 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.rdoc +75 -7
- data/Manifest.txt +0 -1
- data/README.rdoc +7 -6
- data/Rakefile +27 -138
- data/Rakefile.cross +5 -5
- data/certs/ged.pem +24 -0
- data/ext/errorcodes.def +8 -0
- data/ext/errorcodes.txt +3 -1
- data/ext/extconf.rb +90 -19
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +35 -1
- data/ext/pg.h +18 -1
- data/ext/pg_coder.c +82 -28
- data/ext/pg_connection.c +538 -279
- data/ext/pg_copy_coder.c +45 -16
- data/ext/pg_record_coder.c +38 -10
- data/ext/pg_result.c +61 -31
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +6 -6
- data/ext/pg_tuple.c +47 -21
- data/ext/pg_type_map.c +41 -8
- data/ext/pg_type_map_all_strings.c +14 -1
- data/ext/pg_type_map_by_class.c +49 -24
- data/ext/pg_type_map_by_column.c +64 -28
- data/ext/pg_type_map_by_mri_type.c +47 -18
- data/ext/pg_type_map_by_oid.c +52 -23
- data/ext/pg_type_map_in_ruby.c +50 -19
- data/ext/pg_util.c +2 -2
- data/lib/2.5/pg_ext.so +0 -0
- data/lib/2.6/pg_ext.so +0 -0
- data/lib/2.7/pg_ext.so +0 -0
- data/lib/3.0/pg_ext.so +0 -0
- data/lib/pg/basic_type_map_based_on_result.rb +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +296 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +369 -56
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +38 -25
- data/lib/x64-mingw32/libpq.dll +0 -0
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +32 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +106 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +81 -230
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -0
- data/lib/2.2/pg_ext.so +0 -0
- data/lib/2.3/pg_ext.so +0 -0
- data/lib/2.4/pg_ext.so +0 -0
- data/lib/pg/basic_type_mapping.rb +0 -522
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -380
- data/spec/pg/basic_type_mapping_spec.rb +0 -630
- data/spec/pg/connection_spec.rb +0 -1949
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -681
- data/spec/pg/tuple_spec.rb +0 -333
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -226
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -1123
- data/spec/pg_spec.rb +0 -50
data/lib/pg/connection.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'pg' unless defined?( PG )
|
5
5
|
require 'uri'
|
6
|
+
require 'io/wait'
|
7
|
+
require 'socket'
|
6
8
|
|
7
9
|
# The PostgreSQL connection class. The interface for this class is based on
|
8
10
|
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
|
@@ -25,62 +27,140 @@ class PG::Connection
|
|
25
27
|
CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
|
26
28
|
|
27
29
|
|
28
|
-
### Quote
|
29
|
-
def self
|
30
|
+
### Quote a single +value+ for use in a connection-parameter string.
|
31
|
+
def self.quote_connstr( value )
|
30
32
|
return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
|
31
33
|
end
|
32
34
|
|
35
|
+
# Convert Hash options to connection String
|
36
|
+
#
|
37
|
+
# Values are properly quoted and escaped.
|
38
|
+
def self.connect_hash_to_string( hash )
|
39
|
+
hash.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
|
40
|
+
end
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
|
40
|
-
option_string = ''
|
42
|
+
# Decode a connection string to Hash options
|
43
|
+
#
|
44
|
+
# Value are properly unquoted and unescaped.
|
45
|
+
def self.connect_string_to_hash( str )
|
41
46
|
options = {}
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
key = nil
|
48
|
+
value = String.new
|
49
|
+
str.scan(/\G\s*(?>([^\s\\\']+)\s*=\s*|([^\s\\\']+)|'((?:[^\'\\]|\\.)*)'|(\\.?)|(\S))(\s|\z)?/m) do
|
50
|
+
|k, word, sq, esc, garbage, sep|
|
51
|
+
raise ArgumentError, "unterminated quoted string in connection info string: #{str.inspect}" if garbage
|
52
|
+
if k
|
53
|
+
key = k
|
54
|
+
else
|
55
|
+
value << (word || (sq || esc).gsub(/\\(.)/, '\\1'))
|
56
|
+
end
|
57
|
+
if sep
|
58
|
+
raise ArgumentError, "missing = after #{value.inspect}" unless key
|
59
|
+
options[key.to_sym] = value
|
60
|
+
key = nil
|
61
|
+
value = String.new
|
62
|
+
end
|
47
63
|
end
|
64
|
+
options
|
65
|
+
end
|
66
|
+
|
67
|
+
# URI defined in RFC3986
|
68
|
+
# This regexp is modified to allow host to specify multiple comma separated components captured as <hostports> and to disallow comma in hostnames.
|
69
|
+
# Taken from: https://github.com/ruby/ruby/blob/be04006c7d2f9aeb7e9d8d09d945b3a9c7850202/lib/uri/rfc3986_parser.rb#L6
|
70
|
+
HOST_AND_PORT = /(?<hostport>(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[-\.!$&-+0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)/
|
71
|
+
POSTGRESQL_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<hostports>#{HOST_AND_PORT}(?:,\g<hostport>)*))(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
|
72
|
+
|
73
|
+
# Parse the connection +args+ into a connection-parameter string.
|
74
|
+
# See PG::Connection.new for valid arguments.
|
75
|
+
#
|
76
|
+
# It accepts:
|
77
|
+
# * an option String kind of "host=name port=5432"
|
78
|
+
# * an option Hash kind of {host: "name", port: 5432}
|
79
|
+
# * URI string
|
80
|
+
# * URI object
|
81
|
+
# * positional arguments
|
82
|
+
#
|
83
|
+
# The method adds the option "hostaddr" and "fallback_application_name" if they aren't already set.
|
84
|
+
# The URI and the options string is passed through and "hostaddr" as well as "fallback_application_name"
|
85
|
+
# are added to the end.
|
86
|
+
def self::parse_connect_args( *args )
|
87
|
+
hash_arg = args.last.is_a?( Hash ) ? args.pop.transform_keys(&:to_sym) : {}
|
88
|
+
option_string = ""
|
89
|
+
iopts = {}
|
48
90
|
|
49
91
|
if args.length == 1
|
50
92
|
case args.first
|
51
|
-
when URI,
|
52
|
-
uri =
|
53
|
-
|
93
|
+
when URI, POSTGRESQL_URI
|
94
|
+
uri = args.first.to_s
|
95
|
+
uri_match = POSTGRESQL_URI.match(uri)
|
96
|
+
if uri_match['query']
|
97
|
+
iopts = URI.decode_www_form(uri_match['query']).to_h.transform_keys(&:to_sym)
|
98
|
+
end
|
99
|
+
# extract "host1,host2" from "host1:5432,host2:5432"
|
100
|
+
iopts[:host] = uri_match['hostports'].split(',', -1).map do |hostport|
|
101
|
+
hostmatch = HOST_AND_PORT.match(hostport)
|
102
|
+
hostmatch['IPv6address'] || hostmatch['IPv4address'] || hostmatch['reg-name']&.gsub(/%(\h\h)/){ $1.hex.chr }
|
103
|
+
end.join(',')
|
104
|
+
oopts = {}
|
54
105
|
when /=/
|
55
106
|
# Option string style
|
56
107
|
option_string = args.first.to_s
|
108
|
+
iopts = connect_string_to_hash(option_string)
|
109
|
+
oopts = {}
|
57
110
|
else
|
58
|
-
# Positional parameters
|
59
|
-
|
111
|
+
# Positional parameters (only host given)
|
112
|
+
iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
|
113
|
+
oopts = iopts.dup
|
60
114
|
end
|
61
115
|
else
|
116
|
+
# Positional parameters
|
62
117
|
max = CONNECT_ARGUMENT_ORDER.length
|
63
118
|
raise ArgumentError,
|
64
119
|
"Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
|
65
120
|
|
66
121
|
CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
|
67
|
-
|
122
|
+
iopts[ k.to_sym ] = v if v
|
123
|
+
end
|
124
|
+
iopts.delete(:tty) # ignore obsolete tty parameter
|
125
|
+
oopts = iopts.dup
|
126
|
+
end
|
127
|
+
|
128
|
+
iopts.merge!( hash_arg )
|
129
|
+
oopts.merge!( hash_arg )
|
130
|
+
|
131
|
+
# Resolve DNS in Ruby to avoid blocking state while connecting, when it ...
|
132
|
+
if (host=iopts[:host]) && !iopts[:hostaddr]
|
133
|
+
hostaddrs = host.split(",", -1).map do |mhost|
|
134
|
+
if !mhost.empty? && !mhost.start_with?("/") && # isn't UnixSocket
|
135
|
+
# isn't a path on Windows
|
136
|
+
(RUBY_PLATFORM !~ /mingw|mswin/ || mhost !~ /\A\w:[\/\\]/)
|
137
|
+
|
138
|
+
if Fiber.respond_to?(:scheduler) &&
|
139
|
+
Fiber.scheduler &&
|
140
|
+
RUBY_VERSION < '3.1.'
|
141
|
+
|
142
|
+
# Use a second thread to avoid blocking of the scheduler.
|
143
|
+
# `IPSocket.getaddress` isn't fiber aware before ruby-3.1.
|
144
|
+
Thread.new{ IPSocket.getaddress(mhost) rescue '' }.value
|
145
|
+
else
|
146
|
+
IPSocket.getaddress(mhost) rescue ''
|
147
|
+
end
|
148
|
+
end
|
68
149
|
end
|
150
|
+
oopts[:hostaddr] = hostaddrs.join(",") if hostaddrs.any?
|
69
151
|
end
|
70
152
|
|
71
|
-
|
153
|
+
if !iopts[:fallback_application_name]
|
154
|
+
oopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
|
155
|
+
end
|
72
156
|
|
73
157
|
if uri
|
74
|
-
uri
|
75
|
-
uri.
|
76
|
-
uri
|
77
|
-
uri.password = nil if options[:password]
|
78
|
-
uri.path = '' if options[:dbname]
|
79
|
-
uri.query = URI.encode_www_form( options )
|
80
|
-
return uri.to_s.sub( /^#{uri.scheme}:(?!\/\/)/, "#{uri.scheme}://" )
|
158
|
+
uri += uri_match['query'] ? "&" : "?"
|
159
|
+
uri += URI.encode_www_form( oopts )
|
160
|
+
return uri
|
81
161
|
else
|
82
|
-
option_string += ' ' unless option_string.empty? &&
|
83
|
-
return option_string +
|
162
|
+
option_string += ' ' unless option_string.empty? && oopts.empty?
|
163
|
+
return option_string + connect_hash_to_string(oopts)
|
84
164
|
end
|
85
165
|
end
|
86
166
|
|
@@ -88,7 +168,7 @@ class PG::Connection
|
|
88
168
|
# call-seq:
|
89
169
|
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
90
170
|
#
|
91
|
-
# Execute a copy process for
|
171
|
+
# Execute a copy process for transferring data to or from the server.
|
92
172
|
#
|
93
173
|
# This issues the SQL COPY command via #exec. The response to this
|
94
174
|
# (if there is no error in the command) is a PG::Result object that
|
@@ -214,6 +294,25 @@ class PG::Connection
|
|
214
294
|
define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
|
215
295
|
end
|
216
296
|
|
297
|
+
#
|
298
|
+
# call-seq:
|
299
|
+
# conn.transaction { |conn| ... } -> result of the block
|
300
|
+
#
|
301
|
+
# Executes a +BEGIN+ at the start of the block,
|
302
|
+
# and a +COMMIT+ at the end of the block, or
|
303
|
+
# +ROLLBACK+ if any exception occurs.
|
304
|
+
def transaction
|
305
|
+
exec "BEGIN"
|
306
|
+
res = yield(self)
|
307
|
+
rescue Exception
|
308
|
+
cancel if transaction_status != PG::PQTRANS_IDLE
|
309
|
+
block
|
310
|
+
exec "ROLLBACK"
|
311
|
+
raise
|
312
|
+
else
|
313
|
+
exec "COMMIT"
|
314
|
+
res
|
315
|
+
end
|
217
316
|
|
218
317
|
### Returns an array of Hashes with connection defaults. See ::conndefaults
|
219
318
|
### for details.
|
@@ -237,17 +336,13 @@ class PG::Connection
|
|
237
336
|
return self.class.conndefaults_hash
|
238
337
|
end
|
239
338
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
def conninfo_hash
|
248
|
-
return self.conninfo.each_with_object({}) do |info, hash|
|
249
|
-
hash[ info[:keyword].to_sym ] = info[:val]
|
250
|
-
end
|
339
|
+
### Return the Postgres connection info structure as a Hash keyed by option
|
340
|
+
### keyword (as a Symbol).
|
341
|
+
###
|
342
|
+
### See also #conninfo
|
343
|
+
def conninfo_hash
|
344
|
+
return self.conninfo.each_with_object({}) do |info, hash|
|
345
|
+
hash[ info[:keyword].to_sym ] = info[:val]
|
251
346
|
end
|
252
347
|
end
|
253
348
|
|
@@ -269,20 +364,238 @@ class PG::Connection
|
|
269
364
|
end
|
270
365
|
end
|
271
366
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
367
|
+
alias sync_get_result get_result
|
368
|
+
def async_get_result(*args)
|
369
|
+
block
|
370
|
+
sync_get_result
|
371
|
+
end
|
372
|
+
|
373
|
+
alias sync_get_copy_data get_copy_data
|
374
|
+
def async_get_copy_data(async=false, decoder=nil)
|
375
|
+
if async
|
376
|
+
return sync_get_copy_data(async, decoder)
|
377
|
+
else
|
378
|
+
while (res=sync_get_copy_data(true, decoder)) == false
|
379
|
+
socket_io.wait_readable
|
380
|
+
consume_input
|
381
|
+
end
|
382
|
+
return res
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# In async_api=false mode all send calls run directly on libpq.
|
387
|
+
# Blocking vs. nonblocking state can be changed in libpq.
|
388
|
+
alias sync_setnonblocking setnonblocking
|
389
|
+
|
390
|
+
# In async_api=true mode (default) all send calls run nonblocking.
|
391
|
+
# The difference is that setnonblocking(true) disables automatic handling of would-block cases.
|
392
|
+
def async_setnonblocking(enabled)
|
393
|
+
singleton_class.async_send_api = !enabled
|
394
|
+
self.flush_data = !enabled
|
395
|
+
sync_setnonblocking(true)
|
396
|
+
end
|
397
|
+
|
398
|
+
# sync/async isnonblocking methods are switched by async_setnonblocking()
|
399
|
+
alias sync_isnonblocking isnonblocking
|
400
|
+
def async_isnonblocking
|
401
|
+
false
|
402
|
+
end
|
403
|
+
|
404
|
+
alias sync_put_copy_data put_copy_data
|
405
|
+
def async_put_copy_data(buffer, encoder=nil)
|
406
|
+
until sync_put_copy_data(buffer, encoder)
|
407
|
+
flush
|
408
|
+
end
|
409
|
+
flush
|
410
|
+
end
|
411
|
+
alias sync_put_copy_end put_copy_end
|
412
|
+
def async_put_copy_end(*args)
|
413
|
+
until sync_put_copy_end(*args)
|
414
|
+
flush
|
415
|
+
end
|
416
|
+
flush
|
417
|
+
end
|
418
|
+
|
419
|
+
if method_defined? :encrypt_password
|
420
|
+
alias sync_encrypt_password encrypt_password
|
421
|
+
def async_encrypt_password( password, username, algorithm=nil )
|
422
|
+
algorithm ||= exec("SHOW password_encryption").getvalue(0,0)
|
423
|
+
sync_encrypt_password(password, username, algorithm)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
alias sync_reset reset
|
428
|
+
def async_reset
|
429
|
+
reset_start
|
430
|
+
async_connect_or_reset(:reset_poll)
|
431
|
+
end
|
432
|
+
|
433
|
+
alias sync_cancel cancel
|
434
|
+
def async_cancel
|
435
|
+
be_pid = backend_pid
|
436
|
+
be_key = backend_key
|
437
|
+
cancel_request = [0x10, 1234, 5678, be_pid, be_key].pack("NnnNN")
|
438
|
+
|
439
|
+
if Fiber.respond_to?(:scheduler) && Fiber.scheduler && RUBY_PLATFORM =~ /mingw|mswin/
|
440
|
+
# Ruby's nonblocking IO is not really supported on Windows.
|
441
|
+
# We work around by using threads and explicit calls to wait_readable/wait_writable.
|
442
|
+
cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
|
443
|
+
begin
|
444
|
+
cl.write_nonblock(cancel_request)
|
445
|
+
rescue IO::WaitReadable, Errno::EINTR
|
446
|
+
cl.wait_writable
|
447
|
+
retry
|
448
|
+
end
|
449
|
+
begin
|
450
|
+
cl.read_nonblock(1)
|
451
|
+
rescue IO::WaitReadable, Errno::EINTR
|
452
|
+
cl.wait_readable
|
453
|
+
retry
|
454
|
+
rescue EOFError
|
455
|
+
end
|
456
|
+
elsif RUBY_ENGINE == 'truffleruby'
|
457
|
+
begin
|
458
|
+
cl = socket_io.remote_address.connect
|
459
|
+
rescue NotImplementedError
|
460
|
+
# Workaround for truffleruby < 21.3.0
|
461
|
+
cl2 = Socket.for_fd(socket_io.fileno)
|
462
|
+
cl2.autoclose = false
|
463
|
+
adr = cl2.remote_address
|
464
|
+
if adr.ip?
|
465
|
+
cl = TCPSocket.new(adr.ip_address, adr.ip_port)
|
466
|
+
cl.autoclose = false
|
467
|
+
else
|
468
|
+
cl = UNIXSocket.new(adr.unix_path)
|
469
|
+
cl.autoclose = false
|
470
|
+
end
|
471
|
+
end
|
472
|
+
cl.write(cancel_request)
|
473
|
+
cl.read(1)
|
474
|
+
else
|
475
|
+
cl = socket_io.remote_address.connect
|
476
|
+
# Send CANCEL_REQUEST_CODE and parameters
|
477
|
+
cl.write(cancel_request)
|
478
|
+
# Wait for the postmaster to close the connection, which indicates that it's processed the request.
|
479
|
+
cl.read(1)
|
480
|
+
end
|
481
|
+
|
482
|
+
cl.close
|
483
|
+
nil
|
484
|
+
rescue SystemCallError => err
|
485
|
+
err.to_s
|
486
|
+
end
|
487
|
+
|
488
|
+
private def async_connect_or_reset(poll_meth)
|
489
|
+
# Now grab a reference to the underlying socket so we know when the connection is established
|
490
|
+
socket = socket_io
|
491
|
+
|
492
|
+
# Track the progress of the connection, waiting for the socket to become readable/writable before polling it
|
493
|
+
poll_status = PG::PGRES_POLLING_WRITING
|
494
|
+
until poll_status == PG::PGRES_POLLING_OK ||
|
495
|
+
poll_status == PG::PGRES_POLLING_FAILED
|
496
|
+
|
497
|
+
# If the socket needs to read, wait 'til it becomes readable to poll again
|
498
|
+
case poll_status
|
499
|
+
when PG::PGRES_POLLING_READING
|
500
|
+
socket.wait_readable
|
501
|
+
|
502
|
+
# ...and the same for when the socket needs to write
|
503
|
+
when PG::PGRES_POLLING_WRITING
|
504
|
+
socket.wait_writable
|
505
|
+
end
|
506
|
+
|
507
|
+
# Check to see if it's finished or failed yet
|
508
|
+
poll_status = send( poll_meth )
|
509
|
+
end
|
510
|
+
|
511
|
+
raise(PG::ConnectionBad, error_message) unless status == PG::CONNECTION_OK
|
512
|
+
|
513
|
+
# Set connection to nonblocking to handle all blocking states in ruby.
|
514
|
+
# That way a fiber scheduler is able to handle IO requests.
|
515
|
+
sync_setnonblocking(true)
|
516
|
+
self.flush_data = true
|
517
|
+
set_default_encoding
|
518
|
+
|
519
|
+
self
|
520
|
+
end
|
521
|
+
|
522
|
+
class << self
|
523
|
+
alias sync_connect new
|
524
|
+
|
525
|
+
def async_connect(*args, **kwargs)
|
526
|
+
conn = PG::Connection.connect_start(*args, **kwargs ) or
|
527
|
+
raise(PG::Error, "Unable to create a new connection")
|
528
|
+
|
529
|
+
raise(PG::ConnectionBad, conn.error_message) if conn.status == PG::CONNECTION_BAD
|
530
|
+
|
531
|
+
conn.send(:async_connect_or_reset, :connect_poll)
|
532
|
+
end
|
533
|
+
|
534
|
+
alias sync_ping ping
|
535
|
+
def async_ping(*args)
|
536
|
+
if Fiber.respond_to?(:scheduler) && Fiber.scheduler
|
537
|
+
# Run PQping in a second thread to avoid blocking of the scheduler.
|
538
|
+
# Unfortunately there's no nonblocking way to run ping.
|
539
|
+
Thread.new { sync_ping(*args) }.value
|
540
|
+
else
|
541
|
+
sync_ping(*args)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
REDIRECT_CLASS_METHODS = {
|
546
|
+
:new => [:async_connect, :sync_connect],
|
547
|
+
:ping => [:async_ping, :sync_ping],
|
548
|
+
}
|
549
|
+
|
550
|
+
# These methods are affected by PQsetnonblocking
|
551
|
+
REDIRECT_SEND_METHODS = {
|
552
|
+
:isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
|
553
|
+
:nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
|
554
|
+
:put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
|
555
|
+
:put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
|
556
|
+
:flush => [:async_flush, :sync_flush],
|
557
|
+
}
|
558
|
+
REDIRECT_METHODS = {
|
559
|
+
:exec => [:async_exec, :sync_exec],
|
560
|
+
:query => [:async_exec, :sync_exec],
|
561
|
+
:exec_params => [:async_exec_params, :sync_exec_params],
|
562
|
+
:prepare => [:async_prepare, :sync_prepare],
|
563
|
+
:exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
|
564
|
+
:describe_portal => [:async_describe_portal, :sync_describe_portal],
|
565
|
+
:describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
|
566
|
+
:setnonblocking => [:async_setnonblocking, :sync_setnonblocking],
|
567
|
+
:get_result => [:async_get_result, :sync_get_result],
|
568
|
+
:get_last_result => [:async_get_last_result, :sync_get_last_result],
|
569
|
+
:get_copy_data => [:async_get_copy_data, :sync_get_copy_data],
|
570
|
+
:reset => [:async_reset, :sync_reset],
|
571
|
+
:set_client_encoding => [:async_set_client_encoding, :sync_set_client_encoding],
|
572
|
+
:client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
|
573
|
+
:cancel => [:async_cancel, :sync_cancel],
|
574
|
+
}
|
575
|
+
|
576
|
+
if PG::Connection.instance_methods.include? :async_encrypt_password
|
577
|
+
REDIRECT_METHODS.merge!({
|
578
|
+
:encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
|
579
|
+
})
|
580
|
+
end
|
581
|
+
|
582
|
+
def async_send_api=(enable)
|
583
|
+
REDIRECT_SEND_METHODS.each do |ali, (async, sync)|
|
584
|
+
undef_method(ali) if method_defined?(ali)
|
585
|
+
alias_method( ali, enable ? async : sync )
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def async_api=(enable)
|
590
|
+
self.async_send_api = enable
|
591
|
+
REDIRECT_METHODS.each do |ali, (async, sync)|
|
592
|
+
remove_method(ali) if method_defined?(ali)
|
593
|
+
alias_method( ali, enable ? async : sync )
|
594
|
+
end
|
595
|
+
REDIRECT_CLASS_METHODS.each do |ali, (async, sync)|
|
596
|
+
singleton_class.remove_method(ali) if method_defined?(ali)
|
597
|
+
singleton_class.alias_method(ali, enable ? async : sync )
|
598
|
+
end
|
286
599
|
end
|
287
600
|
end
|
288
601
|
|
data/lib/pg/version.rb
ADDED
data/lib/pg.rb
CHANGED
@@ -1,15 +1,28 @@
|
|
1
|
+
|
1
2
|
# -*- ruby -*-
|
2
3
|
# frozen_string_literal: true
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# The top-level PG namespace.
|
6
|
+
module PG
|
7
|
+
|
8
|
+
# Is this file part of a fat binary gem with bundled libpq?
|
9
|
+
bundled_libpq_path = File.join(__dir__, RUBY_PLATFORM.gsub(/^i386-/, "x86-"))
|
10
|
+
if File.exist?(bundled_libpq_path)
|
11
|
+
POSTGRESQL_LIB_PATH = bundled_libpq_path
|
12
|
+
else
|
13
|
+
bundled_libpq_path = nil
|
14
|
+
# Try to load libpq path as found by extconf.rb
|
15
|
+
begin
|
16
|
+
require "pg/postgresql_lib_path"
|
17
|
+
rescue LoadError
|
18
|
+
# rake-compiler doesn't use regular "make install", but uses it's own install tasks.
|
19
|
+
# It therefore doesn't copy pg/postgresql_lib_path.rb in case of "rake compile".
|
20
|
+
POSTGRESQL_LIB_PATH = false
|
21
|
+
end
|
22
|
+
end
|
11
23
|
|
12
|
-
|
24
|
+
add_dll_path = proc do |path, &block|
|
25
|
+
if RUBY_PLATFORM =~/(mswin|mingw)/i && path && File.exist?(path)
|
13
26
|
begin
|
14
27
|
require 'ruby_installer/runtime'
|
15
28
|
RubyInstaller::Runtime.add_dll_directory(path, &block)
|
@@ -19,28 +32,24 @@ rescue LoadError
|
|
19
32
|
block.call
|
20
33
|
ENV['PATH'] = old_path
|
21
34
|
end
|
35
|
+
else
|
36
|
+
# No need to set a load path manually - it's set as library rpath.
|
37
|
+
block.call
|
22
38
|
end
|
39
|
+
end
|
23
40
|
|
24
|
-
|
25
|
-
|
26
|
-
|
41
|
+
# Add a load path to the one retrieved from pg_config
|
42
|
+
add_dll_path.call(POSTGRESQL_LIB_PATH) do
|
43
|
+
if bundled_libpq_path
|
44
|
+
# It's a Windows binary gem, try the <major>.<minor> subdirectory
|
45
|
+
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
46
|
+
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
27
47
|
require "#{major_minor}/pg_ext"
|
48
|
+
else
|
49
|
+
require 'pg_ext'
|
28
50
|
end
|
29
|
-
else
|
30
|
-
raise
|
31
51
|
end
|
32
52
|
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
# The top-level PG namespace.
|
37
|
-
module PG
|
38
|
-
|
39
|
-
# Library version
|
40
|
-
VERSION = '1.2.3'
|
41
|
-
|
42
|
-
# VCS revision
|
43
|
-
REVISION = %q$Revision: 6f611e78845a $
|
44
53
|
|
45
54
|
class NotAllCopyDataRetrieved < PG::Error
|
46
55
|
end
|
@@ -65,10 +74,14 @@ module PG
|
|
65
74
|
require 'pg/binary_decoder'
|
66
75
|
require 'pg/text_encoder'
|
67
76
|
require 'pg/text_decoder'
|
68
|
-
require 'pg/
|
77
|
+
require 'pg/basic_type_registry'
|
78
|
+
require 'pg/basic_type_map_based_on_result'
|
79
|
+
require 'pg/basic_type_map_for_queries'
|
80
|
+
require 'pg/basic_type_map_for_results'
|
69
81
|
require 'pg/type_map_by_column'
|
70
82
|
require 'pg/connection'
|
71
83
|
require 'pg/result'
|
72
84
|
require 'pg/tuple'
|
85
|
+
require 'pg/version'
|
73
86
|
|
74
87
|
end # module PG
|
data/lib/x64-mingw32/libpq.dll
CHANGED
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
PGHOST = 'localhost'
|
4
|
+
PGDB = 'test'
|
5
|
+
#SOCKHOST = 'github.com'
|
6
|
+
SOCKHOST = 'it-trac.laika.com'
|
7
|
+
|
8
|
+
# Load pg first, so the libssl.so that libpq is linked against is loaded.
|
9
|
+
require 'pg'
|
10
|
+
$stderr.puts "connecting to postgres://#{PGHOST}/#{PGDB}"
|
11
|
+
conn = PG.connect( PGHOST, :dbname => PGDB )
|
12
|
+
|
13
|
+
# Now load OpenSSL, which might be linked against a different libssl.
|
14
|
+
require 'socket'
|
15
|
+
require 'openssl'
|
16
|
+
$stderr.puts "Connecting to #{SOCKHOST}"
|
17
|
+
sock = TCPSocket.open( SOCKHOST, 443 )
|
18
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
19
|
+
sock = OpenSSL::SSL::SSLSocket.new( sock, ctx )
|
20
|
+
sock.sync_close = true
|
21
|
+
|
22
|
+
# The moment of truth...
|
23
|
+
$stderr.puts "Attempting to connect..."
|
24
|
+
begin
|
25
|
+
sock.connect
|
26
|
+
rescue Errno
|
27
|
+
$stderr.puts "Got an error connecting, but no segfault."
|
28
|
+
else
|
29
|
+
$stderr.puts "Nope, no segfault!"
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
= postgres
|
2
|
+
|
3
|
+
* https://github.com/ged/ruby-pg
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
This is an old, deprecated version of the Ruby PostgreSQL driver that hasn't
|
8
|
+
been maintained or supported since early 2008.
|
9
|
+
|
10
|
+
You should install/require 'pg' instead.
|
11
|
+
|
12
|
+
If you need the 'postgres' gem for legacy code that can't be converted, you can
|
13
|
+
still install it using an explicit version, like so:
|
14
|
+
|
15
|
+
gem install postgres -v '0.7.9.2008.01.28'
|
16
|
+
gem uninstall postgres -v '>0.7.9.2008.01.28'
|
17
|
+
|
18
|
+
If you have any questions, the nice folks in the Google group can help:
|
19
|
+
|
20
|
+
http://goo.gl/OjOPP / ruby-pg@googlegroups.com
|
21
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'hoe'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
Hoe.spec 'postgres' do
|
9
|
+
self.developer 'Michael Granger', 'ged@FaerieMUD.org'
|
10
|
+
self.dependency 'pg', '~> 0'
|
11
|
+
self.spec_extras[:date] = Date.parse( '2008/01/30' )
|
12
|
+
|
13
|
+
line = '-' * 75
|
14
|
+
msg = paragraphs_of( 'README.txt', 3..-1 )
|
15
|
+
msg.unshift( line )
|
16
|
+
msg.push( line )
|
17
|
+
|
18
|
+
self.spec_extras[:post_install_message] = msg.join( "\n\n" ) + "\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
# vim: syntax=ruby
|