sequel 4.8.0 → 4.9.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +48 -0
  3. data/doc/association_basics.rdoc +1 -1
  4. data/doc/opening_databases.rdoc +4 -0
  5. data/doc/postgresql.rdoc +27 -3
  6. data/doc/release_notes/4.9.0.txt +190 -0
  7. data/doc/security.rdoc +1 -1
  8. data/doc/testing.rdoc +2 -2
  9. data/doc/validations.rdoc +8 -0
  10. data/lib/sequel/adapters/jdbc.rb +5 -3
  11. data/lib/sequel/adapters/jdbc/derby.rb +2 -8
  12. data/lib/sequel/adapters/jdbc/h2.rb +2 -13
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -16
  14. data/lib/sequel/adapters/mysql2.rb +11 -1
  15. data/lib/sequel/adapters/postgres.rb +33 -10
  16. data/lib/sequel/adapters/shared/db2.rb +2 -10
  17. data/lib/sequel/adapters/shared/mssql.rb +10 -8
  18. data/lib/sequel/adapters/shared/oracle.rb +9 -24
  19. data/lib/sequel/adapters/shared/postgres.rb +32 -9
  20. data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -4
  21. data/lib/sequel/adapters/shared/sqlite.rb +4 -7
  22. data/lib/sequel/database/schema_methods.rb +15 -0
  23. data/lib/sequel/dataset.rb +1 -1
  24. data/lib/sequel/dataset/actions.rb +159 -27
  25. data/lib/sequel/dataset/graph.rb +29 -7
  26. data/lib/sequel/dataset/misc.rb +6 -0
  27. data/lib/sequel/dataset/placeholder_literalizer.rb +164 -0
  28. data/lib/sequel/dataset/query.rb +2 -0
  29. data/lib/sequel/dataset/sql.rb +103 -91
  30. data/lib/sequel/extensions/current_datetime_timestamp.rb +57 -0
  31. data/lib/sequel/extensions/pg_array.rb +68 -106
  32. data/lib/sequel/extensions/pg_hstore.rb +5 -5
  33. data/lib/sequel/extensions/schema_dumper.rb +49 -49
  34. data/lib/sequel/model.rb +4 -2
  35. data/lib/sequel/model/associations.rb +1 -1
  36. data/lib/sequel/model/base.rb +136 -3
  37. data/lib/sequel/model/errors.rb +6 -0
  38. data/lib/sequel/plugins/defaults_setter.rb +1 -1
  39. data/lib/sequel/plugins/eager_each.rb +9 -0
  40. data/lib/sequel/plugins/nested_attributes.rb +2 -2
  41. data/lib/sequel/plugins/timestamps.rb +2 -2
  42. data/lib/sequel/plugins/touch.rb +2 -2
  43. data/lib/sequel/sql.rb +20 -15
  44. data/lib/sequel/version.rb +1 -1
  45. data/spec/adapters/postgres_spec.rb +70 -8
  46. data/spec/core/dataset_spec.rb +172 -27
  47. data/spec/core/expression_filters_spec.rb +3 -3
  48. data/spec/core/object_graph_spec.rb +17 -1
  49. data/spec/core/placeholder_literalizer_spec.rb +128 -0
  50. data/spec/core/schema_spec.rb +54 -0
  51. data/spec/extensions/current_datetime_timestamp_spec.rb +27 -0
  52. data/spec/extensions/defaults_setter_spec.rb +12 -0
  53. data/spec/extensions/eager_each_spec.rb +6 -0
  54. data/spec/extensions/nested_attributes_spec.rb +14 -2
  55. data/spec/extensions/pg_array_spec.rb +15 -7
  56. data/spec/extensions/shared_caching_spec.rb +5 -5
  57. data/spec/extensions/timestamps_spec.rb +9 -0
  58. data/spec/extensions/touch_spec.rb +9 -0
  59. data/spec/integration/database_test.rb +1 -1
  60. data/spec/integration/dataset_test.rb +27 -5
  61. data/spec/model/eager_loading_spec.rb +32 -0
  62. data/spec/model/model_spec.rb +119 -9
  63. metadata +8 -2
@@ -90,11 +90,18 @@ module Sequel
90
90
  # Only allow table aliases that haven't been used
91
91
  raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
92
92
 
93
+ table_alias_qualifier = qualifier_from_alias_symbol(table_alias, table)
94
+ implicit_qualifier = options[:implicit_qualifier]
95
+ ds = self
96
+
93
97
  # Use a from_self if this is already a joined table (or from_self specifically disabled for graphs)
94
- ds = (@opts[:graph_from_self] != false && !@opts[:graph] && (@opts[:from].length > 1 || @opts[:join])) ? from_self(:alias=>options[:from_self_alias] || first_source) : self
98
+ if (@opts[:graph_from_self] != false && !@opts[:graph] && (@opts[:from].length > 1 || @opts[:join]))
99
+ implicit_qualifier = options[:from_self_alias] || first_source
100
+ ds = ds.from_self(:alias=>implicit_qualifier)
101
+ end
95
102
 
96
103
  # Join the table early in order to avoid cloning the dataset twice
97
- ds = ds.join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias, :implicit_qualifier=>options[:implicit_qualifier], :qualify=>options[:qualify], &block)
104
+ ds = ds.join_table(options[:join_type] || :left_outer, table, join_conditions, :table_alias=>table_alias_qualifier, :implicit_qualifier=>implicit_qualifier, :qualify=>options[:qualify], &block)
98
105
  opts = ds.opts
99
106
 
100
107
  # Whether to include the table in the result set
@@ -108,7 +115,8 @@ module Sequel
108
115
  select = opts[:select].dup
109
116
  [:column_aliases, :table_aliases, :column_alias_num].each{|k| graph[k] = graph[k].dup}
110
117
  else
111
- master = alias_symbol(ds.first_source_alias)
118
+ qualifier = ds.first_source_alias
119
+ master = alias_symbol(qualifier)
112
120
  raise_alias_error.call if master == table_alias
113
121
  # Master hash storing all .graph related information
114
122
  graph = opts[:graph] = {}
@@ -143,11 +151,11 @@ module Sequel
143
151
  end
144
152
  column_aliases[column] = [master, column]
145
153
  end
146
- select = qualified_expression(select, master)
154
+ select = qualified_expression(select, qualifier)
147
155
  else
148
156
  select = columns.map do |column|
149
157
  column_aliases[column] = [master, column]
150
- SQL::QualifiedIdentifier.new(master, column)
158
+ SQL::QualifiedIdentifier.new(qualifier, column)
151
159
  end
152
160
  end
153
161
  end
@@ -179,9 +187,9 @@ module Sequel
179
187
  column_alias = :"#{column_alias}_#{column_alias_num}"
180
188
  ca_num[column_alias] += 1
181
189
  end
182
- [column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table_alias, column), column_alias)]
190
+ [column_alias, SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table_alias_qualifier, column), column_alias)]
183
191
  else
184
- ident = SQL::QualifiedIdentifier.new(table_alias, column)
192
+ ident = SQL::QualifiedIdentifier.new(table_alias_qualifier, column)
185
193
  [column, ident]
186
194
  end
187
195
  column_aliases[col_alias] = [table_alias, column]
@@ -229,6 +237,20 @@ module Sequel
229
237
 
230
238
  private
231
239
 
240
+ # Wrap the alias symbol in an SQL::Identifier if the identifier on which is based
241
+ # is an SQL::Identifier. This works around cases where the alias symbol contains
242
+ # double embedded underscores which would be considered an implicit qualified identifier
243
+ # if not wrapped in an SQL::Identifier.
244
+ def qualifier_from_alias_symbol(aliaz, identifier)
245
+ identifier = identifier.column if identifier.is_a?(SQL::QualifiedIdentifier)
246
+ case identifier
247
+ when SQL::Identifier
248
+ Sequel.identifier(aliaz)
249
+ else
250
+ aliaz
251
+ end
252
+ end
253
+
232
254
  # Transform the hash of graph aliases and return a two element array
233
255
  # where the first element is an array of identifiers suitable to pass to
234
256
  # a select method, and the second is a new hash of preprocessed graph aliases.
@@ -36,6 +36,12 @@ module Sequel
36
36
  o.is_a?(self.class) && db == o.db && opts == o.opts && sql == o.sql
37
37
  end
38
38
 
39
+ # An object representing the current date or time, should be an instance
40
+ # of Sequel.datetime_class.
41
+ def current_datetime
42
+ Sequel.datetime_class.now
43
+ end
44
+
39
45
  # Alias for ==
40
46
  def eql?(o)
41
47
  self == o
@@ -0,0 +1,164 @@
1
+ module Sequel
2
+ class Dataset
3
+ # PlaceholderLiteralizer allows you to record the application of arbitrary changes
4
+ # to a dataset with placeholder arguments, recording where those placeholder arguments
5
+ # are used in the query. When running the query, the literalization process is much
6
+ # faster as Sequel can skip most of the work it normal has to do when literalizing a
7
+ # dataset.
8
+ #
9
+ # Basically, this enables optimizations that allow Sequel to cache the SQL produced
10
+ # for a given dataset, so that it doesn't need to recompute that information every
11
+ # time.
12
+ #
13
+ # Example:
14
+ #
15
+ # loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
16
+ # ds.where(:id=>pl.arg).exclude(:name=>pl.arg).limit(1)
17
+ # end
18
+ # loader.first(1, "foo")
19
+ # # SELECT * FROM items WHERE ((id = 1) AND (name != 'foo')) LIMIT 1
20
+ # loader.first(2, "bar")
21
+ # # SELECT * FROM items WHERE ((id = 2) AND (name != 'bar')) LIMIT 1
22
+ #
23
+ # Caveats:
24
+ #
25
+ # Note that this method does not handle all possible cases. For example:
26
+ #
27
+ # loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
28
+ # ds.join(pl.arg, :item_id=>:id)
29
+ # end
30
+ # loader(:cart_items)
31
+ #
32
+ # Will not qualify the item_id column with cart_items. In this type of situation it's
33
+ # best to add a table alias when joining:
34
+ #
35
+ # loader = Sequel::Dataset::PlaceholderLiteralizer.loader(DB[:items]) do |pl, ds|
36
+ # ds.join(Sequel.as(pl.arg, :t), :item_id=>:id)
37
+ # end
38
+ # loader(:cart_items)
39
+ #
40
+ # There are other similar cases that are not handled, mainly when Sequel changes the
41
+ # SQL produced depending on the types of the arguments.
42
+ class PlaceholderLiteralizer
43
+ # A placeholder argument used by the PlaceholderLiteralizer. This records the offset
44
+ # that the argument should be used in the resulting SQL.
45
+ class Argument
46
+ # Set the recorder, the argument position, and any transforming block to use
47
+ # for this placeholder.
48
+ def initialize(recorder, pos, transformer=nil)
49
+ @recorder = recorder
50
+ @pos = pos
51
+ @transformer = transformer
52
+ end
53
+
54
+ # Record the SQL query offset, argument position, and transforming block where the
55
+ # argument should be literalized.
56
+ def sql_literal_append(ds, sql)
57
+ @recorder.use(sql, @pos, @transformer)
58
+ end
59
+
60
+ # Return a new Argument object for the same recorder and argument position, but with a
61
+ # different transformer block.
62
+ def transform(&block)
63
+ Argument.new(@recorder, @pos, block)
64
+ end
65
+ end
66
+
67
+ # Records the offsets at which the placeholder arguments are used in
68
+ # the SQL query.
69
+ class Recorder
70
+ # Yields the receiver and the dataset to the block, which should
71
+ # call #arg on the receiver for each placeholder argument, and
72
+ # return the dataset that you want to load.
73
+ def loader(dataset)
74
+ @argn = -1
75
+ @args = []
76
+ ds = yield self, dataset
77
+ sql = ds.sql
78
+
79
+ last_offset = 0
80
+ fragments = @args.map do |used_sql, offset, arg, t|
81
+ raise Error, "placeholder literalizer argument literalized into different string than dataset returned" unless used_sql.equal?(sql)
82
+ a = [sql[last_offset...offset], arg, t]
83
+ last_offset = offset
84
+ a
85
+ end
86
+ final_sql = sql[last_offset..-1]
87
+
88
+ arity = @argn+1
89
+ PlaceholderLiteralizer.new(ds.clone, fragments, final_sql, arity)
90
+ end
91
+
92
+ # Return an Argument with the specified position, or the next position. In
93
+ # general you shouldn't mix calls with an argument and calls without an
94
+ # argument for the same receiver.
95
+ def arg(v=(no_arg_given = true; @argn+=1))
96
+ unless no_arg_given
97
+ @argn = v if @argn < v
98
+ end
99
+ Argument.new(self, v)
100
+ end
101
+
102
+ # Record the offset at which the argument is used in the SQL query, and any
103
+ # transforming
104
+ def use(sql, arg, transformer)
105
+ @args << [sql, sql.length, arg, transformer]
106
+ end
107
+ end
108
+
109
+ # Create a PlaceholderLiteralizer by yielding a Recorder and dataset to the
110
+ # given block, recording the offsets at which the recorders arguments
111
+ # are used in the query.
112
+ def self.loader(dataset, &block)
113
+ Recorder.new.loader(dataset, &block)
114
+ end
115
+
116
+ # Save the dataset, array of SQL fragments, and ending SQL string.
117
+ def initialize(dataset, fragments, final_sql, arity)
118
+ @dataset = dataset
119
+ @fragments = fragments
120
+ @final_sql = final_sql
121
+ @arity = arity
122
+ end
123
+
124
+ # Return an array of all objects by running the SQL query for the given arguments.
125
+ # If a block is given, yields all objects to the block after loading them.
126
+ def all(*args, &block)
127
+ @dataset.with_sql_all(sql(*args), &block)
128
+ end
129
+
130
+ # Run the SQL query for the given arguments, yielding each returned row to the block.
131
+ def each(*args, &block)
132
+ @dataset.with_sql_each(sql(*args), &block)
133
+ end
134
+
135
+ # Run the SQL query for the given arguments, returning the first row.
136
+ def first(*args)
137
+ @dataset.with_sql_first(sql(*args))
138
+ end
139
+
140
+ # Run the SQL query for the given arguments, returning the first value. For this to
141
+ # make sense, the dataset should return a single row with a single value (or no rows).
142
+ def get(*args)
143
+ @dataset.with_sql_single_value(sql(*args))
144
+ end
145
+
146
+ # Return the SQL query to use for the given arguments.
147
+ def sql(*args)
148
+ raise Error, "wrong number of arguments (#{args.length} for #{@arity})" unless args.length == @arity
149
+ s = ''
150
+ ds = @dataset
151
+ @fragments.each do |sql, i, transformer|
152
+ s << sql
153
+ v = args.fetch(i)
154
+ v = transformer.call(v) if transformer
155
+ ds.literal_append(s, v)
156
+ end
157
+ if sql = @final_sql
158
+ s << sql
159
+ end
160
+ s
161
+ end
162
+ end
163
+ end
164
+ end
@@ -1049,6 +1049,8 @@ module Sequel
1049
1049
  end
1050
1050
  when String
1051
1051
  LiteralString.new("(#{expr})")
1052
+ when PlaceholderLiteralizer::Argument
1053
+ expr.transform{|v| filter_expr(v)}
1052
1054
  else
1053
1055
  raise(Error, "Invalid filter argument: #{expr.inspect}")
1054
1056
  end
@@ -62,15 +62,8 @@ module Sequel
62
62
  clone(:columns=>columns, :values=>values)._insert_sql
63
63
  end
64
64
 
65
- # Returns a literal representation of a value to be used as part
66
- # of an SQL expression.
65
+ # Append a literal representation of a value to the given SQL string.
67
66
  #
68
- # DB[:items].literal("abc'def\\") #=> "'abc''def\\\\'"
69
- # DB[:items].literal(:items__id) #=> "items.id"
70
- # DB[:items].literal([1, 2, 3]) => "(1, 2, 3)"
71
- # DB[:items].literal(DB[:items]) => "(SELECT * FROM items)"
72
- # DB[:items].literal(:x + 1 > :y) => "((x + 1) > y)"
73
- #
74
67
  # If an unsupported object is given, an +Error+ is raised.
75
68
  def literal_append(sql, v)
76
69
  case v
@@ -104,9 +97,9 @@ module Sequel
104
97
  when Array
105
98
  literal_array_append(sql, v)
106
99
  when Time
107
- sql << (v.is_a?(SQLTime) ? literal_sqltime(v) : literal_time(v))
100
+ v.is_a?(SQLTime) ? literal_sqltime_append(sql, v) : literal_time_append(sql, v)
108
101
  when DateTime
109
- sql << literal_datetime(v)
102
+ literal_datetime_append(sql, v)
110
103
  when Date
111
104
  sql << literal_date(v)
112
105
  when Dataset
@@ -190,6 +183,9 @@ module Sequel
190
183
  AS = ' AS '.freeze
191
184
  ASC = ' ASC'.freeze
192
185
  BACKSLASH = "\\".freeze
186
+ BITCOMP_CLOSE = ") - 1)".freeze
187
+ BITCOMP_OPEN = "((0 - ".freeze
188
+ BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
193
189
  BOOL_FALSE = "'f'".freeze
194
190
  BOOL_TRUE = "'t'".freeze
195
191
  BRACKET_CLOSE = ']'.freeze
@@ -295,13 +291,13 @@ module Sequel
295
291
  END
296
292
  end
297
293
 
298
- # SQL fragment for AliasedExpression
294
+ # Append literalization of aliased expression to SQL string.
299
295
  def aliased_expression_sql_append(sql, ae)
300
296
  literal_append(sql, ae.expression)
301
297
  as_sql_append(sql, ae.alias)
302
298
  end
303
299
 
304
- # SQL fragment for Array
300
+ # Append literalization of array to SQL string.
305
301
  def array_sql_append(sql, a)
306
302
  if a.empty?
307
303
  sql << ARRAY_EMPTY
@@ -312,7 +308,7 @@ module Sequel
312
308
  end
313
309
  end
314
310
 
315
- # SQL fragment for BooleanConstants
311
+ # Append literalization of boolean constant to SQL string.
316
312
  def boolean_constant_sql_append(sql, constant)
317
313
  if (constant == true || constant == false) && !supports_where_true?
318
314
  sql << (constant == true ? CONDITION_TRUE : CONDITION_FALSE)
@@ -321,7 +317,7 @@ module Sequel
321
317
  end
322
318
  end
323
319
 
324
- # SQL fragment for CaseExpression
320
+ # Append literalization of case expression to SQL string.
325
321
  def case_expression_sql_append(sql, ce)
326
322
  sql << CASE_OPEN
327
323
  if ce.expression?
@@ -341,7 +337,7 @@ module Sequel
341
337
  sql << CASE_END
342
338
  end
343
339
 
344
- # SQL fragment for the SQL CAST expression
340
+ # Append literalization of cast expression to SQL string.
345
341
  def cast_sql_append(sql, expr, type)
346
342
  sql << CAST_OPEN
347
343
  literal_append(sql, expr)
@@ -349,12 +345,12 @@ module Sequel
349
345
  sql << PAREN_CLOSE
350
346
  end
351
347
 
352
- # SQL fragment for specifying all columns in a given table
348
+ # Append literalization of column all selection to SQL string.
353
349
  def column_all_sql_append(sql, ca)
354
350
  qualified_identifier_sql_append(sql, ca.table, WILDCARD)
355
351
  end
356
352
 
357
- # SQL fragment for the complex expression.
353
+ # Append literalization of complex expression to SQL string.
358
354
  def complex_expression_sql_append(sql, op, args)
359
355
  case op
360
356
  when *IS_OPERATORS
@@ -459,18 +455,18 @@ module Sequel
459
455
  end
460
456
  end
461
457
 
462
- # SQL fragment for constants
458
+ # Append literalization of constant to SQL string.
463
459
  def constant_sql_append(sql, constant)
464
460
  sql << constant.to_s
465
461
  end
466
462
 
467
- # SQL fragment for delayed evaluations, evaluating the
468
- # object and literalizing the returned value.
463
+ # Append literalization of delayed evaluation to SQL string,
464
+ # causing the delayed evaluation proc to be evaluated.
469
465
  def delayed_evaluation_sql_append(sql, callable)
470
466
  literal_append(sql, callable.call)
471
467
  end
472
468
 
473
- # SQL fragment specifying an emulated SQL function call.
469
+ # Append literalization of emulated function call to SQL string.
474
470
  # By default, assumes just the function name may need to
475
471
  # be emulated, adapters should set an EMULATED_FUNCTION_MAP
476
472
  # hash mapping emulated functions to native functions in
@@ -479,12 +475,12 @@ module Sequel
479
475
  _function_sql_append(sql, native_function_name(f.f), f.args)
480
476
  end
481
477
 
482
- # SQL fragment specifying an SQL function call without emulation.
478
+ # Append literalization of function call to SQL string.
483
479
  def function_sql_append(sql, f)
484
480
  _function_sql_append(sql, f.f, f.args)
485
481
  end
486
482
 
487
- # SQL fragment specifying a JOIN clause without ON or USING.
483
+ # Append literalization of JOIN clause without ON or USING to SQL string.
488
484
  def join_clause_sql_append(sql, jc)
489
485
  table = jc.table
490
486
  table_alias = jc.table_alias
@@ -494,14 +490,14 @@ module Sequel
494
490
  as_sql_append(sql, table_alias) if table_alias
495
491
  end
496
492
 
497
- # SQL fragment specifying a JOIN clause with ON.
493
+ # Append literalization of JOIN ON clause to SQL string.
498
494
  def join_on_clause_sql_append(sql, jc)
499
495
  join_clause_sql_append(sql, jc)
500
496
  sql << ON
501
497
  literal_append(sql, filter_expr(jc.on))
502
498
  end
503
499
 
504
- # SQL fragment specifying a JOIN clause with USING.
500
+ # Append literalization of JOIN USING clause to SQL string.
505
501
  def join_using_clause_sql_append(sql, jc)
506
502
  join_clause_sql_append(sql, jc)
507
503
  sql << USING
@@ -509,14 +505,13 @@ module Sequel
509
505
  sql << PAREN_CLOSE
510
506
  end
511
507
 
512
- # SQL fragment for NegativeBooleanConstants
508
+ # Append literalization of negative boolean constant to SQL string.
513
509
  def negative_boolean_constant_sql_append(sql, constant)
514
510
  sql << NOT_SPACE
515
511
  boolean_constant_sql_append(sql, constant)
516
512
  end
517
513
 
518
- # SQL fragment for the ordered expression, used in the ORDER BY
519
- # clause.
514
+ # Append literalization of ordered expression to SQL string.
520
515
  def ordered_expression_sql_append(sql, oe)
521
516
  literal_append(sql, oe.expression)
522
517
  sql << (oe.descending ? DESC : ASC)
@@ -528,7 +523,7 @@ module Sequel
528
523
  end
529
524
  end
530
525
 
531
- # SQL fragment for a literal string with placeholders
526
+ # Append literalization of placeholder literal string to SQL string.
532
527
  def placeholder_literal_string_sql_append(sql, pls)
533
528
  args = pls.args
534
529
  str = pls.str
@@ -572,8 +567,7 @@ module Sequel
572
567
  sql << PAREN_CLOSE if pls.parens
573
568
  end
574
569
 
575
- # SQL fragment for the qualifed identifier, specifying
576
- # a table and a column (or schema and table).
570
+ # Append literalization of qualified identifier to SQL string.
577
571
  # If 3 arguments are given, the 2nd should be the table/qualifier and the third should be
578
572
  # column/qualified. If 2 arguments are given, the 2nd should be an SQL::QualifiedIdentifier.
579
573
  def qualified_identifier_sql_append(sql, table, column=(c = table.column; table = table.table; c))
@@ -582,6 +576,7 @@ module Sequel
582
576
  identifier_append(sql, column)
583
577
  end
584
578
 
579
+ # Append literalization of unqualified identifier to SQL string.
585
580
  # Adds quoting to identifiers (columns and tables). If identifiers are not
586
581
  # being quoted, returns name as a string. If identifiers are being quoted
587
582
  # quote the name with quoted_identifier.
@@ -599,8 +594,7 @@ module Sequel
599
594
  end
600
595
  end
601
596
 
602
- # Separates the schema from the table and returns a string with them
603
- # quoted (if quoting identifiers)
597
+ # Append literalization of identifier or unqualified identifier to SQL string.
604
598
  def quote_schema_table_append(sql, table)
605
599
  schema, table = schema_and_table(table)
606
600
  if schema
@@ -610,6 +604,7 @@ module Sequel
610
604
  quote_identifier_append(sql, table)
611
605
  end
612
606
 
607
+ # Append literalization of quoted identifier to SQL string.
613
608
  # This method quotes the given name with the SQL standard double quote.
614
609
  # should be overridden by subclasses to provide quoting not matching the
615
610
  # SQL standard, such as backtick (used by MySQL and SQLite).
@@ -657,7 +652,7 @@ module Sequel
657
652
  end
658
653
  end
659
654
 
660
- # SQL fragment for specifying subscripts (SQL array accesses)
655
+ # Append literalization of subscripts (SQL array accesses) to SQL string.
661
656
  def subscript_sql_append(sql, s)
662
657
  literal_append(sql, s.f)
663
658
  sql << BRACKET_OPEN
@@ -673,7 +668,7 @@ module Sequel
673
668
  sql << BRACKET_CLOSE
674
669
  end
675
670
 
676
- # The SQL fragment for the given window's options.
671
+ # Append literalization of windows (for window functions) to SQL string.
677
672
  def window_sql_append(sql, opts)
678
673
  raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
679
674
  sql << PAREN_OPEN
@@ -714,7 +709,7 @@ module Sequel
714
709
  sql << PAREN_CLOSE
715
710
  end
716
711
 
717
- # The SQL fragment for the given window function's function and window.
712
+ # Append literalization of window function calls to SQL string.
718
713
  def window_function_sql_append(sql, function, window)
719
714
  literal_append(sql, function)
720
715
  sql << OVER
@@ -815,7 +810,7 @@ module Sequel
815
810
  options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
816
811
  end
817
812
 
818
- # SQL fragment for specifying an alias. expression should already be literalized.
813
+ # Append aliasing expression to SQL string.
819
814
  def as_sql_append(sql, aliaz)
820
815
  sql << AS
821
816
  quote_identifier_append(sql, aliaz)
@@ -840,6 +835,7 @@ module Sequel
840
835
  sql
841
836
  end
842
837
 
838
+ # Append column list to SQL string.
843
839
  # Converts an array of column names into a comma seperated string of
844
840
  # column names. If the array is empty, a wildcard (*) is returned.
845
841
  def column_list_append(sql, columns)
@@ -850,24 +846,50 @@ module Sequel
850
846
  end
851
847
  end
852
848
 
853
- # Yield each two pair of arguments to the block, which should
854
- # return a string representing the SQL code for those
855
- # two arguments. If more than 2 arguments are provided, all
856
- # calls to the block # after the first will have a LiteralString
857
- # as the first argument, representing the application of the block to
858
- # the previous arguments.
849
+ # Yield each pair of arguments to the block, which should
850
+ # return an object representing the SQL expression for those
851
+ # two arguments. For more than two arguments, the first
852
+ # argument to the block will be result of the previous block call.
859
853
  def complex_expression_arg_pairs(args)
860
854
  case args.length
861
855
  when 1
862
- literal(args.at(0))
856
+ args.at(0)
863
857
  when 2
864
858
  yield args.at(0), args.at(1)
865
859
  else
866
- args.inject{|m, a| LiteralString.new(yield(m, a))}
860
+ args.inject{|m, a| yield(m, a)}
861
+ end
862
+ end
863
+
864
+ # Append the literalization of the args using complex_expression_arg_pairs
865
+ # to the given SQL string, used when database operator/function is 2-ary
866
+ # where Sequel expression is N-ary.
867
+ def complex_expression_arg_pairs_append(sql, args, &block)
868
+ literal_append(sql, complex_expression_arg_pairs(args, &block))
869
+ end
870
+
871
+ # Append literalization of complex expression to SQL string, for
872
+ # operators unsupported by some databases. Used by adapters for databases
873
+ # that don't support the operators natively.
874
+ def complex_expression_emulate_append(sql, op, args)
875
+ case op
876
+ when :%
877
+ complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
878
+ when :>>
879
+ complex_expression_arg_pairs_append(sql, args){|a, b| Sequel./(a, Sequel.function(:power, 2, b))}
880
+ when :<<
881
+ complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.*(a, Sequel.function(:power, 2, b))}
882
+ when :&, :|, :^
883
+ f = BITWISE_METHOD_MAP[op]
884
+ complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(f, a, b)}
885
+ when :'B~'
886
+ sql << BITCOMP_OPEN
887
+ literal_append(sql, args.at(0))
888
+ sql << BITCOMP_CLOSE
867
889
  end
868
890
  end
869
891
 
870
- # The SQL to use for the dataset used in a UNION/INTERSECT/EXCEPT clause.
892
+ # Append literalization of dataset used in UNION/INTERSECT/EXCEPT clause to SQL string.
871
893
  def compound_dataset_sql_append(sql, ds)
872
894
  subselect_sql_append(sql, ds)
873
895
  end
@@ -891,8 +913,7 @@ module Sequel
891
913
  sql << DELETE
892
914
  end
893
915
 
894
- # Converts an array of expressions into a comma separated string of
895
- # expressions.
916
+ # Append literalization of array of expressions to SQL string.
896
917
  def expression_list_append(sql, columns)
897
918
  c = false
898
919
  co = COMMA
@@ -941,8 +962,8 @@ module Sequel
941
962
  sprintf(FORMAT_TIMESTAMP_USEC, usec)
942
963
  end
943
964
 
944
- # Append the value, but special case regular (non-literal, non-blob) strings
945
- # so that they are considered as identifiers and not SQL strings.
965
+ # Append literalization of identifier to SQL string, considering regular strings
966
+ # as SQL identifiers instead of SQL strings.
946
967
  def identifier_append(sql, v)
947
968
  if v.is_a?(String)
948
969
  case v
@@ -958,7 +979,7 @@ module Sequel
958
979
  end
959
980
  end
960
981
 
961
- # Append all identifiers in args interspersed by commas.
982
+ # Append literalization of array of identifiers to SQL string.
962
983
  def identifier_list_append(sql, args)
963
984
  c = false
964
985
  comma = COMMA
@@ -975,7 +996,6 @@ module Sequel
975
996
  (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
976
997
  end
977
998
 
978
- # SQL fragment specifying the table to insert INTO
979
999
  def insert_into_sql(sql)
980
1000
  sql << INTO
981
1001
  source_list_append(sql, @opts[:from])
@@ -986,7 +1006,6 @@ module Sequel
986
1006
  INSERT_CLAUSE_METHODS
987
1007
  end
988
1008
 
989
- # SQL fragment specifying the columns to insert into
990
1009
  def insert_columns_sql(sql)
991
1010
  columns = opts[:columns]
992
1011
  if columns && !columns.empty?
@@ -1000,7 +1019,6 @@ module Sequel
1000
1019
  sql << INSERT
1001
1020
  end
1002
1021
 
1003
- # SQL fragment specifying the values to insert.
1004
1022
  def insert_values_sql(sql)
1005
1023
  case values = opts[:values]
1006
1024
  when Array
@@ -1020,7 +1038,6 @@ module Sequel
1020
1038
  end
1021
1039
  end
1022
1040
 
1023
- # SQL fragment specifying the values to return.
1024
1041
  def insert_returning_sql(sql)
1025
1042
  if opts.has_key?(:returning)
1026
1043
  sql << RETURNING
@@ -1041,7 +1058,8 @@ module Sequel
1041
1058
  (opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
1042
1059
  end
1043
1060
 
1044
- # SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
1061
+ # Append a literalization of the array to SQL string.
1062
+ # Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
1045
1063
  def literal_array_append(sql, v)
1046
1064
  if Sequel.condition_specifier?(v)
1047
1065
  literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
@@ -1056,12 +1074,12 @@ module Sequel
1056
1074
  v.nan? || v.infinite? ? "'#{d}'" : d
1057
1075
  end
1058
1076
 
1059
- # SQL fragment for SQL::Blob
1077
+ # Append literalization of SQL::Blob to SQL string.
1060
1078
  def literal_blob_append(sql, v)
1061
1079
  literal_string_append(sql, v)
1062
1080
  end
1063
1081
 
1064
- # SQL fragment for Dataset. Does a subselect inside parantheses.
1082
+ # Append literalization of dataset to SQL string. Does a subselect inside parantheses.
1065
1083
  def literal_dataset_append(sql, v)
1066
1084
  sql << LATERAL if v.opts[:lateral]
1067
1085
  sql << PAREN_OPEN
@@ -1083,7 +1101,12 @@ module Sequel
1083
1101
  format_timestamp(v)
1084
1102
  end
1085
1103
 
1086
- # SQL fragment for SQL::Expression, result depends on the specific type of expression.
1104
+ # Append literalization of DateTime to SQL string.
1105
+ def literal_datetime_append(sql, v)
1106
+ sql << literal_datetime(v)
1107
+ end
1108
+
1109
+ # Append literalization of SQL::Expression to SQL string.
1087
1110
  def literal_expression_append(sql, v)
1088
1111
  v.to_s_append(self, sql)
1089
1112
  end
@@ -1098,7 +1121,7 @@ module Sequel
1098
1121
  v.to_s
1099
1122
  end
1100
1123
 
1101
- # SQL fragment for Hash, treated as an expression
1124
+ # Append literalization of Hash to SQL string, treating hash as a boolean expression.
1102
1125
  def literal_hash_append(sql, v)
1103
1126
  literal_expression_append(sql, SQL::BooleanExpression.from_value_pairs(v))
1104
1127
  end
@@ -1113,11 +1136,9 @@ module Sequel
1113
1136
  NULL
1114
1137
  end
1115
1138
 
1116
- # SQL fragment for a type of object not handled by Dataset#literal.
1117
- # Calls +sql_literal+ if object responds to it, otherwise raises an error.
1118
- # Classes implementing +sql_literal+ should call a class-specific method on the dataset
1119
- # provided and should add that method to Sequel::Dataset, allowing for adapters
1120
- # to provide customized literalizations.
1139
+ # Append a literalization of the object to the given SQL string.
1140
+ # Calls +sql_literal_append+ if object responds to it, otherwise
1141
+ # calls +sql_literal+ if object responds to it, otherwise raises an error.
1121
1142
  # If a database specific type is allowed, this should be overriden in a subclass.
1122
1143
  def literal_other_append(sql, v)
1123
1144
  if v.respond_to?(:sql_literal_append)
@@ -1134,19 +1155,17 @@ module Sequel
1134
1155
  v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
1135
1156
  end
1136
1157
 
1137
- # SQL fragment for String. Doubles \ and ' by default.
1158
+ # Append literalization of Sequel::SQLTime to SQL string.
1159
+ def literal_sqltime_append(sql, v)
1160
+ sql << literal_sqltime(v)
1161
+ end
1162
+
1163
+ # Append literalization of string to SQL string.
1138
1164
  def literal_string_append(sql, v)
1139
1165
  sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
1140
1166
  end
1141
1167
 
1142
- # Converts a symbol into a column name. This method supports underscore
1143
- # notation in order to express qualified (two underscores) and aliased
1144
- # (three underscores) columns:
1145
- #
1146
- # dataset.literal(:abc) #=> "abc"
1147
- # dataset.literal(:abc___a) #=> "abc AS a"
1148
- # dataset.literal(:items__abc) #=> "items.abc"
1149
- # dataset.literal(:items__abc___a) #=> "items.abc AS a"
1168
+ # Append literalization of symbol to SQL string.
1150
1169
  def literal_symbol_append(sql, v)
1151
1170
  c_table, column, c_alias = split_symbol(v)
1152
1171
  if c_table
@@ -1162,6 +1181,11 @@ module Sequel
1162
1181
  format_timestamp(v)
1163
1182
  end
1164
1183
 
1184
+ # Append literalization of Time to SQL string.
1185
+ def literal_time_append(sql, v)
1186
+ sql << literal_time(v)
1187
+ end
1188
+
1165
1189
  # SQL fragment for true
1166
1190
  def literal_true
1167
1191
  BOOL_TRUE
@@ -1203,13 +1227,11 @@ module Sequel
1203
1227
  SELECT_CLAUSE_METHODS
1204
1228
  end
1205
1229
 
1206
- # Modify the sql to add the columns selected
1207
1230
  def select_columns_sql(sql)
1208
1231
  sql << SPACE
1209
1232
  column_list_append(sql, @opts[:select])
1210
1233
  end
1211
1234
 
1212
- # Modify the sql to add the DISTINCT modifier
1213
1235
  def select_distinct_sql(sql)
1214
1236
  if distinct = @opts[:distinct]
1215
1237
  sql << DISTINCT
@@ -1234,7 +1256,6 @@ module Sequel
1234
1256
  end
1235
1257
  end
1236
1258
 
1237
- # Modify the sql to add the list of tables to select FROM
1238
1259
  def select_from_sql(sql)
1239
1260
  if f = @opts[:from]
1240
1261
  sql << FROM
@@ -1243,7 +1264,6 @@ module Sequel
1243
1264
  end
1244
1265
  alias delete_from_sql select_from_sql
1245
1266
 
1246
- # Modify the sql to add the expressions to GROUP BY
1247
1267
  def select_group_sql(sql)
1248
1268
  if group = @opts[:group]
1249
1269
  sql << GROUP_BY
@@ -1262,7 +1282,6 @@ module Sequel
1262
1282
  end
1263
1283
  end
1264
1284
 
1265
- # Modify the sql to add the filter criteria in the HAVING clause
1266
1285
  def select_having_sql(sql)
1267
1286
  if having = @opts[:having]
1268
1287
  sql << HAVING
@@ -1270,14 +1289,12 @@ module Sequel
1270
1289
  end
1271
1290
  end
1272
1291
 
1273
- # Modify the sql to add the list of tables to JOIN to
1274
1292
  def select_join_sql(sql)
1275
1293
  if js = @opts[:join]
1276
1294
  js.each{|j| literal_append(sql, j)}
1277
1295
  end
1278
1296
  end
1279
1297
 
1280
- # Modify the sql to limit the number of rows returned and offset
1281
1298
  def select_limit_sql(sql)
1282
1299
  if l = @opts[:limit]
1283
1300
  sql << LIMIT
@@ -1289,7 +1306,6 @@ module Sequel
1289
1306
  end
1290
1307
  end
1291
1308
 
1292
- # Modify the sql to support the different types of locking modes.
1293
1309
  def select_lock_sql(sql)
1294
1310
  case l = @opts[:lock]
1295
1311
  when :update
@@ -1299,7 +1315,6 @@ module Sequel
1299
1315
  end
1300
1316
  end
1301
1317
 
1302
- # Modify the sql to add the expressions to ORDER BY
1303
1318
  def select_order_sql(sql)
1304
1319
  if o = @opts[:order]
1305
1320
  sql << ORDER_BY
@@ -1313,7 +1328,6 @@ module Sequel
1313
1328
  sql << SELECT
1314
1329
  end
1315
1330
 
1316
- # Modify the sql to add the filter criteria in the WHERE clause
1317
1331
  def select_where_sql(sql)
1318
1332
  if w = @opts[:where]
1319
1333
  sql << WHERE
@@ -1323,7 +1337,6 @@ module Sequel
1323
1337
  alias delete_where_sql select_where_sql
1324
1338
  alias update_where_sql select_where_sql
1325
1339
 
1326
- # SQL Fragment specifying the WITH clause
1327
1340
  def select_with_sql(sql)
1328
1341
  ws = opts[:with]
1329
1342
  return if !ws || ws.empty?
@@ -1353,7 +1366,8 @@ module Sequel
1353
1366
  SQL_WITH
1354
1367
  end
1355
1368
 
1356
- # Converts an array of source names into into a comma separated list.
1369
+ # Append literalization of array of sources/tables to SQL string, raising an Error if there
1370
+ # are no sources.
1357
1371
  def source_list_append(sql, sources)
1358
1372
  raise(Error, 'No source specified for query') if sources.nil? || sources == []
1359
1373
  identifier_list_append(sql, sources)
@@ -1372,7 +1386,8 @@ module Sequel
1372
1386
 
1373
1387
  # SQL to use if this dataset uses static SQL. Since static SQL
1374
1388
  # can be a PlaceholderLiteralString in addition to a String,
1375
- # we literalize nonstrings.
1389
+ # we literalize nonstrings. If there is an append_sql for this
1390
+ # dataset, append to that SQL instead of returning the value.
1376
1391
  def static_sql(sql)
1377
1392
  if append_sql = @opts[:append_sql]
1378
1393
  if sql.is_a?(String)
@@ -1389,7 +1404,7 @@ module Sequel
1389
1404
  end
1390
1405
  end
1391
1406
 
1392
- # SQL fragment for a subselect using the given database's SQL.
1407
+ # Append literalization of the subselect to SQL String.
1393
1408
  def subselect_sql_append(sql, ds)
1394
1409
  ds.clone(:append_sql=>sql).sql
1395
1410
  end
@@ -1399,15 +1414,12 @@ module Sequel
1399
1414
  UPDATE_CLAUSE_METHODS
1400
1415
  end
1401
1416
 
1402
- # SQL fragment specifying the tables from with to delete.
1403
- # Includes join table if modifying joins is allowed.
1404
1417
  def update_table_sql(sql)
1405
1418
  sql << SPACE
1406
1419
  source_list_append(sql, @opts[:from])
1407
1420
  select_join_sql(sql) if supports_modifying_joins?
1408
1421
  end
1409
1422
 
1410
- # The SQL fragment specifying the columns and values to SET.
1411
1423
  def update_set_sql(sql)
1412
1424
  values = opts[:values]
1413
1425
  sql << SET