pg 1.0.0 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +141 -0
  7. data/.gitignore +22 -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.md +884 -0
  16. data/Manifest.txt +8 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.md +300 -0
  19. data/README.md +286 -0
  20. data/Rakefile +38 -138
  21. data/Rakefile.cross +63 -63
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/certs/larskanis-2023.pem +24 -0
  25. data/ext/errorcodes.def +80 -0
  26. data/ext/errorcodes.rb +1 -1
  27. data/ext/errorcodes.txt +22 -2
  28. data/ext/extconf.rb +106 -25
  29. data/ext/gvl_wrappers.c +4 -0
  30. data/ext/gvl_wrappers.h +23 -0
  31. data/ext/pg.c +213 -155
  32. data/ext/pg.h +89 -23
  33. data/ext/pg_binary_decoder.c +162 -16
  34. data/ext/pg_binary_encoder.c +238 -13
  35. data/ext/pg_coder.c +159 -35
  36. data/ext/pg_connection.c +1557 -972
  37. data/ext/pg_copy_coder.c +364 -38
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +522 -0
  40. data/ext/pg_result.c +708 -215
  41. data/ext/pg_text_decoder.c +627 -43
  42. data/ext/pg_text_encoder.c +206 -62
  43. data/ext/pg_tuple.c +572 -0
  44. data/ext/pg_type_map.c +45 -11
  45. data/ext/pg_type_map_all_strings.c +21 -7
  46. data/ext/pg_type_map_by_class.c +59 -27
  47. data/ext/pg_type_map_by_column.c +80 -37
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +62 -29
  50. data/ext/pg_type_map_in_ruby.c +56 -22
  51. data/ext/{util.c → pg_util.c} +12 -12
  52. data/ext/{util.h → pg_util.h} +2 -2
  53. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  54. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  55. data/lib/pg/basic_type_map_for_results.rb +104 -0
  56. data/lib/pg/basic_type_registry.rb +299 -0
  57. data/lib/pg/binary_decoder/date.rb +9 -0
  58. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  59. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  60. data/lib/pg/coder.rb +36 -13
  61. data/lib/pg/connection.rb +749 -70
  62. data/lib/pg/exceptions.rb +16 -2
  63. data/lib/pg/result.rb +14 -2
  64. data/lib/pg/text_decoder/date.rb +18 -0
  65. data/lib/pg/text_decoder/inet.rb +9 -0
  66. data/lib/pg/text_decoder/json.rb +14 -0
  67. data/lib/pg/text_decoder/numeric.rb +9 -0
  68. data/lib/pg/text_decoder/timestamp.rb +30 -0
  69. data/lib/pg/text_encoder/date.rb +12 -0
  70. data/lib/pg/text_encoder/inet.rb +28 -0
  71. data/lib/pg/text_encoder/json.rb +14 -0
  72. data/lib/pg/text_encoder/numeric.rb +9 -0
  73. data/lib/pg/text_encoder/timestamp.rb +24 -0
  74. data/lib/pg/tuple.rb +30 -0
  75. data/lib/pg/type_map_by_column.rb +3 -2
  76. data/lib/pg/version.rb +4 -0
  77. data/lib/pg.rb +96 -39
  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 +34 -0
  90. data/rakelib/task_extension.rb +46 -0
  91. data/sample/array_insert.rb +20 -0
  92. data/sample/async_api.rb +102 -0
  93. data/sample/async_copyto.rb +39 -0
  94. data/sample/async_mixed.rb +56 -0
  95. data/sample/check_conn.rb +21 -0
  96. data/sample/copydata.rb +71 -0
  97. data/sample/copyfrom.rb +81 -0
  98. data/sample/copyto.rb +19 -0
  99. data/sample/cursor.rb +21 -0
  100. data/sample/disk_usage_report.rb +177 -0
  101. data/sample/issue-119.rb +94 -0
  102. data/sample/losample.rb +69 -0
  103. data/sample/minimal-testcase.rb +17 -0
  104. data/sample/notify_wait.rb +72 -0
  105. data/sample/pg_statistics.rb +285 -0
  106. data/sample/replication_monitor.rb +222 -0
  107. data/sample/test_binary_values.rb +33 -0
  108. data/sample/wal_shipper.rb +434 -0
  109. data/sample/warehouse_partitions.rb +311 -0
  110. data/translation/.po4a-version +7 -0
  111. data/translation/po/all.pot +936 -0
  112. data/translation/po/ja.po +1036 -0
  113. data/translation/po4a.cfg +12 -0
  114. data.tar.gz.sig +0 -0
  115. metadata +147 -219
  116. metadata.gz.sig +0 -0
  117. data/ChangeLog +0 -6595
  118. data/History.rdoc +0 -422
  119. data/README.ja.rdoc +0 -14
  120. data/README.rdoc +0 -167
  121. data/lib/pg/basic_type_mapping.rb +0 -426
  122. data/lib/pg/constants.rb +0 -11
  123. data/lib/pg/text_decoder.rb +0 -51
  124. data/lib/pg/text_encoder.rb +0 -35
  125. data/spec/data/expected_trace.out +0 -26
  126. data/spec/data/random_binary_data +0 -0
  127. data/spec/helpers.rb +0 -348
  128. data/spec/pg/basic_type_mapping_spec.rb +0 -305
  129. data/spec/pg/connection_spec.rb +0 -1719
  130. data/spec/pg/result_spec.rb +0 -456
  131. data/spec/pg/type_map_by_class_spec.rb +0 -138
  132. data/spec/pg/type_map_by_column_spec.rb +0 -222
  133. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  134. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  135. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  136. data/spec/pg/type_map_spec.rb +0 -22
  137. data/spec/pg/type_spec.rb +0 -777
  138. data/spec/pg_spec.rb +0 -50
data/spec/helpers.rb DELETED
@@ -1,348 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'pathname'
4
- require 'rspec'
5
- require 'shellwords'
6
- require 'pg'
7
-
8
- DEFAULT_TEST_DIR_STR = File.join(Dir.pwd, "tmp_test_specs")
9
- TEST_DIR_STR = ENV['RUBY_PG_TEST_DIR'] || DEFAULT_TEST_DIR_STR
10
- TEST_DIRECTORY = Pathname.new(TEST_DIR_STR)
11
-
12
- module PG::TestingHelpers
13
-
14
- ### Automatically set up the database when it's used, and wrap a transaction around
15
- ### examples that don't disable it.
16
- def self::included( mod )
17
- super
18
-
19
- if mod.respond_to?( :around )
20
-
21
- mod.before( :all ) { @conn = setup_testing_db(described_class ? described_class.name : mod.description) }
22
-
23
- mod.around( :each ) do |example|
24
- begin
25
- @conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
26
- if PG.respond_to?( :library_version )
27
- desc = example.source_location.join(':')
28
- @conn.exec_params %Q{SET application_name TO '%s'} %
29
- [@conn.escape_string(desc.slice(-60))]
30
- end
31
- example.run
32
- ensure
33
- @conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
34
- end
35
- end
36
-
37
- mod.after( :all ) { teardown_testing_db(@conn) }
38
- end
39
-
40
- end
41
-
42
-
43
- #
44
- # Examples
45
- #
46
-
47
- # Set some ANSI escape code constants (Shamelessly stolen from Perl's
48
- # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
49
- ANSI_ATTRIBUTES = {
50
- 'clear' => 0,
51
- 'reset' => 0,
52
- 'bold' => 1,
53
- 'dark' => 2,
54
- 'underline' => 4,
55
- 'underscore' => 4,
56
- 'blink' => 5,
57
- 'reverse' => 7,
58
- 'concealed' => 8,
59
-
60
- 'black' => 30, 'on_black' => 40,
61
- 'red' => 31, 'on_red' => 41,
62
- 'green' => 32, 'on_green' => 42,
63
- 'yellow' => 33, 'on_yellow' => 43,
64
- 'blue' => 34, 'on_blue' => 44,
65
- 'magenta' => 35, 'on_magenta' => 45,
66
- 'cyan' => 36, 'on_cyan' => 46,
67
- 'white' => 37, 'on_white' => 47
68
- }
69
-
70
-
71
- ###############
72
- module_function
73
- ###############
74
-
75
- ### Create a string that contains the ANSI codes specified and return it
76
- def ansi_code( *attributes )
77
- attributes.flatten!
78
- attributes.collect! {|at| at.to_s }
79
-
80
- return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
81
- attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
82
-
83
- # $stderr.puts " attr is: %p" % [attributes]
84
- if attributes.empty?
85
- return ''
86
- else
87
- return "\e[%sm" % attributes
88
- end
89
- end
90
-
91
-
92
- ### Colorize the given +string+ with the specified +attributes+ and return it, handling
93
- ### line-endings, color reset, etc.
94
- def colorize( *args )
95
- string = ''
96
-
97
- if block_given?
98
- string = yield
99
- else
100
- string = args.shift
101
- end
102
-
103
- ending = string[/(\s)$/] || ''
104
- string = string.rstrip
105
-
106
- return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
107
- end
108
-
109
-
110
- ### Output a message with highlighting.
111
- def message( *msg )
112
- $stderr.puts( colorize(:bold) { msg.flatten.join(' ') } )
113
- end
114
-
115
-
116
- ### Output a logging message if $VERBOSE is true
117
- def trace( *msg )
118
- return unless $VERBOSE
119
- output = colorize( msg.flatten.join(' '), 'yellow' )
120
- $stderr.puts( output )
121
- end
122
-
123
-
124
- ### Return the specified args as a string, quoting any that have a space.
125
- def quotelist( *args )
126
- return args.flatten.collect {|part| part.to_s =~ /\s/ ? part.to_s.inspect : part.to_s }
127
- end
128
-
129
-
130
- ### Run the specified command +cmd+ with system(), failing if the execution
131
- ### fails.
132
- def run( *cmd )
133
- cmd.flatten!
134
-
135
- if cmd.length > 1
136
- trace( quotelist(*cmd) )
137
- else
138
- trace( cmd )
139
- end
140
-
141
- system( *cmd )
142
- raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
143
- end
144
-
145
-
146
- ### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
147
- ### +logpath+, failing if the execution fails.
148
- def log_and_run( logpath, *cmd )
149
- cmd.flatten!
150
-
151
- if cmd.length > 1
152
- trace( quotelist(*cmd) )
153
- else
154
- trace( cmd )
155
- end
156
-
157
- # Eliminate the noise of creating/tearing down the database by
158
- # redirecting STDERR/STDOUT to a logfile
159
- logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
160
- system( *cmd, [STDOUT, STDERR] => logfh )
161
-
162
- raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
163
- end
164
-
165
-
166
- ### Check the current directory for directories that look like they're
167
- ### testing directories from previous tests, and tell any postgres instances
168
- ### running in them to shut down.
169
- def stop_existing_postmasters
170
- # tmp_test_0.22329534700318
171
- pat = Pathname.getwd + 'tmp_test_*'
172
- Pathname.glob( pat.to_s ).each do |testdir|
173
- datadir = testdir + 'data'
174
- pidfile = datadir + 'postmaster.pid'
175
- if pidfile.exist? && pid = pidfile.read.chomp.to_i
176
- $stderr.puts "pidfile (%p) exists: %d" % [ pidfile, pid ]
177
- begin
178
- Process.kill( 0, pid )
179
- rescue Errno::ESRCH
180
- $stderr.puts "No postmaster running for %s" % [ datadir ]
181
- # Process isn't alive, so don't try to stop it
182
- else
183
- $stderr.puts "Stopping lingering database at PID %d" % [ pid ]
184
- run 'pg_ctl', '-D', datadir.to_s, '-m', 'fast', 'stop'
185
- end
186
- else
187
- $stderr.puts "No pidfile (%p)" % [ pidfile ]
188
- end
189
- end
190
- end
191
-
192
-
193
- ### Set up a PostgreSQL database instance for testing.
194
- def setup_testing_db( description )
195
- require 'pg'
196
- stop_existing_postmasters()
197
-
198
- puts "Setting up test database for #{description}"
199
- @test_pgdata = TEST_DIRECTORY + 'data'
200
- @test_pgdata.mkpath
201
-
202
- @port = 54321
203
- ENV['PGPORT'] = @port.to_s
204
- ENV['PGHOST'] = 'localhost'
205
- @conninfo = "host=localhost port=#{@port} dbname=test"
206
-
207
- @logfile = TEST_DIRECTORY + 'setup.log'
208
- trace "Command output logged to #{@logfile}"
209
-
210
- begin
211
- unless (@test_pgdata+"postgresql.conf").exist?
212
- FileUtils.rm_rf( @test_pgdata, :verbose => $DEBUG )
213
- $stderr.puts "Running initdb"
214
- log_and_run @logfile, 'initdb', '-E', 'UTF8', '--no-locale', '-D', @test_pgdata.to_s
215
- end
216
-
217
- trace "Starting postgres"
218
- log_and_run @logfile, 'pg_ctl', '-w', '-o', "-k #{TEST_DIRECTORY.to_s.dump}",
219
- '-D', @test_pgdata.to_s, 'start'
220
- sleep 2
221
-
222
- $stderr.puts "Creating the test DB"
223
- log_and_run @logfile, 'psql', '-e', '-c', 'DROP DATABASE IF EXISTS test', 'postgres'
224
- log_and_run @logfile, 'createdb', '-e', 'test'
225
-
226
- rescue => err
227
- $stderr.puts "%p during test setup: %s" % [ err.class, err.message ]
228
- $stderr.puts "See #{@logfile} for details."
229
- $stderr.puts *err.backtrace if $DEBUG
230
- fail
231
- end
232
-
233
- conn = PG.connect( @conninfo )
234
- conn.set_notice_processor do |message|
235
- $stderr.puts( description + ':' + message ) if $DEBUG
236
- end
237
-
238
- return conn
239
- end
240
-
241
-
242
- def teardown_testing_db( conn )
243
- puts "Tearing down test database"
244
-
245
- if conn
246
- check_for_lingering_connections( conn )
247
- conn.finish
248
- end
249
-
250
- log_and_run @logfile, 'pg_ctl', '-D', @test_pgdata.to_s, 'stop'
251
- end
252
-
253
-
254
- def check_for_lingering_connections( conn )
255
- conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
256
- conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid && ["client backend", nil].include?(row["backend_type"]) }
257
- unless conns.empty?
258
- puts "Lingering connections remain:"
259
- conns.each do |row|
260
- puts " [%s] {%s} %s -- %s" % row.values_at( 'pid', 'state', 'application_name', 'query' )
261
- end
262
- end
263
- end
264
- end
265
-
266
-
267
- # Retrieve the names of the column types of a given result set.
268
- def result_typenames(res)
269
- @conn.exec( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
270
- res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
271
- values[0]
272
- end
273
-
274
-
275
- # A matcher for checking the status of a PG::Connection to ensure it's still
276
- # usable.
277
- class ConnStillUsableMatcher
278
-
279
- def initialize
280
- @conn = nil
281
- @problem = nil
282
- end
283
-
284
- def matches?( conn )
285
- @conn = conn
286
- @problem = self.check_for_problems
287
- return @problem.nil?
288
- end
289
-
290
- def check_for_problems
291
- return "is finished" if @conn.finished?
292
- return "has bad status" unless @conn.status == PG::CONNECTION_OK
293
- return "has bad transaction status (%d)" % [ @conn.transaction_status ] unless
294
- @conn.transaction_status.between?( PG::PQTRANS_IDLE, PG::PQTRANS_INTRANS )
295
- return "is not usable." unless self.can_exec_query?
296
- return nil
297
- end
298
-
299
- def can_exec_query?
300
- @conn.send_query( "VALUES (1)" )
301
- @conn.get_last_result.values == [["1"]]
302
- end
303
-
304
- def failure_message
305
- return "expected %p to be usable, but it %s" % [ @conn, @problem ]
306
- end
307
-
308
- def failure_message_when_negated
309
- "expected %p not to be usable, but it still is" % [ @conn ]
310
- end
311
-
312
- end
313
-
314
-
315
- ### Return a ConnStillUsableMatcher to be used like:
316
- ###
317
- ### expect( pg_conn ).to still_be_usable
318
- ###
319
- def still_be_usable
320
- return ConnStillUsableMatcher.new
321
- end
322
-
323
- end
324
-
325
-
326
- RSpec.configure do |config|
327
- config.include( PG::TestingHelpers )
328
-
329
- config.run_all_when_everything_filtered = true
330
- config.filter_run :focus
331
- config.order = 'random'
332
- config.mock_with( :rspec ) do |mock|
333
- mock.syntax = :expect
334
- end
335
-
336
- if RUBY_PLATFORM =~ /mingw|mswin/
337
- config.filter_run_excluding :unix
338
- else
339
- config.filter_run_excluding :windows
340
- end
341
- config.filter_run_excluding :socket_io unless
342
- PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
343
-
344
- config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
345
- config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
346
- config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
347
- config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
348
- end
@@ -1,305 +0,0 @@
1
- #!/usr/bin/env rspec
2
- # encoding: utf-8
3
-
4
- require_relative '../helpers'
5
-
6
- require 'pg'
7
-
8
- describe 'Basic type mapping' do
9
-
10
- describe PG::BasicTypeMapForQueries do
11
- let!(:basic_type_mapping) do
12
- PG::BasicTypeMapForQueries.new @conn
13
- end
14
-
15
- #
16
- # Encoding Examples
17
- #
18
-
19
- it "should do basic param encoding", :ruby_19 do
20
- res = @conn.exec_params( "SELECT $1::int8,$2::float,$3,$4::TEXT",
21
- [1, 2.1, true, "b"], nil, basic_type_mapping )
22
-
23
- expect( res.values ).to eq( [
24
- [ "1", "2.1", "t", "b" ],
25
- ] )
26
-
27
- expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
28
- end
29
-
30
- it "should do array param encoding" do
31
- res = @conn.exec_params( "SELECT $1,$2,$3,$4", [
32
- [1, 2, 3], [[1, 2], [3, nil]],
33
- [1.11, 2.21],
34
- ['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
35
- ], nil, basic_type_mapping )
36
-
37
- expect( res.values ).to eq( [[
38
- '{1,2,3}', '{{1,2},{3,NULL}}',
39
- '{1.11,2.21}',
40
- '{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
41
- ]] )
42
-
43
- expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
44
- end
45
- end
46
-
47
-
48
-
49
- describe PG::BasicTypeMapForResults do
50
- let!(:basic_type_mapping) do
51
- PG::BasicTypeMapForResults.new @conn
52
- end
53
-
54
- #
55
- # Decoding Examples
56
- #
57
-
58
- it "should do OID based type conversions", :ruby_19 do
59
- res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
60
- expect( res.map_types!(basic_type_mapping).values ).to eq( [
61
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
62
- [ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
63
- ] )
64
- end
65
-
66
- #
67
- # Decoding Examples text+binary format converters
68
- #
69
-
70
- describe "connection wide type mapping" do
71
- before :each do
72
- @conn.type_map_for_results = basic_type_mapping
73
- end
74
-
75
- after :each do
76
- @conn.type_map_for_results = PG::TypeMapAllStrings.new
77
- end
78
-
79
- it "should do boolean type conversions" do
80
- [1, 0].each do |format|
81
- res = @conn.exec( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
82
- expect( res.values ).to eq( [[true, false, nil]] )
83
- end
84
- end
85
-
86
- it "should do binary type conversions" do
87
- [1, 0].each do |format|
88
- res = @conn.exec( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
89
- expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
90
- expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
91
- end
92
- end
93
-
94
- it "should do integer type conversions" do
95
- [1, 0].each do |format|
96
- res = @conn.exec( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
97
- expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
98
- end
99
- end
100
-
101
- it "should do string type conversions" do
102
- @conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
103
- [1, 0].each do |format|
104
- res = @conn.exec( "SELECT 'abcäöü'::TEXT", [], format )
105
- expect( res.values ).to eq( [['abcäöü']] )
106
- expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
107
- end
108
- end
109
-
110
- it "should do float type conversions" do
111
- [1, 0].each do |format|
112
- res = @conn.exec( "SELECT -8.999e3::FLOAT4,
113
- 8.999e10::FLOAT4,
114
- -8999999999e-99::FLOAT8,
115
- NULL::FLOAT4,
116
- 'NaN'::FLOAT4,
117
- 'Infinity'::FLOAT4,
118
- '-Infinity'::FLOAT4
119
- ", [], format )
120
- expect( res.getvalue(0,0) ).to be_within(1e-2).of(-8.999e3)
121
- expect( res.getvalue(0,1) ).to be_within(1e5).of(8.999e10)
122
- expect( res.getvalue(0,2) ).to be_within(1e-109).of(-8999999999e-99)
123
- expect( res.getvalue(0,3) ).to be_nil
124
- expect( res.getvalue(0,4) ).to be_nan
125
- expect( res.getvalue(0,5) ).to eq( Float::INFINITY )
126
- expect( res.getvalue(0,6) ).to eq( -Float::INFINITY )
127
- end
128
- end
129
-
130
- it "should do datetime without time zone type conversions" do
131
- [0].each do |format|
132
- res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
133
- CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITHOUT TIME ZONE),
134
- CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
135
- CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
136
- expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
137
- expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123))
138
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
139
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
140
- end
141
- end
142
-
143
- it "should do datetime with time zone type conversions" do
144
- [0].each do |format|
145
- res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
146
- CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITH TIME ZONE),
147
- CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
148
- CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
149
- expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59, "+02:00") )
150
- expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123, "-03:00"))
151
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
152
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
153
- end
154
- end
155
-
156
- it "should do date type conversions" do
157
- [0].each do |format|
158
- res = @conn.exec( "SELECT CAST('2113-12-31' AS DATE),
159
- CAST('1913-12-31' AS DATE),
160
- CAST('infinity' AS DATE),
161
- CAST('-infinity' AS DATE)", [], format )
162
- expect( res.getvalue(0,0) ).to eq( Date.new(2113, 12, 31) )
163
- expect( res.getvalue(0,1) ).to eq( Date.new(1913, 12, 31) )
164
- expect( res.getvalue(0,2) ).to eq( 'infinity' )
165
- expect( res.getvalue(0,3) ).to eq( '-infinity' )
166
- end
167
- end
168
-
169
- it "should do JSON conversions", :postgresql_94 do
170
- [0].each do |format|
171
- ['JSON', 'JSONB'].each do |type|
172
- res = @conn.exec( "SELECT CAST('123' AS #{type}),
173
- CAST('12.3' AS #{type}),
174
- CAST('true' AS #{type}),
175
- CAST('false' AS #{type}),
176
- CAST('null' AS #{type}),
177
- CAST('[1, \"a\", null]' AS #{type}),
178
- CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
179
- expect( res.getvalue(0,0) ).to eq( 123 )
180
- expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
181
- expect( res.getvalue(0,2) ).to eq( true )
182
- expect( res.getvalue(0,3) ).to eq( false )
183
- expect( res.getvalue(0,4) ).to eq( nil )
184
- expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
185
- expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
186
- end
187
- end
188
- end
189
-
190
- it "should do array type conversions" do
191
- [0].each do |format|
192
- res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
193
- CAST('{1,2,3}' AS INT4[]),
194
- CAST('{1,2,3}' AS INT8[]),
195
- CAST('{1,2,3}' AS TEXT[]),
196
- CAST('{1,2,3}' AS VARCHAR[]),
197
- CAST('{1,2,3}' AS FLOAT4[]),
198
- CAST('{1,2,3}' AS FLOAT8[])
199
- ", [], format )
200
- expect( res.getvalue(0,0) ).to eq( [1,2,3] )
201
- expect( res.getvalue(0,1) ).to eq( [[1,2],[3,4]] )
202
- expect( res.getvalue(0,2) ).to eq( [1,2,3] )
203
- expect( res.getvalue(0,3) ).to eq( [1,2,3] )
204
- expect( res.getvalue(0,4) ).to eq( ['1','2','3'] )
205
- expect( res.getvalue(0,5) ).to eq( ['1','2','3'] )
206
- expect( res.getvalue(0,6) ).to eq( [1.0,2.0,3.0] )
207
- expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
208
- end
209
- end
210
- end
211
-
212
- context "with usage of result oids for copy decoder selection" do
213
- it "can type cast #copy_data output with explicit decoder" do
214
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
215
- @conn.exec( "INSERT INTO copytable VALUES ('a', 123, '{5,4,3}'), ('b', 234, '{2,3}')" )
216
-
217
- # Retrieve table OIDs per empty result.
218
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
219
- tm = basic_type_mapping.build_column_map( res )
220
- row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
221
-
222
- rows = []
223
- @conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
224
- while row=@conn.get_copy_data
225
- rows << row
226
- end
227
- end
228
- expect( rows ).to eq( [['a', 123, [5,4,3]], ['b', 234, [2,3]]] )
229
- end
230
- end
231
- end
232
-
233
-
234
- describe PG::BasicTypeMapBasedOnResult do
235
- let!(:basic_type_mapping) do
236
- PG::BasicTypeMapBasedOnResult.new @conn
237
- end
238
-
239
- context "with usage of result oids for bind params encoder selection" do
240
- it "can type cast query params" do
241
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
242
-
243
- # Retrieve table OIDs per empty result.
244
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
245
- tm = basic_type_mapping.build_column_map( res )
246
-
247
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['a', 123, [5,4,3]], 0, tm )
248
- @conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['b', 234, [2,3]], 0, tm )
249
- res = @conn.exec( "SELECT * FROM copytable" )
250
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
251
- end
252
-
253
- it "can do JSON conversions", :postgresql_94 do
254
- ['JSON', 'JSONB'].each do |type|
255
- sql = "SELECT CAST('123' AS #{type}),
256
- CAST('12.3' AS #{type}),
257
- CAST('true' AS #{type}),
258
- CAST('false' AS #{type}),
259
- CAST('null' AS #{type}),
260
- CAST('[1, \"a\", null]' AS #{type}),
261
- CAST('{\"b\" : [2,3]}' AS #{type})"
262
-
263
- tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
264
- expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
265
-
266
- res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
267
- [ 123,
268
- 12.3,
269
- true,
270
- false,
271
- nil,
272
- [1, "a", nil],
273
- {"b" => [2, 3]},
274
- ], 0, tm )
275
-
276
- expect( res.getvalue(0,0) ).to eq( "123" )
277
- expect( res.getvalue(0,1) ).to eq( "12.3" )
278
- expect( res.getvalue(0,2) ).to eq( "true" )
279
- expect( res.getvalue(0,3) ).to eq( "false" )
280
- expect( res.getvalue(0,4) ).to eq( nil )
281
- expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
282
- expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
283
- end
284
- end
285
- end
286
-
287
- context "with usage of result oids for copy encoder selection" do
288
- it "can type cast #copy_data input with explicit encoder" do
289
- @conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
290
-
291
- # Retrieve table OIDs per empty result set.
292
- res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
293
- tm = basic_type_mapping.build_column_map( res )
294
- row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
295
-
296
- @conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
297
- @conn.put_copy_data ['a', 123, [5,4,3]]
298
- @conn.put_copy_data ['b', 234, [2,3]]
299
- end
300
- res = @conn.exec( "SELECT * FROM copytable" )
301
- expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
302
- end
303
- end
304
- end
305
- end