pg 1.2.0-x86-mingw32 → 1.3.0.rc1-x86-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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +80 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +98 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +9 -8
  18. data/Rakefile +31 -140
  19. data/Rakefile.cross +54 -56
  20. data/certs/ged.pem +24 -0
  21. data/ext/errorcodes.def +8 -0
  22. data/ext/errorcodes.txt +3 -1
  23. data/ext/extconf.rb +90 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +35 -1
  27. data/ext/pg.h +18 -0
  28. data/ext/pg_coder.c +90 -24
  29. data/ext/pg_connection.c +538 -279
  30. data/ext/pg_copy_coder.c +45 -15
  31. data/ext/pg_record_coder.c +38 -9
  32. data/ext/pg_result.c +70 -34
  33. data/ext/pg_text_decoder.c +1 -1
  34. data/ext/pg_text_encoder.c +6 -6
  35. data/ext/pg_tuple.c +47 -21
  36. data/ext/pg_type_map.c +41 -8
  37. data/ext/pg_type_map_all_strings.c +14 -1
  38. data/ext/pg_type_map_by_class.c +50 -21
  39. data/ext/pg_type_map_by_column.c +64 -28
  40. data/ext/pg_type_map_by_mri_type.c +47 -18
  41. data/ext/pg_type_map_by_oid.c +52 -23
  42. data/ext/pg_type_map_in_ruby.c +50 -19
  43. data/ext/pg_util.c +2 -2
  44. data/lib/2.5/pg_ext.so +0 -0
  45. data/lib/2.6/pg_ext.so +0 -0
  46. data/lib/2.7/pg_ext.so +0 -0
  47. data/lib/3.0/pg_ext.so +0 -0
  48. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  49. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  50. data/lib/pg/basic_type_map_for_results.rb +81 -0
  51. data/lib/pg/basic_type_registry.rb +296 -0
  52. data/lib/pg/coder.rb +1 -1
  53. data/lib/pg/connection.rb +369 -56
  54. data/lib/pg/version.rb +4 -0
  55. data/lib/pg.rb +38 -24
  56. data/lib/x86-mingw32/libpq.dll +0 -0
  57. data/misc/openssl-pg-segfault.rb +31 -0
  58. data/misc/postgres/History.txt +9 -0
  59. data/misc/postgres/Manifest.txt +5 -0
  60. data/misc/postgres/README.txt +21 -0
  61. data/misc/postgres/Rakefile +21 -0
  62. data/misc/postgres/lib/postgres.rb +16 -0
  63. data/misc/ruby-pg/History.txt +9 -0
  64. data/misc/ruby-pg/Manifest.txt +5 -0
  65. data/misc/ruby-pg/README.txt +21 -0
  66. data/misc/ruby-pg/Rakefile +21 -0
  67. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  68. data/pg.gemspec +32 -0
  69. data/sample/array_insert.rb +20 -0
  70. data/sample/async_api.rb +106 -0
  71. data/sample/async_copyto.rb +39 -0
  72. data/sample/async_mixed.rb +56 -0
  73. data/sample/check_conn.rb +21 -0
  74. data/sample/copydata.rb +71 -0
  75. data/sample/copyfrom.rb +81 -0
  76. data/sample/copyto.rb +19 -0
  77. data/sample/cursor.rb +21 -0
  78. data/sample/disk_usage_report.rb +177 -0
  79. data/sample/issue-119.rb +94 -0
  80. data/sample/losample.rb +69 -0
  81. data/sample/minimal-testcase.rb +17 -0
  82. data/sample/notify_wait.rb +72 -0
  83. data/sample/pg_statistics.rb +285 -0
  84. data/sample/replication_monitor.rb +222 -0
  85. data/sample/test_binary_values.rb +33 -0
  86. data/sample/wal_shipper.rb +434 -0
  87. data/sample/warehouse_partitions.rb +311 -0
  88. data.tar.gz.sig +0 -0
  89. metadata +83 -232
  90. metadata.gz.sig +0 -0
  91. data/ChangeLog +0 -0
  92. data/lib/2.2/pg_ext.so +0 -0
  93. data/lib/2.3/pg_ext.so +0 -0
  94. data/lib/2.4/pg_ext.so +0 -0
  95. data/lib/libpq.dll +0 -0
  96. data/lib/pg/basic_type_mapping.rb +0 -522
  97. data/spec/data/expected_trace.out +0 -26
  98. data/spec/data/random_binary_data +0 -0
  99. data/spec/helpers.rb +0 -382
  100. data/spec/pg/basic_type_mapping_spec.rb +0 -645
  101. data/spec/pg/connection_spec.rb +0 -1911
  102. data/spec/pg/connection_sync_spec.rb +0 -41
  103. data/spec/pg/result_spec.rb +0 -681
  104. data/spec/pg/tuple_spec.rb +0 -333
  105. data/spec/pg/type_map_by_class_spec.rb +0 -138
  106. data/spec/pg/type_map_by_column_spec.rb +0 -226
  107. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  108. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  109. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  110. data/spec/pg/type_map_spec.rb +0 -22
  111. data/spec/pg/type_spec.rb +0 -1123
  112. 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 the given +value+ for use in a connection-parameter string.
29
- def self::quote_connstr( value )
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
- ### Parse the connection +args+ into a connection-parameter string. See PG::Connection.new
35
- ### for valid arguments.
36
- def self::parse_connect_args( *args )
37
- return '' if args.empty?
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
- # Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
44
- # together with PQescapeLiteral().
45
- if PG::Connection.instance_methods.find {|m| m.to_sym == :escape_literal }
46
- options[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
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, /\A#{URI::ABS_URI_REF}\z/
52
- uri = URI(args.first)
53
- options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
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
- options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
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
- options[ k.to_sym ] = v if v
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
- options.merge!( hash_arg )
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.host = nil if options[:host]
75
- uri.port = nil if options[:port]
76
- uri.user = nil if options[:user]
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? && options.empty?
83
- return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
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 transfering data to or from the server.
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
- # Method 'conninfo' was introduced in PostgreSQL 9.3.
241
- if self.instance_methods.find{|m| m.to_sym == :conninfo }
242
-
243
- ### Return the Postgres connection info structure as a Hash keyed by option
244
- ### keyword (as a Symbol).
245
- ###
246
- ### See also #conninfo
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
- REDIRECT_METHODS = {
273
- :exec => [:async_exec, :sync_exec],
274
- :query => [:async_exec, :sync_exec],
275
- :exec_params => [:async_exec_params, :sync_exec_params],
276
- :prepare => [:async_prepare, :sync_prepare],
277
- :exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
278
- :describe_portal => [:async_describe_portal, :sync_describe_portal],
279
- :describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
280
- }
281
-
282
- def self.async_api=(enable)
283
- REDIRECT_METHODS.each do |ali, (async, sync)|
284
- remove_method(ali) if method_defined?(ali)
285
- alias_method( ali, enable ? async : sync )
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
@@ -0,0 +1,4 @@
1
+ module PG
2
+ # Library version
3
+ VERSION = '1.3.0.rc1'
4
+ end
data/lib/pg.rb CHANGED
@@ -1,15 +1,28 @@
1
+
1
2
  # -*- ruby -*-
2
3
  # frozen_string_literal: true
3
4
 
4
- begin
5
- require 'pg_ext'
6
- rescue LoadError
7
- # If it's a Windows binary gem, try the <major>.<minor> subdirectory
8
- if RUBY_PLATFORM =~/(mswin|mingw)/i
9
- major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
10
- raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
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
- add_dll_path = proc do |path, &block|
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,27 +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
- # Temporary add this directory for DLL search, so that libpq.dll can be found.
25
- add_dll_path.call(__dir__) do
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}"
26
47
  require "#{major_minor}/pg_ext"
48
+ else
49
+ require 'pg_ext'
27
50
  end
28
- else
29
- raise
30
51
  end
31
52
 
32
- end
33
-
34
-
35
- # The top-level PG namespace.
36
- module PG
37
-
38
- # Library version
39
- VERSION = '1.2.0'
40
-
41
- # VCS revision
42
- REVISION = %q$Revision: 6f611e78845a $
43
53
 
44
54
  class NotAllCopyDataRetrieved < PG::Error
45
55
  end
@@ -64,10 +74,14 @@ module PG
64
74
  require 'pg/binary_decoder'
65
75
  require 'pg/text_encoder'
66
76
  require 'pg/text_decoder'
67
- require 'pg/basic_type_mapping'
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'
68
81
  require 'pg/type_map_by_column'
69
82
  require 'pg/connection'
70
83
  require 'pg/result'
71
84
  require 'pg/tuple'
85
+ require 'pg/version'
72
86
 
73
87
  end # module PG
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,9 @@
1
+ == v0.8.0 [2012-02-09] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ This placeholder version.
4
+
5
+
6
+ == v0.7.9.2008.01.28 [2008-01-28] Jeff Davis <<ruby-pg@j-davis.com>>
7
+
8
+ The last actual version.
9
+
@@ -0,0 +1,5 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/postgres.rb
@@ -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