pg 0.17.1 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +0 -3506
- data/History.rdoc +308 -0
- data/Manifest.txt +35 -19
- data/README-Windows.rdoc +17 -28
- data/README.ja.rdoc +1 -2
- data/README.rdoc +113 -14
- data/Rakefile +67 -30
- data/Rakefile.cross +109 -83
- data/ext/errorcodes.def +101 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +33 -2
- data/ext/extconf.rb +55 -58
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +262 -130
- data/ext/pg.h +266 -54
- data/ext/pg_binary_decoder.c +229 -0
- data/ext/pg_binary_encoder.c +163 -0
- data/ext/pg_coder.c +561 -0
- data/ext/pg_connection.c +1689 -990
- data/ext/pg_copy_coder.c +599 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +897 -164
- data/ext/pg_text_decoder.c +987 -0
- data/ext/pg_text_encoder.c +814 -0
- data/ext/pg_tuple.c +549 -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 +244 -0
- data/ext/pg_type_map_by_column.c +313 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +356 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/pg_util.c +149 -0
- data/ext/pg_util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +522 -0
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +104 -0
- data/lib/pg/connection.rb +153 -41
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +33 -6
- data/lib/pg/text_decoder.rb +46 -0
- data/lib/pg/text_encoder.rb +59 -0
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +16 -0
- data/lib/pg.rb +29 -9
- data/spec/{lib/helpers.rb → helpers.rb} +151 -64
- data/spec/pg/basic_type_mapping_spec.rb +630 -0
- data/spec/pg/connection_spec.rb +1180 -477
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +456 -120
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +226 -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 +1123 -0
- data/spec/pg_spec.rb +26 -20
- data.tar.gz.sig +0 -0
- metadata +148 -91
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module PG
|
5
|
+
module BinaryDecoder
|
6
|
+
# Convenience classes for timezone options
|
7
|
+
class TimestampUtc < Timestamp
|
8
|
+
def initialize(params={})
|
9
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
class TimestampUtcToLocal < Timestamp
|
13
|
+
def initialize(params={})
|
14
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class TimestampLocal < Timestamp
|
18
|
+
def initialize(params={})
|
19
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end # module PG
|
data/lib/pg/coder.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module PG
|
5
|
+
|
6
|
+
class Coder
|
7
|
+
|
8
|
+
module BinaryFormatting
|
9
|
+
Params = { format: 1 }
|
10
|
+
def initialize( params={} )
|
11
|
+
super(params.merge(Params))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Create a new coder object based on the attribute Hash.
|
17
|
+
def initialize(params={})
|
18
|
+
params.each do |key, val|
|
19
|
+
send("#{key}=", val)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def dup
|
24
|
+
self.class.new(to_h)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns coder attributes as Hash.
|
28
|
+
def to_h
|
29
|
+
{
|
30
|
+
oid: oid,
|
31
|
+
format: format,
|
32
|
+
flags: flags,
|
33
|
+
name: name,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(v)
|
38
|
+
self.class == v.class && to_h == v.to_h
|
39
|
+
end
|
40
|
+
|
41
|
+
def marshal_dump
|
42
|
+
Marshal.dump(to_h)
|
43
|
+
end
|
44
|
+
|
45
|
+
def marshal_load(str)
|
46
|
+
initialize Marshal.load(str)
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
str = self.to_s
|
51
|
+
oid_str = " oid=#{oid}" unless oid==0
|
52
|
+
format_str = " format=#{format}" unless format==0
|
53
|
+
name_str = " #{name.inspect}" if name
|
54
|
+
str[-1,0] = "#{name_str} #{oid_str}#{format_str}"
|
55
|
+
str
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect_short
|
59
|
+
str = case format
|
60
|
+
when 0 then "T"
|
61
|
+
when 1 then "B"
|
62
|
+
else format.to_s
|
63
|
+
end
|
64
|
+
str += "E" if respond_to?(:encode)
|
65
|
+
str += "D" if respond_to?(:decode)
|
66
|
+
|
67
|
+
"#{name || self.class.name}:#{str}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class CompositeCoder < Coder
|
72
|
+
def to_h
|
73
|
+
super.merge!({
|
74
|
+
elements_type: elements_type,
|
75
|
+
needs_quotation: needs_quotation?,
|
76
|
+
delimiter: delimiter,
|
77
|
+
})
|
78
|
+
end
|
79
|
+
|
80
|
+
def inspect
|
81
|
+
str = super
|
82
|
+
str[-1,0] = " elements_type=#{elements_type.inspect} #{needs_quotation? ? 'needs' : 'no'} quotation"
|
83
|
+
str
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class CopyCoder < Coder
|
88
|
+
def to_h
|
89
|
+
super.merge!({
|
90
|
+
type_map: type_map,
|
91
|
+
delimiter: delimiter,
|
92
|
+
null_string: null_string,
|
93
|
+
})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class RecordCoder < Coder
|
98
|
+
def to_h
|
99
|
+
super.merge!({
|
100
|
+
type_map: type_map,
|
101
|
+
})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # module PG
|
data/lib/pg/connection.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
5
|
+
require 'uri'
|
4
6
|
|
5
7
|
# The PostgreSQL connection class. The interface for this class is based on
|
6
8
|
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
|
@@ -34,48 +36,57 @@ class PG::Connection
|
|
34
36
|
def self::parse_connect_args( *args )
|
35
37
|
return '' if args.empty?
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
|
40
|
+
option_string = ''
|
41
|
+
options = {}
|
40
42
|
|
41
43
|
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
|
42
44
|
# together with PQescapeLiteral().
|
43
|
-
if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
|
44
|
-
|
45
|
-
appname = PG::Connection.quote_connstr( appname )
|
46
|
-
connopts = ["fallback_application_name=#{appname}"]
|
47
|
-
else
|
48
|
-
connopts = []
|
45
|
+
if PG::Connection.instance_methods.find {|m| m.to_sym == :escape_literal }
|
46
|
+
options[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
|
49
47
|
end
|
50
48
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
49
|
+
if args.length == 1
|
50
|
+
case args.first
|
51
|
+
when URI, /\A#{URI::ABS_URI_REF}\z/
|
52
|
+
uri = URI(args.first)
|
53
|
+
options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
|
54
|
+
when /=/
|
55
|
+
# Option string style
|
56
|
+
option_string = args.first.to_s
|
57
|
+
else
|
58
|
+
# Positional parameters
|
59
|
+
options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
|
56
60
|
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Option string style
|
60
|
-
if args.length == 1 && args.first.to_s.index( '=' )
|
61
|
-
connopts.unshift( args.first )
|
62
|
-
|
63
|
-
# Append positional parameters
|
64
61
|
else
|
65
|
-
|
66
|
-
|
62
|
+
max = CONNECT_ARGUMENT_ORDER.length
|
63
|
+
raise ArgumentError,
|
64
|
+
"Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
|
66
|
+
CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
|
67
|
+
options[ k.to_sym ] = v if v
|
71
68
|
end
|
72
69
|
end
|
73
70
|
|
74
|
-
|
71
|
+
options.merge!( hash_arg )
|
72
|
+
|
73
|
+
if uri
|
74
|
+
uri.host = nil if options[:host]
|
75
|
+
uri.port = nil if options[:port]
|
76
|
+
uri.user = nil if options[:user]
|
77
|
+
uri.password = nil if options[:password]
|
78
|
+
uri.path = '' if options[:dbname]
|
79
|
+
uri.query = URI.encode_www_form( options )
|
80
|
+
return uri.to_s.sub( /^#{uri.scheme}:(?!\/\/)/, "#{uri.scheme}://" )
|
81
|
+
else
|
82
|
+
option_string += ' ' unless option_string.empty? && options.empty?
|
83
|
+
return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
|
84
|
+
end
|
75
85
|
end
|
76
86
|
|
87
|
+
|
77
88
|
# call-seq:
|
78
|
-
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
|
89
|
+
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
79
90
|
#
|
80
91
|
# Execute a copy process for transfering data to or from the server.
|
81
92
|
#
|
@@ -99,13 +110,26 @@ class PG::Connection
|
|
99
110
|
# of blocking mode of operation, #copy_data is preferred to raw calls
|
100
111
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
101
112
|
#
|
113
|
+
# _coder_ can be a PG::Coder derivation
|
114
|
+
# (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
|
115
|
+
# This enables encoding of data fields given to #put_copy_data
|
116
|
+
# or decoding of fields received by #get_copy_data.
|
117
|
+
#
|
102
118
|
# Example with CSV input format:
|
103
|
-
# conn.exec "create table my_table (a text,b text,c text,d text
|
104
|
-
# conn.copy_data "COPY my_table FROM
|
105
|
-
# conn.put_copy_data "some,
|
106
|
-
# conn.put_copy_data "more,
|
119
|
+
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
120
|
+
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
121
|
+
# conn.put_copy_data "some,data,to,copy\n"
|
122
|
+
# conn.put_copy_data "more,data,to,copy\n"
|
123
|
+
# end
|
124
|
+
# This creates +my_table+ and inserts two CSV rows.
|
125
|
+
#
|
126
|
+
# The same with text format encoder PG::TextEncoder::CopyRow
|
127
|
+
# and Array input:
|
128
|
+
# enco = PG::TextEncoder::CopyRow.new
|
129
|
+
# conn.copy_data "COPY my_table FROM STDIN", enco do
|
130
|
+
# conn.put_copy_data ['some', 'data', 'to', 'copy']
|
131
|
+
# conn.put_copy_data ['more', 'data', 'to', 'copy']
|
107
132
|
# end
|
108
|
-
# This creates +my_table+ and inserts two rows.
|
109
133
|
#
|
110
134
|
# Example with CSV output format:
|
111
135
|
# conn.copy_data "COPY my_table TO STDOUT CSV" do
|
@@ -114,14 +138,31 @@ class PG::Connection
|
|
114
138
|
# end
|
115
139
|
# end
|
116
140
|
# This prints all rows of +my_table+ to stdout:
|
117
|
-
# "some,
|
118
|
-
# "more,
|
119
|
-
|
141
|
+
# "some,data,to,copy\n"
|
142
|
+
# "more,data,to,copy\n"
|
143
|
+
#
|
144
|
+
# The same with text format decoder PG::TextDecoder::CopyRow
|
145
|
+
# and Array output:
|
146
|
+
# deco = PG::TextDecoder::CopyRow.new
|
147
|
+
# conn.copy_data "COPY my_table TO STDOUT", deco do
|
148
|
+
# while row=conn.get_copy_data
|
149
|
+
# p row
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
# This receives all rows of +my_table+ as ruby array:
|
153
|
+
# ["some", "data", "to", "copy"]
|
154
|
+
# ["more", "data", "to", "copy"]
|
155
|
+
|
156
|
+
def copy_data( sql, coder=nil )
|
120
157
|
res = exec( sql )
|
121
158
|
|
122
159
|
case res.result_status
|
123
160
|
when PGRES_COPY_IN
|
124
161
|
begin
|
162
|
+
if coder
|
163
|
+
old_coder = self.encoder_for_put_copy_data
|
164
|
+
self.encoder_for_put_copy_data = coder
|
165
|
+
end
|
125
166
|
yield res
|
126
167
|
rescue Exception => err
|
127
168
|
errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
|
@@ -131,10 +172,16 @@ class PG::Connection
|
|
131
172
|
else
|
132
173
|
put_copy_end
|
133
174
|
get_last_result
|
175
|
+
ensure
|
176
|
+
self.encoder_for_put_copy_data = old_coder if coder
|
134
177
|
end
|
135
178
|
|
136
179
|
when PGRES_COPY_OUT
|
137
180
|
begin
|
181
|
+
if coder
|
182
|
+
old_coder = self.decoder_for_get_copy_data
|
183
|
+
self.decoder_for_get_copy_data = coder
|
184
|
+
end
|
138
185
|
yield res
|
139
186
|
rescue Exception => err
|
140
187
|
cancel
|
@@ -145,7 +192,7 @@ class PG::Connection
|
|
145
192
|
raise
|
146
193
|
else
|
147
194
|
res = get_last_result
|
148
|
-
if res.result_status != PGRES_COMMAND_OK
|
195
|
+
if !res || res.result_status != PGRES_COMMAND_OK
|
149
196
|
while get_copy_data
|
150
197
|
end
|
151
198
|
while get_result
|
@@ -153,6 +200,8 @@ class PG::Connection
|
|
153
200
|
raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
|
154
201
|
end
|
155
202
|
res
|
203
|
+
ensure
|
204
|
+
self.decoder_for_get_copy_data = old_coder if coder
|
156
205
|
end
|
157
206
|
|
158
207
|
else
|
@@ -172,8 +221,71 @@ class PG::Connection
|
|
172
221
|
return self.class.conndefaults
|
173
222
|
end
|
174
223
|
|
175
|
-
|
224
|
+
### Return the Postgres connection defaults structure as a Hash keyed by option
|
225
|
+
### keyword (as a Symbol).
|
226
|
+
###
|
227
|
+
### See also #conndefaults
|
228
|
+
def self.conndefaults_hash
|
229
|
+
return self.conndefaults.each_with_object({}) do |info, hash|
|
230
|
+
hash[ info[:keyword].to_sym ] = info[:val]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
### Returns a Hash with connection defaults. See ::conndefaults_hash
|
235
|
+
### for details.
|
236
|
+
def conndefaults_hash
|
237
|
+
return self.class.conndefaults_hash
|
238
|
+
end
|
239
|
+
|
240
|
+
# Method 'conninfo' was introduced in PostgreSQL 9.3.
|
241
|
+
if self.instance_methods.find{|m| m.to_sym == :conninfo }
|
176
242
|
|
177
|
-
|
178
|
-
|
243
|
+
### Return the Postgres connection info structure as a Hash keyed by option
|
244
|
+
### keyword (as a Symbol).
|
245
|
+
###
|
246
|
+
### See also #conninfo
|
247
|
+
def conninfo_hash
|
248
|
+
return self.conninfo.each_with_object({}) do |info, hash|
|
249
|
+
hash[ info[:keyword].to_sym ] = info[:val]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
179
253
|
|
254
|
+
# Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
|
255
|
+
if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
|
256
|
+
# call-seq:
|
257
|
+
# conn.ssl_attributes -> Hash<String,String>
|
258
|
+
#
|
259
|
+
# Returns SSL-related information about the connection as key/value pairs
|
260
|
+
#
|
261
|
+
# The available attributes varies depending on the SSL library being used,
|
262
|
+
# and the type of connection.
|
263
|
+
#
|
264
|
+
# See also #ssl_attribute
|
265
|
+
def ssl_attributes
|
266
|
+
ssl_attribute_names.each.with_object({}) do |n,h|
|
267
|
+
h[n] = ssl_attribute(n)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
REDIRECT_METHODS = {
|
273
|
+
:exec => [:async_exec, :sync_exec],
|
274
|
+
:query => [:async_exec, :sync_exec],
|
275
|
+
:exec_params => [:async_exec_params, :sync_exec_params],
|
276
|
+
:prepare => [:async_prepare, :sync_prepare],
|
277
|
+
:exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
|
278
|
+
:describe_portal => [:async_describe_portal, :sync_describe_portal],
|
279
|
+
:describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
|
280
|
+
}
|
281
|
+
|
282
|
+
def self.async_api=(enable)
|
283
|
+
REDIRECT_METHODS.each do |ali, (async, sync)|
|
284
|
+
remove_method(ali) if method_defined?(ali)
|
285
|
+
alias_method( ali, enable ? async : sync )
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# pg-1.1.0+ defaults to libpq's async API for query related blocking methods
|
290
|
+
self.async_api = true
|
291
|
+
end # class PG::Connection
|
data/lib/pg/constants.rb
CHANGED
data/lib/pg/exceptions.rb
CHANGED
data/lib/pg/result.rb
CHANGED
@@ -1,16 +1,43 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
4
5
|
|
5
6
|
|
6
7
|
class PG::Result
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
# Apply a type map for all value retrieving methods.
|
10
|
+
#
|
11
|
+
# +type_map+: a PG::TypeMap instance.
|
12
|
+
#
|
13
|
+
# This method is equal to #type_map= , but returns self, so that calls can be chained.
|
14
|
+
#
|
15
|
+
# See also PG::BasicTypeMapForResults
|
16
|
+
def map_types!(type_map)
|
17
|
+
self.type_map = type_map
|
18
|
+
return self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set the data type for all field name returning methods.
|
22
|
+
#
|
23
|
+
# +type+: a Symbol defining the field name type.
|
24
|
+
#
|
25
|
+
# This method is equal to #field_name_type= , but returns self, so that calls can be chained.
|
26
|
+
def field_names_as(type)
|
27
|
+
self.field_name_type = type
|
28
|
+
return self
|
29
|
+
end
|
30
|
+
|
31
|
+
### Return a String representation of the object suitable for debugging.
|
32
|
+
def inspect
|
33
|
+
str = self.to_s
|
34
|
+
str[-1,0] = if cleared?
|
35
|
+
" cleared"
|
36
|
+
else
|
37
|
+
" status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
|
38
|
+
end
|
39
|
+
return str
|
11
40
|
end
|
12
41
|
|
13
42
|
end # class PG::Result
|
14
43
|
|
15
|
-
# Backward-compatible alias
|
16
|
-
PGresult = PG::Result
|