do_postgres 0.9.12-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,8 @@
1
+ == 0.9.11 2009-01-19
2
+ * Improvements
3
+ * Ruby 1.9 support
4
+ * Fixes
5
+ * Fix build issue on certain platforms introduces with 0.9.10
6
+
7
+ == 0.9.9 2008-11-27
8
+ * No changes since 0.9.8
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007, 2008, 2009 Yehuda Katz, Dirkjan Bussink
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,34 @@
1
+ .gitignore
2
+ History.txt
3
+ LICENSE
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ autobuild.rb
8
+ buildfile
9
+ ext-java/src/main/java/DoPostgresExtService.java
10
+ ext-java/src/main/java/do_postgres/PostgresDriverDefinition.java
11
+ ext/do_postgres_ext/do_postgres_ext.c
12
+ ext/do_postgres_ext/extconf.rb
13
+ lib/do_postgres.rb
14
+ lib/do_postgres/transaction.rb
15
+ lib/do_postgres/version.rb
16
+ script/timezone_spec_runner.rb
17
+ script/timezones.txt
18
+ spec/command_spec.rb
19
+ spec/connection_spec.rb
20
+ spec/reader_spec.rb
21
+ spec/result_spec.rb
22
+ spec/spec.opts
23
+ spec/spec_helper.rb
24
+ spec/typecast/bigdecimal_spec.rb
25
+ spec/typecast/boolean_spec.rb
26
+ spec/typecast/byte_array_spec.rb
27
+ spec/typecast/class_spec.rb
28
+ spec/typecast/date_spec.rb
29
+ spec/typecast/datetime_spec.rb
30
+ spec/typecast/float_spec.rb
31
+ spec/typecast/integer_spec.rb
32
+ spec/typecast/nil_spec.rb
33
+ spec/typecast/string_spec.rb
34
+ spec/typecast/time_spec.rb
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ = do_postgres
2
+
3
+ A PostgreSQL driver for DataObjects
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+
5
+ require 'pathname'
6
+ require 'lib/do_postgres/version'
7
+
8
+ ROOT = Pathname(__FILE__).dirname.expand_path
9
+ JRUBY = RUBY_PLATFORM =~ /java/
10
+ WINDOWS = Gem.win_platform?
11
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
12
+ BINARY_VERSION = '5.0.77'
13
+
14
+ Dir['tasks/*.rake'].each { |f| import f }
15
+
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 ])
@@ -0,0 +1,939 @@
1
+ #include <libpq-fe.h>
2
+ #include <postgres.h>
3
+ #include <mb/pg_wchar.h>
4
+ #include <catalog/pg_type.h>
5
+
6
+ /* Undefine constants Postgres also defines */
7
+ #undef PACKAGE_BUGREPORT
8
+ #undef PACKAGE_NAME
9
+ #undef PACKAGE_STRING
10
+ #undef PACKAGE_TARNAME
11
+ #undef PACKAGE_VERSION
12
+
13
+ #ifdef _WIN32
14
+ /* On Windows this stuff is also defined by Postgres, but we don't
15
+ want to use Postgres' version actually */
16
+ #undef fsync
17
+ #undef vsnprintf
18
+ #undef snprintf
19
+ #undef sprintf
20
+ #undef printf
21
+ #define cCommand_execute cCommand_execute_sync
22
+ #define do_int64 signed __int64
23
+ #else
24
+ #define cCommand_execute cCommand_execute_async
25
+ #define do_int64 signed long long int
26
+ #endif
27
+
28
+ #include <ruby.h>
29
+ #include <string.h>
30
+ #include <math.h>
31
+ #include <ctype.h>
32
+ #include <time.h>
33
+
34
+ #define ID_CONST_GET rb_intern("const_get")
35
+ #define ID_PATH rb_intern("path")
36
+ #define ID_NEW rb_intern("new")
37
+ #define ID_ESCAPE rb_intern("escape_sql")
38
+
39
+ #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
40
+ #define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
41
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
42
+ #define POSTGRES_CLASS(klass, parent) (rb_define_class_under(mPostgres, klass, parent))
43
+ #define DEBUG(value) data_objects_debug(value)
44
+ #define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
45
+
46
+ #ifndef RSTRING_PTR
47
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
48
+ #endif
49
+
50
+ #ifndef RSTRING_LEN
51
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
52
+ #endif
53
+
54
+ #ifndef RARRAY_LEN
55
+ #define RARRAY_LEN(a) RARRAY(a)->len
56
+ #endif
57
+
58
+
59
+ // To store rb_intern values
60
+ static ID ID_NEW_DATE;
61
+ static ID ID_LOGGER;
62
+ static ID ID_DEBUG;
63
+ static ID ID_LEVEL;
64
+ static ID ID_TO_S;
65
+ static ID ID_RATIONAL;
66
+
67
+ static VALUE mExtlib;
68
+ static VALUE mDO;
69
+ static VALUE cDO_Quoting;
70
+ static VALUE cDO_Connection;
71
+ static VALUE cDO_Command;
72
+ static VALUE cDO_Result;
73
+ static VALUE cDO_Reader;
74
+
75
+ static VALUE rb_cDate;
76
+ static VALUE rb_cDateTime;
77
+ static VALUE rb_cBigDecimal;
78
+ static VALUE rb_cByteArray;
79
+
80
+ static VALUE mPostgres;
81
+ static VALUE cConnection;
82
+ static VALUE cCommand;
83
+ static VALUE cResult;
84
+ static VALUE cReader;
85
+
86
+ static VALUE eArgumentError;
87
+ static VALUE ePostgresError;
88
+
89
+ static void data_objects_debug(VALUE string, struct timeval* start) {
90
+ struct timeval stop;
91
+ char *message;
92
+
93
+ char *query = RSTRING_PTR(string);
94
+ int length = RSTRING_LEN(string);
95
+ char total_time[32];
96
+ do_int64 duration = 0;
97
+
98
+ VALUE logger = rb_funcall(mPostgres, ID_LOGGER, 0);
99
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
100
+
101
+ if (0 == log_level) {
102
+ gettimeofday(&stop, NULL);
103
+
104
+ duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
105
+
106
+ snprintf(total_time, 32, "%.6f", duration / 1000000.0);
107
+ message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
108
+ snprintf(message, length + strlen(total_time) + 4, "(%s) %s", total_time, query);
109
+ rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
110
+ }
111
+ }
112
+
113
+ static char * get_uri_option(VALUE query_hash, char * key) {
114
+ VALUE query_value;
115
+ char * value = NULL;
116
+
117
+ if(!rb_obj_is_kind_of(query_hash, rb_cHash)) { return NULL; }
118
+
119
+ query_value = rb_hash_aref(query_hash, RUBY_STRING(key));
120
+
121
+ if (Qnil != query_value) {
122
+ value = StringValuePtr(query_value);
123
+ }
124
+
125
+ return value;
126
+ }
127
+
128
+ /* ====== Time/Date Parsing Helper Functions ====== */
129
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
130
+ do_int64 a, b, c;
131
+ a = *numerator;
132
+ b = *denominator;
133
+ while ( a != 0 ) {
134
+ c = a; a = b % a; b = c;
135
+ }
136
+ *numerator = *numerator / b;
137
+ *denominator = *denominator / b;
138
+ }
139
+
140
+ // Generate the date integer which Date.civil_to_jd returns
141
+ static int jd_from_date(int year, int month, int day) {
142
+ int a, b;
143
+ if ( month <= 2 ) {
144
+ year -= 1;
145
+ month += 12;
146
+ }
147
+ a = year / 100;
148
+ b = 2 - a + (a / 4);
149
+ return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524;
150
+ }
151
+
152
+ static VALUE parse_date(const char *date) {
153
+ int year, month, day;
154
+ int jd, ajd;
155
+ VALUE rational;
156
+
157
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
158
+
159
+ jd = jd_from_date(year, month, day);
160
+
161
+ // Math from Date.jd_to_ajd
162
+ ajd = jd * 2 - 1;
163
+ rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
164
+
165
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
166
+ }
167
+
168
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
169
+ static VALUE seconds_to_offset(do_int64 num) {
170
+ do_int64 den = 86400;
171
+ reduce(&num, &den);
172
+ return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
173
+ }
174
+
175
+ static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
176
+ do_int64 seconds = 0;
177
+
178
+ seconds += hour_offset * 3600;
179
+ seconds += minute_offset * 60;
180
+
181
+ return seconds_to_offset(seconds);
182
+ }
183
+
184
+ static VALUE parse_date_time(const char *date) {
185
+ VALUE ajd, offset;
186
+
187
+ int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
188
+ int jd;
189
+ do_int64 num, den;
190
+
191
+ long int gmt_offset;
192
+ int is_dst;
193
+
194
+ time_t rawtime;
195
+ struct tm * timeinfo;
196
+
197
+ int tokens_read, max_tokens;
198
+
199
+ if (0 != strchr(date, '.')) {
200
+ // This is a datetime with sub-second precision
201
+ tokens_read = sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
202
+ max_tokens = 9;
203
+ } else {
204
+ // This is a datetime second precision
205
+ tokens_read = sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
206
+ max_tokens = 8;
207
+ }
208
+
209
+ if (max_tokens == tokens_read) {
210
+ // We read the Date, Time, and Timezone info
211
+ minute_offset *= hour_offset < 0 ? -1 : 1;
212
+ } else if ((max_tokens - 1) == tokens_read) {
213
+ // We read the Date and Time, but no Minute Offset
214
+ minute_offset = 0;
215
+ } else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
216
+ if (tokens_read == 3) {
217
+ hour = 0;
218
+ min = 0;
219
+ hour_offset = 0;
220
+ minute_offset = 0;
221
+ sec = 0;
222
+ }
223
+ // We read the Date and Time, default to the current locale's offset
224
+
225
+ // Get localtime
226
+ time(&rawtime);
227
+ timeinfo = localtime(&rawtime);
228
+
229
+ is_dst = timeinfo->tm_isdst * 3600;
230
+
231
+ // Reset to GM Time
232
+ timeinfo = gmtime(&rawtime);
233
+
234
+ gmt_offset = mktime(timeinfo) - rawtime;
235
+
236
+ if ( is_dst > 0 )
237
+ gmt_offset -= is_dst;
238
+
239
+ hour_offset = -(gmt_offset / 3600);
240
+ minute_offset = -(gmt_offset % 3600 / 60);
241
+
242
+ } else {
243
+ // Something went terribly wrong
244
+ rb_raise(ePostgresError, "Couldn't parse date: %s", date);
245
+ }
246
+
247
+ jd = jd_from_date(year, month, day);
248
+
249
+ // Generate ajd with fractional days for the time
250
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
251
+ num = (hour * 1440) + (min * 24);
252
+
253
+ // Modify the numerator so when we apply the timezone everything works out
254
+ num -= (hour_offset * 1440) + (minute_offset * 24);
255
+
256
+ den = (24 * 1440);
257
+ reduce(&num, &den);
258
+
259
+ num = (num * 86400) + (sec * den);
260
+ den = den * 86400;
261
+ reduce(&num, &den);
262
+
263
+ num = (jd * den) + num;
264
+
265
+ num = num * 2;
266
+ num = num - den;
267
+ den = den * 2;
268
+
269
+ reduce(&num, &den);
270
+
271
+ ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
272
+ offset = timezone_to_offset(hour_offset, minute_offset);
273
+
274
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
275
+ }
276
+
277
+ static VALUE parse_time(const char *date) {
278
+
279
+ int year, month, day, hour, min, sec, usec, tokens;
280
+ char subsec[7];
281
+
282
+ if (0 != strchr(date, '.')) {
283
+ // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
284
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
285
+ usec = atoi(subsec);
286
+ usec *= pow(10, (6 - strlen(subsec)));
287
+ } else {
288
+ tokens = sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
289
+ if (tokens == 3) {
290
+ hour = 0;
291
+ min = 0;
292
+ sec = 0;
293
+ }
294
+ usec = 0;
295
+ }
296
+
297
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
298
+ }
299
+
300
+ /* ===== Typecasting Functions ===== */
301
+
302
+ static VALUE infer_ruby_type(Oid type) {
303
+ switch(type) {
304
+ case BITOID:
305
+ case VARBITOID:
306
+ case INT2OID:
307
+ case INT4OID:
308
+ case INT8OID:
309
+ return rb_cInteger;
310
+ case FLOAT4OID:
311
+ case FLOAT8OID:
312
+ return rb_cFloat;
313
+ case NUMERICOID:
314
+ case CASHOID:
315
+ return rb_cBigDecimal;
316
+ case BOOLOID:
317
+ return rb_cTrueClass;
318
+ case TIMESTAMPTZOID:
319
+ case TIMESTAMPOID:
320
+ return rb_cDateTime;
321
+ case DATEOID:
322
+ return rb_cDate;
323
+ case BYTEAOID:
324
+ return rb_cByteArray;
325
+ default:
326
+ return rb_cString;
327
+ }
328
+ }
329
+
330
+ static VALUE typecast(const char *value, long length, const VALUE type) {
331
+
332
+ if (type == rb_cInteger) {
333
+ return rb_cstr2inum(value, 10);
334
+ } else if (type == rb_cString) {
335
+ return TAINTED_STRING(value, length);
336
+ } else if (type == rb_cFloat) {
337
+ return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
338
+ } else if (type == rb_cBigDecimal) {
339
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING(value, length));
340
+ } else if (type == rb_cDate) {
341
+ return parse_date(value);
342
+ } else if (type == rb_cDateTime) {
343
+ return parse_date_time(value);
344
+ } else if (type == rb_cTime) {
345
+ return parse_time(value);
346
+ } else if (type == rb_cTrueClass) {
347
+ return *value == 't' ? Qtrue : Qfalse;
348
+ } else if (type == rb_cByteArray) {
349
+ size_t new_length = 0;
350
+ char* unescaped = (char *)PQunescapeBytea((unsigned char*)value, &new_length);
351
+ VALUE byte_array = rb_funcall(rb_cByteArray, ID_NEW, 1, TAINTED_STRING(unescaped, new_length));
352
+ PQfreemem(unescaped);
353
+ return byte_array;
354
+ } else if (type == rb_cClass) {
355
+ return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING(value, length));
356
+ } else if (type == rb_cObject) {
357
+ return rb_marshal_load(rb_str_new2(value));
358
+ } else if (type == rb_cNilClass) {
359
+ return Qnil;
360
+ } else {
361
+ return TAINTED_STRING(value, length);
362
+ }
363
+
364
+ }
365
+
366
+ /* ====== Public API ======= */
367
+ static VALUE cConnection_dispose(VALUE self) {
368
+ VALUE connection_container = rb_iv_get(self, "@connection");
369
+
370
+ PGconn *db;
371
+
372
+ if (Qnil == connection_container)
373
+ return Qfalse;
374
+
375
+ db = DATA_PTR(connection_container);
376
+
377
+ if (NULL == db)
378
+ return Qfalse;
379
+
380
+ PQfinish(db);
381
+ rb_iv_set(self, "@connection", Qnil);
382
+
383
+ return Qtrue;
384
+ }
385
+
386
+ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
387
+ VALUE type_strings = rb_ary_new();
388
+ VALUE array = rb_ary_new();
389
+
390
+ int i, j;
391
+
392
+ for ( i = 0; i < argc; i++) {
393
+ rb_ary_push(array, argv[i]);
394
+ }
395
+
396
+ for (i = 0; i < RARRAY_LEN(array); i++) {
397
+ VALUE entry = rb_ary_entry(array, i);
398
+ if(TYPE(entry) == T_CLASS) {
399
+ rb_ary_push(type_strings, entry);
400
+ } else if (TYPE(entry) == T_ARRAY) {
401
+ for (j = 0; j < RARRAY_LEN(entry); j++) {
402
+ VALUE sub_entry = rb_ary_entry(entry, j);
403
+ if(TYPE(sub_entry) == T_CLASS) {
404
+ rb_ary_push(type_strings, sub_entry);
405
+ } else {
406
+ rb_raise(eArgumentError, "Invalid type given");
407
+ }
408
+ }
409
+ } else {
410
+ rb_raise(eArgumentError, "Invalid type given");
411
+ }
412
+ }
413
+
414
+ rb_iv_set(self, "@field_types", type_strings);
415
+
416
+ return array;
417
+ }
418
+
419
+ static VALUE build_query_from_args(VALUE klass, int count, VALUE *args[]) {
420
+ VALUE query = rb_iv_get(klass, "@text");
421
+
422
+ int i;
423
+ VALUE array = rb_ary_new();
424
+ for ( i = 0; i < count; i++) {
425
+ rb_ary_push(array, (VALUE)args[i]);
426
+ }
427
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
428
+
429
+ return query;
430
+ }
431
+
432
+ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
433
+ PGconn *db = DATA_PTR(rb_iv_get(self, "@connection"));
434
+
435
+ const char *source = RSTRING_PTR(string);
436
+ int source_len = RSTRING_LEN(string);
437
+
438
+ char *escaped;
439
+ int quoted_length = 0;
440
+ VALUE result;
441
+
442
+ // Allocate space for the escaped version of 'string'
443
+ // http://www.postgresql.org/docs/8.3/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING
444
+ escaped = (char *)calloc(source_len * 2 + 3, sizeof(char));
445
+
446
+ // Escape 'source' using the current charset in use on the conection 'db'
447
+ quoted_length = PQescapeStringConn(db, escaped + 1, source, source_len, NULL);
448
+
449
+ // Wrap the escaped string in single-quotes, this is DO's convention
450
+ escaped[quoted_length + 1] = escaped[0] = '\'';
451
+
452
+ result = rb_str_new(escaped, quoted_length + 2);
453
+ free(escaped);
454
+ return result;
455
+ }
456
+
457
+ static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
458
+ PGconn *db = DATA_PTR(rb_iv_get(self, "@connection"));
459
+
460
+ const unsigned char *source = (unsigned char*) RSTRING_PTR(string);
461
+ size_t source_len = RSTRING_LEN(string);
462
+
463
+ unsigned char *escaped;
464
+ unsigned char *escaped_quotes;
465
+ size_t quoted_length = 0;
466
+ VALUE result;
467
+
468
+ // Allocate space for the escaped version of 'string'
469
+ // http://www.postgresql.org/docs/8.3/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING
470
+ escaped = PQescapeByteaConn(db, source, source_len, &quoted_length);
471
+ escaped_quotes = (unsigned char *)calloc(quoted_length + 1, sizeof(unsigned char));
472
+ memcpy(escaped_quotes + 1, escaped, quoted_length);
473
+
474
+ // Wrap the escaped string in single-quotes, this is DO's convention (replace trailing \0)
475
+ escaped_quotes[quoted_length] = escaped_quotes[0] = '\'';
476
+
477
+ result = TAINTED_STRING((char*)escaped_quotes, quoted_length + 1);
478
+ PQfreemem(escaped);
479
+ free(escaped_quotes);
480
+ return result;
481
+ }
482
+
483
+ #ifdef _WIN32
484
+ static PGresult* cCommand_execute_sync(PGconn *db, VALUE query) {
485
+ PGresult *response;
486
+ struct timeval start;
487
+ char* str = StringValuePtr(query);
488
+
489
+ while ((response = PQgetResult(db)) != NULL) {
490
+ PQclear(response);
491
+ }
492
+
493
+ gettimeofday(&start, NULL);
494
+
495
+ response = PQexec(db, str);
496
+
497
+ if (response == NULL) {
498
+ if(PQstatus(db) != CONNECTION_OK) {
499
+ PQreset(db);
500
+ if (PQstatus(db) == CONNECTION_OK) {
501
+ response = PQexec(db, str);
502
+ }
503
+ }
504
+
505
+ if(response == NULL) {
506
+ rb_raise(ePostgresError, PQerrorMessage(db));
507
+ }
508
+ }
509
+
510
+ data_objects_debug(query, &start);
511
+ return response;
512
+ }
513
+ #else
514
+ static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
515
+ int socket_fd;
516
+ int retval;
517
+ fd_set rset;
518
+ PGresult *response;
519
+ struct timeval start;
520
+ char* str = StringValuePtr(query);
521
+
522
+ while ((response = PQgetResult(db)) != NULL) {
523
+ PQclear(response);
524
+ }
525
+
526
+ retval = PQsendQuery(db, str);
527
+
528
+ if (!retval) {
529
+ if(PQstatus(db) != CONNECTION_OK) {
530
+ PQreset(db);
531
+ if (PQstatus(db) == CONNECTION_OK) {
532
+ retval = PQsendQuery(db, str);
533
+ }
534
+ }
535
+
536
+ if(!retval) {
537
+ rb_raise(ePostgresError, PQerrorMessage(db));
538
+ }
539
+ }
540
+
541
+ gettimeofday(&start, NULL);
542
+ socket_fd = PQsocket(db);
543
+
544
+ for(;;) {
545
+ FD_ZERO(&rset);
546
+ FD_SET(socket_fd, &rset);
547
+ retval = rb_thread_select(socket_fd + 1, &rset, NULL, NULL, NULL);
548
+ if (retval < 0) {
549
+ rb_sys_fail(0);
550
+ }
551
+
552
+ if (retval == 0) {
553
+ continue;
554
+ }
555
+
556
+ if (PQconsumeInput(db) == 0) {
557
+ rb_raise(ePostgresError, PQerrorMessage(db));
558
+ }
559
+
560
+ if (PQisBusy(db) == 0) {
561
+ break;
562
+ }
563
+ }
564
+
565
+ data_objects_debug(query, &start);
566
+ return PQgetResult(db);
567
+ }
568
+ #endif
569
+
570
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
571
+ PGresult *result = NULL;
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";
581
+
582
+ PGconn *db;
583
+
584
+ r_host = rb_funcall(uri, rb_intern("host"), 0);
585
+ if ( Qnil != r_host ) {
586
+ host = StringValuePtr(r_host);
587
+ }
588
+
589
+ r_user = rb_funcall(uri, rb_intern("user"), 0);
590
+ if (Qnil != r_user) {
591
+ user = StringValuePtr(r_user);
592
+ }
593
+
594
+ r_password = rb_funcall(uri, rb_intern("password"), 0);
595
+ if (Qnil != r_password) {
596
+ password = StringValuePtr(r_password);
597
+ }
598
+
599
+ r_path = rb_funcall(uri, rb_intern("path"), 0);
600
+ path = StringValuePtr(r_path);
601
+ if (Qnil != r_path) {
602
+ database = strtok(path, "/");
603
+ }
604
+
605
+ if (NULL == database || 0 == strlen(database)) {
606
+ rb_raise(ePostgresError, "Database must be specified");
607
+ }
608
+
609
+ r_port = rb_funcall(uri, rb_intern("port"), 0);
610
+ if (Qnil != r_port) {
611
+ r_port = rb_funcall(r_port, rb_intern("to_s"), 0);
612
+ port = StringValuePtr(r_port);
613
+ }
614
+
615
+ // Pull the querystring off the URI
616
+ r_query = rb_funcall(uri, rb_intern("query"), 0);
617
+
618
+ search_path = get_uri_option(r_query, "search_path");
619
+
620
+ db = PQsetdbLogin(
621
+ host,
622
+ port,
623
+ NULL,
624
+ NULL,
625
+ database,
626
+ user,
627
+ password
628
+ );
629
+
630
+ if ( PQstatus(db) == CONNECTION_BAD ) {
631
+ rb_raise(ePostgresError, PQerrorMessage(db));
632
+ }
633
+
634
+ if (search_path != NULL) {
635
+ search_path_query = (char *)calloc(256, sizeof(char));
636
+ snprintf(search_path_query, 256, "set search_path to %s;", search_path);
637
+ r_query = rb_str_new2(search_path_query);
638
+ result = cCommand_execute(db, r_query);
639
+
640
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
641
+ free(search_path_query);
642
+ rb_raise(ePostgresError, PQresultErrorMessage(result));
643
+ }
644
+
645
+ free(search_path_query);
646
+ }
647
+
648
+ r_options = rb_str_new2(backslash_off);
649
+ result = cCommand_execute(db, r_options);
650
+
651
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
652
+ rb_warn(PQresultErrorMessage(result));
653
+ }
654
+
655
+ r_options = rb_str_new2(standard_strings_on);
656
+ result = cCommand_execute(db, r_options);
657
+
658
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
659
+ rb_warn(PQresultErrorMessage(result));
660
+ }
661
+
662
+ r_options = rb_str_new2(warning_messages);
663
+ result = cCommand_execute(db, r_options);
664
+
665
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
666
+ rb_warn(PQresultErrorMessage(result));
667
+ }
668
+
669
+ encoding = get_uri_option(r_query, "encoding");
670
+ if (!encoding) { encoding = get_uri_option(r_query, "charset"); }
671
+ if (!encoding) { encoding = "utf8"; }
672
+
673
+ #ifdef HAVE_PQSETCLIENTENCODING
674
+ if(PQsetClientEncoding(db, encoding)) {
675
+ rb_raise(ePostgresError, "Couldn't set encoding: %s", encoding);
676
+ }
677
+ #endif
678
+
679
+ rb_iv_set(self, "@uri", uri);
680
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
681
+
682
+ return Qtrue;
683
+ }
684
+
685
+ static VALUE cConnection_character_set(VALUE self) {
686
+ VALUE connection_container = rb_iv_get(self, "@connection");
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);
699
+ }
700
+
701
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
702
+ VALUE connection = rb_iv_get(self, "@connection");
703
+ VALUE postgres_connection = rb_iv_get(connection, "@connection");
704
+ if (Qnil == postgres_connection) {
705
+ rb_raise(ePostgresError, "This connection has already been closed.");
706
+ }
707
+
708
+ PGconn *db = DATA_PTR(postgres_connection);
709
+ PGresult *response;
710
+ int status;
711
+
712
+ VALUE affected_rows = Qnil;
713
+ VALUE insert_id = Qnil;
714
+
715
+ VALUE query = build_query_from_args(self, argc, argv);
716
+
717
+ response = cCommand_execute(db, query);
718
+
719
+ status = PQresultStatus(response);
720
+
721
+ if ( status == PGRES_TUPLES_OK ) {
722
+ insert_id = INT2NUM(atoi(PQgetvalue(response, 0, 0)));
723
+ affected_rows = INT2NUM(atoi(PQcmdTuples(response)));
724
+ }
725
+ else if ( status == PGRES_COMMAND_OK ) {
726
+ insert_id = Qnil;
727
+ affected_rows = INT2NUM(atoi(PQcmdTuples(response)));
728
+ }
729
+ else {
730
+ char *message = PQresultErrorMessage(response);
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));
734
+ }
735
+
736
+ PQclear(response);
737
+
738
+ return rb_funcall(cResult, ID_NEW, 3, self, affected_rows, insert_id);
739
+ }
740
+
741
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
742
+ VALUE reader, query;
743
+ VALUE field_names, field_types;
744
+
745
+ int i;
746
+ int field_count;
747
+ int infer_types = 0;
748
+
749
+ VALUE connection = rb_iv_get(self, "@connection");
750
+ VALUE postgres_connection = rb_iv_get(connection, "@connection");
751
+ if (Qnil == postgres_connection) {
752
+ rb_raise(ePostgresError, "This connection has already been closed.");
753
+ }
754
+
755
+ PGconn *db = DATA_PTR(postgres_connection);
756
+ PGresult *response;
757
+
758
+ query = build_query_from_args(self, argc, argv);
759
+
760
+ response = cCommand_execute(db, query);
761
+
762
+ if ( PQresultStatus(response) != PGRES_TUPLES_OK ) {
763
+ char *message = PQresultErrorMessage(response);
764
+ PQclear(response);
765
+ rb_raise(ePostgresError, "%sQuery: %s\n", message, StringValuePtr(query));
766
+ }
767
+
768
+ field_count = PQnfields(response);
769
+
770
+ reader = rb_funcall(cReader, ID_NEW, 0);
771
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
772
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
773
+ rb_iv_set(reader, "@row_count", INT2NUM(PQntuples(response)));
774
+
775
+ field_names = rb_ary_new();
776
+ field_types = rb_iv_get(self, "@field_types");
777
+
778
+ if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
779
+ field_types = rb_ary_new();
780
+ infer_types = 1;
781
+ } else if (RARRAY_LEN(field_types) != field_count) {
782
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
783
+ // and error
784
+ rb_funcall(reader, rb_intern("close"), 0);
785
+ rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
786
+ }
787
+
788
+ for ( i = 0; i < field_count; i++ ) {
789
+ rb_ary_push(field_names, rb_str_new2(PQfname(response, i)));
790
+ if ( infer_types == 1 ) {
791
+ rb_ary_push(field_types, infer_ruby_type(PQftype(response, i)));
792
+ }
793
+ }
794
+
795
+ rb_iv_set(reader, "@position", INT2NUM(0));
796
+ rb_iv_set(reader, "@fields", field_names);
797
+ rb_iv_set(reader, "@field_types", field_types);
798
+
799
+ return reader;
800
+ }
801
+
802
+ static VALUE cReader_close(VALUE self) {
803
+ VALUE reader_container = rb_iv_get(self, "@reader");
804
+
805
+ PGresult *reader;
806
+
807
+ if (Qnil == reader_container)
808
+ return Qfalse;
809
+
810
+ reader = DATA_PTR(reader_container);
811
+
812
+ if (NULL == reader)
813
+ return Qfalse;
814
+
815
+ PQclear(reader);
816
+ rb_iv_set(self, "@reader", Qnil);
817
+ return Qtrue;
818
+ }
819
+
820
+ static VALUE cReader_next(VALUE self) {
821
+ PGresult *reader = DATA_PTR(rb_iv_get(self, "@reader"));
822
+
823
+ int field_count;
824
+ int row_count;
825
+ int i;
826
+ int position;
827
+
828
+ VALUE array = rb_ary_new();
829
+ VALUE field_types, field_type;
830
+ VALUE value;
831
+
832
+ row_count = NUM2INT(rb_iv_get(self, "@row_count"));
833
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
834
+ field_types = rb_iv_get(self, "@field_types");
835
+ position = NUM2INT(rb_iv_get(self, "@position"));
836
+
837
+ if ( position > (row_count-1) ) {
838
+ rb_iv_set(self, "@values", Qnil);
839
+ return Qfalse;
840
+ }
841
+
842
+ for ( i = 0; i < field_count; i++ ) {
843
+ field_type = rb_ary_entry(field_types, i);
844
+
845
+ // Always return nil if the value returned from Postgres is null
846
+ if (!PQgetisnull(reader, position, i)) {
847
+ value = typecast(PQgetvalue(reader, position, i), PQgetlength(reader, position, i), field_type);
848
+ } else {
849
+ value = Qnil;
850
+ }
851
+
852
+ rb_ary_push(array, value);
853
+ }
854
+
855
+ rb_iv_set(self, "@values", array);
856
+ rb_iv_set(self, "@position", INT2NUM(position+1));
857
+
858
+ return Qtrue;
859
+ }
860
+
861
+ static VALUE cReader_values(VALUE self) {
862
+
863
+ VALUE values = rb_iv_get(self, "@values");
864
+ if(values == Qnil) {
865
+ rb_raise(ePostgresError, "Reader not initialized");
866
+ return Qnil;
867
+ } else {
868
+ return values;
869
+ }
870
+ }
871
+
872
+ static VALUE cReader_fields(VALUE self) {
873
+ return rb_iv_get(self, "@fields");
874
+ }
875
+
876
+ static VALUE cReader_field_count(VALUE self) {
877
+ return rb_iv_get(self, "@field_count");
878
+ }
879
+
880
+ void Init_do_postgres_ext() {
881
+ rb_require("date");
882
+ rb_require("bigdecimal");
883
+
884
+ // Get references classes needed for Date/Time parsing
885
+ rb_cDate = CONST_GET(rb_mKernel, "Date");
886
+ rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
887
+ rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
888
+
889
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
890
+
891
+ #ifdef RUBY_LESS_THAN_186
892
+ ID_NEW_DATE = rb_intern("new0");
893
+ #else
894
+ ID_NEW_DATE = rb_intern("new!");
895
+ #endif
896
+ ID_LOGGER = rb_intern("logger");
897
+ ID_DEBUG = rb_intern("debug");
898
+ ID_LEVEL = rb_intern("level");
899
+ ID_TO_S = rb_intern("to_s");
900
+ ID_RATIONAL = rb_intern("Rational");
901
+
902
+ // Get references to the Extlib module
903
+ mExtlib = CONST_GET(rb_mKernel, "Extlib");
904
+ rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
905
+
906
+ // Get references to the DataObjects module and its classes
907
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
908
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
909
+ cDO_Connection = CONST_GET(mDO, "Connection");
910
+ cDO_Command = CONST_GET(mDO, "Command");
911
+ cDO_Result = CONST_GET(mDO, "Result");
912
+ cDO_Reader = CONST_GET(mDO, "Reader");
913
+
914
+ eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
915
+ mPostgres = rb_define_module_under(mDO, "Postgres");
916
+ ePostgresError = rb_define_class("PostgresError", rb_eStandardError);
917
+
918
+ cConnection = POSTGRES_CLASS("Connection", cDO_Connection);
919
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
920
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
921
+ rb_define_method(cConnection, "character_set", cConnection_character_set , 0);
922
+ rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
923
+ rb_define_method(cConnection, "quote_byte_array", cConnection_quote_byte_array, 1);
924
+
925
+ cCommand = POSTGRES_CLASS("Command", cDO_Command);
926
+ rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
927
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
928
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
929
+
930
+ cResult = POSTGRES_CLASS("Result", cDO_Result);
931
+
932
+ cReader = POSTGRES_CLASS("Reader", cDO_Reader);
933
+ rb_define_method(cReader, "close", cReader_close, 0);
934
+ rb_define_method(cReader, "next!", cReader_next, 0);
935
+ rb_define_method(cReader, "values", cReader_values, 0);
936
+ rb_define_method(cReader, "fields", cReader_fields, 0);
937
+ rb_define_method(cReader, "field_count", cReader_field_count, 0);
938
+
939
+ }