sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -0,0 +1,97 @@
1
+ = Database Transactions
2
+
3
+ Sequel uses autocommit mode by default for all of its database adapters, so in general in Sequel if you want to use database transactions, you need to be explicit about it. There are a few cases where transactions are used implicitly by default:
4
+
5
+ * Dataset#import to insert many records at once
6
+ * Model#save
7
+ * Model#destroy
8
+ * Migrations
9
+ * A few model plugins
10
+
11
+ Everywhere else, it is up to use to use a database transaction if you want to.
12
+
13
+ == Basic Transaction Usage
14
+
15
+ In Sequel, the <tt>Database#transaction</tt> method should be called if you want to use a database transaction. This method must be called with a block. If the block does not raise an exception, the transaction is committed:
16
+
17
+ DB.transaction do # BEGIN
18
+ DB[:foo].insert(1) # INSERT
19
+ end # COMMIT
20
+
21
+ If the block raises a Sequel::Rollback exception, the transaction is rolled back, but no exception is raised outside the block:
22
+
23
+ DB.transaction do # BEGIN
24
+ raise Sequel::Rollback
25
+ end # ROLLBACK
26
+ # no exception raised
27
+
28
+ If any other exception is raised, the transaction is rolled back, and the exception is raised outside the block:
29
+
30
+ # ArgumentError raised
31
+ DB.transaction do # BEGIN
32
+ raise ArgumentError
33
+ end # ROLLBACK
34
+ # ArgumentError raised
35
+
36
+ == Nested Transaction Calls / Savepoints
37
+
38
+ You can nest calls to transaction, which by default just reuses the existing transaction:
39
+
40
+ DB.transaction do # BEGIN
41
+ DB.transaction do
42
+ DB[:foo].insert(1) # INSERT
43
+ end
44
+ end # COMMIT
45
+
46
+ You can use the <tt>:savepoint => true</tt> option in the inner transaction to explicitly use a savepoint (if the database supports it):
47
+
48
+ DB.transaction do # BEGIN
49
+ DB.transaction(:savepoint => true) do # SAVEPOINT
50
+ DB[:foo].insert(1) # INSERT
51
+ end # RELEASE SAVEPOINT
52
+ end # COMMIT
53
+
54
+ If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
55
+
56
+ DB.transaction do # BEGIN
57
+ DB.transaction(:savepoint => true) do # SAVEPOINT
58
+ raise Sequel::Rollback
59
+ end # ROLLBACK TO SAVEPOINT
60
+ # no exception raised
61
+ end # COMMIT
62
+
63
+ Other exceptions, unless rescued inside the outer transaction block, will rollback the savepoint and the outer transactions, since they are reraised by the transaction code:
64
+
65
+ DB.transaction do # BEGIN
66
+ DB.transaction(:savepoint => true) do # SAVEPOINT
67
+ raise ArgumentError
68
+ end # ROLLBACK TO SAVEPOINT
69
+ end # ROLLBACK
70
+ # ArgumentError raised
71
+
72
+ == Prepared Transactions / Two-Phase Commit
73
+
74
+ Sequel supports database prepared transactions on PostreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
75
+
76
+ To use prepared transactions in Sequel, you provide a string as the value of the :prepare option:
77
+
78
+ DB.transaction(:prepare => 'foo') do # BEGIN
79
+ DB[:foo].insert(1) # INSERT
80
+ end # PREPARE TRANSACTION 'foo'
81
+
82
+ Later, you can commit the prepared transaction:
83
+
84
+ DB.commit_prepared_transaction('foo')
85
+
86
+ or roll the prepared transaction back:
87
+
88
+ DB.rollback_prepared_transaction('foo')
89
+
90
+ == Transaction Isolation Levels
91
+
92
+ The SQL standard supports 4 isolation levels: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE. Not all databases implement the levels as specified in the standard (or implement the levels at all), but on PostgreSQL, MySQL, and Microsoft SQL Server, you can specify which transaction isolation level you want to use via the :isolation option to <tt>Database#transaction</tt>. The isolation level is specified as one of the following symbols: :uncommitted, :committed, :repeatable, and :serializable. Using this option make Sequel use the correct transaction isolation syntax for your database:
93
+
94
+ DB.transaction(:isolation => :serializable) do # BEGIN
95
+ # SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
96
+ DB[:foo].insert(1) # INSERT
97
+ end # COMMIT
@@ -40,7 +40,7 @@ available. For example, you are no longer able to do:
40
40
  # WHERE a AND (b OR NOT c)
41
41
 
42
42
  Because Symbol#&, Symbol#| and Symbol#~ are not defined when the core
43
- extensions are turned off. However, with virtual rows allow almost the same
43
+ extensions are turned off. However, virtual rows allow almost the same
44
44
  syntax even without the core extensions:
45
45
 
46
46
  dataset.filter{a & (b | ~c)}
@@ -87,7 +87,7 @@ and second is the difference between the the use of "a". In the regular proc,
87
87
  you couldn't call c without an explicit receiver in the proc, unless the self of the
88
88
  surrounding scope responded to it. For a, note how ruby calls the method on
89
89
  the receiver of the surrounding scope in the regular proc, which returns an integer,
90
- and does the substraction before Sequel gets access to it. In the instance evaled
90
+ and does the substitution before Sequel gets access to it. In the instance evaled
91
91
  proc, calling a without a receiver calls the a method on the VirtualRow instance.
92
92
  For b, note that it operates the same in both cases, as it is a local variable.
93
93
 
@@ -4,6 +4,8 @@ module Sequel
4
4
  # The ADO adapter provides connectivity to ADO databases in Windows.
5
5
  module ADO
6
6
  class Database < Sequel::Database
7
+ DISCONNECT_ERROR_RE = /Communication link failure/
8
+
7
9
  set_adapter_scheme :ado
8
10
 
9
11
  def initialize(opts)
@@ -12,12 +14,13 @@ module Sequel
12
14
  when /Microsoft\.(Jet|ACE)\.OLEDB/io
13
15
  Sequel.ts_require 'adapters/shared/access'
14
16
  extend Sequel::Access::DatabaseMethods
15
- when nil
17
+ else
16
18
  @opts[:driver] ||= 'SQL Server'
17
19
  case @opts[:driver]
18
20
  when 'SQL Server'
19
21
  Sequel.ts_require 'adapters/ado/mssql'
20
22
  extend Sequel::ADO::MSSQL::DatabaseMethods
23
+ set_mssql_unicode_strings
21
24
  end
22
25
  end
23
26
  end
@@ -86,9 +89,17 @@ module Sequel
86
89
  end
87
90
  end
88
91
 
92
+ def database_error_classes
93
+ [::WIN32OLERuntimeError]
94
+ end
95
+
89
96
  def disconnect_connection(conn)
90
97
  conn.Close
91
98
  end
99
+
100
+ def disconnect_error?(e, opts)
101
+ super || (e.is_a?(::WIN32OLERuntimeError) && e.message =~ DISCONNECT_ERROR_RE)
102
+ end
92
103
  end
93
104
 
94
105
  class Dataset < Sequel::Dataset
@@ -31,6 +31,10 @@ module Sequel
31
31
  def datetime(s)
32
32
  Sequel.database_to_application_timestamp(s)
33
33
  end
34
+
35
+ def time(s)
36
+ Sequel.string_to_time(s)
37
+ end
34
38
 
35
39
  # Don't raise an error if the value is a string and the declared
36
40
  # type doesn't match a known type, just return the value.
@@ -1,23 +1,24 @@
1
1
  require 'db2/db2cli'
2
+ Sequel.require %w'shared/db2', 'adapters'
2
3
 
3
4
  module Sequel
4
5
  module DB2
5
6
  class Database < Sequel::Database
6
- set_adapter_scheme :db2
7
+ include DatabaseMethods
7
8
 
8
- include DB2CLI
9
+ set_adapter_scheme :db2
9
10
 
10
11
  TEMPORARY = 'GLOBAL TEMPORARY '.freeze
11
12
 
12
- rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
13
+ rc, @@env = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_ENV, DB2CLI::SQL_NULL_HANDLE)
13
14
  #check_error(rc, "Could not allocate DB2 environment")
14
15
 
15
16
  def connect(server)
16
17
  opts = server_opts(server)
17
- rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
18
+ rc, dbc = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_DBC, @@env)
18
19
  check_error(rc, "Could not allocate database connection")
19
20
 
20
- rc = SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
21
+ rc = DB2CLI.SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
21
22
  check_error(rc, "Could not connect to database")
22
23
 
23
24
  dbc
@@ -32,102 +33,138 @@ module Sequel
32
33
  DB2::Dataset.new(self, opts)
33
34
  end
34
35
 
35
- def execute(sql, opts={})
36
+ def execute(sql, opts={}, &block)
37
+ synchronize(opts[:server]){|conn| log_connection_execute(conn, sql, &block)}
38
+ end
39
+ alias do execute
40
+
41
+ def execute_insert(sql, opts={})
36
42
  synchronize(opts[:server]) do |conn|
37
- rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
38
- check_error(rc, "Could not allocate statement")
39
-
40
- begin
41
- rc = log_yield(sql){SQLExecDirect(sth, sql)}
42
- check_error(rc, "Could not execute statement")
43
-
44
- yield(sth) if block_given?
45
-
46
- rc, rpc = SQLRowCount(sth)
47
- check_error(rc, "Could not get RPC")
48
- rpc
49
- ensure
50
- rc = SQLFreeHandle(SQL_HANDLE_STMT, sth)
51
- check_error(rc, "Could not free statement")
43
+ log_connection_execute(conn, sql)
44
+ sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
45
+ log_connection_execute(conn, sql) do |sth|
46
+ rc, name, buflen, datatype, size, digits, nullable = DB2CLI.SQLDescribeCol(sth, 1, 256)
47
+ check_error(rc, "Could not describe column")
48
+ if (rc = DB2CLI.SQLFetch(sth)) != DB2CLI::SQL_NO_DATA_FOUND
49
+ rc, v = DB2CLI.SQLGetData(sth, 1, datatype, size)
50
+ check_error(rc, "Could not get data")
51
+ if v.is_a?(String)
52
+ return v.to_i
53
+ else
54
+ return nil
55
+ end
56
+ end
52
57
  end
53
58
  end
54
59
  end
55
- alias_method :do, :execute
56
-
57
- private
58
60
 
59
61
  def check_error(rc, msg)
60
62
  case rc
61
- when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
63
+ when DB2CLI::SQL_SUCCESS, DB2CLI::SQL_SUCCESS_WITH_INFO
62
64
  nil
63
65
  else
64
66
  raise DatabaseError, msg
65
67
  end
66
68
  end
67
69
 
70
+ private
71
+
72
+ def begin_transaction(conn, opts={})
73
+ log_yield(TRANSACTION_BEGIN){DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_OFF)}
74
+ conn
75
+ end
76
+
77
+ def rollback_transaction(conn, opts={})
78
+ log_yield(TRANSACTION_ROLLBACK){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_ROLLBACK)}
79
+ ensure
80
+ DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_ON)
81
+ end
82
+
83
+ def commit_transaction(conn, opts={})
84
+ log_yield(TRANSACTION_COMMIT){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_COMMIT)}
85
+ ensure
86
+ DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_ON)
87
+ end
88
+
89
+ def log_connection_execute(conn, sql)
90
+ rc, sth = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_STMT, conn)
91
+ check_error(rc, "Could not allocate statement")
92
+
93
+ begin
94
+ rc = log_yield(sql){DB2CLI.SQLExecDirect(sth, sql)}
95
+ check_error(rc, "Could not execute statement: #{sql}")
96
+
97
+ yield(sth) if block_given?
98
+
99
+ rc, rpc = DB2CLI.SQLRowCount(sth)
100
+ check_error(rc, "Could not get RPC")
101
+ rpc
102
+ ensure
103
+ rc = DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_STMT, sth)
104
+ check_error(rc, "Could not free statement")
105
+ end
106
+ end
107
+
68
108
  def disconnect_connection(conn)
69
- rc = SQLDisconnect(conn)
109
+ rc = DB2CLI.SQLDisconnect(conn)
70
110
  check_error(rc, "Could not disconnect from database")
71
111
 
72
- rc = SQLFreeHandle(SQL_HANDLE_DBC, conn)
112
+ rc = DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_DBC, conn)
73
113
  check_error(rc, "Could not free Database handle")
74
114
  end
75
115
  end
76
116
 
77
117
  class Dataset < Sequel::Dataset
118
+ include DatasetMethods
119
+
78
120
  MAX_COL_SIZE = 256
79
121
 
80
122
  def fetch_rows(sql)
81
123
  execute(sql) do |sth|
82
- @column_info = get_column_info(sth)
83
- @columns = @column_info.map {|c| output_identifier(c[:name])}
84
- while (rc = SQLFetch(@handle)) != SQL_NO_DATA_FOUND
85
- @db.check_error(rc, "Could not fetch row")
86
- yield hash_row(sth)
124
+ offset = @opts[:offset]
125
+ db = @db
126
+ i = 1
127
+ column_info = get_column_info(sth)
128
+ cols = column_info.map{|c| c.at(1)}
129
+ cols.delete(row_number_column) if offset
130
+ @columns = cols
131
+ while (rc = DB2CLI.SQLFetch(sth)) != DB2CLI::SQL_NO_DATA_FOUND
132
+ db.check_error(rc, "Could not fetch row")
133
+ row = {}
134
+ column_info.each do |i, c, t, s|
135
+ rc, v = DB2CLI.SQLGetData(sth, i, t, s)
136
+ db.check_error(rc, "Could not get data")
137
+ row[c] = convert_type(v)
138
+ end
139
+ row.delete(row_number_column) if offset
140
+ yield row
87
141
  end
88
142
  end
89
143
  self
90
144
  end
91
145
 
92
- # DB2 supports window functions
93
- def supports_window_functions?
94
- true
95
- end
96
-
97
146
  private
98
147
 
99
148
  def get_column_info(sth)
100
- rc, column_count = SQLNumResultCols(sth)
101
- @db.check_error(rc, "Could not get number of result columns")
149
+ db = @db
150
+ rc, column_count = DB2CLI.SQLNumResultCols(sth)
151
+ db.check_error(rc, "Could not get number of result columns")
102
152
 
103
153
  (1..column_count).map do |i|
104
- rc, name, buflen, datatype, size, digits, nullable = SQLDescribeCol(sth, i, MAX_COL_SIZE)
105
- @b.check_error(rc, "Could not describe column")
106
-
107
- {:name => name, :db2_type => datatype, :precision => size}
154
+ rc, name, buflen, datatype, size, digits, nullable = DB2CLI.SQLDescribeCol(sth, i, MAX_COL_SIZE)
155
+ db.check_error(rc, "Could not describe column")
156
+ [i, output_identifier(name), datatype, size]
108
157
  end
109
158
  end
110
159
 
111
- def hash_row(sth)
112
- row = {}
113
- @column_info.each_with_index do |c, i|
114
- rc, v = SQLGetData(sth, i+1, c[:db2_type], c[:precision])
115
- @db.check_error(rc, "Could not get data")
116
-
117
- row[output_identifier(c[:name])] = convert_type(v)
118
- end
119
- row
120
- end
121
-
122
160
  def convert_type(v)
123
161
  case v
124
162
  when DB2CLI::Date
125
- DBI::Date.new(v.year, v.month, v.day)
163
+ Date.new(v.year, v.month, v.day)
126
164
  when DB2CLI::Time
127
- DBI::Time.new(v.hour, v.minute, v.second)
165
+ Sequel::SQLTime.create(v.hour, v.minute, v.second)
128
166
  when DB2CLI::Timestamp
129
- DBI::Timestamp.new(v.year, v.month, v.day,
130
- v.hour, v.minute, v.second, v.fraction)
167
+ Sequel.database_to_application_timestamp(v.to_s)
131
168
  when DB2CLI::Null
132
169
  nil
133
170
  else
@@ -36,6 +36,8 @@ module Sequel
36
36
  # Sequel::DataObjects::Database object, or hack DataObjects (or Extlib) to
37
37
  # use a pool size at least as large as the pool size being used by Sequel.
38
38
  class Database < Sequel::Database
39
+ DISCONNECT_ERROR_RE = /terminating connection due to administrator command/
40
+
39
41
  set_adapter_scheme :do
40
42
 
41
43
  # Call the DATABASE_SETUP proc directly after initialization,
@@ -145,10 +147,20 @@ module Sequel
145
147
  :execute_non_query
146
148
  end
147
149
 
150
+ # dataobjects uses the DataObjects::Error class as the main error class.
151
+ def database_error_classes
152
+ [::DataObjects::Error]
153
+ end
154
+
148
155
  # Close the given database connection.
149
156
  def disconnect_connection(c)
150
157
  c.close
151
158
  end
159
+
160
+ # Recognize DataObjects::ConnectionError instances as disconnect errors.
161
+ def disconnect_error?(e, opts)
162
+ super || (e.is_a?(::DataObjects::Error) && (e.is_a?(::DataObjects::ConnectionError) || e.message =~ DISCONNECT_ERROR_RE))
163
+ end
152
164
 
153
165
  # Execute SQL on the connection by creating a command first
154
166
  def log_connection_execute(conn, sql)