sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -1,139 +1,228 @@
1
1
  require 'db2/db2cli'
2
+ Sequel.require %w'shared/db2', 'adapters'
2
3
 
3
4
  module Sequel
4
5
  module DB2
6
+
7
+ @convert_smallint_to_bool = true
8
+
9
+ # Underlying error raised by Sequel, since ruby-db2 doesn't
10
+ # use exceptions.
11
+ class DB2Error < StandardError
12
+ end
13
+
14
+ class << self
15
+ # Whether to convert smallint values to bool, true by default.
16
+ # Can also be overridden per dataset.
17
+ attr_accessor :convert_smallint_to_bool
18
+ end
19
+
20
+ tt = Class.new do
21
+ def boolean(s) !s.to_i.zero? end
22
+ def date(s) Date.new(s.year, s.month, s.day) end
23
+ def time(s) Sequel::SQLTime.create(s.hour, s.minute, s.second) end
24
+ end.new
25
+
26
+ # Hash holding type translation methods, used by Dataset#fetch_rows.
27
+ DB2_TYPES = {
28
+ :boolean => tt.method(:boolean),
29
+ DB2CLI::SQL_BLOB => ::Sequel::SQL::Blob.method(:new),
30
+ DB2CLI::SQL_TYPE_DATE => tt.method(:date),
31
+ DB2CLI::SQL_TYPE_TIME => tt.method(:time),
32
+ DB2CLI::SQL_DECIMAL => ::BigDecimal.method(:new)
33
+ }
34
+ DB2_TYPES[DB2CLI::SQL_CLOB] = DB2_TYPES[DB2CLI::SQL_BLOB]
35
+
5
36
  class Database < Sequel::Database
6
- set_adapter_scheme :db2
37
+ include DatabaseMethods
7
38
 
8
- include DB2CLI
39
+ set_adapter_scheme :db2
9
40
 
10
41
  TEMPORARY = 'GLOBAL TEMPORARY '.freeze
42
+ rc, NullHandle = DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_ENV, DB2CLI::SQL_NULL_HANDLE)
11
43
 
12
- rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
13
- #check_error(rc, "Could not allocate DB2 environment")
44
+ # Hash of connection procs for converting
45
+ attr_reader :conversion_procs
46
+
47
+ def initialize(opts={})
48
+ super
49
+ @conversion_procs = DB2_TYPES.dup
50
+ @conversion_procs[DB2CLI::SQL_TYPE_TIMESTAMP] = method(:to_application_timestamp_db2)
51
+ end
14
52
 
15
53
  def connect(server)
16
54
  opts = server_opts(server)
17
- rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
18
- check_error(rc, "Could not allocate database connection")
19
-
20
- rc = SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
21
- check_error(rc, "Could not connect to database")
22
-
55
+ dbc = checked_error("Could not allocate database connection"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_DBC, NullHandle)}
56
+ checked_error("Could not connect to database"){DB2CLI.SQLConnect(dbc, opts[:database], opts[:user], opts[:password])}
23
57
  dbc
24
58
  end
25
59
 
26
- def test_connection(server=nil)
27
- synchronize(server){|conn|}
28
- true
60
+ def execute(sql, opts={}, &block)
61
+ synchronize(opts[:server]){|conn| log_connection_execute(conn, sql, &block)}
29
62
  end
63
+ alias do execute
30
64
 
31
- def dataset(opts = nil)
32
- DB2::Dataset.new(self, opts)
33
- end
34
-
35
- def execute(sql, opts={})
65
+ def execute_insert(sql, opts={})
36
66
  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")
67
+ log_connection_execute(conn, sql)
68
+ sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
69
+ log_connection_execute(conn, sql) do |sth|
70
+ name, buflen, datatype, size, digits, nullable = checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, 1, 256)}
71
+ if DB2CLI.SQLFetch(sth) != DB2CLI::SQL_NO_DATA_FOUND
72
+ v, _ = checked_error("Could not get data"){DB2CLI.SQLGetData(sth, 1, datatype, size)}
73
+ if v.is_a?(String)
74
+ return v.to_i
75
+ else
76
+ return nil
77
+ end
78
+ end
52
79
  end
53
80
  end
54
81
  end
55
- alias_method :do, :execute
56
-
57
- private
58
82
 
83
+ ERROR_MAP = {}
84
+ %w'SQL_INVALID_HANDLE SQL_STILL_EXECUTING SQL_ERROR'.each do |s|
85
+ ERROR_MAP[DB2CLI.const_get(s)] = s
86
+ end
59
87
  def check_error(rc, msg)
60
88
  case rc
61
- when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
89
+ when DB2CLI::SQL_SUCCESS, DB2CLI::SQL_SUCCESS_WITH_INFO, DB2CLI::SQL_NO_DATA_FOUND
62
90
  nil
91
+ when DB2CLI::SQL_INVALID_HANDLE, DB2CLI::SQL_STILL_EXECUTING
92
+ e = DB2Error.new("#{ERROR_MAP[rc]}: #{msg}")
93
+ e.set_backtrace(caller)
94
+ raise_error(e, :disconnect=>true)
63
95
  else
64
- raise DatabaseError, msg
96
+ e = DB2Error.new("#{ERROR_MAP[rc] || "Error code #{rc}"}: #{msg}")
97
+ e.set_backtrace(caller)
98
+ raise_error(e, :disconnect=>true)
65
99
  end
66
100
  end
67
101
 
68
- def disconnect_connection(conn)
69
- rc = SQLDisconnect(conn)
70
- check_error(rc, "Could not disconnect from database")
102
+ def checked_error(msg)
103
+ rc, *ary= yield
104
+ check_error(rc, msg)
105
+ ary.length <= 1 ? ary.first : ary
106
+ end
107
+
108
+ def to_application_timestamp_db2(v)
109
+ to_application_timestamp(v.to_s)
110
+ end
111
+
112
+ private
113
+
114
+ def begin_transaction(conn, opts={})
115
+ log_yield(TRANSACTION_BEGIN){DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_OFF)}
116
+ end
117
+
118
+ def remove_transaction(conn, committed)
119
+ DB2CLI.SQLSetConnectAttr(conn, DB2CLI::SQL_ATTR_AUTOCOMMIT, DB2CLI::SQL_AUTOCOMMIT_ON)
120
+ ensure
121
+ super
122
+ end
123
+
124
+ def rollback_transaction(conn, opts={})
125
+ log_yield(TRANSACTION_ROLLBACK){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_ROLLBACK)}
126
+ end
71
127
 
72
- rc = SQLFreeHandle(SQL_HANDLE_DBC, conn)
73
- check_error(rc, "Could not free Database handle")
128
+ def commit_transaction(conn, opts={})
129
+ log_yield(TRANSACTION_COMMIT){DB2CLI.SQLEndTran(DB2CLI::SQL_HANDLE_DBC, conn, DB2CLI::SQL_COMMIT)}
130
+ end
131
+
132
+ def log_connection_execute(conn, sql)
133
+ sth = checked_error("Could not allocate statement"){DB2CLI.SQLAllocHandle(DB2CLI::SQL_HANDLE_STMT, conn)}
134
+
135
+ begin
136
+ checked_error("Could not execute statement: #{sql}"){log_yield(sql){DB2CLI.SQLExecDirect(sth, sql)}}
137
+
138
+ if block_given?
139
+ yield(sth)
140
+ else
141
+ checked_error("Could not get RPC"){DB2CLI.SQLRowCount(sth)}
142
+ end
143
+ ensure
144
+ checked_error("Could not free statement"){DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_STMT, sth)}
145
+ end
146
+ end
147
+
148
+ # Convert smallint type to boolean if convert_smallint_to_bool is true
149
+ def schema_column_type(db_type)
150
+ if DB2.convert_smallint_to_bool && db_type =~ /smallint/i
151
+ :boolean
152
+ else
153
+ super
154
+ end
155
+ end
156
+
157
+ def disconnect_connection(conn)
158
+ checked_error("Could not disconnect from database"){DB2CLI.SQLDisconnect(conn)}
159
+ checked_error("Could not free Database handle"){DB2CLI.SQLFreeHandle(DB2CLI::SQL_HANDLE_DBC, conn)}
74
160
  end
75
161
  end
76
162
 
77
163
  class Dataset < Sequel::Dataset
164
+ include DatasetMethods
165
+
166
+ Database::DatasetClass = self
78
167
  MAX_COL_SIZE = 256
79
168
 
169
+ # Whether to convert smallint to boolean arguments for this dataset.
170
+ # Defaults to the DB2 module setting.
171
+ def convert_smallint_to_bool
172
+ defined?(@convert_smallint_to_bool) ? @convert_smallint_to_bool : (@convert_smallint_to_bool = DB2.convert_smallint_to_bool)
173
+ end
174
+
175
+ # Override the default DB2.convert_smallint_to_bool setting for this dataset.
176
+ attr_writer :convert_smallint_to_bool
177
+
80
178
  def fetch_rows(sql)
81
179
  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)
180
+ offset = @opts[:offset]
181
+ db = @db
182
+ i = 1
183
+ column_info = get_column_info(sth)
184
+ cols = column_info.map{|c| c.at(1)}
185
+ cols.delete(row_number_column) if offset
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
+ row.delete(row_number_column) if offset
202
+ yield row
87
203
  end
88
204
  end
89
205
  self
90
206
  end
91
207
 
92
- # DB2 supports window functions
93
- def supports_window_functions?
94
- true
95
- end
96
-
97
208
  private
98
209
 
99
210
  def get_column_info(sth)
100
- rc, column_count = SQLNumResultCols(sth)
101
- @db.check_error(rc, "Could not get number of result columns")
211
+ db = @db
212
+ column_count = db.checked_error("Could not get number of result columns"){DB2CLI.SQLNumResultCols(sth)}
213
+ convert = convert_smallint_to_bool
214
+ cps = db.conversion_procs
102
215
 
103
216
  (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}
217
+ name, buflen, datatype, size, digits, nullable = db.checked_error("Could not describe column"){DB2CLI.SQLDescribeCol(sth, i, MAX_COL_SIZE)}
218
+ pr = if datatype == DB2CLI::SQL_SMALLINT && convert && size <= 5 && digits <= 1
219
+ cps[:boolean]
220
+ else
221
+ cps[datatype]
222
+ end
223
+ [i, output_identifier(name), datatype, size, pr]
108
224
  end
109
225
  end
110
-
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
- def convert_type(v)
123
- case v
124
- when DB2CLI::Date
125
- DBI::Date.new(v.year, v.month, v.day)
126
- when DB2CLI::Time
127
- DBI::Time.new(v.hour, v.minute, v.second)
128
- when DB2CLI::Timestamp
129
- DBI::Timestamp.new(v.year, v.month, v.day,
130
- v.hour, v.minute, v.second, v.fraction)
131
- when DB2CLI::Null
132
- nil
133
- else
134
- v
135
- end
136
- end
137
226
  end
138
227
  end
139
228
  end
@@ -63,10 +63,6 @@ module Sequel
63
63
  ::DBI.connect(dbname, opts[:user], opts[:password])
64
64
  end
65
65
 
66
- def dataset(opts = nil)
67
- DBI::Dataset.new(self, opts)
68
- end
69
-
70
66
  def execute(sql, opts={})
71
67
  synchronize(opts[:server]) do |conn|
72
68
  r = log_yield(sql){conn.execute(sql)}
@@ -88,26 +84,30 @@ module Sequel
88
84
  end
89
85
 
90
86
  class Dataset < Sequel::Dataset
87
+ Database::DatasetClass = self
88
+
91
89
  def fetch_rows(sql)
92
90
  execute(sql) do |s|
93
91
  begin
94
- @columns = s.column_names.map{|c| output_identifier(c)}
95
- s.fetch{|r| yield hash_row(s, r)}
92
+ columns = cols = s.column_names.map{|c| output_identifier(c)}
93
+ if opts[:offset] && offset_returns_row_number_column?
94
+ rn = row_number_column
95
+ columns = columns.dup
96
+ columns.delete(rn)
97
+ end
98
+ @columns = columns
99
+ s.fetch do |r|
100
+ row = {}
101
+ cols.each{|c| row[c] = r.shift}
102
+ row.delete(rn) if rn
103
+ yield row
104
+ end
96
105
  ensure
97
106
  s.finish rescue nil
98
107
  end
99
108
  end
100
109
  self
101
110
  end
102
-
103
- private
104
-
105
- def hash_row(stmt, row)
106
- @columns.inject({}) do |m, c|
107
- m[c] = row.shift
108
- m
109
- end
110
- end
111
111
  end
112
112
  end
113
113
  end
@@ -9,11 +9,6 @@ module Sequel
9
9
  module DatabaseMethods
10
10
  include Sequel::MySQL::DatabaseMethods
11
11
 
12
- # Return instance of Sequel::DataObjects::MySQL::Dataset with the given opts.
13
- def dataset(opts=nil)
14
- Sequel::DataObjects::MySQL::Dataset.new(self, opts)
15
- end
16
-
17
12
  private
18
13
 
19
14
  # The database name for the given database. Need to parse it out
@@ -23,25 +18,36 @@ module Sequel
23
18
  (m = /\/(.*)/.match(URI.parse(uri).path)) && m[1]
24
19
  end
25
20
 
21
+ # Recognize the tinyint(1) column as boolean.
26
22
  def schema_column_type(db_type)
27
23
  db_type == 'tinyint(1)' ? :boolean : super
28
24
  end
25
+
26
+ # Apply the connectiong setting SQLs for every new connection.
27
+ def setup_connection(conn)
28
+ mysql_connection_setting_sqls.each{|sql| log_yield(sql){conn.create_command(sql).execute_non_query}}
29
+ super
30
+ end
29
31
  end
30
32
 
31
33
  # Dataset class for MySQL datasets accessed via DataObjects.
32
34
  class Dataset < DataObjects::Dataset
33
35
  include Sequel::MySQL::DatasetMethods
36
+ APOS = Dataset::APOS
37
+ APOS_RE = Dataset::APOS_RE
38
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
34
39
 
35
- # Use execute_insert to execute the replace_sql.
36
- def replace(*args)
37
- execute_insert(replace_sql(*args))
40
+ # The DataObjects MySQL driver uses the number of rows actually modified in the update,
41
+ # instead of the number of matched by the filter.
42
+ def provides_accurate_rows_matched?
43
+ false
38
44
  end
39
-
45
+
40
46
  private
41
47
 
42
48
  # do_mysql sets NO_BACKSLASH_ESCAPES, so use standard SQL string escaping
43
- def literal_string(s)
44
- "'#{s.gsub("'", "''")}'"
49
+ def literal_string_append(sql, s)
50
+ sql << APOS << s.gsub(APOS_RE, DOUBLE_APOS) << APOS
45
51
  end
46
52
  end
47
53
  end
@@ -7,40 +7,6 @@ module Sequel
7
7
  # Adapter, Database, and Dataset support for accessing a PostgreSQL
8
8
  # database via DataObjects.
9
9
  module Postgres
10
- # Methods to add to the DataObjects adapter/connection to allow it to work
11
- # with the shared PostgreSQL code.
12
- module AdapterMethods
13
- include Sequel::Postgres::AdapterMethods
14
-
15
- # Give the DataObjects adapter a direct execute method, which creates
16
- # a statement with the given sql and executes it.
17
- def execute(sql, args=nil)
18
- command = create_command(sql)
19
- begin
20
- if block_given?
21
- begin
22
- yield(reader = @db.log_yield(sql){command.execute_reader})
23
- ensure
24
- reader.close if reader
25
- end
26
- else
27
- @db.log_yield(sql){command.execute_non_query}
28
- end
29
- rescue ::DataObjects::Error => e
30
- @db.send(:raise_error, e)
31
- end
32
- end
33
-
34
- private
35
-
36
- # DataObjects specific method of getting specific values from a result set.
37
- def single_value(reader)
38
- while(reader.next!) do
39
- return reader.values.at(0)
40
- end
41
- end
42
- end
43
-
44
10
  # Methods to add to Database instances that access PostgreSQL via
45
11
  # DataObjects.
46
12
  module DatabaseMethods
@@ -55,37 +21,15 @@ module Sequel
55
21
  end
56
22
  end
57
23
 
58
- # Return instance of Sequel::DataObjects::Postgres::Dataset with the given opts.
59
- def dataset(opts=nil)
60
- Sequel::DataObjects::Postgres::Dataset.new(self, opts)
61
- end
62
-
63
- # Run the INSERT sql on the database and return the primary key
64
- # for the record.
65
- def execute_insert(sql, opts={})
66
- synchronize(opts[:server]) do |conn|
67
- com = conn.create_command(sql)
68
- log_yield(sql){com.execute_non_query}
69
- insert_result(conn, opts[:table], opts[:values])
70
- end
71
- end
72
-
73
24
  private
74
25
 
75
26
  # Extend the adapter with the DataObjects PostgreSQL AdapterMethods
76
27
  def setup_connection(conn)
77
28
  conn = super(conn)
78
- conn.extend(Sequel::DataObjects::Postgres::AdapterMethods)
79
- conn.db = self
80
- conn.apply_connection_settings
29
+ connection_configuration_sqls.each{|sql| log_yield(sql){conn.create_command(sql).execute_non_query}}
81
30
  conn
82
31
  end
83
32
  end
84
-
85
- # Dataset subclass used for datasets that connect to PostgreSQL via DataObjects.
86
- class Dataset < DataObjects::Dataset
87
- include Sequel::Postgres::DatasetMethods
88
- end
89
33
  end
90
34
  end
91
35
  end
@@ -8,11 +8,6 @@ module Sequel
8
8
  module DatabaseMethods
9
9
  include Sequel::SQLite::DatabaseMethods
10
10
 
11
- # Return Sequel::DataObjects::SQLite::Dataset object with the given opts.
12
- def dataset(opts=nil)
13
- Sequel::DataObjects::SQLite::Dataset.new(self, opts)
14
- end
15
-
16
11
  private
17
12
 
18
13
  # Default to a single connection for a memory database.
@@ -30,11 +25,6 @@ module Sequel
30
25
  super
31
26
  end
32
27
  end
33
-
34
- # Dataset class for SQLite datasets accessed via DataObjects.
35
- class Dataset < DataObjects::Dataset
36
- include Sequel::SQLite::DatasetMethods
37
- end
38
28
  end
39
29
  end
40
30
  end
@@ -17,16 +17,20 @@ module Sequel
17
17
  Sequel.tsk_require 'do_postgres'
18
18
  Sequel.ts_require 'adapters/do/postgres'
19
19
  db.extend(Sequel::DataObjects::Postgres::DatabaseMethods)
20
+ db.extend_datasets Sequel::Postgres::DatasetMethods
20
21
  end,
21
22
  :mysql=>proc do |db|
22
23
  Sequel.tsk_require 'do_mysql'
23
24
  Sequel.ts_require 'adapters/do/mysql'
24
25
  db.extend(Sequel::DataObjects::MySQL::DatabaseMethods)
26
+ db.dataset_class = Sequel::DataObjects::MySQL::Dataset
25
27
  end,
26
28
  :sqlite3=>proc do |db|
27
29
  Sequel.tsk_require 'do_sqlite3'
28
30
  Sequel.ts_require 'adapters/do/sqlite'
29
31
  db.extend(Sequel::DataObjects::SQLite::DatabaseMethods)
32
+ db.extend_datasets Sequel::SQLite::DatasetMethods
33
+ db.set_integer_booleans
30
34
  end
31
35
  }
32
36
 
@@ -36,6 +40,8 @@ module Sequel
36
40
  # Sequel::DataObjects::Database object, or hack DataObjects (or Extlib) to
37
41
  # use a pool size at least as large as the pool size being used by Sequel.
38
42
  class Database < Sequel::Database
43
+ DISCONNECT_ERROR_RE = /terminating connection due to administrator command/
44
+
39
45
  set_adapter_scheme :do
40
46
 
41
47
  # Call the DATABASE_SETUP proc directly after initialization,
@@ -55,11 +61,6 @@ module Sequel
55
61
  setup_connection(::DataObjects::Connection.new(uri(server_opts(server))))
56
62
  end
57
63
 
58
- # Return a Sequel::DataObjects::Dataset object for this database.
59
- def dataset(opts = nil)
60
- DataObjects::Dataset.new(self, opts)
61
- end
62
-
63
64
  # Execute the given SQL. If a block is given, the DataObjects::Reader
64
65
  # created is yielded to it. A block should not be provided unless a
65
66
  # a SELECT statement is being used (or something else that returns rows).
@@ -114,53 +115,32 @@ module Sequel
114
115
 
115
116
  private
116
117
 
117
- # DataObjects uses a special transaction object to keep track of
118
- # transactions. Unfortunately, it tries to create a new connection
119
- # to do a transaction. So we close the connection created and
120
- # substitute our own.
121
- def begin_transaction(conn, opts={})
122
- return super if supports_savepoints?
123
- log_yield(TRANSACTION_BEGIN) do
124
- t = ::DataObjects::Transaction.create_for_uri(uri)
125
- t.instance_variable_get(:@connection).close
126
- t.instance_variable_set(:@connection, conn)
127
- t.begin
128
- t
129
- end
130
- end
131
-
132
- # DataObjects requires transactions be prepared before being
133
- # committed, so we do that.
134
- def commit_transaction(t, opts={})
135
- return super if supports_savepoints?
136
- log_yield(TRANSACTION_ROLLBACK) do
137
- t.prepare
138
- t.commit
139
- end
140
- end
141
-
142
118
  # Method to call on a statement object to execute SQL that does
143
119
  # not return any rows.
144
120
  def connection_execute_method
145
121
  :execute_non_query
146
122
  end
147
123
 
124
+ # dataobjects uses the DataObjects::Error class as the main error class.
125
+ def database_error_classes
126
+ [::DataObjects::Error]
127
+ end
128
+
148
129
  # Close the given database connection.
149
130
  def disconnect_connection(c)
150
131
  c.close
151
132
  end
133
+
134
+ # Recognize DataObjects::ConnectionError instances as disconnect errors.
135
+ def disconnect_error?(e, opts)
136
+ super || (e.is_a?(::DataObjects::Error) && (e.is_a?(::DataObjects::ConnectionError) || e.message =~ DISCONNECT_ERROR_RE))
137
+ end
152
138
 
153
139
  # Execute SQL on the connection by creating a command first
154
140
  def log_connection_execute(conn, sql)
155
141
  log_yield(sql){conn.create_command(sql).execute_non_query}
156
142
  end
157
143
 
158
- # We use the transactions rollback method to rollback.
159
- def rollback_transaction(t, opts={})
160
- return super if supports_savepoints?
161
- log_yield(TRANSACTION_COMMIT){t.rollback}
162
- end
163
-
164
144
  # Allow extending the given connection when it is first created.
165
145
  # By default, just returns the connection.
166
146
  def setup_connection(conn)
@@ -170,6 +150,8 @@ module Sequel
170
150
 
171
151
  # Dataset class for Sequel::DataObjects::Database objects.
172
152
  class Dataset < Sequel::Dataset
153
+ Database::DatasetClass = self
154
+
173
155
  # Execute the SQL on the database and yield the rows as hashes
174
156
  # with symbol keys.
175
157
  def fetch_rows(sql)