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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/.gemtest +0 -0
- data/BSDL +22 -0
- data/ChangeLog +6595 -0
- data/Contributors.rdoc +46 -0
- data/History.rdoc +492 -0
- data/LICENSE +56 -0
- data/Manifest.txt +72 -0
- data/POSTGRES +23 -0
- data/README-OS_X.rdoc +68 -0
- data/README-Windows.rdoc +56 -0
- data/README.ja.rdoc +14 -0
- data/README.rdoc +178 -0
- data/Rakefile +215 -0
- data/Rakefile.cross +298 -0
- data/ext/errorcodes.def +968 -0
- data/ext/errorcodes.rb +45 -0
- data/ext/errorcodes.txt +478 -0
- data/ext/extconf.rb +94 -0
- data/ext/gvl_wrappers.c +17 -0
- data/ext/gvl_wrappers.h +241 -0
- data/ext/pg.c +640 -0
- data/ext/pg.h +365 -0
- data/ext/pg_binary_decoder.c +229 -0
- data/ext/pg_binary_encoder.c +162 -0
- data/ext/pg_coder.c +549 -0
- data/ext/pg_connection.c +4252 -0
- data/ext/pg_copy_coder.c +596 -0
- data/ext/pg_errors.c +95 -0
- data/ext/pg_result.c +1501 -0
- data/ext/pg_text_decoder.c +981 -0
- data/ext/pg_text_encoder.c +682 -0
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +166 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +312 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +355 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/ext/vc/pg.sln +26 -0
- data/ext/vc/pg_18/pg.vcproj +216 -0
- data/ext/vc/pg_19/pg_19.vcproj +209 -0
- data/lib/pg.rb +74 -0
- data/lib/pg/basic_type_mapping.rb +459 -0
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +83 -0
- data/lib/pg/connection.rb +291 -0
- data/lib/pg/constants.rb +11 -0
- data/lib/pg/exceptions.rb +11 -0
- data/lib/pg/result.rb +31 -0
- data/lib/pg/text_decoder.rb +47 -0
- data/lib/pg/text_encoder.rb +69 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/data/expected_trace.out +26 -0
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +380 -0
- data/spec/pg/basic_type_mapping_spec.rb +508 -0
- data/spec/pg/connection_spec.rb +1872 -0
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +491 -0
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +222 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +949 -0
- data/spec/pg_spec.rb +50 -0
- metadata +322 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
module PG
|
4
|
+
module BinaryDecoder
|
5
|
+
# Convenience classes for timezone options
|
6
|
+
class TimestampUtc < Timestamp
|
7
|
+
def initialize(params={})
|
8
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class TimestampUtcToLocal < Timestamp
|
12
|
+
def initialize(params={})
|
13
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class TimestampLocal < Timestamp
|
17
|
+
def initialize(params={})
|
18
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end # module PG
|
data/lib/pg/coder.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
module PG
|
4
|
+
|
5
|
+
class Coder
|
6
|
+
|
7
|
+
module BinaryFormatting
|
8
|
+
Params = { format: 1 }
|
9
|
+
def initialize( params={} )
|
10
|
+
super(params.merge(Params))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Create a new coder object based on the attribute Hash.
|
16
|
+
def initialize(params={})
|
17
|
+
params.each do |key, val|
|
18
|
+
send("#{key}=", val)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def dup
|
23
|
+
self.class.new(to_h)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns coder attributes as Hash.
|
27
|
+
def to_h
|
28
|
+
{
|
29
|
+
oid: oid,
|
30
|
+
format: format,
|
31
|
+
name: name,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(v)
|
36
|
+
self.class == v.class && to_h == v.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
def marshal_dump
|
40
|
+
Marshal.dump(to_h)
|
41
|
+
end
|
42
|
+
|
43
|
+
def marshal_load(str)
|
44
|
+
initialize Marshal.load(str)
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
str = self.to_s
|
49
|
+
oid_str = " oid=#{oid}" unless oid==0
|
50
|
+
format_str = " format=#{format}" unless format==0
|
51
|
+
name_str = " #{name.inspect}" if name
|
52
|
+
str[-1,0] = "#{name_str} #{oid_str}#{format_str}"
|
53
|
+
str
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class CompositeCoder < Coder
|
58
|
+
def to_h
|
59
|
+
super.merge!({
|
60
|
+
elements_type: elements_type,
|
61
|
+
needs_quotation: needs_quotation?,
|
62
|
+
delimiter: delimiter,
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
str = super
|
68
|
+
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
|
69
|
+
str
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class CopyCoder < Coder
|
74
|
+
def to_h
|
75
|
+
super.merge!({
|
76
|
+
type_map: type_map,
|
77
|
+
delimiter: delimiter,
|
78
|
+
null_string: null_string,
|
79
|
+
})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end # module PG
|
83
|
+
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'pg' unless defined?( PG )
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
# The PostgreSQL connection class. The interface for this class is based on
|
7
|
+
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
|
8
|
+
# application programmer's interface to PostgreSQL. Some familiarity with libpq
|
9
|
+
# is recommended, but not necessary.
|
10
|
+
#
|
11
|
+
# For example, to send query to the database on the localhost:
|
12
|
+
#
|
13
|
+
# require 'pg'
|
14
|
+
# conn = PG::Connection.open(:dbname => 'test')
|
15
|
+
# res = conn.exec_params('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
|
16
|
+
# # Equivalent to:
|
17
|
+
# # res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
|
18
|
+
#
|
19
|
+
# See the PG::Result class for information on working with the results of a query.
|
20
|
+
#
|
21
|
+
class PG::Connection
|
22
|
+
|
23
|
+
# The order the options are passed to the ::connect method.
|
24
|
+
CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
|
25
|
+
|
26
|
+
|
27
|
+
### Quote the given +value+ for use in a connection-parameter string.
|
28
|
+
def self::quote_connstr( value )
|
29
|
+
return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
### Parse the connection +args+ into a connection-parameter string. See PG::Connection.new
|
34
|
+
### for valid arguments.
|
35
|
+
def self::parse_connect_args( *args )
|
36
|
+
return '' if args.empty?
|
37
|
+
|
38
|
+
hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
|
39
|
+
option_string = ''
|
40
|
+
options = {}
|
41
|
+
|
42
|
+
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
|
43
|
+
# together with PQescapeLiteral().
|
44
|
+
if PG::Connection.instance_methods.find {|m| m.to_sym == :escape_literal }
|
45
|
+
options[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
|
46
|
+
end
|
47
|
+
|
48
|
+
if args.length == 1
|
49
|
+
case args.first
|
50
|
+
when URI, /\A#{URI.regexp}\z/
|
51
|
+
uri = URI(args.first)
|
52
|
+
options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
|
53
|
+
when /=/
|
54
|
+
# Option string style
|
55
|
+
option_string = args.first.to_s
|
56
|
+
else
|
57
|
+
# Positional parameters
|
58
|
+
options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
|
59
|
+
end
|
60
|
+
else
|
61
|
+
max = CONNECT_ARGUMENT_ORDER.length
|
62
|
+
raise ArgumentError,
|
63
|
+
"Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
|
64
|
+
|
65
|
+
CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
|
66
|
+
options[ k.to_sym ] = v if v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
options.merge!( hash_arg )
|
71
|
+
|
72
|
+
if uri
|
73
|
+
uri.host = nil if options[:host]
|
74
|
+
uri.port = nil if options[:port]
|
75
|
+
uri.user = nil if options[:user]
|
76
|
+
uri.password = nil if options[:password]
|
77
|
+
uri.path = '' if options[:dbname]
|
78
|
+
uri.query = URI.encode_www_form( options )
|
79
|
+
return uri.to_s.sub( /^#{uri.scheme}:(?!\/\/)/, "#{uri.scheme}://" )
|
80
|
+
else
|
81
|
+
option_string += ' ' unless option_string.empty? && options.empty?
|
82
|
+
return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# call-seq:
|
88
|
+
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
89
|
+
#
|
90
|
+
# Execute a copy process for transfering data to or from the server.
|
91
|
+
#
|
92
|
+
# This issues the SQL COPY command via #exec. The response to this
|
93
|
+
# (if there is no error in the command) is a PG::Result object that
|
94
|
+
# is passed to the block, bearing a status code of PGRES_COPY_OUT or
|
95
|
+
# PGRES_COPY_IN (depending on the specified copy direction).
|
96
|
+
# The application should then use #put_copy_data or #get_copy_data
|
97
|
+
# to receive or transmit data rows and should return from the block
|
98
|
+
# when finished.
|
99
|
+
#
|
100
|
+
# #copy_data returns another PG::Result object when the data transfer
|
101
|
+
# is complete. An exception is raised if some problem was encountered,
|
102
|
+
# so it isn't required to make use of any of them.
|
103
|
+
# At this point further SQL commands can be issued via #exec.
|
104
|
+
# (It is not possible to execute other SQL commands using the same
|
105
|
+
# connection while the COPY operation is in progress.)
|
106
|
+
#
|
107
|
+
# This method ensures, that the copy process is properly terminated
|
108
|
+
# in case of client side or server side failures. Therefore, in case
|
109
|
+
# of blocking mode of operation, #copy_data is preferred to raw calls
|
110
|
+
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
111
|
+
#
|
112
|
+
# _coder_ can be a PG::Coder derivation
|
113
|
+
# (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
|
114
|
+
# This enables encoding of data fields given to #put_copy_data
|
115
|
+
# or decoding of fields received by #get_copy_data.
|
116
|
+
#
|
117
|
+
# Example with CSV input format:
|
118
|
+
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
119
|
+
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
120
|
+
# conn.put_copy_data "some,data,to,copy\n"
|
121
|
+
# conn.put_copy_data "more,data,to,copy\n"
|
122
|
+
# end
|
123
|
+
# This creates +my_table+ and inserts two CSV rows.
|
124
|
+
#
|
125
|
+
# The same with text format encoder PG::TextEncoder::CopyRow
|
126
|
+
# and Array input:
|
127
|
+
# enco = PG::TextEncoder::CopyRow.new
|
128
|
+
# conn.copy_data "COPY my_table FROM STDIN", enco do
|
129
|
+
# conn.put_copy_data ['some', 'data', 'to', 'copy']
|
130
|
+
# conn.put_copy_data ['more', 'data', 'to', 'copy']
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# Example with CSV output format:
|
134
|
+
# conn.copy_data "COPY my_table TO STDOUT CSV" do
|
135
|
+
# while row=conn.get_copy_data
|
136
|
+
# p row
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
# This prints all rows of +my_table+ to stdout:
|
140
|
+
# "some,data,to,copy\n"
|
141
|
+
# "more,data,to,copy\n"
|
142
|
+
#
|
143
|
+
# The same with text format decoder PG::TextDecoder::CopyRow
|
144
|
+
# and Array output:
|
145
|
+
# deco = PG::TextDecoder::CopyRow.new
|
146
|
+
# conn.copy_data "COPY my_table TO STDOUT", deco do
|
147
|
+
# while row=conn.get_copy_data
|
148
|
+
# p row
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# This receives all rows of +my_table+ as ruby array:
|
152
|
+
# ["some", "data", "to", "copy"]
|
153
|
+
# ["more", "data", "to", "copy"]
|
154
|
+
|
155
|
+
def copy_data( sql, coder=nil )
|
156
|
+
res = exec( sql )
|
157
|
+
|
158
|
+
case res.result_status
|
159
|
+
when PGRES_COPY_IN
|
160
|
+
begin
|
161
|
+
if coder
|
162
|
+
old_coder = self.encoder_for_put_copy_data
|
163
|
+
self.encoder_for_put_copy_data = coder
|
164
|
+
end
|
165
|
+
yield res
|
166
|
+
rescue Exception => err
|
167
|
+
errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
|
168
|
+
put_copy_end( errmsg )
|
169
|
+
get_result
|
170
|
+
raise
|
171
|
+
else
|
172
|
+
put_copy_end
|
173
|
+
get_last_result
|
174
|
+
ensure
|
175
|
+
self.encoder_for_put_copy_data = old_coder if coder
|
176
|
+
end
|
177
|
+
|
178
|
+
when PGRES_COPY_OUT
|
179
|
+
begin
|
180
|
+
if coder
|
181
|
+
old_coder = self.decoder_for_get_copy_data
|
182
|
+
self.decoder_for_get_copy_data = coder
|
183
|
+
end
|
184
|
+
yield res
|
185
|
+
rescue Exception => err
|
186
|
+
cancel
|
187
|
+
while get_copy_data
|
188
|
+
end
|
189
|
+
while get_result
|
190
|
+
end
|
191
|
+
raise
|
192
|
+
else
|
193
|
+
res = get_last_result
|
194
|
+
if !res || res.result_status != PGRES_COMMAND_OK
|
195
|
+
while get_copy_data
|
196
|
+
end
|
197
|
+
while get_result
|
198
|
+
end
|
199
|
+
raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
|
200
|
+
end
|
201
|
+
res
|
202
|
+
ensure
|
203
|
+
self.decoder_for_get_copy_data = old_coder if coder
|
204
|
+
end
|
205
|
+
|
206
|
+
else
|
207
|
+
raise ArgumentError, "SQL command is no COPY statement: #{sql}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Backward-compatibility aliases for stuff that's moved into PG.
|
212
|
+
class << self
|
213
|
+
define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
### Returns an array of Hashes with connection defaults. See ::conndefaults
|
218
|
+
### for details.
|
219
|
+
def conndefaults
|
220
|
+
return self.class.conndefaults
|
221
|
+
end
|
222
|
+
|
223
|
+
### Return the Postgres connection defaults structure as a Hash keyed by option
|
224
|
+
### keyword (as a Symbol).
|
225
|
+
###
|
226
|
+
### See also #conndefaults
|
227
|
+
def self.conndefaults_hash
|
228
|
+
return self.conndefaults.each_with_object({}) do |info, hash|
|
229
|
+
hash[ info[:keyword].to_sym ] = info[:val]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
### Returns a Hash with connection defaults. See ::conndefaults_hash
|
234
|
+
### for details.
|
235
|
+
def conndefaults_hash
|
236
|
+
return self.class.conndefaults_hash
|
237
|
+
end
|
238
|
+
|
239
|
+
# Method 'conninfo' was introduced in PostgreSQL 9.3.
|
240
|
+
if self.instance_methods.find{|m| m.to_sym == :conninfo }
|
241
|
+
|
242
|
+
### Return the Postgres connection info structure as a Hash keyed by option
|
243
|
+
### keyword (as a Symbol).
|
244
|
+
###
|
245
|
+
### See also #conninfo
|
246
|
+
def conninfo_hash
|
247
|
+
return self.conninfo.each_with_object({}) do |info, hash|
|
248
|
+
hash[ info[:keyword].to_sym ] = info[:val]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
|
254
|
+
if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
|
255
|
+
# call-seq:
|
256
|
+
# conn.ssl_attributes -> Hash<String,String>
|
257
|
+
#
|
258
|
+
# Returns SSL-related information about the connection as key/value pairs
|
259
|
+
#
|
260
|
+
# The available attributes varies depending on the SSL library being used,
|
261
|
+
# and the type of connection.
|
262
|
+
#
|
263
|
+
# See also #ssl_attribute
|
264
|
+
def ssl_attributes
|
265
|
+
ssl_attribute_names.each.with_object({}) do |n,h|
|
266
|
+
h[n] = ssl_attribute(n)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
REDIRECT_METHODS = {
|
272
|
+
:exec => [:async_exec, :sync_exec],
|
273
|
+
:query => [:async_exec, :sync_exec],
|
274
|
+
:exec_params => [:async_exec_params, :sync_exec_params],
|
275
|
+
:prepare => [:async_prepare, :sync_prepare],
|
276
|
+
:exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
|
277
|
+
:describe_portal => [:async_describe_portal, :sync_describe_portal],
|
278
|
+
:describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
|
279
|
+
}
|
280
|
+
|
281
|
+
def self.async_api=(enable)
|
282
|
+
REDIRECT_METHODS.each do |ali, (async, sync)|
|
283
|
+
remove_method(ali) if method_defined?(ali)
|
284
|
+
alias_method( ali, enable ? async : sync )
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# pg-1.1.0+ defaults to libpq's async API for query related blocking methods
|
289
|
+
self.async_api = true
|
290
|
+
end # class PG::Connection
|
291
|
+
|
data/lib/pg/constants.rb
ADDED
data/lib/pg/result.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'pg' unless defined?( PG )
|
4
|
+
|
5
|
+
|
6
|
+
class PG::Result
|
7
|
+
|
8
|
+
# Apply a type map for all value retrieving methods.
|
9
|
+
#
|
10
|
+
# +type_map+: a PG::TypeMap instance.
|
11
|
+
#
|
12
|
+
# See PG::BasicTypeMapForResults
|
13
|
+
def map_types!(type_map)
|
14
|
+
self.type_map = type_map
|
15
|
+
return self
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
### Return a String representation of the object suitable for debugging.
|
20
|
+
def inspect
|
21
|
+
str = self.to_s
|
22
|
+
str[-1,0] = if cleared?
|
23
|
+
" cleared"
|
24
|
+
else
|
25
|
+
" status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
|
26
|
+
end
|
27
|
+
return str
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class PG::Result
|
31
|
+
|