pg 1.1.0.pre20180730171000 → 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.
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