do_postgres 0.9.12-x86-mswin32-60 → 0.10.0-x86-mswin32-60
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.
- 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
|
}
|