pg 0.21.0 → 1.1.4
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/History.rdoc +98 -0
- data/Manifest.txt +5 -1
- data/README.rdoc +14 -4
- data/Rakefile +4 -5
- data/Rakefile.cross +17 -21
- data/ext/errorcodes.def +12 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +4 -1
- data/ext/extconf.rb +14 -32
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +23 -50
- data/ext/pg.h +51 -81
- data/ext/pg_binary_decoder.c +73 -6
- data/ext/pg_coder.c +52 -3
- data/ext/pg_connection.c +369 -219
- data/ext/pg_copy_coder.c +10 -5
- data/ext/pg_result.c +343 -119
- data/ext/pg_text_decoder.c +597 -37
- data/ext/pg_text_encoder.c +6 -7
- data/ext/pg_tuple.c +541 -0
- data/ext/util.c +6 -6
- data/ext/util.h +2 -2
- data/lib/pg.rb +5 -7
- data/lib/pg/basic_type_mapping.rb +40 -7
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +27 -3
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +1 -1
- data/lib/pg/text_decoder.rb +19 -23
- data/lib/pg/text_encoder.rb +35 -1
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/spec/helpers.rb +49 -21
- data/spec/pg/basic_type_mapping_spec.rb +230 -27
- data/spec/pg/connection_spec.rb +473 -277
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +48 -13
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +1 -1
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- 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 +184 -12
- data/spec/pg_spec.rb +2 -2
- metadata +37 -33
- 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,
|
@@ -163,21 +176,8 @@ describe PG::Connection do
|
|
163
176
|
it "can connect asynchronously", :socket_io 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
|
@@ -188,28 +188,79 @@ describe PG::Connection do
|
|
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", :socket_io 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
|
@@ -282,18 +333,15 @@ describe PG::Connection do
|
|
282
333
|
|
283
334
|
trace_data = trace_file.read
|
284
335
|
|
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
|
336
|
+
# For async_exec the output will be different:
|
337
|
+
# From backend> Z
|
338
|
+
# From backend (#4)> 5
|
339
|
+
# +From backend> Z
|
340
|
+
# +From backend (#4)> 5
|
341
|
+
# From backend> T
|
342
|
+
trace_data.sub!( /(From backend> Z\nFrom backend \(#4\)> 5\n){3}/m, '\\1\\1' )
|
295
343
|
|
296
|
-
expect( trace_data ).to eq(
|
344
|
+
expect( trace_data ).to eq( EXPECTED_TRACE_OUTPUT )
|
297
345
|
end
|
298
346
|
|
299
347
|
it "allows a query to be cancelled" do
|
@@ -388,22 +436,11 @@ describe PG::Connection do
|
|
388
436
|
end
|
389
437
|
end
|
390
438
|
|
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
439
|
it "supports explicitly calling #exec_params" do
|
403
440
|
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
404
|
-
@conn.
|
405
|
-
@conn.
|
406
|
-
@conn.
|
441
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
442
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
443
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
407
444
|
|
408
445
|
res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
|
409
446
|
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
@@ -524,7 +561,7 @@ describe PG::Connection do
|
|
524
561
|
@conn.exec( 'UNLISTEN woo' )
|
525
562
|
end
|
526
563
|
|
527
|
-
it "can receive notices while waiting for NOTIFY without exceeding the timeout"
|
564
|
+
it "can receive notices while waiting for NOTIFY without exceeding the timeout" do
|
528
565
|
notices = []
|
529
566
|
@conn.set_notice_processor do |msg|
|
530
567
|
notices << [msg, Time.now]
|
@@ -586,7 +623,7 @@ describe PG::Connection do
|
|
586
623
|
expect( @conn ).to still_be_usable
|
587
624
|
end
|
588
625
|
|
589
|
-
it "can handle server errors in #copy_data for output"
|
626
|
+
it "can handle server errors in #copy_data for output" do
|
590
627
|
@conn.exec "ROLLBACK"
|
591
628
|
@conn.transaction do
|
592
629
|
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
@@ -718,11 +755,6 @@ describe PG::Connection do
|
|
718
755
|
expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
|
719
756
|
end
|
720
757
|
|
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
758
|
it "can return the default connection options" do
|
727
759
|
expect( described_class.conndefaults ).to be_a( Array )
|
728
760
|
expect( described_class.conndefaults ).to all( be_a(Hash) )
|
@@ -733,7 +765,7 @@ describe PG::Connection do
|
|
733
765
|
it "can return the default connection options as a Hash" do
|
734
766
|
expect( described_class.conndefaults_hash ).to be_a( Hash )
|
735
767
|
expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
|
736
|
-
expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
|
768
|
+
expect( ['5432', '54321', @port.to_s] ).to include( described_class.conndefaults_hash[:port] )
|
737
769
|
expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
|
738
770
|
end
|
739
771
|
|
@@ -779,18 +811,52 @@ describe PG::Connection do
|
|
779
811
|
end
|
780
812
|
end
|
781
813
|
|
814
|
+
describe "deprecated password encryption method" do
|
815
|
+
it "can encrypt password for a given user" do
|
816
|
+
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
817
|
+
end
|
782
818
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
819
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
820
|
+
expect {
|
821
|
+
described_class.encrypt_password( nil, nil )
|
822
|
+
}.to raise_error( TypeError )
|
823
|
+
expect {
|
824
|
+
described_class.encrypt_password( "postgres", nil )
|
825
|
+
}.to raise_error( TypeError )
|
826
|
+
expect {
|
827
|
+
described_class.encrypt_password( nil, "postgres" )
|
828
|
+
}.to raise_error( TypeError )
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
describe "password encryption method", :postgresql_10 do
|
833
|
+
it "can encrypt without algorithm" do
|
834
|
+
expect( @conn.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
835
|
+
expect( @conn.encrypt_password("postgres", "postgres", nil) ).to match( /\S+/ )
|
836
|
+
end
|
837
|
+
|
838
|
+
it "can encrypt with algorithm" do
|
839
|
+
expect( @conn.encrypt_password("postgres", "postgres", "md5") ).to match( /md5\S+/i )
|
840
|
+
expect( @conn.encrypt_password("postgres", "postgres", "scram-sha-256") ).to match( /SCRAM-SHA-256\S+/i )
|
841
|
+
end
|
842
|
+
|
843
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
844
|
+
expect {
|
845
|
+
@conn.encrypt_password( nil, nil )
|
846
|
+
}.to raise_error( TypeError )
|
847
|
+
expect {
|
848
|
+
@conn.encrypt_password( "postgres", nil )
|
849
|
+
}.to raise_error( TypeError )
|
850
|
+
expect {
|
851
|
+
@conn.encrypt_password( nil, "postgres" )
|
852
|
+
}.to raise_error( TypeError )
|
853
|
+
expect {
|
854
|
+
@conn.encrypt_password( "postgres", "postgres", :invalid )
|
855
|
+
}.to raise_error( TypeError )
|
856
|
+
expect {
|
857
|
+
@conn.encrypt_password( "postgres", "postgres", "invalid" )
|
858
|
+
}.to raise_error( PG::Error, /unrecognized/ )
|
859
|
+
end
|
794
860
|
end
|
795
861
|
|
796
862
|
|
@@ -832,7 +898,7 @@ describe PG::Connection do
|
|
832
898
|
end
|
833
899
|
|
834
900
|
|
835
|
-
it "
|
901
|
+
it "handles server close while asynchronous connect", :socket_io do
|
836
902
|
serv = TCPServer.new( '127.0.0.1', 54320 )
|
837
903
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
838
904
|
expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
|
@@ -844,11 +910,29 @@ describe PG::Connection do
|
|
844
910
|
expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
|
845
911
|
end
|
846
912
|
|
847
|
-
it "discards previous results
|
913
|
+
it "discards previous results at #discard_results" do
|
914
|
+
@conn.send_query( "select 1" )
|
915
|
+
@conn.discard_results
|
916
|
+
@conn.send_query( "select 41 as one" )
|
917
|
+
res = @conn.get_last_result
|
918
|
+
expect( res.to_a ).to eq( [{ 'one' => '41' }] )
|
919
|
+
end
|
920
|
+
|
921
|
+
it "discards previous results (if any) before waiting on #exec" do
|
922
|
+
@conn.send_query( "select 1" )
|
923
|
+
res = @conn.exec( "select 42 as one" )
|
924
|
+
expect( res.to_a ).to eq( [{ 'one' => '42' }] )
|
925
|
+
end
|
848
926
|
|
849
|
-
it "
|
927
|
+
it "discards previous errors before waiting on #exec", :without_transaction do
|
928
|
+
@conn.send_query( "ERROR" )
|
929
|
+
res = @conn.exec( "select 43 as one" )
|
930
|
+
expect( res.to_a ).to eq( [{ 'one' => '43' }] )
|
931
|
+
end
|
932
|
+
|
933
|
+
it "calls the block if one is provided to #exec" do
|
850
934
|
result = nil
|
851
|
-
@conn.
|
935
|
+
@conn.exec( "select 47 as one" ) do |pg_res|
|
852
936
|
result = pg_res[0]
|
853
937
|
end
|
854
938
|
expect( result ).to eq( { 'one' => '47' } )
|
@@ -861,6 +945,20 @@ describe PG::Connection do
|
|
861
945
|
expect { conn.finish }.to raise_error( PG::ConnectionBad, /connection is closed/i )
|
862
946
|
end
|
863
947
|
|
948
|
+
it "can use conn.reset to restart the connection" do
|
949
|
+
ios = IO.pipe
|
950
|
+
conn = PG.connect( @conninfo )
|
951
|
+
|
952
|
+
# Close the two pipe file descriptors, so that the file descriptor of
|
953
|
+
# newly established connection is probably distinct from the previous one.
|
954
|
+
ios.each(&:close)
|
955
|
+
conn.reset
|
956
|
+
|
957
|
+
# The new connection should work even when the file descriptor has changed.
|
958
|
+
expect( conn.exec("SELECT 1").values ).to eq([["1"]])
|
959
|
+
conn.close
|
960
|
+
end
|
961
|
+
|
864
962
|
it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction, :socket_io do
|
865
963
|
conn = PG.connect( @conninfo )
|
866
964
|
io = conn.socket_io
|
@@ -889,155 +987,147 @@ describe PG::Connection do
|
|
889
987
|
expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
|
890
988
|
end
|
891
989
|
|
892
|
-
|
990
|
+
it "sets the fallback_application_name on new connections" do
|
991
|
+
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
893
992
|
|
894
|
-
|
895
|
-
|
896
|
-
|
993
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
994
|
+
expect( conn_name ).to include( $0[0..10] )
|
995
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
996
|
+
expect( conn_name.length ).to be <= 64
|
997
|
+
end
|
897
998
|
|
898
|
-
|
999
|
+
it "sets a shortened fallback_application_name on new connections" do
|
1000
|
+
old_0 = $0
|
1001
|
+
begin
|
1002
|
+
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
899
1003
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
900
|
-
|
901
1004
|
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
902
1005
|
expect( conn_name ).to include( $0[0..10] )
|
903
1006
|
expect( conn_name ).to include( $0[-10..-1] )
|
904
1007
|
expect( conn_name.length ).to be <= 64
|
1008
|
+
ensure
|
1009
|
+
$0 = old_0
|
905
1010
|
end
|
1011
|
+
end
|
906
1012
|
|
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' )
|
1013
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1014
|
+
"any number of arguments" do
|
926
1015
|
|
927
|
-
|
928
|
-
|
929
|
-
conn.finish
|
1016
|
+
@conn.exec( 'ROLLBACK' )
|
1017
|
+
@conn.exec( 'LISTEN knees' )
|
930
1018
|
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
end
|
935
|
-
@conn.exec( 'UNLISTEN knees' )
|
1019
|
+
conn = described_class.connect( @conninfo )
|
1020
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1021
|
+
conn.finish
|
936
1022
|
|
937
|
-
|
938
|
-
|
939
|
-
|
1023
|
+
event, pid, msg = nil
|
1024
|
+
@conn.wait_for_notify( 10 ) do |*args|
|
1025
|
+
event, pid, msg = *args
|
940
1026
|
end
|
1027
|
+
@conn.exec( 'UNLISTEN knees' )
|
941
1028
|
|
942
|
-
|
943
|
-
|
944
|
-
|
1029
|
+
expect( event ).to eq( 'knees' )
|
1030
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1031
|
+
expect( msg ).to eq( 'skirt and boots' )
|
1032
|
+
end
|
945
1033
|
|
946
|
-
|
947
|
-
|
948
|
-
|
1034
|
+
it "accepts nil as the timeout in #wait_for_notify " do
|
1035
|
+
@conn.exec( 'ROLLBACK' )
|
1036
|
+
@conn.exec( 'LISTEN knees' )
|
949
1037
|
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
end
|
954
|
-
@conn.exec( 'UNLISTEN knees' )
|
1038
|
+
conn = described_class.connect( @conninfo )
|
1039
|
+
conn.exec( %Q{NOTIFY knees} )
|
1040
|
+
conn.finish
|
955
1041
|
|
956
|
-
|
957
|
-
|
1042
|
+
event, pid = nil
|
1043
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1044
|
+
event, pid = *args
|
958
1045
|
end
|
1046
|
+
@conn.exec( 'UNLISTEN knees' )
|
959
1047
|
|
960
|
-
|
961
|
-
|
962
|
-
|
1048
|
+
expect( event ).to eq( 'knees' )
|
1049
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1050
|
+
end
|
963
1051
|
|
964
|
-
|
965
|
-
|
966
|
-
|
1052
|
+
it "sends nil as the payload if the notification wasn't given one" do
|
1053
|
+
@conn.exec( 'ROLLBACK' )
|
1054
|
+
@conn.exec( 'LISTEN knees' )
|
967
1055
|
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
end
|
972
|
-
@conn.exec( 'UNLISTEN knees' )
|
1056
|
+
conn = described_class.connect( @conninfo )
|
1057
|
+
conn.exec( %Q{NOTIFY knees} )
|
1058
|
+
conn.finish
|
973
1059
|
|
974
|
-
|
1060
|
+
payload = :notnil
|
1061
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1062
|
+
payload = args[ 2 ]
|
975
1063
|
end
|
1064
|
+
@conn.exec( 'UNLISTEN knees' )
|
976
1065
|
|
977
|
-
|
978
|
-
|
1066
|
+
expect( payload ).to be_nil()
|
1067
|
+
end
|
979
1068
|
|
980
|
-
|
981
|
-
|
1069
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1070
|
+
"two arguments" do
|
982
1071
|
|
983
|
-
|
984
|
-
|
985
|
-
conn.finish
|
1072
|
+
@conn.exec( 'ROLLBACK' )
|
1073
|
+
@conn.exec( 'LISTEN knees' )
|
986
1074
|
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
end
|
991
|
-
@conn.exec( 'UNLISTEN knees' )
|
1075
|
+
conn = described_class.connect( @conninfo )
|
1076
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1077
|
+
conn.finish
|
992
1078
|
|
993
|
-
|
994
|
-
|
995
|
-
|
1079
|
+
event, pid, msg = nil
|
1080
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2|
|
1081
|
+
event, pid, msg = arg1, arg2
|
996
1082
|
end
|
1083
|
+
@conn.exec( 'UNLISTEN knees' )
|
997
1084
|
|
998
|
-
|
999
|
-
|
1085
|
+
expect( event ).to eq( 'knees' )
|
1086
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1087
|
+
expect( msg ).to be_nil()
|
1088
|
+
end
|
1000
1089
|
|
1001
|
-
|
1002
|
-
|
1090
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it " +
|
1091
|
+
"doesn't accept arguments" do
|
1003
1092
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
conn.finish
|
1093
|
+
@conn.exec( 'ROLLBACK' )
|
1094
|
+
@conn.exec( 'LISTEN knees' )
|
1007
1095
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
end
|
1012
|
-
@conn.exec( 'UNLISTEN knees' )
|
1096
|
+
conn = described_class.connect( @conninfo )
|
1097
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1098
|
+
conn.finish
|
1013
1099
|
|
1014
|
-
|
1100
|
+
notification_received = false
|
1101
|
+
@conn.wait_for_notify( 10 ) do
|
1102
|
+
notification_received = true
|
1015
1103
|
end
|
1104
|
+
@conn.exec( 'UNLISTEN knees' )
|
1016
1105
|
|
1017
|
-
|
1018
|
-
|
1106
|
+
expect( notification_received ).to be_truthy()
|
1107
|
+
end
|
1019
1108
|
|
1020
|
-
|
1021
|
-
|
1109
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1110
|
+
"three arguments" do
|
1022
1111
|
|
1023
|
-
|
1024
|
-
|
1025
|
-
conn.finish
|
1112
|
+
@conn.exec( 'ROLLBACK' )
|
1113
|
+
@conn.exec( 'LISTEN knees' )
|
1026
1114
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
end
|
1031
|
-
@conn.exec( 'UNLISTEN knees' )
|
1115
|
+
conn = described_class.connect( @conninfo )
|
1116
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1117
|
+
conn.finish
|
1032
1118
|
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1119
|
+
event, pid, msg = nil
|
1120
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
|
1121
|
+
event, pid, msg = arg1, arg2, arg3
|
1036
1122
|
end
|
1123
|
+
@conn.exec( 'UNLISTEN knees' )
|
1037
1124
|
|
1125
|
+
expect( event ).to eq( 'knees' )
|
1126
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1127
|
+
expect( msg ).to eq( 'skirt and boots' )
|
1038
1128
|
end
|
1039
1129
|
|
1040
|
-
context "
|
1130
|
+
context "server ping", :without_transaction do
|
1041
1131
|
|
1042
1132
|
it "pings successfully with connection string" do
|
1043
1133
|
ping = described_class.ping(@conninfo)
|
@@ -1065,76 +1155,82 @@ describe PG::Connection do
|
|
1065
1155
|
expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
|
1066
1156
|
end
|
1067
1157
|
|
1068
|
-
it "returns
|
1158
|
+
it "returns error when ping connection arguments are wrong" do
|
1069
1159
|
ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
|
1070
|
-
expect( ping ).
|
1160
|
+
expect( ping ).to_not eq( PG::PQPING_OK )
|
1071
1161
|
end
|
1072
1162
|
|
1163
|
+
it "returns correct response when ping connection arguments are wrong" do
|
1164
|
+
ping = described_class.ping(
|
1165
|
+
:host => 'localhost',
|
1166
|
+
:invalid_option => 9999,
|
1167
|
+
:dbname => :test)
|
1168
|
+
expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
|
1169
|
+
end
|
1073
1170
|
|
1074
1171
|
end
|
1075
1172
|
|
1076
|
-
|
1077
|
-
describe "set_single_row_mode" do
|
1173
|
+
describe "set_single_row_mode" do
|
1078
1174
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1175
|
+
it "raises an error when called at the wrong time" do
|
1176
|
+
expect {
|
1177
|
+
@conn.set_single_row_mode
|
1178
|
+
}.to raise_error(PG::Error)
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
it "should work in single row mode" do
|
1182
|
+
@conn.send_query( "SELECT generate_series(1,10)" )
|
1183
|
+
@conn.set_single_row_mode
|
1184
|
+
|
1185
|
+
results = []
|
1186
|
+
loop do
|
1187
|
+
@conn.block
|
1188
|
+
res = @conn.get_result or break
|
1189
|
+
results << res
|
1190
|
+
end
|
1191
|
+
expect( results.length ).to eq( 11 )
|
1192
|
+
results[0..-2].each do |res|
|
1193
|
+
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1194
|
+
values = res.field_values('generate_series')
|
1195
|
+
expect( values.length ).to eq( 1 )
|
1196
|
+
expect( values.first.to_i ).to be > 0
|
1083
1197
|
end
|
1198
|
+
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1199
|
+
expect( results.last.ntuples ).to eq( 0 )
|
1200
|
+
end
|
1084
1201
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1202
|
+
it "should receive rows before entire query is finished" do
|
1203
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
|
1204
|
+
@conn.set_single_row_mode
|
1088
1205
|
|
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 )
|
1206
|
+
start_time = Time.now
|
1207
|
+
first_row_time = nil
|
1208
|
+
loop do
|
1209
|
+
res = @conn.get_result or break
|
1210
|
+
res.check
|
1211
|
+
first_row_time = Time.now unless first_row_time
|
1104
1212
|
end
|
1213
|
+
expect( (Time.now - start_time) ).to be >= 0.9
|
1214
|
+
expect( (first_row_time - start_time) ).to be < 0.9
|
1215
|
+
end
|
1105
1216
|
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1217
|
+
it "should receive rows before entire query fails" do
|
1218
|
+
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1219
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1220
|
+
@conn.set_single_row_mode
|
1109
1221
|
|
1110
|
-
|
1111
|
-
|
1222
|
+
first_result = nil
|
1223
|
+
expect do
|
1112
1224
|
loop do
|
1113
1225
|
res = @conn.get_result or break
|
1114
1226
|
res.check
|
1115
|
-
|
1227
|
+
first_result ||= res
|
1116
1228
|
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
|
1229
|
+
end.to raise_error(PG::Error)
|
1230
|
+
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1231
|
+
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1137
1232
|
end
|
1233
|
+
|
1138
1234
|
end
|
1139
1235
|
|
1140
1236
|
context "multinationalization support", :ruby_19 do
|
@@ -1144,7 +1240,7 @@ describe PG::Connection do
|
|
1144
1240
|
out_string = nil
|
1145
1241
|
@conn.transaction do |conn|
|
1146
1242
|
conn.internal_encoding = 'iso8859-1'
|
1147
|
-
res = conn.
|
1243
|
+
res = conn.exec_params("VALUES ('fantasia')", [], 0)
|
1148
1244
|
out_string = res[0]['column1']
|
1149
1245
|
end
|
1150
1246
|
expect( out_string ).to eq( 'fantasia' )
|
@@ -1155,7 +1251,7 @@ describe PG::Connection do
|
|
1155
1251
|
out_string = nil
|
1156
1252
|
@conn.transaction do |conn|
|
1157
1253
|
conn.internal_encoding = 'utf-8'
|
1158
|
-
res = conn.
|
1254
|
+
res = conn.exec_params("VALUES ('世界線航跡蔵')", [], 0)
|
1159
1255
|
out_string = res[0]['column1']
|
1160
1256
|
end
|
1161
1257
|
expect( out_string ).to eq( '世界線航跡蔵' )
|
@@ -1167,7 +1263,7 @@ describe PG::Connection do
|
|
1167
1263
|
@conn.transaction do |conn|
|
1168
1264
|
conn.internal_encoding = 'EUC-JP'
|
1169
1265
|
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1170
|
-
res = conn.
|
1266
|
+
res = conn.exec_params(stmt, [], 0)
|
1171
1267
|
out_string = res[0]['column1']
|
1172
1268
|
end
|
1173
1269
|
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
@@ -1180,7 +1276,7 @@ describe PG::Connection do
|
|
1180
1276
|
@conn.transaction do |conn|
|
1181
1277
|
conn.internal_encoding = 'EUC-JP'
|
1182
1278
|
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1183
|
-
res = conn.
|
1279
|
+
res = conn.exec_params(stmt, [], 0)
|
1184
1280
|
conn.internal_encoding = 'utf-8'
|
1185
1281
|
out_string = res[0]['column1']
|
1186
1282
|
end
|
@@ -1193,52 +1289,74 @@ describe PG::Connection do
|
|
1193
1289
|
expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
|
1194
1290
|
end
|
1195
1291
|
|
1292
|
+
it "the connection should use JOHAB dummy encoding when it's set to JOHAB" do
|
1293
|
+
@conn.set_client_encoding "JOHAB"
|
1294
|
+
val = @conn.exec("SELECT chr(x'3391'::int)").values[0][0]
|
1295
|
+
expect( val.encoding.name ).to eq( "JOHAB" )
|
1296
|
+
expect( val.unpack("H*")[0] ).to eq( "dc65" )
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
it "can retrieve server encoding as text" do
|
1300
|
+
enc = @conn.parameter_status "server_encoding"
|
1301
|
+
expect( enc ).to eq( "UTF8" )
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
it "can retrieve server encoding as ruby encoding" do
|
1305
|
+
expect( @conn.external_encoding ).to eq( Encoding::UTF_8 )
|
1306
|
+
end
|
1307
|
+
|
1196
1308
|
it "uses the client encoding for escaped string" do
|
1197
|
-
original = "Möhre to
|
1309
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1198
1310
|
@conn.set_client_encoding( "euc_jp" )
|
1199
1311
|
escaped = @conn.escape( original )
|
1200
1312
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1201
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
|
1313
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::EUC_JP) )
|
1202
1314
|
end
|
1203
1315
|
|
1204
|
-
it "uses the client encoding for escaped literal"
|
1205
|
-
original = "Möhre to
|
1316
|
+
it "uses the client encoding for escaped literal" do
|
1317
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1206
1318
|
@conn.set_client_encoding( "euc_jp" )
|
1207
1319
|
escaped = @conn.escape_literal( original )
|
1208
1320
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1209
|
-
expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
|
1321
|
+
expect( escaped ).to eq( "'Möhre to ''scape'".encode(Encoding::EUC_JP) )
|
1210
1322
|
end
|
1211
1323
|
|
1212
|
-
it "uses the client encoding for escaped identifier"
|
1213
|
-
original = "Möhre to
|
1324
|
+
it "uses the client encoding for escaped identifier" do
|
1325
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1214
1326
|
@conn.set_client_encoding( "euc_jp" )
|
1215
1327
|
escaped = @conn.escape_identifier( original )
|
1216
1328
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1217
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1329
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1218
1330
|
end
|
1219
1331
|
|
1220
1332
|
it "uses the client encoding for quote_ident" do
|
1221
|
-
original = "Möhre to
|
1333
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1222
1334
|
@conn.set_client_encoding( "euc_jp" )
|
1223
1335
|
escaped = @conn.quote_ident( original )
|
1224
1336
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1225
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1337
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1226
1338
|
end
|
1227
1339
|
|
1228
1340
|
it "uses the previous string encoding for escaped string" do
|
1229
|
-
original = "Möhre to
|
1341
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1230
1342
|
@conn.set_client_encoding( "euc_jp" )
|
1231
1343
|
escaped = described_class.escape( original )
|
1232
1344
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1233
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
|
1345
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::ISO8859_1) )
|
1234
1346
|
end
|
1235
1347
|
|
1236
1348
|
it "uses the previous string encoding for quote_ident" do
|
1237
|
-
original = "Möhre to
|
1349
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1238
1350
|
@conn.set_client_encoding( "euc_jp" )
|
1239
1351
|
escaped = described_class.quote_ident( original )
|
1240
1352
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1241
|
-
expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
|
1353
|
+
expect( escaped.encode ).to eq( "\"Möhre to 'scape\"".encode(Encoding::ISO8859_1) )
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
it "raises appropriate error if set_client_encoding is called with invalid arguments" do
|
1357
|
+
expect { @conn.set_client_encoding( "invalid" ) }.to raise_error(PG::Error, /invalid value/)
|
1358
|
+
expect { @conn.set_client_encoding( :invalid ) }.to raise_error(TypeError)
|
1359
|
+
expect { @conn.set_client_encoding( nil ) }.to raise_error(TypeError)
|
1242
1360
|
end
|
1243
1361
|
end
|
1244
1362
|
|
@@ -1253,22 +1371,11 @@ describe PG::Connection do
|
|
1253
1371
|
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1254
1372
|
end
|
1255
1373
|
|
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
1374
|
it "should convert query string to #exec" do
|
1263
1375
|
r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
|
1264
1376
|
expect( r.values ).to eq( [['grün']] )
|
1265
1377
|
end
|
1266
1378
|
|
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
1379
|
it "should convert strings and parameters to #prepare and #exec_prepared" do
|
1273
1380
|
@conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
|
1274
1381
|
r = @conn.exec_prepared("weiß1".encode("utf-32le"),
|
@@ -1293,8 +1400,8 @@ describe PG::Connection do
|
|
1293
1400
|
expect( @conn.get_last_result.values ).to eq( [['grün']] )
|
1294
1401
|
end
|
1295
1402
|
|
1296
|
-
it "should convert query string and parameters to #
|
1297
|
-
@conn.
|
1403
|
+
it "should convert query string and parameters to #send_query_params" do
|
1404
|
+
@conn.send_query_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1298
1405
|
['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
|
1299
1406
|
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1300
1407
|
end
|
@@ -1328,9 +1435,54 @@ describe PG::Connection do
|
|
1328
1435
|
end
|
1329
1436
|
end
|
1330
1437
|
|
1438
|
+
it "rejects command strings with zero bytes" do
|
1439
|
+
expect{ @conn.exec( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1440
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1441
|
+
expect{ @conn.prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1442
|
+
expect{ @conn.prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1443
|
+
expect{ @conn.exec_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1444
|
+
expect{ @conn.describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1445
|
+
expect{ @conn.describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1446
|
+
expect{ @conn.send_query( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1447
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1448
|
+
expect{ @conn.send_prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1449
|
+
expect{ @conn.send_prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1450
|
+
expect{ @conn.send_query_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1451
|
+
expect{ @conn.send_describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1452
|
+
expect{ @conn.send_describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
it "rejects query params with zero bytes" do
|
1456
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1457
|
+
expect{ @conn.exec_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1458
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1459
|
+
expect{ @conn.send_query_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
it "rejects string with zero bytes in escape" do
|
1463
|
+
expect{ @conn.escape( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
it "rejects string with zero bytes in escape_literal" do
|
1467
|
+
expect{ @conn.escape_literal( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
it "rejects string with zero bytes in escape_identifier" do
|
1471
|
+
expect{ @conn.escape_identifier( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
it "rejects string with zero bytes in quote_ident" do
|
1475
|
+
expect{ described_class.quote_ident( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
it "rejects Array with string with zero bytes" do
|
1479
|
+
original = ["xyz", "2\x00"]
|
1480
|
+
expect{ described_class.quote_ident( original ) }.to raise_error(ArgumentError, /null byte/)
|
1481
|
+
end
|
1482
|
+
|
1331
1483
|
it "can quote bigger strings with quote_ident" do
|
1332
1484
|
original = "'01234567\"" * 100
|
1333
|
-
escaped = described_class.quote_ident( original
|
1485
|
+
escaped = described_class.quote_ident( original )
|
1334
1486
|
expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
|
1335
1487
|
end
|
1336
1488
|
|
@@ -1408,7 +1560,7 @@ describe PG::Connection do
|
|
1408
1560
|
conn.finish if conn
|
1409
1561
|
end
|
1410
1562
|
|
1411
|
-
it "handles clearing result in or after set_notice_receiver"
|
1563
|
+
it "handles clearing result in or after set_notice_receiver" do
|
1412
1564
|
r = nil
|
1413
1565
|
@conn.set_notice_receiver do |result|
|
1414
1566
|
r = result
|
@@ -1423,7 +1575,7 @@ describe PG::Connection do
|
|
1423
1575
|
@conn.set_notice_receiver
|
1424
1576
|
end
|
1425
1577
|
|
1426
|
-
it "receives properly encoded messages in the notice callbacks"
|
1578
|
+
it "receives properly encoded messages in the notice callbacks" do
|
1427
1579
|
[:receiver, :processor].each do |kind|
|
1428
1580
|
notices = []
|
1429
1581
|
@conn.internal_encoding = 'utf-8'
|
@@ -1451,7 +1603,7 @@ describe PG::Connection do
|
|
1451
1603
|
end
|
1452
1604
|
end
|
1453
1605
|
|
1454
|
-
it "receives properly encoded text from wait_for_notify"
|
1606
|
+
it "receives properly encoded text from wait_for_notify" do
|
1455
1607
|
@conn.internal_encoding = 'utf-8'
|
1456
1608
|
@conn.exec( 'ROLLBACK' )
|
1457
1609
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1468,7 +1620,7 @@ describe PG::Connection do
|
|
1468
1620
|
expect( msg.encoding ).to eq( Encoding::UTF_8 )
|
1469
1621
|
end
|
1470
1622
|
|
1471
|
-
it "returns properly encoded text from notifies"
|
1623
|
+
it "returns properly encoded text from notifies" do
|
1472
1624
|
@conn.internal_encoding = 'utf-8'
|
1473
1625
|
@conn.exec( 'ROLLBACK' )
|
1474
1626
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1524,9 +1676,14 @@ describe PG::Connection do
|
|
1524
1676
|
end
|
1525
1677
|
|
1526
1678
|
it "shouldn't type map params unless requested" do
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1679
|
+
if @conn.server_version < 100000
|
1680
|
+
expect{
|
1681
|
+
@conn.exec_params( "SELECT $1", [5] )
|
1682
|
+
}.to raise_error(PG::IndeterminateDatatype)
|
1683
|
+
else
|
1684
|
+
# PostgreSQL-10 maps to TEXT type (OID 25)
|
1685
|
+
expect( @conn.exec_params( "SELECT $1", [5] ).ftype(0)).to eq(25)
|
1686
|
+
end
|
1530
1687
|
end
|
1531
1688
|
|
1532
1689
|
it "should raise an error on invalid encoder to put_copy_data" do
|
@@ -1673,4 +1830,43 @@ describe PG::Connection do
|
|
1673
1830
|
end
|
1674
1831
|
end
|
1675
1832
|
end
|
1833
|
+
|
1834
|
+
describe "deprecated forms of methods" do
|
1835
|
+
it "should forward exec to exec_params" do
|
1836
|
+
res = @conn.exec("VALUES($1::INT)", [7]).values
|
1837
|
+
expect(res).to eq( [["7"]] )
|
1838
|
+
res = @conn.exec("VALUES($1::INT)", [7], 1).values
|
1839
|
+
expect(res).to eq( [[[7].pack("N")]] )
|
1840
|
+
res = @conn.exec("VALUES(8)", [], 1).values
|
1841
|
+
expect(res).to eq( [[[8].pack("N")]] )
|
1842
|
+
end
|
1843
|
+
|
1844
|
+
it "should forward exec_params to exec" do
|
1845
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)").values
|
1846
|
+
expect(res).to eq( [["4"]] )
|
1847
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil).values
|
1848
|
+
expect(res).to eq( [["4"]] )
|
1849
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil).values
|
1850
|
+
expect(res).to eq( [["4"]] )
|
1851
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, 1).values
|
1852
|
+
expect(res).to eq( [["4"]] )
|
1853
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil).values
|
1854
|
+
expect(res).to eq( [["4"]] )
|
1855
|
+
expect{
|
1856
|
+
@conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil, nil).values
|
1857
|
+
}.to raise_error(ArgumentError)
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
it "should forward send_query to send_query_params" do
|
1861
|
+
@conn.send_query("VALUES($1)", [5])
|
1862
|
+
expect(@conn.get_last_result.values).to eq( [["5"]] )
|
1863
|
+
end
|
1864
|
+
|
1865
|
+
it "shouldn't forward send_query_params to send_query" do
|
1866
|
+
expect{ @conn.send_query_params("VALUES(4)").values }
|
1867
|
+
.to raise_error(ArgumentError)
|
1868
|
+
expect{ @conn.send_query_params("VALUES(4)", nil).values }
|
1869
|
+
.to raise_error(TypeError)
|
1870
|
+
end
|
1871
|
+
end
|
1676
1872
|
end
|