pg 0.18.3 → 1.4.3

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