pg 1.2.3-x86-mingw32 → 1.3.0.rc4-x86-mingw32
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/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +130 -0
- data/.gitignore +13 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.rdoc +77 -7
- data/Manifest.txt +0 -1
- data/README.rdoc +7 -6
- data/Rakefile +27 -138
- data/Rakefile.cross +6 -5
- data/certs/ged.pem +24 -0
- data/ext/errorcodes.def +8 -0
- data/ext/errorcodes.txt +3 -1
- data/ext/extconf.rb +91 -19
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +59 -4
- data/ext/pg.h +18 -1
- data/ext/pg_coder.c +82 -28
- data/ext/pg_connection.c +676 -492
- data/ext/pg_copy_coder.c +45 -16
- data/ext/pg_record_coder.c +39 -11
- data/ext/pg_result.c +61 -31
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +6 -6
- data/ext/pg_tuple.c +47 -21
- data/ext/pg_type_map.c +41 -8
- data/ext/pg_type_map_all_strings.c +14 -1
- data/ext/pg_type_map_by_class.c +49 -24
- data/ext/pg_type_map_by_column.c +64 -28
- data/ext/pg_type_map_by_mri_type.c +47 -18
- data/ext/pg_type_map_by_oid.c +52 -23
- data/ext/pg_type_map_in_ruby.c +50 -19
- data/ext/pg_util.c +2 -2
- data/lib/2.5/pg_ext.so +0 -0
- data/lib/2.6/pg_ext.so +0 -0
- data/lib/2.7/pg_ext.so +0 -0
- data/lib/3.0/pg_ext.so +0 -0
- data/lib/3.1/pg_ext.so +0 -0
- data/lib/pg/basic_type_map_based_on_result.rb +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +296 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +587 -58
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +40 -27
- data/lib/x86-mingw32/libpq.dll +0 -0
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +32 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +106 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +93 -233
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -0
- data/lib/2.2/pg_ext.so +0 -0
- data/lib/2.3/pg_ext.so +0 -0
- data/lib/2.4/pg_ext.so +0 -0
- data/lib/pg/basic_type_mapping.rb +0 -522
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -380
- data/spec/pg/basic_type_mapping_spec.rb +0 -630
- data/spec/pg/connection_spec.rb +0 -1949
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -681
- data/spec/pg/tuple_spec.rb +0 -333
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -226
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -1123
- data/spec/pg_spec.rb +0 -50
data/ChangeLog
DELETED
File without changes
|
data/lib/2.2/pg_ext.so
DELETED
Binary file
|
data/lib/2.3/pg_ext.so
DELETED
Binary file
|
data/lib/2.4/pg_ext.so
DELETED
Binary file
|
@@ -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
|