sequel 4.9.0 → 4.10.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +79 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/Rakefile +2 -12
  6. data/bin/sequel +1 -0
  7. data/doc/advanced_associations.rdoc +82 -25
  8. data/doc/association_basics.rdoc +21 -22
  9. data/doc/core_extensions.rdoc +1 -1
  10. data/doc/opening_databases.rdoc +7 -0
  11. data/doc/release_notes/4.10.0.txt +226 -0
  12. data/doc/security.rdoc +1 -0
  13. data/doc/testing.rdoc +7 -7
  14. data/doc/transactions.rdoc +8 -0
  15. data/lib/sequel/adapters/jdbc.rb +160 -168
  16. data/lib/sequel/adapters/jdbc/db2.rb +17 -18
  17. data/lib/sequel/adapters/jdbc/derby.rb +5 -28
  18. data/lib/sequel/adapters/jdbc/h2.rb +11 -22
  19. data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
  20. data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
  21. data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
  22. data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
  23. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
  24. data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
  25. data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
  26. data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
  27. data/lib/sequel/adapters/openbase.rb +1 -7
  28. data/lib/sequel/adapters/postgres.rb +1 -1
  29. data/lib/sequel/adapters/shared/access.rb +3 -6
  30. data/lib/sequel/adapters/shared/cubrid.rb +24 -9
  31. data/lib/sequel/adapters/shared/db2.rb +13 -5
  32. data/lib/sequel/adapters/shared/firebird.rb +16 -16
  33. data/lib/sequel/adapters/shared/informix.rb +2 -5
  34. data/lib/sequel/adapters/shared/mssql.rb +72 -63
  35. data/lib/sequel/adapters/shared/mysql.rb +72 -40
  36. data/lib/sequel/adapters/shared/oracle.rb +27 -15
  37. data/lib/sequel/adapters/shared/postgres.rb +24 -44
  38. data/lib/sequel/adapters/shared/progress.rb +1 -5
  39. data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
  40. data/lib/sequel/adapters/shared/sqlite.rb +21 -6
  41. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
  42. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
  43. data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
  44. data/lib/sequel/core.rb +14 -9
  45. data/lib/sequel/database/dataset_defaults.rb +1 -0
  46. data/lib/sequel/database/misc.rb +12 -0
  47. data/lib/sequel/database/query.rb +4 -1
  48. data/lib/sequel/database/schema_methods.rb +3 -2
  49. data/lib/sequel/database/transactions.rb +47 -17
  50. data/lib/sequel/dataset/features.rb +12 -2
  51. data/lib/sequel/dataset/mutation.rb +2 -0
  52. data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
  53. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  54. data/lib/sequel/dataset/query.rb +1 -1
  55. data/lib/sequel/dataset/sql.rb +132 -70
  56. data/lib/sequel/extensions/columns_introspection.rb +1 -1
  57. data/lib/sequel/extensions/null_dataset.rb +8 -4
  58. data/lib/sequel/extensions/pg_array.rb +4 -4
  59. data/lib/sequel/extensions/pg_row.rb +1 -0
  60. data/lib/sequel/model/associations.rb +468 -188
  61. data/lib/sequel/model/base.rb +88 -13
  62. data/lib/sequel/plugins/association_pks.rb +23 -64
  63. data/lib/sequel/plugins/auto_validations.rb +3 -2
  64. data/lib/sequel/plugins/dataset_associations.rb +1 -3
  65. data/lib/sequel/plugins/many_through_many.rb +18 -65
  66. data/lib/sequel/plugins/pg_array_associations.rb +97 -86
  67. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  68. data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
  69. data/lib/sequel/plugins/rcte_tree.rb +12 -16
  70. data/lib/sequel/plugins/sharding.rb +21 -3
  71. data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
  72. data/lib/sequel/plugins/subclasses.rb +1 -9
  73. data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
  74. data/lib/sequel/plugins/tree.rb +2 -2
  75. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  76. data/lib/sequel/version.rb +1 -1
  77. data/spec/adapters/mssql_spec.rb +57 -15
  78. data/spec/adapters/mysql_spec.rb +11 -0
  79. data/spec/bin_spec.rb +2 -2
  80. data/spec/core/database_spec.rb +38 -4
  81. data/spec/core/dataset_spec.rb +45 -7
  82. data/spec/core/placeholder_literalizer_spec.rb +17 -0
  83. data/spec/core/schema_spec.rb +6 -1
  84. data/spec/extensions/active_model_spec.rb +18 -9
  85. data/spec/extensions/association_pks_spec.rb +20 -18
  86. data/spec/extensions/association_proxies_spec.rb +9 -9
  87. data/spec/extensions/auto_validations_spec.rb +6 -0
  88. data/spec/extensions/columns_introspection_spec.rb +1 -0
  89. data/spec/extensions/constraint_validations_spec.rb +3 -1
  90. data/spec/extensions/many_through_many_spec.rb +191 -111
  91. data/spec/extensions/pg_array_associations_spec.rb +133 -103
  92. data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
  93. data/spec/extensions/rcte_tree_spec.rb +35 -27
  94. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
  95. data/spec/extensions/sharding_spec.rb +2 -2
  96. data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
  97. data/spec/extensions/to_dot_spec.rb +1 -0
  98. data/spec/extensions/touch_spec.rb +2 -2
  99. data/spec/integration/associations_test.rb +130 -37
  100. data/spec/integration/dataset_test.rb +17 -0
  101. data/spec/integration/model_test.rb +17 -0
  102. data/spec/integration/schema_test.rb +14 -0
  103. data/spec/integration/transaction_test.rb +25 -1
  104. data/spec/model/association_reflection_spec.rb +63 -24
  105. data/spec/model/associations_spec.rb +104 -57
  106. data/spec/model/base_spec.rb +14 -1
  107. data/spec/model/class_dataset_methods_spec.rb +1 -0
  108. data/spec/model/eager_loading_spec.rb +221 -74
  109. data/spec/model/model_spec.rb +119 -1
  110. metadata +4 -2
@@ -44,7 +44,7 @@ module Sequel
44
44
  # If given, +type+ can be :select, :insert, :update, or :delete, in which case it
45
45
  # determines whether WITH is supported for the respective statement type.
46
46
  def supports_cte?(type=:select)
47
- send(:"#{type}_clause_methods").include?(:"#{type}_with_sql")
47
+ false
48
48
  end
49
49
 
50
50
  # Whether the dataset supports common table expressions (the WITH clause)
@@ -99,6 +99,11 @@ module Sequel
99
99
  def supports_lateral_subqueries?
100
100
  false
101
101
  end
102
+
103
+ # Whether limits are supported in correlated subqueries. True by default.
104
+ def supports_limits_in_correlated_subqueries?
105
+ true
106
+ end
102
107
 
103
108
  # Whether modifying joined datasets is supported.
104
109
  def supports_modifying_joins?
@@ -111,6 +116,11 @@ module Sequel
111
116
  true
112
117
  end
113
118
 
119
+ # Whether offsets are supported in correlated subqueries, true by default.
120
+ def supports_offsets_in_correlated_subqueries?
121
+ true
122
+ end
123
+
114
124
  # Whether the dataset supports or can fully emulate the DISTINCT ON clause,
115
125
  # including respecting the ORDER BY clause, false by default
116
126
  def supports_ordered_distinct_on?
@@ -130,7 +140,7 @@ module Sequel
130
140
  # Whether the RETURNING clause is supported for the given type of query.
131
141
  # +type+ can be :insert, :update, or :delete.
132
142
  def supports_returning?(type)
133
- send(:"#{type}_clause_methods").include?(:"#{type}_returning_sql")
143
+ false
134
144
  end
135
145
 
136
146
  # Whether the database supports SELECT *, column FROM table
@@ -58,6 +58,7 @@ module Sequel
58
58
  # Set the method to call on identifiers going into the database for this dataset
59
59
  def identifier_input_method=(v)
60
60
  raise_if_frozen!
61
+ skip_symbol_cache!
61
62
  @identifier_input_method = v
62
63
  end
63
64
 
@@ -77,6 +78,7 @@ module Sequel
77
78
  # Set whether to quote identifiers for this dataset
78
79
  def quote_identifiers=(v)
79
80
  raise_if_frozen!
81
+ skip_symbol_cache!
80
82
  @quote_identifiers = v
81
83
  end
82
84
 
@@ -54,7 +54,11 @@ module Sequel
54
54
  # Record the SQL query offset, argument position, and transforming block where the
55
55
  # argument should be literalized.
56
56
  def sql_literal_append(ds, sql)
57
- @recorder.use(sql, @pos, @transformer)
57
+ if ds.opts[:placeholder_literal_null]
58
+ ds.send(:literal_append, sql, nil)
59
+ else
60
+ @recorder.use(sql, @pos, @transformer)
61
+ end
58
62
  end
59
63
 
60
64
  # Return a new Argument object for the same recorder and argument position, but with a
@@ -74,7 +78,7 @@ module Sequel
74
78
  @argn = -1
75
79
  @args = []
76
80
  ds = yield self, dataset
77
- sql = ds.sql
81
+ sql = ds.clone(:placeholder_literalizer=>self).sql
78
82
 
79
83
  last_offset = 0
80
84
  fragments = @args.map do |used_sql, offset, arg, t|
@@ -150,8 +154,12 @@ module Sequel
150
154
  ds = @dataset
151
155
  @fragments.each do |sql, i, transformer|
152
156
  s << sql
153
- v = args.fetch(i)
154
- v = transformer.call(v) if transformer
157
+ if i.is_a?(Integer)
158
+ v = args.fetch(i)
159
+ v = transformer.call(v) if transformer
160
+ else
161
+ v = i.call
162
+ end
155
163
  ds.literal_append(s, v)
156
164
  end
157
165
  if sql = @final_sql
@@ -165,6 +165,12 @@ module Sequel
165
165
  @opts[:bind_vars].has_key?(k)
166
166
  end
167
167
 
168
+ # The symbol cache should always be skipped, since placeholders
169
+ # are symbols.
170
+ def skip_symbol_cache?
171
+ true
172
+ end
173
+
168
174
  # Use a clone of the dataset extended with prepared statement
169
175
  # support and using the same argument hash so that you can use
170
176
  # bind variables/prepared arguments in subselects.
@@ -892,7 +892,7 @@ module Sequel
892
892
  # DB[:items].with(:items, DB[:syx].where(:name.like('A%')))
893
893
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
894
894
  def with(name, dataset, opts=OPTS)
895
- raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
895
+ raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
896
896
  if hoist_cte?(dataset)
897
897
  s, ds = hoist_cte(dataset)
898
898
  s.with(name, ds, opts)
@@ -5,16 +5,6 @@ module Sequel
5
5
  # These are methods you can call to see what SQL will be generated by the dataset.
6
6
  # ---------------------
7
7
 
8
- # Returns a DELETE SQL query string. See +delete+.
9
- #
10
- # dataset.filter{|o| o.price >= 100}.delete_sql
11
- # # => "DELETE FROM items WHERE (price >= 100)"
12
- def delete_sql
13
- return static_sql(opts[:sql]) if opts[:sql]
14
- check_modification_allowed!
15
- clause_sql(:delete)
16
- end
17
-
18
8
  # Returns an EXISTS clause for the dataset as a +LiteralString+.
19
9
  #
20
10
  # DB.select(1).where(DB[:items].exists)
@@ -59,7 +49,7 @@ module Sequel
59
49
  columns = [columns().last]
60
50
  values = [DEFAULT]
61
51
  end
62
- clone(:columns=>columns, :values=>values)._insert_sql
52
+ clone(:columns=>columns, :values=>values).send(:_insert_sql)
63
53
  end
64
54
 
65
55
  # Append a literal representation of a value to the given SQL string.
@@ -68,7 +58,16 @@ module Sequel
68
58
  def literal_append(sql, v)
69
59
  case v
70
60
  when Symbol
71
- literal_symbol_append(sql, v)
61
+ if skip_symbol_cache?
62
+ literal_symbol_append(sql, v)
63
+ else
64
+ unless l = db.literal_symbol(v)
65
+ l = ''
66
+ literal_symbol_append(l, v)
67
+ db.literal_symbol_set(v, l)
68
+ end
69
+ sql << l
70
+ end
72
71
  when String
73
72
  case v
74
73
  when LiteralString
@@ -116,17 +115,32 @@ module Sequel
116
115
  # This method should be overridden by descendants if the support
117
116
  # inserting multiple records in a single SQL statement.
118
117
  def multi_insert_sql(columns, values)
119
- values.map{|r| insert_sql(columns, r)}
118
+ case multi_insert_sql_strategy
119
+ when :values
120
+ sql = LiteralString.new('VALUES ')
121
+ expression_list_append(sql, values.map{|r| Array(r)})
122
+ [insert_sql(columns, sql)]
123
+ when :union
124
+ c = false
125
+ sql = LiteralString.new('')
126
+ u = UNION_ALL_SELECT
127
+ f = empty_from_sql
128
+ values.each do |v|
129
+ if c
130
+ sql << u
131
+ else
132
+ sql << SELECT << SPACE
133
+ c = true
134
+ end
135
+ expression_list_append(sql, v)
136
+ sql << f if f
137
+ end
138
+ [insert_sql(columns, sql)]
139
+ else
140
+ values.map{|r| insert_sql(columns, r)}
141
+ end
120
142
  end
121
143
 
122
- # Returns a SELECT SQL query string.
123
- #
124
- # dataset.select_sql # => "SELECT * FROM items"
125
- def select_sql
126
- return static_sql(@opts[:sql]) if @opts[:sql]
127
- clause_sql(:select)
128
- end
129
-
130
144
  # Same as +select_sql+, not aliased directly to make subclassing simpler.
131
145
  def sql
132
146
  select_sql
@@ -157,7 +171,7 @@ module Sequel
157
171
  def update_sql(values = OPTS)
158
172
  return static_sql(opts[:sql]) if opts[:sql]
159
173
  check_modification_allowed!
160
- clone(:values=>values)._update_sql
174
+ clone(:values=>values).send(:_update_sql)
161
175
  end
162
176
 
163
177
  # ---------------------
@@ -171,6 +185,47 @@ module Sequel
171
185
  clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
172
186
  end
173
187
 
188
+ # Define a dataset literalization method for the given type in the given module,
189
+ # using the given clauses.
190
+ #
191
+ # Arguments:
192
+ # mod :: Module in which to define method
193
+ # type :: Type of SQL literalization method to create, either :select, :insert, :update, or :delete
194
+ # clauses :: array of clauses that make up the SQL query for the type. This can either be a single
195
+ # array of symbols/strings, or it can be an array of pairs, with the first element in
196
+ # each pair being an if/elsif/else code fragment, and the second element in each pair
197
+ # being an array of symbol/strings for the appropriate branch.
198
+ def self.def_sql_method(mod, type, clauses)
199
+ priv = type == :update || type == :insert
200
+
201
+ lines = []
202
+ lines << 'private' if priv
203
+ lines << "def #{'_' if priv}#{type}_sql"
204
+ lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
205
+ lines << 'check_modification_allowed!' if type == :delete
206
+ lines << 'sql = @opts[:append_sql] || sql_string_origin'
207
+
208
+ if clauses.all?{|c| c.is_a?(Array)}
209
+ clauses.each do |i, cs|
210
+ lines << i
211
+ lines.concat(clause_methods(type, cs).map{|x| "#{x}(sql)"})
212
+ end
213
+ lines << 'end'
214
+ else
215
+ lines.concat(clause_methods(type, clauses).map{|x| "#{x}(sql)"})
216
+ end
217
+
218
+ lines << 'sql'
219
+ lines << 'end'
220
+
221
+ mod.class_eval lines.join("\n"), __FILE__, __LINE__
222
+ end
223
+
224
+ def_sql_method(self, :delete, %w'delete from where')
225
+ def_sql_method(self, :insert, %w'insert into columns values')
226
+ def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock')
227
+ def_sql_method(self, :update, %w'update table set where')
228
+
174
229
  # Map of emulated function names to native function names.
175
230
  EMULATED_FUNCTION_MAP = {}
176
231
 
@@ -210,7 +265,6 @@ module Sequel
210
265
  DEFAULT = LiteralString.new('DEFAULT').freeze
211
266
  DEFAULT_VALUES = " DEFAULT VALUES".freeze
212
267
  DELETE = 'DELETE'.freeze
213
- DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'delete from where')
214
268
  DESC = ' DESC'.freeze
215
269
  DISTINCT = " DISTINCT".freeze
216
270
  DOT = '.'.freeze
@@ -234,7 +288,6 @@ module Sequel
234
288
  GROUP_BY = " GROUP BY ".freeze
235
289
  HAVING = " HAVING ".freeze
236
290
  INSERT = "INSERT".freeze
237
- INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'insert into columns values')
238
291
  INTO = " INTO ".freeze
239
292
  IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
240
293
  IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
@@ -263,7 +316,6 @@ module Sequel
263
316
  QUOTE_RE = /"/.freeze
264
317
  RETURNING = " RETURNING ".freeze
265
318
  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
319
  SET = ' SET '.freeze
268
320
  SPACE = ' '.freeze
269
321
  SQL_WITH = "WITH ".freeze
@@ -275,8 +327,8 @@ module Sequel
275
327
  REGEXP_OPERATORS = ::Sequel::SQL::ComplexExpression::REGEXP_OPERATORS
276
328
  UNDERSCORE = '_'.freeze
277
329
  UPDATE = 'UPDATE'.freeze
278
- UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'update table set where')
279
330
  USING = ' USING ('.freeze
331
+ UNION_ALL_SELECT = ' UNION ALL SELECT '.freeze
280
332
  VALUES = " VALUES ".freeze
281
333
  V190 = '1.9.0'.freeze
282
334
  WHERE = " WHERE ".freeze
@@ -463,7 +515,11 @@ module Sequel
463
515
  # Append literalization of delayed evaluation to SQL string,
464
516
  # causing the delayed evaluation proc to be evaluated.
465
517
  def delayed_evaluation_sql_append(sql, callable)
466
- literal_append(sql, callable.call)
518
+ if recorder = @opts[:placeholder_literalizer]
519
+ recorder.use(sql, callable, nil)
520
+ else
521
+ literal_append(sql, callable.call)
522
+ end
467
523
  end
468
524
 
469
525
  # Append literalization of emulated function call to SQL string.
@@ -718,16 +774,6 @@ module Sequel
718
774
 
719
775
  protected
720
776
 
721
- # Formats in INSERT statement using the stored columns and values.
722
- def _insert_sql
723
- clause_sql(:insert)
724
- end
725
-
726
- # Formats an UPDATE statement using the stored values.
727
- def _update_sql
728
- clause_sql(:update)
729
- end
730
-
731
777
  # Return a from_self dataset if an order or limit is specified, so it works as expected
732
778
  # with UNION, EXCEPT, and INTERSECT clauses.
733
779
  def compound_from_self
@@ -828,13 +874,6 @@ module Sequel
828
874
  check_modification_allowed!
829
875
  end
830
876
 
831
- # Prepare an SQL statement by calling all clause methods for the given statement type.
832
- def clause_sql(type)
833
- sql = @opts[:append_sql] || sql_string_origin
834
- send("#{type}_clause_methods").each{|x| send(x, sql)}
835
- sql
836
- end
837
-
838
877
  # Append column list to SQL string.
839
878
  # Converts an array of column names into a comma seperated string of
840
879
  # column names. If the array is empty, a wildcard (*) is returned.
@@ -904,15 +943,23 @@ module Sequel
904
943
  requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
905
944
  end
906
945
 
907
- # The order of methods to call to build the DELETE SQL statement
908
- def delete_clause_methods
909
- DELETE_CLAUSE_METHODS
910
- end
911
-
912
946
  def delete_delete_sql(sql)
913
947
  sql << DELETE
914
948
  end
915
949
 
950
+ def delete_from_sql(sql)
951
+ if f = @opts[:from]
952
+ sql << FROM
953
+ source_list_append(sql, f)
954
+ end
955
+ end
956
+
957
+ # An SQL FROM clause to use in SELECT statements where the dataset has
958
+ # no from tables.
959
+ def empty_from_sql
960
+ nil
961
+ end
962
+
916
963
  # Append literalization of array of expressions to SQL string.
917
964
  def expression_list_append(sql, columns)
918
965
  c = false
@@ -1001,11 +1048,6 @@ module Sequel
1001
1048
  source_list_append(sql, @opts[:from])
1002
1049
  end
1003
1050
 
1004
- # The order of methods to call to build the INSERT SQL statement
1005
- def insert_clause_methods
1006
- INSERT_CLAUSE_METHODS
1007
- end
1008
-
1009
1051
  def insert_columns_sql(sql)
1010
1052
  columns = opts[:columns]
1011
1053
  if columns && !columns.empty?
@@ -1191,6 +1233,14 @@ module Sequel
1191
1233
  BOOL_TRUE
1192
1234
  end
1193
1235
 
1236
+ # What strategy to use for import/multi_insert. While SQL-92 defaults
1237
+ # to allowing multiple rows in a VALUES clause, there are enough databases
1238
+ # that don't allow that that it can't be the default. Use separate queries
1239
+ # by default, which works everywhere.
1240
+ def multi_insert_sql_strategy
1241
+ :separate
1242
+ end
1243
+
1194
1244
  # Get the native function name given the emulated function name.
1195
1245
  def native_function_name(emulated_function)
1196
1246
  self.class.const_get(:EMULATED_FUNCTION_MAP).fetch(emulated_function, emulated_function)
@@ -1222,11 +1272,6 @@ module Sequel
1222
1272
  Qualifier.new(self, table).transform(e)
1223
1273
  end
1224
1274
 
1225
- # The order of methods to call to build the SELECT SQL statement
1226
- def select_clause_methods
1227
- SELECT_CLAUSE_METHODS
1228
- end
1229
-
1230
1275
  def select_columns_sql(sql)
1231
1276
  sql << SPACE
1232
1277
  column_list_append(sql, @opts[:select])
@@ -1260,9 +1305,10 @@ module Sequel
1260
1305
  if f = @opts[:from]
1261
1306
  sql << FROM
1262
1307
  source_list_append(sql, f)
1308
+ elsif f = empty_from_sql
1309
+ sql << f
1263
1310
  end
1264
1311
  end
1265
- alias delete_from_sql select_from_sql
1266
1312
 
1267
1313
  def select_group_sql(sql)
1268
1314
  if group = @opts[:group]
@@ -1299,13 +1345,15 @@ module Sequel
1299
1345
  if l = @opts[:limit]
1300
1346
  sql << LIMIT
1301
1347
  literal_append(sql, l)
1302
- end
1303
- if o = @opts[:offset]
1304
- sql << OFFSET
1305
- literal_append(sql, o)
1348
+ if o = @opts[:offset]
1349
+ sql << OFFSET
1350
+ literal_append(sql, o)
1351
+ end
1352
+ elsif @opts[:offset]
1353
+ select_only_offset_sql(sql)
1306
1354
  end
1307
1355
  end
1308
-
1356
+
1309
1357
  def select_lock_sql(sql)
1310
1358
  case l = @opts[:lock]
1311
1359
  when :update
@@ -1315,6 +1363,14 @@ module Sequel
1315
1363
  end
1316
1364
  end
1317
1365
 
1366
+ # Used only if there is an offset and no limit, making it easier to override
1367
+ # in the adapter, as many databases do not support just a plain offset with
1368
+ # no limit.
1369
+ def select_only_offset_sql(sql)
1370
+ sql << OFFSET
1371
+ literal_append(sql, @opts[:offset])
1372
+ end
1373
+
1318
1374
  def select_order_sql(sql)
1319
1375
  if o = @opts[:order]
1320
1376
  sql << ORDER_BY
@@ -1338,6 +1394,7 @@ module Sequel
1338
1394
  alias update_where_sql select_where_sql
1339
1395
 
1340
1396
  def select_with_sql(sql)
1397
+ return unless supports_cte?
1341
1398
  ws = opts[:with]
1342
1399
  return if !ws || ws.empty?
1343
1400
  sql << select_with_sql_base
@@ -1366,6 +1423,16 @@ module Sequel
1366
1423
  SQL_WITH
1367
1424
  end
1368
1425
 
1426
+ # Whether the symbol cache should be skipped when literalizing the dataset
1427
+ def skip_symbol_cache?
1428
+ @skip_symbol_cache
1429
+ end
1430
+
1431
+ # Set the dataset to skip the symbol cache
1432
+ def skip_symbol_cache!
1433
+ @skip_symbol_cache = true
1434
+ end
1435
+
1369
1436
  # Append literalization of array of sources/tables to SQL string, raising an Error if there
1370
1437
  # are no sources.
1371
1438
  def source_list_append(sql, sources)
@@ -1409,11 +1476,6 @@ module Sequel
1409
1476
  ds.clone(:append_sql=>sql).sql
1410
1477
  end
1411
1478
 
1412
- # The order of methods to call to build the UPDATE SQL statement
1413
- def update_clause_methods
1414
- UPDATE_CLAUSE_METHODS
1415
- end
1416
-
1417
1479
  def update_table_sql(sql)
1418
1480
  sql << SPACE
1419
1481
  source_list_append(sql, @opts[:from])