sequel 3.21.0 → 3.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -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
+ function_sql
294
+ join_clause_sql
295
+ join_on_clause_sql
296
+ join_using_clause_sql
297
+ negative_boolean_constant_sql
298
+ ordered_expression_sql
299
+ placeholder_literal_string_sql
300
+ qualified_identifier_sql
301
+ quote_identifier
302
+ quote_schema_table
303
+ quoted_identifier
304
+ subscript_sql
305
+ window_sql
306
+ window_function_sql
307
+ END
308
+ PRIVATE_APPEND_METHODS = (<<-END).split.map{|x| x.to_sym}
309
+ argument_list
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,214 @@ 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 specifying an emulated SQL function call.
500
+ # By default, assumes just the function name may need to
501
+ # be emulated, adapters should set an EMULATED_FUNCTION_MAP
502
+ # hash mapping emulated functions to native functions in
503
+ # their dataset class to setup the emulation.
504
+ def emulated_function_sql_append(sql, f)
505
+ _function_sql_append(sql, native_function_name(f.f), f.args)
311
506
  end
312
507
 
313
- # SQL fragment specifying an SQL function call
314
- def function_sql(f)
315
- args = f.args
316
- "#{f.f}#{args.empty? ? '()' : literal(args)}"
508
+ # SQL fragment specifying an SQL function call without emulation.
509
+ def function_sql_append(sql, f)
510
+ _function_sql_append(sql, f.f, f.args)
317
511
  end
318
512
 
319
513
  # SQL fragment specifying a JOIN clause without ON or USING.
320
- def join_clause_sql(jc)
514
+ def join_clause_sql_append(sql, jc)
321
515
  table = jc.table
322
516
  table_alias = jc.table_alias
323
517
  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}"
518
+ sql << SPACE << join_type_sql(jc.join_type) << SPACE
519
+ identifier_append(sql, table)
520
+ as_sql_append(sql, table_alias) if table_alias
326
521
  end
327
522
 
328
523
  # 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))}"
524
+ def join_on_clause_sql_append(sql, jc)
525
+ join_clause_sql_append(sql, jc)
526
+ sql << ON
527
+ literal_append(sql, filter_expr(jc.on))
331
528
  end
332
529
 
333
530
  # 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)})"
531
+ def join_using_clause_sql_append(sql, jc)
532
+ join_clause_sql_append(sql, jc)
533
+ sql << USING
534
+ column_list_append(sql, jc.using)
535
+ sql << PAREN_CLOSE
336
536
  end
337
537
 
338
538
  # SQL fragment for NegativeBooleanConstants
339
- def negative_boolean_constant_sql(constant)
340
- "NOT #{boolean_constant_sql(constant)}"
539
+ def negative_boolean_constant_sql_append(sql, constant)
540
+ sql << NOT_SPACE
541
+ boolean_constant_sql_append(sql, constant)
341
542
  end
342
543
 
343
544
  # SQL fragment for the ordered expression, used in the ORDER BY
344
545
  # clause.
345
- def ordered_expression_sql(oe)
346
- s = "#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
546
+ def ordered_expression_sql_append(sql, oe)
547
+ literal_append(sql, oe.expression)
548
+ sql << (oe.descending ? DESC : ASC)
347
549
  case oe.nulls
348
550
  when :first
349
- "#{s} NULLS FIRST"
551
+ sql << NULLS_FIRST
350
552
  when :last
351
- "#{s} NULLS LAST"
352
- else
353
- s
553
+ sql << NULLS_LAST
354
554
  end
355
555
  end
356
556
 
357
557
  # SQL fragment for a literal string with placeholders
358
- def placeholder_literal_string_sql(pls)
558
+ def placeholder_literal_string_sql_append(sql, pls)
359
559
  args = pls.args
360
- s = if args.is_a?(Hash)
560
+ str = pls.str
561
+ sql << PAREN_OPEN if pls.parens
562
+ if args.is_a?(Hash)
361
563
  re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
362
- pls.str.gsub(re){literal(args[$1.to_sym])}
564
+ loop do
565
+ previous, q, str = str.partition(re)
566
+ sql << previous
567
+ literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
568
+ break if str.empty?
569
+ end
570
+ elsif str.is_a?(Array)
571
+ len = args.length
572
+ str.each_with_index do |s, i|
573
+ sql << s
574
+ literal_append(sql, args[i]) unless i == len
575
+ end
363
576
  else
364
577
  i = -1
365
- pls.str.gsub(QUESTION_MARK){literal(args.at(i+=1))}
578
+ loop do
579
+ previous, q, str = str.partition(QUESTION_MARK)
580
+ sql << previous
581
+ literal_append(sql, args.at(i+=1)) unless q.empty?
582
+ break if str.empty?
583
+ end
366
584
  end
367
- s = "(#{s})" if pls.parens
368
- s
585
+ sql << PAREN_CLOSE if pls.parens
369
586
  end
370
587
 
371
588
  # SQL fragment for the qualifed identifier, specifying
372
589
  # 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('.')
590
+ # If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
591
+ # column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
592
+ def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
593
+ identifier_append(sql, table)
594
+ sql << DOT
595
+ identifier_append(sql, column)
375
596
  end
376
597
 
377
598
  # Adds quoting to identifiers (columns and tables). If identifiers are not
378
599
  # being quoted, returns name as a string. If identifiers are being quoted
379
600
  # 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
601
+ def quote_identifier_append(sql, name)
602
+ if name.is_a?(LiteralString)
603
+ sql << name
604
+ else
605
+ name = name.value if name.is_a?(SQL::Identifier)
606
+ name = input_identifier(name)
607
+ if quote_identifiers?
608
+ quoted_identifier_append(sql, name)
609
+ else
610
+ sql << name
611
+ end
612
+ end
386
613
  end
387
614
 
388
615
  # Separates the schema from the table and returns a string with them
389
616
  # quoted (if quoting identifiers)
390
- def quote_schema_table(table)
617
+ def quote_schema_table_append(sql, table)
391
618
  schema, table = schema_and_table(table)
392
- "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
619
+ if schema
620
+ quote_identifier_append(sql, schema)
621
+ sql << DOT
622
+ end
623
+ quote_identifier_append(sql, table)
393
624
  end
394
625
 
395
626
  # This method quotes the given name with the SQL standard double quote.
396
627
  # should be overridden by subclasses to provide quoting not matching the
397
628
  # SQL standard, such as backtick (used by MySQL and SQLite).
398
- def quoted_identifier(name)
399
- "\"#{name.to_s.gsub('"', '""')}\""
629
+ def quoted_identifier_append(sql, name)
630
+ sql << QUOTE << name.to_s.gsub(QUOTE_RE, DOUBLE_QUOTE) << QUOTE
400
631
  end
401
632
 
402
633
  # Split the schema information from the table
@@ -418,34 +649,59 @@ module Sequel
418
649
  end
419
650
 
420
651
  # SQL fragment for specifying subscripts (SQL array accesses)
421
- def subscript_sql(s)
422
- "#{literal(s.f)}[#{expression_list(s.sub)}]"
652
+ def subscript_sql_append(sql, s)
653
+ literal_append(sql, s.f)
654
+ sql << BRACKET_OPEN
655
+ expression_list_append(sql, s.sub)
656
+ sql << BRACKET_CLOSE
423
657
  end
424
658
 
425
659
  # The SQL fragment for the given window's options.
426
- def window_sql(opts)
660
+ def window_sql_append(sql, opts)
427
661
  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]
662
+ sql << PAREN_OPEN
663
+ window, part, order, frame = opts.values_at(:window, :partition, :order, :frame)
664
+ space = false
665
+ space_s = SPACE
666
+ if window
667
+ literal_append(sql, window)
668
+ space = true
669
+ end
670
+ if part
671
+ sql << space_s if space
672
+ sql << PARTITION_BY
673
+ expression_list_append(sql, Array(part))
674
+ space = true
675
+ end
676
+ if order
677
+ sql << space_s if space
678
+ sql << ORDER_BY_NS
679
+ expression_list_append(sql, Array(order))
680
+ space = true
681
+ end
682
+ case frame
432
683
  when nil
433
- nil
684
+ # nothing
434
685
  when :all
435
- "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
686
+ sql << space_s if space
687
+ sql << FRAME_ALL
436
688
  when :rows
437
- "ROWS UNBOUNDED PRECEDING"
689
+ sql << space_s if space
690
+ sql << FRAME_ROWS
438
691
  when String
439
- opts[:frame]
692
+ sql << space_s if space
693
+ sql << frame
440
694
  else
441
695
  raise Error, "invalid window frame clause, should be :all, :rows, a string, or nil"
442
696
  end
443
- "(#{[window, partition, order, frame].compact.join(' ')})"
697
+ sql << PAREN_CLOSE
444
698
  end
445
699
 
446
700
  # 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)}"
701
+ def window_function_sql_append(sql, function, window)
702
+ literal_append(sql, function)
703
+ sql << OVER
704
+ literal_append(sql, window)
449
705
  end
450
706
 
451
707
  protected
@@ -468,6 +724,16 @@ module Sequel
468
724
 
469
725
  private
470
726
 
727
+ # Backbone of function_sql_append and emulated_function_sql_append.
728
+ def _function_sql_append(sql, name, args)
729
+ sql << name.to_s
730
+ if args.empty?
731
+ sql << FUNCTION_EMPTY
732
+ else
733
+ literal_append(sql, args)
734
+ end
735
+ end
736
+
471
737
  # Formats the truncate statement. Assumes the table given has already been
472
738
  # literalized.
473
739
  def _truncate_sql(table)
@@ -517,14 +783,20 @@ module Sequel
517
783
  options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
518
784
  end
519
785
 
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)
786
+ def argument_list_append(sql, args)
787
+ c = false
788
+ comma = COMMA
789
+ args.each do |a|
790
+ sql << comma if c
791
+ sql << a.to_s
792
+ c ||= true
793
+ end
523
794
  end
524
795
 
525
796
  # SQL fragment for specifying an alias. expression should already be literalized.
526
- def as_sql(expression, aliaz)
527
- "#{expression} AS #{quote_identifier(aliaz)}"
797
+ def as_sql_append(sql, aliaz)
798
+ sql << AS
799
+ quote_identifier_append(sql, aliaz)
528
800
  end
529
801
 
530
802
  # Raise an InvalidOperation exception if deletion is not allowed
@@ -534,38 +806,88 @@ module Sequel
534
806
  raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
535
807
  end
536
808
 
809
+ # Alias of check_modification_allowed!
810
+ def check_truncation_allowed!
811
+ check_modification_allowed!
812
+ end
813
+
537
814
  # Prepare an SQL statement by calling all clause methods for the given statement type.
538
815
  def clause_sql(type)
539
- sql = type.to_s.upcase
816
+ sql = @opts[:append_sql] || sql_string_origin
540
817
  send("#{type}_clause_methods").each{|x| send(x, sql)}
541
818
  sql
542
819
  end
543
820
 
544
821
  # Converts an array of column names into a comma seperated string of
545
822
  # 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)
823
+ def column_list_append(sql, columns)
824
+ if (columns.nil? || columns.empty?)
825
+ sql << WILDCARD
826
+ else
827
+ expression_list_append(sql, columns)
828
+ end
548
829
  end
549
-
830
+
831
+ # Yield each two pair of arguments to the block, which should
832
+ # return a string representing the SQL code for those
833
+ # two arguments. If more than 2 arguments are provided, all
834
+ # calls to the block # after the first will have a LiteralString
835
+ # as the first argument, representing the application of the block to
836
+ # the previous arguments.
837
+ def complex_expression_arg_pairs(args)
838
+ case args.length
839
+ when 1
840
+ literal(args.at(0))
841
+ when 2
842
+ yield args.at(0), args.at(1)
843
+ else
844
+ args.inject{|m, a| LiteralString.new(yield(m, a))}
845
+ end
846
+ end
847
+
848
+ # The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause.
849
+ def compound_dataset_sql_append(sql, ds)
850
+ subselect_sql_append(sql, ds)
851
+ end
852
+
550
853
  # The alias to use for datasets, takes a number to make sure the name is unique.
551
854
  def dataset_alias(number)
552
855
  :"#{DATASET_ALIAS_BASE_NAME}#{number}"
553
856
  end
554
857
 
858
+ # The strftime format to use when literalizing the time.
859
+ def default_timestamp_format
860
+ requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
861
+ end
862
+
555
863
  # The order of methods to call to build the DELETE SQL statement
556
864
  def delete_clause_methods
557
865
  DELETE_CLAUSE_METHODS
558
866
  end
559
867
 
868
+ def delete_delete_sql(sql)
869
+ sql << DELETE
870
+ end
871
+
560
872
  # Converts an array of expressions into a comma separated string of
561
873
  # expressions.
562
- def expression_list(columns)
563
- columns.map{|i| literal(i)}.join(COMMA_SEPARATOR)
874
+ def expression_list_append(sql, columns)
875
+ c = false
876
+ co = COMMA
877
+ columns.each do |col|
878
+ sql << co if c
879
+ literal_append(sql, col)
880
+ c ||= true
881
+ end
564
882
  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
883
+
884
+ def empty_array_value(op, cols)
885
+ if Sequel.empty_array_handle_nulls
886
+ c = Array(cols)
887
+ SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
888
+ else
889
+ {1 => ((op == :IN) ? 0 : 1)}
890
+ end
569
891
  end
570
892
 
571
893
  # Format the timestamp based on the default_timestamp_format, with a couple
@@ -573,10 +895,10 @@ module Sequel
573
895
  # database supports them), and override %z to always use a numeric offset
574
896
  # of hours and minutes.
575
897
  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?
898
+ v2 = db.from_application_timestamp(v)
899
+ fmt = default_timestamp_format.gsub(FORMAT_TIMESTAMP_RE) do |m|
900
+ if m == FORMAT_USEC
901
+ format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < V190 ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
580
902
  else
581
903
  if supports_timestamp_timezones?
582
904
  # Would like to just use %z format, but it doesn't appear to work on Windows
@@ -591,15 +913,33 @@ module Sequel
591
913
 
592
914
  # Return the SQL timestamp fragment to use for the timezone offset.
593
915
  def format_timestamp_offset(hour, minute)
594
- sprintf("%+03i%02i", hour, minute)
916
+ sprintf(FORMAT_OFFSET, hour, minute)
595
917
  end
596
918
 
597
919
  # Return the SQL timestamp fragment to use for the fractional time part.
598
920
  # Should start with the decimal point. Uses 6 decimal places by default.
599
921
  def format_timestamp_usec(usec)
600
- sprintf(".%06d", usec)
922
+ sprintf(FORMAT_TIMESTAMP_USEC, usec)
923
+ end
924
+
925
+ # Append the value, but special case regular (non-literal, non-blob) strings
926
+ # so that they are considered as identifiers and not SQL strings.
927
+ def identifier_append(sql, v)
928
+ if v.is_a?(String)
929
+ case v
930
+ when LiteralString
931
+ sql << v
932
+ when SQL::Blob
933
+ literal_append(sql, v)
934
+ else
935
+ quote_identifier_append(sql, v)
936
+ end
937
+ else
938
+ literal_append(sql, v)
939
+ end
601
940
  end
602
-
941
+ alias table_ref_append identifier_append
942
+
603
943
  # Modify the identifier returned from the database based on the
604
944
  # identifier_output_method.
605
945
  def input_identifier(v)
@@ -608,7 +948,8 @@ module Sequel
608
948
 
609
949
  # SQL fragment specifying the table to insert INTO
610
950
  def insert_into_sql(sql)
611
- sql << " INTO #{source_list(@opts[:from])}"
951
+ sql << INTO
952
+ source_list_append(sql, @opts[:from])
612
953
  end
613
954
 
614
955
  # The order of methods to call to build the INSERT SQL statement
@@ -619,27 +960,57 @@ module Sequel
619
960
  # SQL fragment specifying the columns to insert into
620
961
  def insert_columns_sql(sql)
621
962
  columns = opts[:columns]
622
- sql << " (#{columns.join(COMMA_SEPARATOR)})" if columns && !columns.empty?
963
+ if columns && !columns.empty?
964
+ sql << PAREN_SPACE_OPEN
965
+ c = false
966
+ co = COMMA
967
+ columns.each do |col|
968
+ sql << co if c
969
+ identifier_append(sql, col)
970
+ c ||= true
971
+ end
972
+ sql << PAREN_CLOSE
973
+ end
974
+ end
975
+
976
+ def insert_insert_sql(sql)
977
+ sql << INSERT
623
978
  end
624
979
 
625
980
  # SQL fragment specifying the values to insert.
626
981
  def insert_values_sql(sql)
627
982
  case values = opts[:values]
628
983
  when Array
629
- sql << (values.empty? ? " DEFAULT VALUES" : " VALUES #{literal(values)}")
984
+ if values.empty?
985
+ sql << DEFAULT_VALUES
986
+ else
987
+ sql << VALUES
988
+ literal_append(sql, values)
989
+ end
630
990
  when Dataset
631
- sql << " #{subselect_sql(values)}"
991
+ sql << SPACE
992
+ subselect_sql_append(sql, values)
632
993
  when LiteralString
633
- sql << " #{values}"
994
+ sql << SPACE << values
634
995
  else
635
996
  raise Error, "Unsupported INSERT values type, should be an Array or Dataset: #{values.inspect}"
636
997
  end
637
998
  end
638
999
 
1000
+ # SQL fragment specifying the values to return.
1001
+ def insert_returning_sql(sql)
1002
+ if opts.has_key?(:returning)
1003
+ sql << RETURNING
1004
+ column_list_append(sql, Array(opts[:returning]))
1005
+ end
1006
+ end
1007
+ alias delete_returning_sql insert_returning_sql
1008
+ alias update_returning_sql insert_returning_sql
1009
+
639
1010
  # SQL fragment specifying a JOIN type, converts underscores to
640
1011
  # spaces and upcases.
641
1012
  def join_type_sql(join_type)
642
- "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
1013
+ "#{join_type.to_s.gsub(UNDERSCORE, SPACE).upcase} JOIN"
643
1014
  end
644
1015
 
645
1016
  # Whether this dataset is a joined dataset
@@ -648,8 +1019,12 @@ module Sequel
648
1019
  end
649
1020
 
650
1021
  # 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)
1022
+ def literal_array_append(sql, v)
1023
+ if Sequel.condition_specifier?(v)
1024
+ literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
1025
+ else
1026
+ array_sql_append(sql, v)
1027
+ end
653
1028
  end
654
1029
 
655
1030
  # SQL fragment for BigDecimal
@@ -659,18 +1034,24 @@ module Sequel
659
1034
  end
660
1035
 
661
1036
  # SQL fragment for SQL::Blob
662
- def literal_blob(v)
663
- literal_string(v)
1037
+ def literal_blob_append(sql, v)
1038
+ literal_string_append(sql, v)
664
1039
  end
665
1040
 
666
1041
  # SQL fragment for Dataset. Does a subselect inside parantheses.
667
- def literal_dataset(v)
668
- "(#{subselect_sql(v)})"
1042
+ def literal_dataset_append(sql, v)
1043
+ sql << PAREN_OPEN
1044
+ subselect_sql_append(sql, v)
1045
+ sql << PAREN_CLOSE
669
1046
  end
670
1047
 
671
1048
  # SQL fragment for Date, using the ISO8601 format.
672
1049
  def literal_date(v)
673
- v.strftime("#{'DATE ' if requires_sql_standard_datetimes?}'%Y-%m-%d'")
1050
+ if requires_sql_standard_datetimes?
1051
+ v.strftime(FORMAT_DATE_STANDARD)
1052
+ else
1053
+ v.strftime(FORMAT_DATE)
1054
+ end
674
1055
  end
675
1056
 
676
1057
  # SQL fragment for DateTime
@@ -679,8 +1060,8 @@ module Sequel
679
1060
  end
680
1061
 
681
1062
  # SQL fragment for SQL::Expression, result depends on the specific type of expression.
682
- def literal_expression(v)
683
- v.to_s(self)
1063
+ def literal_expression_append(sql, v)
1064
+ v.to_s_append(self, sql)
684
1065
  end
685
1066
 
686
1067
  # SQL fragment for false
@@ -694,15 +1075,15 @@ module Sequel
694
1075
  end
695
1076
 
696
1077
  # SQL fragment for Hash, treated as an expression
697
- def literal_hash(v)
698
- literal_expression(SQL::BooleanExpression.from_value_pairs(v))
1078
+ def literal_hash_append(sql, v)
1079
+ literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
699
1080
  end
700
1081
 
701
1082
  # SQL fragment for Integer
702
1083
  def literal_integer(v)
703
1084
  v.to_s
704
1085
  end
705
-
1086
+
706
1087
  # SQL fragment for nil
707
1088
  def literal_nil
708
1089
  NULL
@@ -714,17 +1095,24 @@ module Sequel
714
1095
  # provided and should add that method to Sequel::Dataset, allowing for adapters
715
1096
  # to provide customized literalizations.
716
1097
  # 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)
1098
+ def literal_other_append(sql, v)
1099
+ if v.respond_to?(:sql_literal_append)
1100
+ v.sql_literal_append(self, sql)
1101
+ elsif v.respond_to?(:sql_literal)
1102
+ sql << v.sql_literal(self)
720
1103
  else
721
1104
  raise Error, "can't express #{v.inspect} as a SQL literal"
722
1105
  end
723
1106
  end
724
1107
 
1108
+ # SQL fragment for Sequel::SQLTime, containing just the time part
1109
+ def literal_sqltime(v)
1110
+ v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
1111
+ end
1112
+
725
1113
  # SQL fragment for String. Doubles \ and ' by default.
726
- def literal_string(v)
727
- "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
1114
+ def literal_string_append(sql, v)
1115
+ sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
728
1116
  end
729
1117
 
730
1118
  # Converts a symbol into a column name. This method supports underscore
@@ -735,10 +1123,14 @@ module Sequel
735
1123
  # dataset.literal(:abc___a) #=> "abc AS a"
736
1124
  # dataset.literal(:items__abc) #=> "items.abc"
737
1125
  # dataset.literal(:items__abc___a) #=> "items.abc AS a"
738
- def literal_symbol(v)
1126
+ def literal_symbol_append(sql, v)
739
1127
  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
1128
+ if c_table
1129
+ quote_identifier_append(sql, c_table)
1130
+ sql << DOT
1131
+ end
1132
+ quote_identifier_append(sql, column)
1133
+ as_sql_append(sql, c_alias) if c_alias
742
1134
  end
743
1135
 
744
1136
  # SQL fragment for Time
@@ -751,6 +1143,11 @@ module Sequel
751
1143
  BOOL_TRUE
752
1144
  end
753
1145
 
1146
+ # Get the native function name given the emulated function name.
1147
+ def native_function_name(emulated_function)
1148
+ self.class.const_get(:EMULATED_FUNCTION_MAP).fetch(emulated_function, emulated_function)
1149
+ end
1150
+
754
1151
  # Returns a qualified column name (including a table name) if the column
755
1152
  # name isn't already qualified.
756
1153
  def qualified_column_name(column, table)
@@ -774,59 +1171,7 @@ module Sequel
774
1171
 
775
1172
  # Qualify the given expression e to the given table.
776
1173
  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
1174
+ Qualifier.new(self, table).transform(e)
830
1175
  end
831
1176
 
832
1177
  # The order of methods to call to build the SELECT SQL statement
@@ -836,13 +1181,19 @@ module Sequel
836
1181
 
837
1182
  # Modify the sql to add the columns selected
838
1183
  def select_columns_sql(sql)
839
- sql << " #{column_list(@opts[:select])}"
1184
+ sql << SPACE
1185
+ column_list_append(sql, @opts[:select])
840
1186
  end
841
1187
 
842
1188
  # Modify the sql to add the DISTINCT modifier
843
1189
  def select_distinct_sql(sql)
844
1190
  if distinct = @opts[:distinct]
845
- sql << " DISTINCT#{" ON (#{expression_list(distinct)})" unless distinct.empty?}"
1191
+ sql << DISTINCT
1192
+ unless distinct.empty?
1193
+ sql << ON_PAREN
1194
+ expression_list_append(sql, distinct)
1195
+ sql << PAREN_CLOSE
1196
+ end
846
1197
  end
847
1198
  end
848
1199
 
@@ -850,60 +1201,100 @@ module Sequel
850
1201
  # This uses a subselect for the compound datasets used, because using parantheses doesn't
851
1202
  # work on all databases. I consider this an ugly hack, but can't I think of a better default.
852
1203
  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}"
1204
+ return unless c = @opts[:compounds]
1205
+ c.each do |type, dataset, all|
1206
+ sql << SPACE << type.to_s.upcase
1207
+ sql << ALL if all
1208
+ sql << SPACE
1209
+ compound_dataset_sql_append(sql, dataset)
857
1210
  end
858
1211
  end
859
1212
 
860
1213
  # Modify the sql to add the list of tables to select FROM
861
1214
  def select_from_sql(sql)
862
- sql << " FROM #{source_list(@opts[:from])}" if @opts[:from]
1215
+ if f = @opts[:from]
1216
+ sql << FROM
1217
+ source_list_append(sql, f)
1218
+ end
863
1219
  end
864
1220
  alias delete_from_sql select_from_sql
865
1221
 
866
1222
  # Modify the sql to add the expressions to GROUP BY
867
1223
  def select_group_sql(sql)
868
- sql << " GROUP BY #{expression_list(@opts[:group])}" if @opts[:group]
1224
+ if group = @opts[:group]
1225
+ sql << GROUP_BY
1226
+ if go = @opts[:group_options]
1227
+ if uses_with_rollup?
1228
+ expression_list_append(sql, group)
1229
+ sql << SPACE_WITH << go.to_s.upcase
1230
+ else
1231
+ sql << go.to_s.upcase << PAREN_OPEN
1232
+ expression_list_append(sql, group)
1233
+ sql << PAREN_CLOSE
1234
+ end
1235
+ else
1236
+ expression_list_append(sql, group)
1237
+ end
1238
+ end
869
1239
  end
870
1240
 
871
1241
  # Modify the sql to add the filter criteria in the HAVING clause
872
1242
  def select_having_sql(sql)
873
- sql << " HAVING #{literal(@opts[:having])}" if @opts[:having]
1243
+ if having = @opts[:having]
1244
+ sql << HAVING
1245
+ literal_append(sql, having)
1246
+ end
874
1247
  end
875
1248
 
876
1249
  # Modify the sql to add the list of tables to JOIN to
877
1250
  def select_join_sql(sql)
878
- @opts[:join].each{|j| sql << literal(j)} if @opts[:join]
1251
+ if js = @opts[:join]
1252
+ js.each{|j| literal_append(sql, j)}
1253
+ end
879
1254
  end
880
1255
 
881
1256
  # Modify the sql to limit the number of rows returned and offset
882
1257
  def select_limit_sql(sql)
883
- sql << " LIMIT #{literal(@opts[:limit])}" if @opts[:limit]
884
- sql << " OFFSET #{literal(@opts[:offset])}" if @opts[:offset]
1258
+ if l = @opts[:limit]
1259
+ sql << LIMIT
1260
+ literal_append(sql, l)
1261
+ end
1262
+ if o = @opts[:offset]
1263
+ sql << OFFSET
1264
+ literal_append(sql, o)
1265
+ end
885
1266
  end
886
1267
 
887
1268
  # Modify the sql to support the different types of locking modes.
888
1269
  def select_lock_sql(sql)
889
- case @opts[:lock]
1270
+ case l = @opts[:lock]
890
1271
  when :update
891
1272
  sql << FOR_UPDATE
892
1273
  when String
893
- sql << " #{@opts[:lock]}"
1274
+ sql << SPACE << l
894
1275
  end
895
1276
  end
896
1277
 
897
1278
  # Modify the sql to add the expressions to ORDER BY
898
1279
  def select_order_sql(sql)
899
- sql << " ORDER BY #{expression_list(@opts[:order])}" if @opts[:order]
1280
+ if o = @opts[:order]
1281
+ sql << ORDER_BY
1282
+ expression_list_append(sql, o)
1283
+ end
900
1284
  end
901
1285
  alias delete_order_sql select_order_sql
902
1286
  alias update_order_sql select_order_sql
903
1287
 
1288
+ def select_select_sql(sql)
1289
+ sql << SELECT
1290
+ end
1291
+
904
1292
  # Modify the sql to add the filter criteria in the WHERE clause
905
1293
  def select_where_sql(sql)
906
- sql << " WHERE #{literal(@opts[:where])}" if @opts[:where]
1294
+ if w = @opts[:where]
1295
+ sql << WHERE
1296
+ literal_append(sql, w)
1297
+ end
907
1298
  end
908
1299
  alias delete_where_sql select_where_sql
909
1300
  alias update_where_sql select_where_sql
@@ -912,8 +1303,26 @@ module Sequel
912
1303
  def select_with_sql(sql)
913
1304
  ws = opts[:with]
914
1305
  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}")
1306
+ sql << select_with_sql_base
1307
+ c = false
1308
+ comma = COMMA
1309
+ ws.each do |w|
1310
+ sql << comma if c
1311
+ quote_identifier_append(sql, w[:name])
1312
+ if args = w[:args]
1313
+ sql << PAREN_OPEN
1314
+ argument_list_append(sql, args)
1315
+ sql << PAREN_CLOSE
1316
+ end
1317
+ sql << AS
1318
+ literal_dataset_append(sql, w[:dataset])
1319
+ c ||= true
1320
+ end
1321
+ sql << SPACE
916
1322
  end
1323
+ alias delete_with_sql select_with_sql
1324
+ alias insert_with_sql select_with_sql
1325
+ alias update_with_sql select_with_sql
917
1326
 
918
1327
  # The base keyword to use for the SQL WITH clause
919
1328
  def select_with_sql_base
@@ -921,46 +1330,52 @@ module Sequel
921
1330
  end
922
1331
 
923
1332
  # 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)
1333
+ def source_list_append(sql, sources)
1334
+ raise(Error, 'No source specified for query') if sources.nil? || sources == []
1335
+ c = false
1336
+ co = COMMA
1337
+ sources.each do |s|
1338
+ sql << co if c
1339
+ identifier_append(sql, s)
1340
+ c ||= true
1341
+ end
927
1342
  end
928
1343
 
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.
1344
+ # Delegate to Sequel.split_symbol.
934
1345
  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
1346
+ Sequel.split_symbol(sym)
945
1347
  end
946
1348
 
1349
+ # The string that is appended to to create the SQL query, the empty
1350
+ # string by default
1351
+ def sql_string_origin
1352
+ ''
1353
+ end
1354
+
947
1355
  # SQL to use if this dataset uses static SQL. Since static SQL
948
1356
  # can be a PlaceholderLiteralString in addition to a String,
949
1357
  # we literalize nonstrings.
950
1358
  def static_sql(sql)
951
- sql.is_a?(String) ? sql : literal(sql)
1359
+ if append_sql = @opts[:append_sql]
1360
+ if sql.is_a?(String)
1361
+ append_sql << sql
1362
+ else
1363
+ literal_append(append_sql, sql)
1364
+ end
1365
+ else
1366
+ if sql.is_a?(String)
1367
+ sql
1368
+ else
1369
+ literal(sql)
1370
+ end
1371
+ end
952
1372
  end
953
1373
 
954
1374
  # SQL fragment for a subselect using the given database's SQL.
955
- def subselect_sql(ds)
956
- ds.sql
1375
+ def subselect_sql_append(sql, ds)
1376
+ ds.clone(:append_sql=>sql).sql
957
1377
  end
958
1378
 
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
1379
  # The order of methods to call to build the UPDATE SQL statement
965
1380
  def update_clause_methods
966
1381
  UPDATE_CLAUSE_METHODS
@@ -969,25 +1384,38 @@ module Sequel
969
1384
  # SQL fragment specifying the tables from with to delete.
970
1385
  # Includes join table if modifying joins is allowed.
971
1386
  def update_table_sql(sql)
972
- sql << " #{source_list(@opts[:from])}"
1387
+ sql << SPACE
1388
+ source_list_append(sql, @opts[:from])
973
1389
  select_join_sql(sql) if supports_modifying_joins?
974
1390
  end
975
1391
 
976
1392
  # The SQL fragment specifying the columns and values to SET.
977
1393
  def update_set_sql(sql)
978
1394
  values = opts[:values]
979
- set = if values.is_a?(Hash)
1395
+ sql << SET
1396
+ if values.is_a?(Hash)
980
1397
  values = opts[:defaults].merge(values) if opts[:defaults]
981
1398
  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)
1399
+ c = false
1400
+ eq = EQUAL
1401
+ values.each do |k, v|
1402
+ sql << COMMA if c
1403
+ if k.is_a?(String) && !k.is_a?(LiteralString)
1404
+ quote_identifier_append(sql, k)
1405
+ else
1406
+ literal_append(sql, k)
1407
+ end
1408
+ sql << eq
1409
+ literal_append(sql, v)
1410
+ c ||= true
1411
+ end
986
1412
  else
987
- # copy values verbatim
988
- values
1413
+ sql << values
989
1414
  end
990
- sql << " SET #{set}"
1415
+ end
1416
+
1417
+ def update_update_sql(sql)
1418
+ sql << UPDATE
991
1419
  end
992
1420
  end
993
1421
  end