sequel 4.23.0 → 4.24.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +26 -0
  3. data/Rakefile +1 -1
  4. data/doc/release_notes/4.24.0.txt +99 -0
  5. data/doc/sql.rdoc +10 -1
  6. data/lib/sequel/adapters/jdbc.rb +7 -0
  7. data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
  8. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  10. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  11. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  12. data/lib/sequel/adapters/jdbc/mssql.rb +1 -1
  13. data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
  14. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +1 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  17. data/lib/sequel/adapters/postgres.rb +14 -6
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/core.rb +12 -1
  20. data/lib/sequel/database/connecting.rb +1 -2
  21. data/lib/sequel/extensions/pg_inet_ops.rb +200 -0
  22. data/lib/sequel/plugins/association_pks.rb +63 -18
  23. data/lib/sequel/plugins/auto_validations.rb +43 -9
  24. data/lib/sequel/plugins/class_table_inheritance.rb +236 -179
  25. data/lib/sequel/plugins/update_refresh.rb +26 -1
  26. data/lib/sequel/plugins/validation_helpers.rb +7 -2
  27. data/lib/sequel/version.rb +1 -1
  28. data/spec/adapters/oracle_spec.rb +1 -1
  29. data/spec/adapters/postgres_spec.rb +61 -0
  30. data/spec/core_extensions_spec.rb +5 -1
  31. data/spec/extensions/association_pks_spec.rb +73 -1
  32. data/spec/extensions/auto_validations_spec.rb +34 -0
  33. data/spec/extensions/class_table_inheritance_spec.rb +58 -54
  34. data/spec/extensions/pg_inet_ops_spec.rb +101 -0
  35. data/spec/extensions/spec_helper.rb +5 -5
  36. data/spec/extensions/update_refresh_spec.rb +12 -0
  37. data/spec/extensions/validation_helpers_spec.rb +7 -0
  38. data/spec/integration/plugin_test.rb +48 -13
  39. metadata +6 -4
  40. data/lib/sequel/adapters/db2.rb +0 -229
  41. data/lib/sequel/adapters/dbi.rb +0 -102
@@ -0,0 +1,101 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ Sequel.extension :pg_inet_ops
4
+
5
+ describe "Sequel::Postgres::InetOp" do
6
+ before do
7
+ db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
8
+ db.extension :pg_inet
9
+ @ds = db.dataset
10
+ @h = Sequel.pg_inet_op(:h)
11
+ end
12
+
13
+ it "#pg_inet should return self" do
14
+ @h.pg_inet.must_be_same_as(@h)
15
+ end
16
+
17
+ it "Sequel.pg_inet_op should return argument if already an InetOp" do
18
+ Sequel.pg_inet_op(@h).must_be_same_as(@h)
19
+ end
20
+
21
+ it "#pg_inet should return a InetOp for literal strings, and expressions" do
22
+ @ds.literal(Sequel.function(:b, :h).pg_inet.abbrev).must_equal "abbrev(b(h))"
23
+ @ds.literal(Sequel.lit('h').pg_inet.abbrev).must_equal "abbrev(h)"
24
+ end
25
+
26
+ it "should define methods for all of the PostgreSQL inet operators" do
27
+ @ds.literal(@h + @h).must_equal "(h + h)"
28
+ @ds.literal(@h - @h).must_equal "(h - h)"
29
+ @ds.literal(@h << @h).must_equal "(h << h)"
30
+ @ds.literal(@h >> @h).must_equal "(h >> h)"
31
+ @ds.literal(@h & @h).must_equal "(h & h)"
32
+ @ds.literal(@h | @h).must_equal "(h | h)"
33
+ @ds.literal(~@h).must_equal "~h"
34
+
35
+ @ds.literal(@h.contained_by(@h)).must_equal "(h << h)"
36
+ @ds.literal(@h.contained_by_or_equals(@h)).must_equal "(h <<= h)"
37
+ @ds.literal(@h.contains(@h)).must_equal "(h >> h)"
38
+ @ds.literal(@h.contains_or_equals(@h)).must_equal "(h >>= h)"
39
+ @ds.literal(@h.contains_or_contained_by(@h)).must_equal "(h && h)"
40
+ end
41
+
42
+ it "should define methods for all of the PostgreSQL inet functions" do
43
+ @ds.literal(@h.abbrev).must_equal "abbrev(h)"
44
+ @ds.literal(@h.broadcast).must_equal "broadcast(h)"
45
+ @ds.literal(@h.family).must_equal "family(h)"
46
+ @ds.literal(@h.host).must_equal "host(h)"
47
+ @ds.literal(@h.hostmask).must_equal "hostmask(h)"
48
+ @ds.literal(@h.masklen).must_equal "masklen(h)"
49
+ @ds.literal(@h.netmask).must_equal "netmask(h)"
50
+ @ds.literal(@h.network).must_equal "network(h)"
51
+ @ds.literal(@h.set_masklen(16)).must_equal "set_masklen(h, 16)"
52
+ @ds.literal(@h.text).must_equal "text(h)"
53
+ end
54
+
55
+ it "should have operators that return booleans return boolean expressions" do
56
+ @ds.literal((@h << @h) & :b).must_equal "((h << h) AND b)"
57
+ @ds.literal((@h >> @h) & :b).must_equal "((h >> h) AND b)"
58
+
59
+ @ds.literal(@h.contained_by(@h) & :b).must_equal "((h << h) AND b)"
60
+ @ds.literal(@h.contained_by_or_equals(@h) & :b).must_equal "((h <<= h) AND b)"
61
+ @ds.literal(@h.contains(@h) & :b).must_equal "((h >> h) AND b)"
62
+ @ds.literal(@h.contains_or_equals(@h) & :b).must_equal "((h >>= h) AND b)"
63
+ @ds.literal(@h.contains_or_contained_by(@h) & :b).must_equal "((h && h) AND b)"
64
+ end
65
+
66
+ it "should have operators that return inet return InetOp" do
67
+ @ds.literal((@h & @h).contains(:b)).must_equal "((h & h) >> b)"
68
+ @ds.literal((@h | @h).contains(:b)).must_equal "((h | h) >> b)"
69
+ @ds.literal((@h + @h).contains(:b)).must_equal "((h + h) >> b)"
70
+ @ds.literal((@h - 3).contains(:b)).must_equal "((h - 3) >> b)"
71
+ @ds.literal((~@h).contains(:b)).must_equal "(~h >> b)"
72
+ end
73
+
74
+ it "should have - operator with inet op return numeric expression" do
75
+ @ds.literal((@h - @h) / :b).must_equal "((h - h) / b)"
76
+ end
77
+
78
+ it "should have function methods returning int return numeric expressions" do
79
+ @ds.literal(@h.family / 2).must_equal "(family(h) / 2)"
80
+ @ds.literal(@h.masklen / 2).must_equal "(masklen(h) / 2)"
81
+ end
82
+
83
+ it "should have function methods returning text return string expressions" do
84
+ @ds.literal(@h.abbrev + :a).must_equal "(abbrev(h) || a)"
85
+ @ds.literal(@h.host + :a).must_equal "(host(h) || a)"
86
+ @ds.literal(@h.text + :a).must_equal "(text(h) || a)"
87
+ end
88
+
89
+ it "should have function methods returning inet return InetOp" do
90
+ @ds.literal(@h.broadcast.contains(:a)).must_equal "(broadcast(h) >> a)"
91
+ @ds.literal(@h.hostmask.contains(:a)).must_equal "(hostmask(h) >> a)"
92
+ @ds.literal(@h.netmask.contains(:a)).must_equal "(netmask(h) >> a)"
93
+ @ds.literal(@h.network.contains(:a)).must_equal "(network(h) >> a)"
94
+ @ds.literal(@h.set_masklen(16).contains(:a)).must_equal "(set_masklen(h, 16) >> a)"
95
+ end
96
+
97
+ it "should string and IPAddr instances in a cast to inet" do
98
+ @ds.literal(Sequel.pg_inet_op('1.2.3.4').contains(:a)).must_equal "(CAST('1.2.3.4' AS inet) >> a)"
99
+ @ds.literal(Sequel.pg_inet_op(IPAddr.new('1.2.3.4')).contains(:a)).must_equal "(CAST('1.2.3.4/32' AS inet) >> a)"
100
+ end
101
+ end
@@ -1,5 +1,10 @@
1
1
  require 'rubygems'
2
2
 
3
+ gem 'minitest'
4
+ require 'minitest/autorun'
5
+ require 'minitest/hooks/default'
6
+ require 'minitest/shared_description'
7
+
3
8
  if ENV['COVERAGE']
4
9
  require File.join(File.dirname(File.expand_path(__FILE__)), "../sequel_coverage")
5
10
  SimpleCov.sequel_coverage(:filter=>%r{lib/sequel/(extensions|plugins)/\w+\.rb\z})
@@ -29,11 +34,6 @@ def skip_warn(s)
29
34
  warn "Skipping test of #{s}" if ENV["SKIPPED_TEST_WARN"]
30
35
  end
31
36
 
32
- gem 'minitest'
33
- require 'minitest/autorun'
34
- require 'minitest/hooks/default'
35
- require 'minitest/shared_description'
36
-
37
37
  Sequel.quote_identifiers = false
38
38
  Sequel.identifier_input_method = nil
39
39
  Sequel.identifier_output_method = nil
@@ -27,6 +27,18 @@ describe "Sequel::Plugins::UpdateRefresh" do
27
27
  o.name.must_equal 'b'
28
28
  end
29
29
 
30
+ it "should support specifying columns to return" do
31
+ @db.extend_datasets{def supports_returning?(x) true end; def update_sql(*); sql = super; update_returning_sql(sql); sql end}
32
+ @c.plugin :update_refresh, :columns => [ :a ]
33
+ @c.dataset = @db[:test]
34
+ @db.sqls
35
+ o = @c.load(:id=>1, :name=>'a')
36
+ o.save
37
+ @db.sqls.must_equal ["UPDATE test SET name = 'a' WHERE (id = 1) RETURNING a"]
38
+ o.name.must_equal 'b'
39
+ end
40
+
41
+
30
42
  it "should refresh the instance after updating when returning specific columns" do
31
43
  @db.extend_datasets{def supports_returning?(x) true end; def update_sql(*); sql = super; update_returning_sql(sql); sql end}
32
44
  @c.plugin :insert_returning_select
@@ -56,6 +56,13 @@ describe "Sequel::Plugins::ValidationHelpers" do
56
56
  @m.must_be :valid?
57
57
  end
58
58
 
59
+ it "should take a :from=>:values option to lookup in values hash" do
60
+ @c.set_validations{validates_max_length(50, :value, :from=>:values)}
61
+ @c.send(:define_method, :value){super() * 2}
62
+ @m.value = ' ' * 26
63
+ @m.must_be :valid?
64
+ end
65
+
59
66
  it "should allow a proc for the :message option" do
60
67
  @c.set_validations{validates_format(/.+_.+/, :value, :message=>proc{|f| "doesn't match #{f.inspect}"})}
61
68
  @m.value = 'abc_'
@@ -33,21 +33,27 @@ describe "Class Table Inheritance Plugin" do
33
33
  end
34
34
  class ::Executive < Manager
35
35
  end
36
+ class ::Ceo < Executive
37
+ end
36
38
  class ::Staff < Employee
37
39
  many_to_one :manager
38
40
  end
39
41
 
40
- @i1 =@db[:employees].insert(:name=>'E', :kind=>'Employee')
42
+
43
+ @i1 = @db[:employees].insert(:name=>'E', :kind=>'Employee')
41
44
  @i2 = @db[:employees].insert(:name=>'S', :kind=>'Staff')
42
45
  @i3 = @db[:employees].insert(:name=>'M', :kind=>'Manager')
43
- @i4 = @db[:employees].insert(:name=>'Ex', :kind=>'Executive')
44
46
  @db[:managers].insert(:id=>@i3, :num_staff=>7)
47
+ @i4 = @db[:employees].insert(:name=>'Ex', :kind=>'Executive')
45
48
  @db[:managers].insert(:id=>@i4, :num_staff=>5)
46
49
  @db[:executives].insert(:id=>@i4, :num_managers=>6)
50
+ @i5 = @db[:employees].insert(:name=>'C', :kind=>'Ceo')
51
+ @db[:managers].insert(:id=>@i5, :num_staff=>2)
52
+ @db[:executives].insert(:id=>@i5, :num_managers=>1)
47
53
  @db[:staff].insert(:id=>@i2, :manager_id=>@i4)
48
54
  end
49
55
  after do
50
- [:Executive, :Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s)}
56
+ [:Ceo, :Executive, :Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s)}
51
57
  end
52
58
  after(:all) do
53
59
  @db.drop_table? :staff, :executives, :managers, :employees
@@ -58,7 +64,8 @@ describe "Class Table Inheritance Plugin" do
58
64
  Employee.load(:id=>@i1, :name=>'E', :kind=>'Employee'),
59
65
  Staff.load(:id=>@i2, :name=>'S', :kind=>'Staff'),
60
66
  Manager.load(:id=>@i3, :name=>'M', :kind=>'Manager'),
61
- Executive.load(:id=>@i4, :name=>'Ex', :kind=>'Executive')
67
+ Executive.load(:id=>@i4, :name=>'Ex', :kind=>'Executive'),
68
+ Ceo.load(:id=>@i5, :name=>'C', :kind=>'Ceo')
62
69
  ]
63
70
  end
64
71
 
@@ -71,6 +78,8 @@ describe "Class Table Inheritance Plugin" do
71
78
  Employee[@i4].num_staff.must_equal 5
72
79
  Employee[@i4][:num_managers].must_equal nil
73
80
  Employee[@i4].num_managers.must_equal 6
81
+ Employee[@i5][:num_managers].must_equal nil
82
+ Employee[@i5].num_managers.must_equal 1
74
83
  end
75
84
 
76
85
  it "should eagerly load columns in subclass tables when retrieving multiple objects" do
@@ -83,6 +92,8 @@ describe "Class Table Inheritance Plugin" do
83
92
  a[3].num_staff.must_equal 5
84
93
  a[3][:num_managers].must_equal nil
85
94
  a[3].num_managers.must_equal 6
95
+ a[4][:num_managers].must_equal 1
96
+ a[4].num_managers.must_equal 1
86
97
  end
87
98
 
88
99
  it "should include schema for columns for tables for ancestor classes" do
@@ -90,6 +101,7 @@ describe "Class Table Inheritance Plugin" do
90
101
  Staff.db_schema.keys.sort_by{|x| x.to_s}.must_equal [:id, :kind, :manager_id, :name]
91
102
  Manager.db_schema.keys.sort_by{|x| x.to_s}.must_equal [:id, :kind, :name, :num_staff]
92
103
  Executive.db_schema.keys.sort_by{|x| x.to_s}.must_equal [:id, :kind, :name, :num_managers, :num_staff]
104
+ Ceo.db_schema.keys.sort_by{|x| x.to_s}.must_equal [:id, :kind, :name, :num_managers, :num_staff]
93
105
  end
94
106
 
95
107
  it "should include columns for tables for ancestor classes" do
@@ -97,10 +109,11 @@ describe "Class Table Inheritance Plugin" do
97
109
  Staff.columns.must_equal [:id, :name, :kind, :manager_id]
98
110
  Manager.columns.must_equal [:id, :name, :kind, :num_staff]
99
111
  Executive.columns.must_equal [:id, :name, :kind, :num_staff, :num_managers]
112
+ Ceo.columns.must_equal [:id, :name, :kind, :num_staff, :num_managers]
100
113
  end
101
114
 
102
115
  it "should delete rows from all tables" do
103
- e = Executive.first
116
+ e = Ceo.first
104
117
  i = e.id
105
118
  e.staff_members_dataset.destroy
106
119
  e.destroy
@@ -114,9 +127,9 @@ describe "Class Table Inheritance Plugin" do
114
127
  end
115
128
 
116
129
  it "should insert rows into all tables" do
117
- e = Executive.create(:name=>'Ex2', :num_managers=>8, :num_staff=>9)
130
+ e = Ceo.create(:name=>'Ex2', :num_managers=>8, :num_staff=>9)
118
131
  i = e.id
119
- @db[:employees][:id=>i].must_equal(:id=>i, :name=>'Ex2', :kind=>'Executive')
132
+ @db[:employees][:id=>i].must_equal(:id=>i, :name=>'Ex2', :kind=>'Ceo')
120
133
  @db[:managers][:id=>i].must_equal(:id=>i, :num_staff=>9)
121
134
  @db[:executives][:id=>i].must_equal(:id=>i, :num_managers=>8)
122
135
  end
@@ -1280,20 +1293,42 @@ describe "AssociationPks plugin" do
1280
1293
  Vocalist.order(:first, :last).all.map{|a| a.hit_pks.sort}.must_equal [[@h1, @h2, @h3], [@h2], []]
1281
1294
  end
1282
1295
 
1296
+ it "should handle :delay association option for new instances" do
1297
+ album_class = Class.new(Album)
1298
+ album_class.many_to_many :tags, :clone=>:tags, :delay_pks=>true, :join_table=>:albums_tags, :left_key=>:album_id
1299
+ album = album_class.new(:name=>'test album')
1300
+ album.tag_pks.must_equal []
1301
+ album.tag_pks = [@t1, @t2]
1302
+ album.tag_pks.must_equal [@t1, @t2]
1303
+ album.save
1304
+ album_class.with_pk!(album.pk).tag_pks.sort.must_equal [@t1, @t2]
1305
+ end
1306
+
1307
+ it "should handle :delay=>:all association option for existing instances" do
1308
+ album_class = Class.new(Album)
1309
+ album_class.many_to_many :tags, :clone=>:tags, :delay_pks=>:always, :join_table=>:albums_tags, :left_key=>:album_id
1310
+ album = album_class.with_pk!(@al1)
1311
+ album.tag_pks.sort.must_equal [@t1, @t2, @t3]
1312
+ album.tag_pks = [@t1, @t2]
1313
+ album.tag_pks.must_equal [@t1, @t2]
1314
+ album.save_changes
1315
+ album_class.with_pk!(album.pk).tag_pks.sort.must_equal [@t1, @t2]
1316
+ end
1317
+
1283
1318
  it "should set associated pks correctly for a one_to_many association" do
1284
1319
  Artist.use_transactions = true
1285
1320
  Album.order(:id).select_map(:artist_id).must_equal [@ar1, @ar1, @ar1]
1286
1321
 
1287
- Artist[@ar2].album_pks = [@t1, @t3]
1288
- Artist[@ar1].album_pks.must_equal [@t2]
1322
+ Artist[@ar2].album_pks = [@al1, @al3]
1323
+ Artist[@ar1].album_pks.must_equal [@al2]
1289
1324
  Album.order(:id).select_map(:artist_id).must_equal [@ar2, @ar1, @ar2]
1290
1325
 
1291
- Artist[@ar1].album_pks = [@t1]
1292
- Artist[@ar2].album_pks.must_equal [@t3]
1326
+ Artist[@ar1].album_pks = [@al1]
1327
+ Artist[@ar2].album_pks.must_equal [@al3]
1293
1328
  Album.order(:id).select_map(:artist_id).must_equal [@ar1, nil, @ar2]
1294
1329
 
1295
- Artist[@ar1].album_pks = [@t1, @t2]
1296
- Artist[@ar2].album_pks.must_equal [@t3]
1330
+ Artist[@ar1].album_pks = [@al1, @al2]
1331
+ Artist[@ar2].album_pks.must_equal [@al3]
1297
1332
  Album.order(:id).select_map(:artist_id).must_equal [@ar1, @ar1, @ar2]
1298
1333
  end
1299
1334
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.23.0
4
+ version: 4.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-01 00:00:00.000000000 Z
11
+ date: 2015-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -224,6 +224,7 @@ extra_rdoc_files:
224
224
  - doc/release_notes/4.21.0.txt
225
225
  - doc/release_notes/4.22.0.txt
226
226
  - doc/release_notes/4.23.0.txt
227
+ - doc/release_notes/4.24.0.txt
227
228
  files:
228
229
  - CHANGELOG
229
230
  - MIT-LICENSE
@@ -335,6 +336,7 @@ files:
335
336
  - doc/release_notes/4.21.0.txt
336
337
  - doc/release_notes/4.22.0.txt
337
338
  - doc/release_notes/4.23.0.txt
339
+ - doc/release_notes/4.24.0.txt
338
340
  - doc/release_notes/4.3.0.txt
339
341
  - doc/release_notes/4.4.0.txt
340
342
  - doc/release_notes/4.5.0.txt
@@ -357,8 +359,6 @@ files:
357
359
  - lib/sequel/adapters/ado/mssql.rb
358
360
  - lib/sequel/adapters/amalgalite.rb
359
361
  - lib/sequel/adapters/cubrid.rb
360
- - lib/sequel/adapters/db2.rb
361
- - lib/sequel/adapters/dbi.rb
362
362
  - lib/sequel/adapters/do.rb
363
363
  - lib/sequel/adapters/do/mysql.rb
364
364
  - lib/sequel/adapters/do/postgres.rb
@@ -483,6 +483,7 @@ files:
483
483
  - lib/sequel/extensions/pg_hstore.rb
484
484
  - lib/sequel/extensions/pg_hstore_ops.rb
485
485
  - lib/sequel/extensions/pg_inet.rb
486
+ - lib/sequel/extensions/pg_inet_ops.rb
486
487
  - lib/sequel/extensions/pg_interval.rb
487
488
  - lib/sequel/extensions/pg_json.rb
488
489
  - lib/sequel/extensions/pg_json_ops.rb
@@ -681,6 +682,7 @@ files:
681
682
  - spec/extensions/pg_enum_spec.rb
682
683
  - spec/extensions/pg_hstore_ops_spec.rb
683
684
  - spec/extensions/pg_hstore_spec.rb
685
+ - spec/extensions/pg_inet_ops_spec.rb
684
686
  - spec/extensions/pg_inet_spec.rb
685
687
  - spec/extensions/pg_interval_spec.rb
686
688
  - spec/extensions/pg_json_ops_spec.rb
@@ -1,229 +0,0 @@
1
- require 'db2/db2cli'
2
- Sequel.require %w'shared/db2', 'adapters'
3
- Sequel::Deprecation.deprecate 'The db2 adapter is deprecated and will be removed in a future version of Sequel. Please switch to the ibmdb adapter.'
4
-
5
- module Sequel
6
- module DB2
7
-
8
- @convert_smallint_to_bool = true
9
-
10
- # Underlying error raised by Sequel, since ruby-db2 doesn't
11
- # use exceptions.
12
- class DB2Error < StandardError
13
- end
14
-
15
- class << self
16
- # Whether to convert smallint values to bool, true by default.
17
- # Can also be overridden per dataset.
18
- attr_accessor :convert_smallint_to_bool
19
- end
20
-
21
- tt = Class.new do
22
- def boolean(s) !s.to_i.zero? end
23
- def date(s) Date.new(s.year, s.month, s.day) end
24
- def time(s) Sequel::SQLTime.create(s.hour, s.minute, s.second) end
25
- end.new
26
-
27
- # Hash holding type translation methods, used by Dataset#fetch_rows.
28
- DB2_TYPES = {
29
- :boolean => tt.method(:boolean),
30
- DB2CLI::SQL_BLOB => ::Sequel::SQL::Blob.method(:new),
31
- DB2CLI::SQL_TYPE_DATE => tt.method(:date),
32
- DB2CLI::SQL_TYPE_TIME => tt.method(:time),
33
- DB2CLI::SQL_DECIMAL => ::BigDecimal.method(:new)
34
- }
35
-
36
- class Database < Sequel::Database
37
- include DatabaseMethods
38
-
39
- set_adapter_scheme :db2
40
-
41
- TEMPORARY = 'GLOBAL TEMPORARY '.freeze
42
- _, NullHandle = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_ENV, DB2CLI::SQL_NULL_HANDLE)
43
-
44
- # Hash of connection procs for converting
45
- attr_reader :conversion_procs
46
-
47
- def connect(server)
48
- opts = server_opts(server)
49
- dbc = checked_error("Could not allocate database connection"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_DBC, NullHandle)}
50
- checked_error("Could not connect to database"){DB2CLI.SQLConnect(dbc, opts[:database], opts[:user], opts[:password])}
51
- dbc
52
- end
53
-
54
- def disconnect_connection(conn)
55
- DB2CLI.SQLDisconnect(conn)
56
- DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_DBC, conn)
57
- end
58
-
59
- def execute(sql, opts=OPTS, &block)
60
- synchronize(opts[:server]){|conn| log_connection_execute(conn, sql, &block)}
61
- end
62
-
63
- def execute_insert(sql, opts=OPTS)
64
- synchronize(opts[:server]) do |conn|
65
- log_connection_execute(conn, sql)
66
- sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
67
- log_connection_execute(conn, sql) do |sth|
68
- _, _, datatype, size, _, _ = checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, 1, 256)}
69
- if DB2CLI.SQLFetch(sth) != DB2CLI::SQL_NO_DATA_FOUND
70
- v, _ = checked_error("Could not get data"){DB2CLI.SQLGetData(sth, 1, datatype, size)}
71
- if v.is_a?(String)
72
- return v.to_i
73
- else
74
- return nil
75
- end
76
- end
77
- end
78
- end
79
- end
80
-
81
- ERROR_MAP = {}
82
- %w'SQL_INVALID_HANDLE SQL_STILL_EXECUTING SQL_ERROR'.each do |s|
83
- ERROR_MAP[DB2CLI.const_get(s)] = s
84
- end
85
- def check_error(rc, msg)
86
- case rc
87
- when DB2CLI::SQL_SUCCESS, DB2CLI::SQL_SUCCESS_WITH_INFO, DB2CLI::SQL_NO_DATA_FOUND
88
- nil
89
- when DB2CLI::SQL_INVALID_HANDLE, DB2CLI::SQL_STILL_EXECUTING
90
- e = DB2Error.new("#{ERROR_MAP[rc]}: #{msg}")
91
- e.set_backtrace(caller)
92
- raise_error(e, :disconnect=>true)
93
- else
94
- e = DB2Error.new("#{ERROR_MAP[rc] || "Error code #{rc}"}: #{msg}")
95
- e.set_backtrace(caller)
96
- raise_error(e, :disconnect=>true)
97
- end
98
- end
99
-
100
- def checked_error(msg)
101
- rc, *ary= yield
102
- check_error(rc, msg)
103
- ary.length <= 1 ? ary.first : ary
104
- end
105
-
106
- def to_application_timestamp_db2(v)
107
- to_application_timestamp(v.to_s)
108
- end
109
-
110
- private
111
-
112
- def adapter_initialize
113
- @conversion_procs = DB2_TYPES.dup
114
- @conversion_procs[DB2CLI::SQL_TYPE_TIMESTAMP] = method(:to_application_timestamp_db2)
115
- end
116
-
117
- def database_error_classes
118
- [DB2Error]
119
- end
120
-
121
- def begin_transaction(conn, opts=OPTS)
122
- log_yield(TRANSACTION_BEGIN){DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_OFF)}
123
- set_transaction_isolation(conn, opts)
124
- end
125
-
126
- def remove_transaction(conn, committed)
127
- DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_ON)
128
- ensure
129
- super
130
- end
131
-
132
- def rollback_transaction(conn, opts=OPTS)
133
- log_yield(TRANSACTION_ROLLBACK){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_ROLLBACK)}
134
- end
135
-
136
- def commit_transaction(conn, opts=OPTS)
137
- log_yield(TRANSACTION_COMMIT){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_COMMIT)}
138
- end
139
-
140
- def log_connection_execute(conn, sql)
141
- sth = checked_error("Could not allocate statement"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_STMT, conn)}
142
-
143
- begin
144
- checked_error("Could not execute statement: #{sql}"){log_yield(sql){DB2CLI.SQLExecDirect(sth, sql)}}
145
-
146
- if block_given?
147
- yield(sth)
148
- else
149
- checked_error("Could not get RPC"){DB2CLI.SQLRowCount(sth)}
150
- end
151
- ensure
152
- checked_error("Could not free statement"){DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_STMT, sth)}
153
- end
154
- end
155
-
156
- # Convert smallint type to boolean if convert_smallint_to_bool is true
157
- def schema_column_type(db_type)
158
- if DB2.convert_smallint_to_bool && db_type =~ /smallint/i
159
- :boolean
160
- else
161
- super
162
- end
163
- end
164
- end
165
-
166
- class Dataset < Sequel::Dataset
167
- include DatasetMethods
168
-
169
- Database::DatasetClass = self
170
- MAX_COL_SIZE = 256
171
-
172
- # Whether to convert smallint to boolean arguments for this dataset.
173
- # Defaults to the DB2 module setting.
174
- def convert_smallint_to_bool
175
- defined?(@convert_smallint_to_bool) ? @convert_smallint_to_bool : (@convert_smallint_to_bool = DB2.convert_smallint_to_bool)
176
- end
177
-
178
- # Override the default DB2.convert_smallint_to_bool setting for this dataset.
179
- attr_writer :convert_smallint_to_bool
180
-
181
- def fetch_rows(sql)
182
- execute(sql) do |sth|
183
- db = @db
184
- column_info = get_column_info(sth)
185
- cols = column_info.map{|c| c.at(1)}
186
- @columns = cols
187
- errors = [DB2CLI::SQL_NO_DATA_FOUND, DB2CLI::SQL_ERROR]
188
- until errors.include?(rc = DB2CLI.SQLFetch(sth))
189
- db.check_error(rc, "Could not fetch row")
190
- row = {}
191
- column_info.each do |i, c, t, s, pr|
192
- v, _ = db.checked_error("Could not get data"){DB2CLI.SQLGetData(sth, i, t, s)}
193
- row[c] = if v == DB2CLI::Null
194
- nil
195
- elsif pr
196
- pr.call(v)
197
- else
198
- v
199
- end
200
- end
201
- yield row
202
- end
203
- end
204
- self
205
- end
206
-
207
- private
208
-
209
- def get_column_info(sth)
210
- db = @db
211
- column_count = db.checked_error("Could not get number of result columns"){DB2CLI.SQLNumResultCols(sth)}
212
- convert = convert_smallint_to_bool
213
- cps = db.conversion_procs
214
-
215
- (1..column_count).map do |i|
216
- name, _, datatype, size, digits, _ = db.checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, i, MAX_COL_SIZE)}
217
- pr = if datatype == DB2CLI::SQL_SMALLINT && convert && size <= 5 && digits <= 1
218
- cps[:boolean]
219
- elsif datatype == DB2CLI::SQL_CLOB && Sequel::DB2.use_clob_as_blob
220
- cps[DB2CLI::SQL_BLOB]
221
- else
222
- cps[datatype]
223
- end
224
- [i, output_identifier(name), datatype, size, pr]
225
- end
226
- end
227
- end
228
- end
229
- end