pg 1.1.4

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 (77) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gemtest +0 -0
  5. data/BSDL +22 -0
  6. data/ChangeLog +6595 -0
  7. data/Contributors.rdoc +46 -0
  8. data/History.rdoc +492 -0
  9. data/LICENSE +56 -0
  10. data/Manifest.txt +72 -0
  11. data/POSTGRES +23 -0
  12. data/README-OS_X.rdoc +68 -0
  13. data/README-Windows.rdoc +56 -0
  14. data/README.ja.rdoc +14 -0
  15. data/README.rdoc +178 -0
  16. data/Rakefile +215 -0
  17. data/Rakefile.cross +298 -0
  18. data/ext/errorcodes.def +968 -0
  19. data/ext/errorcodes.rb +45 -0
  20. data/ext/errorcodes.txt +478 -0
  21. data/ext/extconf.rb +94 -0
  22. data/ext/gvl_wrappers.c +17 -0
  23. data/ext/gvl_wrappers.h +241 -0
  24. data/ext/pg.c +640 -0
  25. data/ext/pg.h +365 -0
  26. data/ext/pg_binary_decoder.c +229 -0
  27. data/ext/pg_binary_encoder.c +162 -0
  28. data/ext/pg_coder.c +549 -0
  29. data/ext/pg_connection.c +4252 -0
  30. data/ext/pg_copy_coder.c +596 -0
  31. data/ext/pg_errors.c +95 -0
  32. data/ext/pg_result.c +1501 -0
  33. data/ext/pg_text_decoder.c +981 -0
  34. data/ext/pg_text_encoder.c +682 -0
  35. data/ext/pg_tuple.c +541 -0
  36. data/ext/pg_type_map.c +166 -0
  37. data/ext/pg_type_map_all_strings.c +116 -0
  38. data/ext/pg_type_map_by_class.c +239 -0
  39. data/ext/pg_type_map_by_column.c +312 -0
  40. data/ext/pg_type_map_by_mri_type.c +284 -0
  41. data/ext/pg_type_map_by_oid.c +355 -0
  42. data/ext/pg_type_map_in_ruby.c +299 -0
  43. data/ext/util.c +149 -0
  44. data/ext/util.h +65 -0
  45. data/ext/vc/pg.sln +26 -0
  46. data/ext/vc/pg_18/pg.vcproj +216 -0
  47. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  48. data/lib/pg.rb +74 -0
  49. data/lib/pg/basic_type_mapping.rb +459 -0
  50. data/lib/pg/binary_decoder.rb +22 -0
  51. data/lib/pg/coder.rb +83 -0
  52. data/lib/pg/connection.rb +291 -0
  53. data/lib/pg/constants.rb +11 -0
  54. data/lib/pg/exceptions.rb +11 -0
  55. data/lib/pg/result.rb +31 -0
  56. data/lib/pg/text_decoder.rb +47 -0
  57. data/lib/pg/text_encoder.rb +69 -0
  58. data/lib/pg/tuple.rb +30 -0
  59. data/lib/pg/type_map_by_column.rb +15 -0
  60. data/spec/data/expected_trace.out +26 -0
  61. data/spec/data/random_binary_data +0 -0
  62. data/spec/helpers.rb +380 -0
  63. data/spec/pg/basic_type_mapping_spec.rb +508 -0
  64. data/spec/pg/connection_spec.rb +1872 -0
  65. data/spec/pg/connection_sync_spec.rb +41 -0
  66. data/spec/pg/result_spec.rb +491 -0
  67. data/spec/pg/tuple_spec.rb +280 -0
  68. data/spec/pg/type_map_by_class_spec.rb +138 -0
  69. data/spec/pg/type_map_by_column_spec.rb +222 -0
  70. data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
  71. data/spec/pg/type_map_by_oid_spec.rb +149 -0
  72. data/spec/pg/type_map_in_ruby_spec.rb +164 -0
  73. data/spec/pg/type_map_spec.rb +22 -0
  74. data/spec/pg/type_spec.rb +949 -0
  75. data/spec/pg_spec.rb +50 -0
  76. metadata +322 -0
  77. metadata.gz.sig +0 -0
@@ -0,0 +1,209 @@
1
+ <?xml version="1.0" encoding="Windows-1252"?>
2
+ <VisualStudioProject
3
+ ProjectType="Visual C++"
4
+ Version="9.00"
5
+ Name="pg_19"
6
+ ProjectGUID="{2EE30C74-074F-4611-B39B-38D5F3C9B071}"
7
+ RootNamespace="pg_19"
8
+ Keyword="Win32Proj"
9
+ TargetFrameworkVersion="196613"
10
+ >
11
+ <Platforms>
12
+ <Platform
13
+ Name="Win32"
14
+ />
15
+ </Platforms>
16
+ <ToolFiles>
17
+ </ToolFiles>
18
+ <Configurations>
19
+ <Configuration
20
+ Name="Debug|Win32"
21
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
22
+ IntermediateDirectory="$(ConfigurationName)"
23
+ ConfigurationType="2"
24
+ CharacterSet="1"
25
+ >
26
+ <Tool
27
+ Name="VCPreBuildEventTool"
28
+ />
29
+ <Tool
30
+ Name="VCCustomBuildTool"
31
+ />
32
+ <Tool
33
+ Name="VCXMLDataGeneratorTool"
34
+ />
35
+ <Tool
36
+ Name="VCWebServiceProxyGeneratorTool"
37
+ />
38
+ <Tool
39
+ Name="VCMIDLTool"
40
+ />
41
+ <Tool
42
+ Name="VCCLCompilerTool"
43
+ Optimization="0"
44
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PG_19_EXPORTS"
45
+ MinimalRebuild="true"
46
+ BasicRuntimeChecks="3"
47
+ RuntimeLibrary="3"
48
+ UsePrecompiledHeader="2"
49
+ WarningLevel="3"
50
+ DebugInformationFormat="4"
51
+ />
52
+ <Tool
53
+ Name="VCManagedResourceCompilerTool"
54
+ />
55
+ <Tool
56
+ Name="VCResourceCompilerTool"
57
+ />
58
+ <Tool
59
+ Name="VCPreLinkEventTool"
60
+ />
61
+ <Tool
62
+ Name="VCLinkerTool"
63
+ LinkIncremental="2"
64
+ GenerateDebugInformation="true"
65
+ SubSystem="2"
66
+ TargetMachine="1"
67
+ />
68
+ <Tool
69
+ Name="VCALinkTool"
70
+ />
71
+ <Tool
72
+ Name="VCManifestTool"
73
+ />
74
+ <Tool
75
+ Name="VCXDCMakeTool"
76
+ />
77
+ <Tool
78
+ Name="VCBscMakeTool"
79
+ />
80
+ <Tool
81
+ Name="VCFxCopTool"
82
+ />
83
+ <Tool
84
+ Name="VCAppVerifierTool"
85
+ />
86
+ <Tool
87
+ Name="VCPostBuildEventTool"
88
+ />
89
+ </Configuration>
90
+ <Configuration
91
+ Name="Release|Win32"
92
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
93
+ IntermediateDirectory="$(ConfigurationName)"
94
+ ConfigurationType="2"
95
+ CharacterSet="1"
96
+ WholeProgramOptimization="1"
97
+ >
98
+ <Tool
99
+ Name="VCPreBuildEventTool"
100
+ />
101
+ <Tool
102
+ Name="VCCustomBuildTool"
103
+ />
104
+ <Tool
105
+ Name="VCXMLDataGeneratorTool"
106
+ />
107
+ <Tool
108
+ Name="VCWebServiceProxyGeneratorTool"
109
+ />
110
+ <Tool
111
+ Name="VCMIDLTool"
112
+ />
113
+ <Tool
114
+ Name="VCCLCompilerTool"
115
+ Optimization="2"
116
+ EnableIntrinsicFunctions="true"
117
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PG_19_EXPORTS"
118
+ RuntimeLibrary="2"
119
+ EnableFunctionLevelLinking="true"
120
+ UsePrecompiledHeader="2"
121
+ WarningLevel="3"
122
+ DebugInformationFormat="3"
123
+ />
124
+ <Tool
125
+ Name="VCManagedResourceCompilerTool"
126
+ />
127
+ <Tool
128
+ Name="VCResourceCompilerTool"
129
+ />
130
+ <Tool
131
+ Name="VCPreLinkEventTool"
132
+ />
133
+ <Tool
134
+ Name="VCLinkerTool"
135
+ LinkIncremental="1"
136
+ GenerateDebugInformation="true"
137
+ SubSystem="2"
138
+ OptimizeReferences="2"
139
+ EnableCOMDATFolding="2"
140
+ TargetMachine="1"
141
+ />
142
+ <Tool
143
+ Name="VCALinkTool"
144
+ />
145
+ <Tool
146
+ Name="VCManifestTool"
147
+ />
148
+ <Tool
149
+ Name="VCXDCMakeTool"
150
+ />
151
+ <Tool
152
+ Name="VCBscMakeTool"
153
+ />
154
+ <Tool
155
+ Name="VCFxCopTool"
156
+ />
157
+ <Tool
158
+ Name="VCAppVerifierTool"
159
+ />
160
+ <Tool
161
+ Name="VCPostBuildEventTool"
162
+ />
163
+ </Configuration>
164
+ </Configurations>
165
+ <References>
166
+ </References>
167
+ <Files>
168
+ <Filter
169
+ Name="Source Files"
170
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
171
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
172
+ >
173
+ <File
174
+ RelativePath="..\..\compat.c"
175
+ >
176
+ </File>
177
+ <File
178
+ RelativePath="..\..\pg.c"
179
+ >
180
+ </File>
181
+ </Filter>
182
+ <Filter
183
+ Name="Header Files"
184
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
185
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
186
+ >
187
+ <File
188
+ RelativePath="..\..\compat.h"
189
+ >
190
+ </File>
191
+ <File
192
+ RelativePath="..\..\pg.h"
193
+ >
194
+ </File>
195
+ </Filter>
196
+ <Filter
197
+ Name="Resource Files"
198
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
199
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
200
+ >
201
+ </Filter>
202
+ <File
203
+ RelativePath=".\ReadMe.txt"
204
+ >
205
+ </File>
206
+ </Files>
207
+ <Globals>
208
+ </Globals>
209
+ </VisualStudioProject>
@@ -0,0 +1,74 @@
1
+ # -*- ruby -*-
2
+
3
+ begin
4
+ require 'pg_ext'
5
+ rescue LoadError
6
+ # If it's a Windows binary gem, try the <major>.<minor> subdirectory
7
+ if RUBY_PLATFORM =~/(mswin|mingw)/i
8
+ major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
9
+ raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
10
+
11
+ add_dll_path = proc do |path, &block|
12
+ begin
13
+ require 'ruby_installer/runtime'
14
+ RubyInstaller::Runtime.add_dll_directory(path, &block)
15
+ rescue LoadError
16
+ old_path = ENV['PATH']
17
+ ENV['PATH'] = "#{path};#{old_path}"
18
+ block.call
19
+ ENV['PATH'] = old_path
20
+ end
21
+ end
22
+
23
+ # Temporary add this directory for DLL search, so that libpq.dll can be found.
24
+ add_dll_path.call(__dir__) do
25
+ require "#{major_minor}/pg_ext"
26
+ end
27
+ else
28
+ raise
29
+ end
30
+
31
+ end
32
+
33
+
34
+ # The top-level PG namespace.
35
+ module PG
36
+
37
+ # Library version
38
+ VERSION = '1.1.4'
39
+
40
+ # VCS revision
41
+ REVISION = %q$Revision: 6f611e78845a $
42
+
43
+ class NotAllCopyDataRetrieved < PG::Error
44
+ end
45
+
46
+ ### Get the PG library version. If +include_buildnum+ is +true+, include the build ID.
47
+ def self::version_string( include_buildnum=false )
48
+ vstring = "%s %s" % [ self.name, VERSION ]
49
+ vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
50
+ return vstring
51
+ end
52
+
53
+
54
+ ### Convenience alias for PG::Connection.new.
55
+ def self::connect( *args )
56
+ return PG::Connection.new( *args )
57
+ end
58
+
59
+
60
+ require 'pg/exceptions'
61
+ require 'pg/constants'
62
+ require 'pg/coder'
63
+ require 'pg/binary_decoder'
64
+ require 'pg/text_encoder'
65
+ require 'pg/text_decoder'
66
+ require 'pg/basic_type_mapping'
67
+ require 'pg/type_map_by_column'
68
+ require 'pg/connection'
69
+ require 'pg/result'
70
+ require 'pg/tuple'
71
+
72
+ end # module PG
73
+
74
+
@@ -0,0 +1,459 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+ # This module defines the mapping between OID and encoder/decoder classes for PG::BasicTypeMapForResults, PG::BasicTypeMapForQueries and PG::BasicTypeMapBasedOnResult.
6
+ #
7
+ # Additional types can be added like so:
8
+ #
9
+ # require 'pg'
10
+ # require 'ipaddr'
11
+ #
12
+ # class InetDecoder < PG::SimpleDecoder
13
+ # def decode(string, tuple=nil, field=nil)
14
+ # IPAddr.new(string)
15
+ # end
16
+ # end
17
+ # class InetEncoder < PG::SimpleEncoder
18
+ # def encode(ip_addr)
19
+ # ip_addr.to_s
20
+ # end
21
+ # end
22
+ #
23
+ # # 0 if for text format, can also be 1 for binary
24
+ # PG::BasicTypeRegistry.register_type(0, 'inet', InetEncoder, InetDecoder)
25
+ module PG::BasicTypeRegistry
26
+ # An instance of this class stores the coders that should be used for a given wire format (text or binary)
27
+ # and type cast direction (encoder or decoder).
28
+ class CoderMap
29
+ # Hash of text types that don't require quotation, when used within composite types.
30
+ # type.name => true
31
+ DONT_QUOTE_TYPES = %w[
32
+ int2 int4 int8
33
+ float4 float8
34
+ oid
35
+ bool
36
+ date timestamp timestamptz
37
+ ].inject({}){|h,e| h[e] = true; h }
38
+
39
+ def initialize(result, coders_by_name, format, arraycoder)
40
+ coder_map = {}
41
+
42
+ _ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' }
43
+ leaves, nodes = nodes.partition { |row| row['typelem'].to_i == 0 }
44
+ arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
45
+
46
+ # populate the enum types
47
+ _enums, leaves = leaves.partition { |row| row['typinput'] == 'enum_in' }
48
+ # enums.each do |row|
49
+ # coder_map[row['oid'].to_i] = OID::Enum.new
50
+ # end
51
+
52
+ # populate the base types
53
+ leaves.find_all { |row| coders_by_name.key?(row['typname']) }.each do |row|
54
+ coder = coders_by_name[row['typname']].dup
55
+ coder.oid = row['oid'].to_i
56
+ coder.name = row['typname']
57
+ coder.format = format
58
+ coder_map[coder.oid] = coder
59
+ end
60
+
61
+ _records_by_oid = result.group_by { |row| row['oid'] }
62
+
63
+ # populate composite types
64
+ # nodes.each do |row|
65
+ # add_oid row, records_by_oid, coder_map
66
+ # end
67
+
68
+ if arraycoder
69
+ # populate array types
70
+ arrays.each do |row|
71
+ elements_coder = coder_map[row['typelem'].to_i]
72
+ next unless elements_coder
73
+
74
+ coder = arraycoder.new
75
+ coder.oid = row['oid'].to_i
76
+ coder.name = row['typname']
77
+ coder.format = format
78
+ coder.elements_type = elements_coder
79
+ coder.needs_quotation = !DONT_QUOTE_TYPES[elements_coder.name]
80
+ coder_map[coder.oid] = coder
81
+ end
82
+ end
83
+
84
+ # populate range types
85
+ # ranges.find_all { |row| coder_map.key? row['rngsubtype'].to_i }.each do |row|
86
+ # subcoder = coder_map[row['rngsubtype'].to_i]
87
+ # range = OID::Range.new subcoder
88
+ # coder_map[row['oid'].to_i] = range
89
+ # end
90
+
91
+ @coders = coder_map.values
92
+ @coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
93
+ @coders_by_oid = @coders.inject({}){|h, t| h[t.oid] = t; h }
94
+ @typenames_by_oid = result.inject({}){|h, t| h[t['oid'].to_i] = t['typname']; h }
95
+ end
96
+
97
+ attr_reader :coders
98
+ attr_reader :coders_by_oid
99
+ attr_reader :coders_by_name
100
+ attr_reader :typenames_by_oid
101
+
102
+ def coder_by_name(name)
103
+ @coders_by_name[name]
104
+ end
105
+
106
+ def coder_by_oid(oid)
107
+ @coders_by_oid[oid]
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ def supports_ranges?(connection)
114
+ connection.server_version >= 90200
115
+ end
116
+
117
+ def build_coder_maps(connection)
118
+ if supports_ranges?(connection)
119
+ result = connection.exec <<-SQL
120
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype
121
+ FROM pg_type as t
122
+ LEFT JOIN pg_range as r ON oid = rngtypid
123
+ SQL
124
+ else
125
+ result = connection.exec <<-SQL
126
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput
127
+ FROM pg_type as t
128
+ SQL
129
+ end
130
+
131
+ [
132
+ [0, :encoder, PG::TextEncoder::Array],
133
+ [0, :decoder, PG::TextDecoder::Array],
134
+ [1, :encoder, nil],
135
+ [1, :decoder, nil],
136
+ ].inject([]) do |h, (format, direction, arraycoder)|
137
+ h[format] ||= {}
138
+ h[format][direction] = CoderMap.new result, CODERS_BY_NAME[format][direction], format, arraycoder
139
+ h
140
+ end
141
+ end
142
+
143
+ ValidFormats = { 0 => true, 1 => true }
144
+ ValidDirections = { :encoder => true, :decoder => true }
145
+
146
+ def check_format_and_direction(format, direction)
147
+ raise(ArgumentError, "Invalid format value %p" % format) unless ValidFormats[format]
148
+ raise(ArgumentError, "Invalid direction %p" % direction) unless ValidDirections[direction]
149
+ end
150
+ protected :check_format_and_direction
151
+
152
+ # The key of this hash maps to the `typname` column from the table.
153
+ # encoder_map is then dynamically built with oids as the key and Type
154
+ # objects as values.
155
+ CODERS_BY_NAME = []
156
+
157
+ # Register an OID type named +name+ with a typecasting encoder and decoder object in
158
+ # +type+. +name+ should correspond to the `typname` column in
159
+ # the `pg_type` table.
160
+ # +format+ can be 0 for text format and 1 for binary.
161
+ def self.register_type(format, name, encoder_class, decoder_class)
162
+ CODERS_BY_NAME[format] ||= { encoder: {}, decoder: {} }
163
+ CODERS_BY_NAME[format][:encoder][name] = encoder_class.new(name: name, format: format) if encoder_class
164
+ CODERS_BY_NAME[format][:decoder][name] = decoder_class.new(name: name, format: format) if decoder_class
165
+ end
166
+
167
+ # Alias the +old+ type to the +new+ type.
168
+ def self.alias_type(format, new, old)
169
+ [:encoder, :decoder].each do |ende|
170
+ enc = CODERS_BY_NAME[format][ende][old]
171
+ if enc
172
+ CODERS_BY_NAME[format][ende][new] = enc
173
+ else
174
+ CODERS_BY_NAME[format][ende].delete(new)
175
+ end
176
+ end
177
+ end
178
+
179
+ register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
180
+ alias_type 0, 'int4', 'int2'
181
+ alias_type 0, 'int8', 'int2'
182
+ alias_type 0, 'oid', 'int2'
183
+
184
+ register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
185
+ register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
186
+ alias_type 0, 'varchar', 'text'
187
+ alias_type 0, 'char', 'text'
188
+ alias_type 0, 'bpchar', 'text'
189
+ alias_type 0, 'xml', 'text'
190
+
191
+ # FIXME: why are we keeping these types as strings?
192
+ # alias_type 'tsvector', 'text'
193
+ # alias_type 'interval', 'text'
194
+ # alias_type 'macaddr', 'text'
195
+ # alias_type 'uuid', 'text'
196
+ #
197
+ # register_type 'money', OID::Money.new
198
+ # There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
199
+ # in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
200
+ register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
201
+ register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
202
+ # register_type 'bit', OID::Bit.new
203
+ # register_type 'varbit', OID::Bit.new
204
+
205
+ register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
206
+ alias_type 0, 'float8', 'float4'
207
+
208
+ register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
209
+ register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
210
+ register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
211
+ # register_type 'time', OID::Time.new
212
+ #
213
+ # register_type 'path', OID::Text.new
214
+ # register_type 'point', OID::Point.new
215
+ # register_type 'polygon', OID::Text.new
216
+ # register_type 'circle', OID::Text.new
217
+ # register_type 'hstore', OID::Hstore.new
218
+ register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
219
+ alias_type 0, 'jsonb', 'json'
220
+ # register_type 'citext', OID::Text.new
221
+ # register_type 'ltree', OID::Text.new
222
+ #
223
+ register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
224
+ alias_type 0, 'cidr', 'inet'
225
+
226
+
227
+
228
+ register_type 1, 'int2', PG::BinaryEncoder::Int2, PG::BinaryDecoder::Integer
229
+ register_type 1, 'int4', PG::BinaryEncoder::Int4, PG::BinaryDecoder::Integer
230
+ register_type 1, 'int8', PG::BinaryEncoder::Int8, PG::BinaryDecoder::Integer
231
+ alias_type 1, 'oid', 'int2'
232
+
233
+ register_type 1, 'text', PG::BinaryEncoder::String, PG::BinaryDecoder::String
234
+ alias_type 1, 'varchar', 'text'
235
+ alias_type 1, 'char', 'text'
236
+ alias_type 1, 'bpchar', 'text'
237
+ alias_type 1, 'xml', 'text'
238
+
239
+ register_type 1, 'bytea', PG::BinaryEncoder::Bytea, PG::BinaryDecoder::Bytea
240
+ register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
241
+ register_type 1, 'float4', nil, PG::BinaryDecoder::Float
242
+ register_type 1, 'float8', nil, PG::BinaryDecoder::Float
243
+ register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtc
244
+ register_type 1, 'timestamptz', nil, PG::BinaryDecoder::TimestampUtcToLocal
245
+ end
246
+
247
+ # Simple set of rules for type casting common PostgreSQL types to Ruby.
248
+ #
249
+ # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
250
+ # PostgreSQL's pg_type table in PG::BasicTypeMapForResults.new .
251
+ #
252
+ # Result values are type casted based on the type OID of the given result column.
253
+ #
254
+ # Higher level libraries will most likely not make use of this class, but use their
255
+ # own set of rules to choose suitable encoders and decoders.
256
+ #
257
+ # Example:
258
+ # conn = PG::Connection.new
259
+ # # Assign a default ruleset for type casts of output values.
260
+ # conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
261
+ # # Execute a query.
262
+ # res = conn.exec_params( "SELECT $1::INT", ['5'] )
263
+ # # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
264
+ # # is done by PG::TextDecoder::Integer internally for all value retrieval methods.
265
+ # res.values # => [[5]]
266
+ #
267
+ # PG::TypeMapByOid#fit_to_result(result, false) can be used to generate
268
+ # a result independent PG::TypeMapByColumn type map, which can subsequently be used
269
+ # to cast #get_copy_data fields:
270
+ #
271
+ # For the following table:
272
+ # conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
273
+ #
274
+ # # Retrieve table OIDs per empty result set.
275
+ # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
276
+ # # Build a type map for common database to ruby type decoders.
277
+ # btm = PG::BasicTypeMapForResults.new(conn)
278
+ # # Build a PG::TypeMapByColumn with decoders suitable for copytable.
279
+ # tm = btm.build_column_map( res )
280
+ # row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
281
+ #
282
+ # conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
283
+ # while row=conn.get_copy_data
284
+ # p row
285
+ # end
286
+ # end
287
+ # This prints the rows with type casted columns:
288
+ # ["a", 123, [5, 4, 3]]
289
+ #
290
+ # See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
291
+ class PG::BasicTypeMapForResults < PG::TypeMapByOid
292
+ include PG::BasicTypeRegistry
293
+
294
+ class WarningTypeMap < PG::TypeMapInRuby
295
+ def initialize(typenames)
296
+ @already_warned = Hash.new{|h, k| h[k] = {} }
297
+ @typenames_by_oid = typenames
298
+ end
299
+
300
+ def typecast_result_value(result, _tuple, field)
301
+ format = result.fformat(field)
302
+ oid = result.ftype(field)
303
+ unless @already_warned[format][oid]
304
+ 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."
305
+ @already_warned[format][oid] = true
306
+ end
307
+ super
308
+ end
309
+ end
310
+
311
+ def initialize(connection)
312
+ @coder_maps = build_coder_maps(connection)
313
+
314
+ # Populate TypeMapByOid hash with decoders
315
+ @coder_maps.map{|f| f[:decoder].coders }.flatten.each do |coder|
316
+ add_coder(coder)
317
+ end
318
+
319
+ typenames = @coder_maps.map{|f| f[:decoder].typenames_by_oid }
320
+ self.default_type_map = WarningTypeMap.new(typenames)
321
+ end
322
+ end
323
+
324
+ # Simple set of rules for type casting common PostgreSQL types from Ruby
325
+ # to PostgreSQL.
326
+ #
327
+ # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
328
+ # PostgreSQL's pg_type table in PG::BasicTypeMapBasedOnResult.new .
329
+ #
330
+ # This class works equal to PG::BasicTypeMapForResults, but does not define decoders for
331
+ # the given result OIDs, but encoders. So it can be used to type cast field values based on
332
+ # the type OID retrieved by a separate SQL query.
333
+ #
334
+ # PG::TypeMapByOid#build_column_map(result) can be used to generate a result independent
335
+ # PG::TypeMapByColumn type map, which can subsequently be used to cast query bind parameters
336
+ # or #put_copy_data fields.
337
+ #
338
+ # Example:
339
+ # conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
340
+ #
341
+ # # Retrieve table OIDs per empty result set.
342
+ # res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
343
+ # # Build a type map for common ruby to database type encoders.
344
+ # btm = PG::BasicTypeMapBasedOnResult.new(conn)
345
+ # # Build a PG::TypeMapByColumn with encoders suitable for copytable.
346
+ # tm = btm.build_column_map( res )
347
+ # row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
348
+ #
349
+ # conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
350
+ # conn.put_copy_data ['a', 123, [5,4,3]]
351
+ # end
352
+ # This inserts a single row into copytable with type casts from ruby to
353
+ # database types.
354
+ class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
355
+ include PG::BasicTypeRegistry
356
+
357
+ def initialize(connection)
358
+ @coder_maps = build_coder_maps(connection)
359
+
360
+ # Populate TypeMapByOid hash with encoders
361
+ @coder_maps.map{|f| f[:encoder].coders }.flatten.each do |coder|
362
+ add_coder(coder)
363
+ end
364
+ end
365
+ end
366
+
367
+ # Simple set of rules for type casting common Ruby types to PostgreSQL.
368
+ #
369
+ # OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
370
+ # PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
371
+ #
372
+ # Query params are type casted based on the class of the given value.
373
+ #
374
+ # Higher level libraries will most likely not make use of this class, but use their
375
+ # own derivation of PG::TypeMapByClass or another set of rules to choose suitable
376
+ # encoders and decoders for the values to be sent.
377
+ #
378
+ # Example:
379
+ # conn = PG::Connection.new
380
+ # # Assign a default ruleset for type casts of input and output values.
381
+ # conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
382
+ # # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
383
+ # # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
384
+ # res = conn.exec_params( "SELECT $1", [5] )
385
+ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
386
+ include PG::BasicTypeRegistry
387
+
388
+ def initialize(connection)
389
+ @coder_maps = build_coder_maps(connection)
390
+
391
+ populate_encoder_list
392
+ @array_encoders_by_klass = array_encoders_by_klass
393
+ @anyarray_encoder = coder_by_name(0, :encoder, '_any')
394
+ end
395
+
396
+ private
397
+
398
+ def coder_by_name(format, direction, name)
399
+ check_format_and_direction(format, direction)
400
+ @coder_maps[format][direction].coder_by_name(name)
401
+ end
402
+
403
+ def populate_encoder_list
404
+ DEFAULT_TYPE_MAP.each do |klass, selector|
405
+ if Array === selector
406
+ format, name, oid_name = selector
407
+ coder = coder_by_name(format, :encoder, name).dup
408
+ if oid_name
409
+ coder.oid = coder_by_name(format, :encoder, oid_name).oid
410
+ else
411
+ coder.oid = 0
412
+ end
413
+ self[klass] = coder
414
+ else
415
+ self[klass] = selector
416
+ end
417
+ end
418
+ end
419
+
420
+ def array_encoders_by_klass
421
+ DEFAULT_ARRAY_TYPE_MAP.inject({}) do |h, (klass, (format, name))|
422
+ h[klass] = coder_by_name(format, :encoder, name)
423
+ h
424
+ end
425
+ end
426
+
427
+ def get_array_type(value)
428
+ elem = value
429
+ while elem.kind_of?(Array)
430
+ elem = elem.first
431
+ end
432
+ @array_encoders_by_klass[elem.class] ||
433
+ elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
434
+ @anyarray_encoder
435
+ end
436
+
437
+ DEFAULT_TYPE_MAP = {
438
+ TrueClass => [1, 'bool', 'bool'],
439
+ FalseClass => [1, 'bool', 'bool'],
440
+ # We use text format and no type OID for numbers, because setting the OID can lead
441
+ # to unnecessary type conversions on server side.
442
+ Integer => [0, 'int8'],
443
+ Float => [0, 'float8'],
444
+ BigDecimal => [0, 'numeric'],
445
+ # We use text format and no type OID for IPAddr, because setting the OID can lead
446
+ # to unnecessary inet/cidr conversions on the server side.
447
+ IPAddr => [0, 'inet'],
448
+ Array => :get_array_type,
449
+ }
450
+
451
+ DEFAULT_ARRAY_TYPE_MAP = {
452
+ TrueClass => [0, '_bool'],
453
+ FalseClass => [0, '_bool'],
454
+ Integer => [0, '_int8'],
455
+ String => [0, '_text'],
456
+ Float => [0, '_float8'],
457
+ }
458
+
459
+ end