sequel 3.21.0 → 3.36.0

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