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