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
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  # ---------------------
4
- # :section: User Methods relating to SQL Creation
4
+ # :section: 3 - User Methods relating to SQL Creation
5
5
  # These are methods you can call to see what SQL will be generated by the dataset.
6
6
  # ---------------------
7
7
 
@@ -20,7 +20,7 @@ module Sequel
20
20
  # DB.select(1).where(DB[:items].exists)
21
21
  # # SELECT 1 WHERE (EXISTS (SELECT * FROM items))
22
22
  def exists
23
- LiteralString.new("EXISTS (#{select_sql})")
23
+ SQL::PlaceholderLiteralString.new(EXISTS, [self], true)
24
24
  end
25
25
 
26
26
  # Returns an INSERT SQL query string. See +insert+.
@@ -49,10 +49,6 @@ module Sequel
49
49
  end
50
50
  when Dataset, Array, LiteralString
51
51
  values = vals
52
- else
53
- if vals.respond_to?(:values) && (v = vals.values).is_a?(Hash)
54
- return insert_sql(v)
55
- end
56
52
  end
57
53
  when 2
58
54
  if (v0 = values.at(0)).is_a?(Array) && ((v1 = values.at(1)).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
@@ -61,7 +57,10 @@ module Sequel
61
57
  end
62
58
  end
63
59
 
64
- columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
60
+ if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
61
+ columns = [columns().last]
62
+ values = [DEFAULT]
63
+ end
65
64
  clone(:columns=>columns, :values=>values)._insert_sql
66
65
  end
67
66
 
@@ -75,41 +74,47 @@ module Sequel
75
74
  # DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
76
75
  #
77
76
  # If an unsupported object is given, an +Error+ is raised.
78
- def literal(v)
77
+ def literal_append(sql, v)
79
78
  case v
80
- when String
81
- return v if v.is_a?(LiteralString)
82
- v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
83
79
  when Symbol
84
- literal_symbol(v)
80
+ literal_symbol_append(sql, v)
81
+ when String
82
+ case v
83
+ when LiteralString
84
+ sql << v
85
+ when SQL::Blob
86
+ literal_blob_append(sql, v)
87
+ else
88
+ literal_string_append(sql, v)
89
+ end
85
90
  when Integer
86
- literal_integer(v)
91
+ sql << literal_integer(v)
87
92
  when Hash
88
- literal_hash(v)
93
+ literal_hash_append(sql, v)
89
94
  when SQL::Expression
90
- literal_expression(v)
95
+ literal_expression_append(sql, v)
91
96
  when Float
92
- literal_float(v)
97
+ sql << literal_float(v)
93
98
  when BigDecimal
94
- literal_big_decimal(v)
99
+ sql << literal_big_decimal(v)
95
100
  when NilClass
96
- literal_nil
101
+ sql << literal_nil
97
102
  when TrueClass
98
- literal_true
103
+ sql << literal_true
99
104
  when FalseClass
100
- literal_false
105
+ sql << literal_false
101
106
  when Array
102
- literal_array(v)
107
+ literal_array_append(sql, v)
103
108
  when Time
104
- literal_time(v)
109
+ sql << (v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v))
105
110
  when DateTime
106
- literal_datetime(v)
111
+ sql << literal_datetime(v)
107
112
  when Date
108
- literal_date(v)
113
+ sql << literal_date(v)
109
114
  when Dataset
110
- literal_dataset(v)
115
+ literal_dataset_append(sql, v)
111
116
  else
112
- literal_other(v)
117
+ literal_other_append(sql, v)
113
118
  end
114
119
  end
115
120
 
@@ -143,8 +148,8 @@ module Sequel
143
148
  if opts[:sql]
144
149
  static_sql(opts[:sql])
145
150
  else
146
- check_modification_allowed!
147
- raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where]
151
+ check_truncation_allowed!
152
+ raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where] || opts[:having]
148
153
  _truncate_sql(source_list(opts[:from]))
149
154
  end
150
155
  end
@@ -163,7 +168,7 @@ module Sequel
163
168
  end
164
169
 
165
170
  # ---------------------
166
- # :section: Internal Methods relating to SQL Creation
171
+ # :section: 9 - Internal Methods relating to SQL Creation
167
172
  # These methods, while public, are not designed to be used directly by the end user.
168
173
  # ---------------------
169
174
 
@@ -173,80 +178,239 @@ module Sequel
173
178
  clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
174
179
  end
175
180
 
181
+ # Map of emulated function names to native function names.
182
+ EMULATED_FUNCTION_MAP = {}
183
+
184
+ WILDCARD = LiteralString.new('*').freeze
185
+ ALL = ' ALL'.freeze
176
186
  AND_SEPARATOR = " AND ".freeze
187
+ APOS = "'".freeze
188
+ APOS_RE = /'/.freeze
189
+ ARRAY_EMPTY = '(NULL)'.freeze
190
+ AS = ' AS '.freeze
191
+ ASC = ' ASC'.freeze
177
192
  BOOL_FALSE = "'f'".freeze
178
193
  BOOL_TRUE = "'t'".freeze
179
- COMMA_SEPARATOR = ', '.freeze
180
- COLUMN_REF_RE1 = /\A(((?!__).)+)__(((?!___).)+)___(.+)\z/.freeze
181
- COLUMN_REF_RE2 = /\A(((?!___).)+)___(.+)\z/.freeze
182
- COLUMN_REF_RE3 = /\A(((?!__).)+)__(.+)\z/.freeze
194
+ BRACKET_CLOSE = ']'.freeze
195
+ BRACKET_OPEN = '['.freeze
196
+ CASE_ELSE = " ELSE ".freeze
197
+ CASE_END = " END)".freeze
198
+ CASE_OPEN = '(CASE'.freeze
199
+ CASE_THEN = " THEN ".freeze
200
+ CASE_WHEN = " WHEN ".freeze
201
+ CAST_OPEN = 'CAST('.freeze
202
+ COLUMN_REF_RE1 = Sequel::COLUMN_REF_RE1
203
+ COLUMN_REF_RE2 = Sequel::COLUMN_REF_RE2
204
+ COLUMN_REF_RE3 = Sequel::COLUMN_REF_RE3
205
+ COMMA = ', '.freeze
206
+ COMMA_SEPARATOR = COMMA
207
+ CONDITION_FALSE = '(1 = 0)'.freeze
208
+ CONDITION_TRUE = '(1 = 1)'.freeze
183
209
  COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
184
- COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
210
+ COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, WILDCARD).as(:count)
185
211
  DATASET_ALIAS_BASE_NAME = 't'.freeze
212
+ DEFAULT = LiteralString.new('DEFAULT').freeze
213
+ DEFAULT_VALUES = " DEFAULT VALUES".freeze
214
+ DELETE = 'DELETE'.freeze
215
+ DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
216
+ DESC = ' DESC'.freeze
217
+ DISTINCT = " DISTINCT".freeze
218
+ DOT = '.'.freeze
219
+ DOUBLE_APOS = "''".freeze
220
+ DOUBLE_QUOTE = '""'.freeze
221
+ EQUAL = ' = '.freeze
222
+ EXTRACT = 'extract('.freeze
223
+ EXISTS = ['EXISTS '.freeze].freeze
186
224
  FOR_UPDATE = ' FOR UPDATE'.freeze
225
+ FORMAT_DATE = "'%Y-%m-%d'".freeze
226
+ FORMAT_DATE_STANDARD = "DATE '%Y-%m-%d'".freeze
227
+ FORMAT_OFFSET = "%+03i%02i".freeze
228
+ FORMAT_TIMESTAMP_RE = /%[Nz]/.freeze
229
+ FORMAT_TIMESTAMP_USEC = ".%06d".freeze
230
+ FORMAT_USEC = '%N'.freeze
231
+ FRAME_ALL = "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING".freeze
232
+ FRAME_ROWS = "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW".freeze
233
+ FROM = ' FROM '.freeze
234
+ FUNCTION_EMPTY = '()'.freeze
235
+ GROUP_BY = " GROUP BY ".freeze
236
+ HAVING = " HAVING ".freeze
237
+ INSERT = "INSERT".freeze
238
+ INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'insert into columns values')
239
+ INTO = " INTO ".freeze
187
240
  IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
188
241
  IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
242
+ LIMIT = " LIMIT ".freeze
189
243
  N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
244
+ NOT_SPACE = 'NOT '.freeze
190
245
  NULL = "NULL".freeze
246
+ NULLS_FIRST = " NULLS FIRST".freeze
247
+ NULLS_LAST = " NULLS LAST".freeze
248
+ OFFSET = " OFFSET ".freeze
249
+ ON = ' ON '.freeze
250
+ ON_PAREN = " ON (".freeze
251
+ ORDER_BY = " ORDER BY ".freeze
252
+ ORDER_BY_NS = "ORDER BY ".freeze
253
+ OVER = ' OVER '.freeze
254
+ PAREN_CLOSE = ')'.freeze
255
+ PAREN_OPEN = '('.freeze
256
+ PAREN_SPACE_OPEN = ' ('.freeze
257
+ PARTITION_BY = "PARTITION BY ".freeze
191
258
  QUALIFY_KEYS = [:select, :where, :having, :order, :group]
192
259
  QUESTION_MARK = '?'.freeze
193
- DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'from where')
194
- INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'into columns values')
195
- SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with distinct columns from join where group having compounds order limit lock')
196
- UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'table set where')
260
+ QUESTION_MARK_RE = /\?/.freeze
261
+ QUOTE = '"'.freeze
262
+ QUOTE_RE = /"/.freeze
263
+ RETURNING = " RETURNING ".freeze
264
+ SELECT = 'SELECT'.freeze
265
+ SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with select distinct columns from join where group having compounds order limit lock')
266
+ SET = ' SET '.freeze
267
+ SPACE = ' '.freeze
268
+ SQL_WITH = "WITH ".freeze
269
+ SPACE_WITH = " WITH ".freeze
270
+ TILDE = '~'.freeze
197
271
  TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
198
272
  STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
199
273
  TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
200
- WILDCARD = LiteralString.new('*').freeze
201
- SQL_WITH = "WITH ".freeze
274
+ REGEXP_OPERATORS = ::Sequel::SQL::ComplexExpression::REGEXP_OPERATORS
275
+ UNDERSCORE = '_'.freeze
276
+ UPDATE = 'UPDATE'.freeze
277
+ UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
278
+ USING = ' USING ('.freeze
279
+ VALUES = " VALUES ".freeze
280
+ V190 = '1.9.0'.freeze
281
+ WHERE = " WHERE ".freeze
282
+
283
+ PUBLIC_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
284
+ literal
285
+ aliased_expression_sql
286
+ array_sql
287
+ boolean_constant_sql
288
+ case_expression_sql
289
+ cast_sql
290
+ column_all_sql
291
+ complex_expression_sql
292
+ constant_sql
293
+ delayed_evaluation_sql
294
+ function_sql
295
+ join_clause_sql
296
+ join_on_clause_sql
297
+ join_using_clause_sql
298
+ negative_boolean_constant_sql
299
+ ordered_expression_sql
300
+ placeholder_literal_string_sql
301
+ qualified_identifier_sql
302
+ quote_identifier
303
+ quote_schema_table
304
+ quoted_identifier
305
+ subscript_sql
306
+ window_sql
307
+ window_function_sql
308
+ END
309
+ PRIVATE_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
310
+ as_sql
311
+ column_list
312
+ compound_dataset_sql
313
+ expression_list
314
+ literal_array
315
+ literal_blob
316
+ literal_dataset
317
+ literal_expression
318
+ literal_hash
319
+ literal_other
320
+ literal_string
321
+ literal_symbol
322
+ source_list
323
+ subselect_sql
324
+ table_ref
325
+ END
326
+ def self.def_append_methods(meths)
327
+ meths.each do |meth|
328
+ class_eval(<<-END, __FILE__, __LINE__ + 1)
329
+ def #{meth}(*args, &block)
330
+ s = ''
331
+ #{meth}_append(s, *args, &block)
332
+ s
333
+ end
334
+ END
335
+ end
336
+ end
337
+ def_append_methods(PUBLIC_APPEND_METHODS + PRIVATE_APPEND_METHODS)
338
+ private *PRIVATE_APPEND_METHODS
202
339
 
203
340
  # SQL fragment for AliasedExpression
204
- def aliased_expression_sql(ae)
205
- as_sql(literal(ae.expression), ae.aliaz)
341
+ def aliased_expression_sql_append(sql, ae)
342
+ literal_append(sql, ae.expression)
343
+ as_sql_append(sql, ae.aliaz)
206
344
  end
207
345
 
208
346
  # SQL fragment for Array
209
- def array_sql(a)
210
- a.empty? ? '(NULL)' : "(#{expression_list(a)})"
347
+ def array_sql_append(sql, a)
348
+ if a.empty?
349
+ sql << ARRAY_EMPTY
350
+ else
351
+ sql << PAREN_OPEN
352
+ expression_list_append(sql, a)
353
+ sql << PAREN_CLOSE
354
+ end
211
355
  end
212
356
 
213
357
  # SQL fragment for BooleanConstants
214
- def boolean_constant_sql(constant)
215
- literal(constant)
358
+ def boolean_constant_sql_append(sql, constant)
359
+ if (constant == true || constant == false) && !supports_where_true?
360
+ sql << (constant == true ? CONDITION_TRUE : CONDITION_FALSE)
361
+ else
362
+ literal_append(sql, constant)
363
+ end
216
364
  end
217
365
 
218
366
  # SQL fragment for CaseExpression
219
- def case_expression_sql(ce)
220
- sql = '(CASE '
221
- sql << "#{literal(ce.expression)} " if ce.expression?
222
- ce.conditions.collect{ |c,r|
223
- sql << "WHEN #{literal(c)} THEN #{literal(r)} "
224
- }
225
- sql << "ELSE #{literal(ce.default)} END)"
367
+ def case_expression_sql_append(sql, ce)
368
+ sql << CASE_OPEN
369
+ if ce.expression?
370
+ sql << SPACE
371
+ literal_append(sql, ce.expression)
372
+ end
373
+ w = CASE_WHEN
374
+ t = CASE_THEN
375
+ ce.conditions.each do |c,r|
376
+ sql << w
377
+ literal_append(sql, c)
378
+ sql << t
379
+ literal_append(sql, r)
380
+ end
381
+ sql << CASE_ELSE
382
+ literal_append(sql, ce.default)
383
+ sql << CASE_END
226
384
  end
227
385
 
228
386
  # SQL fragment for the SQL CAST expression
229
- def cast_sql(expr, type)
230
- "CAST(#{literal(expr)} AS #{db.cast_type_literal(type)})"
387
+ def cast_sql_append(sql, expr, type)
388
+ sql << CAST_OPEN
389
+ literal_append(sql, expr)
390
+ sql << AS << db.cast_type_literal(type).to_s
391
+ sql << PAREN_CLOSE
231
392
  end
232
393
 
233
394
  # SQL fragment for specifying all columns in a given table
234
- def column_all_sql(ca)
235
- "#{quote_schema_table(ca.table)}.*"
395
+ def column_all_sql_append(sql, ca)
396
+ qualified_identifier_sql_append(sql, ca.table, WILDCARD)
236
397
  end
237
398
 
238
- # SQL fragment for complex expressions
239
- def complex_expression_sql(op, args)
399
+ # SQL fragment for the complex expression.
400
+ def complex_expression_sql_append(sql, op, args)
240
401
  case op
241
402
  when *IS_OPERATORS
242
403
  r = args.at(1)
243
404
  if r.nil? || supports_is_true?
244
405
  raise(InvalidOperation, 'Invalid argument used for IS operator') unless v = IS_LITERALS[r]
245
- "(#{literal(args.at(0))} #{op} #{v})"
406
+ sql << PAREN_OPEN
407
+ literal_append(sql, args.at(0))
408
+ sql << SPACE << op.to_s << SPACE
409
+ sql << v << PAREN_CLOSE
246
410
  elsif op == :IS
247
- complex_expression_sql(:"=", args)
411
+ complex_expression_sql_append(sql, :"=", args)
248
412
  else
249
- complex_expression_sql(:OR, [SQL::BooleanExpression.new(:"!=", *args), SQL::BooleanExpression.new(:IS, args.at(0), nil)])
413
+ complex_expression_sql_append(sql, :OR, [SQL::BooleanExpression.new(:"!=", *args), SQL::BooleanExpression.new(:IS, args.at(0), nil)])
250
414
  end
251
415
  when :IN, :"NOT IN"
252
416
  cols = args.at(0)
@@ -256,147 +420,220 @@ module Sequel
256
420
  val_array = true
257
421
  empty_val_array = vals == []
258
422
  end
259
- if col_array
260
- if empty_val_array
261
- if op == :IN
262
- literal(SQL::BooleanExpression.from_value_pairs(cols.to_a.map{|x| [x, x]}, :AND, true))
263
- else
264
- literal(1=>1)
265
- end
266
- elsif !supports_multiple_column_in?
423
+ if empty_val_array
424
+ literal_append(sql, empty_array_value(op, cols))
425
+ elsif col_array
426
+ if !supports_multiple_column_in?
267
427
  if val_array
268
428
  expr = SQL::BooleanExpression.new(:OR, *vals.to_a.map{|vs| SQL::BooleanExpression.from_value_pairs(cols.to_a.zip(vs).map{|c, v| [c, v]})})
269
- literal(op == :IN ? expr : ~expr)
429
+ literal_append(sql, op == :IN ? expr : ~expr)
270
430
  else
271
431
  old_vals = vals
432
+ vals = vals.naked if vals.is_a?(Sequel::Dataset)
272
433
  vals = vals.to_a
273
434
  val_cols = old_vals.columns
274
- complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
435
+ complex_expression_sql_append(sql, op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
275
436
  end
276
437
  else
277
438
  # If the columns and values are both arrays, use array_sql instead of
278
439
  # literal so that if values is an array of two element arrays, it
279
440
  # will be treated as a value list instead of a condition specifier.
280
- "(#{literal(cols)} #{op} #{val_array ? array_sql(vals) : literal(vals)})"
281
- end
282
- else
283
- if empty_val_array
284
- if op == :IN
285
- literal(SQL::BooleanExpression.from_value_pairs([[cols, cols]], :AND, true))
441
+ sql << PAREN_OPEN
442
+ literal_append(sql, cols)
443
+ sql << SPACE << op.to_s << SPACE
444
+ if val_array
445
+ array_sql_append(sql, vals)
286
446
  else
287
- literal(1=>1)
447
+ literal_append(sql, vals)
288
448
  end
289
- else
290
- "(#{literal(cols)} #{op} #{literal(vals)})"
449
+ sql << PAREN_CLOSE
291
450
  end
451
+ else
452
+ sql << PAREN_OPEN
453
+ literal_append(sql, cols)
454
+ sql << SPACE << op.to_s << SPACE
455
+ literal_append(sql, vals)
456
+ sql << PAREN_CLOSE
292
457
  end
293
458
  when *TWO_ARITY_OPERATORS
294
- "(#{literal(args.at(0))} #{op} #{literal(args.at(1))})"
459
+ if REGEXP_OPERATORS.include?(op) && !supports_regexp?
460
+ raise InvalidOperation, "Pattern matching via regular expressions is not supported on #{db.database_type}"
461
+ end
462
+ sql << PAREN_OPEN
463
+ literal_append(sql, args.at(0))
464
+ sql << SPACE << op.to_s << SPACE
465
+ literal_append(sql, args.at(1))
466
+ sql << PAREN_CLOSE
295
467
  when *N_ARITY_OPERATORS
296
- "(#{args.collect{|a| literal(a)}.join(" #{op} ")})"
468
+ sql << PAREN_OPEN
469
+ c = false
470
+ op_str = " #{op} "
471
+ args.each do |a|
472
+ sql << op_str if c
473
+ literal_append(sql, a)
474
+ c ||= true
475
+ end
476
+ sql << PAREN_CLOSE
297
477
  when :NOT
298
- "NOT #{literal(args.at(0))}"
478
+ sql << NOT_SPACE
479
+ literal_append(sql, args.at(0))
299
480
  when :NOOP
300
- literal(args.at(0))
481
+ literal_append(sql, args.at(0))
301
482
  when :'B~'
302
- "~#{literal(args.at(0))}"
483
+ sql << TILDE
484
+ literal_append(sql, args.at(0))
485
+ when :extract
486
+ sql << EXTRACT << args.at(0).to_s << FROM
487
+ literal_append(sql, args.at(1))
488
+ sql << PAREN_CLOSE
303
489
  else
304
490
  raise(InvalidOperation, "invalid operator #{op}")
305
491
  end
306
492
  end
307
493
 
308
494
  # SQL fragment for constants
309
- def constant_sql(constant)
310
- constant.to_s
495
+ def constant_sql_append(sql, constant)
496
+ sql << constant.to_s
497
+ end
498
+
499
+ # SQL fragment for delayed evaluations, evaluating the
500
+ # object and literalizing the returned value.
501
+ def delayed_evaluation_sql_append(sql, callable)
502
+ literal_append(sql, callable.call)
503
+ end
504
+
505
+ # SQL fragment specifying an emulated SQL function call.
506
+ # By default, assumes just the function name may need to
507
+ # be emulated, adapters should set an EMULATED_FUNCTION_MAP
508
+ # hash mapping emulated functions to native functions in
509
+ # their dataset class to setup the emulation.
510
+ def emulated_function_sql_append(sql, f)
511
+ _function_sql_append(sql, native_function_name(f.f), f.args)
311
512
  end
312
513
 
313
- # SQL fragment specifying an SQL function call
314
- def function_sql(f)
315
- args = f.args
316
- "#{f.f}#{args.empty? ? '()' : literal(args)}"
514
+ # SQL fragment specifying an SQL function call without emulation.
515
+ def function_sql_append(sql, f)
516
+ _function_sql_append(sql, f.f, f.args)
317
517
  end
318
518
 
319
519
  # SQL fragment specifying a JOIN clause without ON or USING.
320
- def join_clause_sql(jc)
520
+ def join_clause_sql_append(sql, jc)
321
521
  table = jc.table
322
522
  table_alias = jc.table_alias
323
523
  table_alias = nil if table == table_alias
324
- tref = table_ref(table)
325
- " #{join_type_sql(jc.join_type)} #{table_alias ? as_sql(tref, table_alias) : tref}"
524
+ sql << SPACE << join_type_sql(jc.join_type) << SPACE
525
+ identifier_append(sql, table)
526
+ as_sql_append(sql, table_alias) if table_alias
326
527
  end
327
528
 
328
529
  # SQL fragment specifying a JOIN clause with ON.
329
- def join_on_clause_sql(jc)
330
- "#{join_clause_sql(jc)} ON #{literal(filter_expr(jc.on))}"
530
+ def join_on_clause_sql_append(sql, jc)
531
+ join_clause_sql_append(sql, jc)
532
+ sql << ON
533
+ literal_append(sql, filter_expr(jc.on))
331
534
  end
332
535
 
333
536
  # SQL fragment specifying a JOIN clause with USING.
334
- def join_using_clause_sql(jc)
335
- "#{join_clause_sql(jc)} USING (#{column_list(jc.using)})"
537
+ def join_using_clause_sql_append(sql, jc)
538
+ join_clause_sql_append(sql, jc)
539
+ sql << USING
540
+ column_list_append(sql, jc.using)
541
+ sql << PAREN_CLOSE
336
542
  end
337
543
 
338
544
  # SQL fragment for NegativeBooleanConstants
339
- def negative_boolean_constant_sql(constant)
340
- "NOT #{boolean_constant_sql(constant)}"
545
+ def negative_boolean_constant_sql_append(sql, constant)
546
+ sql << NOT_SPACE
547
+ boolean_constant_sql_append(sql, constant)
341
548
  end
342
549
 
343
550
  # SQL fragment for the ordered expression, used in the ORDER BY
344
551
  # clause.
345
- def ordered_expression_sql(oe)
346
- s = "#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
552
+ def ordered_expression_sql_append(sql, oe)
553
+ literal_append(sql, oe.expression)
554
+ sql << (oe.descending ? DESC : ASC)
347
555
  case oe.nulls
348
556
  when :first
349
- "#{s} NULLS FIRST"
557
+ sql << NULLS_FIRST
350
558
  when :last
351
- "#{s} NULLS LAST"
352
- else
353
- s
559
+ sql << NULLS_LAST
354
560
  end
355
561
  end
356
562
 
357
563
  # SQL fragment for a literal string with placeholders
358
- def placeholder_literal_string_sql(pls)
564
+ def placeholder_literal_string_sql_append(sql, pls)
359
565
  args = pls.args
360
- s = if args.is_a?(Hash)
566
+ str = pls.str
567
+ sql << PAREN_OPEN if pls.parens
568
+ if args.is_a?(Hash)
361
569
  re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
362
- pls.str.gsub(re){literal(args[$1.to_sym])}
570
+ loop do
571
+ previous, q, str = str.partition(re)
572
+ sql << previous
573
+ literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
574
+ break if str.empty?
575
+ end
576
+ elsif str.is_a?(Array)
577
+ len = args.length
578
+ str.each_with_index do |s, i|
579
+ sql << s
580
+ literal_append(sql, args[i]) unless i == len
581
+ end
363
582
  else
364
583
  i = -1
365
- pls.str.gsub(QUESTION_MARK){literal(args.at(i+=1))}
584
+ loop do
585
+ previous, q, str = str.partition(QUESTION_MARK)
586
+ sql << previous
587
+ literal_append(sql, args.at(i+=1)) unless q.empty?
588
+ break if str.empty?
589
+ end
366
590
  end
367
- s = "(#{s})" if pls.parens
368
- s
591
+ sql << PAREN_CLOSE if pls.parens
369
592
  end
370
593
 
371
594
  # SQL fragment for the qualifed identifier, specifying
372
595
  # a table and a column (or schema and table).
373
- def qualified_identifier_sql(qcr)
374
- [qcr.table, qcr.column].map{|x| [SQL::QualifiedIdentifier, SQL::Identifier, Symbol].any?{|c| x.is_a?(c)} ? literal(x) : quote_identifier(x)}.join('.')
596
+ # If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
597
+ # column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
598
+ def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
599
+ identifier_append(sql, table)
600
+ sql << DOT
601
+ identifier_append(sql, column)
375
602
  end
376
603
 
377
604
  # Adds quoting to identifiers (columns and tables). If identifiers are not
378
605
  # being quoted, returns name as a string. If identifiers are being quoted
379
606
  # quote the name with quoted_identifier.
380
- def quote_identifier(name)
381
- return name if name.is_a?(LiteralString)
382
- name = name.value if name.is_a?(SQL::Identifier)
383
- name = input_identifier(name)
384
- name = quoted_identifier(name) if quote_identifiers?
385
- name
607
+ def quote_identifier_append(sql, name)
608
+ if name.is_a?(LiteralString)
609
+ sql << name
610
+ else
611
+ name = name.value if name.is_a?(SQL::Identifier)
612
+ name = input_identifier(name)
613
+ if quote_identifiers?
614
+ quoted_identifier_append(sql, name)
615
+ else
616
+ sql << name
617
+ end
618
+ end
386
619
  end
387
620
 
388
621
  # Separates the schema from the table and returns a string with them
389
622
  # quoted (if quoting identifiers)
390
- def quote_schema_table(table)
623
+ def quote_schema_table_append(sql, table)
391
624
  schema, table = schema_and_table(table)
392
- "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
625
+ if schema
626
+ quote_identifier_append(sql, schema)
627
+ sql << DOT
628
+ end
629
+ quote_identifier_append(sql, table)
393
630
  end
394
631
 
395
632
  # This method quotes the given name with the SQL standard double quote.
396
633
  # should be overridden by subclasses to provide quoting not matching the
397
634
  # SQL standard, such as backtick (used by MySQL and SQLite).
398
- def quoted_identifier(name)
399
- "\"#{name.to_s.gsub('"', '""')}\""
635
+ def quoted_identifier_append(sql, name)
636
+ sql << QUOTE << name.to_s.gsub(QUOTE_RE, DOUBLE_QUOTE) << QUOTE
400
637
  end
401
638
 
402
639
  # Split the schema information from the table
@@ -418,34 +655,59 @@ module Sequel
418
655
  end
419
656
 
420
657
  # SQL fragment for specifying subscripts (SQL array accesses)
421
- def subscript_sql(s)
422
- "#{literal(s.f)}[#{expression_list(s.sub)}]"
658
+ def subscript_sql_append(sql, s)
659
+ literal_append(sql, s.f)
660
+ sql << BRACKET_OPEN
661
+ expression_list_append(sql, s.sub)
662
+ sql << BRACKET_CLOSE
423
663
  end
424
664
 
425
665
  # The SQL fragment for the given window's options.
426
- def window_sql(opts)
666
+ def window_sql_append(sql, opts)
427
667
  raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
428
- window = literal(opts[:window]) if opts[:window]
429
- partition = "PARTITION BY #{expression_list(Array(opts[:partition]))}" if opts[:partition]
430
- order = "ORDER BY #{expression_list(Array(opts[:order]))}" if opts[:order]
431
- frame = case opts[:frame]
668
+ sql << PAREN_OPEN
669
+ window, part, order, frame = opts.values_at(:window, :partition, :order, :frame)
670
+ space = false
671
+ space_s = SPACE
672
+ if window
673
+ literal_append(sql, window)
674
+ space = true
675
+ end
676
+ if part
677
+ sql << space_s if space
678
+ sql << PARTITION_BY
679
+ expression_list_append(sql, Array(part))
680
+ space = true
681
+ end
682
+ if order
683
+ sql << space_s if space
684
+ sql << ORDER_BY_NS
685
+ expression_list_append(sql, Array(order))
686
+ space = true
687
+ end
688
+ case frame
432
689
  when nil
433
- nil
690
+ # nothing
434
691
  when :all
435
- "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
692
+ sql << space_s if space
693
+ sql << FRAME_ALL
436
694
  when :rows
437
- "ROWS UNBOUNDED PRECEDING"
695
+ sql << space_s if space
696
+ sql << FRAME_ROWS
438
697
  when String
439
- opts[:frame]
698
+ sql << space_s if space
699
+ sql << frame
440
700
  else
441
701
  raise Error, "invalid window frame clause, should be :all, :rows, a string, or nil"
442
702
  end
443
- "(#{[window, partition, order, frame].compact.join(' ')})"
703
+ sql << PAREN_CLOSE
444
704
  end
445
705
 
446
706
  # The SQL fragment for the given window function's function and window.
447
- def window_function_sql(function, window)
448
- "#{literal(function)} OVER #{literal(window)}"
707
+ def window_function_sql_append(sql, function, window)
708
+ literal_append(sql, function)
709
+ sql << OVER
710
+ literal_append(sql, window)
449
711
  end
450
712
 
451
713
  protected
@@ -468,6 +730,16 @@ module Sequel
468
730
 
469
731
  private
470
732
 
733
+ # Backbone of function_sql_append and emulated_function_sql_append.
734
+ def _function_sql_append(sql, name, args)
735
+ sql << name.to_s
736
+ if args.empty?
737
+ sql << FUNCTION_EMPTY
738
+ else
739
+ literal_append(sql, args)
740
+ end
741
+ end
742
+
471
743
  # Formats the truncate statement. Assumes the table given has already been
472
744
  # literalized.
473
745
  def _truncate_sql(table)
@@ -517,14 +789,10 @@ module Sequel
517
789
  options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
518
790
  end
519
791
 
520
- # Do a simple join of the arguments (which should be strings or symbols) separated by commas
521
- def argument_list(args)
522
- args.join(COMMA_SEPARATOR)
523
- end
524
-
525
792
  # SQL fragment for specifying an alias. expression should already be literalized.
526
- def as_sql(expression, aliaz)
527
- "#{expression} AS #{quote_identifier(aliaz)}"
793
+ def as_sql_append(sql, aliaz)
794
+ sql << AS
795
+ quote_identifier_append(sql, aliaz)
528
796
  end
529
797
 
530
798
  # Raise an InvalidOperation exception if deletion is not allowed
@@ -534,38 +802,88 @@ module Sequel
534
802
  raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
535
803
  end
536
804
 
805
+ # Alias of check_modification_allowed!
806
+ def check_truncation_allowed!
807
+ check_modification_allowed!
808
+ end
809
+
537
810
  # Prepare an SQL statement by calling all clause methods for the given statement type.
538
811
  def clause_sql(type)
539
- sql = type.to_s.upcase
812
+ sql = @opts[:append_sql] || sql_string_origin
540
813
  send("#{type}_clause_methods").each{|x| send(x, sql)}
541
814
  sql
542
815
  end
543
816
 
544
817
  # Converts an array of column names into a comma seperated string of
545
818
  # column names. If the array is empty, a wildcard (*) is returned.
546
- def column_list(columns)
547
- (columns.nil? || columns.empty?) ? WILDCARD : expression_list(columns)
819
+ def column_list_append(sql, columns)
820
+ if (columns.nil? || columns.empty?)
821
+ sql << WILDCARD
822
+ else
823
+ expression_list_append(sql, columns)
824
+ end
548
825
  end
549
-
826
+
827
+ # Yield each two pair of arguments to the block, which should
828
+ # return a string representing the SQL code for those
829
+ # two arguments. If more than 2 arguments are provided, all
830
+ # calls to the block # after the first will have a LiteralString
831
+ # as the first argument, representing the application of the block to
832
+ # the previous arguments.
833
+ def complex_expression_arg_pairs(args)
834
+ case args.length
835
+ when 1
836
+ literal(args.at(0))
837
+ when 2
838
+ yield args.at(0), args.at(1)
839
+ else
840
+ args.inject{|m, a| LiteralString.new(yield(m, a))}
841
+ end
842
+ end
843
+
844
+ # The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause.
845
+ def compound_dataset_sql_append(sql, ds)
846
+ subselect_sql_append(sql, ds)
847
+ end
848
+
550
849
  # The alias to use for datasets, takes a number to make sure the name is unique.
551
850
  def dataset_alias(number)
552
851
  :"#{DATASET_ALIAS_BASE_NAME}#{number}"
553
852
  end
554
853
 
854
+ # The strftime format to use when literalizing the time.
855
+ def default_timestamp_format
856
+ requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
857
+ end
858
+
555
859
  # The order of methods to call to build the DELETE SQL statement
556
860
  def delete_clause_methods
557
861
  DELETE_CLAUSE_METHODS
558
862
  end
559
863
 
864
+ def delete_delete_sql(sql)
865
+ sql << DELETE
866
+ end
867
+
560
868
  # Converts an array of expressions into a comma separated string of
561
869
  # expressions.
562
- def expression_list(columns)
563
- columns.map{|i| literal(i)}.join(COMMA_SEPARATOR)
870
+ def expression_list_append(sql, columns)
871
+ c = false
872
+ co = COMMA
873
+ columns.each do |col|
874
+ sql << co if c
875
+ literal_append(sql, col)
876
+ c ||= true
877
+ end
564
878
  end
565
-
566
- # The strftime format to use when literalizing the time.
567
- def default_timestamp_format
568
- requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
879
+
880
+ def empty_array_value(op, cols)
881
+ if Sequel.empty_array_handle_nulls
882
+ c = Array(cols)
883
+ SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
884
+ else
885
+ {1 => ((op == :IN) ? 0 : 1)}
886
+ end
569
887
  end
570
888
 
571
889
  # Format the timestamp based on the default_timestamp_format, with a couple
@@ -573,10 +891,10 @@ module Sequel
573
891
  # database supports them), and override %z to always use a numeric offset
574
892
  # of hours and minutes.
575
893
  def format_timestamp(v)
576
- v2 = Sequel.application_to_database_timestamp(v)
577
- fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
578
- if m == '%N'
579
- format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
894
+ v2 = db.from_application_timestamp(v)
895
+ fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
896
+ if m == FORMAT_USEC
897
+ format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < V190 ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
580
898
  else
581
899
  if supports_timestamp_timezones?
582
900
  # Would like to just use %z format, but it doesn't appear to work on Windows
@@ -591,13 +909,42 @@ module Sequel
591
909
 
592
910
  # Return the SQL timestamp fragment to use for the timezone offset.
593
911
  def format_timestamp_offset(hour, minute)
594
- sprintf("%+03i%02i", hour, minute)
912
+ sprintf(FORMAT_OFFSET, hour, minute)
595
913
  end
596
914
 
597
915
  # Return the SQL timestamp fragment to use for the fractional time part.
598
916
  # Should start with the decimal point. Uses 6 decimal places by default.
599
917
  def format_timestamp_usec(usec)
600
- sprintf(".%06d", usec)
918
+ sprintf(FORMAT_TIMESTAMP_USEC, usec)
919
+ end
920
+
921
+ # Append the value, but special case regular (non-literal, non-blob) strings
922
+ # so that they are considered as identifiers and not SQL strings.
923
+ def identifier_append(sql, v)
924
+ if v.is_a?(String)
925
+ case v
926
+ when LiteralString
927
+ sql << v
928
+ when SQL::Blob
929
+ literal_append(sql, v)
930
+ else
931
+ quote_identifier_append(sql, v)
932
+ end
933
+ else
934
+ literal_append(sql, v)
935
+ end
936
+ end
937
+ alias table_ref_append identifier_append
938
+
939
+ # Append all identifiers in args interspersed by commas.
940
+ def identifier_list_append(sql, args)
941
+ c = false
942
+ comma = COMMA
943
+ args.each do |a|
944
+ sql << comma if c
945
+ identifier_append(sql, a)
946
+ c ||= true
947
+ end
601
948
  end
602
949
 
603
950
  # Modify the identifier returned from the database based on the
@@ -608,7 +955,8 @@ module Sequel
608
955
 
609
956
  # SQL fragment specifying the table to insert INTO
610
957
  def insert_into_sql(sql)
611
- sql << " INTO #{source_list(@opts[:from])}"
958
+ sql << INTO
959
+ source_list_append(sql, @opts[:from])
612
960
  end
613
961
 
614
962
  # The order of methods to call to build the INSERT SQL statement
@@ -619,27 +967,51 @@ module Sequel
619
967
  # SQL fragment specifying the columns to insert into
620
968
  def insert_columns_sql(sql)
621
969
  columns = opts[:columns]
622
- sql << " (#{columns.join(COMMA_SEPARATOR)})" if columns && !columns.empty?
970
+ if columns && !columns.empty?
971
+ sql << PAREN_SPACE_OPEN
972
+ identifier_list_append(sql, columns)
973
+ sql << PAREN_CLOSE
974
+ end
975
+ end
976
+
977
+ def insert_insert_sql(sql)
978
+ sql << INSERT
623
979
  end
624
980
 
625
981
  # SQL fragment specifying the values to insert.
626
982
  def insert_values_sql(sql)
627
983
  case values = opts[:values]
628
984
  when Array
629
- sql << (values.empty? ? " DEFAULT VALUES" : " VALUES #{literal(values)}")
985
+ if values.empty?
986
+ sql << DEFAULT_VALUES
987
+ else
988
+ sql << VALUES
989
+ literal_append(sql, values)
990
+ end
630
991
  when Dataset
631
- sql << " #{subselect_sql(values)}"
992
+ sql << SPACE
993
+ subselect_sql_append(sql, values)
632
994
  when LiteralString
633
- sql << " #{values}"
995
+ sql << SPACE << values
634
996
  else
635
997
  raise Error, "Unsupported INSERT values type, should be an Array or Dataset: #{values.inspect}"
636
998
  end
637
999
  end
638
1000
 
1001
+ # SQL fragment specifying the values to return.
1002
+ def insert_returning_sql(sql)
1003
+ if opts.has_key?(:returning)
1004
+ sql << RETURNING
1005
+ column_list_append(sql, Array(opts[:returning]))
1006
+ end
1007
+ end
1008
+ alias delete_returning_sql insert_returning_sql
1009
+ alias update_returning_sql insert_returning_sql
1010
+
639
1011
  # SQL fragment specifying a JOIN type, converts underscores to
640
1012
  # spaces and upcases.
641
1013
  def join_type_sql(join_type)
642
- "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
1014
+ "#{join_type.to_s.gsub(UNDERSCORE, SPACE).upcase} JOIN"
643
1015
  end
644
1016
 
645
1017
  # Whether this dataset is a joined dataset
@@ -648,8 +1020,12 @@ module Sequel
648
1020
  end
649
1021
 
650
1022
  # SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
651
- def literal_array(v)
652
- Sequel.condition_specifier?(v) ? literal_expression(SQL::BooleanExpression.from_value_pairs(v)) : array_sql(v)
1023
+ def literal_array_append(sql, v)
1024
+ if Sequel.condition_specifier?(v)
1025
+ literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
1026
+ else
1027
+ array_sql_append(sql, v)
1028
+ end
653
1029
  end
654
1030
 
655
1031
  # SQL fragment for BigDecimal
@@ -659,18 +1035,24 @@ module Sequel
659
1035
  end
660
1036
 
661
1037
  # SQL fragment for SQL::Blob
662
- def literal_blob(v)
663
- literal_string(v)
1038
+ def literal_blob_append(sql, v)
1039
+ literal_string_append(sql, v)
664
1040
  end
665
1041
 
666
1042
  # SQL fragment for Dataset. Does a subselect inside parantheses.
667
- def literal_dataset(v)
668
- "(#{subselect_sql(v)})"
1043
+ def literal_dataset_append(sql, v)
1044
+ sql << PAREN_OPEN
1045
+ subselect_sql_append(sql, v)
1046
+ sql << PAREN_CLOSE
669
1047
  end
670
1048
 
671
1049
  # SQL fragment for Date, using the ISO8601 format.
672
1050
  def literal_date(v)
673
- v.strftime("#{'DATE ' if requires_sql_standard_datetimes?}'%Y-%m-%d'")
1051
+ if requires_sql_standard_datetimes?
1052
+ v.strftime(FORMAT_DATE_STANDARD)
1053
+ else
1054
+ v.strftime(FORMAT_DATE)
1055
+ end
674
1056
  end
675
1057
 
676
1058
  # SQL fragment for DateTime
@@ -679,8 +1061,8 @@ module Sequel
679
1061
  end
680
1062
 
681
1063
  # SQL fragment for SQL::Expression, result depends on the specific type of expression.
682
- def literal_expression(v)
683
- v.to_s(self)
1064
+ def literal_expression_append(sql, v)
1065
+ v.to_s_append(self, sql)
684
1066
  end
685
1067
 
686
1068
  # SQL fragment for false
@@ -694,15 +1076,15 @@ module Sequel
694
1076
  end
695
1077
 
696
1078
  # SQL fragment for Hash, treated as an expression
697
- def literal_hash(v)
698
- literal_expression(SQL::BooleanExpression.from_value_pairs(v))
1079
+ def literal_hash_append(sql, v)
1080
+ literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
699
1081
  end
700
1082
 
701
1083
  # SQL fragment for Integer
702
1084
  def literal_integer(v)
703
1085
  v.to_s
704
1086
  end
705
-
1087
+
706
1088
  # SQL fragment for nil
707
1089
  def literal_nil
708
1090
  NULL
@@ -714,17 +1096,24 @@ module Sequel
714
1096
  # provided and should add that method to Sequel::Dataset, allowing for adapters
715
1097
  # to provide customized literalizations.
716
1098
  # If a database specific type is allowed, this should be overriden in a subclass.
717
- def literal_other(v)
718
- if v.respond_to?(:sql_literal)
719
- v.sql_literal(self)
1099
+ def literal_other_append(sql, v)
1100
+ if v.respond_to?(:sql_literal_append)
1101
+ v.sql_literal_append(self, sql)
1102
+ elsif v.respond_to?(:sql_literal)
1103
+ sql << v.sql_literal(self)
720
1104
  else
721
1105
  raise Error, "can't express #{v.inspect} as a SQL literal"
722
1106
  end
723
1107
  end
724
1108
 
1109
+ # SQL fragment for Sequel::SQLTime, containing just the time part
1110
+ def literal_sqltime(v)
1111
+ v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
1112
+ end
1113
+
725
1114
  # SQL fragment for String. Doubles \ and ' by default.
726
- def literal_string(v)
727
- "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
1115
+ def literal_string_append(sql, v)
1116
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
728
1117
  end
729
1118
 
730
1119
  # Converts a symbol into a column name. This method supports underscore
@@ -735,10 +1124,14 @@ module Sequel
735
1124
  # dataset.literal(:abc___a) #=> "abc AS a"
736
1125
  # dataset.literal(:items__abc) #=> "items.abc"
737
1126
  # dataset.literal(:items__abc___a) #=> "items.abc AS a"
738
- def literal_symbol(v)
1127
+ def literal_symbol_append(sql, v)
739
1128
  c_table, column, c_alias = split_symbol(v)
740
- qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
741
- c_alias ? as_sql(qc, c_alias) : qc
1129
+ if c_table
1130
+ quote_identifier_append(sql, c_table)
1131
+ sql << DOT
1132
+ end
1133
+ quote_identifier_append(sql, column)
1134
+ as_sql_append(sql, c_alias) if c_alias
742
1135
  end
743
1136
 
744
1137
  # SQL fragment for Time
@@ -751,6 +1144,11 @@ module Sequel
751
1144
  BOOL_TRUE
752
1145
  end
753
1146
 
1147
+ # Get the native function name given the emulated function name.
1148
+ def native_function_name(emulated_function)
1149
+ self.class.const_get(:EMULATED_FUNCTION_MAP).fetch(emulated_function, emulated_function)
1150
+ end
1151
+
754
1152
  # Returns a qualified column name (including a table name) if the column
755
1153
  # name isn't already qualified.
756
1154
  def qualified_column_name(column, table)
@@ -774,59 +1172,7 @@ module Sequel
774
1172
 
775
1173
  # Qualify the given expression e to the given table.
776
1174
  def qualified_expression(e, table)
777
- case e
778
- when Symbol
779
- t, column, aliaz = split_symbol(e)
780
- if t
781
- e
782
- elsif aliaz
783
- SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table, SQL::Identifier.new(column)), aliaz)
784
- else
785
- SQL::QualifiedIdentifier.new(table, e)
786
- end
787
- when Array
788
- e.map{|a| qualified_expression(a, table)}
789
- when Hash
790
- h = {}
791
- e.each{|k,v| h[qualified_expression(k, table)] = qualified_expression(v, table)}
792
- h
793
- when SQL::Identifier
794
- SQL::QualifiedIdentifier.new(table, e)
795
- when SQL::OrderedExpression
796
- SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending, :nulls=>e.nulls)
797
- when SQL::AliasedExpression
798
- SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
799
- when SQL::CaseExpression
800
- args = [qualified_expression(e.conditions, table), qualified_expression(e.default, table)]
801
- args << qualified_expression(e.expression, table) if e.expression?
802
- SQL::CaseExpression.new(*args)
803
- when SQL::Cast
804
- SQL::Cast.new(qualified_expression(e.expr, table), e.type)
805
- when SQL::Function
806
- SQL::Function.new(e.f, *qualified_expression(e.args, table))
807
- when SQL::ComplexExpression
808
- SQL::ComplexExpression.new(e.op, *qualified_expression(e.args, table))
809
- when SQL::Subscript
810
- SQL::Subscript.new(qualified_expression(e.f, table), qualified_expression(e.sub, table))
811
- when SQL::WindowFunction
812
- SQL::WindowFunction.new(qualified_expression(e.function, table), qualified_expression(e.window, table))
813
- when SQL::Window
814
- o = e.opts.dup
815
- o[:partition] = qualified_expression(o[:partition], table) if o[:partition]
816
- o[:order] = qualified_expression(o[:order], table) if o[:order]
817
- SQL::Window.new(o)
818
- when SQL::PlaceholderLiteralString
819
- args = if e.args.is_a?(Hash)
820
- h = {}
821
- e.args.each{|k,v| h[k] = qualified_expression(v, table)}
822
- h
823
- else
824
- qualified_expression(e.args, table)
825
- end
826
- SQL::PlaceholderLiteralString.new(e.str, args, e.parens)
827
- else
828
- e
829
- end
1175
+ Qualifier.new(self, table).transform(e)
830
1176
  end
831
1177
 
832
1178
  # The order of methods to call to build the SELECT SQL statement
@@ -836,13 +1182,19 @@ module Sequel
836
1182
 
837
1183
  # Modify the sql to add the columns selected
838
1184
  def select_columns_sql(sql)
839
- sql << " #{column_list(@opts[:select])}"
1185
+ sql << SPACE
1186
+ column_list_append(sql, @opts[:select])
840
1187
  end
841
1188
 
842
1189
  # Modify the sql to add the DISTINCT modifier
843
1190
  def select_distinct_sql(sql)
844
1191
  if distinct = @opts[:distinct]
845
- sql << " DISTINCT#{" ON (#{expression_list(distinct)})" unless distinct.empty?}"
1192
+ sql << DISTINCT
1193
+ unless distinct.empty?
1194
+ sql << ON_PAREN
1195
+ expression_list_append(sql, distinct)
1196
+ sql << PAREN_CLOSE
1197
+ end
846
1198
  end
847
1199
  end
848
1200
 
@@ -850,60 +1202,100 @@ module Sequel
850
1202
  # This uses a subselect for the compound datasets used, because using parantheses doesn't
851
1203
  # work on all databases. I consider this an ugly hack, but can't I think of a better default.
852
1204
  def select_compounds_sql(sql)
853
- return unless @opts[:compounds]
854
- @opts[:compounds].each do |type, dataset, all|
855
- compound_sql = subselect_sql(dataset)
856
- sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}"
1205
+ return unless c = @opts[:compounds]
1206
+ c.each do |type, dataset, all|
1207
+ sql << SPACE << type.to_s.upcase
1208
+ sql << ALL if all
1209
+ sql << SPACE
1210
+ compound_dataset_sql_append(sql, dataset)
857
1211
  end
858
1212
  end
859
1213
 
860
1214
  # Modify the sql to add the list of tables to select FROM
861
1215
  def select_from_sql(sql)
862
- sql << " FROM #{source_list(@opts[:from])}" if @opts[:from]
1216
+ if f = @opts[:from]
1217
+ sql << FROM
1218
+ source_list_append(sql, f)
1219
+ end
863
1220
  end
864
1221
  alias delete_from_sql select_from_sql
865
1222
 
866
1223
  # Modify the sql to add the expressions to GROUP BY
867
1224
  def select_group_sql(sql)
868
- sql << " GROUP BY #{expression_list(@opts[:group])}" if @opts[:group]
1225
+ if group = @opts[:group]
1226
+ sql << GROUP_BY
1227
+ if go = @opts[:group_options]
1228
+ if uses_with_rollup?
1229
+ expression_list_append(sql, group)
1230
+ sql << SPACE_WITH << go.to_s.upcase
1231
+ else
1232
+ sql << go.to_s.upcase << PAREN_OPEN
1233
+ expression_list_append(sql, group)
1234
+ sql << PAREN_CLOSE
1235
+ end
1236
+ else
1237
+ expression_list_append(sql, group)
1238
+ end
1239
+ end
869
1240
  end
870
1241
 
871
1242
  # Modify the sql to add the filter criteria in the HAVING clause
872
1243
  def select_having_sql(sql)
873
- sql << " HAVING #{literal(@opts[:having])}" if @opts[:having]
1244
+ if having = @opts[:having]
1245
+ sql << HAVING
1246
+ literal_append(sql, having)
1247
+ end
874
1248
  end
875
1249
 
876
1250
  # Modify the sql to add the list of tables to JOIN to
877
1251
  def select_join_sql(sql)
878
- @opts[:join].each{|j| sql << literal(j)} if @opts[:join]
1252
+ if js = @opts[:join]
1253
+ js.each{|j| literal_append(sql, j)}
1254
+ end
879
1255
  end
880
1256
 
881
1257
  # Modify the sql to limit the number of rows returned and offset
882
1258
  def select_limit_sql(sql)
883
- sql << " LIMIT #{literal(@opts[:limit])}" if @opts[:limit]
884
- sql << " OFFSET #{literal(@opts[:offset])}" if @opts[:offset]
1259
+ if l = @opts[:limit]
1260
+ sql << LIMIT
1261
+ literal_append(sql, l)
1262
+ end
1263
+ if o = @opts[:offset]
1264
+ sql << OFFSET
1265
+ literal_append(sql, o)
1266
+ end
885
1267
  end
886
1268
 
887
1269
  # Modify the sql to support the different types of locking modes.
888
1270
  def select_lock_sql(sql)
889
- case @opts[:lock]
1271
+ case l = @opts[:lock]
890
1272
  when :update
891
1273
  sql << FOR_UPDATE
892
1274
  when String
893
- sql << " #{@opts[:lock]}"
1275
+ sql << SPACE << l
894
1276
  end
895
1277
  end
896
1278
 
897
1279
  # Modify the sql to add the expressions to ORDER BY
898
1280
  def select_order_sql(sql)
899
- sql << " ORDER BY #{expression_list(@opts[:order])}" if @opts[:order]
1281
+ if o = @opts[:order]
1282
+ sql << ORDER_BY
1283
+ expression_list_append(sql, o)
1284
+ end
900
1285
  end
901
1286
  alias delete_order_sql select_order_sql
902
1287
  alias update_order_sql select_order_sql
903
1288
 
1289
+ def select_select_sql(sql)
1290
+ sql << SELECT
1291
+ end
1292
+
904
1293
  # Modify the sql to add the filter criteria in the WHERE clause
905
1294
  def select_where_sql(sql)
906
- sql << " WHERE #{literal(@opts[:where])}" if @opts[:where]
1295
+ if w = @opts[:where]
1296
+ sql << WHERE
1297
+ literal_append(sql, w)
1298
+ end
907
1299
  end
908
1300
  alias delete_where_sql select_where_sql
909
1301
  alias update_where_sql select_where_sql
@@ -912,8 +1304,26 @@ module Sequel
912
1304
  def select_with_sql(sql)
913
1305
  ws = opts[:with]
914
1306
  return if !ws || ws.empty?
915
- sql.replace("#{select_with_sql_base}#{ws.map{|w| "#{quote_identifier(w[:name])}#{"(#{argument_list(w[:args])})" if w[:args]} AS #{literal_dataset(w[:dataset])}"}.join(COMMA_SEPARATOR)} #{sql}")
1307
+ sql << select_with_sql_base
1308
+ c = false
1309
+ comma = COMMA
1310
+ ws.each do |w|
1311
+ sql << comma if c
1312
+ quote_identifier_append(sql, w[:name])
1313
+ if args = w[:args]
1314
+ sql << PAREN_OPEN
1315
+ identifier_list_append(sql, args)
1316
+ sql << PAREN_CLOSE
1317
+ end
1318
+ sql << AS
1319
+ literal_dataset_append(sql, w[:dataset])
1320
+ c ||= true
1321
+ end
1322
+ sql << SPACE
916
1323
  end
1324
+ alias delete_with_sql select_with_sql
1325
+ alias insert_with_sql select_with_sql
1326
+ alias update_with_sql select_with_sql
917
1327
 
918
1328
  # The base keyword to use for the SQL WITH clause
919
1329
  def select_with_sql_base
@@ -921,46 +1331,46 @@ module Sequel
921
1331
  end
922
1332
 
923
1333
  # Converts an array of source names into into a comma separated list.
924
- def source_list(source)
925
- raise(Error, 'No source specified for query') if source.nil? || source.empty?
926
- source.map{|s| table_ref(s)}.join(COMMA_SEPARATOR)
1334
+ def source_list_append(sql, sources)
1335
+ raise(Error, 'No source specified for query') if sources.nil? || sources == []
1336
+ identifier_list_append(sql, sources)
927
1337
  end
928
1338
 
929
- # Splits the symbol into three parts. Each part will
930
- # either be a string or nil.
931
- #
932
- # For columns, these parts are the table, column, and alias.
933
- # For tables, these parts are the schema, table, and alias.
1339
+ # Delegate to Sequel.split_symbol.
934
1340
  def split_symbol(sym)
935
- s = sym.to_s
936
- if m = COLUMN_REF_RE1.match(s)
937
- [m[1], m[3], m[5]]
938
- elsif m = COLUMN_REF_RE2.match(s)
939
- [nil, m[1], m[3]]
940
- elsif m = COLUMN_REF_RE3.match(s)
941
- [m[1], m[3], nil]
942
- else
943
- [nil, s, nil]
944
- end
1341
+ Sequel.split_symbol(sym)
945
1342
  end
946
1343
 
1344
+ # The string that is appended to to create the SQL query, the empty
1345
+ # string by default
1346
+ def sql_string_origin
1347
+ ''
1348
+ end
1349
+
947
1350
  # SQL to use if this dataset uses static SQL. Since static SQL
948
1351
  # can be a PlaceholderLiteralString in addition to a String,
949
1352
  # we literalize nonstrings.
950
1353
  def static_sql(sql)
951
- sql.is_a?(String) ? sql : literal(sql)
1354
+ if append_sql = @opts[:append_sql]
1355
+ if sql.is_a?(String)
1356
+ append_sql << sql
1357
+ else
1358
+ literal_append(append_sql, sql)
1359
+ end
1360
+ else
1361
+ if sql.is_a?(String)
1362
+ sql
1363
+ else
1364
+ literal(sql)
1365
+ end
1366
+ end
952
1367
  end
953
1368
 
954
1369
  # SQL fragment for a subselect using the given database's SQL.
955
- def subselect_sql(ds)
956
- ds.sql
1370
+ def subselect_sql_append(sql, ds)
1371
+ ds.clone(:append_sql=>sql).sql
957
1372
  end
958
1373
 
959
- # SQL fragment specifying a table name.
960
- def table_ref(t)
961
- t.is_a?(String) ? quote_identifier(t) : literal(t)
962
- end
963
-
964
1374
  # The order of methods to call to build the UPDATE SQL statement
965
1375
  def update_clause_methods
966
1376
  UPDATE_CLAUSE_METHODS
@@ -969,25 +1379,38 @@ module Sequel
969
1379
  # SQL fragment specifying the tables from with to delete.
970
1380
  # Includes join table if modifying joins is allowed.
971
1381
  def update_table_sql(sql)
972
- sql << " #{source_list(@opts[:from])}"
1382
+ sql << SPACE
1383
+ source_list_append(sql, @opts[:from])
973
1384
  select_join_sql(sql) if supports_modifying_joins?
974
1385
  end
975
1386
 
976
1387
  # The SQL fragment specifying the columns and values to SET.
977
1388
  def update_set_sql(sql)
978
1389
  values = opts[:values]
979
- set = if values.is_a?(Hash)
1390
+ sql << SET
1391
+ if values.is_a?(Hash)
980
1392
  values = opts[:defaults].merge(values) if opts[:defaults]
981
1393
  values = values.merge(opts[:overrides]) if opts[:overrides]
982
- # get values from hash
983
- values.map do |k, v|
984
- "#{k.is_a?(String) && !k.is_a?(LiteralString) ? quote_identifier(k) : literal(k)} = #{literal(v)}"
985
- end.join(COMMA_SEPARATOR)
1394
+ c = false
1395
+ eq = EQUAL
1396
+ values.each do |k, v|
1397
+ sql << COMMA if c
1398
+ if k.is_a?(String) && !k.is_a?(LiteralString)
1399
+ quote_identifier_append(sql, k)
1400
+ else
1401
+ literal_append(sql, k)
1402
+ end
1403
+ sql << eq
1404
+ literal_append(sql, v)
1405
+ c ||= true
1406
+ end
986
1407
  else
987
- # copy values verbatim
988
- values
1408
+ sql << values
989
1409
  end
990
- sql << " SET #{set}"
1410
+ end
1411
+
1412
+ def update_update_sql(sql)
1413
+ sql << UPDATE
991
1414
  end
992
1415
  end
993
1416
  end