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