pg 1.0.0 → 1.2.3
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.tar.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +156 -0
- data/Manifest.txt +8 -2
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +55 -9
- data/Rakefile +9 -7
- data/Rakefile.cross +58 -57
- data/ext/errorcodes.def +68 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +19 -2
- data/ext/extconf.rb +7 -5
- data/ext/pg.c +141 -98
- data/ext/pg.h +64 -21
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +73 -12
- data/ext/pg_connection.c +625 -346
- data/ext/pg_copy_coder.c +16 -8
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +571 -191
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +185 -54
- data/ext/pg_tuple.c +549 -0
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +9 -4
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +8 -6
- data/lib/pg/basic_type_mapping.rb +121 -25
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +22 -3
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +52 -20
- data/spec/pg/basic_type_mapping_spec.rb +362 -37
- data/spec/pg/connection_spec.rb +376 -146
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +240 -15
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +363 -17
- data/spec/pg_spec.rb +1 -1
- metadata +47 -47
- metadata.gz.sig +0 -0
data/ext/pg_type_map.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_all_strings.c - PG::TypeMapAllStrings class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
* This is the default typemap.
|
6
6
|
*
|
@@ -33,9 +33,9 @@ pg_tmas_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
|
33
33
|
len = PQgetlength( p_result->pgresult, tuple, field );
|
34
34
|
|
35
35
|
if ( 0 == PQfformat(p_result->pgresult, field) ) {
|
36
|
-
ret = pg_text_dec_string(NULL, val, len, tuple, field,
|
36
|
+
ret = pg_text_dec_string(NULL, val, len, tuple, field, p_result->enc_idx);
|
37
37
|
} else {
|
38
|
-
ret = pg_bin_dec_bytea(NULL, val, len, tuple, field,
|
38
|
+
ret = pg_bin_dec_bytea(NULL, val, len, tuple, field, p_result->enc_idx);
|
39
39
|
}
|
40
40
|
|
41
41
|
return ret;
|
@@ -99,7 +99,7 @@ init_pg_type_map_all_strings()
|
|
99
99
|
* This type map casts all values received from the database server to Strings
|
100
100
|
* and sends all values to the server after conversion to String by +#to_s+ .
|
101
101
|
* That means, it is hard coded to PG::TextEncoder::String for value encoding
|
102
|
-
* and to PG::TextDecoder::String for text format
|
102
|
+
* and to PG::TextDecoder::String for text format respectively PG::BinaryDecoder::Bytea
|
103
103
|
* for binary format received from the server.
|
104
104
|
*
|
105
105
|
* It is suitable for type casting query bind parameters, result values and
|
data/ext/pg_type_map_by_class.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_by_class.c - PG::TypeMapByClass class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
* This type map can be used to select value encoders based on the class
|
6
6
|
* of the given value to be send.
|
@@ -28,7 +28,7 @@ typedef struct {
|
|
28
28
|
* We use 8 Bits of the klass object id as index to a 256 entry cache.
|
29
29
|
* This avoids full lookups in most cases.
|
30
30
|
*/
|
31
|
-
#define CACHE_LOOKUP(this, klass) ( &this->cache_row[(klass >> 8) & 0xff] )
|
31
|
+
#define CACHE_LOOKUP(this, klass) ( &this->cache_row[(((unsigned long)klass) >> 8) & 0xff] )
|
32
32
|
|
33
33
|
|
34
34
|
static t_pg_coder *
|
@@ -66,7 +66,7 @@ pg_tmbk_lookup_klass(t_tmbk *this, VALUE klass, VALUE param_value)
|
|
66
66
|
Data_Get_Struct(obj, t_pg_coder, p_coder);
|
67
67
|
}else{
|
68
68
|
if( RB_TYPE_P(obj, T_SYMBOL) ){
|
69
|
-
/* A
|
69
|
+
/* A Symbol: Call the method with this name. */
|
70
70
|
obj = rb_funcall(this->self, SYM2ID(obj), 1, param_value);
|
71
71
|
}else{
|
72
72
|
/* A Proc object (or something that responds to #call). */
|
@@ -126,7 +126,11 @@ pg_tmbk_mark( t_tmbk *this )
|
|
126
126
|
{
|
127
127
|
rb_gc_mark(this->typemap.default_typemap);
|
128
128
|
rb_gc_mark(this->klass_to_coder);
|
129
|
-
|
129
|
+
rb_gc_mark(this->self);
|
130
|
+
/* Clear the cache, to be safe from changes of klass VALUE by GC.compact.
|
131
|
+
* TODO: Move cache clearing to compactation callback provided by Ruby-2.7+.
|
132
|
+
*/
|
133
|
+
memset(&this->cache_row, 0, sizeof(this->cache_row));
|
130
134
|
}
|
131
135
|
|
132
136
|
static VALUE
|
@@ -235,5 +239,6 @@ init_pg_type_map_by_class()
|
|
235
239
|
rb_define_method( rb_cTypeMapByClass, "[]=", pg_tmbk_aset, 2 );
|
236
240
|
rb_define_method( rb_cTypeMapByClass, "[]", pg_tmbk_aref, 1 );
|
237
241
|
rb_define_method( rb_cTypeMapByClass, "coders", pg_tmbk_coders, 0 );
|
242
|
+
/* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
|
238
243
|
rb_include_module( rb_cTypeMapByClass, rb_mDefaultTypeMappable );
|
239
244
|
}
|
data/ext/pg_type_map_by_column.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -99,11 +99,11 @@ pg_tmbc_result_value( t_typemap *p_typemap, VALUE result, int tuple, int field )
|
|
99
99
|
int len = PQgetlength( p_result->pgresult, tuple, field );
|
100
100
|
|
101
101
|
if( p_coder->dec_func ){
|
102
|
-
return p_coder->dec_func(p_coder, val, len, tuple, field,
|
102
|
+
return p_coder->dec_func(p_coder, val, len, tuple, field, p_result->enc_idx);
|
103
103
|
} else {
|
104
104
|
t_pg_coder_dec_func dec_func;
|
105
105
|
dec_func = pg_coder_dec_func( p_coder, PQfformat(p_result->pgresult, field) );
|
106
|
-
return dec_func(p_coder, val, len, tuple, field,
|
106
|
+
return dec_func(p_coder, val, len, tuple, field, p_result->enc_idx);
|
107
107
|
}
|
108
108
|
}
|
109
109
|
|
@@ -292,13 +292,13 @@ init_pg_type_map_by_column()
|
|
292
292
|
*
|
293
293
|
* This type map casts values by a coder assigned per field/column.
|
294
294
|
*
|
295
|
-
* Each PG
|
296
|
-
* that is defined at
|
295
|
+
* Each PG::TypeMapByColumn has a fixed list of either encoders or decoders,
|
296
|
+
* that is defined at TypeMapByColumn.new . A type map with encoders is usable for type casting
|
297
297
|
* query bind parameters and COPY data for PG::Connection#put_copy_data .
|
298
298
|
* A type map with decoders is usable for type casting of result values and
|
299
299
|
* COPY data from PG::Connection#get_copy_data .
|
300
300
|
*
|
301
|
-
* PG::
|
301
|
+
* PG::TypeMapByColumn objects are in particular useful in conjunction with prepared statements,
|
302
302
|
* since they can be cached alongside with the statement handle.
|
303
303
|
*
|
304
304
|
* This type map strategy is also used internally by PG::TypeMapByOid, when the
|
@@ -308,5 +308,6 @@ init_pg_type_map_by_column()
|
|
308
308
|
rb_define_alloc_func( rb_cTypeMapByColumn, pg_tmbc_s_allocate );
|
309
309
|
rb_define_method( rb_cTypeMapByColumn, "initialize", pg_tmbc_init, 1 );
|
310
310
|
rb_define_method( rb_cTypeMapByColumn, "coders", pg_tmbc_coders, 0 );
|
311
|
+
/* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
|
311
312
|
rb_include_module( rb_cTypeMapByColumn, rb_mDefaultTypeMappable );
|
312
313
|
}
|
data/ext/pg_type_map_by_oid.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_by_oid.c - PG::TypeMapByOid class extension
|
3
|
-
* $Id
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -110,7 +110,7 @@ pg_tmbo_result_value(t_typemap *p_typemap, VALUE result, int tuple, int field)
|
|
110
110
|
char * val = PQgetvalue( p_result->pgresult, tuple, field );
|
111
111
|
int len = PQgetlength( p_result->pgresult, tuple, field );
|
112
112
|
t_pg_coder_dec_func dec_func = pg_coder_dec_func( p_coder, format );
|
113
|
-
return dec_func( p_coder, val, len, tuple, field,
|
113
|
+
return dec_func( p_coder, val, len, tuple, field, p_result->enc_idx );
|
114
114
|
}
|
115
115
|
|
116
116
|
default_tm = DATA_PTR( this->typemap.default_typemap );
|
@@ -351,5 +351,6 @@ init_pg_type_map_by_oid()
|
|
351
351
|
rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup=", pg_tmbo_max_rows_for_online_lookup_set, 1 );
|
352
352
|
rb_define_method( rb_cTypeMapByOid, "max_rows_for_online_lookup", pg_tmbo_max_rows_for_online_lookup_get, 0 );
|
353
353
|
rb_define_method( rb_cTypeMapByOid, "build_column_map", pg_tmbo_build_column_map, 1 );
|
354
|
+
/* rb_mDefaultTypeMappable = rb_define_module_under( rb_cTypeMap, "DefaultTypeMappable"); */
|
354
355
|
rb_include_module( rb_cTypeMapByOid, rb_mDefaultTypeMappable );
|
355
356
|
}
|
data/ext/pg_type_map_in_ruby.c
CHANGED
data/ext/{util.c → pg_util.c}
RENAMED
@@ -1,11 +1,11 @@
|
|
1
1
|
/*
|
2
|
-
*
|
3
|
-
* $Id
|
2
|
+
* pg_util.c - Utils for ruby-pg
|
3
|
+
* $Id$
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
7
7
|
#include "pg.h"
|
8
|
-
#include "
|
8
|
+
#include "pg_util.h"
|
9
9
|
|
10
10
|
static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
11
11
|
|
@@ -15,19 +15,19 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
|
|
15
15
|
* in-place (with _out_ == _in_).
|
16
16
|
*/
|
17
17
|
void
|
18
|
-
base64_encode( char *out, char *in, int len)
|
18
|
+
base64_encode( char *out, const char *in, int len)
|
19
19
|
{
|
20
|
-
unsigned char *in_ptr = (unsigned char *)in + len;
|
20
|
+
const unsigned char *in_ptr = (const unsigned char *)in + len;
|
21
21
|
char *out_ptr = out + BASE64_ENCODED_SIZE(len);
|
22
22
|
int part_len = len % 3;
|
23
23
|
|
24
24
|
if( part_len > 0 ){
|
25
|
-
long byte2 =
|
25
|
+
long byte2 = 0;
|
26
26
|
long byte1 = part_len > 1 ? *--in_ptr : 0;
|
27
27
|
long byte0 = *--in_ptr;
|
28
28
|
long triple = (byte0 << 16) + (byte1 << 8) + byte2;
|
29
29
|
|
30
|
-
*--out_ptr =
|
30
|
+
*--out_ptr = '=';
|
31
31
|
*--out_ptr = part_len > 1 ? base64_encode_table[(triple >> 1 * 6) & 0x3F] : '=';
|
32
32
|
*--out_ptr = base64_encode_table[(triple >> 2 * 6) & 0x3F];
|
33
33
|
*--out_ptr = base64_encode_table[(triple >> 3 * 6) & 0x3F];
|
@@ -72,12 +72,12 @@ static const unsigned char base64_decode_table[] =
|
|
72
72
|
* It is possible to decode a string in-place (with _out_ == _in_).
|
73
73
|
*/
|
74
74
|
int
|
75
|
-
base64_decode( char *out, char *in, unsigned int len)
|
75
|
+
base64_decode( char *out, const char *in, unsigned int len)
|
76
76
|
{
|
77
77
|
unsigned char a, b, c, d;
|
78
|
-
unsigned char *in_ptr = (unsigned char *)in;
|
78
|
+
const unsigned char *in_ptr = (const unsigned char *)in;
|
79
79
|
unsigned char *out_ptr = (unsigned char *)out;
|
80
|
-
unsigned char *iend_ptr = (unsigned char *)in + len;
|
80
|
+
const unsigned char *iend_ptr = (unsigned char *)in + len;
|
81
81
|
|
82
82
|
for(;;){
|
83
83
|
if( in_ptr+3 < iend_ptr &&
|
data/ext/{util.h → pg_util.h}
RENAMED
@@ -57,8 +57,8 @@
|
|
57
57
|
#define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
|
58
58
|
#define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
|
59
59
|
|
60
|
-
void base64_encode( char *out, char *in, int len);
|
61
|
-
int base64_decode( char *out, char *in, unsigned int len);
|
60
|
+
void base64_encode( char *out, const char *in, int len);
|
61
|
+
int base64_decode( char *out, const char *in, unsigned int len);
|
62
62
|
|
63
63
|
int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
|
64
64
|
|
data/lib/pg.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
begin
|
4
5
|
require 'pg_ext'
|
@@ -21,7 +22,8 @@ rescue LoadError
|
|
21
22
|
end
|
22
23
|
|
23
24
|
# Temporary add this directory for DLL search, so that libpq.dll can be found.
|
24
|
-
|
25
|
+
# mingw32-platform strings differ (RUBY_PLATFORM=i386-mingw32 vs. x86-mingw32 for rubygems)
|
26
|
+
add_dll_path.call(File.join(__dir__, RUBY_PLATFORM.gsub(/^i386-/, "x86-"))) do
|
25
27
|
require "#{major_minor}/pg_ext"
|
26
28
|
end
|
27
29
|
else
|
@@ -35,10 +37,10 @@ end
|
|
35
37
|
module PG
|
36
38
|
|
37
39
|
# Library version
|
38
|
-
VERSION = '1.
|
40
|
+
VERSION = '1.2.3'
|
39
41
|
|
40
42
|
# VCS revision
|
41
|
-
REVISION = %q$Revision:
|
43
|
+
REVISION = %q$Revision: 6f611e78845a $
|
42
44
|
|
43
45
|
class NotAllCopyDataRetrieved < PG::Error
|
44
46
|
end
|
@@ -60,13 +62,13 @@ module PG
|
|
60
62
|
require 'pg/exceptions'
|
61
63
|
require 'pg/constants'
|
62
64
|
require 'pg/coder'
|
65
|
+
require 'pg/binary_decoder'
|
63
66
|
require 'pg/text_encoder'
|
64
67
|
require 'pg/text_decoder'
|
65
68
|
require 'pg/basic_type_mapping'
|
66
69
|
require 'pg/type_map_by_column'
|
67
70
|
require 'pg/connection'
|
68
71
|
require 'pg/result'
|
72
|
+
require 'pg/tuple'
|
69
73
|
|
70
74
|
end # module PG
|
71
|
-
|
72
|
-
|
@@ -1,7 +1,28 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
4
5
|
|
6
|
+
# This module defines the mapping between OID and encoder/decoder classes for PG::BasicTypeMapForResults, PG::BasicTypeMapForQueries and PG::BasicTypeMapBasedOnResult.
|
7
|
+
#
|
8
|
+
# Additional types can be added like so:
|
9
|
+
#
|
10
|
+
# require 'pg'
|
11
|
+
# require 'ipaddr'
|
12
|
+
#
|
13
|
+
# class InetDecoder < PG::SimpleDecoder
|
14
|
+
# def decode(string, tuple=nil, field=nil)
|
15
|
+
# IPAddr.new(string)
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# class InetEncoder < PG::SimpleEncoder
|
19
|
+
# def encode(ip_addr)
|
20
|
+
# ip_addr.to_s
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # 0 if for text format, can also be 1 for binary
|
25
|
+
# PG::BasicTypeRegistry.register_type(0, 'inet', InetEncoder, InetDecoder)
|
5
26
|
module PG::BasicTypeRegistry
|
6
27
|
# An instance of this class stores the coders that should be used for a given wire format (text or binary)
|
7
28
|
# and type cast direction (encoder or decoder).
|
@@ -97,13 +118,13 @@ module PG::BasicTypeRegistry
|
|
97
118
|
def build_coder_maps(connection)
|
98
119
|
if supports_ranges?(connection)
|
99
120
|
result = connection.exec <<-SQL
|
100
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype
|
121
|
+
SELECT t.oid, t.typname::text, t.typelem, t.typdelim, t.typinput::text, r.rngsubtype
|
101
122
|
FROM pg_type as t
|
102
123
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
103
124
|
SQL
|
104
125
|
else
|
105
126
|
result = connection.exec <<-SQL
|
106
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput
|
127
|
+
SELECT t.oid, t.typname::text, t.typelem, t.typdelim, t.typinput::text
|
107
128
|
FROM pg_type as t
|
108
129
|
SQL
|
109
130
|
end
|
@@ -134,19 +155,38 @@ module PG::BasicTypeRegistry
|
|
134
155
|
# objects as values.
|
135
156
|
CODERS_BY_NAME = []
|
136
157
|
|
137
|
-
|
138
|
-
|
139
|
-
#
|
158
|
+
public
|
159
|
+
|
160
|
+
# Register an encoder or decoder instance for casting a PostgreSQL type.
|
161
|
+
#
|
162
|
+
# Coder#name must correspond to the +typname+ column in the +pg_type+ table.
|
163
|
+
# Coder#format can be 0 for text format and 1 for binary.
|
164
|
+
def self.register_coder(coder)
|
165
|
+
h = CODERS_BY_NAME[coder.format] ||= { encoder: {}, decoder: {} }
|
166
|
+
name = coder.name || raise(ArgumentError, "name of #{coder.inspect} must be defined")
|
167
|
+
h[:encoder][name] = coder if coder.respond_to?(:encode)
|
168
|
+
h[:decoder][name] = coder if coder.respond_to?(:decode)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Register the given +encoder_class+ and/or +decoder_class+ for casting a PostgreSQL type.
|
172
|
+
#
|
173
|
+
# +name+ must correspond to the +typname+ column in the +pg_type+ table.
|
174
|
+
# +format+ can be 0 for text format and 1 for binary.
|
140
175
|
def self.register_type(format, name, encoder_class, decoder_class)
|
141
|
-
|
142
|
-
|
143
|
-
CODERS_BY_NAME[format][:decoder][name] = decoder_class.new(name: name, format: format) if decoder_class
|
176
|
+
register_coder(encoder_class.new(name: name, format: format)) if encoder_class
|
177
|
+
register_coder(decoder_class.new(name: name, format: format)) if decoder_class
|
144
178
|
end
|
145
179
|
|
146
180
|
# Alias the +old+ type to the +new+ type.
|
147
181
|
def self.alias_type(format, new, old)
|
148
|
-
|
149
|
-
|
182
|
+
[:encoder, :decoder].each do |ende|
|
183
|
+
enc = CODERS_BY_NAME[format][ende][old]
|
184
|
+
if enc
|
185
|
+
CODERS_BY_NAME[format][ende][new] = enc
|
186
|
+
else
|
187
|
+
CODERS_BY_NAME[format][ende].delete(new)
|
188
|
+
end
|
189
|
+
end
|
150
190
|
end
|
151
191
|
|
152
192
|
register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
|
@@ -154,7 +194,7 @@ module PG::BasicTypeRegistry
|
|
154
194
|
alias_type 0, 'int8', 'int2'
|
155
195
|
alias_type 0, 'oid', 'int2'
|
156
196
|
|
157
|
-
|
197
|
+
register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
|
158
198
|
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
159
199
|
alias_type 0, 'varchar', 'text'
|
160
200
|
alias_type 0, 'char', 'text'
|
@@ -193,8 +233,8 @@ module PG::BasicTypeRegistry
|
|
193
233
|
# register_type 'citext', OID::Text.new
|
194
234
|
# register_type 'ltree', OID::Text.new
|
195
235
|
#
|
196
|
-
|
197
|
-
|
236
|
+
register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
|
237
|
+
alias_type 0, 'cidr', 'inet'
|
198
238
|
|
199
239
|
|
200
240
|
|
@@ -213,12 +253,14 @@ module PG::BasicTypeRegistry
|
|
213
253
|
register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
|
214
254
|
register_type 1, 'float4', nil, PG::BinaryDecoder::Float
|
215
255
|
register_type 1, 'float8', nil, PG::BinaryDecoder::Float
|
256
|
+
register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtc
|
257
|
+
register_type 1, 'timestamptz', nil, PG::BinaryDecoder::TimestampUtcToLocal
|
216
258
|
end
|
217
259
|
|
218
260
|
# Simple set of rules for type casting common PostgreSQL types to Ruby.
|
219
261
|
#
|
220
262
|
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
221
|
-
# PostgreSQL's pg_type table in PG::BasicTypeMapForResults.new .
|
263
|
+
# PostgreSQL's +pg_type+ table in PG::BasicTypeMapForResults.new .
|
222
264
|
#
|
223
265
|
# Result values are type casted based on the type OID of the given result column.
|
224
266
|
#
|
@@ -235,7 +277,7 @@ end
|
|
235
277
|
# # is done by PG::TextDecoder::Integer internally for all value retrieval methods.
|
236
278
|
# res.values # => [[5]]
|
237
279
|
#
|
238
|
-
# PG::TypeMapByOid#
|
280
|
+
# PG::TypeMapByOid#build_column_map(result) can be used to generate
|
239
281
|
# a result independent PG::TypeMapByColumn type map, which can subsequently be used
|
240
282
|
# to cast #get_copy_data fields:
|
241
283
|
#
|
@@ -258,7 +300,7 @@ end
|
|
258
300
|
# This prints the rows with type casted columns:
|
259
301
|
# ["a", 123, [5, 4, 3]]
|
260
302
|
#
|
261
|
-
# See also PG::BasicTypeMapBasedOnResult for the encoder direction.
|
303
|
+
# See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
|
262
304
|
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
263
305
|
include PG::BasicTypeRegistry
|
264
306
|
|
@@ -272,7 +314,7 @@ class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
|
272
314
|
format = result.fformat(field)
|
273
315
|
oid = result.ftype(field)
|
274
316
|
unless @already_warned[format][oid]
|
275
|
-
|
317
|
+
$stderr.puts "Warning: no type cast defined for type #{@typenames_by_oid[format][oid].inspect} with oid #{oid}. Please cast this type explicitly to TEXT to be safe for future changes."
|
276
318
|
@already_warned[format][oid] = true
|
277
319
|
end
|
278
320
|
super
|
@@ -296,7 +338,7 @@ end
|
|
296
338
|
# to PostgreSQL.
|
297
339
|
#
|
298
340
|
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
299
|
-
# PostgreSQL's pg_type table in PG::BasicTypeMapBasedOnResult.new .
|
341
|
+
# PostgreSQL's +pg_type+ table in PG::BasicTypeMapBasedOnResult.new .
|
300
342
|
#
|
301
343
|
# This class works equal to PG::BasicTypeMapForResults, but does not define decoders for
|
302
344
|
# the given result OIDs, but encoders. So it can be used to type cast field values based on
|
@@ -351,21 +393,54 @@ end
|
|
351
393
|
# # Assign a default ruleset for type casts of input and output values.
|
352
394
|
# conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
|
353
395
|
# # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
|
354
|
-
# # The format of the parameter is set to
|
396
|
+
# # The format of the parameter is set to 0 (text) and the OID of this parameter is set to 20 (int8).
|
355
397
|
# res = conn.exec_params( "SELECT $1", [5] )
|
356
398
|
class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
357
399
|
include PG::BasicTypeRegistry
|
358
400
|
|
359
401
|
def initialize(connection)
|
360
402
|
@coder_maps = build_coder_maps(connection)
|
361
|
-
|
362
|
-
populate_encoder_list
|
363
403
|
@array_encoders_by_klass = array_encoders_by_klass
|
364
|
-
@
|
404
|
+
@encode_array_as = :array
|
405
|
+
init_encoders
|
365
406
|
end
|
366
407
|
|
408
|
+
# Change the mechanism that is used to encode ruby array values
|
409
|
+
#
|
410
|
+
# Possible values:
|
411
|
+
# * +:array+ : Encode the ruby array as a PostgreSQL array.
|
412
|
+
# The array element type is inferred from the class of the first array element. This is the default.
|
413
|
+
# * +:json+ : Encode the ruby array as a JSON document.
|
414
|
+
# * +:record+ : Encode the ruby array as a composite type row.
|
415
|
+
# * <code>"_type"</code> : Encode the ruby array as a particular PostgreSQL type.
|
416
|
+
# All PostgreSQL array types are supported.
|
417
|
+
# If there's an encoder registered for the elements +type+, it will be used.
|
418
|
+
# Otherwise a string conversion (by +value.to_s+) is done.
|
419
|
+
def encode_array_as=(pg_type)
|
420
|
+
case pg_type
|
421
|
+
when :array
|
422
|
+
when :json
|
423
|
+
when :record
|
424
|
+
when /\A_/
|
425
|
+
else
|
426
|
+
raise ArgumentError, "invalid pg_type #{pg_type.inspect}"
|
427
|
+
end
|
428
|
+
|
429
|
+
@encode_array_as = pg_type
|
430
|
+
|
431
|
+
init_encoders
|
432
|
+
end
|
433
|
+
|
434
|
+
attr_reader :encode_array_as
|
435
|
+
|
367
436
|
private
|
368
437
|
|
438
|
+
def init_encoders
|
439
|
+
coders.each { |kl, c| self[kl] = nil } # Clear type map
|
440
|
+
populate_encoder_list
|
441
|
+
@textarray_encoder = coder_by_name(0, :encoder, '_text')
|
442
|
+
end
|
443
|
+
|
369
444
|
def coder_by_name(format, direction, name)
|
370
445
|
check_format_and_direction(format, direction)
|
371
446
|
@coder_maps[format][direction].coder_by_name(name)
|
@@ -383,7 +458,19 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
383
458
|
end
|
384
459
|
self[klass] = coder
|
385
460
|
else
|
386
|
-
|
461
|
+
|
462
|
+
case @encode_array_as
|
463
|
+
when :array
|
464
|
+
self[klass] = selector
|
465
|
+
when :json
|
466
|
+
self[klass] = PG::TextEncoder::JSON.new
|
467
|
+
when :record
|
468
|
+
self[klass] = PG::TextEncoder::Record.new type_map: self
|
469
|
+
when /\A_/
|
470
|
+
self[klass] = coder_by_name(0, :encoder, @encode_array_as) || raise(ArgumentError, "unknown array type #{@encode_array_as.inspect}")
|
471
|
+
else
|
472
|
+
raise ArgumentError, "invalid pg_type #{@encode_array_as.inspect}"
|
473
|
+
end
|
387
474
|
end
|
388
475
|
end
|
389
476
|
end
|
@@ -402,7 +489,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
402
489
|
end
|
403
490
|
@array_encoders_by_klass[elem.class] ||
|
404
491
|
elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
|
405
|
-
@
|
492
|
+
@textarray_encoder
|
406
493
|
end
|
407
494
|
|
408
495
|
DEFAULT_TYPE_MAP = {
|
@@ -412,6 +499,12 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
412
499
|
# to unnecessary type conversions on server side.
|
413
500
|
Integer => [0, 'int8'],
|
414
501
|
Float => [0, 'float8'],
|
502
|
+
BigDecimal => [0, 'numeric'],
|
503
|
+
Time => [0, 'timestamptz'],
|
504
|
+
# We use text format and no type OID for IPAddr, because setting the OID can lead
|
505
|
+
# to unnecessary inet/cidr conversions on the server side.
|
506
|
+
IPAddr => [0, 'inet'],
|
507
|
+
Hash => [0, 'json'],
|
415
508
|
Array => :get_array_type,
|
416
509
|
}
|
417
510
|
|
@@ -421,6 +514,9 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
421
514
|
Integer => [0, '_int8'],
|
422
515
|
String => [0, '_text'],
|
423
516
|
Float => [0, '_float8'],
|
517
|
+
BigDecimal => [0, '_numeric'],
|
518
|
+
Time => [0, '_timestamptz'],
|
519
|
+
IPAddr => [0, '_inet'],
|
424
520
|
}
|
425
521
|
|
426
522
|
end
|