pg 1.4.4 → 1.5.9
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Gemfile +6 -0
- data/{History.rdoc → History.md} +303 -151
- data/README.ja.md +300 -0
- data/README.md +286 -0
- data/Rakefile +16 -4
- data/Rakefile.cross +15 -14
- data/certs/kanis@comcard.de.pem +20 -0
- data/certs/larskanis-2023.pem +24 -0
- data/certs/larskanis-2024.pem +24 -0
- data/ext/errorcodes.def +8 -5
- data/ext/errorcodes.txt +3 -5
- data/ext/extconf.rb +7 -0
- data/ext/pg.c +15 -30
- data/ext/pg.h +10 -6
- data/ext/pg_binary_decoder.c +81 -0
- data/ext/pg_binary_encoder.c +224 -0
- data/ext/pg_coder.c +16 -7
- data/ext/pg_connection.c +220 -82
- data/ext/pg_copy_coder.c +315 -22
- data/ext/pg_record_coder.c +11 -10
- data/ext/pg_result.c +93 -19
- data/ext/pg_text_decoder.c +31 -10
- data/ext/pg_text_encoder.c +38 -19
- data/ext/pg_tuple.c +34 -31
- data/ext/pg_type_map.c +3 -2
- data/ext/pg_type_map_all_strings.c +2 -2
- data/ext/pg_type_map_by_class.c +5 -3
- data/ext/pg_type_map_by_column.c +7 -3
- data/ext/pg_type_map_by_oid.c +7 -4
- data/ext/pg_type_map_in_ruby.c +5 -2
- data/lib/pg/basic_type_map_based_on_result.rb +21 -1
- data/lib/pg/basic_type_map_for_queries.rb +19 -10
- data/lib/pg/basic_type_map_for_results.rb +26 -3
- data/lib/pg/basic_type_registry.rb +44 -34
- data/lib/pg/binary_decoder/date.rb +9 -0
- data/lib/pg/binary_decoder/timestamp.rb +26 -0
- data/lib/pg/binary_encoder/timestamp.rb +20 -0
- data/lib/pg/coder.rb +15 -13
- data/lib/pg/connection.rb +158 -64
- data/lib/pg/exceptions.rb +13 -0
- data/lib/pg/text_decoder/date.rb +21 -0
- data/lib/pg/text_decoder/inet.rb +9 -0
- data/lib/pg/text_decoder/json.rb +17 -0
- data/lib/pg/text_decoder/numeric.rb +9 -0
- data/lib/pg/text_decoder/timestamp.rb +30 -0
- data/lib/pg/text_encoder/date.rb +13 -0
- data/lib/pg/text_encoder/inet.rb +31 -0
- data/lib/pg/text_encoder/json.rb +17 -0
- data/lib/pg/text_encoder/numeric.rb +9 -0
- data/lib/pg/text_encoder/timestamp.rb +24 -0
- data/lib/pg/version.rb +1 -1
- data/lib/pg.rb +65 -15
- data/pg.gemspec +7 -3
- data/rakelib/task_extension.rb +1 -1
- data.tar.gz.sig +2 -4
- metadata +104 -46
- metadata.gz.sig +0 -0
- data/.appveyor.yml +0 -36
- data/.gems +0 -6
- data/.gemtest +0 -0
- data/.github/workflows/binary-gems.yml +0 -86
- data/.github/workflows/source-gem.yml +0 -131
- data/.gitignore +0 -13
- data/.hgsigs +0 -34
- data/.hgtags +0 -41
- data/.irbrc +0 -23
- data/.pryrc +0 -23
- data/.tm_properties +0 -21
- data/.travis.yml +0 -49
- data/README.ja.rdoc +0 -13
- data/README.rdoc +0 -233
- data/lib/pg/binary_decoder.rb +0 -23
- data/lib/pg/constants.rb +0 -12
- data/lib/pg/text_decoder.rb +0 -46
- data/lib/pg/text_encoder.rb +0 -59
data/ext/pg_result.c
CHANGED
@@ -183,7 +183,7 @@ static const rb_data_type_t pgresult_type = {
|
|
183
183
|
pg_compact_callback(pgresult_gc_compact),
|
184
184
|
},
|
185
185
|
0, 0,
|
186
|
-
RUBY_TYPED_FREE_IMMEDIATELY,
|
186
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | PG_RUBY_TYPED_FROZEN_SHAREABLE,
|
187
187
|
};
|
188
188
|
|
189
189
|
/* Needed by sequel_pg gem, do not delete */
|
@@ -208,6 +208,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
208
208
|
|
209
209
|
this = (t_pg_result *)xmalloc(sizeof(*this) + sizeof(*this->fnames) * nfields);
|
210
210
|
this->pgresult = result;
|
211
|
+
/* Initialize connection and typemap prior to any object allocations,
|
212
|
+
* to make sure valid objects are marked. */
|
211
213
|
this->connection = rb_pgconn;
|
212
214
|
this->typemap = pg_typemap_all_strings;
|
213
215
|
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
@@ -224,7 +226,8 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
224
226
|
t_typemap *p_typemap = RTYPEDDATA_DATA(typemap);
|
225
227
|
|
226
228
|
this->enc_idx = p_conn->enc_idx;
|
227
|
-
|
229
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
230
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
228
231
|
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
229
232
|
this->flags = p_conn->flags;
|
230
233
|
} else {
|
@@ -374,17 +377,37 @@ VALUE
|
|
374
377
|
pg_result_clear(VALUE self)
|
375
378
|
{
|
376
379
|
t_pg_result *this = pgresult_get_this(self);
|
380
|
+
rb_check_frozen(self);
|
377
381
|
pgresult_clear( this );
|
378
382
|
return Qnil;
|
379
383
|
}
|
380
384
|
|
385
|
+
/*
|
386
|
+
* call-seq:
|
387
|
+
* res.freeze
|
388
|
+
*
|
389
|
+
* Freeze the PG::Result object and unlink the result from the related PG::Connection.
|
390
|
+
*
|
391
|
+
* A frozen PG::Result object doesn't allow any streaming and it can't be cleared.
|
392
|
+
* It also denies setting a type_map or field_name_type.
|
393
|
+
*
|
394
|
+
*/
|
395
|
+
static VALUE
|
396
|
+
pg_result_freeze(VALUE self)
|
397
|
+
{
|
398
|
+
t_pg_result *this = pgresult_get_this(self);
|
399
|
+
|
400
|
+
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
401
|
+
return rb_call_super(0, NULL);
|
402
|
+
}
|
403
|
+
|
381
404
|
/*
|
382
405
|
* call-seq:
|
383
406
|
* res.cleared? -> boolean
|
384
407
|
*
|
385
408
|
* Returns +true+ if the backend result memory has been freed.
|
386
409
|
*/
|
387
|
-
VALUE
|
410
|
+
static VALUE
|
388
411
|
pgresult_cleared_p( VALUE self )
|
389
412
|
{
|
390
413
|
t_pg_result *this = pgresult_get_this(self);
|
@@ -401,7 +424,7 @@ pgresult_cleared_p( VALUE self )
|
|
401
424
|
* All other Result objects are automatically cleared by the GC when the object is no longer in use or manually by PG::Result#clear .
|
402
425
|
*
|
403
426
|
*/
|
404
|
-
VALUE
|
427
|
+
static VALUE
|
405
428
|
pgresult_autoclear_p( VALUE self )
|
406
429
|
{
|
407
430
|
t_pg_result *this = pgresult_get_this(self);
|
@@ -477,7 +500,8 @@ static void pgresult_init_fnames(VALUE self)
|
|
477
500
|
|
478
501
|
for( i=0; i<nfields; i++ ){
|
479
502
|
char *cfname = PQfname(this->pgresult, i);
|
480
|
-
|
503
|
+
VALUE fname = pg_cstr_to_sym(cfname, this->flags, this->enc_idx);
|
504
|
+
RB_OBJ_WRITE(self, &this->fnames[i], fname);
|
481
505
|
this->nfields = i + 1;
|
482
506
|
}
|
483
507
|
this->nfields = nfields;
|
@@ -527,6 +551,8 @@ static void pgresult_init_fnames(VALUE self)
|
|
527
551
|
* * +PGRES_SINGLE_TUPLE+
|
528
552
|
* * +PGRES_PIPELINE_SYNC+
|
529
553
|
* * +PGRES_PIPELINE_ABORTED+
|
554
|
+
*
|
555
|
+
* Use <tt>res.res_status</tt> to retrieve the string representation.
|
530
556
|
*/
|
531
557
|
static VALUE
|
532
558
|
pgresult_result_status(VALUE self)
|
@@ -536,16 +562,38 @@ pgresult_result_status(VALUE self)
|
|
536
562
|
|
537
563
|
/*
|
538
564
|
* call-seq:
|
539
|
-
*
|
565
|
+
* PG::Result.res_status( status ) -> String
|
540
566
|
*
|
541
567
|
* Returns the string representation of +status+.
|
542
568
|
*
|
543
569
|
*/
|
544
570
|
static VALUE
|
545
|
-
|
571
|
+
pgresult_s_res_status(VALUE self, VALUE status)
|
572
|
+
{
|
573
|
+
return rb_utf8_str_new_cstr(PQresStatus(NUM2INT(status)));
|
574
|
+
}
|
575
|
+
|
576
|
+
/*
|
577
|
+
* call-seq:
|
578
|
+
* res.res_status -> String
|
579
|
+
* res.res_status( status ) -> String
|
580
|
+
*
|
581
|
+
* Returns the string representation of the status of the result or of the provided +status+.
|
582
|
+
*
|
583
|
+
*/
|
584
|
+
static VALUE
|
585
|
+
pgresult_res_status(int argc, VALUE *argv, VALUE self)
|
546
586
|
{
|
547
587
|
t_pg_result *this = pgresult_get_this_safe(self);
|
548
|
-
VALUE ret
|
588
|
+
VALUE ret;
|
589
|
+
|
590
|
+
if( argc == 0 ){
|
591
|
+
ret = rb_str_new2(PQresStatus(PQresultStatus(this->pgresult)));
|
592
|
+
}else if( argc == 1 ){
|
593
|
+
ret = rb_str_new2(PQresStatus(NUM2INT(argv[0])));
|
594
|
+
}else{
|
595
|
+
rb_raise(rb_eArgError, "only 0 or 1 arguments expected");
|
596
|
+
}
|
549
597
|
PG_ENCODING_SET_NOCHECK(ret, this->enc_idx);
|
550
598
|
return ret;
|
551
599
|
}
|
@@ -616,7 +664,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
616
664
|
* An example:
|
617
665
|
*
|
618
666
|
* begin
|
619
|
-
* conn.exec( "SELECT * FROM
|
667
|
+
* conn.exec( "SELECT * FROM nonexistent_table" )
|
620
668
|
* rescue PG::Error => err
|
621
669
|
* p [
|
622
670
|
* err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
|
@@ -636,7 +684,7 @@ pgresult_verbose_error_message(VALUE self, VALUE verbosity, VALUE show_context)
|
|
636
684
|
*
|
637
685
|
* Outputs:
|
638
686
|
*
|
639
|
-
* ["ERROR", "42P01", "relation \"
|
687
|
+
* ["ERROR", "42P01", "relation \"nonexistent_table\" does not exist", nil, nil,
|
640
688
|
* "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
|
641
689
|
*/
|
642
690
|
static VALUE
|
@@ -685,6 +733,21 @@ pgresult_nfields(VALUE self)
|
|
685
733
|
return INT2NUM(PQnfields(pgresult_get(self)));
|
686
734
|
}
|
687
735
|
|
736
|
+
/*
|
737
|
+
* call-seq:
|
738
|
+
* res.binary_tuples() -> Integer
|
739
|
+
*
|
740
|
+
* Returns 1 if the PGresult contains binary data and 0 if it contains text data.
|
741
|
+
*
|
742
|
+
* This function is deprecated (except for its use in connection with COPY), because it is possible for a single PGresult to contain text data in some columns and binary data in others.
|
743
|
+
* Result#fformat is preferred. binary_tuples returns 1 only if all columns of the result are binary (format 1).
|
744
|
+
*/
|
745
|
+
static VALUE
|
746
|
+
pgresult_binary_tuples(VALUE self)
|
747
|
+
{
|
748
|
+
return INT2NUM(PQbinaryTuples(pgresult_get(self)));
|
749
|
+
}
|
750
|
+
|
688
751
|
/*
|
689
752
|
* call-seq:
|
690
753
|
* res.fname( index ) -> String or Symbol
|
@@ -1087,7 +1150,7 @@ pgresult_aref(VALUE self, VALUE index)
|
|
1087
1150
|
}
|
1088
1151
|
/* Store a copy of the filled hash for use at the next row. */
|
1089
1152
|
if( num_tuples > 10 )
|
1090
|
-
this->tuple_hash
|
1153
|
+
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
1091
1154
|
|
1092
1155
|
return tuple;
|
1093
1156
|
}
|
@@ -1269,7 +1332,7 @@ static void ensure_init_for_tuple(VALUE self)
|
|
1269
1332
|
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
1270
1333
|
}
|
1271
1334
|
rb_obj_freeze(field_map);
|
1272
|
-
this->field_map
|
1335
|
+
RB_OBJ_WRITE(self, &this->field_map, field_map);
|
1273
1336
|
}
|
1274
1337
|
}
|
1275
1338
|
|
@@ -1357,11 +1420,13 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
1357
1420
|
t_pg_result *this = pgresult_get_this(self);
|
1358
1421
|
t_typemap *p_typemap;
|
1359
1422
|
|
1423
|
+
rb_check_frozen(self);
|
1360
1424
|
/* Check type of method param */
|
1361
1425
|
TypedData_Get_Struct(typemap, t_typemap, &pg_typemap_type, p_typemap);
|
1362
1426
|
|
1363
|
-
|
1364
|
-
|
1427
|
+
typemap = p_typemap->funcs.fit_to_result( typemap, self );
|
1428
|
+
RB_OBJ_WRITE(self, &this->typemap, typemap);
|
1429
|
+
this->p_typemap = RTYPEDDATA_DATA( typemap );
|
1365
1430
|
|
1366
1431
|
return typemap;
|
1367
1432
|
}
|
@@ -1441,10 +1506,11 @@ VALUE
|
|
1441
1506
|
pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* data)
|
1442
1507
|
{
|
1443
1508
|
t_pg_result *this;
|
1444
|
-
int nfields;
|
1509
|
+
int nfields, nfields2;
|
1445
1510
|
PGconn *pgconn;
|
1446
1511
|
PGresult *pgresult;
|
1447
1512
|
|
1513
|
+
rb_check_frozen(self);
|
1448
1514
|
RETURN_ENUMERATOR(self, 0, NULL);
|
1449
1515
|
|
1450
1516
|
this = pgresult_get_this_safe(self);
|
@@ -1467,6 +1533,12 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
1467
1533
|
pg_result_check( self );
|
1468
1534
|
}
|
1469
1535
|
|
1536
|
+
nfields2 = PQnfields(pgresult);
|
1537
|
+
if( nfields != nfields2 ){
|
1538
|
+
pgresult_clear( this );
|
1539
|
+
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, nfields2);
|
1540
|
+
}
|
1541
|
+
|
1470
1542
|
if( yielder( self, ntuples, nfields, data ) ){
|
1471
1543
|
pgresult_clear( this );
|
1472
1544
|
}
|
@@ -1480,9 +1552,6 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
1480
1552
|
if( pgresult == NULL )
|
1481
1553
|
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
|
1482
1554
|
|
1483
|
-
if( nfields != PQnfields(pgresult) )
|
1484
|
-
rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, PQnfields(pgresult));
|
1485
|
-
|
1486
1555
|
this->pgresult = pgresult;
|
1487
1556
|
}
|
1488
1557
|
|
@@ -1586,6 +1655,8 @@ static VALUE
|
|
1586
1655
|
pgresult_field_name_type_set(VALUE self, VALUE sym)
|
1587
1656
|
{
|
1588
1657
|
t_pg_result *this = pgresult_get_this(self);
|
1658
|
+
|
1659
|
+
rb_check_frozen(self);
|
1589
1660
|
if( this->nfields != -1 ) rb_raise(rb_eArgError, "field names are already materialized");
|
1590
1661
|
|
1591
1662
|
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
@@ -1632,7 +1703,8 @@ init_pg_result(void)
|
|
1632
1703
|
|
1633
1704
|
/****** PG::Result INSTANCE METHODS: libpq ******/
|
1634
1705
|
rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
|
1635
|
-
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
|
1706
|
+
rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, -1);
|
1707
|
+
rb_define_singleton_method(rb_cPGresult, "res_status", pgresult_s_res_status, 1);
|
1636
1708
|
rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
|
1637
1709
|
rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
|
1638
1710
|
#ifdef HAVE_PQRESULTVERBOSEERRORMESSAGE
|
@@ -1642,12 +1714,14 @@ init_pg_result(void)
|
|
1642
1714
|
rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
|
1643
1715
|
rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
|
1644
1716
|
rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
|
1717
|
+
rb_define_method(rb_cPGresult, "freeze", pg_result_freeze, 0 );
|
1645
1718
|
rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
|
1646
1719
|
rb_define_alias (rb_cPGresult, "check_result", "check");
|
1647
1720
|
rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
|
1648
1721
|
rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
|
1649
1722
|
rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
|
1650
1723
|
rb_define_alias(rb_cPGresult, "num_fields", "nfields");
|
1724
|
+
rb_define_method(rb_cPGresult, "binary_tuples", pgresult_binary_tuples, 0);
|
1651
1725
|
rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
|
1652
1726
|
rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
|
1653
1727
|
rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
|
data/ext/pg_text_decoder.c
CHANGED
@@ -43,7 +43,6 @@
|
|
43
43
|
#include <string.h>
|
44
44
|
|
45
45
|
VALUE rb_mPG_TextDecoder;
|
46
|
-
static ID s_id_decode;
|
47
46
|
static ID s_id_Rational;
|
48
47
|
static ID s_id_new;
|
49
48
|
static ID s_id_utc;
|
@@ -164,6 +163,8 @@ pg_text_dec_integer(t_pg_coder *conv, const char *val, int len, int tuple, int f
|
|
164
163
|
* This is a decoder class for conversion of PostgreSQL numeric types
|
165
164
|
* to Ruby BigDecimal objects.
|
166
165
|
*
|
166
|
+
* As soon as this class is used, it requires the 'bigdecimal' gem.
|
167
|
+
*
|
167
168
|
*/
|
168
169
|
static VALUE
|
169
170
|
pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
|
@@ -171,6 +172,19 @@ pg_text_dec_numeric(t_pg_coder *conv, const char *val, int len, int tuple, int f
|
|
171
172
|
return rb_funcall(rb_cObject, s_id_BigDecimal, 1, rb_str_new(val, len));
|
172
173
|
}
|
173
174
|
|
175
|
+
/* called per autoload when TextDecoder::Numeric is used */
|
176
|
+
static VALUE
|
177
|
+
init_pg_text_decoder_numeric(VALUE rb_mPG_TextDecoder)
|
178
|
+
{
|
179
|
+
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
180
|
+
s_id_BigDecimal = rb_intern("BigDecimal");
|
181
|
+
|
182
|
+
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
|
183
|
+
pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
184
|
+
|
185
|
+
return Qnil;
|
186
|
+
}
|
187
|
+
|
174
188
|
/*
|
175
189
|
* Document-class: PG::TextDecoder::Float < PG::SimpleDecoder
|
176
190
|
*
|
@@ -799,6 +813,7 @@ static VALUE pg_text_dec_timestamp(t_pg_coder *conv, const char *val, int len, i
|
|
799
813
|
* This is a decoder class for conversion of PostgreSQL inet type
|
800
814
|
* to Ruby IPAddr values.
|
801
815
|
*
|
816
|
+
* As soon as this class is used, it requires the ruby standard library 'ipaddr'.
|
802
817
|
*/
|
803
818
|
static VALUE
|
804
819
|
pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int field, int enc_idx)
|
@@ -922,8 +937,9 @@ pg_text_dec_inet(t_pg_coder *conv, const char *val, int len, int tuple, int fiel
|
|
922
937
|
return ip;
|
923
938
|
}
|
924
939
|
|
925
|
-
|
926
|
-
|
940
|
+
/* called per autoload when TextDecoder::Inet is used */
|
941
|
+
static VALUE
|
942
|
+
init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
|
927
943
|
{
|
928
944
|
rb_require("ipaddr");
|
929
945
|
s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
|
@@ -942,14 +958,21 @@ init_pg_text_decoder(void)
|
|
942
958
|
s_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");
|
943
959
|
rb_global_variable(&s_vmasks6);
|
944
960
|
|
945
|
-
|
961
|
+
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
|
962
|
+
pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
|
963
|
+
|
964
|
+
return Qnil;
|
965
|
+
}
|
966
|
+
|
967
|
+
|
968
|
+
void
|
969
|
+
init_pg_text_decoder(void)
|
970
|
+
{
|
946
971
|
s_id_Rational = rb_intern("Rational");
|
947
972
|
s_id_new = rb_intern("new");
|
948
973
|
s_id_utc = rb_intern("utc");
|
949
974
|
s_id_getlocal = rb_intern("getlocal");
|
950
975
|
|
951
|
-
rb_require("bigdecimal");
|
952
|
-
s_id_BigDecimal = rb_intern("BigDecimal");
|
953
976
|
s_nan = rb_eval_string("0.0/0.0");
|
954
977
|
rb_global_variable(&s_nan);
|
955
978
|
s_pos_inf = rb_eval_string("1.0/0.0");
|
@@ -959,6 +982,8 @@ init_pg_text_decoder(void)
|
|
959
982
|
|
960
983
|
/* This module encapsulates all decoder classes with text input format */
|
961
984
|
rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
|
985
|
+
rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_inet", init_pg_text_decoder_inet, 0);
|
986
|
+
rb_define_private_method(rb_singleton_class(rb_mPG_TextDecoder), "init_numeric", init_pg_text_decoder_numeric, 0);
|
962
987
|
|
963
988
|
/* Make RDoc aware of the decoder classes... */
|
964
989
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Boolean", rb_cPG_SimpleDecoder ); */
|
@@ -967,8 +992,6 @@ init_pg_text_decoder(void)
|
|
967
992
|
pg_define_coder( "Integer", pg_text_dec_integer, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
968
993
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Float", rb_cPG_SimpleDecoder ); */
|
969
994
|
pg_define_coder( "Float", pg_text_dec_float, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
970
|
-
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Numeric", rb_cPG_SimpleDecoder ); */
|
971
|
-
pg_define_coder( "Numeric", pg_text_dec_numeric, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
972
995
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "String", rb_cPG_SimpleDecoder ); */
|
973
996
|
pg_define_coder( "String", pg_text_dec_string, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
974
997
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Bytea", rb_cPG_SimpleDecoder ); */
|
@@ -977,8 +1000,6 @@ init_pg_text_decoder(void)
|
|
977
1000
|
pg_define_coder( "Identifier", pg_text_dec_identifier, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder );
|
978
1001
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Timestamp", rb_cPG_SimpleDecoder ); */
|
979
1002
|
pg_define_coder( "Timestamp", pg_text_dec_timestamp, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
|
980
|
-
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
|
981
|
-
pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
|
982
1003
|
|
983
1004
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Array", rb_cPG_CompositeDecoder ); */
|
984
1005
|
pg_define_coder( "Array", pg_text_dec_array, rb_cPG_CompositeDecoder, rb_mPG_TextDecoder );
|
data/ext/pg_text_encoder.c
CHANGED
@@ -119,6 +119,10 @@ pg_text_enc_boolean(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
119
119
|
int
|
120
120
|
pg_coder_enc_to_s(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
121
121
|
{
|
122
|
+
/* Attention:
|
123
|
+
* In contrast to all other encoders, the "this" pointer of this encoder can be NULL.
|
124
|
+
* This is because it is used as a fall-back if no encoder is defined.
|
125
|
+
*/
|
122
126
|
VALUE str = rb_obj_as_string(value);
|
123
127
|
if( ENCODING_GET(str) == enc_idx ){
|
124
128
|
*intermediate = str;
|
@@ -345,6 +349,8 @@ pg_text_enc_float(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
345
349
|
*
|
346
350
|
* It converts Integer, Float and BigDecimal objects.
|
347
351
|
* All other objects are expected to respond to +to_s+.
|
352
|
+
*
|
353
|
+
* As soon as this class is used, it requires the 'bigdecimal' gem.
|
348
354
|
*/
|
349
355
|
static int
|
350
356
|
pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediate, int enc_idx)
|
@@ -371,6 +377,21 @@ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
371
377
|
}
|
372
378
|
}
|
373
379
|
|
380
|
+
/* called per autoload when TextEncoder::Numeric is used */
|
381
|
+
static VALUE
|
382
|
+
init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
|
383
|
+
{
|
384
|
+
s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
|
385
|
+
rb_global_variable(&s_str_F);
|
386
|
+
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
387
|
+
s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
388
|
+
|
389
|
+
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
|
390
|
+
pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
391
|
+
|
392
|
+
return Qnil;
|
393
|
+
}
|
394
|
+
|
374
395
|
|
375
396
|
static const char hextab[] = {
|
376
397
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
@@ -383,8 +404,12 @@ static const char hextab[] = {
|
|
383
404
|
*
|
384
405
|
* The binary String is converted to hexadecimal representation for transmission
|
385
406
|
* in text format. For query bind parameters it is recommended to use
|
386
|
-
* PG::BinaryEncoder::Bytea
|
387
|
-
* CPU usage.
|
407
|
+
* PG::BinaryEncoder::Bytea or the hash form <tt>{value: binary_string, format: 1}</tt> instead,
|
408
|
+
* in order to decrease network traffic and CPU usage.
|
409
|
+
* See PG::Connection#exec_params for using the hash form.
|
410
|
+
*
|
411
|
+
* This encoder is particular useful when PG::TextEncoder::CopyRow is used with the COPY command.
|
412
|
+
* In this case there's no way to change the format of a single column to binary, so that the data have to be converted to bytea hex representation.
|
388
413
|
*
|
389
414
|
*/
|
390
415
|
static int
|
@@ -418,7 +443,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
418
443
|
t_pg_composite_coder *this = _this;
|
419
444
|
char *ptr1;
|
420
445
|
char *ptr2;
|
421
|
-
int
|
446
|
+
int backslashes = 0;
|
422
447
|
int needquote;
|
423
448
|
|
424
449
|
/* count data plus backslashes; detect chars needing quotes */
|
@@ -435,7 +460,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
435
460
|
|
436
461
|
if (ch == '"' || ch == '\\'){
|
437
462
|
needquote = 1;
|
438
|
-
|
463
|
+
backslashes++;
|
439
464
|
} else if (ch == '{' || ch == '}' || ch == this->delimiter ||
|
440
465
|
ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'){
|
441
466
|
needquote = 1;
|
@@ -444,12 +469,12 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
444
469
|
|
445
470
|
if( needquote ){
|
446
471
|
ptr1 = p_in + strlen;
|
447
|
-
ptr2 = p_out + strlen +
|
472
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
448
473
|
/* Write end quote */
|
449
474
|
*--ptr2 = '"';
|
450
475
|
|
451
476
|
/* Then store the escaped string on the final position, walking
|
452
|
-
* right to left, until all
|
477
|
+
* right to left, until all backslashes are placed. */
|
453
478
|
while( ptr1 != p_in ) {
|
454
479
|
*--ptr2 = *--ptr1;
|
455
480
|
if(*ptr2 == '"' || *ptr2 == '\\'){
|
@@ -458,7 +483,7 @@ quote_array_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
458
483
|
}
|
459
484
|
/* Write start quote */
|
460
485
|
*p_out = '"';
|
461
|
-
return strlen +
|
486
|
+
return strlen + backslashes + 2;
|
462
487
|
} else {
|
463
488
|
if( p_in != p_out )
|
464
489
|
memcpy( p_out, p_in, strlen );
|
@@ -673,22 +698,22 @@ static int
|
|
673
698
|
quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
674
699
|
char *ptr1;
|
675
700
|
char *ptr2;
|
676
|
-
int
|
701
|
+
int backslashes = 0;
|
677
702
|
|
678
703
|
/* count required backlashs */
|
679
704
|
for(ptr1 = p_in; ptr1 != p_in + strlen; ptr1++) {
|
680
705
|
if (*ptr1 == '\''){
|
681
|
-
|
706
|
+
backslashes++;
|
682
707
|
}
|
683
708
|
}
|
684
709
|
|
685
710
|
ptr1 = p_in + strlen;
|
686
|
-
ptr2 = p_out + strlen +
|
711
|
+
ptr2 = p_out + strlen + backslashes + 2;
|
687
712
|
/* Write end quote */
|
688
713
|
*--ptr2 = '\'';
|
689
714
|
|
690
715
|
/* Then store the escaped string on the final position, walking
|
691
|
-
* right to left, until all
|
716
|
+
* right to left, until all backslashes are placed. */
|
692
717
|
while( ptr1 != p_in ) {
|
693
718
|
*--ptr2 = *--ptr1;
|
694
719
|
if(*ptr2 == '\''){
|
@@ -697,7 +722,7 @@ quote_literal_buffer( void *_this, char *p_in, int strlen, char *p_out ){
|
|
697
722
|
}
|
698
723
|
/* Write start quote */
|
699
724
|
*p_out = '\'';
|
700
|
-
return strlen +
|
725
|
+
return strlen + backslashes + 2;
|
701
726
|
}
|
702
727
|
|
703
728
|
|
@@ -780,14 +805,10 @@ init_pg_text_encoder(void)
|
|
780
805
|
s_id_encode = rb_intern("encode");
|
781
806
|
s_id_to_i = rb_intern("to_i");
|
782
807
|
s_id_to_s = rb_intern("to_s");
|
783
|
-
s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
|
784
|
-
rb_global_variable(&s_str_F);
|
785
|
-
rb_require("bigdecimal");
|
786
|
-
s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
787
|
-
|
788
808
|
|
789
809
|
/* This module encapsulates all encoder classes with text output format */
|
790
810
|
rb_mPG_TextEncoder = rb_define_module_under( rb_mPG, "TextEncoder" );
|
811
|
+
rb_define_private_method(rb_singleton_class(rb_mPG_TextEncoder), "init_numeric", init_pg_text_encoder_numeric, 0);
|
791
812
|
|
792
813
|
/* Make RDoc aware of the encoder classes... */
|
793
814
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Boolean", rb_cPG_SimpleEncoder ); */
|
@@ -796,8 +817,6 @@ init_pg_text_encoder(void)
|
|
796
817
|
pg_define_coder( "Integer", pg_text_enc_integer, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
797
818
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Float", rb_cPG_SimpleEncoder ); */
|
798
819
|
pg_define_coder( "Float", pg_text_enc_float, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
799
|
-
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Numeric", rb_cPG_SimpleEncoder ); */
|
800
|
-
pg_define_coder( "Numeric", pg_text_enc_numeric, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
801
820
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "String", rb_cPG_SimpleEncoder ); */
|
802
821
|
pg_define_coder( "String", pg_coder_enc_to_s, rb_cPG_SimpleEncoder, rb_mPG_TextEncoder );
|
803
822
|
/* dummy = rb_define_class_under( rb_mPG_TextEncoder, "Bytea", rb_cPG_SimpleEncoder ); */
|