pg 0.21.0 → 1.2.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +184 -0
- data/Manifest.txt +8 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +58 -13
- data/Rakefile +10 -9
- data/Rakefile.cross +68 -71
- data/ext/errorcodes.def +76 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +21 -2
- data/ext/extconf.rb +18 -36
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +154 -144
- data/ext/pg.h +68 -95
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +73 -12
- data/ext/pg_connection.c +699 -459
- data/ext/pg_copy_coder.c +16 -8
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +571 -195
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +185 -54
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +9 -4
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +8 -10
- data/lib/pg/basic_type_mapping.rb +121 -25
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +28 -4
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +61 -33
- data/spec/pg/basic_type_mapping_spec.rb +362 -37
- data/spec/pg/connection_spec.rb +602 -329
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +242 -17
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +364 -18
- data/spec/pg_spec.rb +2 -2
- metadata +48 -43
- metadata.gz.sig +0 -0
- data/lib/pg/deprecated_constants.rb +0 -21
data/spec/pg/connection_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -118,6 +118,19 @@ describe PG::Connection do
|
|
118
118
|
expect( described_class.parse_connect_args ).to eq( '' )
|
119
119
|
end
|
120
120
|
|
121
|
+
it "connects successfully with connection string" do
|
122
|
+
conninfo_with_colon_in_password = "host=localhost user=a port=555 dbname=test password=a:a"
|
123
|
+
|
124
|
+
string = described_class.parse_connect_args( conninfo_with_colon_in_password )
|
125
|
+
|
126
|
+
expect( string ).to be_a( String )
|
127
|
+
expect( string ).to match( %r{(^|\s)user=a} )
|
128
|
+
expect( string ).to match( %r{(^|\s)password=a:a} )
|
129
|
+
expect( string ).to match( %r{(^|\s)host=localhost} )
|
130
|
+
expect( string ).to match( %r{(^|\s)port=555} )
|
131
|
+
expect( string ).to match( %r{(^|\s)dbname=test} )
|
132
|
+
end
|
133
|
+
|
121
134
|
it "connects successfully with connection string" do
|
122
135
|
tmpconn = described_class.connect( @conninfo )
|
123
136
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
@@ -139,7 +152,7 @@ describe PG::Connection do
|
|
139
152
|
tmpconn.finish
|
140
153
|
end
|
141
154
|
|
142
|
-
it "connects using a hash of optional connection parameters"
|
155
|
+
it "connects using a hash of optional connection parameters" do
|
143
156
|
tmpconn = described_class.connect(
|
144
157
|
:host => 'localhost',
|
145
158
|
:port => @port,
|
@@ -160,56 +173,94 @@ describe PG::Connection do
|
|
160
173
|
end
|
161
174
|
end
|
162
175
|
|
163
|
-
it "can connect asynchronously"
|
176
|
+
it "can connect asynchronously" do
|
164
177
|
tmpconn = described_class.connect_start( @conninfo )
|
165
178
|
expect( tmpconn ).to be_a( described_class )
|
166
|
-
socket = tmpconn.socket_io
|
167
|
-
status = tmpconn.connect_poll
|
168
|
-
|
169
|
-
while status != PG::PGRES_POLLING_OK
|
170
|
-
if status == PG::PGRES_POLLING_READING
|
171
|
-
select( [socket], [], [], 5.0 ) or
|
172
|
-
raise "Asynchronous connection timed out!"
|
173
|
-
|
174
|
-
elsif status == PG::PGRES_POLLING_WRITING
|
175
|
-
select( [], [socket], [], 5.0 ) or
|
176
|
-
raise "Asynchronous connection timed out!"
|
177
|
-
end
|
178
|
-
status = tmpconn.connect_poll
|
179
|
-
end
|
180
179
|
|
180
|
+
wait_for_polling_ok(tmpconn)
|
181
181
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
182
182
|
tmpconn.finish
|
183
183
|
end
|
184
184
|
|
185
|
-
it "can connect asynchronously for the duration of a block"
|
185
|
+
it "can connect asynchronously for the duration of a block" do
|
186
186
|
conn = nil
|
187
187
|
|
188
188
|
described_class.connect_start(@conninfo) do |tmpconn|
|
189
189
|
expect( tmpconn ).to be_a( described_class )
|
190
190
|
conn = tmpconn
|
191
|
-
socket = tmpconn.socket_io
|
192
|
-
status = tmpconn.connect_poll
|
193
|
-
|
194
|
-
while status != PG::PGRES_POLLING_OK
|
195
|
-
if status == PG::PGRES_POLLING_READING
|
196
|
-
if(not select([socket],[],[],5.0))
|
197
|
-
raise "Asynchronous connection timed out!"
|
198
|
-
end
|
199
|
-
elsif(status == PG::PGRES_POLLING_WRITING)
|
200
|
-
if(not select([],[socket],[],5.0))
|
201
|
-
raise "Asynchronous connection timed out!"
|
202
|
-
end
|
203
|
-
end
|
204
|
-
status = tmpconn.connect_poll
|
205
|
-
end
|
206
191
|
|
192
|
+
wait_for_polling_ok(tmpconn)
|
207
193
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
208
194
|
end
|
209
195
|
|
210
196
|
expect( conn ).to be_finished()
|
211
197
|
end
|
212
198
|
|
199
|
+
context "with async established connection" do
|
200
|
+
before :each do
|
201
|
+
@conn2 = described_class.connect_start( @conninfo )
|
202
|
+
wait_for_polling_ok(@conn2)
|
203
|
+
expect( @conn2 ).to still_be_usable
|
204
|
+
end
|
205
|
+
|
206
|
+
after :each do
|
207
|
+
expect( @conn2 ).to still_be_usable
|
208
|
+
@conn2.close
|
209
|
+
end
|
210
|
+
|
211
|
+
it "conn.send_query and IO.select work" do
|
212
|
+
@conn2.send_query("SELECT 1")
|
213
|
+
res = wait_for_query_result(@conn2)
|
214
|
+
expect( res.values ).to eq([["1"]])
|
215
|
+
end
|
216
|
+
|
217
|
+
it "conn.send_query and conn.block work" do
|
218
|
+
@conn2.send_query("SELECT 2")
|
219
|
+
@conn2.block
|
220
|
+
res = @conn2.get_last_result
|
221
|
+
expect( res.values ).to eq([["2"]])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "conn.async_query works" do
|
225
|
+
res = @conn2.async_query("SELECT 3")
|
226
|
+
expect( res.values ).to eq([["3"]])
|
227
|
+
expect( @conn2 ).to still_be_usable
|
228
|
+
|
229
|
+
res = @conn2.query("SELECT 4")
|
230
|
+
end
|
231
|
+
|
232
|
+
it "can use conn.reset_start to restart the connection" do
|
233
|
+
ios = IO.pipe
|
234
|
+
conn = described_class.connect_start( @conninfo )
|
235
|
+
wait_for_polling_ok(conn)
|
236
|
+
|
237
|
+
# Close the two pipe file descriptors, so that the file descriptor of
|
238
|
+
# newly established connection is probably distinct from the previous one.
|
239
|
+
ios.each(&:close)
|
240
|
+
conn.reset_start
|
241
|
+
wait_for_polling_ok(conn, :reset_poll)
|
242
|
+
|
243
|
+
# The new connection should work even when the file descriptor has changed.
|
244
|
+
conn.send_query("SELECT 1")
|
245
|
+
res = wait_for_query_result(conn)
|
246
|
+
expect( res.values ).to eq([["1"]])
|
247
|
+
|
248
|
+
conn.close
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should properly close a socket IO when GC'ed" do
|
252
|
+
# This results in
|
253
|
+
# Errno::ENOTSOCK: An operation was attempted on something that is not a socket.
|
254
|
+
# on Windows when rb_w32_unwrap_io_handle() isn't called in pgconn_gc_free().
|
255
|
+
5.times do
|
256
|
+
conn = described_class.connect( @conninfo )
|
257
|
+
conn.socket_io.close
|
258
|
+
end
|
259
|
+
GC.start
|
260
|
+
IO.pipe.each(&:close)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
213
264
|
it "raises proper error when sending fails" do
|
214
265
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
215
266
|
expect{ conn.exec 'SELECT 1' }.to raise_error(PG::UnableToSend, /no connection/)
|
@@ -219,7 +270,7 @@ describe PG::Connection do
|
|
219
270
|
described_class.connect(@conninfo).finish
|
220
271
|
sleep 0.5
|
221
272
|
res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
|
222
|
-
WHERE usename IS NOT NULL])
|
273
|
+
WHERE usename IS NOT NULL AND application_name != ''])
|
223
274
|
# there's still the global @conn, but should be no more
|
224
275
|
expect( res[0]['n'] ).to eq( '1' )
|
225
276
|
end
|
@@ -228,7 +279,7 @@ describe PG::Connection do
|
|
228
279
|
expect( @conn.db ).to eq( "test" )
|
229
280
|
expect( @conn.user ).to be_a_kind_of( String )
|
230
281
|
expect( @conn.pass ).to eq( "" )
|
231
|
-
expect( @conn.port ).to eq(
|
282
|
+
expect( @conn.port ).to eq( @port )
|
232
283
|
expect( @conn.tty ).to eq( "" )
|
233
284
|
expect( @conn.options ).to eq( "" )
|
234
285
|
end
|
@@ -237,7 +288,20 @@ describe PG::Connection do
|
|
237
288
|
expect( @conn.host ).to eq( "localhost" )
|
238
289
|
end
|
239
290
|
|
240
|
-
|
291
|
+
it "can set error verbosity" do
|
292
|
+
old = @conn.set_error_verbosity( PG::PQERRORS_TERSE )
|
293
|
+
new = @conn.set_error_verbosity( old )
|
294
|
+
expect( new ).to eq( PG::PQERRORS_TERSE )
|
295
|
+
end
|
296
|
+
|
297
|
+
it "can set error context visibility", :postgresql_96 do
|
298
|
+
old = @conn.set_error_context_visibility( PG::PQSHOW_CONTEXT_NEVER )
|
299
|
+
new = @conn.set_error_context_visibility( old )
|
300
|
+
expect( new ).to eq( PG::PQSHOW_CONTEXT_NEVER )
|
301
|
+
end
|
302
|
+
|
303
|
+
let(:expected_trace_output) do
|
304
|
+
%{
|
241
305
|
To backend> Msg Q
|
242
306
|
To backend> "SELECT 1 AS one"
|
243
307
|
To backend> Msg complete, length 21
|
@@ -265,6 +329,7 @@ describe PG::Connection do
|
|
265
329
|
From backend (#4)> 5
|
266
330
|
From backend> T
|
267
331
|
}.gsub( /^\t{2}/, '' ).lstrip
|
332
|
+
end
|
268
333
|
|
269
334
|
it "trace and untrace client-server communication", :unix do
|
270
335
|
# be careful to explicitly close files so that the
|
@@ -275,23 +340,20 @@ describe PG::Connection do
|
|
275
340
|
@conn.trace( trace_io )
|
276
341
|
trace_io.close
|
277
342
|
|
278
|
-
|
343
|
+
@conn.exec("SELECT 1 AS one")
|
279
344
|
@conn.untrace
|
280
345
|
|
281
|
-
|
346
|
+
@conn.exec("SELECT 2 AS two")
|
282
347
|
|
283
348
|
trace_data = trace_file.read
|
284
349
|
|
285
|
-
|
286
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
# +From backend (#4)>
|
290
|
-
#
|
291
|
-
|
292
|
-
expected_trace_output.sub!( /From backend \(#4\)> 13/, 'From backend (#4)> 11' )
|
293
|
-
expected_trace_output.sub!( /From backend> "SELECT 1"/, 'From backend> "SELECT"' )
|
294
|
-
end
|
350
|
+
# For async_exec the output will be different:
|
351
|
+
# From backend> Z
|
352
|
+
# From backend (#4)> 5
|
353
|
+
# +From backend> Z
|
354
|
+
# +From backend (#4)> 5
|
355
|
+
# From backend> T
|
356
|
+
trace_data.sub!( /(From backend> Z\nFrom backend \(#4\)> 5\n){3}/m, '\\1\\1' )
|
295
357
|
|
296
358
|
expect( trace_data ).to eq( expected_trace_output )
|
297
359
|
end
|
@@ -308,8 +370,6 @@ describe PG::Connection do
|
|
308
370
|
end
|
309
371
|
|
310
372
|
it "can stop a thread that runs a blocking query with async_exec" do
|
311
|
-
pending "this does not work on Rubinius" if RUBY_ENGINE=='rbx'
|
312
|
-
|
313
373
|
start = Time.now
|
314
374
|
t = Thread.new do
|
315
375
|
@conn.async_exec( 'select pg_sleep(10)' )
|
@@ -323,24 +383,16 @@ describe PG::Connection do
|
|
323
383
|
|
324
384
|
it "should work together with signal handlers", :unix do
|
325
385
|
signal_received = false
|
326
|
-
trap '
|
386
|
+
trap 'USR2' do
|
327
387
|
signal_received = true
|
328
388
|
end
|
329
389
|
|
330
390
|
Thread.new do
|
331
391
|
sleep 0.1
|
332
|
-
Process.kill("
|
392
|
+
Process.kill("USR2", Process.pid)
|
333
393
|
end
|
334
394
|
@conn.exec("select pg_sleep(0.3)")
|
335
395
|
expect( signal_received ).to be_truthy
|
336
|
-
|
337
|
-
signal_received = false
|
338
|
-
Thread.new do
|
339
|
-
sleep 0.1
|
340
|
-
Process.kill("USR1", Process.pid)
|
341
|
-
end
|
342
|
-
@conn.async_exec("select pg_sleep(0.3)")
|
343
|
-
expect( signal_received ).to be_truthy
|
344
396
|
end
|
345
397
|
|
346
398
|
|
@@ -388,22 +440,11 @@ describe PG::Connection do
|
|
388
440
|
end
|
389
441
|
end
|
390
442
|
|
391
|
-
|
392
|
-
it "supports parameters passed to #exec (backward compatibility)" do
|
393
|
-
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
394
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
395
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
396
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
397
|
-
|
398
|
-
res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
|
399
|
-
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
400
|
-
end
|
401
|
-
|
402
443
|
it "supports explicitly calling #exec_params" do
|
403
444
|
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
404
|
-
@conn.
|
405
|
-
@conn.
|
406
|
-
@conn.
|
445
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
446
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
447
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
407
448
|
|
408
449
|
res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
|
409
450
|
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
@@ -524,7 +565,7 @@ describe PG::Connection do
|
|
524
565
|
@conn.exec( 'UNLISTEN woo' )
|
525
566
|
end
|
526
567
|
|
527
|
-
it "can receive notices while waiting for NOTIFY without exceeding the timeout"
|
568
|
+
it "can receive notices while waiting for NOTIFY without exceeding the timeout" do
|
528
569
|
notices = []
|
529
570
|
@conn.set_notice_processor do |msg|
|
530
571
|
notices << [msg, Time.now]
|
@@ -534,7 +575,7 @@ describe PG::Connection do
|
|
534
575
|
expect( @conn.wait_for_notify( 1 ) ).to be_nil
|
535
576
|
expect( notices.first ).to_not be_nil
|
536
577
|
et = Time.now
|
537
|
-
expect( (et - notices.first[1]) ).to be >= 0.
|
578
|
+
expect( (et - notices.first[1]) ).to be >= 0.3
|
538
579
|
expect( (et - st) ).to be >= 0.9
|
539
580
|
expect( (et - st) ).to be < 1.4
|
540
581
|
end
|
@@ -586,7 +627,7 @@ describe PG::Connection do
|
|
586
627
|
expect( @conn ).to still_be_usable
|
587
628
|
end
|
588
629
|
|
589
|
-
it "can handle server errors in #copy_data for output"
|
630
|
+
it "can handle server errors in #copy_data for output" do
|
590
631
|
@conn.exec "ROLLBACK"
|
591
632
|
@conn.transaction do
|
592
633
|
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
@@ -638,7 +679,7 @@ describe PG::Connection do
|
|
638
679
|
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
639
680
|
@conn.put_copy_data "xyz\n"
|
640
681
|
end
|
641
|
-
}.to raise_error(PG::Error, /invalid input syntax for integer/)
|
682
|
+
}.to raise_error(PG::Error, /invalid input syntax for .*integer/)
|
642
683
|
end
|
643
684
|
expect( @conn ).to still_be_usable
|
644
685
|
end
|
@@ -718,11 +759,6 @@ describe PG::Connection do
|
|
718
759
|
expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
|
719
760
|
end
|
720
761
|
|
721
|
-
|
722
|
-
it "can encrypt a string given a password and username" do
|
723
|
-
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
724
|
-
end
|
725
|
-
|
726
762
|
it "can return the default connection options" do
|
727
763
|
expect( described_class.conndefaults ).to be_a( Array )
|
728
764
|
expect( described_class.conndefaults ).to all( be_a(Hash) )
|
@@ -733,7 +769,7 @@ describe PG::Connection do
|
|
733
769
|
it "can return the default connection options as a Hash" do
|
734
770
|
expect( described_class.conndefaults_hash ).to be_a( Hash )
|
735
771
|
expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
|
736
|
-
expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
|
772
|
+
expect( ['5432', '54321', @port.to_s] ).to include( described_class.conndefaults_hash[:port] )
|
737
773
|
expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
|
738
774
|
end
|
739
775
|
|
@@ -779,18 +815,52 @@ describe PG::Connection do
|
|
779
815
|
end
|
780
816
|
end
|
781
817
|
|
818
|
+
describe "deprecated password encryption method" do
|
819
|
+
it "can encrypt password for a given user" do
|
820
|
+
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
821
|
+
end
|
782
822
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
823
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
824
|
+
expect {
|
825
|
+
described_class.encrypt_password( nil, nil )
|
826
|
+
}.to raise_error( TypeError )
|
827
|
+
expect {
|
828
|
+
described_class.encrypt_password( "postgres", nil )
|
829
|
+
}.to raise_error( TypeError )
|
830
|
+
expect {
|
831
|
+
described_class.encrypt_password( nil, "postgres" )
|
832
|
+
}.to raise_error( TypeError )
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
describe "password encryption method", :postgresql_10 do
|
837
|
+
it "can encrypt without algorithm" do
|
838
|
+
expect( @conn.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
839
|
+
expect( @conn.encrypt_password("postgres", "postgres", nil) ).to match( /\S+/ )
|
840
|
+
end
|
841
|
+
|
842
|
+
it "can encrypt with algorithm" do
|
843
|
+
expect( @conn.encrypt_password("postgres", "postgres", "md5") ).to match( /md5\S+/i )
|
844
|
+
expect( @conn.encrypt_password("postgres", "postgres", "scram-sha-256") ).to match( /SCRAM-SHA-256\S+/i )
|
845
|
+
end
|
846
|
+
|
847
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
848
|
+
expect {
|
849
|
+
@conn.encrypt_password( nil, nil )
|
850
|
+
}.to raise_error( TypeError )
|
851
|
+
expect {
|
852
|
+
@conn.encrypt_password( "postgres", nil )
|
853
|
+
}.to raise_error( TypeError )
|
854
|
+
expect {
|
855
|
+
@conn.encrypt_password( nil, "postgres" )
|
856
|
+
}.to raise_error( TypeError )
|
857
|
+
expect {
|
858
|
+
@conn.encrypt_password( "postgres", "postgres", :invalid )
|
859
|
+
}.to raise_error( TypeError )
|
860
|
+
expect {
|
861
|
+
@conn.encrypt_password( "postgres", "postgres", "invalid" )
|
862
|
+
}.to raise_error( PG::Error, /unrecognized/ )
|
863
|
+
end
|
794
864
|
end
|
795
865
|
|
796
866
|
|
@@ -832,7 +902,7 @@ describe PG::Connection do
|
|
832
902
|
end
|
833
903
|
|
834
904
|
|
835
|
-
it "
|
905
|
+
it "handles server close while asynchronous connect" do
|
836
906
|
serv = TCPServer.new( '127.0.0.1', 54320 )
|
837
907
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
838
908
|
expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
|
@@ -844,11 +914,29 @@ describe PG::Connection do
|
|
844
914
|
expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
|
845
915
|
end
|
846
916
|
|
847
|
-
it "discards previous results
|
917
|
+
it "discards previous results at #discard_results" do
|
918
|
+
@conn.send_query( "select 1" )
|
919
|
+
@conn.discard_results
|
920
|
+
@conn.send_query( "select 41 as one" )
|
921
|
+
res = @conn.get_last_result
|
922
|
+
expect( res.to_a ).to eq( [{ 'one' => '41' }] )
|
923
|
+
end
|
924
|
+
|
925
|
+
it "discards previous results (if any) before waiting on #exec" do
|
926
|
+
@conn.send_query( "select 1" )
|
927
|
+
res = @conn.exec( "select 42 as one" )
|
928
|
+
expect( res.to_a ).to eq( [{ 'one' => '42' }] )
|
929
|
+
end
|
930
|
+
|
931
|
+
it "discards previous errors before waiting on #exec", :without_transaction do
|
932
|
+
@conn.send_query( "ERROR" )
|
933
|
+
res = @conn.exec( "select 43 as one" )
|
934
|
+
expect( res.to_a ).to eq( [{ 'one' => '43' }] )
|
935
|
+
end
|
848
936
|
|
849
|
-
it "calls the block if one is provided to #
|
937
|
+
it "calls the block if one is provided to #exec" do
|
850
938
|
result = nil
|
851
|
-
@conn.
|
939
|
+
@conn.exec( "select 47 as one" ) do |pg_res|
|
852
940
|
result = pg_res[0]
|
853
941
|
end
|
854
942
|
expect( result ).to eq( { 'one' => '47' } )
|
@@ -861,7 +949,21 @@ describe PG::Connection do
|
|
861
949
|
expect { conn.finish }.to raise_error( PG::ConnectionBad, /connection is closed/i )
|
862
950
|
end
|
863
951
|
|
864
|
-
it "
|
952
|
+
it "can use conn.reset to restart the connection" do
|
953
|
+
ios = IO.pipe
|
954
|
+
conn = PG.connect( @conninfo )
|
955
|
+
|
956
|
+
# Close the two pipe file descriptors, so that the file descriptor of
|
957
|
+
# newly established connection is probably distinct from the previous one.
|
958
|
+
ios.each(&:close)
|
959
|
+
conn.reset
|
960
|
+
|
961
|
+
# The new connection should work even when the file descriptor has changed.
|
962
|
+
expect( conn.exec("SELECT 1").values ).to eq([["1"]])
|
963
|
+
conn.close
|
964
|
+
end
|
965
|
+
|
966
|
+
it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction do
|
865
967
|
conn = PG.connect( @conninfo )
|
866
968
|
io = conn.socket_io
|
867
969
|
conn.finish
|
@@ -869,7 +971,7 @@ describe PG::Connection do
|
|
869
971
|
expect { conn.socket_io }.to raise_error( PG::ConnectionBad, /connection is closed/i )
|
870
972
|
end
|
871
973
|
|
872
|
-
it "closes the IO fetched from #socket_io when the connection is reset", :without_transaction
|
974
|
+
it "closes the IO fetched from #socket_io when the connection is reset", :without_transaction do
|
873
975
|
conn = PG.connect( @conninfo )
|
874
976
|
io = conn.socket_io
|
875
977
|
conn.reset
|
@@ -889,155 +991,147 @@ describe PG::Connection do
|
|
889
991
|
expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
|
890
992
|
end
|
891
993
|
|
892
|
-
|
994
|
+
it "sets the fallback_application_name on new connections" do
|
995
|
+
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
893
996
|
|
894
|
-
|
895
|
-
|
896
|
-
|
997
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
998
|
+
expect( conn_name ).to include( $0[0..10] )
|
999
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
1000
|
+
expect( conn_name.length ).to be <= 64
|
1001
|
+
end
|
897
1002
|
|
898
|
-
|
1003
|
+
it "sets a shortened fallback_application_name on new connections" do
|
1004
|
+
old_0 = $0
|
1005
|
+
begin
|
1006
|
+
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
899
1007
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
900
|
-
|
901
1008
|
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
902
1009
|
expect( conn_name ).to include( $0[0..10] )
|
903
1010
|
expect( conn_name ).to include( $0[-10..-1] )
|
904
1011
|
expect( conn_name.length ).to be <= 64
|
1012
|
+
ensure
|
1013
|
+
$0 = old_0
|
905
1014
|
end
|
1015
|
+
end
|
906
1016
|
|
907
|
-
|
908
|
-
|
909
|
-
begin
|
910
|
-
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
911
|
-
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
912
|
-
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
913
|
-
expect( conn_name ).to include( $0[0..10] )
|
914
|
-
expect( conn_name ).to include( $0[-10..-1] )
|
915
|
-
expect( conn_name.length ).to be <= 64
|
916
|
-
ensure
|
917
|
-
$0 = old_0
|
918
|
-
end
|
919
|
-
end
|
920
|
-
|
921
|
-
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
922
|
-
"any number of arguments" do
|
923
|
-
|
924
|
-
@conn.exec( 'ROLLBACK' )
|
925
|
-
@conn.exec( 'LISTEN knees' )
|
1017
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1018
|
+
"any number of arguments" do
|
926
1019
|
|
927
|
-
|
928
|
-
|
929
|
-
conn.finish
|
1020
|
+
@conn.exec( 'ROLLBACK' )
|
1021
|
+
@conn.exec( 'LISTEN knees' )
|
930
1022
|
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
end
|
935
|
-
@conn.exec( 'UNLISTEN knees' )
|
1023
|
+
conn = described_class.connect( @conninfo )
|
1024
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1025
|
+
conn.finish
|
936
1026
|
|
937
|
-
|
938
|
-
|
939
|
-
|
1027
|
+
event, pid, msg = nil
|
1028
|
+
@conn.wait_for_notify( 10 ) do |*args|
|
1029
|
+
event, pid, msg = *args
|
940
1030
|
end
|
1031
|
+
@conn.exec( 'UNLISTEN knees' )
|
941
1032
|
|
942
|
-
|
943
|
-
|
944
|
-
|
1033
|
+
expect( event ).to eq( 'knees' )
|
1034
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1035
|
+
expect( msg ).to eq( 'skirt and boots' )
|
1036
|
+
end
|
945
1037
|
|
946
|
-
|
947
|
-
|
948
|
-
|
1038
|
+
it "accepts nil as the timeout in #wait_for_notify " do
|
1039
|
+
@conn.exec( 'ROLLBACK' )
|
1040
|
+
@conn.exec( 'LISTEN knees' )
|
949
1041
|
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
end
|
954
|
-
@conn.exec( 'UNLISTEN knees' )
|
1042
|
+
conn = described_class.connect( @conninfo )
|
1043
|
+
conn.exec( %Q{NOTIFY knees} )
|
1044
|
+
conn.finish
|
955
1045
|
|
956
|
-
|
957
|
-
|
1046
|
+
event, pid = nil
|
1047
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1048
|
+
event, pid = *args
|
958
1049
|
end
|
1050
|
+
@conn.exec( 'UNLISTEN knees' )
|
959
1051
|
|
960
|
-
|
961
|
-
|
962
|
-
|
1052
|
+
expect( event ).to eq( 'knees' )
|
1053
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1054
|
+
end
|
963
1055
|
|
964
|
-
|
965
|
-
|
966
|
-
|
1056
|
+
it "sends nil as the payload if the notification wasn't given one" do
|
1057
|
+
@conn.exec( 'ROLLBACK' )
|
1058
|
+
@conn.exec( 'LISTEN knees' )
|
967
1059
|
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
end
|
972
|
-
@conn.exec( 'UNLISTEN knees' )
|
1060
|
+
conn = described_class.connect( @conninfo )
|
1061
|
+
conn.exec( %Q{NOTIFY knees} )
|
1062
|
+
conn.finish
|
973
1063
|
|
974
|
-
|
1064
|
+
payload = :notnil
|
1065
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1066
|
+
payload = args[ 2 ]
|
975
1067
|
end
|
1068
|
+
@conn.exec( 'UNLISTEN knees' )
|
976
1069
|
|
977
|
-
|
978
|
-
|
1070
|
+
expect( payload ).to be_nil()
|
1071
|
+
end
|
979
1072
|
|
980
|
-
|
981
|
-
|
1073
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1074
|
+
"two arguments" do
|
982
1075
|
|
983
|
-
|
984
|
-
|
985
|
-
conn.finish
|
1076
|
+
@conn.exec( 'ROLLBACK' )
|
1077
|
+
@conn.exec( 'LISTEN knees' )
|
986
1078
|
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
end
|
991
|
-
@conn.exec( 'UNLISTEN knees' )
|
1079
|
+
conn = described_class.connect( @conninfo )
|
1080
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1081
|
+
conn.finish
|
992
1082
|
|
993
|
-
|
994
|
-
|
995
|
-
|
1083
|
+
event, pid, msg = nil
|
1084
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2|
|
1085
|
+
event, pid, msg = arg1, arg2
|
996
1086
|
end
|
1087
|
+
@conn.exec( 'UNLISTEN knees' )
|
997
1088
|
|
998
|
-
|
999
|
-
|
1089
|
+
expect( event ).to eq( 'knees' )
|
1090
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1091
|
+
expect( msg ).to be_nil()
|
1092
|
+
end
|
1000
1093
|
|
1001
|
-
|
1002
|
-
|
1094
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it " +
|
1095
|
+
"doesn't accept arguments" do
|
1003
1096
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
conn.finish
|
1097
|
+
@conn.exec( 'ROLLBACK' )
|
1098
|
+
@conn.exec( 'LISTEN knees' )
|
1007
1099
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
end
|
1012
|
-
@conn.exec( 'UNLISTEN knees' )
|
1100
|
+
conn = described_class.connect( @conninfo )
|
1101
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1102
|
+
conn.finish
|
1013
1103
|
|
1014
|
-
|
1104
|
+
notification_received = false
|
1105
|
+
@conn.wait_for_notify( 10 ) do
|
1106
|
+
notification_received = true
|
1015
1107
|
end
|
1108
|
+
@conn.exec( 'UNLISTEN knees' )
|
1016
1109
|
|
1017
|
-
|
1018
|
-
|
1110
|
+
expect( notification_received ).to be_truthy()
|
1111
|
+
end
|
1019
1112
|
|
1020
|
-
|
1021
|
-
|
1113
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1114
|
+
"three arguments" do
|
1022
1115
|
|
1023
|
-
|
1024
|
-
|
1025
|
-
conn.finish
|
1116
|
+
@conn.exec( 'ROLLBACK' )
|
1117
|
+
@conn.exec( 'LISTEN knees' )
|
1026
1118
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
end
|
1031
|
-
@conn.exec( 'UNLISTEN knees' )
|
1119
|
+
conn = described_class.connect( @conninfo )
|
1120
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1121
|
+
conn.finish
|
1032
1122
|
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1123
|
+
event, pid, msg = nil
|
1124
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
|
1125
|
+
event, pid, msg = arg1, arg2, arg3
|
1036
1126
|
end
|
1127
|
+
@conn.exec( 'UNLISTEN knees' )
|
1037
1128
|
|
1129
|
+
expect( event ).to eq( 'knees' )
|
1130
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1131
|
+
expect( msg ).to eq( 'skirt and boots' )
|
1038
1132
|
end
|
1039
1133
|
|
1040
|
-
context "
|
1134
|
+
context "server ping", :without_transaction do
|
1041
1135
|
|
1042
1136
|
it "pings successfully with connection string" do
|
1043
1137
|
ping = described_class.ping(@conninfo)
|
@@ -1065,125 +1159,119 @@ describe PG::Connection do
|
|
1065
1159
|
expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
|
1066
1160
|
end
|
1067
1161
|
|
1068
|
-
it "returns
|
1162
|
+
it "returns error when ping connection arguments are wrong" do
|
1069
1163
|
ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
|
1070
|
-
expect( ping ).
|
1164
|
+
expect( ping ).to_not eq( PG::PQPING_OK )
|
1071
1165
|
end
|
1072
1166
|
|
1167
|
+
it "returns correct response when ping connection arguments are wrong" do
|
1168
|
+
ping = described_class.ping(
|
1169
|
+
:host => 'localhost',
|
1170
|
+
:invalid_option => 9999,
|
1171
|
+
:dbname => :test)
|
1172
|
+
expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
|
1173
|
+
end
|
1073
1174
|
|
1074
1175
|
end
|
1075
1176
|
|
1076
|
-
|
1077
|
-
describe "set_single_row_mode" do
|
1177
|
+
describe "set_single_row_mode" do
|
1078
1178
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1179
|
+
it "raises an error when called at the wrong time" do
|
1180
|
+
expect {
|
1181
|
+
@conn.set_single_row_mode
|
1182
|
+
}.to raise_error(PG::Error)
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
it "should work in single row mode" do
|
1186
|
+
@conn.send_query( "SELECT generate_series(1,10)" )
|
1187
|
+
@conn.set_single_row_mode
|
1188
|
+
|
1189
|
+
results = []
|
1190
|
+
loop do
|
1191
|
+
@conn.block
|
1192
|
+
res = @conn.get_result or break
|
1193
|
+
results << res
|
1083
1194
|
end
|
1195
|
+
expect( results.length ).to eq( 11 )
|
1196
|
+
results[0..-2].each do |res|
|
1197
|
+
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1198
|
+
values = res.field_values('generate_series')
|
1199
|
+
expect( values.length ).to eq( 1 )
|
1200
|
+
expect( values.first.to_i ).to be > 0
|
1201
|
+
end
|
1202
|
+
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1203
|
+
expect( results.last.ntuples ).to eq( 0 )
|
1204
|
+
end
|
1084
1205
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1206
|
+
it "should receive rows before entire query is finished" do
|
1207
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
|
1208
|
+
@conn.set_single_row_mode
|
1088
1209
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
expect( results.length ).to eq( 11 )
|
1096
|
-
results[0..-2].each do |res|
|
1097
|
-
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1098
|
-
values = res.field_values('generate_series')
|
1099
|
-
expect( values.length ).to eq( 1 )
|
1100
|
-
expect( values.first.to_i ).to be > 0
|
1101
|
-
end
|
1102
|
-
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1103
|
-
expect( results.last.ntuples ).to eq( 0 )
|
1210
|
+
start_time = Time.now
|
1211
|
+
first_row_time = nil
|
1212
|
+
loop do
|
1213
|
+
res = @conn.get_result or break
|
1214
|
+
res.check
|
1215
|
+
first_row_time = Time.now unless first_row_time
|
1104
1216
|
end
|
1217
|
+
expect( (Time.now - start_time) ).to be >= 0.9
|
1218
|
+
expect( (first_row_time - start_time) ).to be < 0.9
|
1219
|
+
end
|
1105
1220
|
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1221
|
+
it "should receive rows before entire query fails" do
|
1222
|
+
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1223
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1224
|
+
@conn.set_single_row_mode
|
1109
1225
|
|
1110
|
-
|
1111
|
-
|
1226
|
+
first_result = nil
|
1227
|
+
expect do
|
1112
1228
|
loop do
|
1113
1229
|
res = @conn.get_result or break
|
1114
1230
|
res.check
|
1115
|
-
|
1231
|
+
first_result ||= res
|
1116
1232
|
end
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
it "should receive rows before entire query fails" do
|
1122
|
-
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1123
|
-
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1124
|
-
@conn.set_single_row_mode
|
1125
|
-
|
1126
|
-
first_result = nil
|
1127
|
-
expect do
|
1128
|
-
loop do
|
1129
|
-
res = @conn.get_result or break
|
1130
|
-
res.check
|
1131
|
-
first_result ||= res
|
1132
|
-
end
|
1133
|
-
end.to raise_error(PG::Error)
|
1134
|
-
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1135
|
-
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1136
|
-
end
|
1233
|
+
end.to raise_error(PG::Error)
|
1234
|
+
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1235
|
+
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1137
1236
|
end
|
1237
|
+
|
1138
1238
|
end
|
1139
1239
|
|
1140
|
-
context "multinationalization support"
|
1240
|
+
context "multinationalization support" do
|
1141
1241
|
|
1142
1242
|
describe "rubyforge #22925: m17n support" do
|
1143
1243
|
it "should return results in the same encoding as the client (iso-8859-1)" do
|
1144
|
-
|
1145
|
-
@conn.
|
1146
|
-
|
1147
|
-
res = conn.exec("VALUES ('fantasia')", [], 0)
|
1148
|
-
out_string = res[0]['column1']
|
1149
|
-
end
|
1244
|
+
@conn.internal_encoding = 'iso8859-1'
|
1245
|
+
res = @conn.exec_params("VALUES ('fantasia')", [], 0)
|
1246
|
+
out_string = res[0]['column1']
|
1150
1247
|
expect( out_string ).to eq( 'fantasia' )
|
1151
1248
|
expect( out_string.encoding ).to eq( Encoding::ISO8859_1 )
|
1152
1249
|
end
|
1153
1250
|
|
1154
1251
|
it "should return results in the same encoding as the client (utf-8)" do
|
1155
|
-
|
1156
|
-
@conn.
|
1157
|
-
|
1158
|
-
res = conn.exec("VALUES ('世界線航跡蔵')", [], 0)
|
1159
|
-
out_string = res[0]['column1']
|
1160
|
-
end
|
1252
|
+
@conn.internal_encoding = 'utf-8'
|
1253
|
+
res = @conn.exec_params("VALUES ('世界線航跡蔵')", [], 0)
|
1254
|
+
out_string = res[0]['column1']
|
1161
1255
|
expect( out_string ).to eq( '世界線航跡蔵' )
|
1162
1256
|
expect( out_string.encoding ).to eq( Encoding::UTF_8 )
|
1163
1257
|
end
|
1164
1258
|
|
1165
1259
|
it "should return results in the same encoding as the client (EUC-JP)" do
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
res = conn.exec(stmt, [], 0)
|
1171
|
-
out_string = res[0]['column1']
|
1172
|
-
end
|
1260
|
+
@conn.internal_encoding = 'EUC-JP'
|
1261
|
+
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1262
|
+
res = @conn.exec_params(stmt, [], 0)
|
1263
|
+
out_string = res[0]['column1']
|
1173
1264
|
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
1174
1265
|
expect( out_string.encoding ).to eq( Encoding::EUC_JP )
|
1175
1266
|
end
|
1176
1267
|
|
1177
1268
|
it "returns the results in the correct encoding even if the client_encoding has " +
|
1178
1269
|
"changed since the results were fetched" do
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
conn.internal_encoding = 'utf-8'
|
1185
|
-
out_string = res[0]['column1']
|
1186
|
-
end
|
1270
|
+
@conn.internal_encoding = 'EUC-JP'
|
1271
|
+
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1272
|
+
res = @conn.exec_params(stmt, [], 0)
|
1273
|
+
@conn.internal_encoding = 'utf-8'
|
1274
|
+
out_string = res[0]['column1']
|
1187
1275
|
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
1188
1276
|
expect( out_string.encoding ).to eq( Encoding::EUC_JP )
|
1189
1277
|
end
|
@@ -1193,53 +1281,90 @@ describe PG::Connection do
|
|
1193
1281
|
expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
|
1194
1282
|
end
|
1195
1283
|
|
1284
|
+
it "the connection should use JOHAB dummy encoding when it's set to JOHAB" do
|
1285
|
+
@conn.set_client_encoding "JOHAB"
|
1286
|
+
val = @conn.exec("SELECT chr(x'3391'::int)").values[0][0]
|
1287
|
+
expect( val.encoding.name ).to eq( "JOHAB" )
|
1288
|
+
expect( val.unpack("H*")[0] ).to eq( "dc65" )
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
it "can retrieve server encoding as text" do
|
1292
|
+
enc = @conn.parameter_status "server_encoding"
|
1293
|
+
expect( enc ).to eq( "UTF8" )
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
it "can retrieve server encoding as ruby encoding" do
|
1297
|
+
expect( @conn.external_encoding ).to eq( Encoding::UTF_8 )
|
1298
|
+
end
|
1299
|
+
|
1196
1300
|
it "uses the client encoding for escaped string" do
|
1197
|
-
original = "Möhre to
|
1301
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1198
1302
|
@conn.set_client_encoding( "euc_jp" )
|
1199
1303
|
escaped = @conn.escape( original )
|
1200
1304
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1201
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
|
1305
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::EUC_JP) )
|
1202
1306
|
end
|
1203
1307
|
|
1204
|
-
it "uses the client encoding for escaped literal"
|
1205
|
-
original = "Möhre to
|
1308
|
+
it "uses the client encoding for escaped literal" do
|
1309
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1206
1310
|
@conn.set_client_encoding( "euc_jp" )
|
1207
1311
|
escaped = @conn.escape_literal( original )
|
1208
1312
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1209
|
-
expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
|
1313
|
+
expect( escaped ).to eq( "'Möhre to ''scape'".encode(Encoding::EUC_JP) )
|
1210
1314
|
end
|
1211
1315
|
|
1212
|
-
it "uses the client encoding for escaped identifier"
|
1213
|
-
original = "Möhre to
|
1316
|
+
it "uses the client encoding for escaped identifier" do
|
1317
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1214
1318
|
@conn.set_client_encoding( "euc_jp" )
|
1215
1319
|
escaped = @conn.escape_identifier( original )
|
1216
1320
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1217
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1321
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1218
1322
|
end
|
1219
1323
|
|
1220
1324
|
it "uses the client encoding for quote_ident" do
|
1221
|
-
original = "Möhre to
|
1325
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1222
1326
|
@conn.set_client_encoding( "euc_jp" )
|
1223
1327
|
escaped = @conn.quote_ident( original )
|
1224
1328
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1225
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1329
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1226
1330
|
end
|
1227
1331
|
|
1228
1332
|
it "uses the previous string encoding for escaped string" do
|
1229
|
-
original = "Möhre to
|
1333
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1230
1334
|
@conn.set_client_encoding( "euc_jp" )
|
1231
1335
|
escaped = described_class.escape( original )
|
1232
1336
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1233
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
|
1337
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::ISO8859_1) )
|
1234
1338
|
end
|
1235
1339
|
|
1236
1340
|
it "uses the previous string encoding for quote_ident" do
|
1237
|
-
original = "Möhre to
|
1341
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1238
1342
|
@conn.set_client_encoding( "euc_jp" )
|
1239
1343
|
escaped = described_class.quote_ident( original )
|
1240
1344
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1241
|
-
expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
|
1345
|
+
expect( escaped.encode ).to eq( "\"Möhre to 'scape\"".encode(Encoding::ISO8859_1) )
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
it "raises appropriate error if set_client_encoding is called with invalid arguments" do
|
1349
|
+
expect { @conn.set_client_encoding( "invalid" ) }.to raise_error(PG::Error, /invalid value/)
|
1350
|
+
expect { @conn.set_client_encoding( :invalid ) }.to raise_error(TypeError)
|
1351
|
+
expect { @conn.set_client_encoding( nil ) }.to raise_error(TypeError)
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
it "can use an encoding with high index for client encoding" do
|
1355
|
+
# Allocate a lot of encoding indices, so that MRI's ENCODING_INLINE_MAX is exceeded
|
1356
|
+
unless Encoding.name_list.include?("pgtest-0")
|
1357
|
+
256.times do |eidx|
|
1358
|
+
Encoding::UTF_8.replicate("pgtest-#{eidx}")
|
1359
|
+
end
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
# Now allocate the JOHAB encoding with an unusual high index
|
1363
|
+
@conn.set_client_encoding "JOHAB"
|
1364
|
+
val = @conn.exec("SELECT chr(x'3391'::int)").values[0][0]
|
1365
|
+
expect( val.encoding.name ).to eq( "JOHAB" )
|
1242
1366
|
end
|
1367
|
+
|
1243
1368
|
end
|
1244
1369
|
|
1245
1370
|
describe "respect and convert character encoding of input strings" do
|
@@ -1253,22 +1378,11 @@ describe PG::Connection do
|
|
1253
1378
|
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1254
1379
|
end
|
1255
1380
|
|
1256
|
-
it "should convert query string and parameters to #async_exec" do
|
1257
|
-
r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
|
1258
|
-
['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
|
1259
|
-
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1260
|
-
end
|
1261
|
-
|
1262
1381
|
it "should convert query string to #exec" do
|
1263
1382
|
r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
|
1264
1383
|
expect( r.values ).to eq( [['grün']] )
|
1265
1384
|
end
|
1266
1385
|
|
1267
|
-
it "should convert query string to #async_exec" do
|
1268
|
-
r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
|
1269
|
-
expect( r.values ).to eq( [['grün']] )
|
1270
|
-
end
|
1271
|
-
|
1272
1386
|
it "should convert strings and parameters to #prepare and #exec_prepared" do
|
1273
1387
|
@conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
|
1274
1388
|
r = @conn.exec_prepared("weiß1".encode("utf-32le"),
|
@@ -1293,8 +1407,8 @@ describe PG::Connection do
|
|
1293
1407
|
expect( @conn.get_last_result.values ).to eq( [['grün']] )
|
1294
1408
|
end
|
1295
1409
|
|
1296
|
-
it "should convert query string and parameters to #
|
1297
|
-
@conn.
|
1410
|
+
it "should convert query string and parameters to #send_query_params" do
|
1411
|
+
@conn.send_query_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1298
1412
|
['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
|
1299
1413
|
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1300
1414
|
end
|
@@ -1328,9 +1442,54 @@ describe PG::Connection do
|
|
1328
1442
|
end
|
1329
1443
|
end
|
1330
1444
|
|
1445
|
+
it "rejects command strings with zero bytes" do
|
1446
|
+
expect{ @conn.exec( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1447
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1448
|
+
expect{ @conn.prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1449
|
+
expect{ @conn.prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1450
|
+
expect{ @conn.exec_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1451
|
+
expect{ @conn.describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1452
|
+
expect{ @conn.describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1453
|
+
expect{ @conn.send_query( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1454
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1455
|
+
expect{ @conn.send_prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1456
|
+
expect{ @conn.send_prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1457
|
+
expect{ @conn.send_query_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1458
|
+
expect{ @conn.send_describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1459
|
+
expect{ @conn.send_describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
it "rejects query params with zero bytes" do
|
1463
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1464
|
+
expect{ @conn.exec_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1465
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1466
|
+
expect{ @conn.send_query_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1467
|
+
end
|
1468
|
+
|
1469
|
+
it "rejects string with zero bytes in escape" do
|
1470
|
+
expect{ @conn.escape( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1471
|
+
end
|
1472
|
+
|
1473
|
+
it "rejects string with zero bytes in escape_literal" do
|
1474
|
+
expect{ @conn.escape_literal( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
it "rejects string with zero bytes in escape_identifier" do
|
1478
|
+
expect{ @conn.escape_identifier( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1479
|
+
end
|
1480
|
+
|
1481
|
+
it "rejects string with zero bytes in quote_ident" do
|
1482
|
+
expect{ described_class.quote_ident( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
it "rejects Array with string with zero bytes" do
|
1486
|
+
original = ["xyz", "2\x00"]
|
1487
|
+
expect{ described_class.quote_ident( original ) }.to raise_error(ArgumentError, /null byte/)
|
1488
|
+
end
|
1489
|
+
|
1331
1490
|
it "can quote bigger strings with quote_ident" do
|
1332
1491
|
original = "'01234567\"" * 100
|
1333
|
-
escaped = described_class.quote_ident( original
|
1492
|
+
escaped = described_class.quote_ident( original )
|
1334
1493
|
expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
|
1335
1494
|
end
|
1336
1495
|
|
@@ -1349,7 +1508,7 @@ describe PG::Connection do
|
|
1349
1508
|
|
1350
1509
|
describe "Ruby 1.9.x default_internal encoding" do
|
1351
1510
|
|
1352
|
-
it "honors the Encoding.default_internal if it's set and the synchronous interface is used" do
|
1511
|
+
it "honors the Encoding.default_internal if it's set and the synchronous interface is used", :without_transaction do
|
1353
1512
|
@conn.transaction do |txn_conn|
|
1354
1513
|
txn_conn.internal_encoding = Encoding::ISO8859_1
|
1355
1514
|
txn_conn.exec( "CREATE TABLE defaultinternaltest ( foo text )" )
|
@@ -1408,7 +1567,7 @@ describe PG::Connection do
|
|
1408
1567
|
conn.finish if conn
|
1409
1568
|
end
|
1410
1569
|
|
1411
|
-
it "handles clearing result in or after set_notice_receiver"
|
1570
|
+
it "handles clearing result in or after set_notice_receiver" do
|
1412
1571
|
r = nil
|
1413
1572
|
@conn.set_notice_receiver do |result|
|
1414
1573
|
r = result
|
@@ -1423,7 +1582,7 @@ describe PG::Connection do
|
|
1423
1582
|
@conn.set_notice_receiver
|
1424
1583
|
end
|
1425
1584
|
|
1426
|
-
it "receives properly encoded messages in the notice callbacks"
|
1585
|
+
it "receives properly encoded messages in the notice callbacks" do
|
1427
1586
|
[:receiver, :processor].each do |kind|
|
1428
1587
|
notices = []
|
1429
1588
|
@conn.internal_encoding = 'utf-8'
|
@@ -1451,9 +1610,8 @@ describe PG::Connection do
|
|
1451
1610
|
end
|
1452
1611
|
end
|
1453
1612
|
|
1454
|
-
it "receives properly encoded text from wait_for_notify", :
|
1613
|
+
it "receives properly encoded text from wait_for_notify", :without_transaction do
|
1455
1614
|
@conn.internal_encoding = 'utf-8'
|
1456
|
-
@conn.exec( 'ROLLBACK' )
|
1457
1615
|
@conn.exec( 'LISTEN "Möhre"' )
|
1458
1616
|
@conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
|
1459
1617
|
event, pid, msg = nil
|
@@ -1464,13 +1622,13 @@ describe PG::Connection do
|
|
1464
1622
|
|
1465
1623
|
expect( event ).to eq( "Möhre" )
|
1466
1624
|
expect( event.encoding ).to eq( Encoding::UTF_8 )
|
1625
|
+
expect( pid ).to be_a_kind_of(Integer)
|
1467
1626
|
expect( msg ).to eq( '世界線航跡蔵' )
|
1468
1627
|
expect( msg.encoding ).to eq( Encoding::UTF_8 )
|
1469
1628
|
end
|
1470
1629
|
|
1471
|
-
it "returns properly encoded text from notifies", :
|
1630
|
+
it "returns properly encoded text from notifies", :without_transaction do
|
1472
1631
|
@conn.internal_encoding = 'utf-8'
|
1473
|
-
@conn.exec( 'ROLLBACK' )
|
1474
1632
|
@conn.exec( 'LISTEN "Möhre"' )
|
1475
1633
|
@conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
|
1476
1634
|
@conn.exec( 'UNLISTEN "Möhre"' )
|
@@ -1484,7 +1642,7 @@ describe PG::Connection do
|
|
1484
1642
|
end
|
1485
1643
|
end
|
1486
1644
|
|
1487
|
-
context "OS thread support"
|
1645
|
+
context "OS thread support" do
|
1488
1646
|
it "Connection#exec shouldn't block a second thread" do
|
1489
1647
|
t = Thread.new do
|
1490
1648
|
@conn.exec( "select pg_sleep(1)" )
|
@@ -1524,9 +1682,14 @@ describe PG::Connection do
|
|
1524
1682
|
end
|
1525
1683
|
|
1526
1684
|
it "shouldn't type map params unless requested" do
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1685
|
+
if @conn.server_version < 100000
|
1686
|
+
expect{
|
1687
|
+
@conn.exec_params( "SELECT $1", [5] )
|
1688
|
+
}.to raise_error(PG::IndeterminateDatatype)
|
1689
|
+
else
|
1690
|
+
# PostgreSQL-10 maps to TEXT type (OID 25)
|
1691
|
+
expect( @conn.exec_params( "SELECT $1", [5] ).ftype(0)).to eq(25)
|
1692
|
+
end
|
1530
1693
|
end
|
1531
1694
|
|
1532
1695
|
it "should raise an error on invalid encoder to put_copy_data" do
|
@@ -1540,12 +1703,12 @@ describe PG::Connection do
|
|
1540
1703
|
row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
1541
1704
|
|
1542
1705
|
@conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1543
|
-
|
1706
|
+
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1544
1707
|
@conn.put_copy_data [1], row_encoder
|
1545
1708
|
@conn.put_copy_data ["2"], row_encoder
|
1546
1709
|
end
|
1547
1710
|
|
1548
|
-
|
1711
|
+
@conn.copy_data( "COPY copytable FROM STDOUT", row_encoder ) do |res|
|
1549
1712
|
@conn.put_copy_data [3]
|
1550
1713
|
@conn.put_copy_data ["4"]
|
1551
1714
|
end
|
@@ -1595,7 +1758,7 @@ describe PG::Connection do
|
|
1595
1758
|
|
1596
1759
|
it "can process #copy_data input queries with row encoder and respects character encoding" do
|
1597
1760
|
@conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1598
|
-
|
1761
|
+
@conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1599
1762
|
@conn2.put_copy_data [1]
|
1600
1763
|
@conn2.put_copy_data ["Möhre".encode("utf-16le")]
|
1601
1764
|
end
|
@@ -1646,7 +1809,7 @@ describe PG::Connection do
|
|
1646
1809
|
it "can process #copy_data output with row decoder and respects character encoding" do
|
1647
1810
|
@conn2.internal_encoding = Encoding::ISO8859_1
|
1648
1811
|
rows = []
|
1649
|
-
|
1812
|
+
@conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
|
1650
1813
|
while row=@conn2.get_copy_data
|
1651
1814
|
rows << row
|
1652
1815
|
end
|
@@ -1673,4 +1836,114 @@ describe PG::Connection do
|
|
1673
1836
|
end
|
1674
1837
|
end
|
1675
1838
|
end
|
1839
|
+
|
1840
|
+
describe :field_name_type do
|
1841
|
+
before :each do
|
1842
|
+
@conn2 = PG.connect(@conninfo)
|
1843
|
+
end
|
1844
|
+
after :each do
|
1845
|
+
@conn2.close
|
1846
|
+
end
|
1847
|
+
|
1848
|
+
it "uses string field names per default" do
|
1849
|
+
expect(@conn2.field_name_type).to eq(:string)
|
1850
|
+
end
|
1851
|
+
|
1852
|
+
it "can set string field names" do
|
1853
|
+
@conn2.field_name_type = :string
|
1854
|
+
expect(@conn2.field_name_type).to eq(:string)
|
1855
|
+
res = @conn2.exec("SELECT 1 as az")
|
1856
|
+
expect(res.field_name_type).to eq(:string)
|
1857
|
+
expect(res.fields).to eq(["az"])
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
it "can set symbol field names" do
|
1861
|
+
@conn2.field_name_type = :symbol
|
1862
|
+
expect(@conn2.field_name_type).to eq(:symbol)
|
1863
|
+
res = @conn2.exec("SELECT 1 as az")
|
1864
|
+
expect(res.field_name_type).to eq(:symbol)
|
1865
|
+
expect(res.fields).to eq([:az])
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
it "can't set invalid values" do
|
1869
|
+
expect{ @conn2.field_name_type = :sym }.to raise_error(ArgumentError, /invalid argument :sym/)
|
1870
|
+
expect{ @conn2.field_name_type = "symbol" }.to raise_error(ArgumentError, /invalid argument "symbol"/)
|
1871
|
+
end
|
1872
|
+
end
|
1873
|
+
|
1874
|
+
describe "deprecated forms of methods" do
|
1875
|
+
if PG::VERSION < "2"
|
1876
|
+
it "should forward exec to exec_params" do
|
1877
|
+
res = @conn.exec("VALUES($1::INT)", [7]).values
|
1878
|
+
expect(res).to eq( [["7"]] )
|
1879
|
+
res = @conn.exec("VALUES($1::INT)", [7], 1).values
|
1880
|
+
expect(res).to eq( [[[7].pack("N")]] )
|
1881
|
+
res = @conn.exec("VALUES(8)", [], 1).values
|
1882
|
+
expect(res).to eq( [[[8].pack("N")]] )
|
1883
|
+
end
|
1884
|
+
|
1885
|
+
it "should forward exec_params to exec" do
|
1886
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)").values
|
1887
|
+
expect(res).to eq( [["4"]] )
|
1888
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil).values
|
1889
|
+
expect(res).to eq( [["4"]] )
|
1890
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil).values
|
1891
|
+
expect(res).to eq( [["4"]] )
|
1892
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, 1).values
|
1893
|
+
expect(res).to eq( [["4"]] )
|
1894
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil).values
|
1895
|
+
expect(res).to eq( [["4"]] )
|
1896
|
+
expect{
|
1897
|
+
@conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil, nil).values
|
1898
|
+
}.to raise_error(ArgumentError)
|
1899
|
+
end
|
1900
|
+
|
1901
|
+
it "should forward send_query to send_query_params" do
|
1902
|
+
@conn.send_query("VALUES($1)", [5])
|
1903
|
+
expect(@conn.get_last_result.values).to eq( [["5"]] )
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
it "should respond_to socket", :unix do
|
1907
|
+
expect( @conn.socket ).to eq( @conn.socket_io.fileno )
|
1908
|
+
end
|
1909
|
+
else
|
1910
|
+
# Method forwarding removed by PG::VERSION >= "2"
|
1911
|
+
it "shouldn't forward exec to exec_params" do
|
1912
|
+
expect do
|
1913
|
+
@conn.exec("VALUES($1::INT)", [7])
|
1914
|
+
end.to raise_error(ArgumentError)
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
it "shouldn't forward exec_params to exec" do
|
1918
|
+
expect do
|
1919
|
+
@conn.exec_params("VALUES(3); VALUES(4)")
|
1920
|
+
end.to raise_error(ArgumentError)
|
1921
|
+
end
|
1922
|
+
|
1923
|
+
it "shouldn't forward send_query to send_query_params" do
|
1924
|
+
expect do
|
1925
|
+
@conn.send_query("VALUES($1)", [5])
|
1926
|
+
end.to raise_error(ArgumentError)
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
it "shouldn't forward async_exec_params to async_exec" do
|
1930
|
+
expect do
|
1931
|
+
@conn.async_exec_params("VALUES(1)")
|
1932
|
+
end.to raise_error(ArgumentError)
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
it "shouldn't respond_to socket" do
|
1936
|
+
expect do
|
1937
|
+
@conn.socket
|
1938
|
+
end.to raise_error(ArgumentError)
|
1939
|
+
end
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
it "shouldn't forward send_query_params to send_query" do
|
1943
|
+
expect{ @conn.send_query_params("VALUES(4)").values }
|
1944
|
+
.to raise_error(ArgumentError)
|
1945
|
+
expect{ @conn.send_query_params("VALUES(4)", nil).values }
|
1946
|
+
.to raise_error(TypeError)
|
1947
|
+
end
|
1948
|
+
end
|
1676
1949
|
end
|