pg 0.15.1-x64-mingw32 → 0.16.0-x64-mingw32

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.
@@ -0,0 +1,89 @@
1
+ /*
2
+ * pg_errors.c - Definition and lookup of error classes.
3
+ *
4
+ */
5
+
6
+ #include "pg.h"
7
+
8
+ VALUE rb_hErrors;
9
+ VALUE rb_ePGerror;
10
+ VALUE rb_eServerError;
11
+ VALUE rb_eUnableToSend;
12
+ VALUE rb_eConnectionBad;
13
+
14
+ static VALUE
15
+ define_error_class(const char *name, const char *baseclass_code)
16
+ {
17
+ VALUE baseclass = rb_eServerError;
18
+ if(baseclass_code)
19
+ {
20
+ baseclass = rb_hash_aref( rb_hErrors, rb_str_new2(baseclass_code) );
21
+ }
22
+ return rb_define_class_under( rb_mPG, name, baseclass );
23
+ }
24
+
25
+ static void
26
+ register_error_class(const char *code, VALUE klass)
27
+ {
28
+ rb_hash_aset( rb_hErrors, rb_str_new2(code), klass );
29
+ }
30
+
31
+ /* Find a proper error class for the given SQLSTATE string
32
+ */
33
+ VALUE
34
+ lookup_error_class(const char *sqlstate)
35
+ {
36
+ VALUE klass;
37
+
38
+ if(sqlstate)
39
+ {
40
+ /* Find the proper error class by the 5-characters SQLSTATE. */
41
+ klass = rb_hash_aref( rb_hErrors, rb_str_new2(sqlstate) );
42
+ if(NIL_P(klass))
43
+ {
44
+ /* The given SQLSTATE couldn't be found. This might happen, if
45
+ * the server side uses a newer version than the client.
46
+ * Try to find a error class by using the 2-characters SQLSTATE.
47
+ */
48
+ klass = rb_hash_aref( rb_hErrors, rb_str_new(sqlstate, 2) );
49
+ if(NIL_P(klass))
50
+ {
51
+ /* Also the 2-characters SQLSTATE is unknown.
52
+ * Use the generic server error instead.
53
+ */
54
+ klass = rb_eServerError;
55
+ }
56
+ }
57
+ }
58
+ else
59
+ {
60
+ /* Unable to retrieve the PG_DIAG_SQLSTATE.
61
+ * Use the generic error instead.
62
+ */
63
+ klass = rb_eUnableToSend;
64
+ }
65
+
66
+ return klass;
67
+ }
68
+
69
+ void
70
+ init_pg_errors()
71
+ {
72
+ rb_hErrors = rb_hash_new();
73
+ rb_define_const( rb_mPG, "ERROR_CLASSES", rb_hErrors );
74
+
75
+ rb_ePGerror = rb_define_class_under( rb_mPG, "Error", rb_eStandardError );
76
+
77
+ /*************************
78
+ * PG::Error
79
+ *************************/
80
+ rb_define_alias( rb_ePGerror, "error", "message" );
81
+ rb_define_attr( rb_ePGerror, "connection", 1, 0 );
82
+ rb_define_attr( rb_ePGerror, "result", 1, 0 );
83
+
84
+ rb_eServerError = rb_define_class_under( rb_mPG, "ServerError", rb_ePGerror );
85
+ rb_eUnableToSend = rb_define_class_under( rb_mPG, "UnableToSend", rb_ePGerror );
86
+ rb_eConnectionBad = rb_define_class_under( rb_mPG, "ConnectionBad", rb_ePGerror );
87
+
88
+ #include "errorcodes.def"
89
+ }
@@ -44,13 +44,14 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
44
44
  VALUE
45
45
  pg_result_check( VALUE self )
46
46
  {
47
- VALUE error, exception;
47
+ VALUE error, exception, klass;
48
48
  VALUE rb_pgconn = rb_iv_get( self, "@connection" );
49
49
  PGconn *conn = pg_get_pgconn(rb_pgconn);
50
50
  PGresult *result;
51
51
  #ifdef M17N_SUPPORTED
52
52
  rb_encoding *enc = pg_conn_enc_get( conn );
53
53
  #endif
54
+ char * sqlstate;
54
55
 
55
56
  Data_Get_Struct(self, PGresult, result);
56
57
 
@@ -87,9 +88,12 @@ pg_result_check( VALUE self )
87
88
  #ifdef M17N_SUPPORTED
88
89
  rb_enc_set_index( error, rb_enc_to_index(enc) );
89
90
  #endif
90
- exception = rb_exc_new3( rb_ePGerror, error );
91
+
92
+ sqlstate = PQresultErrorField( result, PG_DIAG_SQLSTATE );
93
+ klass = lookup_error_class( sqlstate );
94
+ exception = rb_exc_new3( klass, error );
91
95
  rb_iv_set( exception, "@connection", rb_pgconn );
92
- rb_iv_set( exception, "@result", self );
96
+ rb_iv_set( exception, "@result", result ? self : Qnil );
93
97
  rb_exc_raise( exception );
94
98
 
95
99
  /* Not reached */
@@ -245,18 +249,18 @@ pgresult_error_message(VALUE self)
245
249
  * conn.exec( "SELECT * FROM nonexistant_table" )
246
250
  * rescue PG::Error => err
247
251
  * p [
248
- * result.error_field( PG::Result::PG_DIAG_SEVERITY ),
249
- * result.error_field( PG::Result::PG_DIAG_SQLSTATE ),
250
- * result.error_field( PG::Result::PG_DIAG_MESSAGE_PRIMARY ),
251
- * result.error_field( PG::Result::PG_DIAG_MESSAGE_DETAIL ),
252
- * result.error_field( PG::Result::PG_DIAG_MESSAGE_HINT ),
253
- * result.error_field( PG::Result::PG_DIAG_STATEMENT_POSITION ),
254
- * result.error_field( PG::Result::PG_DIAG_INTERNAL_POSITION ),
255
- * result.error_field( PG::Result::PG_DIAG_INTERNAL_QUERY ),
256
- * result.error_field( PG::Result::PG_DIAG_CONTEXT ),
257
- * result.error_field( PG::Result::PG_DIAG_SOURCE_FILE ),
258
- * result.error_field( PG::Result::PG_DIAG_SOURCE_LINE ),
259
- * result.error_field( PG::Result::PG_DIAG_SOURCE_FUNCTION ),
252
+ * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
253
+ * err.result.error_field( PG::Result::PG_DIAG_SQLSTATE ),
254
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_PRIMARY ),
255
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_DETAIL ),
256
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_HINT ),
257
+ * err.result.error_field( PG::Result::PG_DIAG_STATEMENT_POSITION ),
258
+ * err.result.error_field( PG::Result::PG_DIAG_INTERNAL_POSITION ),
259
+ * err.result.error_field( PG::Result::PG_DIAG_INTERNAL_QUERY ),
260
+ * err.result.error_field( PG::Result::PG_DIAG_CONTEXT ),
261
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_FILE ),
262
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_LINE ),
263
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_FUNCTION ),
260
264
  * ]
261
265
  * end
262
266
  *
@@ -510,6 +514,31 @@ pgresult_fsize(VALUE self, VALUE index)
510
514
  return INT2NUM(PQfsize(result, i));
511
515
  }
512
516
 
517
+
518
+ static VALUE
519
+ pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
520
+ {
521
+ VALUE val;
522
+ if ( PQgetisnull(result, tuple_num, field_num) ) {
523
+ return Qnil;
524
+ }
525
+ else {
526
+ val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
527
+ PQgetlength(result, tuple_num, field_num) );
528
+
529
+ #ifdef M17N_SUPPORTED
530
+ /* associate client encoding for text format only */
531
+ if ( 0 == PQfformat(result, field_num) ) {
532
+ ASSOCIATE_INDEX( val, self );
533
+ } else {
534
+ rb_enc_associate( val, rb_ascii8bit_encoding() );
535
+ }
536
+ #endif
537
+
538
+ return val;
539
+ }
540
+ }
541
+
513
542
  /*
514
543
  * call-seq:
515
544
  * res.getvalue( tup_num, field_num )
@@ -520,7 +549,6 @@ pgresult_fsize(VALUE self, VALUE index)
520
549
  static VALUE
521
550
  pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
522
551
  {
523
- VALUE val;
524
552
  PGresult *result;
525
553
  int i = NUM2INT(tup_num);
526
554
  int j = NUM2INT(field_num);
@@ -532,21 +560,7 @@ pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
532
560
  if(j < 0 || j >= PQnfields(result)) {
533
561
  rb_raise(rb_eArgError,"invalid field number %d", j);
534
562
  }
535
- if(PQgetisnull(result, i, j))
536
- return Qnil;
537
- val = rb_tainted_str_new(PQgetvalue(result, i, j),
538
- PQgetlength(result, i, j));
539
-
540
- #ifdef M17N_SUPPORTED
541
- /* associate client encoding for text format only */
542
- if ( 0 == PQfformat(result, j) ) {
543
- ASSOCIATE_INDEX( val, self );
544
- } else {
545
- rb_enc_associate( val, rb_ascii8bit_encoding() );
546
- }
547
- #endif
548
-
549
- return val;
563
+ return pgresult_value(self, result, i, j);
550
564
  }
551
565
 
552
566
  /*
@@ -696,7 +710,7 @@ pgresult_aref(VALUE self, VALUE index)
696
710
  PGresult *result = pgresult_get(self);
697
711
  int tuple_num = NUM2INT(index);
698
712
  int field_num;
699
- VALUE fname,val;
713
+ VALUE fname;
700
714
  VALUE tuple;
701
715
 
702
716
  if ( tuple_num < 0 || tuple_num >= PQntuples(result) )
@@ -706,24 +720,7 @@ pgresult_aref(VALUE self, VALUE index)
706
720
  for ( field_num = 0; field_num < PQnfields(result); field_num++ ) {
707
721
  fname = rb_tainted_str_new2( PQfname(result,field_num) );
708
722
  ASSOCIATE_INDEX(fname, self);
709
- if ( PQgetisnull(result, tuple_num, field_num) ) {
710
- rb_hash_aset( tuple, fname, Qnil );
711
- }
712
- else {
713
- val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
714
- PQgetlength(result, tuple_num, field_num) );
715
-
716
- #ifdef M17N_SUPPORTED
717
- /* associate client encoding for text format only */
718
- if ( 0 == PQfformat(result, field_num) ) {
719
- ASSOCIATE_INDEX( val, self );
720
- } else {
721
- rb_enc_associate( val, rb_ascii8bit_encoding() );
722
- }
723
- #endif
724
-
725
- rb_hash_aset( tuple, fname, val );
726
- }
723
+ rb_hash_aset( tuple, fname, pgresult_value(self, result, tuple_num, field_num) );
727
724
  }
728
725
  return tuple;
729
726
  }
@@ -748,23 +745,7 @@ pgresult_each_row(VALUE self)
748
745
 
749
746
  /* populate the row */
750
747
  for ( field = 0; field < num_fields; field++ ) {
751
- if ( PQgetisnull(result, row, field) ) {
752
- rb_ary_store( new_row, field, Qnil );
753
- }
754
- else {
755
- VALUE val = rb_tainted_str_new( PQgetvalue(result, row, field),
756
- PQgetlength(result, row, field) );
757
-
758
- #ifdef M17N_SUPPORTED
759
- /* associate client encoding for text format only */
760
- if ( 0 == PQfformat(result, field) ) {
761
- ASSOCIATE_INDEX( val, self );
762
- } else {
763
- rb_enc_associate( val, rb_ascii8bit_encoding() );
764
- }
765
- #endif
766
- rb_ary_store( new_row, field, val );
767
- }
748
+ rb_ary_store( new_row, field, pgresult_value(self, result, row, field) );
768
749
  }
769
750
  rb_yield( new_row );
770
751
  }
Binary file
data/lib/pg.rb CHANGED
@@ -19,7 +19,7 @@ end
19
19
  module PG
20
20
 
21
21
  # Library version
22
- VERSION = '0.15.1'
22
+ VERSION = '0.16.0'
23
23
 
24
24
  # VCS revision
25
25
  REVISION = %q$Revision$
@@ -247,6 +247,13 @@ module PG::TestingHelpers
247
247
  end
248
248
  end
249
249
  end
250
+
251
+ def connection_string_should_contain_application_name(conn_args, app_name)
252
+ conn_name = conn_args.match(/application_name='(.*)'/)[1]
253
+ conn_name.should include(app_name[0..10])
254
+ conn_name.should include(app_name[-10..-1])
255
+ conn_name.length.should <= 64
256
+ end
250
257
  end
251
258
 
252
259
 
@@ -270,9 +277,11 @@ RSpec.configure do |config|
270
277
  PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
271
278
 
272
279
  if !PG.respond_to?( :library_version )
273
- config.filter_run_excluding( :postgresql_91, :postgresql_92 )
280
+ config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93 )
274
281
  elsif PG.library_version < 90200
275
- config.filter_run_excluding( :postgresql_92 )
282
+ config.filter_run_excluding( :postgresql_92, :postgresql_93 )
283
+ elsif PG.library_version < 90300
284
+ config.filter_run_excluding( :postgresql_93 )
276
285
  end
277
286
  end
278
287
 
@@ -177,6 +177,11 @@ describe PG::Connection do
177
177
  conn.should be_finished()
178
178
  end
179
179
 
180
+ it "raises proper error when sending fails" do
181
+ conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
182
+ expect{ conn.exec 'SELECT 1' }.to raise_error(PG::UnableToSend, /no connection/)
183
+ end
184
+
180
185
  it "doesn't leave stale server connections after finish" do
181
186
  described_class.connect(@conninfo).finish
182
187
  sleep 0.5
@@ -257,6 +262,18 @@ describe PG::Connection do
257
262
  error.should == true
258
263
  end
259
264
 
265
+ it "can stop a thread that runs a blocking query" do
266
+ start = Time.now
267
+ t = Thread.new do
268
+ @conn.async_exec( 'select pg_sleep(10)' )
269
+ end
270
+ sleep 0.1
271
+
272
+ t.kill
273
+ t.join
274
+ (Time.now - start).should < 10
275
+ end
276
+
260
277
  it "automatically rolls back a transaction started with Connection#transaction if an exception " +
261
278
  "is raised" do
262
279
  # abort the per-example transaction so we can test our own
@@ -276,6 +293,16 @@ describe PG::Connection do
276
293
  res.ntuples.should == 0
277
294
  end
278
295
 
296
+ it "returns the block result from Connection#transaction" do
297
+ # abort the per-example transaction so we can test our own
298
+ @conn.exec( 'ROLLBACK' )
299
+
300
+ res = @conn.transaction do
301
+ "transaction result"
302
+ end
303
+ res.should == "transaction result"
304
+ end
305
+
279
306
  it "not read past the end of a large object" do
280
307
  @conn.transaction do
281
308
  oid = @conn.lo_create( 0 )
@@ -400,6 +427,21 @@ describe PG::Connection do
400
427
  @conn.exec( 'UNLISTEN woo' )
401
428
  end
402
429
 
430
+ it "can receive notices while waiting for NOTIFY without exceeding the timeout", :postgresql_90 do
431
+ notices = []
432
+ @conn.set_notice_processor do |msg|
433
+ notices << [msg, Time.now]
434
+ end
435
+ st = Time.now
436
+ @conn.send_query "SELECT pg_sleep(0.5); do $$ BEGIN RAISE NOTICE 'woohoo'; END; $$ LANGUAGE plpgsql;"
437
+ @conn.wait_for_notify( 1 ).should be_nil
438
+ notices.first.should_not be_nil
439
+ et = Time.now
440
+ (et - notices.first[1]).should >= 0.4
441
+ (et - st).should >= 0.9
442
+ (et - st).should < 1.4
443
+ end
444
+
403
445
  it "yields the result if block is given to exec" do
404
446
  rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
405
447
  values = []
@@ -543,7 +585,7 @@ describe PG::Connection do
543
585
  conn = PG.connect( @conninfo )
544
586
 
545
587
  conn.finish
546
- expect { conn.finish }.to raise_error( PG::Error, /connection is closed/i )
588
+ expect { conn.finish }.to raise_error( PG::ConnectionBad, /connection is closed/i )
547
589
  end
548
590
 
549
591
  it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction, :socket_io do
@@ -551,7 +593,7 @@ describe PG::Connection do
551
593
  io = conn.socket_io
552
594
  conn.finish
553
595
  io.should be_closed()
554
- expect { conn.socket_io }.to raise_error( PG::Error, /connection is closed/i )
596
+ expect { conn.socket_io }.to raise_error( PG::ConnectionBad, /connection is closed/i )
555
597
  end
556
598
 
557
599
  it "closes the IO fetched from #socket_io when the connection is reset", :without_transaction, :socket_io do
@@ -563,6 +605,16 @@ describe PG::Connection do
563
605
  conn.finish
564
606
  end
565
607
 
608
+ it "block should raise ConnectionBad for a closed connection" do
609
+ serv = TCPServer.new( '127.0.0.1', 54320 )
610
+ conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
611
+ while [PG::CONNECTION_STARTED, PG::CONNECTION_MADE].include?(conn.connect_poll)
612
+ sleep 0.1
613
+ end
614
+ serv.close
615
+ expect{ conn.block }.to raise_error(PG::ConnectionBad, /server closed the connection unexpectedly/)
616
+ expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
617
+ end
566
618
 
567
619
  context "under PostgreSQL 9", :postgresql_90 do
568
620
 
@@ -571,14 +623,16 @@ describe PG::Connection do
571
623
  end
572
624
 
573
625
  it "sets the fallback_application_name on new connections" do
574
- PG::Connection.parse_connect_args( 'dbname=test' ).should include( $0 )
626
+ conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
627
+ connection_string_should_contain_application_name(conn_string, $0)
575
628
  end
576
629
 
577
630
  it "sets a shortened fallback_application_name on new connections" do
578
631
  old_0 = $0
579
632
  begin
580
633
  $0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
581
- PG::Connection.parse_connect_args( 'dbname=test' ).should match(/\/this\/is\/a.*\.\.\..*\/beloved\/ruby/)
634
+ conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
635
+ connection_string_should_contain_application_name(conn_string, $0)
582
636
  ensure
583
637
  $0 = old_0
584
638
  end
@@ -874,17 +928,53 @@ describe PG::Connection do
874
928
  end
875
929
 
876
930
  it "uses the client encoding for escaped string" do
877
- original = "string to escape".force_encoding( "euc-jp" )
931
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
878
932
  @conn.set_client_encoding( "euc_jp" )
879
933
  escaped = @conn.escape( original )
880
934
  escaped.encoding.should == Encoding::EUC_JP
935
+ escaped.should == "string to"
881
936
  end
882
937
 
883
- it "escapes string as literal", :postgresql_90 do
884
- original = "string to\0 escape"
938
+ it "uses the client encoding for escaped literal", :postgresql_90 do
939
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
940
+ @conn.set_client_encoding( "euc_jp" )
885
941
  escaped = @conn.escape_literal( original )
942
+ escaped.encoding.should == Encoding::EUC_JP
886
943
  escaped.should == "'string to'"
887
944
  end
945
+
946
+ it "uses the client encoding for escaped identifier", :postgresql_90 do
947
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
948
+ @conn.set_client_encoding( "euc_jp" )
949
+ escaped = @conn.escape_identifier( original )
950
+ escaped.encoding.should == Encoding::EUC_JP
951
+ escaped.should == "\"string to\""
952
+ end
953
+
954
+ it "uses the client encoding for quote_ident" do
955
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
956
+ @conn.set_client_encoding( "euc_jp" )
957
+ escaped = @conn.quote_ident( original )
958
+ escaped.encoding.should == Encoding::EUC_JP
959
+ escaped.should == "\"string to\""
960
+ end
961
+
962
+ it "uses the previous string encoding for escaped string" do
963
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
964
+ @conn.set_client_encoding( "euc_jp" )
965
+ escaped = described_class.escape( original )
966
+ escaped.encoding.should == Encoding::ISO8859_1
967
+ escaped.should == "string to"
968
+ end
969
+
970
+ it "uses the previous string encoding for quote_ident" do
971
+ original = "string to\0 escape".force_encoding( "iso8859-1" )
972
+ @conn.set_client_encoding( "euc_jp" )
973
+ escaped = described_class.quote_ident( original )
974
+ escaped.encoding.should == Encoding::ISO8859_1
975
+ escaped.should == "\"string to\""
976
+ end
977
+
888
978
  end
889
979
 
890
980
 
@@ -1010,7 +1100,7 @@ describe PG::Connection do
1010
1100
  end
1011
1101
 
1012
1102
  context "OS thread support", :ruby_19 do
1013
- it "described_class#exec shouldn't block a second thread" do
1103
+ it "Connection#exec shouldn't block a second thread" do
1014
1104
  t = Thread.new do
1015
1105
  @conn.exec( "select pg_sleep(1)" )
1016
1106
  end
@@ -1019,5 +1109,20 @@ describe PG::Connection do
1019
1109
  t.should be_alive()
1020
1110
  t.join
1021
1111
  end
1112
+
1113
+ it "Connection.new shouldn't block a second thread" do
1114
+ serv = nil
1115
+ t = Thread.new do
1116
+ serv = TCPServer.new( '127.0.0.1', 54320 )
1117
+ expect {
1118
+ described_class.new( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
1119
+ }.to raise_error(PG::ConnectionBad, /server closed the connection unexpectedly/)
1120
+ end
1121
+
1122
+ sleep 0.5
1123
+ t.should be_alive()
1124
+ serv.close
1125
+ t.join
1126
+ end
1022
1127
  end
1023
1128
  end