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