pg 1.3.0.rc2-x64-mingw-ucrt

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 (111) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.gemtest +0 -0
  6. data/.github/workflows/binary-gems.yml +85 -0
  7. data/.github/workflows/source-gem.yml +130 -0
  8. data/.gitignore +13 -0
  9. data/.hgsigs +34 -0
  10. data/.hgtags +41 -0
  11. data/.irbrc +23 -0
  12. data/.pryrc +23 -0
  13. data/.tm_properties +21 -0
  14. data/.travis.yml +49 -0
  15. data/BSDL +22 -0
  16. data/Contributors.rdoc +46 -0
  17. data/Gemfile +14 -0
  18. data/History.rdoc +648 -0
  19. data/LICENSE +56 -0
  20. data/Manifest.txt +72 -0
  21. data/POSTGRES +23 -0
  22. data/README-OS_X.rdoc +68 -0
  23. data/README-Windows.rdoc +56 -0
  24. data/README.ja.rdoc +13 -0
  25. data/README.rdoc +214 -0
  26. data/Rakefile +106 -0
  27. data/Rakefile.cross +300 -0
  28. data/certs/ged.pem +24 -0
  29. data/ext/errorcodes.def +1040 -0
  30. data/ext/errorcodes.rb +45 -0
  31. data/ext/errorcodes.txt +496 -0
  32. data/ext/extconf.rb +165 -0
  33. data/ext/gvl_wrappers.c +21 -0
  34. data/ext/gvl_wrappers.h +264 -0
  35. data/ext/pg.c +732 -0
  36. data/ext/pg.h +385 -0
  37. data/ext/pg_binary_decoder.c +229 -0
  38. data/ext/pg_binary_encoder.c +163 -0
  39. data/ext/pg_coder.c +615 -0
  40. data/ext/pg_connection.c +4415 -0
  41. data/ext/pg_copy_coder.c +628 -0
  42. data/ext/pg_errors.c +95 -0
  43. data/ext/pg_record_coder.c +519 -0
  44. data/ext/pg_result.c +1683 -0
  45. data/ext/pg_text_decoder.c +987 -0
  46. data/ext/pg_text_encoder.c +814 -0
  47. data/ext/pg_tuple.c +575 -0
  48. data/ext/pg_type_map.c +199 -0
  49. data/ext/pg_type_map_all_strings.c +129 -0
  50. data/ext/pg_type_map_by_class.c +269 -0
  51. data/ext/pg_type_map_by_column.c +349 -0
  52. data/ext/pg_type_map_by_mri_type.c +313 -0
  53. data/ext/pg_type_map_by_oid.c +385 -0
  54. data/ext/pg_type_map_in_ruby.c +330 -0
  55. data/ext/pg_util.c +149 -0
  56. data/ext/pg_util.h +65 -0
  57. data/ext/vc/pg.sln +26 -0
  58. data/ext/vc/pg_18/pg.vcproj +216 -0
  59. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  60. data/lib/3.1/pg_ext.so +0 -0
  61. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  62. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  63. data/lib/pg/basic_type_map_for_results.rb +81 -0
  64. data/lib/pg/basic_type_registry.rb +296 -0
  65. data/lib/pg/binary_decoder.rb +23 -0
  66. data/lib/pg/coder.rb +104 -0
  67. data/lib/pg/connection.rb +813 -0
  68. data/lib/pg/constants.rb +12 -0
  69. data/lib/pg/exceptions.rb +12 -0
  70. data/lib/pg/result.rb +43 -0
  71. data/lib/pg/text_decoder.rb +46 -0
  72. data/lib/pg/text_encoder.rb +59 -0
  73. data/lib/pg/tuple.rb +30 -0
  74. data/lib/pg/type_map_by_column.rb +16 -0
  75. data/lib/pg/version.rb +4 -0
  76. data/lib/pg.rb +87 -0
  77. data/lib/x64-mingw-ucrt/libpq.dll +0 -0
  78. data/misc/openssl-pg-segfault.rb +31 -0
  79. data/misc/postgres/History.txt +9 -0
  80. data/misc/postgres/Manifest.txt +5 -0
  81. data/misc/postgres/README.txt +21 -0
  82. data/misc/postgres/Rakefile +21 -0
  83. data/misc/postgres/lib/postgres.rb +16 -0
  84. data/misc/ruby-pg/History.txt +9 -0
  85. data/misc/ruby-pg/Manifest.txt +5 -0
  86. data/misc/ruby-pg/README.txt +21 -0
  87. data/misc/ruby-pg/Rakefile +21 -0
  88. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  89. data/pg.gemspec +32 -0
  90. data/sample/array_insert.rb +20 -0
  91. data/sample/async_api.rb +106 -0
  92. data/sample/async_copyto.rb +39 -0
  93. data/sample/async_mixed.rb +56 -0
  94. data/sample/check_conn.rb +21 -0
  95. data/sample/copydata.rb +71 -0
  96. data/sample/copyfrom.rb +81 -0
  97. data/sample/copyto.rb +19 -0
  98. data/sample/cursor.rb +21 -0
  99. data/sample/disk_usage_report.rb +177 -0
  100. data/sample/issue-119.rb +94 -0
  101. data/sample/losample.rb +69 -0
  102. data/sample/minimal-testcase.rb +17 -0
  103. data/sample/notify_wait.rb +72 -0
  104. data/sample/pg_statistics.rb +285 -0
  105. data/sample/replication_monitor.rb +222 -0
  106. data/sample/test_binary_values.rb +33 -0
  107. data/sample/wal_shipper.rb +434 -0
  108. data/sample/warehouse_partitions.rb +311 -0
  109. data.tar.gz.sig +0 -0
  110. metadata +188 -0
  111. metadata.gz.sig +0 -0
@@ -0,0 +1,813 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'pg' unless defined?( PG )
5
+ require 'uri'
6
+ require 'io/wait'
7
+ require 'socket'
8
+
9
+ # The PostgreSQL connection class. The interface for this class is based on
10
+ # {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
11
+ # application programmer's interface to PostgreSQL. Some familiarity with libpq
12
+ # is recommended, but not necessary.
13
+ #
14
+ # For example, to send query to the database on the localhost:
15
+ #
16
+ # require 'pg'
17
+ # conn = PG::Connection.open(:dbname => 'test')
18
+ # res = conn.exec_params('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
19
+ # # Equivalent to:
20
+ # # res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
21
+ #
22
+ # See the PG::Result class for information on working with the results of a query.
23
+ #
24
+ class PG::Connection
25
+
26
+ # The order the options are passed to the ::connect method.
27
+ CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
28
+
29
+
30
+ ### Quote a single +value+ for use in a connection-parameter string.
31
+ def self.quote_connstr( value )
32
+ return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
33
+ end
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
41
+
42
+ # Decode a connection string to Hash options
43
+ #
44
+ # Value are properly unquoted and unescaped.
45
+ def self.connect_string_to_hash( str )
46
+ options = {}
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
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 = {}
90
+
91
+ if args.length == 1
92
+ case args.first
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 = {}
105
+ when /=/
106
+ # Option string style
107
+ option_string = args.first.to_s
108
+ iopts = connect_string_to_hash(option_string)
109
+ oopts = {}
110
+ else
111
+ # Positional parameters (only host given)
112
+ iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
113
+ oopts = iopts.dup
114
+ end
115
+ else
116
+ # Positional parameters
117
+ max = CONNECT_ARGUMENT_ORDER.length
118
+ raise ArgumentError,
119
+ "Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
120
+
121
+ CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,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
149
+ end
150
+ oopts[:hostaddr] = hostaddrs.join(",") if hostaddrs.any?
151
+ end
152
+
153
+ if !iopts[:fallback_application_name]
154
+ oopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
155
+ end
156
+
157
+ if uri
158
+ uri += uri_match['query'] ? "&" : "?"
159
+ uri += URI.encode_www_form( oopts )
160
+ return uri
161
+ else
162
+ option_string += ' ' unless option_string.empty? && oopts.empty?
163
+ return option_string + connect_hash_to_string(oopts)
164
+ end
165
+ end
166
+
167
+
168
+ # call-seq:
169
+ # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
170
+ #
171
+ # Execute a copy process for transferring data to or from the server.
172
+ #
173
+ # This issues the SQL COPY command via #exec. The response to this
174
+ # (if there is no error in the command) is a PG::Result object that
175
+ # is passed to the block, bearing a status code of PGRES_COPY_OUT or
176
+ # PGRES_COPY_IN (depending on the specified copy direction).
177
+ # The application should then use #put_copy_data or #get_copy_data
178
+ # to receive or transmit data rows and should return from the block
179
+ # when finished.
180
+ #
181
+ # #copy_data returns another PG::Result object when the data transfer
182
+ # is complete. An exception is raised if some problem was encountered,
183
+ # so it isn't required to make use of any of them.
184
+ # At this point further SQL commands can be issued via #exec.
185
+ # (It is not possible to execute other SQL commands using the same
186
+ # connection while the COPY operation is in progress.)
187
+ #
188
+ # This method ensures, that the copy process is properly terminated
189
+ # in case of client side or server side failures. Therefore, in case
190
+ # of blocking mode of operation, #copy_data is preferred to raw calls
191
+ # of #put_copy_data, #get_copy_data and #put_copy_end.
192
+ #
193
+ # _coder_ can be a PG::Coder derivation
194
+ # (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
195
+ # This enables encoding of data fields given to #put_copy_data
196
+ # or decoding of fields received by #get_copy_data.
197
+ #
198
+ # Example with CSV input format:
199
+ # conn.exec "create table my_table (a text,b text,c text,d text)"
200
+ # conn.copy_data "COPY my_table FROM STDIN CSV" do
201
+ # conn.put_copy_data "some,data,to,copy\n"
202
+ # conn.put_copy_data "more,data,to,copy\n"
203
+ # end
204
+ # This creates +my_table+ and inserts two CSV rows.
205
+ #
206
+ # The same with text format encoder PG::TextEncoder::CopyRow
207
+ # and Array input:
208
+ # enco = PG::TextEncoder::CopyRow.new
209
+ # conn.copy_data "COPY my_table FROM STDIN", enco do
210
+ # conn.put_copy_data ['some', 'data', 'to', 'copy']
211
+ # conn.put_copy_data ['more', 'data', 'to', 'copy']
212
+ # end
213
+ #
214
+ # Example with CSV output format:
215
+ # conn.copy_data "COPY my_table TO STDOUT CSV" do
216
+ # while row=conn.get_copy_data
217
+ # p row
218
+ # end
219
+ # end
220
+ # This prints all rows of +my_table+ to stdout:
221
+ # "some,data,to,copy\n"
222
+ # "more,data,to,copy\n"
223
+ #
224
+ # The same with text format decoder PG::TextDecoder::CopyRow
225
+ # and Array output:
226
+ # deco = PG::TextDecoder::CopyRow.new
227
+ # conn.copy_data "COPY my_table TO STDOUT", deco do
228
+ # while row=conn.get_copy_data
229
+ # p row
230
+ # end
231
+ # end
232
+ # This receives all rows of +my_table+ as ruby array:
233
+ # ["some", "data", "to", "copy"]
234
+ # ["more", "data", "to", "copy"]
235
+
236
+ def copy_data( sql, coder=nil )
237
+ res = exec( sql )
238
+
239
+ case res.result_status
240
+ when PGRES_COPY_IN
241
+ begin
242
+ if coder
243
+ old_coder = self.encoder_for_put_copy_data
244
+ self.encoder_for_put_copy_data = coder
245
+ end
246
+ yield res
247
+ rescue Exception => err
248
+ errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
249
+ put_copy_end( errmsg )
250
+ get_result
251
+ raise
252
+ else
253
+ put_copy_end
254
+ get_last_result
255
+ ensure
256
+ self.encoder_for_put_copy_data = old_coder if coder
257
+ end
258
+
259
+ when PGRES_COPY_OUT
260
+ begin
261
+ if coder
262
+ old_coder = self.decoder_for_get_copy_data
263
+ self.decoder_for_get_copy_data = coder
264
+ end
265
+ yield res
266
+ rescue Exception => err
267
+ cancel
268
+ while get_copy_data
269
+ end
270
+ while get_result
271
+ end
272
+ raise
273
+ else
274
+ res = get_last_result
275
+ if !res || res.result_status != PGRES_COMMAND_OK
276
+ while get_copy_data
277
+ end
278
+ while get_result
279
+ end
280
+ raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
281
+ end
282
+ res
283
+ ensure
284
+ self.decoder_for_get_copy_data = old_coder if coder
285
+ end
286
+
287
+ else
288
+ raise ArgumentError, "SQL command is no COPY statement: #{sql}"
289
+ end
290
+ end
291
+
292
+ # Backward-compatibility aliases for stuff that's moved into PG.
293
+ class << self
294
+ define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
295
+ end
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
316
+
317
+ ### Returns an array of Hashes with connection defaults. See ::conndefaults
318
+ ### for details.
319
+ def conndefaults
320
+ return self.class.conndefaults
321
+ end
322
+
323
+ ### Return the Postgres connection defaults structure as a Hash keyed by option
324
+ ### keyword (as a Symbol).
325
+ ###
326
+ ### See also #conndefaults
327
+ def self.conndefaults_hash
328
+ return self.conndefaults.each_with_object({}) do |info, hash|
329
+ hash[ info[:keyword].to_sym ] = info[:val]
330
+ end
331
+ end
332
+
333
+ ### Returns a Hash with connection defaults. See ::conndefaults_hash
334
+ ### for details.
335
+ def conndefaults_hash
336
+ return self.class.conndefaults_hash
337
+ end
338
+
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]
346
+ end
347
+ end
348
+
349
+ # Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
350
+ if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
351
+ # call-seq:
352
+ # conn.ssl_attributes -> Hash<String,String>
353
+ #
354
+ # Returns SSL-related information about the connection as key/value pairs
355
+ #
356
+ # The available attributes varies depending on the SSL library being used,
357
+ # and the type of connection.
358
+ #
359
+ # See also #ssl_attribute
360
+ def ssl_attributes
361
+ ssl_attribute_names.each.with_object({}) do |n,h|
362
+ h[n] = ssl_attribute(n)
363
+ end
364
+ end
365
+ end
366
+
367
+ # call-seq:
368
+ # conn.get_result() -> PG::Result
369
+ # conn.get_result() {|pg_result| block }
370
+ #
371
+ # Blocks waiting for the next result from a call to
372
+ # #send_query (or another asynchronous command), and returns
373
+ # it. Returns +nil+ if no more results are available.
374
+ #
375
+ # Note: call this function repeatedly until it returns +nil+, or else
376
+ # you will not be able to issue further commands.
377
+ #
378
+ # If the optional code block is given, it will be passed <i>result</i> as an argument,
379
+ # and the PG::Result object will automatically be cleared when the block terminates.
380
+ # In this instance, <code>conn.exec</code> returns the value of the block.
381
+ def get_result(*args)
382
+ block
383
+ sync_get_result
384
+ end
385
+ alias async_get_result get_result
386
+
387
+ # call-seq:
388
+ # conn.get_copy_data( [ nonblock = false [, decoder = nil ]] ) -> Object
389
+ #
390
+ # Return one row of data, +nil+
391
+ # if the copy is done, or +false+ if the call would
392
+ # block (only possible if _nonblock_ is true).
393
+ #
394
+ # If _decoder_ is not set or +nil+, data is returned as binary string.
395
+ #
396
+ # If _decoder_ is set to a PG::Coder derivation, the return type depends on this decoder.
397
+ # PG::TextDecoder::CopyRow decodes the received data fields from one row of PostgreSQL's
398
+ # COPY text format to an Array of Strings.
399
+ # Optionally the decoder can type cast the single fields to various Ruby types in one step,
400
+ # if PG::TextDecoder::CopyRow#type_map is set accordingly.
401
+ #
402
+ # See also #copy_data.
403
+ #
404
+ def get_copy_data(async=false, decoder=nil)
405
+ if async
406
+ return sync_get_copy_data(async, decoder)
407
+ else
408
+ while (res=sync_get_copy_data(true, decoder)) == false
409
+ socket_io.wait_readable
410
+ consume_input
411
+ end
412
+ return res
413
+ end
414
+ end
415
+ alias async_get_copy_data get_copy_data
416
+
417
+
418
+ # In async_api=true mode (default) all send calls run nonblocking.
419
+ # The difference is that setnonblocking(true) disables automatic handling of would-block cases.
420
+ # In async_api=false mode all send calls run directly on libpq.
421
+ # Blocking vs. nonblocking state can be changed in libpq.
422
+
423
+ # call-seq:
424
+ # conn.setnonblocking(Boolean) -> nil
425
+ #
426
+ # Sets the nonblocking status of the connection.
427
+ # In the blocking state, calls to #send_query
428
+ # will block until the message is sent to the server,
429
+ # but will not wait for the query results.
430
+ # In the nonblocking state, calls to #send_query
431
+ # will return an error if the socket is not ready for
432
+ # writing.
433
+ # Note: This function does not affect #exec, because
434
+ # that function doesn't return until the server has
435
+ # processed the query and returned the results.
436
+ #
437
+ # Returns +nil+.
438
+ def setnonblocking(enabled)
439
+ singleton_class.async_send_api = !enabled
440
+ self.flush_data = !enabled
441
+ sync_setnonblocking(true)
442
+ end
443
+ alias async_setnonblocking setnonblocking
444
+
445
+ # sync/async isnonblocking methods are switched by async_setnonblocking()
446
+
447
+ # call-seq:
448
+ # conn.isnonblocking() -> Boolean
449
+ #
450
+ # Returns the blocking status of the database connection.
451
+ # Returns +true+ if the connection is set to nonblocking mode and +false+ if blocking.
452
+ def isnonblocking
453
+ false
454
+ end
455
+ alias async_isnonblocking isnonblocking
456
+ alias nonblocking? isnonblocking
457
+
458
+ # call-seq:
459
+ # conn.put_copy_data( buffer [, encoder] ) -> Boolean
460
+ #
461
+ # Transmits _buffer_ as copy data to the server.
462
+ # Returns true if the data was sent, false if it was
463
+ # not sent (false is only possible if the connection
464
+ # is in nonblocking mode, and this command would block).
465
+ #
466
+ # _encoder_ can be a PG::Coder derivation (typically PG::TextEncoder::CopyRow).
467
+ # This encodes the data fields given as _buffer_ from an Array of Strings to
468
+ # PostgreSQL's COPY text format inclusive proper escaping. Optionally
469
+ # the encoder can type cast the fields from various Ruby types in one step,
470
+ # if PG::TextEncoder::CopyRow#type_map is set accordingly.
471
+ #
472
+ # Raises an exception if an error occurs.
473
+ #
474
+ # See also #copy_data.
475
+ #
476
+ def put_copy_data(buffer, encoder=nil)
477
+ until sync_put_copy_data(buffer, encoder)
478
+ flush
479
+ end
480
+ flush
481
+ end
482
+ alias async_put_copy_data put_copy_data
483
+
484
+ # call-seq:
485
+ # conn.put_copy_end( [ error_message ] ) -> Boolean
486
+ #
487
+ # Sends end-of-data indication to the server.
488
+ #
489
+ # _error_message_ is an optional parameter, and if set,
490
+ # forces the COPY command to fail with the string
491
+ # _error_message_.
492
+ #
493
+ # Returns true if the end-of-data was sent, #false* if it was
494
+ # not sent (*false* is only possible if the connection
495
+ # is in nonblocking mode, and this command would block).
496
+ def put_copy_end(*args)
497
+ until sync_put_copy_end(*args)
498
+ flush
499
+ end
500
+ flush
501
+ end
502
+ alias async_put_copy_end put_copy_end
503
+
504
+ if method_defined? :sync_encrypt_password
505
+ # call-seq:
506
+ # conn.encrypt_password( password, username, algorithm=nil ) -> String
507
+ #
508
+ # This function is intended to be used by client applications that wish to send commands like <tt>ALTER USER joe PASSWORD 'pwd'</tt>.
509
+ # 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.
510
+ # Instead, use this function to convert the password to encrypted form before it is sent.
511
+ #
512
+ # The +password+ and +username+ arguments are the cleartext password, and the SQL name of the user it is for.
513
+ # +algorithm+ specifies the encryption algorithm to use to encrypt the password.
514
+ # 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).
515
+ # Note that support for +scram-sha-256+ was introduced in PostgreSQL version 10, and will not work correctly with older server versions.
516
+ # If algorithm is omitted or +nil+, this function will query the server for the current value of the +password_encryption+ setting.
517
+ # That can block, and will fail if the current transaction is aborted, or if the connection is busy executing another query.
518
+ # 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.
519
+ #
520
+ # Return value is the encrypted password.
521
+ # The caller can assume the string doesn't contain any special characters that would require escaping.
522
+ #
523
+ # Available since PostgreSQL-10.
524
+ # See also corresponding {libpq function}[https://www.postgresql.org/docs/current/libpq-misc.html#LIBPQ-PQENCRYPTPASSWORDCONN].
525
+ def encrypt_password( password, username, algorithm=nil )
526
+ algorithm ||= exec("SHOW password_encryption").getvalue(0,0)
527
+ sync_encrypt_password(password, username, algorithm)
528
+ end
529
+ alias async_encrypt_password encrypt_password
530
+ end
531
+
532
+ # call-seq:
533
+ # conn.reset()
534
+ #
535
+ # Resets the backend connection. This method closes the
536
+ # backend connection and tries to re-connect.
537
+ def reset
538
+ reset_start
539
+ async_connect_or_reset(:reset_poll)
540
+ end
541
+ alias async_reset reset
542
+
543
+ # call-seq:
544
+ # conn.cancel() -> String
545
+ #
546
+ # Requests cancellation of the command currently being
547
+ # processed.
548
+ #
549
+ # Returns +nil+ on success, or a string containing the
550
+ # error message if a failure occurs.
551
+ def cancel
552
+ be_pid = backend_pid
553
+ be_key = backend_key
554
+ cancel_request = [0x10, 1234, 5678, be_pid, be_key].pack("NnnNN")
555
+
556
+ if Fiber.respond_to?(:scheduler) && Fiber.scheduler && RUBY_PLATFORM =~ /mingw|mswin/
557
+ # Ruby's nonblocking IO is not really supported on Windows.
558
+ # We work around by using threads and explicit calls to wait_readable/wait_writable.
559
+ cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
560
+ begin
561
+ cl.write_nonblock(cancel_request)
562
+ rescue IO::WaitReadable, Errno::EINTR
563
+ cl.wait_writable
564
+ retry
565
+ end
566
+ begin
567
+ cl.read_nonblock(1)
568
+ rescue IO::WaitReadable, Errno::EINTR
569
+ cl.wait_readable
570
+ retry
571
+ rescue EOFError
572
+ end
573
+ elsif RUBY_ENGINE == 'truffleruby'
574
+ begin
575
+ cl = socket_io.remote_address.connect
576
+ rescue NotImplementedError
577
+ # Workaround for truffleruby < 21.3.0
578
+ cl2 = Socket.for_fd(socket_io.fileno)
579
+ cl2.autoclose = false
580
+ adr = cl2.remote_address
581
+ if adr.ip?
582
+ cl = TCPSocket.new(adr.ip_address, adr.ip_port)
583
+ cl.autoclose = false
584
+ else
585
+ cl = UNIXSocket.new(adr.unix_path)
586
+ cl.autoclose = false
587
+ end
588
+ end
589
+ cl.write(cancel_request)
590
+ cl.read(1)
591
+ else
592
+ cl = socket_io.remote_address.connect
593
+ # Send CANCEL_REQUEST_CODE and parameters
594
+ cl.write(cancel_request)
595
+ # Wait for the postmaster to close the connection, which indicates that it's processed the request.
596
+ cl.read(1)
597
+ end
598
+
599
+ cl.close
600
+ nil
601
+ rescue SystemCallError => err
602
+ err.to_s
603
+ end
604
+ alias async_cancel cancel
605
+
606
+ private def async_connect_or_reset(poll_meth)
607
+ # Now grab a reference to the underlying socket so we know when the connection is established
608
+ socket = socket_io
609
+
610
+ # Track the progress of the connection, waiting for the socket to become readable/writable before polling it
611
+ poll_status = PG::PGRES_POLLING_WRITING
612
+ until poll_status == PG::PGRES_POLLING_OK ||
613
+ poll_status == PG::PGRES_POLLING_FAILED
614
+
615
+ # If the socket needs to read, wait 'til it becomes readable to poll again
616
+ case poll_status
617
+ when PG::PGRES_POLLING_READING
618
+ socket.wait_readable
619
+
620
+ # ...and the same for when the socket needs to write
621
+ when PG::PGRES_POLLING_WRITING
622
+ socket.wait_writable
623
+ end
624
+
625
+ # Check to see if it's finished or failed yet
626
+ poll_status = send( poll_meth )
627
+ end
628
+
629
+ raise(PG::ConnectionBad, error_message) unless status == PG::CONNECTION_OK
630
+
631
+ # Set connection to nonblocking to handle all blocking states in ruby.
632
+ # That way a fiber scheduler is able to handle IO requests.
633
+ sync_setnonblocking(true)
634
+ self.flush_data = true
635
+ set_default_encoding
636
+
637
+ self
638
+ end
639
+
640
+ class << self
641
+ # call-seq:
642
+ # PG::Connection.new -> conn
643
+ # PG::Connection.new(connection_hash) -> conn
644
+ # PG::Connection.new(connection_string) -> conn
645
+ # PG::Connection.new(host, port, options, tty, dbname, user, password) -> conn
646
+ #
647
+ # Create a connection to the specified server.
648
+ #
649
+ # +connection_hash+ must be a ruby Hash with connection parameters.
650
+ # See the {list of valid parameters}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS] in the PostgreSQL documentation.
651
+ #
652
+ # There are two accepted formats for +connection_string+: plain <code>keyword = value</code> strings and URIs.
653
+ # See the documentation of {connection strings}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING].
654
+ #
655
+ # The positional parameter form has the same functionality except that the missing parameters will always take on default values. The parameters are:
656
+ # [+host+]
657
+ # server hostname
658
+ # [+port+]
659
+ # server port number
660
+ # [+options+]
661
+ # backend options
662
+ # [+tty+]
663
+ # (ignored in all versions of PostgreSQL)
664
+ # [+dbname+]
665
+ # connecting database name
666
+ # [+user+]
667
+ # login user name
668
+ # [+password+]
669
+ # login password
670
+ #
671
+ # Examples:
672
+ #
673
+ # # Connect using all defaults
674
+ # PG::Connection.new
675
+ #
676
+ # # As a Hash
677
+ # PG::Connection.new( dbname: 'test', port: 5432 )
678
+ #
679
+ # # As a String
680
+ # PG::Connection.new( "dbname=test port=5432" )
681
+ #
682
+ # # As an Array
683
+ # PG::Connection.new( nil, 5432, nil, nil, 'test', nil, nil )
684
+ #
685
+ # # As an URI
686
+ # PG::Connection.new( "postgresql://user:pass@pgsql.example.com:5432/testdb?sslmode=require" )
687
+ #
688
+ # If the Ruby default internal encoding is set (i.e., <code>Encoding.default_internal != nil</code>), the
689
+ # connection will have its +client_encoding+ set accordingly.
690
+ #
691
+ # Raises a PG::Error if the connection fails.
692
+ def new(*args, **kwargs)
693
+ conn = PG::Connection.connect_start(*args, **kwargs ) or
694
+ raise(PG::Error, "Unable to create a new connection")
695
+
696
+ raise(PG::ConnectionBad, conn.error_message) if conn.status == PG::CONNECTION_BAD
697
+
698
+ conn.send(:async_connect_or_reset, :connect_poll)
699
+ end
700
+ alias async_connect new
701
+ alias connect new
702
+ alias open new
703
+ alias setdb new
704
+ alias setdblogin new
705
+
706
+ # call-seq:
707
+ # PG::Connection.ping(connection_hash) -> Integer
708
+ # PG::Connection.ping(connection_string) -> Integer
709
+ # PG::Connection.ping(host, port, options, tty, dbname, login, password) -> Integer
710
+ #
711
+ # Check server status.
712
+ #
713
+ # See PG::Connection.new for a description of the parameters.
714
+ #
715
+ # Returns one of:
716
+ # [+PQPING_OK+]
717
+ # server is accepting connections
718
+ # [+PQPING_REJECT+]
719
+ # server is alive but rejecting connections
720
+ # [+PQPING_NO_RESPONSE+]
721
+ # could not establish connection
722
+ # [+PQPING_NO_ATTEMPT+]
723
+ # connection not attempted (bad params)
724
+ def ping(*args)
725
+ if Fiber.respond_to?(:scheduler) && Fiber.scheduler
726
+ # Run PQping in a second thread to avoid blocking of the scheduler.
727
+ # Unfortunately there's no nonblocking way to run ping.
728
+ Thread.new { sync_ping(*args) }.value
729
+ else
730
+ sync_ping(*args)
731
+ end
732
+ end
733
+ alias async_ping ping
734
+
735
+ REDIRECT_CLASS_METHODS = {
736
+ :new => [:async_connect, :sync_connect],
737
+ :connect => [:async_connect, :sync_connect],
738
+ :open => [:async_connect, :sync_connect],
739
+ :setdb => [:async_connect, :sync_connect],
740
+ :setdblogin => [:async_connect, :sync_connect],
741
+ :ping => [:async_ping, :sync_ping],
742
+ }
743
+
744
+ # These methods are affected by PQsetnonblocking
745
+ REDIRECT_SEND_METHODS = {
746
+ :isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
747
+ :nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
748
+ :put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
749
+ :put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
750
+ :flush => [:async_flush, :sync_flush],
751
+ }
752
+ REDIRECT_METHODS = {
753
+ :exec => [:async_exec, :sync_exec],
754
+ :query => [:async_exec, :sync_exec],
755
+ :exec_params => [:async_exec_params, :sync_exec_params],
756
+ :prepare => [:async_prepare, :sync_prepare],
757
+ :exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
758
+ :describe_portal => [:async_describe_portal, :sync_describe_portal],
759
+ :describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
760
+ :setnonblocking => [:async_setnonblocking, :sync_setnonblocking],
761
+ :get_result => [:async_get_result, :sync_get_result],
762
+ :get_last_result => [:async_get_last_result, :sync_get_last_result],
763
+ :get_copy_data => [:async_get_copy_data, :sync_get_copy_data],
764
+ :reset => [:async_reset, :sync_reset],
765
+ :set_client_encoding => [:async_set_client_encoding, :sync_set_client_encoding],
766
+ :client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
767
+ :cancel => [:async_cancel, :sync_cancel],
768
+ }
769
+
770
+ if PG::Connection.instance_methods.include? :async_encrypt_password
771
+ REDIRECT_METHODS.merge!({
772
+ :encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
773
+ })
774
+ end
775
+
776
+ def async_send_api=(enable)
777
+ REDIRECT_SEND_METHODS.each do |ali, (async, sync)|
778
+ undef_method(ali) if method_defined?(ali)
779
+ alias_method( ali, enable ? async : sync )
780
+ end
781
+ end
782
+
783
+ # Switch between sync and async libpq API.
784
+ #
785
+ # PG::Connection.async_api = true
786
+ # this is the default.
787
+ # It sets an alias from #exec to #async_exec, #reset to #async_reset and so on.
788
+ #
789
+ # PG::Connection.async_api = false
790
+ # sets an alias from #exec to #sync_exec, #reset to #sync_reset and so on.
791
+ #
792
+ # pg-1.1.0+ defaults to libpq's async API for query related blocking methods.
793
+ # pg-1.3.0+ defaults to libpq's async API for all possibly blocking methods.
794
+ #
795
+ # _PLEASE_ _NOTE_: This method is not part of the public API and is for debug and development use only.
796
+ # Do not use this method in production code.
797
+ # Any issues with the default setting of <tt>async_api=true</tt> should be reported to the maintainers instead.
798
+ #
799
+ def async_api=(enable)
800
+ self.async_send_api = enable
801
+ REDIRECT_METHODS.each do |ali, (async, sync)|
802
+ remove_method(ali) if method_defined?(ali)
803
+ alias_method( ali, enable ? async : sync )
804
+ end
805
+ REDIRECT_CLASS_METHODS.each do |ali, (async, sync)|
806
+ singleton_class.remove_method(ali) if method_defined?(ali)
807
+ singleton_class.alias_method(ali, enable ? async : sync )
808
+ end
809
+ end
810
+ end
811
+
812
+ self.async_api = true
813
+ end # class PG::Connection