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

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.
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