pg 1.1.0.pre20180730171000 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +6595 -0
  5. data/History.rdoc +24 -6
  6. data/Manifest.txt +70 -2
  7. data/Rakefile +2 -3
  8. data/Rakefile.cross +3 -3
  9. data/ext/errorcodes.def +4 -0
  10. data/ext/errorcodes.txt +2 -1
  11. data/ext/pg.c +9 -4
  12. data/ext/pg.h +18 -6
  13. data/ext/pg_binary_decoder.c +10 -6
  14. data/ext/pg_binary_encoder.c +1 -1
  15. data/ext/pg_connection.c +31 -29
  16. data/ext/pg_result.c +2 -0
  17. data/ext/pg_text_decoder.c +1 -1
  18. data/ext/pg_text_encoder.c +6 -7
  19. data/ext/pg_tuple.c +4 -3
  20. data/ext/pg_type_map.c +1 -1
  21. data/ext/pg_type_map_all_strings.c +1 -1
  22. data/ext/pg_type_map_by_class.c +1 -1
  23. data/ext/pg_type_map_by_column.c +1 -1
  24. data/ext/pg_type_map_by_mri_type.c +1 -1
  25. data/ext/pg_type_map_by_oid.c +1 -1
  26. data/ext/pg_type_map_in_ruby.c +1 -1
  27. data/ext/util.c +1 -1
  28. data/lib/pg.rb +2 -2
  29. data/spec/helpers.rb +8 -8
  30. data/spec/pg/basic_type_mapping_spec.rb +5 -5
  31. data/spec/pg/connection_spec.rb +131 -17
  32. data/spec/pg/tuple_spec.rb +16 -2
  33. data/spec/pg/type_spec.rb +6 -0
  34. metadata +38 -79
  35. metadata.gz.sig +0 -0
  36. data/.gems +0 -6
  37. data/.hgignore +0 -21
  38. data/.hgsigs +0 -29
  39. data/.hgtags +0 -36
  40. data/.hoerc +0 -2
  41. data/.irbrc +0 -23
  42. data/.pryrc +0 -23
  43. data/.tm_properties +0 -21
  44. data/.travis.yml +0 -41
  45. data/Gemfile +0 -2
  46. data/appveyor.yml +0 -50
  47. data/certs/ged.pem +0 -26
  48. data/misc/openssl-pg-segfault.rb +0 -31
  49. data/misc/postgres/History.txt +0 -9
  50. data/misc/postgres/Manifest.txt +0 -5
  51. data/misc/postgres/README.txt +0 -21
  52. data/misc/postgres/Rakefile +0 -21
  53. data/misc/postgres/lib/postgres.rb +0 -16
  54. data/misc/ruby-pg/History.txt +0 -9
  55. data/misc/ruby-pg/Manifest.txt +0 -5
  56. data/misc/ruby-pg/README.txt +0 -21
  57. data/misc/ruby-pg/Rakefile +0 -21
  58. data/misc/ruby-pg/lib/ruby/pg.rb +0 -16
  59. data/pg.gemspec +0 -61
  60. data/sample/array_insert.rb +0 -20
  61. data/sample/async_api.rb +0 -106
  62. data/sample/async_copyto.rb +0 -39
  63. data/sample/async_mixed.rb +0 -56
  64. data/sample/check_conn.rb +0 -21
  65. data/sample/copydata.rb +0 -71
  66. data/sample/copyfrom.rb +0 -81
  67. data/sample/copyto.rb +0 -19
  68. data/sample/cursor.rb +0 -21
  69. data/sample/disk_usage_report.rb +0 -177
  70. data/sample/issue-119.rb +0 -94
  71. data/sample/losample.rb +0 -69
  72. data/sample/minimal-testcase.rb +0 -17
  73. data/sample/notify_wait.rb +0 -72
  74. data/sample/pg_statistics.rb +0 -285
  75. data/sample/replication_monitor.rb +0 -222
  76. data/sample/test_binary_values.rb +0 -33
  77. data/sample/wal_shipper.rb +0 -434
  78. data/sample/warehouse_partitions.rb +0 -311
@@ -419,6 +419,8 @@ static void pgresult_init_fnames(VALUE self)
419
419
  *
420
420
  * The class to represent the query result tuples (rows).
421
421
  * An instance of this class is created as the result of every query.
422
+ * All result rows and columns are stored in a memory block attached to the PG::Result object.
423
+ * Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
422
424
  *
423
425
  * Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
424
426
  * You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_decoder.c - PG::TextDecoder module
3
- * $Id$
3
+ * $Id: pg_text_decoder.c,v cee615e0ea2c 2018/07/30 05:27:05 lars $
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_text_encoder.c - PG::TextEncoder module
3
- * $Id$
3
+ * $Id: pg_text_encoder.c,v e57f6b452eb3 2018/08/18 10:58:52 lars $
4
4
  *
5
5
  */
6
6
 
@@ -468,20 +468,19 @@ pg_text_enc_array(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
468
468
  static char *
469
469
  quote_identifier( VALUE value, VALUE out_string, char *current_out ){
470
470
  char *p_in = RSTRING_PTR(value);
471
- char *ptr1;
472
471
  size_t strlen = RSTRING_LEN(value);
472
+ char *p_inend = p_in + strlen;
473
473
  char *end_capa = current_out;
474
474
 
475
475
  PG_RB_STR_ENSURE_CAPA( out_string, strlen + 2, current_out, end_capa );
476
476
  *current_out++ = '"';
477
- for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
478
- char c = *ptr1;
477
+ for(; p_in != p_inend; p_in++) {
478
+ char c = *p_in;
479
479
  if (c == '"'){
480
- strlen++;
481
- PG_RB_STR_ENSURE_CAPA( out_string, p_in - ptr1 + strlen + 1, current_out, end_capa );
480
+ PG_RB_STR_ENSURE_CAPA( out_string, p_inend - p_in + 2, current_out, end_capa );
482
481
  *current_out++ = '"';
483
482
  } else if (c == 0){
484
- break;
483
+ rb_raise(rb_eArgError, "string contains null byte");
485
484
  }
486
485
  *current_out++ = c;
487
486
  }
@@ -311,9 +311,9 @@ pg_tuple_yield_key_value(VALUE key, VALUE index, VALUE _this)
311
311
 
312
312
  /*
313
313
  * call-seq:
314
- * tup.each{ |value| ... }
314
+ * tup.each{ |key, value| ... }
315
315
  *
316
- * Invokes block for each field value in the tuple.
316
+ * Invokes block for each field name and value in the tuple.
317
317
  */
318
318
  static VALUE
319
319
  pg_tuple_each(VALUE self)
@@ -354,7 +354,8 @@ pg_tuple_each_value(VALUE self)
354
354
  RETURN_SIZED_ENUMERATOR(self, 0, NULL, pg_tuple_num_fields_for_enum);
355
355
 
356
356
  for(field_num = 0; field_num < this->num_fields; field_num++) {
357
- rb_yield(pg_tuple_aref(self, INT2NUM(field_num)));
357
+ VALUE value = pg_tuple_materialize_field(this, field_num);
358
+ rb_yield(value);
358
359
  }
359
360
 
360
361
  pg_tuple_detach(this);
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id$
3
+ * $Id: pg_type_map.c,v 2af122820861 2017/01/14 19:56:36 lars $
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
3
- * $Id$
3
+ * $Id: pg_type_map_all_strings.c,v c53f993a4254 2014/12/12 21:57:29 lars $
4
4
  *
5
5
  * This is the default typemap.
6
6
  *
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_class.c - PG::TypeMapByClass class extension
3
- * $Id$
3
+ * $Id: pg_type_map_by_class.c,v eeb8a82c5328 2014/11/10 19:34:02 lars $
4
4
  *
5
5
  * This type map can be used to select value encoders based on the class
6
6
  * of the given value to be send.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_column_map.c - PG::ColumnMap class extension
3
- * $Id$
3
+ * $Id: pg_type_map_by_column.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
3
- * $Id$
3
+ * $Id: pg_type_map_by_mri_type.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
4
4
  *
5
5
  * This type map can be used to select value encoders based on the MRI-internal
6
6
  * value type code.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_by_oid.c - PG::TypeMapByOid class extension
3
- * $Id$
3
+ * $Id: pg_type_map_by_oid.c,v c99d26015e3c 2014/12/12 20:58:25 lars $
4
4
  *
5
5
  */
6
6
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
3
- * $Id$
3
+ * $Id: pg_type_map_in_ruby.c,v 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
4
4
  *
5
5
  */
6
6
 
data/ext/util.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * util.c - Utils for ruby-pg
3
- * $Id$
3
+ * $Id: util.c,v fc1c4deb1398 2018/06/25 12:02:09 kanis $
4
4
  *
5
5
  */
6
6
 
data/lib/pg.rb CHANGED
@@ -35,10 +35,10 @@ end
35
35
  module PG
36
36
 
37
37
  # Library version
38
- VERSION = '1.1.0.pre20180730171000'
38
+ VERSION = '1.1.4'
39
39
 
40
40
  # VCS revision
41
- REVISION = %q$Revision$
41
+ REVISION = %q$Revision: 6f611e78845a $
42
42
 
43
43
  class NotAllCopyDataRetrieved < PG::Error
44
44
  end
@@ -22,6 +22,7 @@ module PG::TestingHelpers
22
22
 
23
23
  mod.around( :each ) do |example|
24
24
  begin
25
+ @conn.set_default_encoding
25
26
  @conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
26
27
  desc = example.source_location.join(':')
27
28
  @conn.exec %Q{SET application_name TO '%s'} %
@@ -197,8 +198,8 @@ module PG::TestingHelpers
197
198
  @test_pgdata = TEST_DIRECTORY + 'data'
198
199
  @test_pgdata.mkpath
199
200
 
200
- @port = 54321
201
- ENV['PGPORT'] = @port.to_s
201
+ ENV['PGPORT'] ||= "54321"
202
+ @port = ENV['PGPORT'].to_i
202
203
  ENV['PGHOST'] = 'localhost'
203
204
  @conninfo = "host=localhost port=#{@port} dbname=test"
204
205
 
@@ -318,20 +319,19 @@ module PG::TestingHelpers
318
319
  return ConnStillUsableMatcher.new
319
320
  end
320
321
 
321
- def wait_for_polling_ok(conn)
322
- socket = conn.socket_io
323
- status = conn.connect_poll
322
+ def wait_for_polling_ok(conn, meth = :connect_poll)
323
+ status = conn.send(meth)
324
324
 
325
325
  while status != PG::PGRES_POLLING_OK
326
326
  if status == PG::PGRES_POLLING_READING
327
- select( [socket], [], [], 5.0 ) or
327
+ select( [conn.socket_io], [], [], 5.0 ) or
328
328
  raise "Asynchronous connection timed out!"
329
329
 
330
330
  elsif status == PG::PGRES_POLLING_WRITING
331
- select( [], [socket], [], 5.0 ) or
331
+ select( [], [conn.socket_io], [], 5.0 ) or
332
332
  raise "Asynchronous connection timed out!"
333
333
  end
334
- status = conn.connect_poll
334
+ status = conn.send(meth)
335
335
  end
336
336
  end
337
337
 
@@ -62,7 +62,7 @@ describe 'Basic type mapping' do
62
62
  it "should do bigdecimal param encoding" do
63
63
  large = ('123456790'*10) << '.' << ('012345679')
64
64
  res = @conn.exec_params( "SELECT $1::numeric,$2::numeric",
65
- [BigDecimal.new('1'), BigDecimal.new(large)], nil, basic_type_mapping )
65
+ [BigDecimal('1'), BigDecimal(large)], nil, basic_type_mapping )
66
66
 
67
67
  expect( res.values ).to eq( [
68
68
  [ "1.0", large ],
@@ -256,10 +256,10 @@ describe 'Basic type mapping' do
256
256
  CAST('294276-12-31 23:58:59.1231+03' AS TIMESTAMP WITH TIME ZONE),
257
257
  CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
258
258
  CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
259
- expect( res.getvalue(0,0).iso8601(3) ).to eq( Time.new(2013, 12, 31, 23, 58, 59, "+02:00").getlocal.iso8601(3) )
260
- expect( res.getvalue(0,1).iso8601(3) ).to eq( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").getlocal.iso8601(3) )
261
- expect( res.getvalue(0,2).iso8601(3) ).to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").getlocal.iso8601(3) )
262
- expect( res.getvalue(0,3).iso8601(3) ).to eq( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").getlocal.iso8601(3) )
259
+ expect( res.getvalue(0,0) ).to be_within(1e-3).of( Time.new(2013, 12, 31, 23, 58, 59, "+02:00").getlocal )
260
+ expect( res.getvalue(0,1) ).to be_within(1e-3).of( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").getlocal )
261
+ expect( res.getvalue(0,2) ).to be_within(1e-3).of( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").getlocal )
262
+ expect( res.getvalue(0,3) ).to be_within(1e-3).of( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").getlocal )
263
263
  expect( res.getvalue(0,4) ).to eq( 'infinity' )
264
264
  expect( res.getvalue(0,5) ).to eq( '-infinity' )
265
265
  end
@@ -228,6 +228,37 @@ describe PG::Connection do
228
228
 
229
229
  res = @conn2.query("SELECT 4")
230
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
231
262
  end
232
263
 
233
264
  it "raises proper error when sending fails" do
@@ -248,7 +279,7 @@ describe PG::Connection do
248
279
  expect( @conn.db ).to eq( "test" )
249
280
  expect( @conn.user ).to be_a_kind_of( String )
250
281
  expect( @conn.pass ).to eq( "" )
251
- expect( @conn.port ).to eq( 54321 )
282
+ expect( @conn.port ).to eq( @port )
252
283
  expect( @conn.tty ).to eq( "" )
253
284
  expect( @conn.options ).to eq( "" )
254
285
  end
@@ -734,7 +765,7 @@ describe PG::Connection do
734
765
  it "can return the default connection options as a Hash" do
735
766
  expect( described_class.conndefaults_hash ).to be_a( Hash )
736
767
  expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
737
- expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
768
+ expect( ['5432', '54321', @port.to_s] ).to include( described_class.conndefaults_hash[:port] )
738
769
  expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
739
770
  end
740
771
 
@@ -914,6 +945,20 @@ describe PG::Connection do
914
945
  expect { conn.finish }.to raise_error( PG::ConnectionBad, /connection is closed/i )
915
946
  end
916
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
+
917
962
  it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction, :socket_io do
918
963
  conn = PG.connect( @conninfo )
919
964
  io = conn.socket_io
@@ -939,7 +984,7 @@ describe PG::Connection do
939
984
  end
940
985
  serv.close
941
986
  expect{ conn.block }.to raise_error(PG::ConnectionBad, /server closed the connection unexpectedly/)
942
- expect{ conn.block }.to raise_error(PG::ConnectionBad, /connection not open/)
987
+ expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
943
988
  end
944
989
 
945
990
  it "sets the fallback_application_name on new connections" do
@@ -1110,8 +1155,16 @@ describe PG::Connection do
1110
1155
  expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
1111
1156
  end
1112
1157
 
1113
- it "returns correct response when ping connection arguments are wrong" do
1158
+ it "returns error when ping connection arguments are wrong" do
1114
1159
  ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
1160
+ expect( ping ).to_not eq( PG::PQPING_OK )
1161
+ end
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)
1115
1168
  expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
1116
1169
  end
1117
1170
 
@@ -1236,52 +1289,68 @@ describe PG::Connection do
1236
1289
  expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
1237
1290
  end
1238
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
+
1239
1308
  it "uses the client encoding for escaped string" do
1240
- original = "Möhre to\0 escape".encode( "utf-16be" )
1309
+ original = "Möhre to 'scape".encode( "utf-16be" )
1241
1310
  @conn.set_client_encoding( "euc_jp" )
1242
1311
  escaped = @conn.escape( original )
1243
1312
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1244
- expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1313
+ expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::EUC_JP) )
1245
1314
  end
1246
1315
 
1247
1316
  it "uses the client encoding for escaped literal" do
1248
- original = "Möhre to\0 escape".encode( "utf-16be" )
1317
+ original = "Möhre to 'scape".encode( "utf-16be" )
1249
1318
  @conn.set_client_encoding( "euc_jp" )
1250
1319
  escaped = @conn.escape_literal( original )
1251
1320
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1252
- expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1321
+ expect( escaped ).to eq( "'Möhre to ''scape'".encode(Encoding::EUC_JP) )
1253
1322
  end
1254
1323
 
1255
1324
  it "uses the client encoding for escaped identifier" do
1256
- original = "Möhre to\0 escape".encode( "utf-16le" )
1325
+ original = "Möhre to 'scape".encode( "utf-16le" )
1257
1326
  @conn.set_client_encoding( "euc_jp" )
1258
1327
  escaped = @conn.escape_identifier( original )
1259
1328
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1260
- expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1329
+ expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
1261
1330
  end
1262
1331
 
1263
1332
  it "uses the client encoding for quote_ident" do
1264
- original = "Möhre to\0 escape".encode( "utf-16le" )
1333
+ original = "Möhre to 'scape".encode( "utf-16le" )
1265
1334
  @conn.set_client_encoding( "euc_jp" )
1266
1335
  escaped = @conn.quote_ident( original )
1267
1336
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1268
- expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1337
+ expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
1269
1338
  end
1270
1339
 
1271
1340
  it "uses the previous string encoding for escaped string" do
1272
- original = "Möhre to\0 escape".encode( "iso-8859-1" )
1341
+ original = "Möhre to 'scape".encode( "iso-8859-1" )
1273
1342
  @conn.set_client_encoding( "euc_jp" )
1274
1343
  escaped = described_class.escape( original )
1275
1344
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1276
- expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
1345
+ expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::ISO8859_1) )
1277
1346
  end
1278
1347
 
1279
1348
  it "uses the previous string encoding for quote_ident" do
1280
- original = "Möhre to\0 escape".encode( "iso-8859-1" )
1349
+ original = "Möhre to 'scape".encode( "iso-8859-1" )
1281
1350
  @conn.set_client_encoding( "euc_jp" )
1282
1351
  escaped = described_class.quote_ident( original )
1283
1352
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1284
- 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) )
1285
1354
  end
1286
1355
 
1287
1356
  it "raises appropriate error if set_client_encoding is called with invalid arguments" do
@@ -1366,9 +1435,54 @@ describe PG::Connection do
1366
1435
  end
1367
1436
  end
1368
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
+
1369
1483
  it "can quote bigger strings with quote_ident" do
1370
1484
  original = "'01234567\"" * 100
1371
- escaped = described_class.quote_ident( original + "\0afterzero" )
1485
+ escaped = described_class.quote_ident( original )
1372
1486
  expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
1373
1487
  end
1374
1488