pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815
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 +1573 -2
- data/History.rdoc +3 -11
- data/Manifest.txt +24 -0
- data/README.rdoc +51 -4
- data/Rakefile +20 -14
- data/Rakefile.cross +39 -32
- data/ext/extconf.rb +27 -26
- data/ext/pg.c +75 -21
- data/ext/pg.h +194 -6
- data/ext/pg_binary_decoder.c +160 -0
- data/ext/pg_binary_encoder.c +160 -0
- data/ext/pg_coder.c +454 -0
- data/ext/pg_connection.c +815 -518
- data/ext/pg_copy_coder.c +557 -0
- data/ext/pg_result.c +258 -103
- data/ext/pg_text_decoder.c +424 -0
- data/ext/pg_text_encoder.c +608 -0
- data/ext/pg_type_map.c +113 -0
- data/ext/pg_type_map_all_strings.c +113 -0
- data/ext/pg_type_map_by_column.c +254 -0
- data/ext/pg_type_map_by_mri_type.c +266 -0
- data/ext/pg_type_map_by_oid.c +341 -0
- data/ext/util.c +121 -0
- data/ext/util.h +63 -0
- data/lib/pg.rb +11 -1
- data/lib/pg/basic_type_mapping.rb +377 -0
- data/lib/pg/coder.rb +74 -0
- data/lib/pg/connection.rb +38 -5
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +42 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/helpers.rb +9 -1
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +232 -13
- data/spec/pg/result_spec.rb +52 -0
- data/spec/pg/type_map_by_column_spec.rb +135 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
- data/spec/pg/type_map_by_oid_spec.rb +133 -0
- data/spec/pg/type_map_spec.rb +39 -0
- data/spec/pg/type_spec.rb +620 -0
- metadata +40 -4
- metadata.gz.sig +0 -0
data/ext/util.c
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
/*
|
2
|
+
* util.c - Utils for ruby-pg
|
3
|
+
* $Id: util.c,v c8d7c26dd595 2014/10/12 17:08:46 lars $
|
4
|
+
*
|
5
|
+
*/
|
6
|
+
|
7
|
+
#include "pg.h"
|
8
|
+
#include "util.h"
|
9
|
+
|
10
|
+
static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
11
|
+
|
12
|
+
/* Encode _len_ bytes at _in_ as base64 and write output to _out_.
|
13
|
+
*
|
14
|
+
* This encoder runs backwards, so that it is possible to encode a string
|
15
|
+
* in-place (with _out_ == _in_).
|
16
|
+
*/
|
17
|
+
void
|
18
|
+
base64_encode( char *out, char *in, int len)
|
19
|
+
{
|
20
|
+
char *in_ptr = in + len;
|
21
|
+
char *out_ptr = out + BASE64_ENCODED_SIZE(len);
|
22
|
+
int part_len = len % 3;
|
23
|
+
|
24
|
+
if( part_len > 0 ){
|
25
|
+
long byte2 = part_len > 2 ? *--in_ptr : 0;
|
26
|
+
long byte1 = part_len > 1 ? *--in_ptr : 0;
|
27
|
+
long byte0 = *--in_ptr;
|
28
|
+
long triple = (byte0 << 16) + (byte1 << 8) + byte2;
|
29
|
+
|
30
|
+
*--out_ptr = part_len > 2 ? base64_encode_table[(triple >> 0 * 6) & 0x3F] : '=';
|
31
|
+
*--out_ptr = part_len > 1 ? base64_encode_table[(triple >> 1 * 6) & 0x3F] : '=';
|
32
|
+
*--out_ptr = base64_encode_table[(triple >> 2 * 6) & 0x3F];
|
33
|
+
*--out_ptr = base64_encode_table[(triple >> 3 * 6) & 0x3F];
|
34
|
+
}
|
35
|
+
|
36
|
+
while( out_ptr > out ){
|
37
|
+
long byte2 = *--in_ptr;
|
38
|
+
long byte1 = *--in_ptr;
|
39
|
+
long byte0 = *--in_ptr;
|
40
|
+
long triple = (byte0 << 16) + (byte1 << 8) + byte2;
|
41
|
+
|
42
|
+
*--out_ptr = base64_encode_table[(triple >> 0 * 6) & 0x3F];
|
43
|
+
*--out_ptr = base64_encode_table[(triple >> 1 * 6) & 0x3F];
|
44
|
+
*--out_ptr = base64_encode_table[(triple >> 2 * 6) & 0x3F];
|
45
|
+
*--out_ptr = base64_encode_table[(triple >> 3 * 6) & 0x3F];
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/*
|
50
|
+
* 0.upto(255).map{|a| "\\x#{ (base64_encode_table.index([a].pack("C")) || 0xff).to_s(16) }" }.join
|
51
|
+
*/
|
52
|
+
static const unsigned char base64_decode_table[] =
|
53
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
54
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
55
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\xff\xff\xff\x3f"
|
56
|
+
"\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\xff\xff\xff"
|
57
|
+
"\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
|
58
|
+
"\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\xff"
|
59
|
+
"\xff\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28"
|
60
|
+
"\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xff\xff\xff\xff\xff"
|
61
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
62
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
63
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
64
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
65
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
66
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
67
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
|
68
|
+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
69
|
+
|
70
|
+
/* Decode _len_ bytes of base64 characters at _in_ and write output to _out_.
|
71
|
+
*
|
72
|
+
* It is possible to decode a string in-place (with _out_ == _in_).
|
73
|
+
*/
|
74
|
+
int
|
75
|
+
base64_decode( char *out, char *in, unsigned int len)
|
76
|
+
{
|
77
|
+
unsigned char a, b, c, d;
|
78
|
+
unsigned char *in_ptr = (unsigned char *)in;
|
79
|
+
unsigned char *out_ptr = (unsigned char *)out;
|
80
|
+
unsigned char *iend_ptr = (unsigned char *)in + len;
|
81
|
+
|
82
|
+
for(;;){
|
83
|
+
if( in_ptr+3 < iend_ptr &&
|
84
|
+
(a=base64_decode_table[in_ptr[0]]) != 0xff &&
|
85
|
+
(b=base64_decode_table[in_ptr[1]]) != 0xff &&
|
86
|
+
(c=base64_decode_table[in_ptr[2]]) != 0xff &&
|
87
|
+
(d=base64_decode_table[in_ptr[3]]) != 0xff )
|
88
|
+
{
|
89
|
+
in_ptr += 4;
|
90
|
+
*out_ptr++ = (a << 2) | (b >> 4);
|
91
|
+
*out_ptr++ = (b << 4) | (c >> 2);
|
92
|
+
*out_ptr++ = (c << 6) | d;
|
93
|
+
} else if (in_ptr < iend_ptr){
|
94
|
+
a = b = c = d = 0xff;
|
95
|
+
while ((a = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
|
96
|
+
if (in_ptr < iend_ptr){
|
97
|
+
while ((b = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
|
98
|
+
if (in_ptr < iend_ptr){
|
99
|
+
while ((c = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
|
100
|
+
if (in_ptr < iend_ptr){
|
101
|
+
while ((d = base64_decode_table[*in_ptr++]) == 0xff && in_ptr < iend_ptr) {}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
if (a != 0xff && b != 0xff) {
|
106
|
+
*out_ptr++ = (a << 2) | (b >> 4);
|
107
|
+
if (c != 0xff) {
|
108
|
+
*out_ptr++ = (b << 4) | (c >> 2);
|
109
|
+
if (d != 0xff)
|
110
|
+
*out_ptr++ = (c << 6) | d;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
} else {
|
114
|
+
break;
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
return (char*)out_ptr - out;
|
120
|
+
}
|
121
|
+
|
data/ext/util.h
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
/*
|
2
|
+
* utils.h
|
3
|
+
*
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef __utils_h
|
7
|
+
#define __utils_h
|
8
|
+
|
9
|
+
#define write_nbo16(l,c) ( \
|
10
|
+
*((unsigned char*)(c)+0)=(unsigned char)(((l)>>8)&0xff), \
|
11
|
+
*((unsigned char*)(c)+1)=(unsigned char)(((l) )&0xff)\
|
12
|
+
)
|
13
|
+
|
14
|
+
#define write_nbo32(l,c) ( \
|
15
|
+
*((unsigned char*)(c)+0)=(unsigned char)(((l)>>24L)&0xff), \
|
16
|
+
*((unsigned char*)(c)+1)=(unsigned char)(((l)>>16L)&0xff), \
|
17
|
+
*((unsigned char*)(c)+2)=(unsigned char)(((l)>> 8L)&0xff), \
|
18
|
+
*((unsigned char*)(c)+3)=(unsigned char)(((l) )&0xff)\
|
19
|
+
)
|
20
|
+
|
21
|
+
#define write_nbo64(l,c) ( \
|
22
|
+
*((unsigned char*)(c)+0)=(unsigned char)(((l)>>56LL)&0xff), \
|
23
|
+
*((unsigned char*)(c)+1)=(unsigned char)(((l)>>48LL)&0xff), \
|
24
|
+
*((unsigned char*)(c)+2)=(unsigned char)(((l)>>40LL)&0xff), \
|
25
|
+
*((unsigned char*)(c)+3)=(unsigned char)(((l)>>32LL)&0xff), \
|
26
|
+
*((unsigned char*)(c)+4)=(unsigned char)(((l)>>24LL)&0xff), \
|
27
|
+
*((unsigned char*)(c)+5)=(unsigned char)(((l)>>16LL)&0xff), \
|
28
|
+
*((unsigned char*)(c)+6)=(unsigned char)(((l)>> 8LL)&0xff), \
|
29
|
+
*((unsigned char*)(c)+7)=(unsigned char)(((l) )&0xff)\
|
30
|
+
)
|
31
|
+
|
32
|
+
#define read_nbo16(c) ((int16_t)( \
|
33
|
+
(((uint16_t)(*((unsigned char*)(c)+0)))<< 8L) | \
|
34
|
+
(((uint16_t)(*((unsigned char*)(c)+1))) ) \
|
35
|
+
))
|
36
|
+
|
37
|
+
#define read_nbo32(c) ((int32_t)( \
|
38
|
+
(((uint32_t)(*((unsigned char*)(c)+0)))<<24L) | \
|
39
|
+
(((uint32_t)(*((unsigned char*)(c)+1)))<<16L) | \
|
40
|
+
(((uint32_t)(*((unsigned char*)(c)+2)))<< 8L) | \
|
41
|
+
(((uint32_t)(*((unsigned char*)(c)+3))) ) \
|
42
|
+
))
|
43
|
+
|
44
|
+
#define read_nbo64(c) ((int64_t)( \
|
45
|
+
(((uint64_t)(*((unsigned char*)(c)+0)))<<56LL) | \
|
46
|
+
(((uint64_t)(*((unsigned char*)(c)+1)))<<48LL) | \
|
47
|
+
(((uint64_t)(*((unsigned char*)(c)+2)))<<40LL) | \
|
48
|
+
(((uint64_t)(*((unsigned char*)(c)+3)))<<32LL) | \
|
49
|
+
(((uint64_t)(*((unsigned char*)(c)+4)))<<24LL) | \
|
50
|
+
(((uint64_t)(*((unsigned char*)(c)+5)))<<16LL) | \
|
51
|
+
(((uint64_t)(*((unsigned char*)(c)+6)))<< 8LL) | \
|
52
|
+
(((uint64_t)(*((unsigned char*)(c)+7))) ) \
|
53
|
+
))
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
#define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
|
58
|
+
#define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
|
59
|
+
|
60
|
+
void base64_encode( char *out, char *in, int len);
|
61
|
+
int base64_decode( char *out, char *in, unsigned int len);
|
62
|
+
|
63
|
+
#endif /* end __utils_h */
|
data/lib/pg.rb
CHANGED
@@ -7,7 +7,12 @@ rescue LoadError
|
|
7
7
|
if RUBY_PLATFORM =~/(mswin|mingw)/i
|
8
8
|
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
9
9
|
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
10
|
+
|
11
|
+
# Set the PATH environment variable, so that libpq.dll can be found.
|
12
|
+
old_path = ENV['PATH']
|
13
|
+
ENV['PATH'] = "#{old_path};#{File.expand_path("../#{RUBY_PLATFORM}", __FILE__)}"
|
10
14
|
require "#{major_minor}/pg_ext"
|
15
|
+
ENV['PATH'] = old_path
|
11
16
|
else
|
12
17
|
raise
|
13
18
|
end
|
@@ -22,7 +27,7 @@ module PG
|
|
22
27
|
VERSION = '0.18.0'
|
23
28
|
|
24
29
|
# VCS revision
|
25
|
-
REVISION = %q$Revision:
|
30
|
+
REVISION = %q$Revision: 57d770944b5d $
|
26
31
|
|
27
32
|
class NotAllCopyDataRetrieved < PG::Error
|
28
33
|
end
|
@@ -43,6 +48,11 @@ module PG
|
|
43
48
|
|
44
49
|
require 'pg/exceptions'
|
45
50
|
require 'pg/constants'
|
51
|
+
require 'pg/coder'
|
52
|
+
require 'pg/text_encoder'
|
53
|
+
require 'pg/text_decoder'
|
54
|
+
require 'pg/basic_type_mapping'
|
55
|
+
require 'pg/type_map_by_column'
|
46
56
|
require 'pg/connection'
|
47
57
|
require 'pg/result'
|
48
58
|
|
@@ -0,0 +1,377 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pg' unless defined?( PG )
|
4
|
+
|
5
|
+
module PG::BasicTypeRegistry
|
6
|
+
# An instance of this class stores the coders that should be used for a given wire format (text or binary)
|
7
|
+
# and type cast direction (encoder or decoder).
|
8
|
+
class CoderMap
|
9
|
+
# Hash of text types that don't require quotation, when used within composite types.
|
10
|
+
# type.name => true
|
11
|
+
DONT_QUOTE_TYPES = %w[
|
12
|
+
int2 int4 int8
|
13
|
+
float4 float8
|
14
|
+
oid
|
15
|
+
bool
|
16
|
+
date timestamp timestamptz
|
17
|
+
].inject({}){|h,e| h[e] = true; h }
|
18
|
+
|
19
|
+
def initialize(result, coders_by_name, format, arraycoder)
|
20
|
+
coder_map = {}
|
21
|
+
|
22
|
+
_ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' }
|
23
|
+
leaves, nodes = nodes.partition { |row| row['typelem'].to_i == 0 }
|
24
|
+
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
|
25
|
+
|
26
|
+
# populate the enum types
|
27
|
+
_enums, leaves = leaves.partition { |row| row['typinput'] == 'enum_in' }
|
28
|
+
# enums.each do |row|
|
29
|
+
# coder_map[row['oid'].to_i] = OID::Enum.new
|
30
|
+
# end
|
31
|
+
|
32
|
+
# populate the base types
|
33
|
+
leaves.find_all { |row| coders_by_name.key?(row['typname']) }.each do |row|
|
34
|
+
coder = coders_by_name[row['typname']].dup
|
35
|
+
coder.oid = row['oid'].to_i
|
36
|
+
coder.name = row['typname']
|
37
|
+
coder.format = format
|
38
|
+
coder_map[coder.oid] = coder
|
39
|
+
end
|
40
|
+
|
41
|
+
_records_by_oid = result.group_by { |row| row['oid'] }
|
42
|
+
|
43
|
+
# populate composite types
|
44
|
+
# nodes.each do |row|
|
45
|
+
# add_oid row, records_by_oid, coder_map
|
46
|
+
# end
|
47
|
+
|
48
|
+
if arraycoder
|
49
|
+
# populate array types
|
50
|
+
arrays.each do |row|
|
51
|
+
elements_coder = coder_map[row['typelem'].to_i]
|
52
|
+
next unless elements_coder
|
53
|
+
|
54
|
+
coder = arraycoder.new
|
55
|
+
coder.oid = row['oid'].to_i
|
56
|
+
coder.name = row['typname']
|
57
|
+
coder.format = format
|
58
|
+
coder.elements_type = elements_coder
|
59
|
+
coder.needs_quotation = !DONT_QUOTE_TYPES[elements_coder.name]
|
60
|
+
coder_map[coder.oid] = coder
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# populate range types
|
65
|
+
# ranges.find_all { |row| coder_map.key? row['rngsubtype'].to_i }.each do |row|
|
66
|
+
# subcoder = coder_map[row['rngsubtype'].to_i]
|
67
|
+
# range = OID::Range.new subcoder
|
68
|
+
# coder_map[row['oid'].to_i] = range
|
69
|
+
# end
|
70
|
+
|
71
|
+
@coders = coder_map.values
|
72
|
+
@coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
|
73
|
+
@coders_by_oid = @coders.inject({}){|h, t| h[t.oid] = t; h }
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :coders
|
77
|
+
attr_reader :coders_by_oid
|
78
|
+
attr_reader :coders_by_name
|
79
|
+
|
80
|
+
def coder_by_name(name)
|
81
|
+
@coders_by_name[name]
|
82
|
+
end
|
83
|
+
|
84
|
+
def coder_by_oid(oid)
|
85
|
+
@coders_by_oid[oid]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def supports_ranges?(connection)
|
92
|
+
connection.server_version >= 90200
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_coder_maps(connection)
|
96
|
+
if supports_ranges?(connection)
|
97
|
+
result = connection.exec <<-SQL
|
98
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype
|
99
|
+
FROM pg_type as t
|
100
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
101
|
+
SQL
|
102
|
+
else
|
103
|
+
result = connection.exec <<-SQL
|
104
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput
|
105
|
+
FROM pg_type as t
|
106
|
+
SQL
|
107
|
+
end
|
108
|
+
|
109
|
+
[
|
110
|
+
[0, :encoder, PG::TextEncoder::Array],
|
111
|
+
[0, :decoder, PG::TextDecoder::Array],
|
112
|
+
[1, :encoder, nil],
|
113
|
+
[1, :decoder, nil],
|
114
|
+
].inject([]) do |h, (format, direction, arraycoder)|
|
115
|
+
h[format] ||= {}
|
116
|
+
h[format][direction] = CoderMap.new result, CODERS_BY_NAME[format][direction], format, arraycoder
|
117
|
+
h
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
ValidFormats = { 0 => true, 1 => true }
|
122
|
+
ValidDirections = { :encoder => true, :decoder => true }
|
123
|
+
|
124
|
+
def check_format_and_direction(format, direction)
|
125
|
+
raise(ArgumentError, "Invalid format value %p" % format) unless ValidFormats[format]
|
126
|
+
raise(ArgumentError, "Invalid direction %p" % direction) unless ValidDirections[direction]
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# The key of this hash maps to the `typname` column from the table.
|
131
|
+
# encoder_map is then dynamically built with oids as the key and Type
|
132
|
+
# objects as values.
|
133
|
+
CODERS_BY_NAME = []
|
134
|
+
|
135
|
+
# Register an OID type named +name+ with a typecasting encoder and decoder object in
|
136
|
+
# +type+. +name+ should correspond to the `typname` column in
|
137
|
+
# the `pg_type` table.
|
138
|
+
def self.register_type(format, name, encoder_class, decoder_class)
|
139
|
+
CODERS_BY_NAME[format] ||= { encoder: {}, decoder: {} }
|
140
|
+
CODERS_BY_NAME[format][:encoder][name] = encoder_class.new(name: name, format: format) if encoder_class
|
141
|
+
CODERS_BY_NAME[format][:decoder][name] = decoder_class.new(name: name, format: format) if decoder_class
|
142
|
+
end
|
143
|
+
|
144
|
+
# Alias the +old+ type to the +new+ type.
|
145
|
+
def self.alias_type(format, new, old)
|
146
|
+
CODERS_BY_NAME[format][:encoder][new] = CODERS_BY_NAME[format][:encoder][old]
|
147
|
+
CODERS_BY_NAME[format][:decoder][new] = CODERS_BY_NAME[format][:decoder][old]
|
148
|
+
end
|
149
|
+
|
150
|
+
register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
|
151
|
+
alias_type 0, 'int4', 'int2'
|
152
|
+
alias_type 0, 'int8', 'int2'
|
153
|
+
alias_type 0, 'oid', 'int2'
|
154
|
+
|
155
|
+
# register_type 0, 'numeric', OID::Decimal.new
|
156
|
+
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
157
|
+
alias_type 0, 'varchar', 'text'
|
158
|
+
alias_type 0, 'char', 'text'
|
159
|
+
alias_type 0, 'bpchar', 'text'
|
160
|
+
alias_type 0, 'xml', 'text'
|
161
|
+
|
162
|
+
# # FIXME: why are we keeping these types as strings?
|
163
|
+
# alias_type 'tsvector', 'text'
|
164
|
+
# alias_type 'interval', 'text'
|
165
|
+
# alias_type 'macaddr', 'text'
|
166
|
+
# alias_type 'uuid', 'text'
|
167
|
+
#
|
168
|
+
# register_type 'money', OID::Money.new
|
169
|
+
# There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
|
170
|
+
# in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
|
171
|
+
register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
|
172
|
+
register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
|
173
|
+
# register_type 'bit', OID::Bit.new
|
174
|
+
# register_type 'varbit', OID::Bit.new
|
175
|
+
#
|
176
|
+
register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
|
177
|
+
alias_type 0, 'float8', 'float4'
|
178
|
+
|
179
|
+
register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
|
180
|
+
register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
|
181
|
+
register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
|
182
|
+
# register_type 'time', OID::Time.new
|
183
|
+
#
|
184
|
+
# register_type 'path', OID::Text.new
|
185
|
+
# register_type 'point', OID::Point.new
|
186
|
+
# register_type 'polygon', OID::Text.new
|
187
|
+
# register_type 'circle', OID::Text.new
|
188
|
+
# register_type 'hstore', OID::Hstore.new
|
189
|
+
# register_type 'json', OID::Json.new
|
190
|
+
# register_type 'citext', OID::Text.new
|
191
|
+
# register_type 'ltree', OID::Text.new
|
192
|
+
#
|
193
|
+
# register_type 'cidr', OID::Cidr.new
|
194
|
+
# alias_type 'inet', 'cidr'
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
|
199
|
+
register_type 1, 'int4', PG::BinaryEncoder::Int4, PG::BinaryDecoder::Integer
|
200
|
+
register_type 1, 'int8', PG::BinaryEncoder::Int8, PG::BinaryDecoder::Integer
|
201
|
+
alias_type 1, 'oid', 'int2'
|
202
|
+
|
203
|
+
register_type 1, 'text', PG::BinaryEncoder::String, PG::BinaryDecoder::String
|
204
|
+
alias_type 1, 'varchar', 'text'
|
205
|
+
alias_type 1, 'char', 'text'
|
206
|
+
alias_type 1, 'bpchar', 'text'
|
207
|
+
alias_type 1, 'xml', 'text'
|
208
|
+
|
209
|
+
register_type 1, 'bytea', PG::BinaryEncoder::Bytea, PG::BinaryDecoder::Bytea
|
210
|
+
register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
|
211
|
+
register_type 1, 'float4', nil, PG::BinaryDecoder::Float
|
212
|
+
register_type 1, 'float8', nil, PG::BinaryDecoder::Float
|
213
|
+
end
|
214
|
+
|
215
|
+
# Simple set of rules for type casting common PostgreSQL types to Ruby.
|
216
|
+
#
|
217
|
+
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
218
|
+
# PostgreSQL's pg_type table in PG::BasicTypeMapForResults.new .
|
219
|
+
#
|
220
|
+
# Result values are type casted based on the type OID of the given result column.
|
221
|
+
#
|
222
|
+
# Higher level libraries will most likely not make use of this class, but use their
|
223
|
+
# own set of rules to choose suitable encoders and decoders.
|
224
|
+
#
|
225
|
+
# Example:
|
226
|
+
# conn = PG::Connection.new
|
227
|
+
# # Assign a default ruleset for type casts of input and output values.
|
228
|
+
# conn.type_mapping = PG::BasicTypeMapping.new(conn)
|
229
|
+
# # Execute a query.
|
230
|
+
# res = conn.exec_params( "SELECT $1::INT", ['5'] )
|
231
|
+
# # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
|
232
|
+
# # is done by PG::TextDecoder::Integer internally for all value retrieval methods.
|
233
|
+
# res.values # => [[5]]
|
234
|
+
#
|
235
|
+
# PG::TypeMapByOid#fit_to_result(result, false) can be used to generate
|
236
|
+
# a result independent PG::TypeMapByColumn type map, which can subsequently be used
|
237
|
+
# to cast #get_copy_data fields. See also PG::BasicTypeMapBasedOnResult .
|
238
|
+
#
|
239
|
+
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
240
|
+
include PG::BasicTypeRegistry
|
241
|
+
|
242
|
+
def initialize(connection)
|
243
|
+
@coder_maps = build_coder_maps(connection)
|
244
|
+
|
245
|
+
# Populate TypeMapByOid hash with decoders
|
246
|
+
@coder_maps.map{|f| f[:decoder].coders }.flatten.each do |coder|
|
247
|
+
add_coder(coder)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Simple set of rules for type casting common PostgreSQL types from Ruby
|
253
|
+
# to PostgreSQL.
|
254
|
+
#
|
255
|
+
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
256
|
+
# PostgreSQL's pg_type table in PG::BasicTypeMapBasedOnResult.new .
|
257
|
+
#
|
258
|
+
# This class works equal to PG::BasicTypeMapForResults, but does not define decoders for
|
259
|
+
# the given result OIDs, but encoders. So it can be used to type cast field values based on
|
260
|
+
# the type OID retrieved by a separate SQL query.
|
261
|
+
#
|
262
|
+
# PG::TypeMapByOid#fit_to_result(result, false) can be used to generate a result independent
|
263
|
+
# PG::TypeMapByColumn type map, which can subsequently be used to cast query bind parameters
|
264
|
+
# or #put_copy_data fields.
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
# conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
|
268
|
+
#
|
269
|
+
# # Retrieve table OIDs per empty result set.
|
270
|
+
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
271
|
+
# tm = basic_type_mapping.fit_to_result( res, false )
|
272
|
+
# row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
273
|
+
#
|
274
|
+
# conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
|
275
|
+
# conn.put_copy_data ['a', 123, [5,4,3]]
|
276
|
+
# end
|
277
|
+
class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
|
278
|
+
include PG::BasicTypeRegistry
|
279
|
+
|
280
|
+
def initialize(connection)
|
281
|
+
@coder_maps = build_coder_maps(connection)
|
282
|
+
|
283
|
+
# Populate TypeMapByOid hash with encoders
|
284
|
+
@coder_maps.map{|f| f[:encoder].coders }.flatten.each do |coder|
|
285
|
+
add_coder(coder)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Simple set of rules for type casting common Ruby types to PostgreSQL.
|
291
|
+
#
|
292
|
+
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
293
|
+
# PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
|
294
|
+
#
|
295
|
+
# Query params are type casted based on the MRI internal type of the given value.
|
296
|
+
#
|
297
|
+
# Higher level libraries will most likely not make use of this class, but use their
|
298
|
+
# own set of rules to choose suitable encoders and decoders.
|
299
|
+
#
|
300
|
+
# Example:
|
301
|
+
# conn = PG::Connection.new
|
302
|
+
# # Assign a default ruleset for type casts of input and output values.
|
303
|
+
# conn.type_mapping_for_queries = PG::BasicTypeMapForQueries.new(conn)
|
304
|
+
# # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
|
305
|
+
# # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
|
306
|
+
# res = conn.exec_params( "SELECT $1", [5] )
|
307
|
+
class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
|
308
|
+
include PG::BasicTypeRegistry
|
309
|
+
|
310
|
+
def initialize(connection)
|
311
|
+
@coder_maps = build_coder_maps(connection)
|
312
|
+
|
313
|
+
populate_encoder_list
|
314
|
+
@array_encoders_by_klass = array_encoders_by_klass
|
315
|
+
@anyarray_encoder = coder_by_name(0, :encoder, '_any')
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def coder_by_name(format, direction, name)
|
321
|
+
check_format_and_direction(format, direction)
|
322
|
+
@coder_maps[format][direction].coder_by_name(name)
|
323
|
+
end
|
324
|
+
|
325
|
+
def populate_encoder_list
|
326
|
+
DEFAULT_TYPE_MAP.each do |mri_type, selector|
|
327
|
+
if Array === selector
|
328
|
+
format, name, oid_name = selector
|
329
|
+
coder = coder_by_name(format, :encoder, name).dup
|
330
|
+
if oid_name
|
331
|
+
coder.oid = coder_by_name(format, :encoder, oid_name).oid
|
332
|
+
else
|
333
|
+
coder.oid = 0
|
334
|
+
end
|
335
|
+
self[mri_type] = coder
|
336
|
+
else
|
337
|
+
self[mri_type] = selector
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def array_encoders_by_klass
|
343
|
+
DEFAULT_ARRAY_TYPE_MAP.inject({}) do |h, (klass, (format, name))|
|
344
|
+
h[klass] = coder_by_name(format, :encoder, name)
|
345
|
+
h
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def get_array_type(value)
|
350
|
+
elem = value
|
351
|
+
while elem.kind_of?(Array)
|
352
|
+
elem = elem.first
|
353
|
+
end
|
354
|
+
@array_encoders_by_klass[elem.class] || @anyarray_encoder
|
355
|
+
end
|
356
|
+
|
357
|
+
DEFAULT_TYPE_MAP = {
|
358
|
+
'T_TRUE'.freeze => [1, 'bool', 'bool'],
|
359
|
+
'T_FALSE'.freeze => [1, 'bool', 'bool'],
|
360
|
+
# We use text format and no type OID for numbers, because setting the OID can lead
|
361
|
+
# to unnecessary type conversions on server side.
|
362
|
+
'T_FIXNUM'.freeze => [0, 'int8'],
|
363
|
+
'T_BIGNUM'.freeze => [0, 'int8'],
|
364
|
+
'T_FLOAT'.freeze => [0, 'float8'],
|
365
|
+
'T_ARRAY'.freeze => :get_array_type,
|
366
|
+
}
|
367
|
+
|
368
|
+
DEFAULT_ARRAY_TYPE_MAP = {
|
369
|
+
TrueClass => [0, '_bool'],
|
370
|
+
FalseClass => [0, '_bool'],
|
371
|
+
Fixnum => [0, '_int8'],
|
372
|
+
Bignum => [0, '_int8'],
|
373
|
+
String => [0, '_text'],
|
374
|
+
Float => [0, '_float8'],
|
375
|
+
}
|
376
|
+
|
377
|
+
end
|