do_postgres 0.9.12-x86-mswin32-60 → 0.10.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/{History.txt → HISTORY.markdown} +6 -2
- data/Manifest.txt +2 -2
- data/{README.txt → README.markdown} +2 -1
- data/Rakefile +2 -2
- data/ext/do_postgres_ext/do_postgres_ext.c +213 -85
- data/ext/do_postgres_ext/error.h +483 -0
- data/ext/do_postgres_ext/extconf.rb +1 -12
- data/ext/do_postgres_ext/pg_config.h +689 -0
- data/lib/do_postgres.rb +16 -19
- data/lib/do_postgres/encoding.rb +42 -0
- data/lib/do_postgres/transaction.rb +10 -0
- data/lib/do_postgres/version.rb +1 -1
- data/lib/do_postgres_ext.so +0 -0
- data/spec/encoding_spec.rb +12 -1
- data/spec/spec_helper.rb +37 -10
- data/tasks/gem.rake +1 -54
- data/tasks/release.rake +7 -7
- data/tasks/retrieve.rake +1 -2
- data/tasks/spec.rake +1 -0
- metadata +13 -10
@@ -1,8 +1,12 @@
|
|
1
|
-
|
1
|
+
## 0.9.12 2009-05-17
|
2
|
+
* Improvements
|
3
|
+
* Windows support
|
4
|
+
|
5
|
+
## 0.9.11 2009-01-19
|
2
6
|
* Improvements
|
3
7
|
* Ruby 1.9 support
|
4
8
|
* Fixes
|
5
9
|
* Fix build issue on certain platforms introduces with 0.9.10
|
6
10
|
|
7
|
-
|
11
|
+
## 0.9.9 2008-11-27
|
8
12
|
* No changes since 0.9.8
|
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -8,9 +8,9 @@ require 'lib/do_postgres/version'
|
|
8
8
|
ROOT = Pathname(__FILE__).dirname.expand_path
|
9
9
|
JRUBY = RUBY_PLATFORM =~ /java/
|
10
10
|
WINDOWS = Gem.win_platform?
|
11
|
-
SUDO =
|
11
|
+
SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
|
12
12
|
BINARY_VERSION = '5.0.77'
|
13
13
|
|
14
|
-
Dir['tasks/*.rake'].each { |f| import f }
|
14
|
+
Dir['tasks/*.rake'].sort.each { |f| import f }
|
15
15
|
|
16
16
|
CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_postgres_ext/Makefile ext-java/target ])
|
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <postgres.h>
|
3
3
|
#include <mb/pg_wchar.h>
|
4
4
|
#include <catalog/pg_type.h>
|
5
|
+
#include <utils/errcodes.h>
|
5
6
|
|
6
7
|
/* Undefine constants Postgres also defines */
|
7
8
|
#undef PACKAGE_BUGREPORT
|
@@ -30,14 +31,13 @@
|
|
30
31
|
#include <math.h>
|
31
32
|
#include <ctype.h>
|
32
33
|
#include <time.h>
|
34
|
+
#include "error.h"
|
33
35
|
|
34
36
|
#define ID_CONST_GET rb_intern("const_get")
|
35
37
|
#define ID_PATH rb_intern("path")
|
36
38
|
#define ID_NEW rb_intern("new")
|
37
39
|
#define ID_ESCAPE rb_intern("escape_sql")
|
38
40
|
|
39
|
-
#define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
|
40
|
-
#define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
|
41
41
|
#define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
|
42
42
|
#define POSTGRES_CLASS(klass, parent) (rb_define_class_under(mPostgres, klass, parent))
|
43
43
|
#define DEBUG(value) data_objects_debug(value)
|
@@ -55,6 +55,36 @@
|
|
55
55
|
#define RARRAY_LEN(a) RARRAY(a)->len
|
56
56
|
#endif
|
57
57
|
|
58
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
59
|
+
#include <ruby/encoding.h>
|
60
|
+
|
61
|
+
#define DO_STR_NEW2(str, encoding) \
|
62
|
+
({ \
|
63
|
+
VALUE _string = rb_str_new2((const char *)str); \
|
64
|
+
if(encoding != -1) { \
|
65
|
+
rb_enc_associate_index(_string, encoding); \
|
66
|
+
} \
|
67
|
+
_string; \
|
68
|
+
})
|
69
|
+
|
70
|
+
#define DO_STR_NEW(str, len, encoding) \
|
71
|
+
({ \
|
72
|
+
VALUE _string = rb_str_new((const char *)str, (long)len); \
|
73
|
+
if(encoding != -1) { \
|
74
|
+
rb_enc_associate_index(_string, encoding); \
|
75
|
+
} \
|
76
|
+
_string; \
|
77
|
+
})
|
78
|
+
|
79
|
+
#else
|
80
|
+
|
81
|
+
#define DO_STR_NEW2(str, doc) \
|
82
|
+
rb_str_new2((const char *)str)
|
83
|
+
|
84
|
+
#define DO_STR_NEW(str, len, doc) \
|
85
|
+
rb_str_new((const char *)str, (long)len)
|
86
|
+
#endif
|
87
|
+
|
58
88
|
|
59
89
|
// To store rb_intern values
|
60
90
|
static ID ID_NEW_DATE;
|
@@ -66,6 +96,7 @@ static ID ID_RATIONAL;
|
|
66
96
|
|
67
97
|
static VALUE mExtlib;
|
68
98
|
static VALUE mDO;
|
99
|
+
static VALUE mEncoding;
|
69
100
|
static VALUE cDO_Quoting;
|
70
101
|
static VALUE cDO_Connection;
|
71
102
|
static VALUE cDO_Command;
|
@@ -84,7 +115,8 @@ static VALUE cResult;
|
|
84
115
|
static VALUE cReader;
|
85
116
|
|
86
117
|
static VALUE eArgumentError;
|
87
|
-
static VALUE
|
118
|
+
static VALUE eConnectionError;
|
119
|
+
static VALUE eDataError;
|
88
120
|
|
89
121
|
static void data_objects_debug(VALUE string, struct timeval* start) {
|
90
122
|
struct timeval stop;
|
@@ -116,7 +148,7 @@ static char * get_uri_option(VALUE query_hash, char * key) {
|
|
116
148
|
|
117
149
|
if(!rb_obj_is_kind_of(query_hash, rb_cHash)) { return NULL; }
|
118
150
|
|
119
|
-
query_value = rb_hash_aref(query_hash,
|
151
|
+
query_value = rb_hash_aref(query_hash, rb_str_new2(key));
|
120
152
|
|
121
153
|
if (Qnil != query_value) {
|
122
154
|
value = StringValuePtr(query_value);
|
@@ -219,7 +251,7 @@ static VALUE parse_date_time(const char *date) {
|
|
219
251
|
hour_offset = 0;
|
220
252
|
minute_offset = 0;
|
221
253
|
sec = 0;
|
222
|
-
}
|
254
|
+
}
|
223
255
|
// We read the Date and Time, default to the current locale's offset
|
224
256
|
|
225
257
|
// Get localtime
|
@@ -241,7 +273,7 @@ static VALUE parse_date_time(const char *date) {
|
|
241
273
|
|
242
274
|
} else {
|
243
275
|
// Something went terribly wrong
|
244
|
-
rb_raise(
|
276
|
+
rb_raise(eDataError, "Couldn't parse date: %s", date);
|
245
277
|
}
|
246
278
|
|
247
279
|
jd = jd_from_date(year, month, day);
|
@@ -327,16 +359,16 @@ static VALUE infer_ruby_type(Oid type) {
|
|
327
359
|
}
|
328
360
|
}
|
329
361
|
|
330
|
-
static VALUE typecast(const char *value, long length, const VALUE type) {
|
362
|
+
static VALUE typecast(const char *value, long length, const VALUE type, int encoding) {
|
331
363
|
|
332
364
|
if (type == rb_cInteger) {
|
333
365
|
return rb_cstr2inum(value, 10);
|
334
366
|
} else if (type == rb_cString) {
|
335
|
-
return
|
367
|
+
return DO_STR_NEW(value, length, encoding);
|
336
368
|
} else if (type == rb_cFloat) {
|
337
369
|
return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
|
338
370
|
} else if (type == rb_cBigDecimal) {
|
339
|
-
return rb_funcall(rb_cBigDecimal, ID_NEW, 1,
|
371
|
+
return rb_funcall(rb_cBigDecimal, ID_NEW, 1, rb_str_new(value, length));
|
340
372
|
} else if (type == rb_cDate) {
|
341
373
|
return parse_date(value);
|
342
374
|
} else if (type == rb_cDateTime) {
|
@@ -348,21 +380,55 @@ static VALUE typecast(const char *value, long length, const VALUE type) {
|
|
348
380
|
} else if (type == rb_cByteArray) {
|
349
381
|
size_t new_length = 0;
|
350
382
|
char* unescaped = (char *)PQunescapeBytea((unsigned char*)value, &new_length);
|
351
|
-
VALUE byte_array = rb_funcall(rb_cByteArray, ID_NEW, 1,
|
383
|
+
VALUE byte_array = rb_funcall(rb_cByteArray, ID_NEW, 1, rb_str_new(unescaped, new_length));
|
352
384
|
PQfreemem(unescaped);
|
353
385
|
return byte_array;
|
354
386
|
} else if (type == rb_cClass) {
|
355
|
-
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1,
|
387
|
+
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, rb_str_new(value, length));
|
356
388
|
} else if (type == rb_cObject) {
|
357
|
-
return rb_marshal_load(
|
389
|
+
return rb_marshal_load(rb_str_new(value, length));
|
358
390
|
} else if (type == rb_cNilClass) {
|
359
391
|
return Qnil;
|
360
392
|
} else {
|
361
|
-
return
|
393
|
+
return DO_STR_NEW(value, length, encoding);
|
362
394
|
}
|
363
395
|
|
364
396
|
}
|
365
397
|
|
398
|
+
static void raise_error(VALUE self, PGresult *result, VALUE query) {
|
399
|
+
VALUE exception;
|
400
|
+
char *message;
|
401
|
+
char *sqlstate;
|
402
|
+
int postgres_errno;
|
403
|
+
|
404
|
+
message = PQresultErrorMessage(result);
|
405
|
+
sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
|
406
|
+
postgres_errno = MAKE_SQLSTATE(sqlstate[0], sqlstate[1], sqlstate[2], sqlstate[3], sqlstate[4]);
|
407
|
+
PQclear(result);
|
408
|
+
|
409
|
+
const char *exception_type = "SQLError";
|
410
|
+
|
411
|
+
struct errcodes *errs;
|
412
|
+
|
413
|
+
for (errs = errors; errs->error_name; errs++) {
|
414
|
+
if(errs->error_no == postgres_errno) {
|
415
|
+
exception_type = errs->exception;
|
416
|
+
break;
|
417
|
+
}
|
418
|
+
}
|
419
|
+
|
420
|
+
VALUE uri = rb_funcall(rb_iv_get(self, "@connection"), rb_intern("to_s"), 0);
|
421
|
+
|
422
|
+
exception = rb_funcall(CONST_GET(mDO, exception_type), ID_NEW, 5,
|
423
|
+
rb_str_new2(message),
|
424
|
+
INT2NUM(postgres_errno),
|
425
|
+
rb_str_new2(sqlstate),
|
426
|
+
query,
|
427
|
+
uri);
|
428
|
+
rb_exc_raise(exception);
|
429
|
+
}
|
430
|
+
|
431
|
+
|
366
432
|
/* ====== Public API ======= */
|
367
433
|
static VALUE cConnection_dispose(VALUE self) {
|
368
434
|
VALUE connection_container = rb_iv_get(self, "@connection");
|
@@ -449,7 +515,8 @@ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
|
|
449
515
|
// Wrap the escaped string in single-quotes, this is DO's convention
|
450
516
|
escaped[quoted_length + 1] = escaped[0] = '\'';
|
451
517
|
|
452
|
-
result =
|
518
|
+
result = DO_STR_NEW(escaped, quoted_length + 2, FIX2INT(rb_iv_get(self, "@encoding_id")));
|
519
|
+
|
453
520
|
free(escaped);
|
454
521
|
return result;
|
455
522
|
}
|
@@ -474,14 +541,16 @@ static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
|
|
474
541
|
// Wrap the escaped string in single-quotes, this is DO's convention (replace trailing \0)
|
475
542
|
escaped_quotes[quoted_length] = escaped_quotes[0] = '\'';
|
476
543
|
|
477
|
-
result =
|
544
|
+
result = rb_str_new((const char *)escaped_quotes, quoted_length + 1);
|
478
545
|
PQfreemem(escaped);
|
479
546
|
free(escaped_quotes);
|
480
547
|
return result;
|
481
548
|
}
|
482
549
|
|
550
|
+
static void full_connect(VALUE self, PGconn *db);
|
551
|
+
|
483
552
|
#ifdef _WIN32
|
484
|
-
static PGresult* cCommand_execute_sync(PGconn *db, VALUE query) {
|
553
|
+
static PGresult* cCommand_execute_sync(VALUE self, PGconn *db, VALUE query) {
|
485
554
|
PGresult *response;
|
486
555
|
struct timeval start;
|
487
556
|
char* str = StringValuePtr(query);
|
@@ -499,11 +568,15 @@ static PGresult* cCommand_execute_sync(PGconn *db, VALUE query) {
|
|
499
568
|
PQreset(db);
|
500
569
|
if (PQstatus(db) == CONNECTION_OK) {
|
501
570
|
response = PQexec(db, str);
|
571
|
+
} else {
|
572
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
573
|
+
full_connect(connection, db);
|
574
|
+
response = PQexec(db, str);
|
502
575
|
}
|
503
576
|
}
|
504
577
|
|
505
578
|
if(response == NULL) {
|
506
|
-
rb_raise(
|
579
|
+
rb_raise(eConnectionError, PQerrorMessage(db));
|
507
580
|
}
|
508
581
|
}
|
509
582
|
|
@@ -511,7 +584,7 @@ static PGresult* cCommand_execute_sync(PGconn *db, VALUE query) {
|
|
511
584
|
return response;
|
512
585
|
}
|
513
586
|
#else
|
514
|
-
static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
587
|
+
static PGresult* cCommand_execute_async(VALUE self, PGconn *db, VALUE query) {
|
515
588
|
int socket_fd;
|
516
589
|
int retval;
|
517
590
|
fd_set rset;
|
@@ -530,11 +603,15 @@ static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
|
530
603
|
PQreset(db);
|
531
604
|
if (PQstatus(db) == CONNECTION_OK) {
|
532
605
|
retval = PQsendQuery(db, str);
|
606
|
+
} else {
|
607
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
608
|
+
full_connect(connection, db);
|
609
|
+
retval = PQsendQuery(db, str);
|
533
610
|
}
|
534
611
|
}
|
535
612
|
|
536
613
|
if(!retval) {
|
537
|
-
rb_raise(
|
614
|
+
rb_raise(eConnectionError, PQerrorMessage(db));
|
538
615
|
}
|
539
616
|
}
|
540
617
|
|
@@ -554,7 +631,7 @@ static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
|
554
631
|
}
|
555
632
|
|
556
633
|
if (PQconsumeInput(db) == 0) {
|
557
|
-
rb_raise(
|
634
|
+
rb_raise(eConnectionError, PQerrorMessage(db));
|
558
635
|
}
|
559
636
|
|
560
637
|
if (PQisBusy(db) == 0) {
|
@@ -568,52 +645,94 @@ static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
|
568
645
|
#endif
|
569
646
|
|
570
647
|
static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
571
|
-
|
572
|
-
VALUE r_host, r_user, r_password, r_path, r_port, r_query, r_options;
|
573
|
-
char *host = NULL, *user = NULL, *password = NULL, *path;
|
574
|
-
char *database = "", *port = "5432";
|
575
|
-
char *encoding = NULL;
|
576
|
-
char *search_path = NULL;
|
577
|
-
char *search_path_query = NULL;
|
578
|
-
char *backslash_off = "SET backslash_quote = off";
|
579
|
-
char *standard_strings_on = "SET standard_conforming_strings = on";
|
580
|
-
char *warning_messages = "SET client_min_messages = warning";
|
648
|
+
VALUE r_host, r_user, r_password, r_path, r_query, r_port;
|
581
649
|
|
582
|
-
PGconn *db;
|
650
|
+
PGconn *db = NULL;
|
651
|
+
|
652
|
+
rb_iv_set(self, "@using_socket", Qfalse);
|
583
653
|
|
584
654
|
r_host = rb_funcall(uri, rb_intern("host"), 0);
|
585
|
-
if (
|
586
|
-
host
|
655
|
+
if (Qnil != r_host) {
|
656
|
+
rb_iv_set(self, "@host", r_host);
|
587
657
|
}
|
588
658
|
|
589
659
|
r_user = rb_funcall(uri, rb_intern("user"), 0);
|
590
660
|
if (Qnil != r_user) {
|
591
|
-
user
|
661
|
+
rb_iv_set(self, "@user", r_user);
|
592
662
|
}
|
593
663
|
|
594
664
|
r_password = rb_funcall(uri, rb_intern("password"), 0);
|
595
665
|
if (Qnil != r_password) {
|
596
|
-
password
|
666
|
+
rb_iv_set(self, "@password", r_password);
|
597
667
|
}
|
598
668
|
|
599
669
|
r_path = rb_funcall(uri, rb_intern("path"), 0);
|
600
|
-
path = StringValuePtr(r_path);
|
601
670
|
if (Qnil != r_path) {
|
602
|
-
|
603
|
-
}
|
604
|
-
|
605
|
-
if (NULL == database || 0 == strlen(database)) {
|
606
|
-
rb_raise(ePostgresError, "Database must be specified");
|
671
|
+
rb_iv_set(self, "@path", r_path);
|
607
672
|
}
|
608
673
|
|
609
674
|
r_port = rb_funcall(uri, rb_intern("port"), 0);
|
610
675
|
if (Qnil != r_port) {
|
611
676
|
r_port = rb_funcall(r_port, rb_intern("to_s"), 0);
|
612
|
-
port
|
677
|
+
rb_iv_set(self, "@port", r_port);
|
613
678
|
}
|
614
679
|
|
615
680
|
// Pull the querystring off the URI
|
616
681
|
r_query = rb_funcall(uri, rb_intern("query"), 0);
|
682
|
+
rb_iv_set(self, "@query", r_query);
|
683
|
+
|
684
|
+
const char* encoding = get_uri_option(r_query, "encoding");
|
685
|
+
if (!encoding) { encoding = get_uri_option(r_query, "charset"); }
|
686
|
+
if (!encoding) { encoding = "UTF-8"; }
|
687
|
+
|
688
|
+
rb_iv_set(self, "@encoding", rb_str_new2(encoding));
|
689
|
+
|
690
|
+
full_connect(self, db);
|
691
|
+
|
692
|
+
rb_iv_set(self, "@uri", uri);
|
693
|
+
|
694
|
+
return Qtrue;
|
695
|
+
}
|
696
|
+
|
697
|
+
static void full_connect(VALUE self, PGconn *db) {
|
698
|
+
|
699
|
+
PGresult *result = NULL;
|
700
|
+
VALUE r_host, r_user, r_password, r_path, r_port, r_query, r_options;
|
701
|
+
char *host = NULL, *user = NULL, *password = NULL, *path;
|
702
|
+
char *database = "", *port = "5432";
|
703
|
+
VALUE encoding = Qnil;
|
704
|
+
char *search_path = NULL;
|
705
|
+
char *search_path_query = NULL;
|
706
|
+
char *backslash_off = "SET backslash_quote = off";
|
707
|
+
char *standard_strings_on = "SET standard_conforming_strings = on";
|
708
|
+
char *warning_messages = "SET client_min_messages = warning";
|
709
|
+
|
710
|
+
if((r_host = rb_iv_get(self, "@host")) != Qnil) {
|
711
|
+
host = StringValuePtr(r_host);
|
712
|
+
}
|
713
|
+
|
714
|
+
if((r_user = rb_iv_get(self, "@user")) != Qnil) {
|
715
|
+
user = StringValuePtr(r_user);
|
716
|
+
}
|
717
|
+
|
718
|
+
if((r_password = rb_iv_get(self, "@password")) != Qnil) {
|
719
|
+
password = StringValuePtr(r_password);
|
720
|
+
}
|
721
|
+
|
722
|
+
if((r_port = rb_iv_get(self, "@port")) != Qnil) {
|
723
|
+
port = StringValuePtr(r_port);
|
724
|
+
}
|
725
|
+
|
726
|
+
if((r_path = rb_iv_get(self, "@path")) != Qnil) {
|
727
|
+
path = StringValuePtr(r_path);
|
728
|
+
database = strtok(path, "/");
|
729
|
+
}
|
730
|
+
|
731
|
+
if (NULL == database || 0 == strlen(database)) {
|
732
|
+
rb_raise(eConnectionError, "Database must be specified");
|
733
|
+
}
|
734
|
+
|
735
|
+
r_query = rb_iv_get(self, "@query");
|
617
736
|
|
618
737
|
search_path = get_uri_option(r_query, "search_path");
|
619
738
|
|
@@ -628,81 +747,78 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
628
747
|
);
|
629
748
|
|
630
749
|
if ( PQstatus(db) == CONNECTION_BAD ) {
|
631
|
-
rb_raise(
|
750
|
+
rb_raise(eConnectionError, PQerrorMessage(db));
|
632
751
|
}
|
633
752
|
|
634
753
|
if (search_path != NULL) {
|
635
754
|
search_path_query = (char *)calloc(256, sizeof(char));
|
636
755
|
snprintf(search_path_query, 256, "set search_path to %s;", search_path);
|
637
756
|
r_query = rb_str_new2(search_path_query);
|
638
|
-
result = cCommand_execute(db, r_query);
|
757
|
+
result = cCommand_execute(self, db, r_query);
|
639
758
|
|
640
759
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
641
760
|
free(search_path_query);
|
642
|
-
|
761
|
+
raise_error(self, result, r_query);
|
643
762
|
}
|
644
763
|
|
645
764
|
free(search_path_query);
|
646
765
|
}
|
647
766
|
|
648
767
|
r_options = rb_str_new2(backslash_off);
|
649
|
-
result = cCommand_execute(db, r_options);
|
768
|
+
result = cCommand_execute(self, db, r_options);
|
650
769
|
|
651
770
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
652
771
|
rb_warn(PQresultErrorMessage(result));
|
653
772
|
}
|
654
773
|
|
655
774
|
r_options = rb_str_new2(standard_strings_on);
|
656
|
-
result = cCommand_execute(db, r_options);
|
775
|
+
result = cCommand_execute(self, db, r_options);
|
657
776
|
|
658
777
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
659
778
|
rb_warn(PQresultErrorMessage(result));
|
660
779
|
}
|
661
780
|
|
662
781
|
r_options = rb_str_new2(warning_messages);
|
663
|
-
result = cCommand_execute(db, r_options);
|
782
|
+
result = cCommand_execute(self, db, r_options);
|
664
783
|
|
665
784
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
666
785
|
rb_warn(PQresultErrorMessage(result));
|
667
786
|
}
|
668
787
|
|
669
|
-
encoding =
|
670
|
-
if (!encoding) { encoding = get_uri_option(r_query, "charset"); }
|
671
|
-
if (!encoding) { encoding = "utf8"; }
|
788
|
+
encoding = rb_iv_get(self, "@encoding");
|
672
789
|
|
673
790
|
#ifdef HAVE_PQSETCLIENTENCODING
|
674
|
-
|
675
|
-
|
791
|
+
VALUE pg_encoding = rb_hash_aref(CONST_GET(mEncoding, "MAP"), encoding);
|
792
|
+
if(pg_encoding != Qnil) {
|
793
|
+
if(PQsetClientEncoding(db, RSTRING_PTR(pg_encoding))) {
|
794
|
+
rb_raise(eConnectionError, "Couldn't set encoding: %s", RSTRING_PTR(encoding));
|
795
|
+
} else {
|
796
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
797
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index(RSTRING_PTR(encoding))));
|
798
|
+
#endif
|
799
|
+
rb_iv_set(self, "@pg_encoding", pg_encoding);
|
800
|
+
}
|
801
|
+
} else {
|
802
|
+
rb_warn("Encoding %s is not a known Ruby encoding for PostgreSQL\n", RSTRING_PTR(encoding));
|
803
|
+
rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
|
804
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
805
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
|
806
|
+
#endif
|
807
|
+
rb_iv_set(self, "@pg_encoding", rb_str_new2("UTF8"));
|
676
808
|
}
|
677
809
|
#endif
|
678
|
-
|
679
|
-
rb_iv_set(self, "@uri", uri);
|
680
810
|
rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
|
681
|
-
|
682
|
-
return Qtrue;
|
683
811
|
}
|
684
812
|
|
685
813
|
static VALUE cConnection_character_set(VALUE self) {
|
686
|
-
|
687
|
-
PGconn *db;
|
688
|
-
|
689
|
-
const char *encoding;
|
690
|
-
|
691
|
-
if (Qnil == connection_container)
|
692
|
-
return Qfalse;
|
693
|
-
|
694
|
-
db = DATA_PTR(connection_container);
|
695
|
-
|
696
|
-
encoding = pg_encoding_to_char(PQclientEncoding(db));
|
697
|
-
|
698
|
-
return rb_funcall(RUBY_STRING(encoding), rb_intern("downcase"), 0);
|
814
|
+
return rb_iv_get(self, "@encoding");
|
699
815
|
}
|
700
816
|
|
701
817
|
static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
|
702
818
|
VALUE connection = rb_iv_get(self, "@connection");
|
703
819
|
VALUE postgres_connection = rb_iv_get(connection, "@connection");
|
704
820
|
if (Qnil == postgres_connection) {
|
705
|
-
rb_raise(
|
821
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
706
822
|
}
|
707
823
|
|
708
824
|
PGconn *db = DATA_PTR(postgres_connection);
|
@@ -714,7 +830,7 @@ static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
|
|
714
830
|
|
715
831
|
VALUE query = build_query_from_args(self, argc, argv);
|
716
832
|
|
717
|
-
response = cCommand_execute(db, query);
|
833
|
+
response = cCommand_execute(self, db, query);
|
718
834
|
|
719
835
|
status = PQresultStatus(response);
|
720
836
|
|
@@ -727,10 +843,7 @@ static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
|
|
727
843
|
affected_rows = INT2NUM(atoi(PQcmdTuples(response)));
|
728
844
|
}
|
729
845
|
else {
|
730
|
-
|
731
|
-
char *sqlstate = PQresultErrorField(response, PG_DIAG_SQLSTATE);
|
732
|
-
PQclear(response);
|
733
|
-
rb_raise(ePostgresError, "(sql_state=%s) %sQuery: %s\n", sqlstate, message, StringValuePtr(query));
|
846
|
+
raise_error(self, response, query);
|
734
847
|
}
|
735
848
|
|
736
849
|
PQclear(response);
|
@@ -749,7 +862,7 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
|
|
749
862
|
VALUE connection = rb_iv_get(self, "@connection");
|
750
863
|
VALUE postgres_connection = rb_iv_get(connection, "@connection");
|
751
864
|
if (Qnil == postgres_connection) {
|
752
|
-
rb_raise(
|
865
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
753
866
|
}
|
754
867
|
|
755
868
|
PGconn *db = DATA_PTR(postgres_connection);
|
@@ -757,17 +870,16 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
|
|
757
870
|
|
758
871
|
query = build_query_from_args(self, argc, argv);
|
759
872
|
|
760
|
-
response = cCommand_execute(db, query);
|
873
|
+
response = cCommand_execute(self, db, query);
|
761
874
|
|
762
875
|
if ( PQresultStatus(response) != PGRES_TUPLES_OK ) {
|
763
|
-
|
764
|
-
PQclear(response);
|
765
|
-
rb_raise(ePostgresError, "%sQuery: %s\n", message, StringValuePtr(query));
|
876
|
+
raise_error(self, response, query);
|
766
877
|
}
|
767
878
|
|
768
879
|
field_count = PQnfields(response);
|
769
880
|
|
770
881
|
reader = rb_funcall(cReader, ID_NEW, 0);
|
882
|
+
rb_iv_set(reader, "@connection", connection);
|
771
883
|
rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
|
772
884
|
rb_iv_set(reader, "@field_count", INT2NUM(field_count));
|
773
885
|
rb_iv_set(reader, "@row_count", INT2NUM(PQntuples(response)));
|
@@ -834,17 +946,25 @@ static VALUE cReader_next(VALUE self) {
|
|
834
946
|
field_types = rb_iv_get(self, "@field_types");
|
835
947
|
position = NUM2INT(rb_iv_get(self, "@position"));
|
836
948
|
|
837
|
-
if ( position > (row_count-1) ) {
|
949
|
+
if ( position > (row_count - 1) ) {
|
838
950
|
rb_iv_set(self, "@values", Qnil);
|
839
951
|
return Qfalse;
|
840
952
|
}
|
841
953
|
|
954
|
+
int enc = -1;
|
955
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
956
|
+
VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
|
957
|
+
if (encoding_id != Qnil) {
|
958
|
+
enc = FIX2INT(encoding_id);
|
959
|
+
}
|
960
|
+
#endif
|
961
|
+
|
842
962
|
for ( i = 0; i < field_count; i++ ) {
|
843
963
|
field_type = rb_ary_entry(field_types, i);
|
844
964
|
|
845
965
|
// Always return nil if the value returned from Postgres is null
|
846
966
|
if (!PQgetisnull(reader, position, i)) {
|
847
|
-
value = typecast(PQgetvalue(reader, position, i), PQgetlength(reader, position, i), field_type);
|
967
|
+
value = typecast(PQgetvalue(reader, position, i), PQgetlength(reader, position, i), field_type, enc);
|
848
968
|
} else {
|
849
969
|
value = Qnil;
|
850
970
|
}
|
@@ -862,7 +982,7 @@ static VALUE cReader_values(VALUE self) {
|
|
862
982
|
|
863
983
|
VALUE values = rb_iv_get(self, "@values");
|
864
984
|
if(values == Qnil) {
|
865
|
-
rb_raise(
|
985
|
+
rb_raise(eDataError, "Reader not initialized");
|
866
986
|
return Qnil;
|
867
987
|
} else {
|
868
988
|
return values;
|
@@ -913,7 +1033,9 @@ void Init_do_postgres_ext() {
|
|
913
1033
|
|
914
1034
|
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
915
1035
|
mPostgres = rb_define_module_under(mDO, "Postgres");
|
916
|
-
|
1036
|
+
eConnectionError = CONST_GET(mDO, "ConnectionError");
|
1037
|
+
eDataError = CONST_GET(mDO, "DataError");
|
1038
|
+
mEncoding = rb_define_module_under(mPostgres, "Encoding");
|
917
1039
|
|
918
1040
|
cConnection = POSTGRES_CLASS("Connection", cDO_Connection);
|
919
1041
|
rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
|
@@ -936,4 +1058,10 @@ void Init_do_postgres_ext() {
|
|
936
1058
|
rb_define_method(cReader, "fields", cReader_fields, 0);
|
937
1059
|
rb_define_method(cReader, "field_count", cReader_field_count, 0);
|
938
1060
|
|
1061
|
+
struct errcodes *errs;
|
1062
|
+
|
1063
|
+
for (errs = errors; errs->error_name; errs++) {
|
1064
|
+
rb_const_set(mPostgres, rb_intern(errs->error_name), INT2NUM(errs->error_no));
|
1065
|
+
}
|
1066
|
+
|
939
1067
|
}
|