sequel 4.9.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
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])