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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +1573 -2
  5. data/History.rdoc +3 -11
  6. data/Manifest.txt +24 -0
  7. data/README.rdoc +51 -4
  8. data/Rakefile +20 -14
  9. data/Rakefile.cross +39 -32
  10. data/ext/extconf.rb +27 -26
  11. data/ext/pg.c +75 -21
  12. data/ext/pg.h +194 -6
  13. data/ext/pg_binary_decoder.c +160 -0
  14. data/ext/pg_binary_encoder.c +160 -0
  15. data/ext/pg_coder.c +454 -0
  16. data/ext/pg_connection.c +815 -518
  17. data/ext/pg_copy_coder.c +557 -0
  18. data/ext/pg_result.c +258 -103
  19. data/ext/pg_text_decoder.c +424 -0
  20. data/ext/pg_text_encoder.c +608 -0
  21. data/ext/pg_type_map.c +113 -0
  22. data/ext/pg_type_map_all_strings.c +113 -0
  23. data/ext/pg_type_map_by_column.c +254 -0
  24. data/ext/pg_type_map_by_mri_type.c +266 -0
  25. data/ext/pg_type_map_by_oid.c +341 -0
  26. data/ext/util.c +121 -0
  27. data/ext/util.h +63 -0
  28. data/lib/pg.rb +11 -1
  29. data/lib/pg/basic_type_mapping.rb +377 -0
  30. data/lib/pg/coder.rb +74 -0
  31. data/lib/pg/connection.rb +38 -5
  32. data/lib/pg/result.rb +13 -3
  33. data/lib/pg/text_decoder.rb +42 -0
  34. data/lib/pg/text_encoder.rb +27 -0
  35. data/lib/pg/type_map_by_column.rb +15 -0
  36. data/spec/helpers.rb +9 -1
  37. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  38. data/spec/pg/connection_spec.rb +232 -13
  39. data/spec/pg/result_spec.rb +52 -0
  40. data/spec/pg/type_map_by_column_spec.rb +135 -0
  41. data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
  42. data/spec/pg/type_map_by_oid_spec.rb +133 -0
  43. data/spec/pg/type_map_spec.rb +39 -0
  44. data/spec/pg/type_spec.rb +620 -0
  45. metadata +40 -4
  46. 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: 8e95248e80f6 $
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