sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -0,0 +1,448 @@
1
+ require 'ibm_db'
2
+ Sequel.require 'adapters/shared/db2'
3
+
4
+ module Sequel
5
+
6
+ module IBMDB
7
+ @convert_smallint_to_bool = true
8
+
9
+ class << self
10
+ # Whether to convert smallint values to bool, true by default.
11
+ # Can also be overridden per dataset.
12
+ attr_accessor :convert_smallint_to_bool
13
+ end
14
+
15
+ tt = Class.new do
16
+ def boolean(s) !s.to_i.zero? end
17
+ def int(s) s.to_i end
18
+ end.new
19
+
20
+ # Hash holding type translation methods, used by Dataset#fetch_rows.
21
+ DB2_TYPES = {
22
+ :boolean => tt.method(:boolean),
23
+ :int => tt.method(:int),
24
+ :blob => ::Sequel::SQL::Blob.method(:new),
25
+ :time => ::Sequel.method(:string_to_time),
26
+ :date => ::Sequel.method(:string_to_date)
27
+ }
28
+ DB2_TYPES[:clob] = DB2_TYPES[:blob]
29
+
30
+ # Wraps an underlying connection to DB2 using IBM_DB.
31
+ class Connection
32
+ # A hash with prepared statement name symbol keys, where each value is
33
+ # a two element array with an sql string and cached Statement value.
34
+ attr_accessor :prepared_statements
35
+
36
+ # Error class for exceptions raised by the connection.
37
+ class Error < StandardError
38
+ end
39
+
40
+ # Create the underlying IBM_DB connection.
41
+ def initialize(connection_string)
42
+ @conn = IBM_DB.connect(connection_string, '', '')
43
+ self.autocommit = true
44
+ @prepared_statements = {}
45
+ end
46
+
47
+ # Check whether the connection is in autocommit state or not.
48
+ def autocommit
49
+ IBM_DB.autocommit(@conn) == 1
50
+ end
51
+
52
+ # Turn autocommit on or off for the connection.
53
+ def autocommit=(value)
54
+ IBM_DB.autocommit(@conn, value ? IBM_DB::SQL_AUTOCOMMIT_ON : IBM_DB::SQL_AUTOCOMMIT_OFF)
55
+ end
56
+
57
+ # Close the connection, disconnecting from DB2.
58
+ def close
59
+ IBM_DB.close(@conn)
60
+ end
61
+
62
+ # Commit the currently outstanding transaction on this connection.
63
+ def commit
64
+ IBM_DB.commit(@conn)
65
+ end
66
+
67
+ # Return the related error message for the connection.
68
+ def error_msg
69
+ IBM_DB.getErrormsg(@conn, IBM_DB::DB_CONN)
70
+ end
71
+
72
+ # Execute the given SQL on the database, and return a Statement instance
73
+ # holding the results.
74
+ def execute(sql)
75
+ stmt = IBM_DB.exec(@conn, sql)
76
+ raise Error, error_msg unless stmt
77
+ Statement.new(stmt)
78
+ end
79
+
80
+ # Execute the related prepared statement on the database with the given
81
+ # arguments.
82
+ def execute_prepared(ps_name, *values)
83
+ stmt = @prepared_statements[ps_name].last
84
+ res = stmt.execute(*values)
85
+ unless res
86
+ raise Error, "Error executing statement #{ps_name}: #{error_msg}"
87
+ end
88
+ stmt
89
+ end
90
+
91
+ # Prepare a statement with the given +sql+ on the database, and
92
+ # cache the prepared statement value by name.
93
+ def prepare(sql, ps_name)
94
+ if stmt = IBM_DB.prepare(@conn, sql)
95
+ ps_name = ps_name.to_sym
96
+ stmt = Statement.new(stmt)
97
+ @prepared_statements[ps_name] = [sql, stmt]
98
+ else
99
+ err = error_msg
100
+ err = "Error preparing #{ps_name} with SQL: #{sql}" if error_msg.nil? || error_msg.empty?
101
+ raise Error, err
102
+ end
103
+ end
104
+
105
+ # Rollback the currently outstanding transaction on this connection.
106
+ def rollback
107
+ IBM_DB.rollback(@conn)
108
+ end
109
+ end
110
+
111
+ # Wraps results returned by queries on IBM_DB.
112
+ class Statement
113
+ # Hold the given statement.
114
+ def initialize(stmt)
115
+ @stmt = stmt
116
+ end
117
+
118
+ # Return the number of rows affected.
119
+ def affected
120
+ IBM_DB.num_rows(@stmt)
121
+ end
122
+
123
+ # If this statement is a prepared statement, execute it on the database
124
+ # with the given values.
125
+ def execute(*values)
126
+ IBM_DB.execute(@stmt, values)
127
+ end
128
+
129
+ # Return the results of a query as an array of values.
130
+ def fetch_array
131
+ IBM_DB.fetch_array(@stmt) if @stmt
132
+ end
133
+
134
+ # Return the field name at the given column in the result set.
135
+ def field_name(ind)
136
+ IBM_DB.field_name(@stmt, ind)
137
+ end
138
+
139
+ # Return the field type for the given field name in the result set.
140
+ def field_type(key)
141
+ IBM_DB.field_type(@stmt, key)
142
+ end
143
+
144
+ # Return the field precision for the given field name in the result set.
145
+ def field_precision(key)
146
+ IBM_DB.field_precision(@stmt, key)
147
+ end
148
+
149
+ # Free the memory related to this result set.
150
+ def free
151
+ IBM_DB.free_result(@stmt)
152
+ end
153
+
154
+ # Return the number of fields in the result set.
155
+ def num_fields
156
+ IBM_DB.num_fields(@stmt)
157
+ end
158
+ end
159
+
160
+ class Database < Sequel::Database
161
+ include Sequel::DB2::DatabaseMethods
162
+
163
+ set_adapter_scheme :ibmdb
164
+
165
+ # Hash of connection procs for converting
166
+ attr_reader :conversion_procs
167
+
168
+ def initialize(opts={})
169
+ super
170
+ @conversion_procs = DB2_TYPES.dup
171
+ @conversion_procs[:timestamp] = method(:to_application_timestamp)
172
+ end
173
+
174
+ # REORG the related table whenever it is altered. This is not always
175
+ # required, but it is necessary for compatibilty with other Sequel
176
+ # code in many cases.
177
+ def alter_table(name, generator=nil, &block)
178
+ res = super
179
+ reorg(name)
180
+ res
181
+ end
182
+
183
+ # Create a new connection object for the given server.
184
+ def connect(server)
185
+ opts = server_opts(server)
186
+
187
+ # use uncataloged connection so that host and port can be supported
188
+ connection_string = ( \
189
+ 'Driver={IBM DB2 ODBC DRIVER};' \
190
+ "Database=#{opts[:database]};" \
191
+ "Hostname=#{opts[:host]};" \
192
+ "Port=#{opts[:port] || 50000};" \
193
+ 'Protocol=TCPIP;' \
194
+ "Uid=#{opts[:user]};" \
195
+ "Pwd=#{opts[:password]};" \
196
+ )
197
+
198
+ Connection.new(connection_string)
199
+ end
200
+
201
+ # Execute the given SQL on the database.
202
+ def execute(sql, opts={}, &block)
203
+ if sql.is_a?(Symbol)
204
+ execute_prepared_statement(sql, opts, &block)
205
+ else
206
+ synchronize(opts[:server]){|c| _execute(c, sql, opts, &block)}
207
+ end
208
+ rescue Connection::Error => e
209
+ raise_error(e)
210
+ end
211
+
212
+ # Execute the given SQL on the database, returning the last inserted
213
+ # identity value.
214
+ def execute_insert(sql, opts={})
215
+ synchronize(opts[:server]) do |c|
216
+ if sql.is_a?(Symbol)
217
+ execute_prepared_statement(sql, opts)
218
+ else
219
+ _execute(c, sql, opts)
220
+ end
221
+ _execute(c, "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1", opts){|stmt| i = stmt.fetch_array.first.to_i; stmt.free; i}
222
+ end
223
+ rescue Connection::Error => e
224
+ raise_error(e)
225
+ end
226
+
227
+ # Execute a prepared statement named by name on the database.
228
+ def execute_prepared_statement(ps_name, opts)
229
+ args = opts[:arguments]
230
+ ps = prepared_statements[ps_name]
231
+ sql = ps.prepared_sql
232
+ synchronize(opts[:server]) do |conn|
233
+ unless conn.prepared_statements.fetch(ps_name, []).first == sql
234
+ log_yield("Preparing #{ps_name}: #{sql}"){conn.prepare(sql, ps_name)}
235
+ end
236
+ args = args.map{|v| v.nil? ? nil : prepared_statement_arg(v)}
237
+ stmt = log_yield("Executing #{ps_name}: #{args.inspect}"){conn.execute_prepared(ps_name, *args)}
238
+
239
+ if block_given?
240
+ begin
241
+ yield(stmt)
242
+ ensure
243
+ stmt.free
244
+ end
245
+ else
246
+ stmt.affected
247
+ end
248
+ end
249
+ end
250
+
251
+ # Convert smallint type to boolean if convert_smallint_to_bool is true
252
+ def schema_column_type(db_type)
253
+ if Sequel::IBMDB.convert_smallint_to_bool && db_type =~ /smallint/i
254
+ :boolean
255
+ else
256
+ super
257
+ end
258
+ end
259
+
260
+ # On DB2, a table might need to be REORGed if you are testing existence
261
+ # of it. This REORGs automatically if the database raises a specific
262
+ # error that indicates it should be REORGed.
263
+ def table_exists?(name)
264
+ v ||= false # only retry once
265
+ sch, table_name = schema_and_table(name)
266
+ name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
267
+ from(name).first
268
+ true
269
+ rescue DatabaseError => e
270
+ if e.to_s =~ /Operation not allowed for reason code "7" on table/ && v == false
271
+ # table probably needs reorg
272
+ reorg(name)
273
+ v = true
274
+ retry
275
+ end
276
+ false
277
+ end
278
+
279
+ private
280
+
281
+ # Execute the given SQL on the database.
282
+ def _execute(conn, sql, opts)
283
+ stmt = log_yield(sql){conn.execute(sql)}
284
+ if block_given?
285
+ begin
286
+ yield(stmt)
287
+ ensure
288
+ stmt.free
289
+ end
290
+ else
291
+ stmt.affected
292
+ end
293
+ end
294
+
295
+ # IBM_DB uses an autocommit setting instead of sending SQL queries.
296
+ # So starting a transaction just turns autocommit off.
297
+ def begin_transaction(conn, opts={})
298
+ log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
299
+ end
300
+
301
+ # This commits transaction in progress on the
302
+ # connection and sets autocommit back on.
303
+ def commit_transaction(conn, opts={})
304
+ log_yield(TRANSACTION_COMMIT){conn.commit}
305
+ end
306
+
307
+ # Close the given connection.
308
+ def disconnect_connection(conn)
309
+ conn.close
310
+ end
311
+
312
+ # Don't convert smallint to boolean for the metadata
313
+ # dataset, since the DB2 metadata does not use
314
+ # boolean columns, and some smallint columns are
315
+ # accidently treated as booleans.
316
+ def metadata_dataset
317
+ ds = super
318
+ ds.convert_smallint_to_bool = false
319
+ ds
320
+ end
321
+
322
+ # Format Numeric, Date, and Time types specially for use
323
+ # as IBM_DB prepared statements argument vlaues.
324
+ def prepared_statement_arg(v)
325
+ case v
326
+ when Numeric
327
+ v.to_s
328
+ when Date, Time
329
+ literal(v).gsub("'", '')
330
+ else
331
+ v
332
+ end
333
+ end
334
+
335
+ # Set autocommit back on
336
+ def remove_transaction(conn, committed)
337
+ conn.autocommit = true
338
+ ensure
339
+ super
340
+ end
341
+
342
+ # This rolls back the transaction in progress on the
343
+ # connection and sets autocommit back on.
344
+ def rollback_transaction(conn, opts={})
345
+ log_yield(TRANSACTION_ROLLBACK){conn.rollback}
346
+ end
347
+ end
348
+
349
+ class Dataset < Sequel::Dataset
350
+ include Sequel::DB2::DatasetMethods
351
+
352
+ Database::DatasetClass = self
353
+
354
+ module CallableStatementMethods
355
+ # Extend given dataset with this module so subselects inside subselects in
356
+ # prepared statements work.
357
+ def subselect_sql(ds)
358
+ ps = ds.to_prepared_statement(:select)
359
+ ps.extend(CallableStatementMethods)
360
+ ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
361
+ ps.prepared_args = prepared_args
362
+ ps.prepared_sql
363
+ end
364
+ end
365
+
366
+ # Methods for DB2 prepared statements using the native driver.
367
+ module PreparedStatementMethods
368
+ include Sequel::Dataset::UnnumberedArgumentMapper
369
+
370
+ private
371
+ # Execute the prepared statement with arguments instead of the given SQL.
372
+ def execute(sql, opts={}, &block)
373
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
374
+ end
375
+
376
+ # Execute the prepared statment with arguments instead of the given SQL.
377
+ def execute_dui(sql, opts={}, &block)
378
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
379
+ end
380
+
381
+ # Execute the prepared statement with arguments instead of the given SQL.
382
+ def execute_insert(sql, opts={}, &block)
383
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
384
+ end
385
+
386
+ end
387
+
388
+ # Emulate support of bind arguments in called statements.
389
+ def call(type, bind_arguments={}, *values, &block)
390
+ ps = to_prepared_statement(type, values)
391
+ ps.extend(CallableStatementMethods)
392
+ ps.call(bind_arguments, &block)
393
+ end
394
+
395
+ # Whether to convert smallint to boolean arguments for this dataset.
396
+ # Defaults to the IBMDB module setting.
397
+ def convert_smallint_to_bool
398
+ defined?(@convert_smallint_to_bool) ? @convert_smallint_to_bool : (@convert_smallint_to_bool = IBMDB.convert_smallint_to_bool)
399
+ end
400
+
401
+ # Override the default IBMDB.convert_smallint_to_bool setting for this dataset.
402
+ attr_writer :convert_smallint_to_bool
403
+
404
+ # Fetch the rows from the database and yield plain hashes.
405
+ def fetch_rows(sql)
406
+ execute(sql) do |stmt|
407
+ offset = @opts[:offset]
408
+ columns = []
409
+ convert = convert_smallint_to_bool
410
+ cps = db.conversion_procs
411
+ stmt.num_fields.times do |i|
412
+ k = stmt.field_name i
413
+ key = output_identifier(k)
414
+ type = stmt.field_type(k).downcase.to_sym
415
+ # decide if it is a smallint from precision
416
+ type = :boolean if type ==:int && convert && stmt.field_precision(k) < 8
417
+ columns << [key, cps[type]]
418
+ end
419
+ cols = columns.map{|c| c.at(0)}
420
+ cols.delete(row_number_column) if offset
421
+ @columns = cols
422
+
423
+ while res = stmt.fetch_array
424
+ row = {}
425
+ res.zip(columns).each do |v, (k, pr)|
426
+ row[k] = ((pr ? pr.call(v) : v) if v)
427
+ end
428
+ row.delete(row_number_column) if offset
429
+ yield row
430
+ end
431
+ end
432
+ self
433
+ end
434
+
435
+ # Store the given type of prepared statement in the associated database
436
+ # with the given name.
437
+ def prepare(type, name=nil, *values)
438
+ ps = to_prepared_statement(type, values)
439
+ ps.extend(PreparedStatementMethods)
440
+ if name
441
+ ps.prepared_statement_name = name
442
+ db.prepared_statements[name] = ps
443
+ end
444
+ ps
445
+ end
446
+ end
447
+ end
448
+ end
@@ -1,21 +1,18 @@
1
1
  require 'informix'
2
+ Sequel.require 'adapters/shared/informix'
2
3
 
3
4
  module Sequel
4
5
  module Informix
5
6
  class Database < Sequel::Database
7
+ include DatabaseMethods
8
+
6
9
  set_adapter_scheme :informix
7
10
 
8
- TEMPORARY = 'TEMP '.freeze
9
-
10
11
  def connect(server)
11
12
  opts = server_opts(server)
12
13
  ::Informix.connect(opts[:database], opts[:user], opts[:password])
13
14
  end
14
15
 
15
- def dataset(opts = nil)
16
- Sequel::Informix::Dataset.new(self, opts)
17
- end
18
-
19
16
  # Returns number of rows affected
20
17
  def execute_dui(sql, opts={})
21
18
  synchronize(opts[:server]){|c| log_yield(sql){c.immediate(sql)}}
@@ -35,7 +32,9 @@ module Sequel
35
32
  end
36
33
 
37
34
  class Dataset < Sequel::Dataset
38
- SELECT_CLAUSE_METHODS = clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
35
+ include DatasetMethods
36
+
37
+ Database::DatasetClass = self
39
38
 
40
39
  def fetch_rows(sql)
41
40
  execute(sql) do |cursor|
@@ -56,22 +55,6 @@ module Sequel
56
55
  end
57
56
  self
58
57
  end
59
-
60
- private
61
-
62
- # Informix does not support INTERSECT or EXCEPT
63
- def supports_intersect_except?
64
- false
65
- end
66
-
67
- def select_clause_methods
68
- SELECT_CLAUSE_METHODS
69
- end
70
-
71
- def select_limit_sql(sql)
72
- sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
73
- sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
74
- end
75
58
  end
76
59
  end
77
60
  end
@@ -1,9 +1,13 @@
1
+ Sequel.require 'adapters/jdbc/transactions'
2
+
1
3
  module Sequel
2
4
  module JDBC
3
5
  # Database and Dataset support for AS400 databases accessed via JDBC.
4
6
  module AS400
5
7
  # Instance methods for AS400 Database objects accessed via JDBC.
6
8
  module DatabaseMethods
9
+ include Sequel::JDBC::Transactions
10
+
7
11
  TRANSACTION_BEGIN = 'Transaction.begin'.freeze
8
12
  TRANSACTION_COMMIT = 'Transaction.commit'.freeze
9
13
  TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
@@ -13,11 +17,6 @@ module Sequel
13
17
  :as400
14
18
  end
15
19
 
16
- # Return Sequel::JDBC::AS400::Dataset object with the given opts.
17
- def dataset(opts=nil)
18
- Sequel::JDBC::AS400::Dataset.new(self, opts)
19
- end
20
-
21
20
  # TODO: Fix for AS400
22
21
  def last_insert_id(conn, opts={})
23
22
  nil
@@ -33,25 +32,7 @@ module Sequel
33
32
  # Use JDBC connection's setAutoCommit to false to start transactions
34
33
  def begin_transaction(conn, opts={})
35
34
  set_transaction_isolation(conn, opts)
36
- log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
37
- conn
38
- end
39
-
40
- # Use JDBC connection's commit method to commit transactions
41
- def commit_transaction(conn, opts={})
42
- log_yield(TRANSACTION_COMMIT){conn.commit}
43
- end
44
-
45
- # Use JDBC connection's setAutoCommit to true to enable default
46
- # auto-commit mode
47
- def remove_transaction(conn)
48
- conn.setAutoCommit(true) if conn
49
- @transactions.delete(Thread.current)
50
- end
51
-
52
- # Use JDBC connection's rollback method to rollback transactions
53
- def rollback_transaction(conn, opts={})
54
- log_yield(TRANSACTION_ROLLBACK){conn.rollback}
35
+ super
55
36
  end
56
37
  end
57
38
 
@@ -88,13 +69,6 @@ module Sequel
88
69
  def supports_window_functions?
89
70
  true
90
71
  end
91
-
92
- private
93
-
94
- # The alias to use for the row_number column when emulating LIMIT and OFFSET
95
- def row_number_column
96
- :x_sequel_row_number_x
97
- end
98
72
  end
99
73
  end
100
74
  end
@@ -0,0 +1,44 @@
1
+ Sequel.require 'adapters/shared/db2'
2
+ Sequel.require 'adapters/jdbc/transactions'
3
+
4
+ module Sequel
5
+ module JDBC
6
+ class Database
7
+ # Alias the generic JDBC versions so they can be called directly later
8
+ alias jdbc_schema_parse_table schema_parse_table
9
+ alias jdbc_tables tables
10
+ alias jdbc_views views
11
+ alias jdbc_indexes indexes
12
+ end
13
+
14
+ # Database and Dataset instance methods for DB2 specific
15
+ # support via JDBC.
16
+ module DB2
17
+ # Database instance methods for DB2 databases accessed via JDBC.
18
+ module DatabaseMethods
19
+ include Sequel::DB2::DatabaseMethods
20
+ include Sequel::JDBC::Transactions
21
+
22
+ %w'schema_parse_table tables views indexes'.each do |s|
23
+ class_eval("def #{s}(*a) jdbc_#{s}(*a) end", __FILE__, __LINE__)
24
+ end
25
+
26
+ private
27
+
28
+ def last_insert_id(conn, opts={})
29
+ statement(conn) do |stmt|
30
+ sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
31
+ rs = log_yield(sql){stmt.executeQuery(sql)}
32
+ rs.next
33
+ rs.getInt(1)
34
+ end
35
+ end
36
+ end
37
+
38
+ # Dataset class for DB2 datasets accessed via JDBC.
39
+ class Dataset < JDBC::Dataset
40
+ include Sequel::DB2::DatasetMethods
41
+ end
42
+ end
43
+ end
44
+ end