pg 0.18.1 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  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/BSDL +2 -2
  15. data/Gemfile +17 -0
  16. data/History.md +901 -0
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.md +300 -0
  20. data/README.md +286 -0
  21. data/Rakefile +43 -131
  22. data/Rakefile.cross +89 -70
  23. data/certs/ged.pem +24 -0
  24. data/certs/kanis@comcard.de.pem +20 -0
  25. data/certs/larskanis-2022.pem +26 -0
  26. data/certs/larskanis-2023.pem +24 -0
  27. data/certs/larskanis-2024.pem +24 -0
  28. data/ext/errorcodes.def +113 -0
  29. data/ext/errorcodes.rb +1 -1
  30. data/ext/errorcodes.txt +36 -2
  31. data/ext/extconf.rb +128 -55
  32. data/ext/gvl_wrappers.c +8 -0
  33. data/ext/gvl_wrappers.h +44 -33
  34. data/ext/pg.c +228 -202
  35. data/ext/pg.h +108 -99
  36. data/ext/pg_binary_decoder.c +164 -16
  37. data/ext/pg_binary_encoder.c +249 -22
  38. data/ext/pg_coder.c +189 -44
  39. data/ext/pg_connection.c +1889 -1195
  40. data/ext/pg_copy_coder.c +398 -42
  41. data/ext/pg_errors.c +1 -1
  42. data/ext/pg_record_coder.c +522 -0
  43. data/ext/pg_result.c +729 -234
  44. data/ext/pg_text_decoder.c +635 -52
  45. data/ext/pg_text_encoder.c +294 -130
  46. data/ext/pg_tuple.c +572 -0
  47. data/ext/pg_type_map.c +64 -23
  48. data/ext/pg_type_map_all_strings.c +21 -7
  49. data/ext/pg_type_map_by_class.c +59 -27
  50. data/ext/pg_type_map_by_column.c +86 -43
  51. data/ext/pg_type_map_by_mri_type.c +50 -21
  52. data/ext/pg_type_map_by_oid.c +62 -29
  53. data/ext/pg_type_map_in_ruby.c +59 -28
  54. data/ext/{util.c → pg_util.c} +13 -13
  55. data/ext/{util.h → pg_util.h} +3 -3
  56. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  57. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  58. data/lib/pg/basic_type_map_for_results.rb +104 -0
  59. data/lib/pg/basic_type_registry.rb +303 -0
  60. data/lib/pg/binary_decoder/date.rb +9 -0
  61. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  62. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  63. data/lib/pg/coder.rb +36 -13
  64. data/lib/pg/connection.rb +813 -74
  65. data/lib/pg/exceptions.rb +16 -2
  66. data/lib/pg/result.rb +24 -7
  67. data/lib/pg/text_decoder/date.rb +18 -0
  68. data/lib/pg/text_decoder/inet.rb +9 -0
  69. data/lib/pg/text_decoder/json.rb +14 -0
  70. data/lib/pg/text_decoder/numeric.rb +9 -0
  71. data/lib/pg/text_decoder/timestamp.rb +30 -0
  72. data/lib/pg/text_encoder/date.rb +12 -0
  73. data/lib/pg/text_encoder/inet.rb +28 -0
  74. data/lib/pg/text_encoder/json.rb +14 -0
  75. data/lib/pg/text_encoder/numeric.rb +9 -0
  76. data/lib/pg/text_encoder/timestamp.rb +24 -0
  77. data/lib/pg/tuple.rb +30 -0
  78. data/lib/pg/type_map_by_column.rb +3 -2
  79. data/lib/pg/version.rb +4 -0
  80. data/lib/pg.rb +106 -41
  81. data/misc/openssl-pg-segfault.rb +31 -0
  82. data/misc/postgres/History.txt +9 -0
  83. data/misc/postgres/Manifest.txt +5 -0
  84. data/misc/postgres/README.txt +21 -0
  85. data/misc/postgres/Rakefile +21 -0
  86. data/misc/postgres/lib/postgres.rb +16 -0
  87. data/misc/ruby-pg/History.txt +9 -0
  88. data/misc/ruby-pg/Manifest.txt +5 -0
  89. data/misc/ruby-pg/README.txt +21 -0
  90. data/misc/ruby-pg/Rakefile +21 -0
  91. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  92. data/pg.gemspec +34 -0
  93. data/rakelib/task_extension.rb +46 -0
  94. data/sample/array_insert.rb +1 -1
  95. data/sample/async_api.rb +4 -8
  96. data/sample/async_copyto.rb +1 -1
  97. data/sample/async_mixed.rb +1 -1
  98. data/sample/check_conn.rb +1 -1
  99. data/sample/copydata.rb +71 -0
  100. data/sample/copyfrom.rb +1 -1
  101. data/sample/copyto.rb +1 -1
  102. data/sample/cursor.rb +1 -1
  103. data/sample/disk_usage_report.rb +6 -15
  104. data/sample/issue-119.rb +2 -2
  105. data/sample/losample.rb +1 -1
  106. data/sample/minimal-testcase.rb +2 -2
  107. data/sample/notify_wait.rb +1 -1
  108. data/sample/pg_statistics.rb +6 -15
  109. data/sample/replication_monitor.rb +9 -18
  110. data/sample/test_binary_values.rb +1 -1
  111. data/sample/wal_shipper.rb +2 -2
  112. data/sample/warehouse_partitions.rb +8 -17
  113. data.tar.gz.sig +0 -0
  114. metadata +135 -207
  115. metadata.gz.sig +0 -0
  116. data/ChangeLog +0 -5378
  117. data/History.rdoc +0 -297
  118. data/README.ja.rdoc +0 -14
  119. data/README.rdoc +0 -161
  120. data/lib/pg/basic_type_mapping.rb +0 -399
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -42
  123. data/lib/pg/text_encoder.rb +0 -27
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -355
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  128. data/spec/pg/connection_spec.rb +0 -1459
  129. data/spec/pg/result_spec.rb +0 -449
  130. data/spec/pg/type_map_by_class_spec.rb +0 -138
  131. data/spec/pg/type_map_by_column_spec.rb +0 -222
  132. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  133. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  134. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  135. data/spec/pg/type_map_spec.rb +0 -22
  136. data/spec/pg/type_spec.rb +0 -665
  137. 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 " [%d] {%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, Time.new(2013,6,30), 4 ],
62
- [ 1, 'a', 2.0, true, Time.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( Time.new(2113, 12, 31) )
163
- expect( res.getvalue(0,1) ).to eq( Time.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