pg 0.18.4 → 0.21.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 +0 -0
- data/BSDL +2 -2
- data/ChangeLog +689 -5
- data/History.rdoc +56 -0
- data/Manifest.txt +1 -18
- data/README.rdoc +13 -9
- data/Rakefile +15 -17
- data/Rakefile.cross +8 -7
- data/ext/errorcodes.def +25 -0
- data/ext/errorcodes.txt +13 -1
- data/ext/extconf.rb +9 -1
- data/ext/gvl_wrappers.h +4 -0
- data/ext/pg.c +4 -3
- data/ext/pg.h +6 -3
- data/ext/pg_binary_encoder.c +8 -8
- data/ext/pg_coder.c +31 -10
- data/ext/pg_connection.c +252 -98
- data/ext/pg_copy_coder.c +34 -4
- data/ext/pg_result.c +20 -14
- data/ext/pg_text_encoder.c +62 -42
- data/ext/pg_type_map.c +14 -7
- data/lib/pg/basic_type_mapping.rb +35 -8
- data/lib/pg/connection.rb +46 -10
- data/lib/pg/deprecated_constants.rb +21 -0
- data/lib/pg/result.rb +10 -5
- data/lib/pg/text_decoder.rb +7 -0
- data/lib/pg/text_encoder.rb +8 -0
- data/lib/pg.rb +21 -9
- data/spec/helpers.rb +6 -9
- data/spec/pg/basic_type_mapping_spec.rb +54 -0
- data/spec/pg/connection_spec.rb +158 -26
- data/spec/pg/result_spec.rb +11 -4
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_spec.rb +82 -2
- data.tar.gz.sig +0 -0
- metadata +50 -64
- 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
data/lib/pg/connection.rb
CHANGED
@@ -110,12 +110,20 @@ class PG::Connection
|
|
110
110
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
111
111
|
#
|
112
112
|
# Example with CSV input format:
|
113
|
-
# 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)"
|
114
114
|
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
115
|
-
# conn.put_copy_data "some,
|
116
|
-
# 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']
|
117
126
|
# end
|
118
|
-
# This creates +my_table+ and inserts two rows.
|
119
127
|
#
|
120
128
|
# Example with CSV output format:
|
121
129
|
# conn.copy_data "COPY my_table TO STDOUT CSV" do
|
@@ -124,8 +132,21 @@ class PG::Connection
|
|
124
132
|
# end
|
125
133
|
# end
|
126
134
|
# This prints all rows of +my_table+ to stdout:
|
127
|
-
# "some,
|
128
|
-
# "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
|
+
|
129
150
|
def copy_data( sql, coder=nil )
|
130
151
|
res = exec( sql )
|
131
152
|
|
@@ -165,7 +186,7 @@ class PG::Connection
|
|
165
186
|
raise
|
166
187
|
else
|
167
188
|
res = get_last_result
|
168
|
-
if res.result_status != PGRES_COMMAND_OK
|
189
|
+
if !res || res.result_status != PGRES_COMMAND_OK
|
169
190
|
while get_copy_data
|
170
191
|
end
|
171
192
|
while get_result
|
@@ -224,8 +245,23 @@ class PG::Connection
|
|
224
245
|
end
|
225
246
|
end
|
226
247
|
|
227
|
-
|
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
|
228
265
|
|
229
|
-
#
|
230
|
-
PGconn = PG::Connection
|
266
|
+
end # class PG::Connection
|
231
267
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
# Warn about use of deprecated constants when this is autoloaded
|
5
|
+
callsite = caller(3).first
|
6
|
+
|
7
|
+
warn <<END_OF_WARNING
|
8
|
+
The PGconn, PGresult, and PGError constants are deprecated, and will be
|
9
|
+
removed as of version 1.0.
|
10
|
+
|
11
|
+
You should use PG::Connection, PG::Result, and PG::Error instead, respectively.
|
12
|
+
|
13
|
+
Called from #{callsite}
|
14
|
+
END_OF_WARNING
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
PGconn = PG::Connection
|
19
|
+
PGresult = PG::Result
|
20
|
+
PGError = PG::Error
|
21
|
+
|
data/lib/pg/result.rb
CHANGED
@@ -12,15 +12,20 @@ class PG::Result
|
|
12
12
|
# See PG::BasicTypeMapForResults
|
13
13
|
def map_types!(type_map)
|
14
14
|
self.type_map = type_map
|
15
|
-
self
|
15
|
+
return self
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
### Return a String representation of the object suitable for debugging.
|
18
20
|
def inspect
|
19
21
|
str = self.to_s
|
20
|
-
str[-1,0] =
|
21
|
-
|
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
|
22
28
|
end
|
29
|
+
|
23
30
|
end # class PG::Result
|
24
31
|
|
25
|
-
# Backward-compatible alias
|
26
|
-
PGresult = PG::Result
|
data/lib/pg/text_decoder.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'date'
|
4
|
+
require 'json'
|
4
5
|
|
5
6
|
module PG
|
6
7
|
module TextDecoder
|
@@ -39,6 +40,12 @@ module PG
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
43
|
+
|
44
|
+
class JSON < SimpleDecoder
|
45
|
+
def decode(string, tuple=nil, field=nil)
|
46
|
+
::JSON.parse(string, quirks_mode: true)
|
47
|
+
end
|
48
|
+
end
|
42
49
|
end
|
43
50
|
end # module PG
|
44
51
|
|
data/lib/pg/text_encoder.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module PG
|
4
6
|
module TextEncoder
|
5
7
|
class Date < SimpleEncoder
|
@@ -22,6 +24,12 @@ module PG
|
|
22
24
|
value.respond_to?(:strftime) ? value.strftime(STRFTIME_ISO_DATETIME_WITH_TIMEZONE) : value
|
23
25
|
end
|
24
26
|
end
|
27
|
+
|
28
|
+
class JSON < SimpleEncoder
|
29
|
+
def encode(value)
|
30
|
+
::JSON.generate(value, quirks_mode: true)
|
31
|
+
end
|
32
|
+
end
|
25
33
|
end
|
26
34
|
end # module PG
|
27
35
|
|
data/lib/pg.rb
CHANGED
@@ -8,11 +8,22 @@ rescue LoadError
|
|
8
8
|
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
9
9
|
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
16
27
|
else
|
17
28
|
raise
|
18
29
|
end
|
@@ -24,10 +35,10 @@ end
|
|
24
35
|
module PG
|
25
36
|
|
26
37
|
# Library version
|
27
|
-
VERSION = '0.
|
38
|
+
VERSION = '0.21.0'
|
28
39
|
|
29
40
|
# VCS revision
|
30
|
-
REVISION = %q$Revision:
|
41
|
+
REVISION = %q$Revision: f6063a34ae2b $
|
31
42
|
|
32
43
|
class NotAllCopyDataRetrieved < PG::Error
|
33
44
|
end
|
@@ -59,6 +70,7 @@ module PG
|
|
59
70
|
end # module PG
|
60
71
|
|
61
72
|
|
62
|
-
|
63
|
-
|
73
|
+
autoload :PGError, 'pg/deprecated_constants'
|
74
|
+
autoload :PGconn, 'pg/deprecated_constants'
|
75
|
+
autoload :PGresult, 'pg/deprecated_constants'
|
64
76
|
|
data/spec/helpers.rb
CHANGED
@@ -339,17 +339,14 @@ RSpec.configure do |config|
|
|
339
339
|
config.filter_run_excluding :socket_io unless
|
340
340
|
PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
|
341
341
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
if !PG.respond_to?( :library_version )
|
346
|
-
config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93, :postgresql_94 )
|
347
|
-
elsif PG.library_version < 90200
|
348
|
-
config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94 )
|
342
|
+
if PG.library_version < 90200
|
343
|
+
config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94, :postgresql_95 )
|
349
344
|
elsif PG.library_version < 90300
|
350
|
-
config.filter_run_excluding( :postgresql_93, :postgresql_94 )
|
345
|
+
config.filter_run_excluding( :postgresql_93, :postgresql_94, :postgresql_95 )
|
351
346
|
elsif PG.library_version < 90400
|
352
|
-
config.filter_run_excluding( :postgresql_94 )
|
347
|
+
config.filter_run_excluding( :postgresql_94, :postgresql_95 )
|
348
|
+
elsif PG.library_version < 90500
|
349
|
+
config.filter_run_excluding( :postgresql_95 )
|
353
350
|
end
|
354
351
|
end
|
355
352
|
|
@@ -166,6 +166,27 @@ describe 'Basic type mapping' do
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
+
it "should do JSON conversions", :postgresql_94 do
|
170
|
+
[0].each do |format|
|
171
|
+
['JSON', 'JSONB'].each do |type|
|
172
|
+
res = @conn.exec( "SELECT CAST('123' AS #{type}),
|
173
|
+
CAST('12.3' AS #{type}),
|
174
|
+
CAST('true' AS #{type}),
|
175
|
+
CAST('false' AS #{type}),
|
176
|
+
CAST('null' AS #{type}),
|
177
|
+
CAST('[1, \"a\", null]' AS #{type}),
|
178
|
+
CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
|
179
|
+
expect( res.getvalue(0,0) ).to eq( 123 )
|
180
|
+
expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
|
181
|
+
expect( res.getvalue(0,2) ).to eq( true )
|
182
|
+
expect( res.getvalue(0,3) ).to eq( false )
|
183
|
+
expect( res.getvalue(0,4) ).to eq( nil )
|
184
|
+
expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
|
185
|
+
expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
169
190
|
it "should do array type conversions" do
|
170
191
|
[0].each do |format|
|
171
192
|
res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
|
@@ -228,6 +249,39 @@ describe 'Basic type mapping' do
|
|
228
249
|
res = @conn.exec( "SELECT * FROM copytable" )
|
229
250
|
expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
|
230
251
|
end
|
252
|
+
|
253
|
+
it "can do JSON conversions", :postgresql_94 do
|
254
|
+
['JSON', 'JSONB'].each do |type|
|
255
|
+
sql = "SELECT CAST('123' AS #{type}),
|
256
|
+
CAST('12.3' AS #{type}),
|
257
|
+
CAST('true' AS #{type}),
|
258
|
+
CAST('false' AS #{type}),
|
259
|
+
CAST('null' AS #{type}),
|
260
|
+
CAST('[1, \"a\", null]' AS #{type}),
|
261
|
+
CAST('{\"b\" : [2,3]}' AS #{type})"
|
262
|
+
|
263
|
+
tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
|
264
|
+
expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
|
265
|
+
|
266
|
+
res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
|
267
|
+
[ 123,
|
268
|
+
12.3,
|
269
|
+
true,
|
270
|
+
false,
|
271
|
+
nil,
|
272
|
+
[1, "a", nil],
|
273
|
+
{"b" => [2, 3]},
|
274
|
+
], 0, tm )
|
275
|
+
|
276
|
+
expect( res.getvalue(0,0) ).to eq( "123" )
|
277
|
+
expect( res.getvalue(0,1) ).to eq( "12.3" )
|
278
|
+
expect( res.getvalue(0,2) ).to eq( "true" )
|
279
|
+
expect( res.getvalue(0,3) ).to eq( "false" )
|
280
|
+
expect( res.getvalue(0,4) ).to eq( nil )
|
281
|
+
expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
|
282
|
+
expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
|
283
|
+
end
|
284
|
+
end
|
231
285
|
end
|
232
286
|
|
233
287
|
context "with usage of result oids for copy encoder selection" do
|
data/spec/pg/connection_spec.rb
CHANGED
@@ -643,6 +643,31 @@ describe PG::Connection do
|
|
643
643
|
expect( @conn ).to still_be_usable
|
644
644
|
end
|
645
645
|
|
646
|
+
it "gracefully handle SQL statements while in #copy_data for input" do
|
647
|
+
@conn.exec "ROLLBACK"
|
648
|
+
@conn.transaction do
|
649
|
+
@conn.exec( "CREATE TEMP TABLE copytable (col1 INT)" )
|
650
|
+
expect {
|
651
|
+
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
652
|
+
@conn.exec "SELECT 1"
|
653
|
+
end
|
654
|
+
}.to raise_error(PG::Error, /no COPY in progress/)
|
655
|
+
end
|
656
|
+
expect( @conn ).to still_be_usable
|
657
|
+
end
|
658
|
+
|
659
|
+
it "gracefully handle SQL statements while in #copy_data for output" do
|
660
|
+
@conn.exec "ROLLBACK"
|
661
|
+
@conn.transaction do
|
662
|
+
expect {
|
663
|
+
@conn.copy_data( "COPY (VALUES(1), (2)) TO STDOUT" ) do |res|
|
664
|
+
@conn.exec "SELECT 3"
|
665
|
+
end
|
666
|
+
}.to raise_error(PG::Error, /no COPY in progress/)
|
667
|
+
end
|
668
|
+
expect( @conn ).to still_be_usable
|
669
|
+
end
|
670
|
+
|
646
671
|
it "should raise an error for non copy statements in #copy_data" do
|
647
672
|
expect {
|
648
673
|
@conn.copy_data( "SELECT 1" ){}
|
@@ -684,13 +709,13 @@ describe PG::Connection do
|
|
684
709
|
end
|
685
710
|
|
686
711
|
it "described_class#block should allow a timeout" do
|
687
|
-
@conn.send_query( "select pg_sleep(
|
712
|
+
@conn.send_query( "select pg_sleep(1)" )
|
688
713
|
|
689
714
|
start = Time.now
|
690
|
-
@conn.block( 0.
|
715
|
+
@conn.block( 0.3 )
|
691
716
|
finish = Time.now
|
692
717
|
|
693
|
-
expect( (finish - start) ).to be_within( 0.
|
718
|
+
expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
|
694
719
|
end
|
695
720
|
|
696
721
|
|
@@ -725,6 +750,25 @@ describe PG::Connection do
|
|
725
750
|
expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
|
726
751
|
end
|
727
752
|
|
753
|
+
describe "connection information related to SSL" do
|
754
|
+
|
755
|
+
it "can retrieve connection's ssl state", :postgresql_95 do
|
756
|
+
expect( @conn.ssl_in_use? ).to be false
|
757
|
+
end
|
758
|
+
|
759
|
+
it "can retrieve connection's ssl attribute_names", :postgresql_95 do
|
760
|
+
expect( @conn.ssl_attribute_names ).to be_a(Array)
|
761
|
+
end
|
762
|
+
|
763
|
+
it "can retrieve a single ssl connection attribute", :postgresql_95 do
|
764
|
+
expect( @conn.ssl_attribute('dbname') ).to eq( nil )
|
765
|
+
end
|
766
|
+
|
767
|
+
it "can retrieve all connection's ssl attributes", :postgresql_95 do
|
768
|
+
expect( @conn.ssl_attributes ).to be_a_kind_of( Hash )
|
769
|
+
end
|
770
|
+
end
|
771
|
+
|
728
772
|
|
729
773
|
it "honors the connect_timeout connection parameter", :postgresql_93 do
|
730
774
|
conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
|
@@ -1070,8 +1114,8 @@ describe PG::Connection do
|
|
1070
1114
|
res.check
|
1071
1115
|
first_row_time = Time.now unless first_row_time
|
1072
1116
|
end
|
1073
|
-
expect( (Time.now - start_time) ).to be >=
|
1074
|
-
expect( (first_row_time - start_time) ).to be <
|
1117
|
+
expect( (Time.now - start_time) ).to be >= 0.9
|
1118
|
+
expect( (first_row_time - start_time) ).to be < 0.9
|
1075
1119
|
end
|
1076
1120
|
|
1077
1121
|
it "should receive rows before entire query fails" do
|
@@ -1150,51 +1194,137 @@ describe PG::Connection do
|
|
1150
1194
|
end
|
1151
1195
|
|
1152
1196
|
it "uses the client encoding for escaped string" do
|
1153
|
-
original = "
|
1197
|
+
original = "Möhre to\0 escape".encode( "utf-16be" )
|
1154
1198
|
@conn.set_client_encoding( "euc_jp" )
|
1155
1199
|
escaped = @conn.escape( original )
|
1156
1200
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1157
|
-
expect( escaped ).to eq( "
|
1201
|
+
expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
|
1158
1202
|
end
|
1159
1203
|
|
1160
1204
|
it "uses the client encoding for escaped literal", :postgresql_90 do
|
1161
|
-
original = "
|
1205
|
+
original = "Möhre to\0 escape".encode( "utf-16be" )
|
1162
1206
|
@conn.set_client_encoding( "euc_jp" )
|
1163
1207
|
escaped = @conn.escape_literal( original )
|
1164
1208
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1165
|
-
expect( escaped ).to eq( "'
|
1209
|
+
expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
|
1166
1210
|
end
|
1167
1211
|
|
1168
1212
|
it "uses the client encoding for escaped identifier", :postgresql_90 do
|
1169
|
-
original = "
|
1213
|
+
original = "Möhre to\0 escape".encode( "utf-16le" )
|
1170
1214
|
@conn.set_client_encoding( "euc_jp" )
|
1171
1215
|
escaped = @conn.escape_identifier( original )
|
1172
1216
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1173
|
-
expect( escaped ).to eq( "\"
|
1217
|
+
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1174
1218
|
end
|
1175
1219
|
|
1176
1220
|
it "uses the client encoding for quote_ident" do
|
1177
|
-
original = "
|
1221
|
+
original = "Möhre to\0 escape".encode( "utf-16le" )
|
1178
1222
|
@conn.set_client_encoding( "euc_jp" )
|
1179
1223
|
escaped = @conn.quote_ident( original )
|
1180
1224
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1181
|
-
expect( escaped ).to eq( "\"
|
1225
|
+
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1182
1226
|
end
|
1183
1227
|
|
1184
1228
|
it "uses the previous string encoding for escaped string" do
|
1185
|
-
original = "
|
1229
|
+
original = "Möhre to\0 escape".encode( "iso-8859-1" )
|
1186
1230
|
@conn.set_client_encoding( "euc_jp" )
|
1187
1231
|
escaped = described_class.escape( original )
|
1188
1232
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1189
|
-
expect( escaped ).to eq( "
|
1233
|
+
expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
|
1190
1234
|
end
|
1191
1235
|
|
1192
1236
|
it "uses the previous string encoding for quote_ident" do
|
1193
|
-
original = "
|
1237
|
+
original = "Möhre to\0 escape".encode( "iso-8859-1" )
|
1194
1238
|
@conn.set_client_encoding( "euc_jp" )
|
1195
1239
|
escaped = described_class.quote_ident( original )
|
1196
1240
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1197
|
-
expect( escaped ).to eq( "\"
|
1241
|
+
expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
|
1242
|
+
end
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
describe "respect and convert character encoding of input strings" do
|
1246
|
+
before :each do
|
1247
|
+
@conn.internal_encoding = __ENCODING__
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
it "should convert query string and parameters to #exec_params" do
|
1251
|
+
r = @conn.exec_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1252
|
+
['grün'.encode('utf-16be'), 'grün'.encode('iso-8859-1')])
|
1253
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
it "should convert query string and parameters to #async_exec" do
|
1257
|
+
r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
|
1258
|
+
['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
|
1259
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
it "should convert query string to #exec" do
|
1263
|
+
r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
|
1264
|
+
expect( r.values ).to eq( [['grün']] )
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
it "should convert query string to #async_exec" do
|
1268
|
+
r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
|
1269
|
+
expect( r.values ).to eq( [['grün']] )
|
1270
|
+
end
|
1271
|
+
|
1272
|
+
it "should convert strings and parameters to #prepare and #exec_prepared" do
|
1273
|
+
@conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
|
1274
|
+
r = @conn.exec_prepared("weiß1".encode("utf-32le"),
|
1275
|
+
['grün'.encode('cp936'), 'grün'.encode('utf-16le')])
|
1276
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
it "should convert strings to #describe_prepared" do
|
1280
|
+
@conn.prepare("weiß2", "VALUES(123)")
|
1281
|
+
r = @conn.describe_prepared("weiß2".encode("utf-16be"))
|
1282
|
+
expect( r.nfields ).to eq( 1 )
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
it "should convert strings to #describe_portal" do
|
1286
|
+
@conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
|
1287
|
+
r = @conn.describe_portal("cörsör".encode("utf-16le"))
|
1288
|
+
expect( r.nfields ).to eq( 3 )
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
it "should convert query string to #send_query" do
|
1292
|
+
@conn.send_query("VALUES('grün')".encode("utf-16be"))
|
1293
|
+
expect( @conn.get_last_result.values ).to eq( [['grün']] )
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
it "should convert query string and parameters to #send_query" do
|
1297
|
+
@conn.send_query("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1298
|
+
['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
|
1299
|
+
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
it "should convert strings and parameters to #send_prepare and #send_query_prepared" do
|
1303
|
+
@conn.send_prepare("weiß3".encode("iso-8859-1"), "VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16be"))
|
1304
|
+
@conn.get_last_result
|
1305
|
+
@conn.send_query_prepared("weiß3".encode("utf-32le"),
|
1306
|
+
['grün'.encode('utf-16le'), 'grün'.encode('iso-8859-1')])
|
1307
|
+
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
it "should convert strings to #send_describe_prepared" do
|
1311
|
+
@conn.prepare("weiß4", "VALUES(123)")
|
1312
|
+
@conn.send_describe_prepared("weiß4".encode("utf-16be"))
|
1313
|
+
expect( @conn.get_last_result.nfields ).to eq( 1 )
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
it "should convert strings to #send_describe_portal" do
|
1317
|
+
@conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
|
1318
|
+
@conn.send_describe_portal("cörsör".encode("utf-16le"))
|
1319
|
+
expect( @conn.get_last_result.nfields ).to eq( 3 )
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
it "should convert error string to #put_copy_end" do
|
1323
|
+
@conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1324
|
+
@conn.exec( "COPY copytable FROM STDIN" )
|
1325
|
+
@conn.put_copy_end("grün".encode("utf-16be"))
|
1326
|
+
expect( @conn.get_result.error_message ).to match(/grün/)
|
1327
|
+
@conn.get_result
|
1198
1328
|
end
|
1199
1329
|
end
|
1200
1330
|
|
@@ -1228,12 +1358,12 @@ describe PG::Connection do
|
|
1228
1358
|
|
1229
1359
|
begin
|
1230
1360
|
prev_encoding = Encoding.default_internal
|
1231
|
-
Encoding.default_internal = Encoding::
|
1361
|
+
Encoding.default_internal = Encoding::ISO8859_2
|
1232
1362
|
|
1233
1363
|
conn = PG.connect( @conninfo )
|
1234
|
-
expect( conn.internal_encoding ).to eq( Encoding::
|
1364
|
+
expect( conn.internal_encoding ).to eq( Encoding::ISO8859_2 )
|
1235
1365
|
res = conn.exec( "SELECT foo FROM defaultinternaltest" )
|
1236
|
-
expect( res[0]['foo'].encoding ).to eq( Encoding::
|
1366
|
+
expect( res[0]['foo'].encoding ).to eq( Encoding::ISO8859_2 )
|
1237
1367
|
ensure
|
1238
1368
|
conn.exec( "DROP TABLE defaultinternaltest" )
|
1239
1369
|
conn.finish if conn
|
@@ -1463,15 +1593,15 @@ describe PG::Connection do
|
|
1463
1593
|
end
|
1464
1594
|
end
|
1465
1595
|
|
1466
|
-
it "can process #copy_data input queries with row encoder" do
|
1596
|
+
it "can process #copy_data input queries with row encoder and respects character encoding" do
|
1467
1597
|
@conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1468
1598
|
res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1469
1599
|
@conn2.put_copy_data [1]
|
1470
|
-
@conn2.put_copy_data ["
|
1600
|
+
@conn2.put_copy_data ["Möhre".encode("utf-16le")]
|
1471
1601
|
end
|
1472
1602
|
|
1473
1603
|
res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
|
1474
|
-
expect( res.values ).to eq( [["1"], ["
|
1604
|
+
expect( res.values ).to eq( [["1"], ["Möhre"]] )
|
1475
1605
|
end
|
1476
1606
|
end
|
1477
1607
|
|
@@ -1513,14 +1643,16 @@ describe PG::Connection do
|
|
1513
1643
|
end
|
1514
1644
|
end
|
1515
1645
|
|
1516
|
-
it "can process #copy_data output with row decoder" do
|
1646
|
+
it "can process #copy_data output with row decoder and respects character encoding" do
|
1647
|
+
@conn2.internal_encoding = Encoding::ISO8859_1
|
1517
1648
|
rows = []
|
1518
|
-
res2 = @conn2.copy_data( "COPY (
|
1649
|
+
res2 = @conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
|
1519
1650
|
while row=@conn2.get_copy_data
|
1520
1651
|
rows << row
|
1521
1652
|
end
|
1522
1653
|
end
|
1523
|
-
expect( rows ).to eq(
|
1654
|
+
expect( rows.last.last.encoding ).to eq( Encoding::ISO8859_1 )
|
1655
|
+
expect( rows ).to eq( [["1"], ["Möhre".encode("iso-8859-1")]] )
|
1524
1656
|
end
|
1525
1657
|
|
1526
1658
|
it "can type cast #copy_data output with explicit decoder" do
|
data/spec/pg/result_spec.rb
CHANGED
@@ -100,11 +100,11 @@ describe PG::Result do
|
|
100
100
|
expect( res[0]['n'] ).to be_nil()
|
101
101
|
end
|
102
102
|
|
103
|
-
it "encapsulates errors in a
|
103
|
+
it "encapsulates errors in a PG::Error object" do
|
104
104
|
exception = nil
|
105
105
|
begin
|
106
106
|
@conn.exec( "SELECT * FROM nonexistant_table" )
|
107
|
-
rescue
|
107
|
+
rescue PG::Error => err
|
108
108
|
exception = err
|
109
109
|
end
|
110
110
|
|
@@ -136,7 +136,7 @@ describe PG::Result do
|
|
136
136
|
exception = nil
|
137
137
|
begin
|
138
138
|
@conn.exec( "INSERT INTO integrity VALUES (NULL)" )
|
139
|
-
rescue
|
139
|
+
rescue PG::Error => err
|
140
140
|
exception = err
|
141
141
|
end
|
142
142
|
result = exception.result
|
@@ -152,7 +152,7 @@ describe PG::Result do
|
|
152
152
|
sqlstate = nil
|
153
153
|
begin
|
154
154
|
res = @conn.exec("SELECT 1/0")
|
155
|
-
rescue
|
155
|
+
rescue PG::Error => e
|
156
156
|
sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
|
157
157
|
end
|
158
158
|
expect( sqlstate ).to eq( 22012 )
|
@@ -403,6 +403,13 @@ describe PG::Result do
|
|
403
403
|
expect( r.cleared? ).to eq(true)
|
404
404
|
end
|
405
405
|
|
406
|
+
it "can be inspected before and after clear" do
|
407
|
+
r = @conn.exec "select 1"
|
408
|
+
expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
|
409
|
+
r.clear
|
410
|
+
expect( r.inspect ).to match(/cleared/)
|
411
|
+
end
|
412
|
+
|
406
413
|
context 'result value conversions with TypeMapByColumn' do
|
407
414
|
let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
|
408
415
|
let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
|
@@ -59,7 +59,7 @@ describe PG::TypeMapByClass do
|
|
59
59
|
it "should retrieve particular conversions" do
|
60
60
|
expect( tm[Integer] ).to eq(binaryenc_int)
|
61
61
|
expect( tm[Float] ).to eq(textenc_float)
|
62
|
-
expect( tm[
|
62
|
+
expect( tm[Range] ).to be_nil
|
63
63
|
expect( derived_tm[raise_class] ).to be_kind_of(Proc)
|
64
64
|
expect( derived_tm[Array] ).to eq(:array_type_map_for)
|
65
65
|
end
|
@@ -102,7 +102,7 @@ describe PG::TypeMapByClass do
|
|
102
102
|
|
103
103
|
it "should allow mixed type conversions" do
|
104
104
|
res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
|
105
|
-
expect( res.values ).to eq([['5', '1.23',
|
105
|
+
expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
|
106
106
|
expect( res.ftype(0) ).to eq(20)
|
107
107
|
end
|
108
108
|
|