pg 0.18.4-x64-mingw32 → 0.19.0.pre20160817083826-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +686 -167
  5. data/History.rdoc +23 -0
  6. data/README.rdoc +6 -6
  7. data/Rakefile +10 -12
  8. data/Rakefile.cross +6 -6
  9. data/ext/errorcodes.def +16 -0
  10. data/ext/errorcodes.txt +5 -1
  11. data/ext/extconf.rb +9 -1
  12. data/ext/gvl_wrappers.h +4 -0
  13. data/ext/pg.c +1 -1
  14. data/ext/pg.h +6 -3
  15. data/ext/pg_binary_decoder.c +1 -1
  16. data/ext/pg_binary_encoder.c +8 -8
  17. data/ext/pg_coder.c +30 -9
  18. data/ext/pg_connection.c +203 -77
  19. data/ext/pg_copy_coder.c +34 -4
  20. data/ext/pg_result.c +2 -2
  21. data/ext/pg_text_decoder.c +1 -1
  22. data/ext/pg_text_encoder.c +62 -42
  23. data/ext/pg_type_map.c +1 -1
  24. data/ext/pg_type_map_all_strings.c +1 -1
  25. data/ext/pg_type_map_by_class.c +1 -1
  26. data/ext/pg_type_map_by_column.c +1 -1
  27. data/ext/pg_type_map_by_mri_type.c +1 -1
  28. data/ext/pg_type_map_by_oid.c +1 -1
  29. data/ext/pg_type_map_in_ruby.c +1 -1
  30. data/ext/util.c +1 -1
  31. data/lib/2.0/pg_ext.so +0 -0
  32. data/lib/2.1/pg_ext.so +0 -0
  33. data/lib/2.2/pg_ext.so +0 -0
  34. data/lib/2.3/pg_ext.so +0 -0
  35. data/lib/libpq.dll +0 -0
  36. data/lib/pg.rb +3 -3
  37. data/lib/pg/basic_type_mapping.rb +35 -8
  38. data/lib/pg/connection.rb +46 -6
  39. data/lib/pg/result.rb +6 -2
  40. data/lib/pg/text_decoder.rb +7 -0
  41. data/lib/pg/text_encoder.rb +8 -0
  42. data/sample/disk_usage_report.rb +1 -1
  43. data/sample/pg_statistics.rb +1 -1
  44. data/sample/replication_monitor.rb +1 -1
  45. data/spec/helpers.rb +6 -9
  46. data/spec/pg/basic_type_mapping_spec.rb +54 -0
  47. data/spec/pg/connection_spec.rb +130 -23
  48. data/spec/pg/type_map_by_class_spec.rb +1 -1
  49. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  50. data/spec/pg/type_spec.rb +82 -2
  51. metadata +33 -35
  52. metadata.gz.sig +0 -0
  53. data/lib/x64-mingw32/libpq.dll +0 -0
@@ -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 3d89d3aae4fd 2015/01/05 16:19:41 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
data/ext/util.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * util.c - Utils for ruby-pg
3
- * $Id: util.c,v 5fb9170f6a7d 2015/06/29 11:15:12 kanis $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
Binary file
Binary file
Binary file
Binary file
Binary file
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("../#{RUBY_PLATFORM}", __FILE__)};#{old_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.18.4'
27
+ VERSION = '0.19.0.pre20160817083826'
28
28
 
29
29
  # VCS revision
30
- REVISION = %q$Revision: da42b972b5ab $
30
+ REVISION = %q$Revision$
31
31
 
32
32
  class NotAllCopyDataRetrieved < PG::Error
33
33
  end
@@ -188,7 +188,8 @@ module PG::BasicTypeRegistry
188
188
  # register_type 'polygon', OID::Text.new
189
189
  # register_type 'circle', OID::Text.new
190
190
  # register_type 'hstore', OID::Hstore.new
191
- # register_type 'json', OID::Json.new
191
+ register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
192
+ alias_type 0, 'jsonb', 'json'
192
193
  # register_type 'citext', OID::Text.new
193
194
  # register_type 'ltree', OID::Text.new
194
195
  #
@@ -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 input and output values.
230
- # conn.type_mapping = PG::BasicTypeMapping.new(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. See also PG::BasicTypeMapBasedOnResult .
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[])" )
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]]
240
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
- # tm = basic_type_mapping.build_column_map( res )
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 MRI internal type of the given value.
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 encoders and decoders.
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.type_mapping_for_queries = PG::BasicTypeMapForQueries.new(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] )
@@ -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,e 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,csv,data,to,copy\n"
116
- # conn.put_copy_data "more,csv,data,to,copy\n"
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,csv,data,to,copy\n"
128
- # "more,csv,data,to,copy\n"
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
 
@@ -224,8 +245,27 @@ class PG::Connection
224
245
  end
225
246
  end
226
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
+
227
266
  end # class PG::Connection
228
267
 
268
+ # :stopdoc:
229
269
  # Backward-compatible alias
230
270
  PGconn = PG::Connection
231
271
 
@@ -12,15 +12,19 @@ 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
22
  str[-1,0] = " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
21
- str
23
+ return str
22
24
  end
25
+
23
26
  end # class PG::Result
24
27
 
28
+ # :stopdoc:
25
29
  # Backward-compatible alias
26
30
  PGresult = PG::Result
@@ -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.load(string)
47
+ end
48
+ end
42
49
  end
43
50
  end # module PG
44
51
 
@@ -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.dump(value)
31
+ end
32
+ end
25
33
  end
26
34
  end # module PG
27
35
 
@@ -24,7 +24,7 @@ rescue LoadError # 1.8 support
24
24
  raise
25
25
  end
26
26
 
27
- SCRIPT_VERSION = %q$Id: disk_usage_report.rb,v 76ebae01c937 2013/03/26 17:50:02 ged $
27
+ SCRIPT_VERSION = %q$Id$
28
28
 
29
29
 
30
30
  ### Gather data and output it to $stdout.
@@ -32,7 +32,7 @@ end
32
32
  ### Optionally run in a continuous loop, displaying deltas.
33
33
  ###
34
34
  class Stats
35
- VERSION = %q$Id: pg_statistics.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
35
+ VERSION = %q$Id$
36
36
 
37
37
  def initialize( opts )
38
38
  @opts = opts
@@ -36,7 +36,7 @@ end
36
36
  ###
37
37
  class PGMonitor
38
38
 
39
- VERSION = %q$Id: replication_monitor.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
39
+ VERSION = %q$Id$
40
40
 
41
41
  # When to consider a slave as 'behind', measured in WAL segments.
42
42
  # The default WAL segment size is 16, so we'll alert after
@@ -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
- config.filter_run_excluding :postgresql_90 unless
343
- PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
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
@@ -684,13 +684,13 @@ describe PG::Connection do
684
684
  end
685
685
 
686
686
  it "described_class#block should allow a timeout" do
687
- @conn.send_query( "select pg_sleep(3)" )
687
+ @conn.send_query( "select pg_sleep(1)" )
688
688
 
689
689
  start = Time.now
690
- @conn.block( 0.1 )
690
+ @conn.block( 0.3 )
691
691
  finish = Time.now
692
692
 
693
- expect( (finish - start) ).to be_within( 0.05 ).of( 0.1 )
693
+ expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
694
694
  end
695
695
 
696
696
 
@@ -725,6 +725,25 @@ describe PG::Connection do
725
725
  expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
726
726
  end
727
727
 
728
+ describe "connection information related to SSL" do
729
+
730
+ it "can retrieve connection's ssl state", :postgresql_95 do
731
+ expect( @conn.ssl_in_use? ).to be false
732
+ end
733
+
734
+ it "can retrieve connection's ssl attribute_names", :postgresql_95 do
735
+ expect( @conn.ssl_attribute_names ).to be_a(Array)
736
+ end
737
+
738
+ it "can retrieve a single ssl connection attribute", :postgresql_95 do
739
+ expect( @conn.ssl_attribute('dbname') ).to eq( nil )
740
+ end
741
+
742
+ it "can retrieve all connection's ssl attributes", :postgresql_95 do
743
+ expect( @conn.ssl_attributes ).to be_a_kind_of( Hash )
744
+ end
745
+ end
746
+
728
747
 
729
748
  it "honors the connect_timeout connection parameter", :postgresql_93 do
730
749
  conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
@@ -1070,8 +1089,8 @@ describe PG::Connection do
1070
1089
  res.check
1071
1090
  first_row_time = Time.now unless first_row_time
1072
1091
  end
1073
- expect( (Time.now - start_time) ).to be >= 1.0
1074
- expect( (first_row_time - start_time) ).to be < 1.0
1092
+ expect( (Time.now - start_time) ).to be >= 0.9
1093
+ expect( (first_row_time - start_time) ).to be < 0.9
1075
1094
  end
1076
1095
 
1077
1096
  it "should receive rows before entire query fails" do
@@ -1150,51 +1169,137 @@ describe PG::Connection do
1150
1169
  end
1151
1170
 
1152
1171
  it "uses the client encoding for escaped string" do
1153
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1172
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1154
1173
  @conn.set_client_encoding( "euc_jp" )
1155
1174
  escaped = @conn.escape( original )
1156
1175
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1157
- expect( escaped ).to eq( "string to" )
1176
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1158
1177
  end
1159
1178
 
1160
1179
  it "uses the client encoding for escaped literal", :postgresql_90 do
1161
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1180
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1162
1181
  @conn.set_client_encoding( "euc_jp" )
1163
1182
  escaped = @conn.escape_literal( original )
1164
1183
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1165
- expect( escaped ).to eq( "'string to'" )
1184
+ expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1166
1185
  end
1167
1186
 
1168
1187
  it "uses the client encoding for escaped identifier", :postgresql_90 do
1169
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1188
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1170
1189
  @conn.set_client_encoding( "euc_jp" )
1171
1190
  escaped = @conn.escape_identifier( original )
1172
1191
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1173
- expect( escaped ).to eq( "\"string to\"" )
1192
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1174
1193
  end
1175
1194
 
1176
1195
  it "uses the client encoding for quote_ident" do
1177
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1196
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1178
1197
  @conn.set_client_encoding( "euc_jp" )
1179
1198
  escaped = @conn.quote_ident( original )
1180
1199
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1181
- expect( escaped ).to eq( "\"string to\"" )
1200
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1182
1201
  end
1183
1202
 
1184
1203
  it "uses the previous string encoding for escaped string" do
1185
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1204
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1186
1205
  @conn.set_client_encoding( "euc_jp" )
1187
1206
  escaped = described_class.escape( original )
1188
1207
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1189
- expect( escaped ).to eq( "string to" )
1208
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
1190
1209
  end
1191
1210
 
1192
1211
  it "uses the previous string encoding for quote_ident" do
1193
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1212
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1194
1213
  @conn.set_client_encoding( "euc_jp" )
1195
1214
  escaped = described_class.quote_ident( original )
1196
1215
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1197
- expect( escaped ).to eq( "\"string to\"" )
1216
+ expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
1217
+ end
1218
+ end
1219
+
1220
+ describe "respect and convert character encoding of input strings" do
1221
+ before :each do
1222
+ @conn.internal_encoding = __ENCODING__
1223
+ end
1224
+
1225
+ it "should convert query string and parameters to #exec_params" do
1226
+ r = @conn.exec_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1227
+ ['grün'.encode('utf-16be'), 'grün'.encode('iso-8859-1')])
1228
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1229
+ end
1230
+
1231
+ it "should convert query string and parameters to #async_exec" do
1232
+ r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
1233
+ ['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
1234
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1235
+ end
1236
+
1237
+ it "should convert query string to #exec" do
1238
+ r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
1239
+ expect( r.values ).to eq( [['grün']] )
1240
+ end
1241
+
1242
+ it "should convert query string to #async_exec" do
1243
+ r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
1244
+ expect( r.values ).to eq( [['grün']] )
1245
+ end
1246
+
1247
+ it "should convert strings and parameters to #prepare and #exec_prepared" do
1248
+ @conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
1249
+ r = @conn.exec_prepared("weiß1".encode("utf-32le"),
1250
+ ['grün'.encode('cp936'), 'grün'.encode('utf-16le')])
1251
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1252
+ end
1253
+
1254
+ it "should convert strings to #describe_prepared" do
1255
+ @conn.prepare("weiß2", "VALUES(123)")
1256
+ r = @conn.describe_prepared("weiß2".encode("utf-16be"))
1257
+ expect( r.nfields ).to eq( 1 )
1258
+ end
1259
+
1260
+ it "should convert strings to #describe_portal" do
1261
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1262
+ r = @conn.describe_portal("cörsör".encode("utf-16le"))
1263
+ expect( r.nfields ).to eq( 3 )
1264
+ end
1265
+
1266
+ it "should convert query string to #send_query" do
1267
+ @conn.send_query("VALUES('grün')".encode("utf-16be"))
1268
+ expect( @conn.get_last_result.values ).to eq( [['grün']] )
1269
+ end
1270
+
1271
+ it "should convert query string and parameters to #send_query" do
1272
+ @conn.send_query("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1273
+ ['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
1274
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1275
+ end
1276
+
1277
+ it "should convert strings and parameters to #send_prepare and #send_query_prepared" do
1278
+ @conn.send_prepare("weiß3".encode("iso-8859-1"), "VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16be"))
1279
+ @conn.get_last_result
1280
+ @conn.send_query_prepared("weiß3".encode("utf-32le"),
1281
+ ['grün'.encode('utf-16le'), 'grün'.encode('iso-8859-1')])
1282
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1283
+ end
1284
+
1285
+ it "should convert strings to #send_describe_prepared" do
1286
+ @conn.prepare("weiß4", "VALUES(123)")
1287
+ @conn.send_describe_prepared("weiß4".encode("utf-16be"))
1288
+ expect( @conn.get_last_result.nfields ).to eq( 1 )
1289
+ end
1290
+
1291
+ it "should convert strings to #send_describe_portal" do
1292
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1293
+ @conn.send_describe_portal("cörsör".encode("utf-16le"))
1294
+ expect( @conn.get_last_result.nfields ).to eq( 3 )
1295
+ end
1296
+
1297
+ it "should convert error string to #put_copy_end" do
1298
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1299
+ @conn.exec( "COPY copytable FROM STDIN" )
1300
+ @conn.put_copy_end("grün".encode("utf-16be"))
1301
+ expect( @conn.get_result.error_message ).to match(/grün/)
1302
+ @conn.get_result
1198
1303
  end
1199
1304
  end
1200
1305
 
@@ -1463,15 +1568,15 @@ describe PG::Connection do
1463
1568
  end
1464
1569
  end
1465
1570
 
1466
- it "can process #copy_data input queries with row encoder" do
1571
+ it "can process #copy_data input queries with row encoder and respects character encoding" do
1467
1572
  @conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1468
1573
  res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1469
1574
  @conn2.put_copy_data [1]
1470
- @conn2.put_copy_data ["2"]
1575
+ @conn2.put_copy_data ["Möhre".encode("utf-16le")]
1471
1576
  end
1472
1577
 
1473
1578
  res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
1474
- expect( res.values ).to eq( [["1"], ["2"]] )
1579
+ expect( res.values ).to eq( [["1"], ["Möhre"]] )
1475
1580
  end
1476
1581
  end
1477
1582
 
@@ -1513,14 +1618,16 @@ describe PG::Connection do
1513
1618
  end
1514
1619
  end
1515
1620
 
1516
- it "can process #copy_data output with row decoder" do
1621
+ it "can process #copy_data output with row decoder and respects character encoding" do
1622
+ @conn2.internal_encoding = Encoding::ISO8859_1
1517
1623
  rows = []
1518
- res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
1624
+ res2 = @conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
1519
1625
  while row=@conn2.get_copy_data
1520
1626
  rows << row
1521
1627
  end
1522
1628
  end
1523
- expect( rows ).to eq( [["1"], ["2"]] )
1629
+ expect( rows.last.last.encoding ).to eq( Encoding::ISO8859_1 )
1630
+ expect( rows ).to eq( [["1"], ["Möhre".encode("iso-8859-1")]] )
1524
1631
  end
1525
1632
 
1526
1633
  it "can type cast #copy_data output with explicit decoder" do