sequel_pg 1.9.0 → 1.10.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7e56aa87f96c34298bba6a14bac49f79938415c9d9d8546b5621ac82c45161a
4
- data.tar.gz: 85a21e58594078a962344e2308d6ae4b860de0b3af34a99be4ad93cdb39f4163
3
+ metadata.gz: d5e59a1152aaf6b21d44b1d57ce4653b6248a61db684a443bcdbbb86fb7c7ff8
4
+ data.tar.gz: 5f3392deba2a7286b6fa24a8c3b93ea8ee362f027c6df75a55574910b62a4c7c
5
5
  SHA512:
6
- metadata.gz: 81ee2f7a944480110a254dbf6517e1b1d6eb64993c6e20f6425eab84e9f833213004c02857148bc36782bcef0862bacd17e8b12a7aa76e2f09b9c9aabe1c73dc
7
- data.tar.gz: 5ff9e1e8750040e82c2cc9157d12f9c4fcf927f2dbb4576483b14d417bb5f65b45cacbd2c588ddc3db9ab5f0eca114f83b69455609e0bf8b6c92ec1b74b7423b
6
+ metadata.gz: 04d3ac1ba009d05ba2dd46d32865926a79a3cae63ef3f29a9d26d5874409fedfc54e6855a0a9ea9e7dd4dd9ef5e8603bda134368e040d9e3858f00b063ca8363
7
+ data.tar.gz: 0d147bca275b89d70255430fdadf4c7afc0a83a5f48414a656c1b583297ac05dea61b0d03d43a78b6bcff31f27adc8d62462e98de4228348171131f4e71144be
data/CHANGELOG CHANGED
@@ -1,3 +1,23 @@
1
+ === 1.10.0 (2018-06-25)
2
+
3
+ * Add native inet/cidr parsers (jeremyevans)
4
+
5
+ * Don't leak memory if unable to create a Sequel::SQL::Blob string when parsing bytea (jeremyevans)
6
+
7
+ * Improve performance of bytea parsing (jeremyevans)
8
+
9
+ * Drop Sequel <4.38.0 support (jeremyevans)
10
+
11
+ * Respect Sequel.application_timezone setting when parsing values for time and timetz columns (jeremyevans)
12
+
13
+ * Respect Sequel::SQLTime.date setting when parsing values for time and timetz columns (jeremyevans)
14
+
15
+ * Improve performance of time parsing (jeremyevans)
16
+
17
+ * Improve performance of date parsing (jeremyevans)
18
+
19
+ * Improve performance of timestamp parsing by borrowing and optimizing ruby-pg's parser (jeremyevans)
20
+
1
21
  === 1.9.0 (2018-06-06)
2
22
 
3
23
  * Return arrays of common data types as PGArray instances automatically with much improved performance (jeremyevans)
@@ -6,6 +6,7 @@ dir_config('pg', ENV["POSTGRES_INCLUDE"] || (IO.popen("pg_config --includedir").
6
6
 
7
7
  if (have_library('pq') || have_library('libpq') || have_library('ms/libpq')) && have_header('libpq-fe.h')
8
8
  have_func 'PQsetSingleRowMode'
9
+ have_func 'timegm'
9
10
  create_makefile("sequel_pg")
10
11
  else
11
12
  puts 'Could not find PostgreSQL build environment (libraries & headers): Makefile not created'
@@ -1,11 +1,18 @@
1
- #define SEQUEL_PG_VERSION_INTEGER 10900
1
+ #define SEQUEL_PG_VERSION_INTEGER 11000
2
2
 
3
3
  #include <string.h>
4
4
  #include <stdio.h>
5
5
  #include <math.h>
6
6
  #include <libpq-fe.h>
7
7
  #include <ruby.h>
8
+ #include <ctype.h>
9
+ #include <sys/types.h>
10
+ #include <time.h>
11
+ #include <arpa/inet.h>
12
+ #include <sys/socket.h>
13
+ #include <string.h>
8
14
 
15
+ #include <ruby/version.h>
9
16
  #include <ruby/encoding.h>
10
17
 
11
18
  #ifndef SPG_MAX_FIELDS
@@ -16,11 +23,36 @@
16
23
 
17
24
  #define SPG_DT_ADD_USEC if (usec != 0) { dt = rb_funcall(dt, spg_id_op_plus, 1, rb_Rational2(INT2NUM(usec), spg_usec_per_day)); }
18
25
 
19
- #define SPG_NO_TZ 0
20
- #define SPG_DB_LOCAL 1
21
- #define SPG_DB_UTC 2
22
- #define SPG_APP_LOCAL 4
23
- #define SPG_APP_UTC 8
26
+ #ifndef RARRAY_AREF
27
+ #define RARRAY_AREF(a, i) (RARRAY_PTR(a)[i])
28
+ #endif
29
+
30
+ #define ntohll(c) ((uint64_t)( \
31
+ (((uint64_t)(*((unsigned char*)(c)+0)))<<56LL) | \
32
+ (((uint64_t)(*((unsigned char*)(c)+1)))<<48LL) | \
33
+ (((uint64_t)(*((unsigned char*)(c)+2)))<<40LL) | \
34
+ (((uint64_t)(*((unsigned char*)(c)+3)))<<32LL) | \
35
+ (((uint64_t)(*((unsigned char*)(c)+4)))<<24LL) | \
36
+ (((uint64_t)(*((unsigned char*)(c)+5)))<<16LL) | \
37
+ (((uint64_t)(*((unsigned char*)(c)+6)))<< 8LL) | \
38
+ (((uint64_t)(*((unsigned char*)(c)+7))) ) \
39
+ ))
40
+
41
+ #define SPG_DB_LOCAL (1)
42
+ #define SPG_DB_UTC (1<<1)
43
+ #define SPG_DB_CUSTOM (1<<2)
44
+ #define SPG_APP_LOCAL (1<<3)
45
+ #define SPG_APP_UTC (1<<4)
46
+ #define SPG_APP_CUSTOM (1<<5)
47
+ #define SPG_TZ_INITIALIZED (1<<6)
48
+ #define SPG_USE_TIME (1<<7)
49
+ #define SPG_HAS_TIMEZONE (1<<8)
50
+
51
+ #define SPG_YEAR_SHIFT 16
52
+ #define SPG_MONTH_SHIFT 8
53
+ #define SPG_MONTH_MASK 0x0000ffff
54
+ #define SPG_DAY_MASK 0x0000001f
55
+ #define SPG_TIME_UTC 32
24
56
 
25
57
  #define SPG_YIELD_NORMAL 0
26
58
  #define SPG_YIELD_COLUMN 1
@@ -37,17 +69,24 @@
37
69
  #define SPG_YIELD_KMV_HASH_GROUPS 12
38
70
  #define SPG_YIELD_MKMV_HASH_GROUPS 13
39
71
 
40
- /* External functions defined by ruby-pg when data objects are structs */
72
+ /* External functions defined by ruby-pg */
41
73
  PGconn* pg_get_pgconn(VALUE);
42
74
  PGresult* pgresult_get(VALUE);
43
75
 
76
+ static int spg_use_ipaddr_alloc;
77
+
44
78
  static VALUE spg_Sequel;
45
79
  static VALUE spg_PGArray;
46
80
  static VALUE spg_Blob;
81
+ static VALUE spg_Blob_instance;
47
82
  static VALUE spg_Kernel;
48
83
  static VALUE spg_Date;
84
+ static VALUE spg_DateTime;
49
85
  static VALUE spg_SQLTime;
50
86
  static VALUE spg_PGError;
87
+ static VALUE spg_IPAddr;
88
+ static VALUE spg_vmasks4;
89
+ static VALUE spg_vmasks6;
51
90
 
52
91
  static VALUE spg_sym_utc;
53
92
  static VALUE spg_sym_local;
@@ -86,6 +125,8 @@ static VALUE spg_sym_cid;
86
125
  static VALUE spg_sym_name;
87
126
  static VALUE spg_sym_tid;
88
127
  static VALUE spg_sym_int2vector;
128
+ static VALUE spg_sym_inet;
129
+ static VALUE spg_sym_cidr;
89
130
 
90
131
  static VALUE spg_nan;
91
132
  static VALUE spg_pos_inf;
@@ -94,6 +135,7 @@ static VALUE spg_usec_per_day;
94
135
 
95
136
  static ID spg_id_BigDecimal;
96
137
  static ID spg_id_new;
138
+ static ID spg_id_date;
97
139
  static ID spg_id_local;
98
140
  static ID spg_id_year;
99
141
  static ID spg_id_month;
@@ -123,12 +165,23 @@ static ID spg_id_columns;
123
165
  static ID spg_id_encoding;
124
166
  static ID spg_id_values;
125
167
 
168
+ static ID spg_id_lshift;
169
+ static ID spg_id_mask;
170
+ static ID spg_id_family;
171
+ static ID spg_id_addr;
172
+ static ID spg_id_mask_addr;
173
+
126
174
  #if HAVE_PQSETSINGLEROWMODE
127
175
  static ID spg_id_get_result;
128
176
  static ID spg_id_clear;
129
177
  static ID spg_id_check;
130
178
  #endif
131
179
 
180
+ struct spg_blob_initialization {
181
+ char *blob_string;
182
+ size_t length;
183
+ };
184
+
132
185
  static int enc_get_index(VALUE val) {
133
186
  int i = ENCODING_GET_INLINED(val);
134
187
  if (i == ENCODING_INLINE_MAX) {
@@ -138,68 +191,68 @@ static int enc_get_index(VALUE val) {
138
191
  }
139
192
 
140
193
  #define PG_ENCODING_SET_NOCHECK(obj,i) \
141
- do { \
142
- if ((i) < ENCODING_INLINE_MAX) \
143
- ENCODING_SET_INLINED((obj), (i)); \
144
- else \
145
- rb_enc_set_index((obj), (i)); \
146
- } while(0)
194
+ do { \
195
+ if ((i) < ENCODING_INLINE_MAX) \
196
+ ENCODING_SET_INLINED((obj), (i)); \
197
+ else \
198
+ rb_enc_set_index((obj), (i)); \
199
+ } while(0)
147
200
 
148
201
  static VALUE
149
202
  pg_text_dec_integer(char *val, int len)
150
203
  {
151
- long i;
152
- int max_len;
153
-
154
- if( sizeof(i) >= 8 && FIXNUM_MAX >= 1000000000000000000LL ){
155
- /* 64 bit system can safely handle all numbers up to 18 digits as Fixnum */
156
- max_len = 18;
157
- } else if( sizeof(i) >= 4 && FIXNUM_MAX >= 1000000000LL ){
158
- /* 32 bit system can safely handle all numbers up to 9 digits as Fixnum */
159
- max_len = 9;
160
- } else {
161
- /* unknown -> don't use fast path for int conversion */
162
- max_len = 0;
163
- }
164
-
165
- if( len <= max_len ){
166
- /* rb_cstr2inum() seems to be slow, so we do the int conversion by hand.
167
- * This proved to be 40% faster by the following benchmark:
168
- *
169
- * conn.type_mapping_for_results = PG::BasicTypeMapForResults.new conn
170
- * Benchmark.measure do
171
- * conn.exec("select generate_series(1,1000000)").values }
172
- * end
173
- */
174
- char *val_pos = val;
175
- char digit = *val_pos;
176
- int neg;
177
- int error = 0;
178
-
179
- if( digit=='-' ){
180
- neg = 1;
181
- i = 0;
182
- }else if( digit>='0' && digit<='9' ){
183
- neg = 0;
184
- i = digit - '0';
185
- } else {
186
- error = 1;
187
- }
188
-
189
- while (!error && (digit=*++val_pos)) {
190
- if( digit>='0' && digit<='9' ){
191
- i = i * 10 + (digit - '0');
192
- } else {
193
- error = 1;
194
- }
195
- }
196
-
197
- if( !error ){
198
- return LONG2FIX(neg ? -i : i);
199
- }
200
- }
201
- /* Fallback to ruby method if number too big or unrecognized. */
202
- return rb_cstr2inum(val, 10);
204
+ long i;
205
+ int max_len;
206
+
207
+ if( sizeof(i) >= 8 && FIXNUM_MAX >= 1000000000000000000LL ){
208
+ /* 64 bit system can safely handle all numbers up to 18 digits as Fixnum */
209
+ max_len = 18;
210
+ } else if( sizeof(i) >= 4 && FIXNUM_MAX >= 1000000000LL ){
211
+ /* 32 bit system can safely handle all numbers up to 9 digits as Fixnum */
212
+ max_len = 9;
213
+ } else {
214
+ /* unknown -> don't use fast path for int conversion */
215
+ max_len = 0;
216
+ }
217
+
218
+ if( len <= max_len ){
219
+ /* rb_cstr2inum() seems to be slow, so we do the int conversion by hand.
220
+ * This proved to be 40% faster by the following benchmark:
221
+ *
222
+ * conn.type_mapping_for_results = PG::BasicTypeMapForResults.new conn
223
+ * Benchmark.measure do
224
+ * conn.exec("select generate_series(1,1000000)").values }
225
+ * end
226
+ */
227
+ char *val_pos = val;
228
+ char digit = *val_pos;
229
+ int neg;
230
+ int error = 0;
231
+
232
+ if( digit=='-' ){
233
+ neg = 1;
234
+ i = 0;
235
+ }else if( digit>='0' && digit<='9' ){
236
+ neg = 0;
237
+ i = digit - '0';
238
+ } else {
239
+ error = 1;
240
+ }
241
+
242
+ while (!error && (digit=*++val_pos)) {
243
+ if( digit>='0' && digit<='9' ){
244
+ i = i * 10 + (digit - '0');
245
+ } else {
246
+ error = 1;
247
+ }
248
+ }
249
+
250
+ if( !error ){
251
+ return LONG2FIX(neg ? -i : i);
252
+ }
253
+ }
254
+ /* Fallback to ruby method if number too big or unrecognized. */
255
+ return rb_cstr2inum(val, 10);
203
256
  }
204
257
 
205
258
  static VALUE spg__array_col_value(char *v, size_t length, VALUE converter, int enc_index, int oid, VALUE db);
@@ -349,43 +402,108 @@ static VALUE parse_pg_array(VALUE self, VALUE pg_array_string, VALUE converter)
349
402
  Qnil);
350
403
  }
351
404
 
352
- static VALUE spg_time(const char *s) {
353
- VALUE now;
354
- int hour, minute, second, tokens, i;
355
- char subsec[7];
405
+ static VALUE spg_timestamp_error(const char *s, VALUE self, const char *error_msg) {
406
+ self = rb_funcall(self, spg_id_db, 0);
407
+ if(RTEST(rb_funcall(self, spg_id_convert_infinite_timestamps, 0))) {
408
+ if((strcmp(s, "infinity") == 0) || (strcmp(s, "-infinity") == 0)) {
409
+ return rb_funcall(self, spg_id_infinite_timestamp_value, 1, rb_tainted_str_new2(s));
410
+ }
411
+ }
412
+ rb_raise(rb_eArgError, "%s", error_msg);
413
+ }
414
+
415
+ static inline int char_to_digit(char c)
416
+ {
417
+ return c - '0';
418
+ }
419
+
420
+ static int str4_to_int(const char *str)
421
+ {
422
+ return char_to_digit(str[0]) * 1000
423
+ + char_to_digit(str[1]) * 100
424
+ + char_to_digit(str[2]) * 10
425
+ + char_to_digit(str[3]);
426
+ }
427
+
428
+ static int str2_to_int(const char *str)
429
+ {
430
+ return char_to_digit(str[0]) * 10
431
+ + char_to_digit(str[1]);
432
+ }
433
+
434
+ static VALUE spg_time(const char *p, size_t length, int current) {
435
+ int hour, minute, second, i;
356
436
  int usec = 0;
437
+ ID meth = spg_id_local;
438
+
439
+ if (length < 8) {
440
+ rb_raise(rb_eArgError, "unexpected time format, too short");
441
+ }
442
+
443
+ if (p[2] == ':' && p[5] == ':') {
444
+ hour = str2_to_int(p);
445
+ minute = str2_to_int(p+3);
446
+ second = str2_to_int(p+6);
447
+ p += 8;
357
448
 
358
- tokens = sscanf(s, "%2d:%2d:%2d.%6s", &hour, &minute, &second, subsec);
359
- if(tokens == 4) {
360
- for(i=0; i<6; i++) {
361
- if(subsec[i] == '-') {
362
- subsec[i] = '\0';
449
+ if (length >= 10 && p[0] == '.') {
450
+ static const int coef[6] = { 100000, 10000, 1000, 100, 10, 1 };
451
+
452
+ p++;
453
+ for (i = 0; i < 6 && isdigit(*p); i++) {
454
+ usec += coef[i] * char_to_digit(*p++);
363
455
  }
364
456
  }
365
- usec = atoi(subsec);
366
- usec *= (int) pow(10, (6 - strlen(subsec)));
367
- } else if(tokens < 3) {
457
+ } else {
368
458
  rb_raise(rb_eArgError, "unexpected time format");
369
459
  }
370
460
 
371
- now = rb_funcall(spg_SQLTime, spg_id_new, 0);
372
- return rb_funcall(spg_SQLTime, spg_id_local, 7, rb_funcall(now, spg_id_year, 0), rb_funcall(now, spg_id_month, 0), rb_funcall(now, spg_id_day, 0), INT2NUM(hour), INT2NUM(minute), INT2NUM(second), INT2NUM(usec));
461
+ if (current & SPG_TIME_UTC) {
462
+ meth = spg_id_utc;
463
+ }
464
+ return rb_funcall(spg_SQLTime, meth, 7,
465
+ INT2NUM(current >> SPG_YEAR_SHIFT),
466
+ INT2NUM((current & SPG_MONTH_MASK) >> SPG_MONTH_SHIFT),
467
+ INT2NUM(current & SPG_DAY_MASK),
468
+ INT2NUM(hour),
469
+ INT2NUM(minute),
470
+ INT2NUM(second),
471
+ INT2NUM(usec));
373
472
  }
374
473
 
375
- static VALUE spg_timestamp_error(const char *s, VALUE self, const char *error_msg) {
376
- self = rb_funcall(self, spg_id_db, 0);
377
- if(RTEST(rb_funcall(self, spg_id_convert_infinite_timestamps, 0))) {
378
- if((strcmp(s, "infinity") == 0) || (strcmp(s, "-infinity") == 0)) {
379
- return rb_funcall(self, spg_id_infinite_timestamp_value, 1, rb_tainted_str_new2(s));
380
- }
474
+ /* Caller should check length is at least 4 */
475
+ static int parse_year(const char **str, size_t *length) {
476
+ int year, i;
477
+ size_t remaining = *length;
478
+ const char * p = *str;
479
+
480
+ year = str4_to_int(p);
481
+ p += 4;
482
+ remaining -= 4;
483
+
484
+ for(i = 0; isdigit(*p) && i < 3; i++, p++, remaining--) {
485
+ year = 10 * year + char_to_digit(*p);
381
486
  }
382
- rb_raise(rb_eArgError, "%s", error_msg);
487
+
488
+ *str = p;
489
+ *length = remaining;
490
+ return year;
383
491
  }
384
492
 
385
- static VALUE spg_date(const char *s, VALUE self) {
493
+ static VALUE spg_date(const char *s, VALUE self, size_t length) {
386
494
  int year, month, day;
495
+ const char *p = s;
387
496
 
388
- if(3 != sscanf(s, "%d-%2d-%2d", &year, &month, &day)) {
497
+ if (length < 10) {
498
+ return spg_timestamp_error(s, self, "unexpected date format, too short");
499
+ }
500
+
501
+ year = parse_year(&p, &length);
502
+
503
+ if (length >= 5 && p[0] == '-' && p[3] == '-') {
504
+ month = str2_to_int(p+1);
505
+ day = str2_to_int(p+4);
506
+ } else {
389
507
  return spg_timestamp_error(s, self, "unexpected date format");
390
508
  }
391
509
 
@@ -397,85 +515,148 @@ static VALUE spg_date(const char *s, VALUE self) {
397
515
  return rb_funcall(spg_Date, spg_id_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
398
516
  }
399
517
 
400
- static VALUE spg_timestamp(const char *s, VALUE self) {
401
- VALUE dtc, dt, rtz, db;
402
- int tz = SPG_NO_TZ;
403
- int year, month, day, hour, min, sec, usec, tokens, utc_offset, len;
404
- int usec_start, usec_stop;
518
+ static VALUE spg_timestamp(const char *s, VALUE self, size_t length, int tz) {
519
+ VALUE dt;
520
+ int year, month, day, hour, min, sec, utc_offset;
405
521
  char offset_sign = 0;
406
- int offset_hour = 0;
407
- int offset_minute = 0;
408
522
  int offset_seconds = 0;
409
- double offset_fraction = 0.0;
523
+ int usec = 0;
524
+ int i;
525
+ const char *p = s;
526
+ size_t remaining = length;
410
527
 
411
- db = rb_funcall(self, spg_id_db, 0);
412
- rtz = rb_funcall(db, spg_id_timezone, 0);
413
- if (rtz != Qnil) {
414
- if (rtz == spg_sym_local) {
415
- tz += SPG_DB_LOCAL;
416
- } else if (rtz == spg_sym_utc) {
417
- tz += SPG_DB_UTC;
418
- } else {
419
- return rb_funcall(db, spg_id_to_application_timestamp, 1, rb_str_new2(s));
420
- }
528
+ if (tz & SPG_DB_CUSTOM || tz & SPG_APP_CUSTOM) {
529
+ return rb_funcall(rb_funcall(self, spg_id_db, 0), spg_id_to_application_timestamp, 1, rb_str_new2(s));
421
530
  }
422
531
 
423
- rtz = rb_funcall(spg_Sequel, spg_id_application_timezone, 0);
424
- if (rtz != Qnil) {
425
- if (rtz == spg_sym_local) {
426
- tz += SPG_APP_LOCAL;
427
- } else if (rtz == spg_sym_utc) {
428
- tz += SPG_APP_UTC;
429
- } else {
430
- return rb_funcall(db, spg_id_to_application_timestamp, 1, rb_str_new2(s));
431
- }
532
+ if (remaining < 19) {
533
+ return spg_timestamp_error(s, self, "unexpected timetamp format, too short");
432
534
  }
433
535
 
434
- if (0 != strchr(s, '.')) {
435
- tokens = sscanf(s, "%d-%2d-%2d %2d:%2d:%2d.%n%d%n%c%02d:%02d:%02d",
436
- &year, &month, &day, &hour, &min, &sec,
437
- &usec_start, &usec, &usec_stop,
438
- &offset_sign, &offset_hour, &offset_minute, &offset_seconds);
439
- if(tokens < 7) {
440
- return spg_timestamp_error(s, self, "unexpected datetime format");
536
+ year = parse_year(&p, &remaining);
537
+
538
+ if (remaining >= 15 && p[0] == '-' && p[3] == '-' && p[6] == ' ' && p[9] == ':' && p[12] == ':') {
539
+ month = str2_to_int(p+1);
540
+ day = str2_to_int(p+4);
541
+ hour = str2_to_int(p+7);
542
+ min = str2_to_int(p+10);
543
+ sec = str2_to_int(p+13);
544
+ p += 15;
545
+ remaining -= 15;
546
+
547
+ if (remaining >= 2 && p[0] == '.') {
548
+ /* microsecond part, up to 6 digits */
549
+ static const int coef[6] = { 100000, 10000, 1000, 100, 10, 1 };
550
+
551
+ p++;
552
+ remaining--;
553
+ for (i = 0; i < 6 && isdigit(*p); i++)
554
+ {
555
+ usec += coef[i] * char_to_digit(*p++);
556
+ remaining--;
557
+ }
441
558
  }
442
- usec *= (int) pow(10, (6 - (usec_stop - usec_start)));
443
- } else {
444
- tokens = sscanf(s, "%d-%2d-%2d %2d:%2d:%2d%c%02d:%02d:%02d",
445
- &year, &month, &day, &hour, &min, &sec,
446
- &offset_sign, &offset_hour, &offset_minute, &offset_seconds);
447
- if (tokens == 3) {
448
- hour = 0;
449
- min = 0;
450
- sec = 0;
451
- } else if (tokens < 6) {
452
- return spg_timestamp_error(s, self, "unexpected datetime format");
559
+
560
+ if ((tz & SPG_HAS_TIMEZONE) && remaining >= 3 && (p[0] == '+' || p[0] == '-')) {
561
+ offset_sign = p[0];
562
+ offset_seconds += str2_to_int(p+1)*3600;
563
+ p += 3;
564
+ remaining -= 3;
565
+
566
+ if (p[0] == ':') {
567
+ p++;
568
+ remaining--;
569
+ }
570
+ if (remaining >= 2 && isdigit(p[0]) && isdigit(p[1]))
571
+ {
572
+ offset_seconds += str2_to_int(p)*60;
573
+ p += 2;
574
+ remaining -= 2;
575
+ }
576
+ if (p[0] == ':')
577
+ {
578
+ p++;
579
+ remaining--;
580
+ }
581
+ if (remaining >= 2 && isdigit(p[0]) && isdigit(p[1]))
582
+ {
583
+ offset_seconds += str2_to_int(p);
584
+ p += 2;
585
+ remaining -= 2;
586
+ }
587
+ if (offset_sign == '-') {
588
+ offset_seconds *= -1;
589
+ }
453
590
  }
454
- usec = 0;
455
- }
456
591
 
457
- len = strlen(s);
458
- if(s[len-3] == ' ' && s[len-2] == 'B' && s[len-1] == 'C') {
459
- year = -year;
460
- year++;
592
+ if (remaining == 3 && p[0] == ' ' && p[1] == 'B' && p[2] == 'C') {
593
+ year = -year;
594
+ year++;
595
+ } else if (remaining != 0) {
596
+ return spg_timestamp_error(s, self, "unexpected timestamp format, remaining data left");
597
+ }
598
+ } else {
599
+ return spg_timestamp_error(s, self, "unexpected timestamp format");
461
600
  }
601
+
462
602
 
463
- if (offset_sign == '-') {
464
- offset_hour *= -1;
465
- offset_minute *= -1;
466
- offset_seconds *= -1;
467
- }
603
+ if (tz & SPG_USE_TIME) {
604
+ #if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 3)) && defined(HAVE_TIMEGM)
605
+ /* Fast path for time conversion */
606
+ struct tm tm;
607
+ struct timespec ts;
608
+ time_t time;
468
609
 
469
- dtc = rb_funcall(spg_Sequel, spg_id_datetime_class, 0);
610
+ tm.tm_year = year - 1900;
611
+ tm.tm_mon = month - 1;
612
+ tm.tm_mday = day;
613
+ tm.tm_hour = hour;
614
+ tm.tm_min = min;
615
+ tm.tm_sec = sec;
616
+ tm.tm_isdst = -1;
617
+
618
+ ts.tv_nsec = usec*1000;
619
+
620
+ if (offset_sign) {
621
+ time = timegm(&tm);
622
+ if (time != -1) {
623
+ ts.tv_sec = time - offset_seconds;
624
+ dt = rb_time_timespec_new(&ts, offset_seconds);
625
+
626
+ if (tz & SPG_APP_UTC) {
627
+ dt = rb_funcall(dt, spg_id_utc, 0);
628
+ } else if (tz & SPG_APP_LOCAL) {
629
+ dt = rb_funcall(dt, spg_id_local, 0);
630
+ }
631
+
632
+ return dt;
633
+ }
634
+ } else {
635
+ if (tz & SPG_DB_UTC) {
636
+ time = timegm(&tm);
637
+ } else {
638
+ time = mktime(&tm);
639
+ }
640
+
641
+ if (time != -1) {
642
+ ts.tv_sec = time;
643
+ if (tz & SPG_APP_UTC) {
644
+ offset_seconds = INT_MAX-1;
645
+ } else {
646
+ offset_seconds = INT_MAX;
647
+ }
648
+
649
+ return rb_time_timespec_new(&ts, offset_seconds);
650
+ }
651
+ }
652
+ #endif
470
653
 
471
- if (dtc == rb_cTime) {
472
654
  if (offset_sign) {
473
655
  /* Offset given, convert to local time if not already in local time.
474
656
  * While PostgreSQL generally returns timestamps in local time, it's unwise to rely on this.
475
657
  */
476
658
  dt = rb_funcall(rb_cTime, spg_id_local, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
477
659
  utc_offset = NUM2INT(rb_funcall(dt, spg_id_utc_offset, 0));
478
- offset_seconds += offset_hour * 3600 + offset_minute * 60;
479
660
  if (utc_offset != offset_seconds) {
480
661
  dt = rb_funcall(dt, spg_id_op_plus, 1, INT2NUM(utc_offset - offset_seconds));
481
662
  }
@@ -484,7 +665,7 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
484
665
  dt = rb_funcall(dt, spg_id_utc, 0);
485
666
  }
486
667
  return dt;
487
- } else if (tz == SPG_NO_TZ) {
668
+ } else if (!(tz & (SPG_APP_LOCAL|SPG_DB_LOCAL|SPG_APP_UTC|SPG_DB_UTC))) {
488
669
  return rb_funcall(rb_cTime, spg_id_local, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
489
670
  }
490
671
 
@@ -506,12 +687,14 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
506
687
  }
507
688
  } else {
508
689
  /* datetime.class == DateTime */
690
+ double offset_fraction;
691
+
509
692
  if (offset_sign) {
510
693
  /* Offset given, handle correct local time.
511
694
  * While PostgreSQL generally returns timestamps in local time, it's unwise to rely on this.
512
695
  */
513
- offset_fraction = offset_hour/24.0 + offset_minute/SPG_MINUTES_PER_DAY + offset_seconds/SPG_SECONDS_PER_DAY;
514
- dt = rb_funcall(dtc, spg_id_new, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), rb_float_new(offset_fraction));
696
+ offset_fraction = offset_seconds/(double)SPG_SECONDS_PER_DAY;
697
+ dt = rb_funcall(spg_DateTime, spg_id_new, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), rb_float_new(offset_fraction));
515
698
  SPG_DT_ADD_USEC
516
699
 
517
700
  if (tz & SPG_APP_LOCAL) {
@@ -521,8 +704,8 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
521
704
  dt = rb_funcall(dt, spg_id_new_offset, 1, INT2NUM(0));
522
705
  }
523
706
  return dt;
524
- } else if (tz == SPG_NO_TZ) {
525
- dt = rb_funcall(dtc, spg_id_new, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
707
+ } else if (!(tz & (SPG_APP_LOCAL|SPG_DB_LOCAL|SPG_APP_UTC|SPG_DB_UTC))) {
708
+ dt = rb_funcall(spg_DateTime, spg_id_new, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
526
709
  SPG_DT_ADD_USEC
527
710
  return dt;
528
711
  }
@@ -530,7 +713,7 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
530
713
  /* No offset given, and some timezone combination given */
531
714
  if (tz & SPG_DB_LOCAL) {
532
715
  offset_fraction = NUM2INT(rb_funcall(rb_funcall(rb_cTime, spg_id_local, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)), spg_id_utc_offset, 0))/SPG_SECONDS_PER_DAY;
533
- dt = rb_funcall(dtc, spg_id_new, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), rb_float_new(offset_fraction));
716
+ dt = rb_funcall(spg_DateTime, spg_id_new, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), rb_float_new(offset_fraction));
534
717
  SPG_DT_ADD_USEC
535
718
  if (tz & SPG_APP_UTC) {
536
719
  return rb_funcall(dt, spg_id_new_offset, 1, INT2NUM(0));
@@ -538,11 +721,12 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
538
721
  return dt;
539
722
  }
540
723
  } else {
541
- dt = rb_funcall(dtc, spg_id_new, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
724
+ dt = rb_funcall(spg_DateTime, spg_id_new, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
542
725
  SPG_DT_ADD_USEC
543
726
  if (tz & SPG_APP_LOCAL) {
544
727
  offset_fraction = NUM2INT(rb_funcall(rb_funcall(rb_cTime, spg_id_local, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)), spg_id_utc_offset, 0))/SPG_SECONDS_PER_DAY;
545
- return rb_funcall(dt, spg_id_new_offset, 1, rb_float_new(offset_fraction));
728
+ dt = rb_funcall(dt, spg_id_new_offset, 1, rb_float_new(offset_fraction));
729
+ return dt;
546
730
  } else {
547
731
  return dt;
548
732
  }
@@ -550,22 +734,144 @@ static VALUE spg_timestamp(const char *s, VALUE self) {
550
734
  }
551
735
  }
552
736
 
737
+ static VALUE spg_inet(char *val, size_t len)
738
+ {
739
+ VALUE ip;
740
+ VALUE ip_int;
741
+ VALUE vmasks;
742
+ unsigned int dst[4];
743
+ char buf[64];
744
+ int af = strchr(val, '.') ? AF_INET : AF_INET6;
745
+ int mask = -1;
746
+
747
+ if (len >= 64) {
748
+ rb_raise(rb_eTypeError, "unable to parse IP address, too long");
749
+ }
750
+
751
+ if (len >= 4) {
752
+ if (val[len-2] == '/') {
753
+ mask = val[len-1] - '0';
754
+ memcpy(buf, val, len-2);
755
+ val = buf;
756
+ val[len-2] = '\0';
757
+ } else if (val[len-3] == '/') {
758
+ mask = (val[len-2]- '0')*10 + val[len-1] - '0';
759
+ memcpy(buf, val, len-3);
760
+ val = buf;
761
+ val[len-3] = '\0';
762
+ } else if (val[len-4] == '/') {
763
+ mask = (val[len-3]- '0')*100 + (val[len-2]- '0')*10 + val[len-1] - '0';
764
+ memcpy(buf, val, len-4);
765
+ val = buf;
766
+ val[len-4] = '\0';
767
+ }
768
+ }
769
+
770
+ if (1 != inet_pton(af, val, (char *)dst)) {
771
+ rb_raise(rb_eTypeError, "unable to parse IP address: %s", val);
772
+ }
773
+
774
+ if (af == AF_INET) {
775
+ unsigned int ip_int_native;
776
+
777
+ if (mask == -1) {
778
+ mask = 32;
779
+ } else if (mask < 0 || mask > 32) {
780
+ rb_raise(rb_eTypeError, "invalid mask for IPv4: %d", mask);
781
+ }
782
+ vmasks = spg_vmasks4;
783
+
784
+ ip_int_native = ntohl(*dst);
785
+
786
+ /* Work around broken IPAddr behavior of convering portion
787
+ of address after netmask to 0 */
788
+ switch (mask) {
789
+ case 0:
790
+ ip_int_native = 0;
791
+ break;
792
+ case 32:
793
+ /* nothing to do */
794
+ break;
795
+ default:
796
+ ip_int_native &= ~((1UL<<(32-mask))-1);
797
+ break;
798
+ }
799
+ ip_int = UINT2NUM(ip_int_native);
800
+ } else {
801
+ unsigned long long * dstllp = (unsigned long long *)dst;
802
+ unsigned long long ip_int_native1;
803
+ unsigned long long ip_int_native2;
804
+
805
+ if (mask == -1) {
806
+ mask = 128;
807
+ } else if (mask < 0 || mask > 128) {
808
+ rb_raise(rb_eTypeError, "invalid mask for IPv6: %d", mask);
809
+ }
810
+ vmasks = spg_vmasks6;
811
+
812
+ ip_int_native1 = ntohll(dstllp);
813
+ dstllp++;
814
+ ip_int_native2 = ntohll(dstllp);
815
+
816
+ if (mask == 128) {
817
+ /* nothing to do */
818
+ } else if (mask == 64) {
819
+ ip_int_native2 = 0;
820
+ } else if (mask == 0) {
821
+ ip_int_native1 = 0;
822
+ ip_int_native2 = 0;
823
+ } else if (mask < 64) {
824
+ ip_int_native1 &= ~((1ULL<<(64-mask))-1);
825
+ ip_int_native2 = 0;
826
+ } else {
827
+ ip_int_native2 &= ~((1ULL<<(128-mask))-1);
828
+ }
829
+
830
+ /* 4 Bignum allocations */
831
+ ip_int = ULL2NUM(ip_int_native1);
832
+ ip_int = rb_funcall(ip_int, spg_id_lshift, 1, INT2NUM(64));
833
+ ip_int = rb_funcall(ip_int, spg_id_op_plus, 1, ULL2NUM(ip_int_native2));
834
+ }
835
+
836
+ if (spg_use_ipaddr_alloc) {
837
+ ip = rb_obj_alloc(spg_IPAddr);
838
+ rb_ivar_set(ip, spg_id_family, INT2NUM(af));
839
+ rb_ivar_set(ip, spg_id_addr, ip_int);
840
+ rb_ivar_set(ip, spg_id_mask_addr, RARRAY_AREF(vmasks, mask));
841
+ } else {
842
+ VALUE ip_args[2];
843
+ ip_args[0] = ip_int;
844
+ ip_args[1] = INT2NUM(af);
845
+ ip = rb_class_new_instance(2, ip_args, spg_IPAddr);
846
+ ip = rb_funcall(ip, spg_id_mask, 1, INT2NUM(mask));
847
+ }
848
+
849
+ return ip;
850
+ }
851
+
852
+ static VALUE spg_create_Blob(VALUE v) {
853
+ struct spg_blob_initialization *bi = (struct spg_blob_initialization *)v;
854
+ if (bi->blob_string == NULL) {
855
+ rb_raise(rb_eNoMemError, "PQunescapeBytea failure: probably not enough memory");
856
+ }
857
+ return rb_obj_taint(rb_str_new_with_class(spg_Blob_instance, bi->blob_string, bi->length));
858
+ }
859
+
553
860
  static VALUE spg_fetch_rows_set_cols(VALUE self, VALUE ignore) {
554
861
  return Qnil;
555
862
  }
556
863
 
557
864
  static VALUE spg__array_col_value(char *v, size_t length, VALUE converter, int enc_index, int oid, VALUE db) {
558
865
  VALUE rv;
559
- size_t l;
866
+ struct spg_blob_initialization bi;
560
867
 
561
868
  switch(oid) {
562
869
  case 16: /* boolean */
563
870
  rv = *v == 't' ? Qtrue : Qfalse;
564
871
  break;
565
872
  case 17: /* bytea */
566
- v = (char *)PQunescapeBytea((unsigned char*)v, &l);
567
- rv = rb_funcall(spg_Blob, spg_id_new, 1, rb_str_new(v, l));
568
- PQfreemem(v);
873
+ bi.blob_string = (char *)PQunescapeBytea((unsigned char*)v, &bi.length);
874
+ rv = rb_ensure(spg_create_Blob, (VALUE)&bi, (VALUE(*)())PQfreemem, (VALUE)bi.blob_string);
569
875
  break;
570
876
  case 20: /* integer */
571
877
  case 21:
@@ -589,15 +895,15 @@ static VALUE spg__array_col_value(char *v, size_t length, VALUE converter, int e
589
895
  rv = rb_funcall(spg_Kernel, spg_id_BigDecimal, 1, rb_str_new(v, length));
590
896
  break;
591
897
  case 1082: /* date */
592
- rv = spg_date(v, db);
898
+ rv = spg_date(v, db, length);
593
899
  break;
594
900
  case 1083: /* time */
595
901
  case 1266:
596
- rv = spg_time(v);
902
+ rv = spg_time(v, length, (int)converter);
597
903
  break;
598
904
  case 1114: /* timestamp */
599
905
  case 1184:
600
- rv = spg_timestamp(v, db);
906
+ rv = spg_timestamp(v, db, length, (int)converter);
601
907
  break;
602
908
  case 18: /* char */
603
909
  case 25: /* text */
@@ -605,6 +911,10 @@ static VALUE spg__array_col_value(char *v, size_t length, VALUE converter, int e
605
911
  rv = rb_tainted_str_new(v, length);
606
912
  PG_ENCODING_SET_NOCHECK(rv, enc_index);
607
913
  break;
914
+ case 869: /* inet */
915
+ case 650: /* cidr */
916
+ rv = spg_inet(v, length);
917
+ break;
608
918
  default:
609
919
  rv = rb_tainted_str_new(v, length);
610
920
  PG_ENCODING_SET_NOCHECK(rv, enc_index);
@@ -629,13 +939,63 @@ static VALUE spg_array_value(char *c_pg_array_string, int array_string_length, V
629
939
  return rb_class_new_instance(2, args, spg_PGArray);
630
940
  }
631
941
 
942
+ static int spg_time_info_bitmask(void) {
943
+ int info = 0;
944
+ VALUE now = rb_funcall(spg_SQLTime, spg_id_date, 0);
945
+
946
+ info = NUM2INT(rb_funcall(now, spg_id_year, 0)) << SPG_YEAR_SHIFT;
947
+ info += NUM2INT(rb_funcall(now, spg_id_month, 0)) << SPG_MONTH_SHIFT;
948
+ info += NUM2INT(rb_funcall(now, spg_id_day, 0));
949
+
950
+ if (rb_funcall(spg_Sequel, spg_id_application_timezone, 0) == spg_sym_utc) {
951
+ info += SPG_TIME_UTC;
952
+ }
953
+
954
+ return info;
955
+ }
956
+
957
+ static int spg_timestamp_info_bitmask(VALUE self) {
958
+ VALUE db, rtz;
959
+ int tz = 0;
960
+
961
+ db = rb_funcall(self, spg_id_db, 0);
962
+ rtz = rb_funcall(db, spg_id_timezone, 0);
963
+ if (rtz != Qnil) {
964
+ if (rtz == spg_sym_local) {
965
+ tz |= SPG_DB_LOCAL;
966
+ } else if (rtz == spg_sym_utc) {
967
+ tz |= SPG_DB_UTC;
968
+ } else {
969
+ tz |= SPG_DB_CUSTOM;
970
+ }
971
+ }
972
+
973
+ rtz = rb_funcall(spg_Sequel, spg_id_application_timezone, 0);
974
+ if (rtz != Qnil) {
975
+ if (rtz == spg_sym_local) {
976
+ tz |= SPG_APP_LOCAL;
977
+ } else if (rtz == spg_sym_utc) {
978
+ tz |= SPG_APP_UTC;
979
+ } else {
980
+ tz |= SPG_APP_CUSTOM;
981
+ }
982
+ }
983
+
984
+ if (rb_cTime == rb_funcall(spg_Sequel, spg_id_datetime_class, 0)) {
985
+ tz |= SPG_USE_TIME;
986
+ }
987
+
988
+ tz |= SPG_TZ_INITIALIZED;
989
+ return tz;
990
+ }
991
+
632
992
  static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* colconvert, int enc_index) {
633
993
  char *v;
634
994
  VALUE rv;
635
- size_t l;
636
995
  int ftype = PQftype(res, j);
637
996
  VALUE array_type;
638
997
  VALUE scalar_oid;
998
+ struct spg_blob_initialization bi;
639
999
 
640
1000
  if(PQgetisnull(res, i, j)) {
641
1001
  rv = Qnil;
@@ -647,9 +1007,8 @@ static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* co
647
1007
  rv = *v == 't' ? Qtrue : Qfalse;
648
1008
  break;
649
1009
  case 17: /* bytea */
650
- v = (char *)PQunescapeBytea((unsigned char*)v, &l);
651
- rv = rb_funcall(spg_Blob, spg_id_new, 1, rb_str_new(v, l));
652
- PQfreemem(v);
1010
+ bi.blob_string = (char *)PQunescapeBytea((unsigned char*)v, &bi.length);
1011
+ rv = rb_ensure(spg_create_Blob, (VALUE)&bi, (VALUE(*)())PQfreemem, (VALUE)bi.blob_string);
653
1012
  break;
654
1013
  case 20: /* integer */
655
1014
  case 21:
@@ -673,15 +1032,15 @@ static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* co
673
1032
  rv = rb_funcall(spg_Kernel, spg_id_BigDecimal, 1, rb_str_new(v, PQgetlength(res, i, j)));
674
1033
  break;
675
1034
  case 1082: /* date */
676
- rv = spg_date(v, self);
1035
+ rv = spg_date(v, self, PQgetlength(res, i, j));
677
1036
  break;
678
1037
  case 1083: /* time */
679
1038
  case 1266:
680
- rv = spg_time(v);
1039
+ rv = spg_time(v, PQgetlength(res, i, j), (int)colconvert[j]);
681
1040
  break;
682
1041
  case 1114: /* timestamp */
683
1042
  case 1184:
684
- rv = spg_timestamp(v, self);
1043
+ rv = spg_timestamp(v, self, PQgetlength(res, i, j), (int)colconvert[j]);
685
1044
  break;
686
1045
  case 18: /* char */
687
1046
  case 25: /* text */
@@ -689,6 +1048,10 @@ static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* co
689
1048
  rv = rb_tainted_str_new(v, PQgetlength(res, i, j));
690
1049
  PG_ENCODING_SET_NOCHECK(rv, enc_index);
691
1050
  break;
1051
+ case 869: /* inet */
1052
+ case 650: /* cidr */
1053
+ rv = spg_inet(v, PQgetlength(res, i, j));
1054
+ break;
692
1055
  /* array types */
693
1056
  case 1009:
694
1057
  case 1014:
@@ -717,6 +1080,8 @@ static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* co
717
1080
  case 1003:
718
1081
  case 1010:
719
1082
  case 1006:
1083
+ case 1041:
1084
+ case 651:
720
1085
  switch(ftype) {
721
1086
  case 1009:
722
1087
  case 1014:
@@ -823,8 +1188,16 @@ static VALUE spg__col_value(VALUE self, PGresult *res, long i, long j, VALUE* co
823
1188
  array_type = spg_sym_int2vector;
824
1189
  scalar_oid = 22;
825
1190
  break;
1191
+ case 1041:
1192
+ array_type = spg_sym_inet;
1193
+ scalar_oid = 869;
1194
+ break;
1195
+ case 651:
1196
+ array_type = spg_sym_cidr;
1197
+ scalar_oid = 650;
1198
+ break;
826
1199
  }
827
- rv = spg_array_value(v, PQgetlength(res, i, j), Qnil, enc_index, scalar_oid, self, array_type);
1200
+ rv = spg_array_value(v, PQgetlength(res, i, j), colconvert[j], enc_index, scalar_oid, self, array_type);
828
1201
  break;
829
1202
  default:
830
1203
  rv = rb_tainted_str_new(v, PQgetlength(res, i, j));
@@ -877,6 +1250,8 @@ static void spg_set_column_info(VALUE self, PGresult *res, VALUE *colsyms, VALUE
877
1250
  long i;
878
1251
  long j;
879
1252
  long nfields;
1253
+ int timestamp_info = 0;
1254
+ int time_info = 0;
880
1255
  VALUE conv_procs = 0;
881
1256
 
882
1257
  nfields = PQnfields(res);
@@ -885,6 +1260,7 @@ static void spg_set_column_info(VALUE self, PGresult *res, VALUE *colsyms, VALUE
885
1260
  colsyms[j] = rb_funcall(self, spg_id_output_identifier, 1, rb_str_new2(PQfname(res, j)));
886
1261
  i = PQftype(res, j);
887
1262
  switch (i) {
1263
+ /* scalar types */
888
1264
  case 16:
889
1265
  case 17:
890
1266
  case 20:
@@ -896,15 +1272,57 @@ static void spg_set_column_info(VALUE self, PGresult *res, VALUE *colsyms, VALUE
896
1272
  case 790:
897
1273
  case 1700:
898
1274
  case 1082:
899
- case 1083:
900
- case 1266:
901
- case 1114:
902
- case 1184:
903
1275
  case 18:
904
1276
  case 25:
905
1277
  case 1043:
1278
+ /* array types */
1279
+ case 1009:
1280
+ case 1014:
1281
+ case 1015:
1282
+ case 1007:
1283
+ case 1016:
1284
+ case 1231:
1285
+ case 1022:
1286
+ case 1000:
1287
+ case 1001:
1288
+ case 1182:
1289
+ case 1005:
1290
+ case 1028:
1291
+ case 1021:
1292
+ case 143:
1293
+ case 791:
1294
+ case 1561:
1295
+ case 1563:
1296
+ case 2951:
1297
+ case 1011:
1298
+ case 1012:
1299
+ case 1003:
1300
+ case 1010:
906
1301
  colconvert[j] = Qnil;
907
1302
  break;
1303
+
1304
+ /* time types */
1305
+ case 1183:
1306
+ case 1083:
1307
+ case 1270:
1308
+ case 1266:
1309
+ if (time_info == 0) {
1310
+ time_info = spg_time_info_bitmask();
1311
+ }
1312
+ colconvert[j] = time_info;
1313
+ break;
1314
+
1315
+ /* timestamp types */
1316
+ case 1115:
1317
+ case 1185:
1318
+ case 1114:
1319
+ case 1184:
1320
+ if (timestamp_info == 0) {
1321
+ timestamp_info = spg_timestamp_info_bitmask(self);
1322
+ }
1323
+ colconvert[j] = (VALUE)((i == 1184 || i == 1185) ? (timestamp_info | SPG_HAS_TIMEZONE) : timestamp_info);
1324
+ break;
1325
+
908
1326
  default:
909
1327
  if (conv_procs == 0) {
910
1328
  conv_procs = rb_funcall(rb_funcall(self, spg_id_db, 0), spg_id_conversion_procs, 0);
@@ -930,14 +1348,13 @@ static VALUE spg_yield_hash_rows(VALUE self, VALUE rres, VALUE ignore) {
930
1348
  VALUE pg_type;
931
1349
  VALUE pg_value;
932
1350
  char type = SPG_YIELD_NORMAL;
1351
+ int enc_index;
933
1352
 
934
1353
  if (!RTEST(rres)) {
935
1354
  return self;
936
1355
  }
937
- Check_Type(rres, T_DATA);
938
1356
  res = pgresult_get(rres);
939
1357
 
940
- int enc_index;
941
1358
  enc_index = enc_get_index(rres);
942
1359
 
943
1360
  ntuples = PQntuples(res);
@@ -1174,7 +1591,6 @@ static VALUE spg_supports_streaming_p(VALUE self) {
1174
1591
  #if HAVE_PQSETSINGLEROWMODE
1175
1592
  static VALUE spg_set_single_row_mode(VALUE self) {
1176
1593
  PGconn *conn;
1177
- Check_Type(self, T_DATA);
1178
1594
  conn = pg_get_pgconn(self);
1179
1595
  if (PQsetSingleRowMode(conn) != 1) {
1180
1596
  rb_raise(spg_PGError, "cannot set single row mode");
@@ -1183,7 +1599,6 @@ static VALUE spg_set_single_row_mode(VALUE self) {
1183
1599
  }
1184
1600
 
1185
1601
  static VALUE spg__yield_each_row(VALUE self) {
1186
- PGconn *conn;
1187
1602
  PGresult *res;
1188
1603
  VALUE rres;
1189
1604
  VALUE rconn;
@@ -1196,21 +1611,18 @@ static VALUE spg__yield_each_row(VALUE self) {
1196
1611
  VALUE pg_type;
1197
1612
  VALUE pg_value = Qnil;
1198
1613
  char type = SPG_YIELD_NORMAL;
1614
+ int enc_index;
1199
1615
 
1200
1616
  rconn = rb_ary_entry(self, 1);
1201
1617
  self = rb_ary_entry(self, 0);
1202
- Check_Type(rconn, T_DATA);
1203
- conn = pg_get_pgconn(rconn);
1204
1618
 
1205
1619
  rres = rb_funcall(rconn, spg_id_get_result, 0);
1206
1620
  if (rres == Qnil) {
1207
1621
  goto end_yield_each_row;
1208
1622
  }
1209
1623
  rb_funcall(rres, spg_id_check, 0);
1210
- Check_Type(rres, T_DATA);
1211
1624
  res = pgresult_get(rres);
1212
1625
 
1213
- int enc_index;
1214
1626
  enc_index = enc_get_index(rres);
1215
1627
 
1216
1628
  /* Only handle regular and model types. All other types require compiling all
@@ -1255,7 +1667,6 @@ static VALUE spg__yield_each_row(VALUE self) {
1255
1667
  goto end_yield_each_row;
1256
1668
  }
1257
1669
  rb_funcall(rres, spg_id_check, 0);
1258
- Check_Type(rres, T_DATA);
1259
1670
  res = pgresult_get(rres);
1260
1671
  }
1261
1672
  rb_funcall(rres, spg_id_clear, 0);
@@ -1268,7 +1679,6 @@ static VALUE spg__flush_results(VALUE rconn) {
1268
1679
  PGconn *conn;
1269
1680
  PGresult *res;
1270
1681
  VALUE error = 0;
1271
- Check_Type(rconn, T_DATA);
1272
1682
  conn = pg_get_pgconn(rconn);
1273
1683
 
1274
1684
  while ((res = PQgetResult(conn)) != NULL) {
@@ -1310,6 +1720,7 @@ void Init_sequel_pg(void) {
1310
1720
 
1311
1721
  spg_Sequel = rb_funcall(rb_cObject, cg, 1, rb_str_new2("Sequel"));
1312
1722
  spg_Postgres = rb_funcall(spg_Sequel, cg, 1, rb_str_new2("Postgres"));
1723
+ rb_global_variable(&spg_Sequel);
1313
1724
 
1314
1725
  if(rb_obj_respond_to(spg_Postgres, rb_intern("sequel_pg_version_supported?"), 0)) {
1315
1726
  if(!RTEST(rb_funcall(spg_Postgres, rb_intern("sequel_pg_version_supported?"), 1, INT2FIX(SEQUEL_PG_VERSION_INTEGER)))) {
@@ -1321,6 +1732,7 @@ void Init_sequel_pg(void) {
1321
1732
 
1322
1733
  spg_id_BigDecimal = rb_intern("BigDecimal");
1323
1734
  spg_id_new = rb_intern("new");
1735
+ spg_id_date = rb_intern("date");
1324
1736
  spg_id_local = rb_intern("local");
1325
1737
  spg_id_year = rb_intern("year");
1326
1738
  spg_id_month = rb_intern("month");
@@ -1351,6 +1763,12 @@ void Init_sequel_pg(void) {
1351
1763
  spg_id_encoding = rb_intern("@encoding");
1352
1764
  spg_id_values = rb_intern("@values");
1353
1765
 
1766
+ spg_id_family = rb_intern("@family");
1767
+ spg_id_addr = rb_intern("@addr");
1768
+ spg_id_mask_addr = rb_intern("@mask_addr");
1769
+ spg_id_lshift = rb_intern("<<");
1770
+ spg_id_mask = rb_intern("mask");
1771
+
1354
1772
  spg_sym_utc = ID2SYM(rb_intern("utc"));
1355
1773
  spg_sym_local = ID2SYM(rb_intern("local"));
1356
1774
  spg_sym_map = ID2SYM(rb_intern("map"));
@@ -1388,29 +1806,42 @@ void Init_sequel_pg(void) {
1388
1806
  spg_sym_name = ID2SYM(rb_intern("name"));
1389
1807
  spg_sym_tid = ID2SYM(rb_intern("tid"));
1390
1808
  spg_sym_int2vector = ID2SYM(rb_intern("int2vector"));
1809
+ spg_sym_inet = ID2SYM(rb_intern("inet"));
1810
+ spg_sym_cidr = ID2SYM(rb_intern("cidr"));
1391
1811
 
1392
1812
  spg_Blob = rb_funcall(rb_funcall(spg_Sequel, cg, 1, rb_str_new2("SQL")), cg, 1, rb_str_new2("Blob"));
1813
+ rb_global_variable(&spg_Blob);
1814
+ spg_Blob_instance = rb_obj_freeze(rb_funcall(spg_Blob, spg_id_new, 0));
1815
+ rb_global_variable(&spg_Blob_instance);
1393
1816
  spg_SQLTime= rb_funcall(spg_Sequel, cg, 1, rb_str_new2("SQLTime"));
1817
+ rb_global_variable(&spg_SQLTime);
1394
1818
  spg_Kernel = rb_funcall(rb_cObject, cg, 1, rb_str_new2("Kernel"));
1819
+ rb_global_variable(&spg_Kernel);
1395
1820
  spg_Date = rb_funcall(rb_cObject, cg, 1, rb_str_new2("Date"));
1821
+ rb_global_variable(&spg_Date);
1822
+ spg_DateTime = rb_funcall(rb_cObject, cg, 1, rb_str_new2("DateTime"));
1823
+ rb_global_variable(&spg_DateTime);
1396
1824
  spg_PGError = rb_funcall(rb_funcall(rb_cObject, cg, 1, rb_str_new2("PG")), cg, 1, rb_str_new2("Error"));
1825
+ rb_global_variable(&spg_PGError);
1397
1826
 
1398
1827
  spg_nan = rb_eval_string("0.0/0.0");
1399
- spg_pos_inf = rb_eval_string("1.0/0.0");
1400
- spg_neg_inf = rb_eval_string("-1.0/0.0");
1401
- spg_usec_per_day = ULL2NUM(86400000000ULL);
1402
-
1403
- rb_global_variable(&spg_Sequel);
1404
- rb_global_variable(&spg_Blob);
1405
- rb_global_variable(&spg_Kernel);
1406
- rb_global_variable(&spg_Date);
1407
- rb_global_variable(&spg_SQLTime);
1408
- rb_global_variable(&spg_PGError);
1409
1828
  rb_global_variable(&spg_nan);
1829
+ spg_pos_inf = rb_eval_string("1.0/0.0");
1410
1830
  rb_global_variable(&spg_pos_inf);
1831
+ spg_neg_inf = rb_eval_string("-1.0/0.0");
1411
1832
  rb_global_variable(&spg_neg_inf);
1833
+ spg_usec_per_day = ULL2NUM(86400000000ULL);
1412
1834
  rb_global_variable(&spg_usec_per_day);
1413
1835
 
1836
+ rb_require("ipaddr");
1837
+ spg_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
1838
+ rb_global_variable(&spg_IPAddr);
1839
+ spg_use_ipaddr_alloc = RTEST(rb_eval_string("IPAddr.new.instance_variables.sort == [:@addr, :@family, :@mask_addr]"));
1840
+ spg_vmasks4 = rb_eval_string("a = [0]*33; a[0] = 0; a[32] = 0xffffffff; 31.downto(1){|i| a[i] = a[i+1] - (1 << (31 - i))}; a.freeze");
1841
+ rb_global_variable(&spg_vmasks4);
1842
+ spg_vmasks6 = rb_eval_string("a = [0]*129; a[0] = 0; a[128] = 0xffffffffffffffffffffffffffffffff; 127.downto(1){|i| a[i] = a[i+1] - (1 << (127 - i))}; a.freeze");
1843
+ rb_global_variable(&spg_vmasks6);
1844
+
1414
1845
  c = rb_funcall(spg_Postgres, cg, 1, rb_str_new2("Dataset"));
1415
1846
  rb_undef_method(c, "yield_hash_rows");
1416
1847
  rb_define_private_method(c, "yield_hash_rows", spg_yield_hash_rows, 2);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel_pg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-06 00:00:00.000000000 Z
11
+ date: 2018-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 4.34.0
33
+ version: 4.38.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 4.34.0
40
+ version: 4.38.0
41
41
  description: |
42
42
  sequel_pg overwrites the inner loop of the Sequel postgres
43
43
  adapter row fetching code with a C version. The C version
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  version: '0'
92
92
  requirements: []
93
93
  rubyforge_project:
94
- rubygems_version: 3.0.0.beta1
94
+ rubygems_version: 2.7.6
95
95
  signing_key:
96
96
  specification_version: 4
97
97
  summary: Faster SELECTs when using Sequel with pg