pg 1.6.2 → 1.7.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +61 -0
- data/Gemfile +5 -4
- data/README.ja.md +81 -30
- data/README.md +20 -10
- data/Rakefile +13 -26
- data/certs/larskanis-2026.pem +24 -0
- data/ext/errorcodes.def +9 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +8 -1
- data/ext/extconf.rb +19 -28
- data/ext/gvl_wrappers.c +2 -2
- data/ext/gvl_wrappers.h +40 -160
- data/ext/pg.c +8 -12
- data/ext/pg.h +14 -15
- data/ext/pg_binary_decoder.c +6 -2
- data/ext/pg_binary_encoder.c +7 -0
- data/ext/pg_cancel_connection.c +6 -6
- data/ext/pg_coder.c +4 -4
- data/ext/pg_connection.c +218 -161
- data/ext/pg_copy_coder.c +34 -21
- data/ext/pg_record_coder.c +10 -6
- data/ext/pg_result.c +131 -61
- data/ext/pg_text_decoder.c +6 -6
- data/ext/pg_text_encoder.c +7 -2
- data/ext/pg_tuple.c +2 -2
- data/ext/pg_type_map.c +41 -1
- data/ext/pg_type_map_all_strings.c +2 -2
- data/ext/pg_type_map_by_class.c +1 -1
- data/ext/pg_type_map_by_column.c +2 -2
- data/ext/pg_type_map_by_oid.c +1 -1
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/lib/pg/basic_type_map_based_on_result.rb +5 -0
- data/lib/pg/basic_type_map_for_queries.rb +9 -2
- data/lib/pg/basic_type_map_for_results.rb +5 -0
- data/lib/pg/connection.rb +166 -76
- data/lib/pg/text_decoder/date.rb +1 -1
- data/lib/pg/text_decoder/json.rb +1 -1
- data/lib/pg/text_encoder/date.rb +7 -1
- data/lib/pg/text_encoder/json.rb +1 -1
- data/lib/pg/version.rb +1 -1
- data/misc/yugabyte/docker-compose.yml +1 -1
- data/pg.gemspec +1 -1
- data/sample/async_mixed.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +19 -20
- metadata.gz.sig +0 -0
- data/ports/patches/openssl/3.5.2/0001-aarch64-mingw.patch +0 -21
- data/ports/patches/postgresql/17.6/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +0 -52
- /data/ports/patches/krb5/{1.22.1 → 1.22.2}/0001-Allow-static-linking-krb5-library.patch +0 -0
- /data/ports/patches/krb5/{1.22.1 → 1.22.2}/0002-unknown-command-line-option-on-clang.patch +0 -0
- /data/ports/patches/postgresql/{17.6 → 18.6}/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +0 -0
data/ext/pg_result.c
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
#include "pg.h"
|
|
8
8
|
|
|
9
9
|
VALUE rb_cPGresult;
|
|
10
|
-
static VALUE sym_symbol, sym_string
|
|
10
|
+
static VALUE sym_symbol, sym_string;
|
|
11
11
|
|
|
12
12
|
static VALUE pgresult_type_map_set( VALUE, VALUE );
|
|
13
13
|
static t_pg_result *pgresult_get_this( VALUE );
|
|
14
14
|
static t_pg_result *pgresult_get_this_safe( VALUE );
|
|
15
|
+
static void ensure_init_for_tuple(VALUE self);
|
|
15
16
|
|
|
16
17
|
#if defined(HAVE_PQRESULTMEMORYSIZE)
|
|
17
18
|
|
|
@@ -114,7 +115,6 @@ pgresult_gc_mark( void *_this )
|
|
|
114
115
|
|
|
115
116
|
rb_gc_mark_movable( this->connection );
|
|
116
117
|
rb_gc_mark_movable( this->typemap );
|
|
117
|
-
rb_gc_mark_movable( this->tuple_hash );
|
|
118
118
|
rb_gc_mark_movable( this->field_map );
|
|
119
119
|
|
|
120
120
|
for( i=0; i < this->nfields; i++ ){
|
|
@@ -130,7 +130,6 @@ pgresult_gc_compact( void *_this )
|
|
|
130
130
|
|
|
131
131
|
pg_gc_location( this->connection );
|
|
132
132
|
pg_gc_location( this->typemap );
|
|
133
|
-
pg_gc_location( this->tuple_hash );
|
|
134
133
|
pg_gc_location( this->field_map );
|
|
135
134
|
|
|
136
135
|
for( i=0; i < this->nfields; i++ ){
|
|
@@ -181,7 +180,7 @@ static const rb_data_type_t pgresult_type = {
|
|
|
181
180
|
pgresult_gc_compact,
|
|
182
181
|
},
|
|
183
182
|
0, 0,
|
|
184
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
183
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
185
184
|
};
|
|
186
185
|
|
|
187
186
|
/* Needed by sequel_pg gem, do not delete */
|
|
@@ -212,7 +211,6 @@ pg_new_result2(PGresult *result, VALUE rb_pgconn)
|
|
|
212
211
|
this->typemap = pg_typemap_all_strings;
|
|
213
212
|
this->p_typemap = RTYPEDDATA_DATA( this->typemap );
|
|
214
213
|
this->nfields = -1;
|
|
215
|
-
this->tuple_hash = Qnil;
|
|
216
214
|
this->field_map = Qnil;
|
|
217
215
|
this->flags = 0;
|
|
218
216
|
self = TypedData_Wrap_Struct(rb_cPGresult, &pgresult_type, this);
|
|
@@ -317,17 +315,17 @@ pg_result_check( VALUE self )
|
|
|
317
315
|
case PGRES_SINGLE_TUPLE:
|
|
318
316
|
case PGRES_EMPTY_QUERY:
|
|
319
317
|
case PGRES_COMMAND_OK:
|
|
320
|
-
#ifdef
|
|
318
|
+
#ifdef LIBPQ_HAS_PIPELINING
|
|
321
319
|
case PGRES_PIPELINE_SYNC:
|
|
322
320
|
#endif
|
|
323
|
-
#ifdef
|
|
321
|
+
#ifdef LIBPQ_HAS_CHUNK_MODE
|
|
324
322
|
case PGRES_TUPLES_CHUNK:
|
|
325
323
|
#endif
|
|
326
324
|
return self;
|
|
327
325
|
case PGRES_BAD_RESPONSE:
|
|
328
326
|
case PGRES_FATAL_ERROR:
|
|
329
327
|
case PGRES_NONFATAL_ERROR:
|
|
330
|
-
#ifdef
|
|
328
|
+
#ifdef LIBPQ_HAS_PIPELINING
|
|
331
329
|
case PGRES_PIPELINE_ABORTED:
|
|
332
330
|
#endif
|
|
333
331
|
error = rb_str_new2( PQresultErrorMessage(this->pgresult) );
|
|
@@ -396,6 +394,7 @@ pg_result_freeze(VALUE self)
|
|
|
396
394
|
{
|
|
397
395
|
t_pg_result *this = pgresult_get_this(self);
|
|
398
396
|
|
|
397
|
+
ensure_init_for_tuple(self);
|
|
399
398
|
RB_OBJ_WRITE(self, &this->connection, Qnil);
|
|
400
399
|
return rb_call_super(0, NULL);
|
|
401
400
|
}
|
|
@@ -466,9 +465,6 @@ pgresult_get(VALUE self)
|
|
|
466
465
|
static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
467
466
|
{
|
|
468
467
|
VALUE fname;
|
|
469
|
-
#ifdef TRUFFLERUBY
|
|
470
|
-
if( flags & (PG_RESULT_FIELD_NAMES_SYMBOL | PG_RESULT_FIELD_NAMES_STATIC_SYMBOL) ){
|
|
471
|
-
#else
|
|
472
468
|
if( flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
473
469
|
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
474
470
|
fname = rb_check_symbol_cstr(cstr, strlen(cstr), enc);
|
|
@@ -477,14 +473,9 @@ static VALUE pg_cstr_to_sym(char *cstr, unsigned int flags, int enc_idx)
|
|
|
477
473
|
PG_ENCODING_SET_NOCHECK(fname, enc_idx);
|
|
478
474
|
fname = rb_str_intern(fname);
|
|
479
475
|
}
|
|
480
|
-
} else if( flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
481
|
-
#endif
|
|
482
|
-
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
483
|
-
fname = ID2SYM(rb_intern3(cstr, strlen(cstr), enc));
|
|
484
476
|
} else {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
fname = rb_obj_freeze(fname);
|
|
477
|
+
rb_encoding *enc = rb_enc_from_index(enc_idx);
|
|
478
|
+
fname = rb_enc_interned_str(cstr, strlen(cstr), enc);
|
|
488
479
|
}
|
|
489
480
|
return fname;
|
|
490
481
|
}
|
|
@@ -513,20 +504,48 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
513
504
|
*
|
|
514
505
|
* The class to represent the query result tuples (rows).
|
|
515
506
|
* An instance of this class is created as the result of every query.
|
|
516
|
-
* All result rows and columns are stored in
|
|
517
|
-
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map .
|
|
507
|
+
* All result rows and columns are stored in an immutable memory block attached to the PG::Result object unless streaming is used.
|
|
518
508
|
*
|
|
519
|
-
*
|
|
520
|
-
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
521
|
-
*
|
|
522
|
-
* Example:
|
|
509
|
+
* A PG::Result has various ways to retrieve the result data:
|
|
523
510
|
* require 'pg'
|
|
524
|
-
* conn = PG.connect(:
|
|
525
|
-
* res
|
|
526
|
-
* res.
|
|
527
|
-
* res
|
|
528
|
-
* res
|
|
511
|
+
* conn = PG.connect(dbname: 'test')
|
|
512
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
513
|
+
* res.num_fields # 3
|
|
514
|
+
* res.num_tuples # 1
|
|
515
|
+
* res.fname(2) # "c"
|
|
516
|
+
* res.fields # ["a", "b", "c"]
|
|
517
|
+
* res.getvalue(0,0) # '1'
|
|
518
|
+
* res[0] # {"a" => "1", "b" => "2", "c" => "3"}
|
|
519
|
+
* res.tuple_values(0) # ["1", "2", nil]
|
|
520
|
+
* res.tuple(0) # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
521
|
+
* res.values # [["1", "2", nil]]
|
|
522
|
+
* res.field_values(:a) # ["1"]
|
|
523
|
+
* res.column_values(1) # ["2"]
|
|
524
|
+
* res.each.first # {"a" => "1", "b" => "2", "c" => nil}
|
|
525
|
+
* res.each_row.first # ["1", "2", nil]
|
|
526
|
+
* res.each_tuple.first # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
527
|
+
*
|
|
528
|
+
* Whenever a value is accessed it is casted to a Ruby object by the assigned #type_map which is PG::TypeMapAllStrings by default.
|
|
529
|
+
* Similarly field names can be retrieved either as strings (default) or as symbols which can be switched per #field_name_type= .
|
|
530
|
+
*
|
|
531
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
532
|
+
* res.type_map = PG::TypeMapByColumn.new([PG::TextDecoder::Integer.new]*3)
|
|
533
|
+
* res.field_name_type = :symbol
|
|
534
|
+
* res.fname(2) # :c
|
|
535
|
+
* res.fields # [:a, :b, :c]
|
|
536
|
+
* res.getvalue(0,0) # 1
|
|
537
|
+
* res[0] # {a: 1, b: 2, c: nil}
|
|
538
|
+
* res.tuple_values(0) # [1, 2, nil]
|
|
539
|
+
* res.tuple(0) # #<PG::Tuple a: 1, b: 2, c: nil>
|
|
540
|
+
* res.values # [[1, 2, nil]]
|
|
541
|
+
* res.field_values(:a) # [1]
|
|
542
|
+
* res.column_values(1) # [2]
|
|
543
|
+
* res.each.first # {a: 1, b: 2, c: nil}
|
|
544
|
+
* res.each_row.first # [1, 2, nil]
|
|
545
|
+
* res.each_tuple.first # #<PG::Tuple a: 1, b: 2, c: nil>
|
|
529
546
|
*
|
|
547
|
+
* Since pg-1.1 the amount of memory in use by a PG::Result object is estimated and passed to ruby's garbage collector.
|
|
548
|
+
* You can invoke the #clear method to force deallocation of memory of the instance when finished with the result for better memory performance.
|
|
530
549
|
*/
|
|
531
550
|
|
|
532
551
|
/**************************************************************************
|
|
@@ -552,7 +571,7 @@ static void pgresult_init_fnames(VALUE self)
|
|
|
552
571
|
* * +PGRES_PIPELINE_SYNC+
|
|
553
572
|
* * +PGRES_PIPELINE_ABORTED+
|
|
554
573
|
*
|
|
555
|
-
* Use
|
|
574
|
+
* Use #res_status or Result.res_status to retrieve the string representation.
|
|
556
575
|
*/
|
|
557
576
|
static VALUE
|
|
558
577
|
pgresult_result_status(VALUE self)
|
|
@@ -564,7 +583,7 @@ pgresult_result_status(VALUE self)
|
|
|
564
583
|
* call-seq:
|
|
565
584
|
* PG::Result.res_status( status ) -> String
|
|
566
585
|
*
|
|
567
|
-
* Returns the string representation of
|
|
586
|
+
* Returns the string representation of #result_status.
|
|
568
587
|
*
|
|
569
588
|
*/
|
|
570
589
|
static VALUE
|
|
@@ -578,7 +597,7 @@ pgresult_s_res_status(VALUE self, VALUE status)
|
|
|
578
597
|
* res.res_status -> String
|
|
579
598
|
* res.res_status( status ) -> String
|
|
580
599
|
*
|
|
581
|
-
* Returns the string representation of the status of the result or of the provided
|
|
600
|
+
* Returns the string representation of the status of the result or of the provided #result_status.
|
|
582
601
|
*
|
|
583
602
|
*/
|
|
584
603
|
static VALUE
|
|
@@ -705,6 +724,10 @@ pgresult_error_field(VALUE self, VALUE field)
|
|
|
705
724
|
* res.ntuples() -> Integer
|
|
706
725
|
*
|
|
707
726
|
* Returns the number of tuples in the query result.
|
|
727
|
+
*
|
|
728
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
729
|
+
* res.ntuples # 1
|
|
730
|
+
* res.num_tuples # 1
|
|
708
731
|
*/
|
|
709
732
|
static VALUE
|
|
710
733
|
pgresult_ntuples(VALUE self)
|
|
@@ -723,6 +746,9 @@ pgresult_ntuples_for_enum(VALUE self, VALUE args, VALUE eobj)
|
|
|
723
746
|
* res.nfields() -> Integer
|
|
724
747
|
*
|
|
725
748
|
* Returns the number of columns in the query result.
|
|
749
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
750
|
+
* res.nfields # 3
|
|
751
|
+
* res.num_fields # 3
|
|
726
752
|
*/
|
|
727
753
|
static VALUE
|
|
728
754
|
pgresult_nfields(VALUE self)
|
|
@@ -752,6 +778,8 @@ pgresult_binary_tuples(VALUE self)
|
|
|
752
778
|
* Returns the name of the column corresponding to _index_.
|
|
753
779
|
* Depending on #field_name_type= it's a String or Symbol.
|
|
754
780
|
*
|
|
781
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
782
|
+
* res.fname(2) # "c"
|
|
755
783
|
*/
|
|
756
784
|
static VALUE
|
|
757
785
|
pgresult_fname(VALUE self, VALUE index)
|
|
@@ -1122,6 +1150,9 @@ pgresult_oid_value(VALUE self)
|
|
|
1122
1150
|
* res[ n ] -> Hash
|
|
1123
1151
|
*
|
|
1124
1152
|
* Returns tuple _n_ as a hash.
|
|
1153
|
+
*
|
|
1154
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1155
|
+
* res[0] # {"a" => "1", "b" => "2", "c" => "3"}
|
|
1125
1156
|
*/
|
|
1126
1157
|
static VALUE
|
|
1127
1158
|
pgresult_aref(VALUE self, VALUE index)
|
|
@@ -1138,16 +1169,11 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
1138
1169
|
if ( tuple_num < 0 || tuple_num >= num_tuples )
|
|
1139
1170
|
rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
|
|
1140
1171
|
|
|
1141
|
-
|
|
1142
|
-
* This is somewhat faster than populating an empty Hash object. */
|
|
1143
|
-
tuple = NIL_P(this->tuple_hash) ? rb_hash_new() : this->tuple_hash;
|
|
1172
|
+
tuple = rb_hash_new_capa(this->nfields);
|
|
1144
1173
|
for ( field_num = 0; field_num < this->nfields; field_num++ ) {
|
|
1145
1174
|
VALUE val = this->p_typemap->funcs.typecast_result_value(this->p_typemap, self, tuple_num, field_num);
|
|
1146
1175
|
rb_hash_aset( tuple, this->fnames[field_num], val );
|
|
1147
1176
|
}
|
|
1148
|
-
/* Store a copy of the filled hash for use at the next row. */
|
|
1149
|
-
if( num_tuples > 10 )
|
|
1150
|
-
RB_OBJ_WRITE(self, &this->tuple_hash, rb_hash_dup(tuple));
|
|
1151
1177
|
|
|
1152
1178
|
return tuple;
|
|
1153
1179
|
}
|
|
@@ -1156,7 +1182,10 @@ pgresult_aref(VALUE self, VALUE index)
|
|
|
1156
1182
|
* call-seq:
|
|
1157
1183
|
* res.each_row { |row| ... }
|
|
1158
1184
|
*
|
|
1159
|
-
* Yields
|
|
1185
|
+
* Yields an Array object for each row in the result.
|
|
1186
|
+
*
|
|
1187
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1188
|
+
* res.each_row.first # ["1", "2", nil]
|
|
1160
1189
|
*/
|
|
1161
1190
|
static VALUE
|
|
1162
1191
|
pgresult_each_row(VALUE self)
|
|
@@ -1191,6 +1220,9 @@ pgresult_each_row(VALUE self)
|
|
|
1191
1220
|
* res.values -> Array
|
|
1192
1221
|
*
|
|
1193
1222
|
* Returns all tuples as an array of arrays.
|
|
1223
|
+
*
|
|
1224
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1225
|
+
* res.values # [["1", "2", nil]]
|
|
1194
1226
|
*/
|
|
1195
1227
|
static VALUE
|
|
1196
1228
|
pgresult_values(VALUE self)
|
|
@@ -1240,12 +1272,13 @@ make_column_result_array( VALUE self, int col )
|
|
|
1240
1272
|
|
|
1241
1273
|
|
|
1242
1274
|
/*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1275
|
+
* call-seq:
|
|
1276
|
+
* res.column_values( n ) -> array
|
|
1245
1277
|
*
|
|
1246
|
-
*
|
|
1247
|
-
* tuple in the result.
|
|
1278
|
+
* Returns an Array of the values from the nth column of each tuple in the result.
|
|
1248
1279
|
*
|
|
1280
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1281
|
+
* res.column_values(1) # ["2"]
|
|
1249
1282
|
*/
|
|
1250
1283
|
static VALUE
|
|
1251
1284
|
pgresult_column_values(VALUE self, VALUE index)
|
|
@@ -1256,11 +1289,13 @@ pgresult_column_values(VALUE self, VALUE index)
|
|
|
1256
1289
|
|
|
1257
1290
|
|
|
1258
1291
|
/*
|
|
1259
|
-
*
|
|
1292
|
+
* call-seq:
|
|
1260
1293
|
* res.field_values( field ) -> array
|
|
1261
1294
|
*
|
|
1262
|
-
*
|
|
1295
|
+
* Returns an Array of the values from the given _field_ of each tuple in the result.
|
|
1263
1296
|
*
|
|
1297
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1298
|
+
* res.field_values(:a) # ["1"]
|
|
1264
1299
|
*/
|
|
1265
1300
|
static VALUE
|
|
1266
1301
|
pgresult_field_values( VALUE self, VALUE field )
|
|
@@ -1281,11 +1316,13 @@ pgresult_field_values( VALUE self, VALUE field )
|
|
|
1281
1316
|
|
|
1282
1317
|
|
|
1283
1318
|
/*
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1319
|
+
* call-seq:
|
|
1320
|
+
* res.tuple_values( n ) -> array
|
|
1286
1321
|
*
|
|
1287
|
-
*
|
|
1322
|
+
* Returns an Array of the field values from the nth row of the result.
|
|
1288
1323
|
*
|
|
1324
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1325
|
+
* res.tuple_values(0) # ["1", "2", nil]
|
|
1289
1326
|
*/
|
|
1290
1327
|
static VALUE
|
|
1291
1328
|
pgresult_tuple_values(VALUE self, VALUE index)
|
|
@@ -1320,11 +1357,12 @@ static void ensure_init_for_tuple(VALUE self)
|
|
|
1320
1357
|
|
|
1321
1358
|
if( this->field_map == Qnil ){
|
|
1322
1359
|
int i;
|
|
1323
|
-
VALUE field_map
|
|
1360
|
+
VALUE field_map;
|
|
1324
1361
|
|
|
1325
1362
|
if( this->nfields == -1 )
|
|
1326
1363
|
pgresult_init_fnames( self );
|
|
1327
1364
|
|
|
1365
|
+
field_map = rb_hash_new_capa(this->nfields);
|
|
1328
1366
|
for( i = 0; i < this->nfields; i++ ){
|
|
1329
1367
|
rb_hash_aset(field_map, this->fnames[i], INT2FIX(i));
|
|
1330
1368
|
}
|
|
@@ -1334,11 +1372,13 @@ static void ensure_init_for_tuple(VALUE self)
|
|
|
1334
1372
|
}
|
|
1335
1373
|
|
|
1336
1374
|
/*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1375
|
+
* call-seq:
|
|
1376
|
+
* res.tuple( n ) -> PG::Tuple
|
|
1339
1377
|
*
|
|
1340
|
-
*
|
|
1378
|
+
* Returns a PG::Tuple from the nth row of the result.
|
|
1341
1379
|
*
|
|
1380
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1381
|
+
* res.tuple(0) # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
1342
1382
|
*/
|
|
1343
1383
|
static VALUE
|
|
1344
1384
|
pgresult_tuple(VALUE self, VALUE index)
|
|
@@ -1363,7 +1403,10 @@ pgresult_tuple(VALUE self, VALUE index)
|
|
|
1363
1403
|
* call-seq:
|
|
1364
1404
|
* res.each{ |tuple| ... }
|
|
1365
1405
|
*
|
|
1366
|
-
*
|
|
1406
|
+
* Yields a Hash object for each row in the result.
|
|
1407
|
+
*
|
|
1408
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1409
|
+
* res.each.first # {"a" => "1", "b" => "2", "c" => nil}
|
|
1367
1410
|
*/
|
|
1368
1411
|
static VALUE
|
|
1369
1412
|
pgresult_each(VALUE self)
|
|
@@ -1381,11 +1424,40 @@ pgresult_each(VALUE self)
|
|
|
1381
1424
|
return self;
|
|
1382
1425
|
}
|
|
1383
1426
|
|
|
1427
|
+
/*
|
|
1428
|
+
* call-seq:
|
|
1429
|
+
* res.each_tuple{ |tuple| ... }
|
|
1430
|
+
*
|
|
1431
|
+
* Yields a PG::Tuple object for each row in the result.
|
|
1432
|
+
*
|
|
1433
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1434
|
+
* res.each_tuple.first # #<PG::Tuple a: "1", b: "2", c: nil>
|
|
1435
|
+
*/
|
|
1436
|
+
static VALUE
|
|
1437
|
+
pgresult_each_tuple(VALUE self)
|
|
1438
|
+
{
|
|
1439
|
+
PGresult *result;
|
|
1440
|
+
int tuple_num;
|
|
1441
|
+
|
|
1442
|
+
RETURN_SIZED_ENUMERATOR(self, 0, NULL, pgresult_ntuples_for_enum);
|
|
1443
|
+
|
|
1444
|
+
result = pgresult_get(self);
|
|
1445
|
+
|
|
1446
|
+
for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
|
|
1447
|
+
VALUE tuple = pgresult_tuple(self, INT2FIX(tuple_num));
|
|
1448
|
+
rb_yield( tuple );
|
|
1449
|
+
}
|
|
1450
|
+
return self;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1384
1453
|
/*
|
|
1385
1454
|
* call-seq:
|
|
1386
1455
|
* res.fields() -> Array
|
|
1387
1456
|
*
|
|
1388
1457
|
* Depending on #field_name_type= returns an array of strings or symbols representing the names of the fields in the result.
|
|
1458
|
+
*
|
|
1459
|
+
* res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
|
1460
|
+
* res.fields # ["a", "b", "c"]
|
|
1389
1461
|
*/
|
|
1390
1462
|
static VALUE
|
|
1391
1463
|
pgresult_fields(VALUE self)
|
|
@@ -1410,6 +1482,7 @@ pgresult_fields(VALUE self)
|
|
|
1410
1482
|
*
|
|
1411
1483
|
* +typemap+ must be a kind of PG::TypeMap .
|
|
1412
1484
|
*
|
|
1485
|
+
* See also #map_types! and PG::BasicTypeMapForResults
|
|
1413
1486
|
*/
|
|
1414
1487
|
static VALUE
|
|
1415
1488
|
pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
@@ -1433,6 +1506,7 @@ pgresult_type_map_set(VALUE self, VALUE typemap)
|
|
|
1433
1506
|
* res.type_map -> value
|
|
1434
1507
|
*
|
|
1435
1508
|
* Returns the PG::TypeMap that is currently set for type casts of result values to ruby objects.
|
|
1509
|
+
* The default is retrieved from PG::Connection#type_map_for_results , which defaults to PG::TypeMapAllStrings .
|
|
1436
1510
|
*
|
|
1437
1511
|
*/
|
|
1438
1512
|
static VALUE
|
|
@@ -1525,7 +1599,7 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
|
1525
1599
|
return self;
|
|
1526
1600
|
rb_raise( rb_eInvalidResultStatus, "PG::Result is not in single row mode");
|
|
1527
1601
|
case PGRES_SINGLE_TUPLE:
|
|
1528
|
-
#ifdef
|
|
1602
|
+
#ifdef LIBPQ_HAS_CHUNK_MODE
|
|
1529
1603
|
case PGRES_TUPLES_CHUNK:
|
|
1530
1604
|
#endif
|
|
1531
1605
|
break;
|
|
@@ -1543,12 +1617,12 @@ pgresult_stream_any(VALUE self, int (*yielder)(VALUE, int, int, void*), void* da
|
|
|
1543
1617
|
pgresult_clear( this );
|
|
1544
1618
|
}
|
|
1545
1619
|
|
|
1546
|
-
if(
|
|
1620
|
+
if( PQisBusy(pgconn) ){
|
|
1547
1621
|
/* wait for input (without blocking) before reading each result */
|
|
1548
1622
|
pgconn_block( 0, NULL, this->connection );
|
|
1549
1623
|
}
|
|
1550
1624
|
|
|
1551
|
-
pgresult =
|
|
1625
|
+
pgresult = PQgetResult(pgconn);
|
|
1552
1626
|
if( pgresult == NULL )
|
|
1553
1627
|
rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
|
|
1554
1628
|
|
|
@@ -1638,7 +1712,6 @@ pgresult_stream_each_tuple(VALUE self)
|
|
|
1638
1712
|
* It can be set to one of:
|
|
1639
1713
|
* * +:string+ to use String based field names
|
|
1640
1714
|
* * +:symbol+ to use Symbol based field names
|
|
1641
|
-
* * +:static_symbol+ to use pinned Symbol (can not be garbage collected) - Don't use this, it will probably be removed in future.
|
|
1642
1715
|
*
|
|
1643
1716
|
* The default is retrieved from PG::Connection#field_name_type , which defaults to +:string+ .
|
|
1644
1717
|
*
|
|
@@ -1661,7 +1734,6 @@ pgresult_field_name_type_set(VALUE self, VALUE sym)
|
|
|
1661
1734
|
|
|
1662
1735
|
this->flags &= ~PG_RESULT_FIELD_NAMES_MASK;
|
|
1663
1736
|
if( sym == sym_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_SYMBOL;
|
|
1664
|
-
else if ( sym == sym_static_symbol ) this->flags |= PG_RESULT_FIELD_NAMES_STATIC_SYMBOL;
|
|
1665
1737
|
else if ( sym == sym_string );
|
|
1666
1738
|
else rb_raise(rb_eArgError, "invalid argument %+"PRIsVALUE, sym);
|
|
1667
1739
|
|
|
@@ -1682,8 +1754,6 @@ pgresult_field_name_type_get(VALUE self)
|
|
|
1682
1754
|
t_pg_result *this = pgresult_get_this(self);
|
|
1683
1755
|
if( this->flags & PG_RESULT_FIELD_NAMES_SYMBOL ){
|
|
1684
1756
|
return sym_symbol;
|
|
1685
|
-
} else if( this->flags & PG_RESULT_FIELD_NAMES_STATIC_SYMBOL ){
|
|
1686
|
-
return sym_static_symbol;
|
|
1687
1757
|
} else {
|
|
1688
1758
|
return sym_string;
|
|
1689
1759
|
}
|
|
@@ -1694,7 +1764,6 @@ init_pg_result(void)
|
|
|
1694
1764
|
{
|
|
1695
1765
|
sym_string = ID2SYM(rb_intern("string"));
|
|
1696
1766
|
sym_symbol = ID2SYM(rb_intern("symbol"));
|
|
1697
|
-
sym_static_symbol = ID2SYM(rb_intern("static_symbol"));
|
|
1698
1767
|
|
|
1699
1768
|
rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
|
|
1700
1769
|
rb_undef_alloc_func(rb_cPGresult);
|
|
@@ -1743,6 +1812,7 @@ init_pg_result(void)
|
|
|
1743
1812
|
rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
|
|
1744
1813
|
rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
|
|
1745
1814
|
rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
|
|
1815
|
+
rb_define_method(rb_cPGresult, "each_tuple", pgresult_each_tuple, 0);
|
|
1746
1816
|
rb_define_method(rb_cPGresult, "values", pgresult_values, 0);
|
|
1747
1817
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
|
1748
1818
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
data/ext/pg_text_decoder.c
CHANGED
|
@@ -942,8 +942,8 @@ static VALUE
|
|
|
942
942
|
init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
|
|
943
943
|
{
|
|
944
944
|
rb_require("ipaddr");
|
|
945
|
+
rb_gc_register_address(&s_IPAddr);
|
|
945
946
|
s_IPAddr = rb_funcall(rb_cObject, rb_intern("const_get"), 1, rb_str_new2("IPAddr"));
|
|
946
|
-
rb_global_variable(&s_IPAddr);
|
|
947
947
|
s_ivar_family = rb_intern("@family");
|
|
948
948
|
s_ivar_addr = rb_intern("@addr");
|
|
949
949
|
s_ivar_mask_addr = rb_intern("@mask_addr");
|
|
@@ -953,10 +953,10 @@ init_pg_text_decoder_inet(VALUE rb_mPG_TextDecoder)
|
|
|
953
953
|
|
|
954
954
|
use_ipaddr_alloc = RTEST(rb_eval_string("IPAddr.new.instance_variables.sort == [:@addr, :@family, :@mask_addr]"));
|
|
955
955
|
|
|
956
|
+
rb_gc_register_address(&s_vmasks4);
|
|
957
|
+
rb_gc_register_address(&s_vmasks6);
|
|
956
958
|
s_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");
|
|
957
|
-
rb_global_variable(&s_vmasks4);
|
|
958
959
|
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");
|
|
959
|
-
rb_global_variable(&s_vmasks6);
|
|
960
960
|
|
|
961
961
|
/* dummy = rb_define_class_under( rb_mPG_TextDecoder, "Inet", rb_cPG_SimpleDecoder ); */
|
|
962
962
|
pg_define_coder( "Inet", pg_text_dec_inet, rb_cPG_SimpleDecoder, rb_mPG_TextDecoder);
|
|
@@ -973,12 +973,12 @@ init_pg_text_decoder(void)
|
|
|
973
973
|
s_id_utc = rb_intern("utc");
|
|
974
974
|
s_id_getlocal = rb_intern("getlocal");
|
|
975
975
|
|
|
976
|
+
rb_gc_register_address(&s_nan);
|
|
977
|
+
rb_gc_register_address(&s_pos_inf);
|
|
978
|
+
rb_gc_register_address(&s_neg_inf);
|
|
976
979
|
s_nan = rb_eval_string("0.0/0.0");
|
|
977
|
-
rb_global_variable(&s_nan);
|
|
978
980
|
s_pos_inf = rb_eval_string("1.0/0.0");
|
|
979
|
-
rb_global_variable(&s_pos_inf);
|
|
980
981
|
s_neg_inf = rb_eval_string("-1.0/0.0");
|
|
981
|
-
rb_global_variable(&s_neg_inf);
|
|
982
982
|
|
|
983
983
|
/* This module encapsulates all decoder classes with text input format */
|
|
984
984
|
rb_mPG_TextDecoder = rb_define_module_under( rb_mPG, "TextDecoder" );
|
data/ext/pg_text_encoder.c
CHANGED
|
@@ -381,8 +381,8 @@ pg_text_enc_numeric(t_pg_coder *this, VALUE value, char *out, VALUE *intermediat
|
|
|
381
381
|
static VALUE
|
|
382
382
|
init_pg_text_encoder_numeric(VALUE rb_mPG_TextDecoder)
|
|
383
383
|
{
|
|
384
|
+
rb_gc_register_address(&s_str_F);
|
|
384
385
|
s_str_F = rb_str_freeze(rb_str_new_cstr("F"));
|
|
385
|
-
rb_global_variable(&s_str_F);
|
|
386
386
|
rb_funcall(rb_mPG, rb_intern("require_bigdecimal_without_warning"), 0);
|
|
387
387
|
s_cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
|
388
388
|
|
|
@@ -430,9 +430,14 @@ pg_text_enc_bytea(t_pg_coder *conv, VALUE value, char *out, VALUE *intermediate,
|
|
|
430
430
|
}
|
|
431
431
|
return (int)(optr - out);
|
|
432
432
|
}else{
|
|
433
|
+
long size;
|
|
433
434
|
*intermediate = rb_obj_as_string(value);
|
|
435
|
+
size = RSTRING_LEN(*intermediate);
|
|
436
|
+
|
|
434
437
|
/* The output starts with "\x" and each character is converted to hex. */
|
|
435
|
-
|
|
438
|
+
if( size > INT_MAX / 2 - 2 )
|
|
439
|
+
rb_raise( rb_eArgError, "too large input string for PG::TextEncoder::Bytea: %ld", size );
|
|
440
|
+
return (int)(2 + size * 2);
|
|
436
441
|
}
|
|
437
442
|
}
|
|
438
443
|
|
data/ext/pg_tuple.c
CHANGED
|
@@ -128,7 +128,7 @@ static const rb_data_type_t pg_tuple_type = {
|
|
|
128
128
|
pg_tuple_gc_compact,
|
|
129
129
|
},
|
|
130
130
|
0, 0,
|
|
131
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
131
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
/*
|
|
@@ -510,7 +510,7 @@ pg_tuple_load(VALUE self, VALUE a)
|
|
|
510
510
|
if (RARRAY_LENINT(field_names) != num_fields)
|
|
511
511
|
rb_raise(rb_eTypeError, "different number of fields and values");
|
|
512
512
|
|
|
513
|
-
field_map =
|
|
513
|
+
field_map = rb_hash_new_capa(num_fields);
|
|
514
514
|
for( i = 0; i < num_fields; i++ ){
|
|
515
515
|
rb_hash_aset(field_map, RARRAY_AREF(field_names, i), INT2FIX(i));
|
|
516
516
|
}
|
data/ext/pg_type_map.c
CHANGED
|
@@ -37,7 +37,7 @@ const rb_data_type_t pg_typemap_type = {
|
|
|
37
37
|
},
|
|
38
38
|
0,
|
|
39
39
|
0,
|
|
40
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
40
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
VALUE rb_cTypeMap;
|
|
@@ -115,6 +115,45 @@ pg_typemap_s_allocate( VALUE klass )
|
|
|
115
115
|
return self;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/*
|
|
119
|
+
* call-seq:
|
|
120
|
+
* res.query_param_encoders(params)
|
|
121
|
+
*
|
|
122
|
+
* Retrieve the encoders that are used to encode the given values to be submitted to the database server.
|
|
123
|
+
* The selection of the encoders is defined in the derived type map class.
|
|
124
|
+
*
|
|
125
|
+
* +params+ must be an Array of values to be encoded.
|
|
126
|
+
* It's like +params+ given to exec_params .
|
|
127
|
+
*
|
|
128
|
+
* Returns an Array with the same length as +params+.
|
|
129
|
+
*
|
|
130
|
+
*/
|
|
131
|
+
static VALUE
|
|
132
|
+
pg_typemap_query_param_encoders( VALUE self, VALUE params )
|
|
133
|
+
{
|
|
134
|
+
t_typemap *this = RTYPEDDATA_DATA( self );
|
|
135
|
+
int nParams;
|
|
136
|
+
int i=0;
|
|
137
|
+
VALUE res;
|
|
138
|
+
|
|
139
|
+
Check_Type(params, T_ARRAY);
|
|
140
|
+
|
|
141
|
+
this->funcs.fit_to_query( self, params );
|
|
142
|
+
|
|
143
|
+
nParams = RARRAY_LENINT(params);
|
|
144
|
+
res = rb_ary_new2(nParams);
|
|
145
|
+
|
|
146
|
+
for ( i = 0; i < nParams; i++ ) {
|
|
147
|
+
t_pg_coder *conv;
|
|
148
|
+
VALUE param_value = rb_ary_entry(params, i);
|
|
149
|
+
|
|
150
|
+
/* Let the given typemap select a coder for this param */
|
|
151
|
+
conv = this->funcs.typecast_query_param(this, param_value, i);
|
|
152
|
+
rb_ary_push(res, conv ? conv->coder_obj : Qnil);
|
|
153
|
+
}
|
|
154
|
+
return res;
|
|
155
|
+
}
|
|
156
|
+
|
|
118
157
|
/*
|
|
119
158
|
* call-seq:
|
|
120
159
|
* res.default_type_map = typemap
|
|
@@ -194,6 +233,7 @@ init_pg_type_map(void)
|
|
|
194
233
|
*/
|
|
195
234
|
rb_cTypeMap = rb_define_class_under( rb_mPG, "TypeMap", rb_cObject );
|
|
196
235
|
rb_define_alloc_func( rb_cTypeMap, pg_typemap_s_allocate );
|
|
236
|
+
rb_define_method( rb_cTypeMap, "query_param_encoders", pg_typemap_query_param_encoders, 1 );
|
|
197
237
|
|
|
198
238
|
rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable");
|
|
199
239
|
rb_define_method( rb_mDefaultTypeMappable, "default_type_map=", pg_typemap_default_type_map_set, 1 );
|
|
@@ -18,7 +18,7 @@ static const rb_data_type_t pg_tmas_type = {
|
|
|
18
18
|
},
|
|
19
19
|
&pg_typemap_type,
|
|
20
20
|
0,
|
|
21
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
21
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
VALUE rb_cTypeMapAllStrings;
|
|
@@ -125,6 +125,6 @@ init_pg_type_map_all_strings(void)
|
|
|
125
125
|
rb_cTypeMapAllStrings = rb_define_class_under( rb_mPG, "TypeMapAllStrings", rb_cTypeMap );
|
|
126
126
|
rb_define_alloc_func( rb_cTypeMapAllStrings, pg_tmas_s_allocate );
|
|
127
127
|
|
|
128
|
-
pg_typemap_all_strings = rb_obj_freeze( rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 ));
|
|
129
128
|
rb_gc_register_address( &pg_typemap_all_strings );
|
|
129
|
+
pg_typemap_all_strings = rb_obj_freeze( rb_funcall( rb_cTypeMapAllStrings, rb_intern("new"), 0 ));
|
|
130
130
|
}
|
data/ext/pg_type_map_by_class.c
CHANGED
|
@@ -157,7 +157,7 @@ static const rb_data_type_t pg_tmbk_type = {
|
|
|
157
157
|
},
|
|
158
158
|
&pg_typemap_type,
|
|
159
159
|
0,
|
|
160
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
160
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
161
161
|
};
|
|
162
162
|
|
|
163
163
|
static VALUE
|
data/ext/pg_type_map_by_column.c
CHANGED
|
@@ -55,7 +55,7 @@ pg_tmbc_fit_to_query( VALUE self, VALUE params )
|
|
|
55
55
|
t_typemap *default_tm;
|
|
56
56
|
|
|
57
57
|
Check_Type(params, T_ARRAY);
|
|
58
|
-
nfields = (
|
|
58
|
+
nfields = RARRAY_LENINT( params );
|
|
59
59
|
if ( this->nfields != nfields ) {
|
|
60
60
|
rb_raise( rb_eArgError, "number of result fields (%d) does not match number of mapped columns (%d)",
|
|
61
61
|
nfields, this->nfields );
|
|
@@ -233,7 +233,7 @@ static const rb_data_type_t pg_tmbc_type = {
|
|
|
233
233
|
},
|
|
234
234
|
&pg_typemap_type,
|
|
235
235
|
0,
|
|
236
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
236
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
static VALUE
|
data/ext/pg_type_map_by_oid.c
CHANGED
|
@@ -194,7 +194,7 @@ static const rb_data_type_t pg_tmbo_type = {
|
|
|
194
194
|
},
|
|
195
195
|
&pg_typemap_type,
|
|
196
196
|
0,
|
|
197
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
197
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
static VALUE
|
data/ext/pg_type_map_in_ruby.c
CHANGED
|
@@ -44,7 +44,7 @@ static const rb_data_type_t pg_tmir_type = {
|
|
|
44
44
|
},
|
|
45
45
|
&pg_typemap_type,
|
|
46
46
|
0,
|
|
47
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED |
|
|
47
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
/*
|