sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,155 @@
1
+ Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
2
+
3
+ module Sequel
4
+ module MySQL
5
+ # This module is used by the mysql and mysql2 adapters to support
6
+ # prepared statements and stored procedures.
7
+ module PreparedStatements
8
+ module DatabaseMethods
9
+ # Support stored procedures on MySQL
10
+ def call_sproc(name, opts={}, &block)
11
+ args = opts[:args] || []
12
+ execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
13
+ end
14
+
15
+ # Executes the given SQL using an available connection, yielding the
16
+ # connection if the block is given.
17
+ def execute(sql, opts={}, &block)
18
+ if opts[:sproc]
19
+ call_sproc(sql, opts, &block)
20
+ elsif sql.is_a?(Symbol)
21
+ execute_prepared_statement(sql, opts, &block)
22
+ else
23
+ synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def add_prepared_statements_cache(conn)
30
+ class << conn
31
+ attr_accessor :prepared_statements
32
+ end
33
+ conn.prepared_statements = {}
34
+ end
35
+
36
+ # Executes a prepared statement on an available connection. If the
37
+ # prepared statement already exists for the connection and has the same
38
+ # SQL, reuse it, otherwise, prepare the new statement. Because of the
39
+ # usual MySQL stupidity, we are forced to name arguments via separate
40
+ # SET queries. Use @sequel_arg_N (for N starting at 1) for these
41
+ # arguments.
42
+ def execute_prepared_statement(ps_name, opts, &block)
43
+ args = opts[:arguments]
44
+ ps = prepared_statement(ps_name)
45
+ sql = ps.prepared_sql
46
+ synchronize(opts[:server]) do |conn|
47
+ unless conn.prepared_statements[ps_name] == sql
48
+ conn.prepared_statements[ps_name] = sql
49
+ _execute(conn, "PREPARE #{ps_name} FROM #{literal(sql)}", opts)
50
+ end
51
+ i = 0
52
+ _execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
53
+ opts = opts.merge(:log_sql=>" (#{sql})") if ps.log_sql
54
+ _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
55
+ end
56
+ end
57
+
58
+ end
59
+ module DatasetMethods
60
+ include Sequel::Dataset::StoredProcedures
61
+
62
+ # Methods to add to MySQL prepared statement calls without using a
63
+ # real database prepared statement and bound variables.
64
+ module CallableStatementMethods
65
+ # Extend given dataset with this module so subselects inside subselects in
66
+ # prepared statements work.
67
+ def subselect_sql_append(sql, ds)
68
+ ps = ds.to_prepared_statement(:select).clone(:append_sql => sql)
69
+ ps.extend(CallableStatementMethods)
70
+ ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
71
+ ps.prepared_args = prepared_args
72
+ ps.prepared_sql
73
+ end
74
+ end
75
+
76
+ # Methods for MySQL prepared statements using the native driver.
77
+ module PreparedStatementMethods
78
+ include Sequel::Dataset::UnnumberedArgumentMapper
79
+
80
+ # Raise a more obvious error if you attempt to call a unnamed prepared statement.
81
+ def call(*)
82
+ raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
83
+ super
84
+ end
85
+
86
+ private
87
+
88
+ # Execute the prepared statement with the bind arguments instead of
89
+ # the given SQL.
90
+ def execute(sql, opts={}, &block)
91
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
92
+ end
93
+
94
+ # Same as execute, explicit due to intricacies of alias and super.
95
+ def execute_dui(sql, opts={}, &block)
96
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
97
+ end
98
+
99
+ # Same as execute, explicit due to intricacies of alias and super.
100
+ def execute_insert(sql, opts={}, &block)
101
+ super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
102
+ end
103
+ end
104
+
105
+ # Methods for MySQL stored procedures using the native driver.
106
+ module StoredProcedureMethods
107
+ include Sequel::Dataset::StoredProcedureMethods
108
+
109
+ private
110
+
111
+ # Execute the database stored procedure with the stored arguments.
112
+ def execute(sql, opts={}, &block)
113
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
114
+ end
115
+
116
+ # Same as execute, explicit due to intricacies of alias and super.
117
+ def execute_dui(sql, opts={}, &block)
118
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
119
+ end
120
+ end
121
+
122
+ # MySQL is different in that it supports prepared statements but not bound
123
+ # variables outside of prepared statements. The default implementation
124
+ # breaks the use of subselects in prepared statements, so extend the
125
+ # temporary prepared statement that this creates with a module that
126
+ # fixes it.
127
+ def call(type, bind_arguments={}, *values, &block)
128
+ ps = to_prepared_statement(type, values)
129
+ ps.extend(CallableStatementMethods)
130
+ ps.call(bind_arguments, &block)
131
+ end
132
+
133
+ # Store the given type of prepared statement in the associated database
134
+ # with the given name.
135
+ def prepare(type, name=nil, *values)
136
+ ps = to_prepared_statement(type, values)
137
+ ps.extend(PreparedStatementMethods)
138
+ if name
139
+ ps.prepared_statement_name = name
140
+ db.set_prepared_statement(name, ps)
141
+ end
142
+ ps
143
+ end
144
+
145
+ private
146
+
147
+ # Extend the dataset with the MySQL stored procedure methods.
148
+ def prepare_extend_sproc(ds)
149
+ ds.extend(StoredProcedureMethods)
150
+ end
151
+
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,9 +1,13 @@
1
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
+
1
3
  module Sequel
2
4
  module Oracle
3
5
  module DatabaseMethods
4
6
  TEMPORARY = 'GLOBAL TEMPORARY '.freeze
5
7
  AUTOINCREMENT = ''.freeze
6
8
 
9
+ attr_accessor :autosequence
10
+
7
11
  def create_sequence(name, opts={})
8
12
  self << create_sequence_sql(name, opts)
9
13
  end
@@ -12,6 +16,10 @@ module Sequel
12
16
  self << create_trigger_sql(*args)
13
17
  end
14
18
 
19
+ def current_user
20
+ @current_user ||= metadata_dataset.get{sys_context('USERENV', 'CURRENT_USER')}
21
+ end
22
+
15
23
  def drop_sequence(name)
16
24
  self << drop_sequence_sql(name)
17
25
  end
@@ -21,33 +29,59 @@ module Sequel
21
29
  :oracle
22
30
  end
23
31
 
24
- def tables(opts={})
25
- ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE')
26
- ds.map{|r| ds.send(:output_identifier, r[:tname])}
32
+ # Oracle namespaces indexes per table.
33
+ def global_index_namespace?
34
+ false
27
35
  end
28
36
 
29
- def table_exists?(name)
30
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
37
+ def tables(opts={})
38
+ m = output_identifier_meth
39
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE').map{|r| m.call(r[:tname])}
31
40
  end
32
41
 
33
42
  def views(opts={})
34
- ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW')
35
- ds.map{|r| ds.send(:output_identifier, r[:tname])}
43
+ m = output_identifier_meth
44
+ metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW').map{|r| m.call(r[:tname])}
36
45
  end
37
46
 
38
47
  def view_exists?(name)
39
- from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'VIEW').count > 0
48
+ m = input_identifier_meth
49
+ metadata_dataset.from(:tab).filter(:tname =>m.call(name), :tabtype => 'VIEW').count > 0
40
50
  end
41
51
 
42
52
  private
43
53
 
44
- def auto_increment_sql
45
- AUTOINCREMENT
54
+ # Handle Oracle specific ALTER TABLE SQL
55
+ def alter_table_sql(table, op)
56
+ case op[:op]
57
+ when :add_column
58
+ if op[:primary_key]
59
+ sqls = []
60
+ sqls << alter_table_sql(table, op.merge(:primary_key=>nil))
61
+ if op[:auto_increment]
62
+ seq_name = default_sequence_name(table, op[:name])
63
+ sqls << drop_sequence_sql(seq_name)
64
+ sqls << create_sequence_sql(seq_name, op)
65
+ sqls << "UPDATE #{quote_schema_table(table)} SET #{quote_identifier(op[:name])} = #{seq_name}.nextval"
66
+ end
67
+ sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"
68
+ sqls
69
+ else
70
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
71
+ end
72
+ when :set_column_null
73
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{op[:null] ? 'NULL' : 'NOT NULL'}"
74
+ when :set_column_type
75
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{type_literal(op)}"
76
+ when :set_column_default
77
+ "ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} DEFAULT #{literal(op[:default])}"
78
+ else
79
+ super(table, op)
80
+ end
46
81
  end
47
82
 
48
- # SQL fragment for showing a table is temporary
49
- def temporary_table_sql
50
- TEMPORARY
83
+ def auto_increment_sql
84
+ AUTOINCREMENT
51
85
  end
52
86
 
53
87
  def create_sequence_sql(name, opts={})
@@ -65,7 +99,7 @@ module Sequel
65
99
  drop_seq_statement = nil
66
100
  generator.columns.each do |c|
67
101
  if c[:auto_increment]
68
- c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
102
+ c[:sequence_name] ||= default_sequence_name(name, c[:name])
69
103
  unless c[:create_sequence] == false
70
104
  drop_seq_statement = drop_sequence_sql(c[:sequence_name])
71
105
  statements << create_sequence_sql(c[:sequence_name], c)
@@ -97,14 +131,137 @@ module Sequel
97
131
  sql
98
132
  end
99
133
 
134
+ def default_sequence_name(table, column)
135
+ "seq_#{table}_#{column}"
136
+ end
137
+
100
138
  def drop_sequence_sql(name)
101
139
  "DROP SEQUENCE #{quote_identifier(name)}"
102
140
  end
141
+
142
+ def remove_cached_schema(table)
143
+ @primary_key_sequences.delete(table)
144
+ super
145
+ end
146
+
147
+ def sequence_for_table(table)
148
+ return nil unless autosequence
149
+ @primary_key_sequences.fetch(table) do |key|
150
+ pk = schema(table).select{|k, v| v[:primary_key]}
151
+ @primary_key_sequences[table] = if pk.length == 1
152
+ seq = "seq_#{table}_#{pk.first.first}"
153
+ seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
154
+ end
155
+ end
156
+ end
157
+
158
+ # Oracle's integer/:number type handles larger values than
159
+ # most other databases's bigint types, so it should be
160
+ # safe to use for Bignum.
161
+ def type_literal_generic_bignum(column)
162
+ :integer
163
+ end
164
+
165
+ # Oracle doesn't have a time type, so use timestamp for all
166
+ # time columns.
167
+ def type_literal_generic_time(column)
168
+ :timestamp
169
+ end
170
+
171
+ # Oracle doesn't have a boolean type or even a reasonable
172
+ # facsimile. Using a char(1) seems to be the recommended way.
173
+ def type_literal_generic_trueclass(column)
174
+ :'char(1)'
175
+ end
176
+
177
+ # SQL fragment for showing a table is temporary
178
+ def temporary_table_sql
179
+ TEMPORARY
180
+ end
103
181
  end
104
182
 
105
183
  module DatasetMethods
106
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order limit lock')
184
+ include EmulateOffsetWithRowNumber
185
+
186
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having compounds order lock')
187
+ ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
188
+ SPACE = Dataset::SPACE
189
+ APOS = Dataset::APOS
190
+ APOS_RE = Dataset::APOS_RE
191
+ DOUBLE_APOS = Dataset::DOUBLE_APOS
192
+ FROM = Dataset::FROM
193
+ BITCOMP_OPEN = "((0 - ".freeze
194
+ BITCOMP_CLOSE = ") - 1)".freeze
195
+ ILIKE_0 = "(UPPER(".freeze
196
+ ILIKE_1 = ") ".freeze
197
+ ILIKE_2 = ' UPPER('.freeze
198
+ ILIKE_3 = "))".freeze
199
+ LIKE = 'LIKE'.freeze
200
+ NOT_LIKE = 'NOT LIKE'.freeze
201
+ TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
202
+ TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
203
+ BOOL_FALSE = "'N'".freeze
204
+ BOOL_TRUE = "'Y'".freeze
205
+ HSTAR = "H*".freeze
206
+ DUAL = ['DUAL'.freeze].freeze
207
+
208
+ # Oracle needs to emulate bitwise operators and ILIKE/NOT ILIKE operators.
209
+ def complex_expression_sql_append(sql, op, args)
210
+ case op
211
+ when :&
212
+ sql << complex_expression_arg_pairs(args){|a, b| "CAST(BITAND(#{literal(a)}, #{literal(b)}) AS INTEGER)"}
213
+ when :|
214
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} - #{complex_expression_sql(:&, [a, b])} + #{literal(b)})"}
215
+ when :^
216
+ sql << complex_expression_arg_pairs(args){|*x| "(#{complex_expression_sql(:|, x)} - #{complex_expression_sql(:&, x)})"}
217
+ when :'B~'
218
+ sql << BITCOMP_OPEN
219
+ literal_append(sql, args.at(0))
220
+ sql << BITCOMP_CLOSE
221
+ when :<<
222
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
223
+ when :>>
224
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
225
+ when :%
226
+ sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
227
+ when :ILIKE, :'NOT ILIKE'
228
+ sql << ILIKE_0
229
+ literal_append(sql, args.at(0))
230
+ sql << ILIKE_1
231
+ sql << (op == :ILIKE ? LIKE : NOT_LIKE)
232
+ sql<< ILIKE_2
233
+ literal_append(sql, args.at(1))
234
+ sql << ILIKE_3
235
+ else
236
+ super
237
+ end
238
+ end
107
239
 
240
+ # Oracle doesn't support CURRENT_TIME, as it doesn't have
241
+ # a type for storing just time values without a date, so
242
+ # use CURRENT_TIMESTAMP in its place.
243
+ def constant_sql_append(sql, c)
244
+ if c == :CURRENT_TIME
245
+ super(sql, :CURRENT_TIMESTAMP)
246
+ else
247
+ super
248
+ end
249
+ end
250
+
251
+ # Oracle treats empty strings like NULL values, and doesn't support
252
+ # char_length, so make char_length use length with a nonempty string.
253
+ # Unfortunately, as Oracle treats the empty string as NULL, there is
254
+ # no way to get trim to return an empty string instead of nil if
255
+ # the string only contains spaces.
256
+ def emulated_function_sql_append(sql, f)
257
+ case f.f
258
+ when :char_length
259
+ literal_append(sql, Sequel::SQL::Function.new(:length, Sequel.join([f.args.first, 'x'])) - 1)
260
+ else
261
+ super
262
+ end
263
+ end
264
+
108
265
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
109
266
  def except(dataset, opts={})
110
267
  opts = {:all=>opts} unless opts.is_a?(Hash)
@@ -112,19 +269,10 @@ module Sequel
112
269
  compound_clone(:minus, dataset, opts)
113
270
  end
114
271
 
272
+ # Use a custom expression with EXISTS to determine whether a dataset
273
+ # is empty.
115
274
  def empty?
116
- db[:dual].where(exists).get(1) == nil
117
- end
118
-
119
- # If this dataset is associated with a sequence, return the most recently
120
- # inserted sequence value.
121
- def insert(*args)
122
- r = super
123
- if s = opts[:sequence]
124
- with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
125
- else
126
- r
127
- end
275
+ db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
128
276
  end
129
277
 
130
278
  # Oracle requires SQL standard datetimes
@@ -139,16 +287,61 @@ module Sequel
139
287
  clone(:sequence=>s)
140
288
  end
141
289
 
290
+ # Handle LIMIT by using a unlimited subselect filtered with ROWNUM.
291
+ def select_sql
292
+ if (limit = @opts[:limit]) && !@opts[:sql]
293
+ ds = clone(:limit=>nil)
294
+ # Lock doesn't work in subselects, so don't use a subselect when locking.
295
+ # Don't use a subselect if custom SQL is used, as it breaks somethings.
296
+ ds = ds.from_self unless @opts[:lock]
297
+ sql = @opts[:append_sql] || ''
298
+ subselect_sql_append(sql, ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
299
+ sql
300
+ else
301
+ super
302
+ end
303
+ end
304
+
305
+ # Oracle requires recursive CTEs to have column aliases.
306
+ def recursive_cte_requires_column_aliases?
307
+ true
308
+ end
309
+
310
+ # Oracle supports GROUP BY CUBE
311
+ def supports_group_cube?
312
+ true
313
+ end
314
+
315
+ # Oracle supports GROUP BY ROLLUP
316
+ def supports_group_rollup?
317
+ true
318
+ end
319
+
142
320
  # Oracle does not support INTERSECT ALL or EXCEPT ALL
143
321
  def supports_intersect_except_all?
144
322
  false
145
323
  end
324
+
325
+ # Oracle does not support IS TRUE.
326
+ def supports_is_true?
327
+ false
328
+ end
329
+
330
+ # Oracle does not support SELECT *, column
331
+ def supports_select_all_and_column?
332
+ false
333
+ end
146
334
 
147
335
  # Oracle supports timezones in literal timestamps.
148
336
  def supports_timestamp_timezones?
149
337
  true
150
338
  end
151
339
 
340
+ # Oracle does not support WHERE 'Y' for WHERE TRUE.
341
+ def supports_where_true?
342
+ false
343
+ end
344
+
152
345
  # Oracle supports window functions
153
346
  def supports_window_functions?
154
347
  true
@@ -158,25 +351,54 @@ module Sequel
158
351
 
159
352
  # Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
160
353
  # the use of AS anywhere, so this disables it in all cases.
161
- def as_sql(expression, aliaz)
162
- "#{expression} #{quote_identifier(aliaz)}"
354
+ def as_sql_append(sql, aliaz)
355
+ sql << SPACE
356
+ quote_identifier_append(sql, aliaz)
163
357
  end
164
358
 
165
359
  # The strftime format to use when literalizing the time.
166
360
  def default_timestamp_format
167
- "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
361
+ TIMESTAMP_FORMAT
362
+ end
363
+
364
+ # If this dataset is associated with a sequence, return the most recently
365
+ # inserted sequence value.
366
+ def execute_insert(sql, opts={})
367
+ f = @opts[:from]
368
+ super(sql, {:table=>(f.first if f), :sequence=>@opts[:sequence]}.merge(opts))
168
369
  end
169
370
 
170
371
  # Use a colon for the timestamp offset, since Oracle appears to require it.
171
372
  def format_timestamp_offset(hour, minute)
172
- sprintf("%+03i:%02i", hour, minute)
373
+ sprintf(TIMESTAMP_OFFSET_FORMAT, hour, minute)
374
+ end
375
+
376
+ # Oracle doesn't support empty values when inserting.
377
+ def insert_supports_empty_values?
378
+ false
379
+ end
380
+
381
+ # Use string in hex format for blob data.
382
+ def literal_blob_append(sql, v)
383
+ sql << APOS << v.unpack(HSTAR).first << APOS
384
+ end
385
+
386
+ # Oracle uses 'N' for false values.
387
+ def literal_false
388
+ BOOL_FALSE
173
389
  end
174
390
 
175
391
  # Oracle uses the SQL standard of only doubling ' inside strings.
176
- def literal_string(v)
177
- "'#{v.gsub("'", "''")}'"
392
+ def literal_string_append(sql, v)
393
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
394
+ end
395
+
396
+ # Oracle uses 'Y' for true values.
397
+ def literal_true
398
+ BOOL_TRUE
178
399
  end
179
400
 
401
+ # Use the Oracle-specific SQL clauses (no limit, since it is emulated).
180
402
  def select_clause_methods
181
403
  SELECT_CLAUSE_METHODS
182
404
  end
@@ -186,18 +408,8 @@ module Sequel
186
408
  # so add the dummy DUAL table if the dataset doesn't select
187
409
  # from a table.
188
410
  def select_from_sql(sql)
189
- sql << " FROM #{source_list(@opts[:from] || ['DUAL'])}"
190
- end
191
-
192
- # Oracle requires a subselect to do limit and offset
193
- def select_limit_sql(sql)
194
- if limit = @opts[:limit]
195
- if (offset = @opts[:offset]) && (offset > 0)
196
- sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
197
- else
198
- sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
199
- end
200
- end
411
+ sql << FROM
412
+ source_list_append(sql, @opts[:from] || DUAL)
201
413
  end
202
414
  end
203
415
  end