pg 0.18.0.pre20141017160319 → 0.18.0.pre20141117110243

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.
data/ext/util.c CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * util.c - Utils for ruby-pg
3
- * $Id: util.c,v 117fb5c5eed7 2014/10/15 18:36:39 lars $
3
+ * $Id$
4
4
  *
5
5
  */
6
6
 
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'] = "#{old_path};#{File.expand_path("../#{RUBY_PLATFORM}", __FILE__)}"
13
+ ENV['PATH'] = "#{File.expand_path("../#{RUBY_PLATFORM}", __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.0'
27
+ VERSION = '0.18.0.pre20141117110243'
28
28
 
29
29
  # VCS revision
30
- REVISION = %q$Revision: 57d770944b5d $
30
+ REVISION = %q$Revision$
31
31
 
32
32
  class NotAllCopyDataRetrieved < PG::Error
33
33
  end
@@ -304,7 +304,7 @@ end
304
304
  # # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
305
305
  # # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
306
306
  # res = conn.exec_params( "SELECT $1", [5] )
307
- class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
307
+ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
308
308
  include PG::BasicTypeRegistry
309
309
 
310
310
  def initialize(connection)
@@ -323,7 +323,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
323
323
  end
324
324
 
325
325
  def populate_encoder_list
326
- DEFAULT_TYPE_MAP.each do |mri_type, selector|
326
+ DEFAULT_TYPE_MAP.each do |klass, selector|
327
327
  if Array === selector
328
328
  format, name, oid_name = selector
329
329
  coder = coder_by_name(format, :encoder, name).dup
@@ -332,9 +332,9 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
332
332
  else
333
333
  coder.oid = 0
334
334
  end
335
- self[mri_type] = coder
335
+ self[klass] = coder
336
336
  else
337
- self[mri_type] = selector
337
+ self[klass] = selector
338
338
  end
339
339
  end
340
340
  end
@@ -351,25 +351,25 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
351
351
  while elem.kind_of?(Array)
352
352
  elem = elem.first
353
353
  end
354
- @array_encoders_by_klass[elem.class] || @anyarray_encoder
354
+ @array_encoders_by_klass[elem.class] ||
355
+ elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
356
+ @anyarray_encoder
355
357
  end
356
358
 
357
359
  DEFAULT_TYPE_MAP = {
358
- 'T_TRUE'.freeze => [1, 'bool', 'bool'],
359
- 'T_FALSE'.freeze => [1, 'bool', 'bool'],
360
+ TrueClass => [1, 'bool', 'bool'],
361
+ FalseClass => [1, 'bool', 'bool'],
360
362
  # We use text format and no type OID for numbers, because setting the OID can lead
361
363
  # to unnecessary type conversions on server side.
362
- 'T_FIXNUM'.freeze => [0, 'int8'],
363
- 'T_BIGNUM'.freeze => [0, 'int8'],
364
- 'T_FLOAT'.freeze => [0, 'float8'],
365
- 'T_ARRAY'.freeze => :get_array_type,
364
+ Integer => [0, 'int8'],
365
+ Float => [0, 'float8'],
366
+ Array => :get_array_type,
366
367
  }
367
368
 
368
369
  DEFAULT_ARRAY_TYPE_MAP = {
369
370
  TrueClass => [0, '_bool'],
370
371
  FalseClass => [0, '_bool'],
371
- Fixnum => [0, '_int8'],
372
- Bignum => [0, '_int8'],
372
+ Integer => [0, '_int8'],
373
373
  String => [0, '_text'],
374
374
  Float => [0, '_float8'],
375
375
  }
@@ -3,6 +3,15 @@
3
3
  module PG
4
4
 
5
5
  class Coder
6
+
7
+ module BinaryFormatting
8
+ Params = { format: 1 }
9
+ def initialize( params={} )
10
+ super(params.merge(Params))
11
+ end
12
+ end
13
+
14
+
6
15
  # Create a new coder object based on the attribute Hash.
7
16
  def initialize(params={})
8
17
  params.each do |key, val|
@@ -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
@@ -343,11 +343,13 @@ RSpec.configure do |config|
343
343
  PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
344
344
 
345
345
  if !PG.respond_to?( :library_version )
346
- config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93 )
346
+ config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93, :postgresql_94 )
347
347
  elsif PG.library_version < 90200
348
- config.filter_run_excluding( :postgresql_92, :postgresql_93 )
348
+ config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94 )
349
349
  elsif PG.library_version < 90300
350
- config.filter_run_excluding( :postgresql_93 )
350
+ config.filter_run_excluding( :postgresql_93, :postgresql_94 )
351
+ elsif PG.library_version < 90400
352
+ config.filter_run_excluding( :postgresql_94 )
351
353
  end
352
354
  end
353
355
 
@@ -73,7 +73,7 @@ describe 'Basic type mapping' do
73
73
  end
74
74
 
75
75
  after :each do
76
- @conn.type_map_for_results = nil
76
+ @conn.type_map_for_results = PG::TypeMapAllStrings.new
77
77
  end
78
78
 
79
79
  it "should do boolean type conversions" do
@@ -157,6 +157,17 @@ describe PG::Connection do
157
157
  expect( res[0]['n'] ).to eq( '1' )
158
158
  end
159
159
 
160
+ it "can retrieve it's connection parameters for the established connection" do
161
+ expect( @conn.db ).to eq( "test" )
162
+ expect( @conn.user ).to be_a_kind_of( String )
163
+ expect( @conn.pass ).to eq( "" )
164
+ expect( @conn.host ).to eq( "localhost" )
165
+ # TODO: Not sure why libpq returns a NULL ptr instead of "127.0.0.1"
166
+ expect( @conn.hostaddr ).to eq( nil ) if @conn.server_version >= 9_04_00
167
+ expect( @conn.port ).to eq( 54321 )
168
+ expect( @conn.tty ).to eq( "" )
169
+ expect( @conn.options ).to eq( "" )
170
+ end
160
171
 
161
172
  EXPECTED_TRACE_OUTPUT = %{
162
173
  To backend> Msg Q
@@ -1293,8 +1304,8 @@ describe PG::Connection do
1293
1304
  end
1294
1305
 
1295
1306
  it "should return nil if no type mapping is set" do
1296
- expect( @conn.type_map_for_queries ).to be_nil
1297
- expect( @conn.type_map_for_results ).to be_nil
1307
+ expect( @conn.type_map_for_queries ).to be_kind_of(PG::TypeMapAllStrings)
1308
+ expect( @conn.type_map_for_results ).to be_kind_of(PG::TypeMapAllStrings)
1298
1309
  end
1299
1310
 
1300
1311
  it "shouldn't type map params unless requested" do
@@ -1331,8 +1342,8 @@ describe PG::Connection do
1331
1342
  context "with default query type map" do
1332
1343
  before :each do
1333
1344
  @conn2 = described_class.new(@conninfo)
1334
- tm = PG::TypeMapByMriType.new
1335
- tm['T_FIXNUM'] = PG::TextEncoder::Integer.new oid: 20
1345
+ tm = PG::TypeMapByClass.new
1346
+ tm[Integer] = PG::TextEncoder::Integer.new oid: 20
1336
1347
  @conn2.type_map_for_queries = tm
1337
1348
 
1338
1349
  row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
@@ -1349,7 +1360,7 @@ describe PG::Connection do
1349
1360
  end
1350
1361
 
1351
1362
  it "should return the current type mapping" do
1352
- expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByMriType)
1363
+ expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByClass)
1353
1364
  end
1354
1365
 
1355
1366
  it "should work with arbitrary number of params in conjunction with type casting" do
@@ -21,6 +21,80 @@ describe PG::Result do
21
21
  expect( list ).to eq [['1', '2']]
22
22
  end
23
23
 
24
+ it "yields a row as an Enumerator" do
25
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
26
+ e = res.each_row
27
+ expect( e ).to be_a_kind_of(Enumerator)
28
+ pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
29
+ expect( e.size ).to eq( 1 )
30
+ expect( e.to_a ).to eq [['1', '2']]
31
+ end
32
+
33
+ it "yields a row as an Enumerator of hashs" do
34
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
35
+ e = res.each
36
+ expect( e ).to be_a_kind_of(Enumerator)
37
+ pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
38
+ expect( e.size ).to eq( 1 )
39
+ expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
40
+ end
41
+
42
+ context "result streaming", :postgresql_92 do
43
+ it "can iterate over all tuples in single row mode" do
44
+ @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
45
+ @conn.set_single_row_mode
46
+ expect(
47
+ @conn.get_result.stream_each.to_a
48
+ ).to eq(
49
+ [{'a'=>"2"}, {'a'=>"3"}, {'a'=>"4"}]
50
+ )
51
+ expect(
52
+ @conn.get_result.enum_for(:stream_each).to_a
53
+ ).to eq(
54
+ [{'b'=>"1", 'c'=>"5"}, {'b'=>"1", 'c'=>"6"}]
55
+ )
56
+ expect( @conn.get_result ).to be_nil
57
+ end
58
+
59
+ it "can iterate over all rows in single row mode" do
60
+ @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
61
+ @conn.set_single_row_mode
62
+ expect(
63
+ @conn.get_result.enum_for(:stream_each_row).to_a
64
+ ).to eq(
65
+ [["2"], ["3"], ["4"]]
66
+ )
67
+ expect(
68
+ @conn.get_result.stream_each_row.to_a
69
+ ).to eq(
70
+ [["1", "5"], ["1", "6"]]
71
+ )
72
+ expect( @conn.get_result ).to be_nil
73
+ end
74
+
75
+ it "complains when not in single row mode" do
76
+ @conn.send_query( "SELECT generate_series(2,4)" )
77
+ expect{
78
+ @conn.get_result.stream_each_row.to_a
79
+ }.to raise_error(PG::InvalidResultStatus, /not in single row mode/)
80
+ end
81
+
82
+ it "complains when intersected with get_result" do
83
+ @conn.send_query( "SELECT 1" )
84
+ @conn.set_single_row_mode
85
+ expect{
86
+ @conn.get_result.stream_each_row.each{ @conn.get_result }
87
+ }.to raise_error(PG::NoResultError, /no result received/)
88
+ end
89
+
90
+ it "raises server errors" do
91
+ @conn.send_query( "SELECT 0/0" )
92
+ expect{
93
+ @conn.get_result.stream_each_row.to_a
94
+ }.to raise_error(PG::DivisionByZero)
95
+ end
96
+ end
97
+
24
98
  it "inserts nil AS NULL and return NULL as nil" do
25
99
  res = @conn.exec("SELECT $1::int AS n", [nil])
26
100
  expect( res[0]['n'] ).to be_nil()
@@ -335,14 +409,14 @@ describe PG::Result do
335
409
 
336
410
  it "should allow reading, assigning and diabling type conversions" do
337
411
  res = @conn.exec( "SELECT 123" )
338
- expect( res.type_map ).to be_nil
412
+ expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
339
413
  res.type_map = PG::TypeMapByColumn.new [textdec_int]
340
414
  expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
341
415
  expect( res.type_map.coders ).to eq( [textdec_int] )
342
416
  res.type_map = PG::TypeMapByColumn.new [textdec_float]
343
417
  expect( res.type_map.coders ).to eq( [textdec_float] )
344
- res.type_map = nil
345
- expect( res.type_map ).to be_nil
418
+ res.type_map = PG::TypeMapAllStrings.new
419
+ expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
346
420
  end
347
421
 
348
422
  it "should be applied to all value retrieving methods" do
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env rspec
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ require 'pg'
7
+
8
+
9
+ describe PG::TypeMapByClass do
10
+
11
+ let!(:textenc_int){ PG::TextEncoder::Integer.new name: 'INT4', oid: 23 }
12
+ let!(:textenc_float){ PG::TextEncoder::Float.new name: 'FLOAT8', oid: 701 }
13
+ let!(:textenc_string){ PG::TextEncoder::String.new name: 'TEXT', oid: 25 }
14
+ let!(:binaryenc_int){ PG::BinaryEncoder::Int8.new name: 'INT8', oid: 20, format: 1 }
15
+ let!(:pass_through_type) do
16
+ type = Class.new(PG::SimpleEncoder) do
17
+ def encode(*v)
18
+ v.inspect
19
+ end
20
+ end.new
21
+ type.oid = 25
22
+ type.format = 0
23
+ type.name = 'pass_through'
24
+ type
25
+ end
26
+
27
+ let!(:tm) do
28
+ tm = PG::TypeMapByClass.new
29
+ tm[Integer] = binaryenc_int
30
+ tm[Float] = textenc_float
31
+ tm[Symbol] = pass_through_type
32
+ tm
33
+ end
34
+
35
+ let!(:raise_class) do
36
+ Class.new
37
+ end
38
+
39
+ let!(:derived_tm) do
40
+ tm = Class.new(PG::TypeMapByClass) do
41
+ def array_type_map_for(value)
42
+ PG::TextEncoder::Array.new name: '_INT4', oid: 1007, elements_type: PG::TextEncoder::Integer.new
43
+ end
44
+ end.new
45
+ tm[Integer] = proc{|value| textenc_int }
46
+ tm[raise_class] = proc{|value| /invalid/ }
47
+ tm[Array] = :array_type_map_for
48
+ tm
49
+ end
50
+
51
+ it "should retrieve all conversions" do
52
+ expect( tm.coders ).to eq( {
53
+ Integer => binaryenc_int,
54
+ Float => textenc_float,
55
+ Symbol => pass_through_type,
56
+ } )
57
+ end
58
+
59
+ it "should retrieve particular conversions" do
60
+ expect( tm[Integer] ).to eq(binaryenc_int)
61
+ expect( tm[Float] ).to eq(textenc_float)
62
+ expect( tm[Bignum] ).to be_nil
63
+ expect( derived_tm[raise_class] ).to be_kind_of(Proc)
64
+ expect( derived_tm[Array] ).to eq(:array_type_map_for)
65
+ end
66
+
67
+ it "should allow deletion of coders" do
68
+ tm[Integer] = nil
69
+ expect( tm[Integer] ).to be_nil
70
+ expect( tm.coders ).to eq( {
71
+ Float => textenc_float,
72
+ Symbol => pass_through_type,
73
+ } )
74
+ end
75
+
76
+ it "forwards query param conversions to the #default_type_map" do
77
+ tm1 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] )
78
+
79
+ tm2 = PG::TypeMapByClass.new
80
+ tm2[Integer] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
81
+ tm2.default_type_map = tm1
82
+
83
+ res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", ['1', 2, 3], 0, tm2 )
84
+
85
+ expect( res.ftype(0) ).to eq( 23 ) # tm1
86
+ expect( res.ftype(1) ).to eq( 21 ) # tm2
87
+ expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
88
+ end
89
+
90
+ #
91
+ # Decoding Examples
92
+ #
93
+
94
+ it "should raise an error when used for results" do
95
+ res = @conn.exec_params( "SELECT 1", [], 1 )
96
+ expect{ res.type_map = tm }.to raise_error(NotImplementedError, /not suitable to map result values/)
97
+ end
98
+
99
+ #
100
+ # Encoding Examples
101
+ #
102
+
103
+ it "should allow mixed type conversions" do
104
+ res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
105
+ expect( res.values ).to eq([['5', '1.23', '[:TestSymbol]']])
106
+ expect( res.ftype(0) ).to eq(20)
107
+ end
108
+
109
+ it "should expire the cache after changes to the coders" do
110
+ res = @conn.exec_params( "SELECT $1", [5], 0, tm )
111
+ expect( res.ftype(0) ).to eq(20)
112
+
113
+ tm[Integer] = textenc_int
114
+
115
+ res = @conn.exec_params( "SELECT $1", [5], 0, tm )
116
+ expect( res.ftype(0) ).to eq(23)
117
+ end
118
+
119
+ it "should allow mixed type conversions with derived type map" do
120
+ res = @conn.exec_params( "SELECT $1, $2", [6, [7]], 0, derived_tm )
121
+ expect( res.values ).to eq([['6', '{7}']])
122
+ expect( res.ftype(0) ).to eq(23)
123
+ expect( res.ftype(1) ).to eq(1007)
124
+ end
125
+
126
+ it "should raise TypeError with derived type map" do
127
+ expect{
128
+ @conn.exec_params( "SELECT $1", [raise_class.new], 0, derived_tm )
129
+ }.to raise_error(TypeError, /invalid type Regexp/)
130
+ end
131
+
132
+ it "should raise error on invalid coder object" do
133
+ tm[TrueClass] = "dummy"
134
+ expect{
135
+ res = @conn.exec_params( "SELECT $1", [true], 0, tm )
136
+ }.to raise_error(NoMethodError, /undefined method.*call.*dummy/)
137
+ end
138
+ end