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