pg 1.2.3 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +80 -0
  6. data/.github/workflows/source-gem.yml +129 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +75 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +5 -5
  20. data/certs/ged.pem +24 -0
  21. data/ext/errorcodes.def +8 -0
  22. data/ext/errorcodes.txt +3 -1
  23. data/ext/extconf.rb +90 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +35 -1
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +538 -279
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +38 -10
  32. data/ext/pg_result.c +61 -31
  33. data/ext/pg_text_decoder.c +1 -1
  34. data/ext/pg_text_encoder.c +6 -6
  35. data/ext/pg_tuple.c +47 -21
  36. data/ext/pg_type_map.c +41 -8
  37. data/ext/pg_type_map_all_strings.c +14 -1
  38. data/ext/pg_type_map_by_class.c +49 -24
  39. data/ext/pg_type_map_by_column.c +64 -28
  40. data/ext/pg_type_map_by_mri_type.c +47 -18
  41. data/ext/pg_type_map_by_oid.c +52 -23
  42. data/ext/pg_type_map_in_ruby.c +50 -19
  43. data/ext/pg_util.c +2 -2
  44. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  45. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  46. data/lib/pg/basic_type_map_for_results.rb +81 -0
  47. data/lib/pg/basic_type_registry.rb +296 -0
  48. data/lib/pg/coder.rb +1 -1
  49. data/lib/pg/connection.rb +369 -56
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +38 -25
  52. data/misc/openssl-pg-segfault.rb +31 -0
  53. data/misc/postgres/History.txt +9 -0
  54. data/misc/postgres/Manifest.txt +5 -0
  55. data/misc/postgres/README.txt +21 -0
  56. data/misc/postgres/Rakefile +21 -0
  57. data/misc/postgres/lib/postgres.rb +16 -0
  58. data/misc/ruby-pg/History.txt +9 -0
  59. data/misc/ruby-pg/Manifest.txt +5 -0
  60. data/misc/ruby-pg/README.txt +21 -0
  61. data/misc/ruby-pg/Rakefile +21 -0
  62. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  63. data/pg.gemspec +32 -0
  64. data/sample/array_insert.rb +20 -0
  65. data/sample/async_api.rb +106 -0
  66. data/sample/async_copyto.rb +39 -0
  67. data/sample/async_mixed.rb +56 -0
  68. data/sample/check_conn.rb +21 -0
  69. data/sample/copydata.rb +71 -0
  70. data/sample/copyfrom.rb +81 -0
  71. data/sample/copyto.rb +19 -0
  72. data/sample/cursor.rb +21 -0
  73. data/sample/disk_usage_report.rb +177 -0
  74. data/sample/issue-119.rb +94 -0
  75. data/sample/losample.rb +69 -0
  76. data/sample/minimal-testcase.rb +17 -0
  77. data/sample/notify_wait.rb +72 -0
  78. data/sample/pg_statistics.rb +285 -0
  79. data/sample/replication_monitor.rb +222 -0
  80. data/sample/test_binary_values.rb +33 -0
  81. data/sample/wal_shipper.rb +434 -0
  82. data/sample/warehouse_partitions.rb +311 -0
  83. data.tar.gz.sig +0 -0
  84. metadata +79 -226
  85. metadata.gz.sig +0 -0
  86. data/ChangeLog +0 -0
  87. data/lib/pg/basic_type_mapping.rb +0 -522
  88. data/spec/data/expected_trace.out +0 -26
  89. data/spec/data/random_binary_data +0 -0
  90. data/spec/helpers.rb +0 -380
  91. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  92. data/spec/pg/connection_spec.rb +0 -1949
  93. data/spec/pg/connection_sync_spec.rb +0 -41
  94. data/spec/pg/result_spec.rb +0 -681
  95. data/spec/pg/tuple_spec.rb +0 -333
  96. data/spec/pg/type_map_by_class_spec.rb +0 -138
  97. data/spec/pg/type_map_by_column_spec.rb +0 -226
  98. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  99. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  100. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  101. data/spec/pg/type_map_spec.rb +0 -22
  102. data/spec/pg/type_spec.rb +0 -1123
  103. data/spec/pg_spec.rb +0 -50
@@ -1,522 +0,0 @@
1
- # -*- ruby -*-
2
- # frozen_string_literal: true
3
-
4
- require 'pg' unless defined?( PG )
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)
26
- module PG::BasicTypeRegistry
27
- # An instance of this class stores the coders that should be used for a given wire format (text or binary)
28
- # and type cast direction (encoder or decoder).
29
- class CoderMap
30
- # Hash of text types that don't require quotation, when used within composite types.
31
- # type.name => true
32
- DONT_QUOTE_TYPES = %w[
33
- int2 int4 int8
34
- float4 float8
35
- oid
36
- bool
37
- date timestamp timestamptz
38
- ].inject({}){|h,e| h[e] = true; h }
39
-
40
- def initialize(result, coders_by_name, format, arraycoder)
41
- coder_map = {}
42
-
43
- _ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' }
44
- leaves, nodes = nodes.partition { |row| row['typelem'].to_i == 0 }
45
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
46
-
47
- # populate the enum types
48
- _enums, leaves = leaves.partition { |row| row['typinput'] == 'enum_in' }
49
- # enums.each do |row|
50
- # coder_map[row['oid'].to_i] = OID::Enum.new
51
- # end
52
-
53
- # populate the base types
54
- leaves.find_all { |row| coders_by_name.key?(row['typname']) }.each do |row|
55
- coder = coders_by_name[row['typname']].dup
56
- coder.oid = row['oid'].to_i
57
- coder.name = row['typname']
58
- coder.format = format
59
- coder_map[coder.oid] = coder
60
- end
61
-
62
- _records_by_oid = result.group_by { |row| row['oid'] }
63
-
64
- # populate composite types
65
- # nodes.each do |row|
66
- # add_oid row, records_by_oid, coder_map
67
- # end
68
-
69
- if arraycoder
70
- # populate array types
71
- arrays.each do |row|
72
- elements_coder = coder_map[row['typelem'].to_i]
73
- next unless elements_coder
74
-
75
- coder = arraycoder.new
76
- coder.oid = row['oid'].to_i
77
- coder.name = row['typname']
78
- coder.format = format
79
- coder.elements_type = elements_coder
80
- coder.needs_quotation = !DONT_QUOTE_TYPES[elements_coder.name]
81
- coder_map[coder.oid] = coder
82
- end
83
- end
84
-
85
- # populate range types
86
- # ranges.find_all { |row| coder_map.key? row['rngsubtype'].to_i }.each do |row|
87
- # subcoder = coder_map[row['rngsubtype'].to_i]
88
- # range = OID::Range.new subcoder
89
- # coder_map[row['oid'].to_i] = range
90
- # end
91
-
92
- @coders = coder_map.values
93
- @coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
94
- @coders_by_oid = @coders.inject({}){|h, t| h[t.oid] = t; h }
95
- @typenames_by_oid = result.inject({}){|h, t| h[t['oid'].to_i] = t['typname']; h }
96
- end
97
-
98
- attr_reader :coders
99
- attr_reader :coders_by_oid
100
- attr_reader :coders_by_name
101
- attr_reader :typenames_by_oid
102
-
103
- def coder_by_name(name)
104
- @coders_by_name[name]
105
- end
106
-
107
- def coder_by_oid(oid)
108
- @coders_by_oid[oid]
109
- end
110
- end
111
-
112
- private
113
-
114
- def supports_ranges?(connection)
115
- connection.server_version >= 90200
116
- end
117
-
118
- def build_coder_maps(connection)
119
- if supports_ranges?(connection)
120
- result = connection.exec <<-SQL
121
- SELECT t.oid, t.typname::text, t.typelem, t.typdelim, t.typinput::text, r.rngsubtype
122
- FROM pg_type as t
123
- LEFT JOIN pg_range as r ON oid = rngtypid
124
- SQL
125
- else
126
- result = connection.exec <<-SQL
127
- SELECT t.oid, t.typname::text, t.typelem, t.typdelim, t.typinput::text
128
- FROM pg_type as t
129
- SQL
130
- end
131
-
132
- [
133
- [0, :encoder, PG::TextEncoder::Array],
134
- [0, :decoder, PG::TextDecoder::Array],
135
- [1, :encoder, nil],
136
- [1, :decoder, nil],
137
- ].inject([]) do |h, (format, direction, arraycoder)|
138
- h[format] ||= {}
139
- h[format][direction] = CoderMap.new result, CODERS_BY_NAME[format][direction], format, arraycoder
140
- h
141
- end
142
- end
143
-
144
- ValidFormats = { 0 => true, 1 => true }
145
- ValidDirections = { :encoder => true, :decoder => true }
146
-
147
- def check_format_and_direction(format, direction)
148
- raise(ArgumentError, "Invalid format value %p" % format) unless ValidFormats[format]
149
- raise(ArgumentError, "Invalid direction %p" % direction) unless ValidDirections[direction]
150
- end
151
- protected :check_format_and_direction
152
-
153
- # The key of this hash maps to the `typname` column from the table.
154
- # encoder_map is then dynamically built with oids as the key and Type
155
- # objects as values.
156
- CODERS_BY_NAME = []
157
-
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.
175
- def self.register_type(format, name, encoder_class, 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
178
- end
179
-
180
- # Alias the +old+ type to the +new+ type.
181
- def self.alias_type(format, new, old)
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
190
- end
191
-
192
- register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
193
- alias_type 0, 'int4', 'int2'
194
- alias_type 0, 'int8', 'int2'
195
- alias_type 0, 'oid', 'int2'
196
-
197
- register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
198
- register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
199
- alias_type 0, 'varchar', 'text'
200
- alias_type 0, 'char', 'text'
201
- alias_type 0, 'bpchar', 'text'
202
- alias_type 0, 'xml', 'text'
203
-
204
- # FIXME: why are we keeping these types as strings?
205
- # alias_type 'tsvector', 'text'
206
- # alias_type 'interval', 'text'
207
- # alias_type 'macaddr', 'text'
208
- # alias_type 'uuid', 'text'
209
- #
210
- # register_type 'money', OID::Money.new
211
- # There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
212
- # in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
213
- register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
214
- register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
215
- # register_type 'bit', OID::Bit.new
216
- # register_type 'varbit', OID::Bit.new
217
-
218
- register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
219
- alias_type 0, 'float8', 'float4'
220
-
221
- register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
222
- register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
223
- register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
224
- # register_type 'time', OID::Time.new
225
- #
226
- # register_type 'path', OID::Text.new
227
- # register_type 'point', OID::Point.new
228
- # register_type 'polygon', OID::Text.new
229
- # register_type 'circle', OID::Text.new
230
- # register_type 'hstore', OID::Hstore.new
231
- register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
232
- alias_type 0, 'jsonb', 'json'
233
- # register_type 'citext', OID::Text.new
234
- # register_type 'ltree', OID::Text.new
235
- #
236
- register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
237
- alias_type 0, 'cidr', 'inet'
238
-
239
-
240
-
241
- register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
242
- register_type 1, 'int4', PG::BinaryEncoder::Int4, PG::BinaryDecoder::Integer
243
- register_type 1, 'int8', PG::BinaryEncoder::Int8, PG::BinaryDecoder::Integer
244
- alias_type 1, 'oid', 'int2'
245
-
246
- register_type 1, 'text', PG::BinaryEncoder::String, PG::BinaryDecoder::String
247
- alias_type 1, 'varchar', 'text'
248
- alias_type 1, 'char', 'text'
249
- alias_type 1, 'bpchar', 'text'
250
- alias_type 1, 'xml', 'text'
251
-
252
- register_type 1, 'bytea', PG::BinaryEncoder::Bytea, PG::BinaryDecoder::Bytea
253
- register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
254
- register_type 1, 'float4', nil, PG::BinaryDecoder::Float
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
258
- end
259
-
260
- # Simple set of rules for type casting common PostgreSQL types to Ruby.
261
- #
262
- # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
263
- # PostgreSQL's +pg_type+ table in PG::BasicTypeMapForResults.new .
264
- #
265
- # Result values are type casted based on the type OID of the given result column.
266
- #
267
- # Higher level libraries will most likely not make use of this class, but use their
268
- # own set of rules to choose suitable encoders and decoders.
269
- #
270
- # Example:
271
- # conn = PG::Connection.new
272
- # # Assign a default ruleset for type casts of output values.
273
- # conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
274
- # # Execute a query.
275
- # res = conn.exec_params( "SELECT $1::INT", ['5'] )
276
- # # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
277
- # # is done by PG::TextDecoder::Integer internally for all value retrieval methods.
278
- # res.values # => [[5]]
279
- #
280
- # PG::TypeMapByOid#build_column_map(result) can be used to generate
281
- # a result independent PG::TypeMapByColumn type map, which can subsequently be used
282
- # to cast #get_copy_data fields:
283
- #
284
- # For the following table:
285
- # conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
286
- #
287
- # # Retrieve table OIDs per empty result set.
288
- # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
289
- # # Build a type map for common database to ruby type decoders.
290
- # btm = PG::BasicTypeMapForResults.new(conn)
291
- # # Build a PG::TypeMapByColumn with decoders suitable for copytable.
292
- # tm = btm.build_column_map( res )
293
- # row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
294
- #
295
- # conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
296
- # while row=conn.get_copy_data
297
- # p row
298
- # end
299
- # end
300
- # This prints the rows with type casted columns:
301
- # ["a", 123, [5, 4, 3]]
302
- #
303
- # See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
304
- class PG::BasicTypeMapForResults < PG::TypeMapByOid
305
- include PG::BasicTypeRegistry
306
-
307
- class WarningTypeMap < PG::TypeMapInRuby
308
- def initialize(typenames)
309
- @already_warned = Hash.new{|h, k| h[k] = {} }
310
- @typenames_by_oid = typenames
311
- end
312
-
313
- def typecast_result_value(result, _tuple, field)
314
- format = result.fformat(field)
315
- oid = result.ftype(field)
316
- unless @already_warned[format][oid]
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."
318
- @already_warned[format][oid] = true
319
- end
320
- super
321
- end
322
- end
323
-
324
- def initialize(connection)
325
- @coder_maps = build_coder_maps(connection)
326
-
327
- # Populate TypeMapByOid hash with decoders
328
- @coder_maps.map{|f| f[:decoder].coders }.flatten.each do |coder|
329
- add_coder(coder)
330
- end
331
-
332
- typenames = @coder_maps.map{|f| f[:decoder].typenames_by_oid }
333
- self.default_type_map = WarningTypeMap.new(typenames)
334
- end
335
- end
336
-
337
- # Simple set of rules for type casting common PostgreSQL types from Ruby
338
- # to PostgreSQL.
339
- #
340
- # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
341
- # PostgreSQL's +pg_type+ table in PG::BasicTypeMapBasedOnResult.new .
342
- #
343
- # This class works equal to PG::BasicTypeMapForResults, but does not define decoders for
344
- # the given result OIDs, but encoders. So it can be used to type cast field values based on
345
- # the type OID retrieved by a separate SQL query.
346
- #
347
- # PG::TypeMapByOid#build_column_map(result) can be used to generate a result independent
348
- # PG::TypeMapByColumn type map, which can subsequently be used to cast query bind parameters
349
- # or #put_copy_data fields.
350
- #
351
- # Example:
352
- # conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
353
- #
354
- # # Retrieve table OIDs per empty result set.
355
- # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
356
- # # Build a type map for common ruby to database type encoders.
357
- # btm = PG::BasicTypeMapBasedOnResult.new(conn)
358
- # # Build a PG::TypeMapByColumn with encoders suitable for copytable.
359
- # tm = btm.build_column_map( res )
360
- # row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
361
- #
362
- # conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
363
- # conn.put_copy_data ['a', 123, [5,4,3]]
364
- # end
365
- # This inserts a single row into copytable with type casts from ruby to
366
- # database types.
367
- class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
368
- include PG::BasicTypeRegistry
369
-
370
- def initialize(connection)
371
- @coder_maps = build_coder_maps(connection)
372
-
373
- # Populate TypeMapByOid hash with encoders
374
- @coder_maps.map{|f| f[:encoder].coders }.flatten.each do |coder|
375
- add_coder(coder)
376
- end
377
- end
378
- end
379
-
380
- # Simple set of rules for type casting common Ruby types to PostgreSQL.
381
- #
382
- # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
383
- # PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
384
- #
385
- # Query params are type casted based on the class of the given value.
386
- #
387
- # Higher level libraries will most likely not make use of this class, but use their
388
- # own derivation of PG::TypeMapByClass or another set of rules to choose suitable
389
- # encoders and decoders for the values to be sent.
390
- #
391
- # Example:
392
- # conn = PG::Connection.new
393
- # # Assign a default ruleset for type casts of input and output values.
394
- # conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
395
- # # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
396
- # # The format of the parameter is set to 0 (text) and the OID of this parameter is set to 20 (int8).
397
- # res = conn.exec_params( "SELECT $1", [5] )
398
- class PG::BasicTypeMapForQueries < PG::TypeMapByClass
399
- include PG::BasicTypeRegistry
400
-
401
- def initialize(connection)
402
- @coder_maps = build_coder_maps(connection)
403
- @array_encoders_by_klass = array_encoders_by_klass
404
- @encode_array_as = :array
405
- init_encoders
406
- end
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
-
436
- private
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
-
444
- def coder_by_name(format, direction, name)
445
- check_format_and_direction(format, direction)
446
- @coder_maps[format][direction].coder_by_name(name)
447
- end
448
-
449
- def populate_encoder_list
450
- DEFAULT_TYPE_MAP.each do |klass, selector|
451
- if Array === selector
452
- format, name, oid_name = selector
453
- coder = coder_by_name(format, :encoder, name).dup
454
- if oid_name
455
- coder.oid = coder_by_name(format, :encoder, oid_name).oid
456
- else
457
- coder.oid = 0
458
- end
459
- self[klass] = coder
460
- else
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
474
- end
475
- end
476
- end
477
-
478
- def array_encoders_by_klass
479
- DEFAULT_ARRAY_TYPE_MAP.inject({}) do |h, (klass, (format, name))|
480
- h[klass] = coder_by_name(format, :encoder, name)
481
- h
482
- end
483
- end
484
-
485
- def get_array_type(value)
486
- elem = value
487
- while elem.kind_of?(Array)
488
- elem = elem.first
489
- end
490
- @array_encoders_by_klass[elem.class] ||
491
- elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
492
- @textarray_encoder
493
- end
494
-
495
- DEFAULT_TYPE_MAP = {
496
- TrueClass => [1, 'bool', 'bool'],
497
- FalseClass => [1, 'bool', 'bool'],
498
- # We use text format and no type OID for numbers, because setting the OID can lead
499
- # to unnecessary type conversions on server side.
500
- Integer => [0, 'int8'],
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'],
508
- Array => :get_array_type,
509
- }
510
-
511
- DEFAULT_ARRAY_TYPE_MAP = {
512
- TrueClass => [0, '_bool'],
513
- FalseClass => [0, '_bool'],
514
- Integer => [0, '_int8'],
515
- String => [0, '_text'],
516
- Float => [0, '_float8'],
517
- BigDecimal => [0, '_numeric'],
518
- Time => [0, '_timestamptz'],
519
- IPAddr => [0, '_inet'],
520
- }
521
-
522
- end
@@ -1,26 +0,0 @@
1
- To backend> Msg Q
2
- To backend> "SELECT 1 AS one"
3
- To backend> Msg complete, length 21
4
- From backend> T
5
- From backend (#4)> 28
6
- From backend (#2)> 1
7
- From backend> "one"
8
- From backend (#4)> 0
9
- From backend (#2)> 0
10
- From backend (#4)> 23
11
- From backend (#2)> 4
12
- From backend (#4)> -1
13
- From backend (#2)> 0
14
- From backend> D
15
- From backend (#4)> 11
16
- From backend (#2)> 1
17
- From backend (#4)> 1
18
- From backend (1)> 1
19
- From backend> C
20
- From backend (#4)> 11
21
- From backend> "SELECT"
22
- From backend> Z
23
- From backend (#4)> 5
24
- From backend> Z
25
- From backend (#4)> 5
26
- From backend> T
Binary file