pg 1.2.3-x86-mingw32 → 1.3.0.rc4-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 +86 -0
  6. data/.github/workflows/source-gem.yml +130 -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 +77 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +6 -5
  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 +91 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +59 -4
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +676 -492
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +39 -11
  32. data/ext/pg_result.c +61 -31
  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 +49 -24
  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/3.1/pg_ext.so +0 -0
  49. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  50. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  51. data/lib/pg/basic_type_map_for_results.rb +81 -0
  52. data/lib/pg/basic_type_registry.rb +296 -0
  53. data/lib/pg/coder.rb +1 -1
  54. data/lib/pg/connection.rb +587 -58
  55. data/lib/pg/version.rb +4 -0
  56. data/lib/pg.rb +40 -27
  57. data/lib/x86-mingw32/libpq.dll +0 -0
  58. data/misc/openssl-pg-segfault.rb +31 -0
  59. data/misc/postgres/History.txt +9 -0
  60. data/misc/postgres/Manifest.txt +5 -0
  61. data/misc/postgres/README.txt +21 -0
  62. data/misc/postgres/Rakefile +21 -0
  63. data/misc/postgres/lib/postgres.rb +16 -0
  64. data/misc/ruby-pg/History.txt +9 -0
  65. data/misc/ruby-pg/Manifest.txt +5 -0
  66. data/misc/ruby-pg/README.txt +21 -0
  67. data/misc/ruby-pg/Rakefile +21 -0
  68. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  69. data/pg.gemspec +32 -0
  70. data/sample/array_insert.rb +20 -0
  71. data/sample/async_api.rb +106 -0
  72. data/sample/async_copyto.rb +39 -0
  73. data/sample/async_mixed.rb +56 -0
  74. data/sample/check_conn.rb +21 -0
  75. data/sample/copydata.rb +71 -0
  76. data/sample/copyfrom.rb +81 -0
  77. data/sample/copyto.rb +19 -0
  78. data/sample/cursor.rb +21 -0
  79. data/sample/disk_usage_report.rb +177 -0
  80. data/sample/issue-119.rb +94 -0
  81. data/sample/losample.rb +69 -0
  82. data/sample/minimal-testcase.rb +17 -0
  83. data/sample/notify_wait.rb +72 -0
  84. data/sample/pg_statistics.rb +285 -0
  85. data/sample/replication_monitor.rb +222 -0
  86. data/sample/test_binary_values.rb +33 -0
  87. data/sample/wal_shipper.rb +434 -0
  88. data/sample/warehouse_partitions.rb +311 -0
  89. data.tar.gz.sig +0 -0
  90. metadata +93 -233
  91. metadata.gz.sig +0 -0
  92. data/ChangeLog +0 -0
  93. data/lib/2.2/pg_ext.so +0 -0
  94. data/lib/2.3/pg_ext.so +0 -0
  95. data/lib/2.4/pg_ext.so +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 -380
  100. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  101. data/spec/pg/connection_spec.rb +0 -1949
  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,9 +3,11 @@
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
- # {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
10
+ # {libpq}[http://www.postgresql.org/docs/current/libpq.html], the C
9
11
  # application programmer's interface to PostgreSQL. Some familiarity with libpq
10
12
  # is recommended, but not necessary.
11
13
  #
@@ -19,68 +21,153 @@ require 'uri'
19
21
  #
20
22
  # See the PG::Result class for information on working with the results of a query.
21
23
  #
24
+ # Many methods of this class have three variants kind of:
25
+ # 1. #exec - the base method which is an alias to #async_exec .
26
+ # This is the method that should be used in general.
27
+ # 2. #async_exec - the async aware version of the method, implemented by libpq's async API.
28
+ # 3. #sync_exec - the method version that is implemented by blocking function(s) of libpq.
29
+ #
30
+ # Sync and async version of the method can be switched by Connection.async_api= , however it is not recommended to change the default.
22
31
  class PG::Connection
23
32
 
24
33
  # The order the options are passed to the ::connect method.
25
34
  CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
26
35
 
27
36
 
28
- ### Quote the given +value+ for use in a connection-parameter string.
29
- def self::quote_connstr( value )
37
+ ### Quote a single +value+ for use in a connection-parameter string.
38
+ def self.quote_connstr( value )
30
39
  return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
31
40
  end
32
41
 
42
+ # Convert Hash options to connection String
43
+ #
44
+ # Values are properly quoted and escaped.
45
+ def self.connect_hash_to_string( hash )
46
+ hash.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
47
+ end
33
48
 
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 = ''
49
+ # Decode a connection string to Hash options
50
+ #
51
+ # Value are properly unquoted and unescaped.
52
+ def self.connect_string_to_hash( str )
41
53
  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 }
54
+ key = nil
55
+ value = String.new
56
+ str.scan(/\G\s*(?>([^\s\\\']+)\s*=\s*|([^\s\\\']+)|'((?:[^\'\\]|\\.)*)'|(\\.?)|(\S))(\s|\z)?/m) do
57
+ |k, word, sq, esc, garbage, sep|
58
+ raise ArgumentError, "unterminated quoted string in connection info string: #{str.inspect}" if garbage
59
+ if k
60
+ key = k
61
+ else
62
+ value << (word || (sq || esc).gsub(/\\(.)/, '\\1'))
63
+ end
64
+ if sep
65
+ raise ArgumentError, "missing = after #{value.inspect}" unless key
66
+ options[key.to_sym] = value
67
+ key = nil
68
+ value = String.new
69
+ end
47
70
  end
71
+ options
72
+ end
73
+
74
+ # URI defined in RFC3986
75
+ # This regexp is modified to allow host to specify multiple comma separated components captured as <hostports> and to disallow comma in hostnames.
76
+ # Taken from: https://github.com/ruby/ruby/blob/be04006c7d2f9aeb7e9d8d09d945b3a9c7850202/lib/uri/rfc3986_parser.rb#L6
77
+ 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*))?)/
78
+ 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/
79
+
80
+ # Parse the connection +args+ into a connection-parameter string.
81
+ # See PG::Connection.new for valid arguments.
82
+ #
83
+ # It accepts:
84
+ # * an option String kind of "host=name port=5432"
85
+ # * an option Hash kind of {host: "name", port: 5432}
86
+ # * URI string
87
+ # * URI object
88
+ # * positional arguments
89
+ #
90
+ # The method adds the option "hostaddr" and "fallback_application_name" if they aren't already set.
91
+ # The URI and the options string is passed through and "hostaddr" as well as "fallback_application_name"
92
+ # are added to the end.
93
+ def self::parse_connect_args( *args )
94
+ hash_arg = args.last.is_a?( Hash ) ? args.pop.transform_keys(&:to_sym) : {}
95
+ option_string = ""
96
+ iopts = {}
48
97
 
49
98
  if args.length == 1
50
99
  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
100
+ when URI, POSTGRESQL_URI
101
+ uri = args.first.to_s
102
+ uri_match = POSTGRESQL_URI.match(uri)
103
+ if uri_match['query']
104
+ iopts = URI.decode_www_form(uri_match['query']).to_h.transform_keys(&:to_sym)
105
+ end
106
+ # extract "host1,host2" from "host1:5432,host2:5432"
107
+ iopts[:host] = uri_match['hostports'].split(',', -1).map do |hostport|
108
+ hostmatch = HOST_AND_PORT.match(hostport)
109
+ hostmatch['IPv6address'] || hostmatch['IPv4address'] || hostmatch['reg-name']&.gsub(/%(\h\h)/){ $1.hex.chr }
110
+ end.join(',')
111
+ oopts = {}
54
112
  when /=/
55
113
  # Option string style
56
114
  option_string = args.first.to_s
115
+ iopts = connect_string_to_hash(option_string)
116
+ oopts = {}
57
117
  else
58
- # Positional parameters
59
- options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
118
+ # Positional parameters (only host given)
119
+ iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
120
+ oopts = iopts.dup
60
121
  end
61
122
  else
123
+ # Positional parameters
62
124
  max = CONNECT_ARGUMENT_ORDER.length
63
125
  raise ArgumentError,
64
126
  "Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
65
127
 
66
128
  CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
67
- options[ k.to_sym ] = v if v
129
+ iopts[ k.to_sym ] = v if v
130
+ end
131
+ iopts.delete(:tty) # ignore obsolete tty parameter
132
+ oopts = iopts.dup
133
+ end
134
+
135
+ iopts.merge!( hash_arg )
136
+ oopts.merge!( hash_arg )
137
+
138
+ # Resolve DNS in Ruby to avoid blocking state while connecting, when it ...
139
+ if (host=iopts[:host]) && !iopts[:hostaddr]
140
+ hostaddrs = host.split(",", -1).map do |mhost|
141
+ if !mhost.empty? && !mhost.start_with?("/") && # isn't UnixSocket
142
+ # isn't a path on Windows
143
+ (RUBY_PLATFORM !~ /mingw|mswin/ || mhost !~ /\A\w:[\/\\]/)
144
+
145
+ if Fiber.respond_to?(:scheduler) &&
146
+ Fiber.scheduler &&
147
+ RUBY_VERSION < '3.1.'
148
+
149
+ # Use a second thread to avoid blocking of the scheduler.
150
+ # `IPSocket.getaddress` isn't fiber aware before ruby-3.1.
151
+ Thread.new{ IPSocket.getaddress(mhost) rescue '' }.value
152
+ else
153
+ IPSocket.getaddress(mhost) rescue ''
154
+ end
155
+ end
68
156
  end
157
+ oopts[:hostaddr] = hostaddrs.join(",") if hostaddrs.any?
69
158
  end
70
159
 
71
- options.merge!( hash_arg )
160
+ if !iopts[:fallback_application_name]
161
+ oopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
162
+ end
72
163
 
73
164
  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}://" )
165
+ uri += uri_match['query'] ? "&" : "?"
166
+ uri += URI.encode_www_form( oopts )
167
+ return uri
81
168
  else
82
- option_string += ' ' unless option_string.empty? && options.empty?
83
- return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
169
+ option_string += ' ' unless option_string.empty? && oopts.empty?
170
+ return option_string + connect_hash_to_string(oopts)
84
171
  end
85
172
  end
86
173
 
@@ -88,7 +175,7 @@ class PG::Connection
88
175
  # call-seq:
89
176
  # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
90
177
  #
91
- # Execute a copy process for transfering data to or from the server.
178
+ # Execute a copy process for transferring data to or from the server.
92
179
  #
93
180
  # This issues the SQL COPY command via #exec. The response to this
94
181
  # (if there is no error in the command) is a PG::Result object that
@@ -214,6 +301,25 @@ class PG::Connection
214
301
  define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
215
302
  end
216
303
 
304
+ #
305
+ # call-seq:
306
+ # conn.transaction { |conn| ... } -> result of the block
307
+ #
308
+ # Executes a +BEGIN+ at the start of the block,
309
+ # and a +COMMIT+ at the end of the block, or
310
+ # +ROLLBACK+ if any exception occurs.
311
+ def transaction
312
+ exec "BEGIN"
313
+ res = yield(self)
314
+ rescue Exception
315
+ cancel if transaction_status != PG::PQTRANS_IDLE
316
+ block
317
+ exec "ROLLBACK"
318
+ raise
319
+ else
320
+ exec "COMMIT"
321
+ res
322
+ end
217
323
 
218
324
  ### Returns an array of Hashes with connection defaults. See ::conndefaults
219
325
  ### for details.
@@ -237,17 +343,13 @@ class PG::Connection
237
343
  return self.class.conndefaults_hash
238
344
  end
239
345
 
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
346
+ ### Return the Postgres connection info structure as a Hash keyed by option
347
+ ### keyword (as a Symbol).
348
+ ###
349
+ ### See also #conninfo
350
+ def conninfo_hash
351
+ return self.conninfo.each_with_object({}) do |info, hash|
352
+ hash[ info[:keyword].to_sym ] = info[:val]
251
353
  end
252
354
  end
253
355
 
@@ -269,23 +371,450 @@ class PG::Connection
269
371
  end
270
372
  end
271
373
 
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 )
374
+ # call-seq:
375
+ # conn.get_result() -> PG::Result
376
+ # conn.get_result() {|pg_result| block }
377
+ #
378
+ # Blocks waiting for the next result from a call to
379
+ # #send_query (or another asynchronous command), and returns
380
+ # it. Returns +nil+ if no more results are available.
381
+ #
382
+ # Note: call this function repeatedly until it returns +nil+, or else
383
+ # you will not be able to issue further commands.
384
+ #
385
+ # If the optional code block is given, it will be passed <i>result</i> as an argument,
386
+ # and the PG::Result object will automatically be cleared when the block terminates.
387
+ # In this instance, <code>conn.exec</code> returns the value of the block.
388
+ def get_result(*args)
389
+ block
390
+ sync_get_result
391
+ end
392
+ alias async_get_result get_result
393
+
394
+ # call-seq:
395
+ # conn.get_copy_data( [ nonblock = false [, decoder = nil ]] ) -> Object
396
+ #
397
+ # Return one row of data, +nil+
398
+ # if the copy is done, or +false+ if the call would
399
+ # block (only possible if _nonblock_ is true).
400
+ #
401
+ # If _decoder_ is not set or +nil+, data is returned as binary string.
402
+ #
403
+ # If _decoder_ is set to a PG::Coder derivation, the return type depends on this decoder.
404
+ # PG::TextDecoder::CopyRow decodes the received data fields from one row of PostgreSQL's
405
+ # COPY text format to an Array of Strings.
406
+ # Optionally the decoder can type cast the single fields to various Ruby types in one step,
407
+ # if PG::TextDecoder::CopyRow#type_map is set accordingly.
408
+ #
409
+ # See also #copy_data.
410
+ #
411
+ def get_copy_data(async=false, decoder=nil)
412
+ if async
413
+ return sync_get_copy_data(async, decoder)
414
+ else
415
+ while (res=sync_get_copy_data(true, decoder)) == false
416
+ socket_io.wait_readable
417
+ consume_input
418
+ end
419
+ return res
420
+ end
421
+ end
422
+ alias async_get_copy_data get_copy_data
423
+
424
+
425
+ # In async_api=true mode (default) all send calls run nonblocking.
426
+ # The difference is that setnonblocking(true) disables automatic handling of would-block cases.
427
+ # In async_api=false mode all send calls run directly on libpq.
428
+ # Blocking vs. nonblocking state can be changed in libpq.
429
+
430
+ # call-seq:
431
+ # conn.setnonblocking(Boolean) -> nil
432
+ #
433
+ # Sets the nonblocking status of the connection.
434
+ # In the blocking state, calls to #send_query
435
+ # will block until the message is sent to the server,
436
+ # but will not wait for the query results.
437
+ # In the nonblocking state, calls to #send_query
438
+ # will return an error if the socket is not ready for
439
+ # writing.
440
+ # Note: This function does not affect #exec, because
441
+ # that function doesn't return until the server has
442
+ # processed the query and returned the results.
443
+ #
444
+ # Returns +nil+.
445
+ def setnonblocking(enabled)
446
+ singleton_class.async_send_api = !enabled
447
+ self.flush_data = !enabled
448
+ sync_setnonblocking(true)
449
+ end
450
+ alias async_setnonblocking setnonblocking
451
+
452
+ # sync/async isnonblocking methods are switched by async_setnonblocking()
453
+
454
+ # call-seq:
455
+ # conn.isnonblocking() -> Boolean
456
+ #
457
+ # Returns the blocking status of the database connection.
458
+ # Returns +true+ if the connection is set to nonblocking mode and +false+ if blocking.
459
+ def isnonblocking
460
+ false
461
+ end
462
+ alias async_isnonblocking isnonblocking
463
+ alias nonblocking? isnonblocking
464
+
465
+ # call-seq:
466
+ # conn.put_copy_data( buffer [, encoder] ) -> Boolean
467
+ #
468
+ # Transmits _buffer_ as copy data to the server.
469
+ # Returns true if the data was sent, false if it was
470
+ # not sent (false is only possible if the connection
471
+ # is in nonblocking mode, and this command would block).
472
+ #
473
+ # _encoder_ can be a PG::Coder derivation (typically PG::TextEncoder::CopyRow).
474
+ # This encodes the data fields given as _buffer_ from an Array of Strings to
475
+ # PostgreSQL's COPY text format inclusive proper escaping. Optionally
476
+ # the encoder can type cast the fields from various Ruby types in one step,
477
+ # if PG::TextEncoder::CopyRow#type_map is set accordingly.
478
+ #
479
+ # Raises an exception if an error occurs.
480
+ #
481
+ # See also #copy_data.
482
+ #
483
+ def put_copy_data(buffer, encoder=nil)
484
+ until sync_put_copy_data(buffer, encoder)
485
+ flush
486
+ end
487
+ flush
488
+ end
489
+ alias async_put_copy_data put_copy_data
490
+
491
+ # call-seq:
492
+ # conn.put_copy_end( [ error_message ] ) -> Boolean
493
+ #
494
+ # Sends end-of-data indication to the server.
495
+ #
496
+ # _error_message_ is an optional parameter, and if set,
497
+ # forces the COPY command to fail with the string
498
+ # _error_message_.
499
+ #
500
+ # Returns true if the end-of-data was sent, #false* if it was
501
+ # not sent (*false* is only possible if the connection
502
+ # is in nonblocking mode, and this command would block).
503
+ def put_copy_end(*args)
504
+ until sync_put_copy_end(*args)
505
+ flush
506
+ end
507
+ flush
508
+ end
509
+ alias async_put_copy_end put_copy_end
510
+
511
+ if method_defined? :sync_encrypt_password
512
+ # call-seq:
513
+ # conn.encrypt_password( password, username, algorithm=nil ) -> String
514
+ #
515
+ # This function is intended to be used by client applications that wish to send commands like <tt>ALTER USER joe PASSWORD 'pwd'</tt>.
516
+ # It is good practice not to send the original cleartext password in such a command, because it might be exposed in command logs, activity displays, and so on.
517
+ # Instead, use this function to convert the password to encrypted form before it is sent.
518
+ #
519
+ # The +password+ and +username+ arguments are the cleartext password, and the SQL name of the user it is for.
520
+ # +algorithm+ specifies the encryption algorithm to use to encrypt the password.
521
+ # Currently supported algorithms are +md5+ and +scram-sha-256+ (+on+ and +off+ are also accepted as aliases for +md5+, for compatibility with older server versions).
522
+ # Note that support for +scram-sha-256+ was introduced in PostgreSQL version 10, and will not work correctly with older server versions.
523
+ # If algorithm is omitted or +nil+, this function will query the server for the current value of the +password_encryption+ setting.
524
+ # That can block, and will fail if the current transaction is aborted, or if the connection is busy executing another query.
525
+ # If you wish to use the default algorithm for the server but want to avoid blocking, query +password_encryption+ yourself before calling #encrypt_password, and pass that value as the algorithm.
526
+ #
527
+ # Return value is the encrypted password.
528
+ # The caller can assume the string doesn't contain any special characters that would require escaping.
529
+ #
530
+ # Available since PostgreSQL-10.
531
+ # See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-misc.html#LIBPQ-PQENCRYPTPASSWORDCONN].
532
+ def encrypt_password( password, username, algorithm=nil )
533
+ algorithm ||= exec("SHOW password_encryption").getvalue(0,0)
534
+ sync_encrypt_password(password, username, algorithm)
535
+ end
536
+ alias async_encrypt_password encrypt_password
537
+ end
538
+
539
+ # call-seq:
540
+ # conn.reset()
541
+ #
542
+ # Resets the backend connection. This method closes the
543
+ # backend connection and tries to re-connect.
544
+ def reset
545
+ reset_start
546
+ async_connect_or_reset(:reset_poll)
547
+ end
548
+ alias async_reset reset
549
+
550
+ # call-seq:
551
+ # conn.cancel() -> String
552
+ #
553
+ # Requests cancellation of the command currently being
554
+ # processed.
555
+ #
556
+ # Returns +nil+ on success, or a string containing the
557
+ # error message if a failure occurs.
558
+ def cancel
559
+ be_pid = backend_pid
560
+ be_key = backend_key
561
+ cancel_request = [0x10, 1234, 5678, be_pid, be_key].pack("NnnNN")
562
+
563
+ if Fiber.respond_to?(:scheduler) && Fiber.scheduler && RUBY_PLATFORM =~ /mingw|mswin/
564
+ # Ruby's nonblocking IO is not really supported on Windows.
565
+ # We work around by using threads and explicit calls to wait_readable/wait_writable.
566
+ cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
567
+ begin
568
+ cl.write_nonblock(cancel_request)
569
+ rescue IO::WaitReadable, Errno::EINTR
570
+ cl.wait_writable
571
+ retry
572
+ end
573
+ begin
574
+ cl.read_nonblock(1)
575
+ rescue IO::WaitReadable, Errno::EINTR
576
+ cl.wait_readable
577
+ retry
578
+ rescue EOFError
579
+ end
580
+ elsif RUBY_ENGINE == 'truffleruby'
581
+ begin
582
+ cl = socket_io.remote_address.connect
583
+ rescue NotImplementedError
584
+ # Workaround for truffleruby < 21.3.0
585
+ cl2 = Socket.for_fd(socket_io.fileno)
586
+ cl2.autoclose = false
587
+ adr = cl2.remote_address
588
+ if adr.ip?
589
+ cl = TCPSocket.new(adr.ip_address, adr.ip_port)
590
+ cl.autoclose = false
591
+ else
592
+ cl = UNIXSocket.new(adr.unix_path)
593
+ cl.autoclose = false
594
+ end
595
+ end
596
+ cl.write(cancel_request)
597
+ cl.read(1)
598
+ else
599
+ cl = socket_io.remote_address.connect
600
+ # Send CANCEL_REQUEST_CODE and parameters
601
+ cl.write(cancel_request)
602
+ # Wait for the postmaster to close the connection, which indicates that it's processed the request.
603
+ cl.read(1)
604
+ end
605
+
606
+ cl.close
607
+ nil
608
+ rescue SystemCallError => err
609
+ err.to_s
610
+ end
611
+ alias async_cancel cancel
612
+
613
+ private def async_connect_or_reset(poll_meth)
614
+ # Now grab a reference to the underlying socket so we know when the connection is established
615
+ socket = socket_io
616
+
617
+ # Track the progress of the connection, waiting for the socket to become readable/writable before polling it
618
+ poll_status = PG::PGRES_POLLING_WRITING
619
+ until poll_status == PG::PGRES_POLLING_OK ||
620
+ poll_status == PG::PGRES_POLLING_FAILED
621
+
622
+ # If the socket needs to read, wait 'til it becomes readable to poll again
623
+ case poll_status
624
+ when PG::PGRES_POLLING_READING
625
+ socket.wait_readable
626
+
627
+ # ...and the same for when the socket needs to write
628
+ when PG::PGRES_POLLING_WRITING
629
+ socket.wait_writable
630
+ end
631
+
632
+ # Check to see if it's finished or failed yet
633
+ poll_status = send( poll_meth )
634
+ end
635
+
636
+ raise(PG::ConnectionBad, error_message) unless status == PG::CONNECTION_OK
637
+
638
+ # Set connection to nonblocking to handle all blocking states in ruby.
639
+ # That way a fiber scheduler is able to handle IO requests.
640
+ sync_setnonblocking(true)
641
+ self.flush_data = true
642
+ set_default_encoding
643
+
644
+ self
645
+ end
646
+
647
+ class << self
648
+ # call-seq:
649
+ # PG::Connection.new -> conn
650
+ # PG::Connection.new(connection_hash) -> conn
651
+ # PG::Connection.new(connection_string) -> conn
652
+ # PG::Connection.new(host, port, options, tty, dbname, user, password) -> conn
653
+ #
654
+ # Create a connection to the specified server.
655
+ #
656
+ # +connection_hash+ must be a ruby Hash with connection parameters.
657
+ # See the {list of valid parameters}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS] in the PostgreSQL documentation.
658
+ #
659
+ # There are two accepted formats for +connection_string+: plain <code>keyword = value</code> strings and URIs.
660
+ # See the documentation of {connection strings}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING].
661
+ #
662
+ # The positional parameter form has the same functionality except that the missing parameters will always take on default values. The parameters are:
663
+ # [+host+]
664
+ # server hostname
665
+ # [+port+]
666
+ # server port number
667
+ # [+options+]
668
+ # backend options
669
+ # [+tty+]
670
+ # (ignored in all versions of PostgreSQL)
671
+ # [+dbname+]
672
+ # connecting database name
673
+ # [+user+]
674
+ # login user name
675
+ # [+password+]
676
+ # login password
677
+ #
678
+ # Examples:
679
+ #
680
+ # # Connect using all defaults
681
+ # PG::Connection.new
682
+ #
683
+ # # As a Hash
684
+ # PG::Connection.new( dbname: 'test', port: 5432 )
685
+ #
686
+ # # As a String
687
+ # PG::Connection.new( "dbname=test port=5432" )
688
+ #
689
+ # # As an Array
690
+ # PG::Connection.new( nil, 5432, nil, nil, 'test', nil, nil )
691
+ #
692
+ # # As an URI
693
+ # PG::Connection.new( "postgresql://user:pass@pgsql.example.com:5432/testdb?sslmode=require" )
694
+ #
695
+ # If the Ruby default internal encoding is set (i.e., <code>Encoding.default_internal != nil</code>), the
696
+ # connection will have its +client_encoding+ set accordingly.
697
+ #
698
+ # Raises a PG::Error if the connection fails.
699
+ def new(*args, **kwargs)
700
+ conn = self.connect_start(*args, **kwargs ) or
701
+ raise(PG::Error, "Unable to create a new connection")
702
+
703
+ raise(PG::ConnectionBad, conn.error_message) if conn.status == PG::CONNECTION_BAD
704
+
705
+ conn.send(:async_connect_or_reset, :connect_poll)
706
+ end
707
+ alias async_connect new
708
+ alias connect new
709
+ alias open new
710
+ alias setdb new
711
+ alias setdblogin new
712
+
713
+ # call-seq:
714
+ # PG::Connection.ping(connection_hash) -> Integer
715
+ # PG::Connection.ping(connection_string) -> Integer
716
+ # PG::Connection.ping(host, port, options, tty, dbname, login, password) -> Integer
717
+ #
718
+ # Check server status.
719
+ #
720
+ # See PG::Connection.new for a description of the parameters.
721
+ #
722
+ # Returns one of:
723
+ # [+PQPING_OK+]
724
+ # server is accepting connections
725
+ # [+PQPING_REJECT+]
726
+ # server is alive but rejecting connections
727
+ # [+PQPING_NO_RESPONSE+]
728
+ # could not establish connection
729
+ # [+PQPING_NO_ATTEMPT+]
730
+ # connection not attempted (bad params)
731
+ def ping(*args)
732
+ if Fiber.respond_to?(:scheduler) && Fiber.scheduler
733
+ # Run PQping in a second thread to avoid blocking of the scheduler.
734
+ # Unfortunately there's no nonblocking way to run ping.
735
+ Thread.new { sync_ping(*args) }.value
736
+ else
737
+ sync_ping(*args)
738
+ end
739
+ end
740
+ alias async_ping ping
741
+
742
+ REDIRECT_CLASS_METHODS = {
743
+ :new => [:async_connect, :sync_connect],
744
+ :connect => [:async_connect, :sync_connect],
745
+ :open => [:async_connect, :sync_connect],
746
+ :setdb => [:async_connect, :sync_connect],
747
+ :setdblogin => [:async_connect, :sync_connect],
748
+ :ping => [:async_ping, :sync_ping],
749
+ }
750
+
751
+ # These methods are affected by PQsetnonblocking
752
+ REDIRECT_SEND_METHODS = {
753
+ :isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
754
+ :nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
755
+ :put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
756
+ :put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
757
+ :flush => [:async_flush, :sync_flush],
758
+ }
759
+ REDIRECT_METHODS = {
760
+ :exec => [:async_exec, :sync_exec],
761
+ :query => [:async_exec, :sync_exec],
762
+ :exec_params => [:async_exec_params, :sync_exec_params],
763
+ :prepare => [:async_prepare, :sync_prepare],
764
+ :exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
765
+ :describe_portal => [:async_describe_portal, :sync_describe_portal],
766
+ :describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
767
+ :setnonblocking => [:async_setnonblocking, :sync_setnonblocking],
768
+ :get_result => [:async_get_result, :sync_get_result],
769
+ :get_last_result => [:async_get_last_result, :sync_get_last_result],
770
+ :get_copy_data => [:async_get_copy_data, :sync_get_copy_data],
771
+ :reset => [:async_reset, :sync_reset],
772
+ :set_client_encoding => [:async_set_client_encoding, :sync_set_client_encoding],
773
+ :client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
774
+ :cancel => [:async_cancel, :sync_cancel],
775
+ }
776
+
777
+ if PG::Connection.instance_methods.include? :async_encrypt_password
778
+ REDIRECT_METHODS.merge!({
779
+ :encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
780
+ })
781
+ end
782
+
783
+ def async_send_api=(enable)
784
+ REDIRECT_SEND_METHODS.each do |ali, (async, sync)|
785
+ undef_method(ali) if method_defined?(ali)
786
+ alias_method( ali, enable ? async : sync )
787
+ end
788
+ end
789
+
790
+ # Switch between sync and async libpq API.
791
+ #
792
+ # PG::Connection.async_api = true
793
+ # this is the default.
794
+ # It sets an alias from #exec to #async_exec, #reset to #async_reset and so on.
795
+ #
796
+ # PG::Connection.async_api = false
797
+ # sets an alias from #exec to #sync_exec, #reset to #sync_reset and so on.
798
+ #
799
+ # pg-1.1.0+ defaults to libpq's async API for query related blocking methods.
800
+ # pg-1.3.0+ defaults to libpq's async API for all possibly blocking methods.
801
+ #
802
+ # _PLEASE_ _NOTE_: This method is not part of the public API and is for debug and development use only.
803
+ # Do not use this method in production code.
804
+ # Any issues with the default setting of <tt>async_api=true</tt> should be reported to the maintainers instead.
805
+ #
806
+ def async_api=(enable)
807
+ self.async_send_api = enable
808
+ REDIRECT_METHODS.each do |ali, (async, sync)|
809
+ remove_method(ali) if method_defined?(ali)
810
+ alias_method( ali, enable ? async : sync )
811
+ end
812
+ REDIRECT_CLASS_METHODS.each do |ali, (async, sync)|
813
+ singleton_class.remove_method(ali) if method_defined?(ali)
814
+ singleton_class.alias_method(ali, enable ? async : sync )
815
+ end
286
816
  end
287
817
  end
288
818
 
289
- # pg-1.1.0+ defaults to libpq's async API for query related blocking methods
290
819
  self.async_api = true
291
820
  end # class PG::Connection