sequel_pg 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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