pg 0.18.1 → 0.19.0
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 +3 -3
- data.tar.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +1004 -4
- data/History.rdoc +66 -0
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +16 -9
- data/Rakefile +26 -16
- data/Rakefile.cross +42 -21
- data/ext/errorcodes.def +16 -0
- data/ext/errorcodes.txt +5 -1
- data/ext/extconf.rb +14 -2
- data/ext/gvl_wrappers.h +4 -0
- data/ext/pg.c +5 -4
- data/ext/pg.h +15 -2
- data/ext/pg_binary_decoder.c +3 -1
- data/ext/pg_binary_encoder.c +14 -12
- data/ext/pg_coder.c +30 -9
- data/ext/pg_connection.c +241 -115
- data/ext/pg_copy_coder.c +34 -4
- data/ext/pg_result.c +5 -5
- data/ext/pg_text_decoder.c +9 -10
- data/ext/pg_text_encoder.c +93 -73
- data/ext/pg_type_map.c +7 -7
- data/ext/pg_type_map_by_column.c +7 -7
- data/ext/pg_type_map_by_mri_type.c +2 -2
- data/ext/pg_type_map_in_ruby.c +4 -7
- data/ext/util.c +3 -3
- data/ext/util.h +1 -1
- data/lib/pg.rb +3 -3
- data/lib/pg/basic_type_mapping.rb +69 -42
- data/lib/pg/connection.rb +84 -34
- data/lib/pg/result.rb +6 -2
- data/lib/pg/text_decoder.rb +12 -3
- data/lib/pg/text_encoder.rb +8 -0
- data/spec/helpers.rb +7 -10
- data/spec/pg/basic_type_mapping_spec.rb +58 -4
- data/spec/pg/connection_spec.rb +251 -34
- data/spec/pg/type_map_by_class_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_spec.rb +144 -32
- metadata +65 -52
- metadata.gz.sig +0 -0
data/ext/pg_type_map.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id: pg_type_map.c,v
|
3
|
+
* $Id: pg_type_map.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -54,12 +54,12 @@ pg_typemap_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno
|
|
54
54
|
}
|
55
55
|
|
56
56
|
const struct pg_typemap_funcs pg_typemap_funcs = {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
pg_typemap_fit_to_result,
|
58
|
+
pg_typemap_fit_to_query,
|
59
|
+
pg_typemap_fit_to_copy_get,
|
60
|
+
pg_typemap_result_value,
|
61
|
+
pg_typemap_typecast_query_param,
|
62
|
+
pg_typemap_typecast_copy_get
|
63
63
|
};
|
64
64
|
|
65
65
|
static VALUE
|
data/ext/pg_type_map_by_column.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_column_map.c - PG::ColumnMap class extension
|
3
|
-
* $Id: pg_type_map_by_column.c,v
|
3
|
+
* $Id: pg_type_map_by_column.c,v fcf731d3dff7 2015/09/08 12:25:06 jfali $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -162,12 +162,12 @@ pg_tmbc_typecast_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, i
|
|
162
162
|
}
|
163
163
|
|
164
164
|
const struct pg_typemap_funcs pg_tmbc_funcs = {
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
165
|
+
pg_tmbc_fit_to_result,
|
166
|
+
pg_tmbc_fit_to_query,
|
167
|
+
pg_tmbc_fit_to_copy_get,
|
168
|
+
pg_tmbc_result_value,
|
169
|
+
pg_tmbc_typecast_query_param,
|
170
|
+
pg_tmbc_typecast_copy_get
|
171
171
|
};
|
172
172
|
|
173
173
|
static void
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_by_mri_type.c - PG::TypeMapByMriType class extension
|
3
|
-
* $Id: pg_type_map_by_mri_type.c,v
|
3
|
+
* $Id: pg_type_map_by_mri_type.c,v 1269b8ad77b8 2015/02/06 16:38:23 lars $
|
4
4
|
*
|
5
5
|
* This type map can be used to select value encoders based on the MRI-internal
|
6
6
|
* value type code.
|
@@ -39,7 +39,7 @@ static VALUE rb_cTypeMapByMriType;
|
|
39
39
|
typedef struct {
|
40
40
|
t_typemap typemap;
|
41
41
|
struct pg_tmbmt_converter {
|
42
|
-
FOR_EACH_MRI_TYPE( DECLARE_CODER )
|
42
|
+
FOR_EACH_MRI_TYPE( DECLARE_CODER )
|
43
43
|
} coders;
|
44
44
|
} t_tmbmt;
|
45
45
|
|
data/ext/pg_type_map_in_ruby.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* pg_type_map_in_ruby.c - PG::TypeMapInRuby class extension
|
3
|
-
* $Id: pg_type_map_in_ruby.c,v
|
3
|
+
* $Id: pg_type_map_in_ruby.c,v 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -212,12 +212,9 @@ pg_tmir_copy_get( t_typemap *p_typemap, VALUE field_str, int fieldno, int format
|
|
212
212
|
rb_encoding *p_encoding = rb_enc_from_index(enc_idx);
|
213
213
|
VALUE enc = rb_enc_from_encoding(p_encoding);
|
214
214
|
/* field_str is reused in-place by pg_text_dec_copy_row(), so we need to make
|
215
|
-
* a copy of the string buffer
|
216
|
-
|
217
|
-
|
218
|
-
VALUE field_str_copy = rb_str_new(RSTRING_PTR(field_str), RSTRING_LEN(field_str));
|
219
|
-
PG_ENCODING_SET_NOCHECK(field_str_copy, ENCODING_GET(field_str));
|
220
|
-
OBJ_INFECT(field_str_copy, field_str);
|
215
|
+
* a copy of the string buffer for use in ruby space. */
|
216
|
+
VALUE field_str_copy = rb_str_dup(field_str);
|
217
|
+
rb_str_modify(field_str_copy);
|
221
218
|
|
222
219
|
return rb_funcall( this->self, s_id_typecast_copy_get, 4, field_str_copy, INT2NUM(fieldno), INT2NUM(format), enc );
|
223
220
|
}
|
data/ext/util.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* util.c - Utils for ruby-pg
|
3
|
-
* $Id: util.c,v
|
3
|
+
* $Id: util.c,v 5fb9170f6a7d 2015/06/29 11:15:12 kanis $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -17,7 +17,7 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
|
|
17
17
|
void
|
18
18
|
base64_encode( char *out, char *in, int len)
|
19
19
|
{
|
20
|
-
char *in_ptr = in + len;
|
20
|
+
unsigned char *in_ptr = (unsigned char *)in + len;
|
21
21
|
char *out_ptr = out + BASE64_ENCODED_SIZE(len);
|
22
22
|
int part_len = len % 3;
|
23
23
|
|
@@ -124,7 +124,7 @@ base64_decode( char *out, char *in, unsigned int len)
|
|
124
124
|
* At most n bytes will be examined from each string.
|
125
125
|
*/
|
126
126
|
int
|
127
|
-
|
127
|
+
rbpg_strncasecmp(const char *s1, const char *s2, size_t n)
|
128
128
|
{
|
129
129
|
while (n-- > 0)
|
130
130
|
{
|
data/ext/util.h
CHANGED
@@ -60,6 +60,6 @@
|
|
60
60
|
void base64_encode( char *out, char *in, int len);
|
61
61
|
int base64_decode( char *out, char *in, unsigned int len);
|
62
62
|
|
63
|
-
int
|
63
|
+
int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
|
64
64
|
|
65
65
|
#endif /* end __utils_h */
|
data/lib/pg.rb
CHANGED
@@ -10,7 +10,7 @@ rescue LoadError
|
|
10
10
|
|
11
11
|
# Set the PATH environment variable, so that libpq.dll can be found.
|
12
12
|
old_path = ENV['PATH']
|
13
|
-
ENV['PATH'] = "#{File.expand_path("
|
13
|
+
ENV['PATH'] = "#{File.expand_path("..", __FILE__)};#{old_path}"
|
14
14
|
require "#{major_minor}/pg_ext"
|
15
15
|
ENV['PATH'] = old_path
|
16
16
|
else
|
@@ -24,10 +24,10 @@ end
|
|
24
24
|
module PG
|
25
25
|
|
26
26
|
# Library version
|
27
|
-
VERSION = '0.
|
27
|
+
VERSION = '0.19.0'
|
28
28
|
|
29
29
|
# VCS revision
|
30
|
-
REVISION = %q$Revision:
|
30
|
+
REVISION = %q$Revision: 8beaa5d72670 $
|
31
31
|
|
32
32
|
class NotAllCopyDataRetrieved < PG::Error
|
33
33
|
end
|
@@ -25,9 +25,9 @@ module PG::BasicTypeRegistry
|
|
25
25
|
|
26
26
|
# populate the enum types
|
27
27
|
_enums, leaves = leaves.partition { |row| row['typinput'] == 'enum_in' }
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
# enums.each do |row|
|
29
|
+
# coder_map[row['oid'].to_i] = OID::Enum.new
|
30
|
+
# end
|
31
31
|
|
32
32
|
# populate the base types
|
33
33
|
leaves.find_all { |row| coders_by_name.key?(row['typname']) }.each do |row|
|
@@ -41,9 +41,9 @@ module PG::BasicTypeRegistry
|
|
41
41
|
_records_by_oid = result.group_by { |row| row['oid'] }
|
42
42
|
|
43
43
|
# populate composite types
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
# nodes.each do |row|
|
45
|
+
# add_oid row, records_by_oid, coder_map
|
46
|
+
# end
|
47
47
|
|
48
48
|
if arraycoder
|
49
49
|
# populate array types
|
@@ -62,11 +62,11 @@ module PG::BasicTypeRegistry
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# populate range types
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
# ranges.find_all { |row| coder_map.key? row['rngsubtype'].to_i }.each do |row|
|
66
|
+
# subcoder = coder_map[row['rngsubtype'].to_i]
|
67
|
+
# range = OID::Range.new subcoder
|
68
|
+
# coder_map[row['oid'].to_i] = range
|
69
|
+
# end
|
70
70
|
|
71
71
|
@coders = coder_map.values
|
72
72
|
@coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
|
@@ -154,46 +154,47 @@ module PG::BasicTypeRegistry
|
|
154
154
|
alias_type 0, 'int8', 'int2'
|
155
155
|
alias_type 0, 'oid', 'int2'
|
156
156
|
|
157
|
-
#
|
157
|
+
# register_type 0, 'numeric', OID::Decimal.new
|
158
158
|
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
159
159
|
alias_type 0, 'varchar', 'text'
|
160
160
|
alias_type 0, 'char', 'text'
|
161
161
|
alias_type 0, 'bpchar', 'text'
|
162
162
|
alias_type 0, 'xml', 'text'
|
163
163
|
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
164
|
+
# FIXME: why are we keeping these types as strings?
|
165
|
+
# alias_type 'tsvector', 'text'
|
166
|
+
# alias_type 'interval', 'text'
|
167
|
+
# alias_type 'macaddr', 'text'
|
168
|
+
# alias_type 'uuid', 'text'
|
169
|
+
#
|
170
|
+
# register_type 'money', OID::Money.new
|
171
171
|
# There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
|
172
172
|
# in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
|
173
173
|
register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
|
174
174
|
register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
175
|
+
# register_type 'bit', OID::Bit.new
|
176
|
+
# register_type 'varbit', OID::Bit.new
|
177
|
+
|
178
178
|
register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
|
179
179
|
alias_type 0, 'float8', 'float4'
|
180
180
|
|
181
181
|
register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
|
182
182
|
register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
|
183
183
|
register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
184
|
+
# register_type 'time', OID::Time.new
|
185
|
+
#
|
186
|
+
# register_type 'path', OID::Text.new
|
187
|
+
# register_type 'point', OID::Point.new
|
188
|
+
# register_type 'polygon', OID::Text.new
|
189
|
+
# register_type 'circle', OID::Text.new
|
190
|
+
# register_type 'hstore', OID::Hstore.new
|
191
|
+
register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
|
192
|
+
alias_type 0, 'jsonb', 'json'
|
193
|
+
# register_type 'citext', OID::Text.new
|
194
|
+
# register_type 'ltree', OID::Text.new
|
195
|
+
#
|
196
|
+
# register_type 'cidr', OID::Cidr.new
|
197
|
+
# alias_type 'inet', 'cidr'
|
197
198
|
|
198
199
|
|
199
200
|
|
@@ -226,8 +227,8 @@ end
|
|
226
227
|
#
|
227
228
|
# Example:
|
228
229
|
# conn = PG::Connection.new
|
229
|
-
# # Assign a default ruleset for type casts of
|
230
|
-
# conn.
|
230
|
+
# # Assign a default ruleset for type casts of output values.
|
231
|
+
# conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
|
231
232
|
# # Execute a query.
|
232
233
|
# res = conn.exec_params( "SELECT $1::INT", ['5'] )
|
233
234
|
# # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
|
@@ -236,8 +237,28 @@ end
|
|
236
237
|
#
|
237
238
|
# PG::TypeMapByOid#fit_to_result(result, false) can be used to generate
|
238
239
|
# a result independent PG::TypeMapByColumn type map, which can subsequently be used
|
239
|
-
# to cast #get_copy_data fields
|
240
|
+
# to cast #get_copy_data fields:
|
241
|
+
#
|
242
|
+
# For the following table:
|
243
|
+
# conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
|
240
244
|
#
|
245
|
+
# # Retrieve table OIDs per empty result set.
|
246
|
+
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
247
|
+
# # Build a type map for common database to ruby type decoders.
|
248
|
+
# btm = PG::BasicTypeMapForResults.new(conn)
|
249
|
+
# # Build a PG::TypeMapByColumn with decoders suitable for copytable.
|
250
|
+
# tm = btm.build_column_map( res )
|
251
|
+
# row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
|
252
|
+
#
|
253
|
+
# conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
|
254
|
+
# while row=conn.get_copy_data
|
255
|
+
# p row
|
256
|
+
# end
|
257
|
+
# end
|
258
|
+
# This prints the rows with type casted columns:
|
259
|
+
# ["a", 123, [5, 4, 3]]
|
260
|
+
#
|
261
|
+
# See also PG::BasicTypeMapBasedOnResult for the encoder direction.
|
241
262
|
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
242
263
|
include PG::BasicTypeRegistry
|
243
264
|
|
@@ -290,12 +311,17 @@ end
|
|
290
311
|
#
|
291
312
|
# # Retrieve table OIDs per empty result set.
|
292
313
|
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
293
|
-
#
|
314
|
+
# # Build a type map for common ruby to database type encoders.
|
315
|
+
# btm = PG::BasicTypeMapBasedOnResult.new(conn)
|
316
|
+
# # Build a PG::TypeMapByColumn with encoders suitable for copytable.
|
317
|
+
# tm = btm.build_column_map( res )
|
294
318
|
# row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
295
319
|
#
|
296
320
|
# conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
|
297
321
|
# conn.put_copy_data ['a', 123, [5,4,3]]
|
298
322
|
# end
|
323
|
+
# This inserts a single row into copytable with type casts from ruby to
|
324
|
+
# database types.
|
299
325
|
class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
|
300
326
|
include PG::BasicTypeRegistry
|
301
327
|
|
@@ -314,15 +340,16 @@ end
|
|
314
340
|
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
315
341
|
# PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
|
316
342
|
#
|
317
|
-
# Query params are type casted based on the
|
343
|
+
# Query params are type casted based on the class of the given value.
|
318
344
|
#
|
319
345
|
# Higher level libraries will most likely not make use of this class, but use their
|
320
|
-
# own set of rules to choose suitable
|
346
|
+
# own derivation of PG::TypeMapByClass or another set of rules to choose suitable
|
347
|
+
# encoders and decoders for the values to be sent.
|
321
348
|
#
|
322
349
|
# Example:
|
323
350
|
# conn = PG::Connection.new
|
324
351
|
# # Assign a default ruleset for type casts of input and output values.
|
325
|
-
# conn.
|
352
|
+
# conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
|
326
353
|
# # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
|
327
354
|
# # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
|
328
355
|
# res = conn.exec_params( "SELECT $1", [5] )
|
data/lib/pg/connection.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'pg' unless defined?( PG )
|
4
|
+
require 'uri'
|
4
5
|
|
5
6
|
# The PostgreSQL connection class. The interface for this class is based on
|
6
7
|
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
|
@@ -34,46 +35,55 @@ class PG::Connection
|
|
34
35
|
def self::parse_connect_args( *args )
|
35
36
|
return '' if args.empty?
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
|
39
|
+
option_string = ''
|
40
|
+
options = {}
|
40
41
|
|
41
42
|
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
|
42
43
|
# 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 = []
|
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 }
|
49
46
|
end
|
50
47
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
if args.length == 1
|
49
|
+
case args.first
|
50
|
+
when URI, URI.regexp
|
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
|
56
59
|
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
60
|
else
|
65
|
-
|
66
|
-
|
61
|
+
max = CONNECT_ARGUMENT_ORDER.length
|
62
|
+
raise ArgumentError,
|
63
|
+
"Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
|
65
|
+
CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
|
66
|
+
options[ k.to_sym ] = v if v
|
71
67
|
end
|
72
68
|
end
|
73
69
|
|
74
|
-
|
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
|
75
84
|
end
|
76
85
|
|
86
|
+
|
77
87
|
# call-seq:
|
78
88
|
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
|
79
89
|
#
|
@@ -100,12 +110,20 @@ class PG::Connection
|
|
100
110
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
101
111
|
#
|
102
112
|
# Example with CSV input format:
|
103
|
-
# conn.exec "create table my_table (a text,b text,c text,d text
|
113
|
+
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
104
114
|
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
105
|
-
# conn.put_copy_data "some,
|
106
|
-
# conn.put_copy_data "more,
|
115
|
+
# conn.put_copy_data "some,data,to,copy\n"
|
116
|
+
# conn.put_copy_data "more,data,to,copy\n"
|
117
|
+
# end
|
118
|
+
# This creates +my_table+ and inserts two CSV rows.
|
119
|
+
#
|
120
|
+
# The same with text format encoder PG::TextEncoder::CopyRow
|
121
|
+
# and Array input:
|
122
|
+
# enco = PG::TextEncoder::CopyRow.new
|
123
|
+
# conn.copy_data "COPY my_table FROM STDIN", enco do
|
124
|
+
# conn.put_copy_data ['some', 'data', 'to', 'copy']
|
125
|
+
# conn.put_copy_data ['more', 'data', 'to', 'copy']
|
107
126
|
# end
|
108
|
-
# This creates +my_table+ and inserts two rows.
|
109
127
|
#
|
110
128
|
# Example with CSV output format:
|
111
129
|
# conn.copy_data "COPY my_table TO STDOUT CSV" do
|
@@ -114,8 +132,21 @@ class PG::Connection
|
|
114
132
|
# end
|
115
133
|
# end
|
116
134
|
# This prints all rows of +my_table+ to stdout:
|
117
|
-
# "some,
|
118
|
-
# "more,
|
135
|
+
# "some,data,to,copy\n"
|
136
|
+
# "more,data,to,copy\n"
|
137
|
+
#
|
138
|
+
# The same with text format decoder PG::TextDecoder::CopyRow
|
139
|
+
# and Array output:
|
140
|
+
# deco = PG::TextDecoder::CopyRow.new
|
141
|
+
# conn.copy_data "COPY my_table TO STDOUT", deco do
|
142
|
+
# while row=conn.get_copy_data
|
143
|
+
# p row
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
# This receives all rows of +my_table+ as ruby array:
|
147
|
+
# ["some", "data", "to", "copy"]
|
148
|
+
# ["more", "data", "to", "copy"]
|
149
|
+
|
119
150
|
def copy_data( sql, coder=nil )
|
120
151
|
res = exec( sql )
|
121
152
|
|
@@ -155,7 +186,7 @@ class PG::Connection
|
|
155
186
|
raise
|
156
187
|
else
|
157
188
|
res = get_last_result
|
158
|
-
if res.result_status != PGRES_COMMAND_OK
|
189
|
+
if !res || res.result_status != PGRES_COMMAND_OK
|
159
190
|
while get_copy_data
|
160
191
|
end
|
161
192
|
while get_result
|
@@ -214,8 +245,27 @@ class PG::Connection
|
|
214
245
|
end
|
215
246
|
end
|
216
247
|
|
248
|
+
# Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
|
249
|
+
if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
|
250
|
+
# call-seq:
|
251
|
+
# conn.ssl_attributes -> Hash<String,String>
|
252
|
+
#
|
253
|
+
# Returns SSL-related information about the connection as key/value pairs
|
254
|
+
#
|
255
|
+
# The available attributes varies depending on the SSL library being used,
|
256
|
+
# and the type of connection.
|
257
|
+
#
|
258
|
+
# See also #ssl_attribute
|
259
|
+
def ssl_attributes
|
260
|
+
ssl_attribute_names.each.with_object({}) do |n,h|
|
261
|
+
h[n] = ssl_attribute(n)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
217
266
|
end # class PG::Connection
|
218
267
|
|
268
|
+
# :stopdoc:
|
219
269
|
# Backward-compatible alias
|
220
270
|
PGconn = PG::Connection
|
221
271
|
|