sequel 4.8.0 → 4.9.0

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