sequel 4.23.0 → 4.24.0

Sign up to get free protection for your applications and to get access to all the features.
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