sequel 2.10.0 → 2.11.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 (78) hide show
  1. data/CHANGELOG +51 -1
  2. data/README.rdoc +2 -2
  3. data/Rakefile +2 -2
  4. data/doc/advanced_associations.rdoc +6 -18
  5. data/doc/release_notes/1.0.txt +38 -0
  6. data/doc/release_notes/1.1.txt +143 -0
  7. data/doc/release_notes/1.3.txt +101 -0
  8. data/doc/release_notes/1.4.0.txt +53 -0
  9. data/doc/release_notes/1.5.0.txt +155 -0
  10. data/doc/release_notes/2.0.0.txt +298 -0
  11. data/doc/release_notes/2.1.0.txt +271 -0
  12. data/doc/release_notes/2.10.0.txt +328 -0
  13. data/doc/release_notes/2.11.0.txt +215 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/lib/sequel_core/adapters/ado.rb +3 -0
  23. data/lib/sequel_core/adapters/db2.rb +0 -11
  24. data/lib/sequel_core/adapters/dbi.rb +0 -11
  25. data/lib/sequel_core/adapters/do.rb +0 -12
  26. data/lib/sequel_core/adapters/firebird.rb +21 -16
  27. data/lib/sequel_core/adapters/informix.rb +1 -11
  28. data/lib/sequel_core/adapters/jdbc.rb +1 -13
  29. data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
  30. data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -17
  31. data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
  32. data/lib/sequel_core/adapters/mysql.rb +31 -27
  33. data/lib/sequel_core/adapters/odbc.rb +34 -28
  34. data/lib/sequel_core/adapters/openbase.rb +0 -11
  35. data/lib/sequel_core/adapters/oracle.rb +11 -9
  36. data/lib/sequel_core/adapters/postgres.rb +14 -17
  37. data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
  38. data/lib/sequel_core/adapters/shared/mysql.rb +29 -14
  39. data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
  40. data/lib/sequel_core/adapters/shared/postgres.rb +30 -35
  41. data/lib/sequel_core/adapters/shared/progress.rb +4 -0
  42. data/lib/sequel_core/adapters/shared/sqlite.rb +73 -13
  43. data/lib/sequel_core/adapters/sqlite.rb +8 -18
  44. data/lib/sequel_core/adapters/utils/date_format.rb +21 -0
  45. data/lib/sequel_core/{dataset → adapters/utils}/stored_procedures.rb +0 -0
  46. data/lib/sequel_core/{dataset → adapters/utils}/unsupported.rb +0 -0
  47. data/lib/sequel_core/core_ext.rb +1 -1
  48. data/lib/sequel_core/core_sql.rb +9 -4
  49. data/lib/sequel_core/database.rb +63 -62
  50. data/lib/sequel_core/dataset.rb +9 -4
  51. data/lib/sequel_core/dataset/convenience.rb +10 -9
  52. data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel_core/dataset/sql.rb +130 -36
  54. data/lib/sequel_core/schema/sql.rb +2 -2
  55. data/lib/sequel_core/sql.rb +44 -51
  56. data/lib/sequel_core/version.rb +1 -1
  57. data/lib/sequel_model/associations.rb +25 -17
  58. data/lib/sequel_model/base.rb +35 -7
  59. data/lib/sequel_model/caching.rb +1 -6
  60. data/lib/sequel_model/record.rb +23 -5
  61. data/lib/sequel_model/validations.rb +20 -5
  62. data/spec/adapters/firebird_spec.rb +6 -1
  63. data/spec/adapters/mysql_spec.rb +12 -0
  64. data/spec/adapters/postgres_spec.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +81 -2
  66. data/spec/integration/dataset_test.rb +2 -2
  67. data/spec/integration/type_test.rb +12 -2
  68. data/spec/sequel_core/core_sql_spec.rb +46 -12
  69. data/spec/sequel_core/database_spec.rb +24 -12
  70. data/spec/sequel_core/dataset_spec.rb +82 -32
  71. data/spec/sequel_core/schema_spec.rb +16 -0
  72. data/spec/sequel_model/associations_spec.rb +89 -0
  73. data/spec/sequel_model/base_spec.rb +66 -0
  74. data/spec/sequel_model/eager_loading_spec.rb +32 -0
  75. data/spec/sequel_model/record_spec.rb +9 -9
  76. data/spec/sequel_model/spec_helper.rb +3 -0
  77. data/spec/sequel_model/validations_spec.rb +63 -3
  78. metadata +41 -4
@@ -52,6 +52,9 @@ module Sequel
52
52
  db.translator.add_translator("real", &prok)
53
53
  db.translator.add_translator("double precision", &prok)
54
54
 
55
+ # Handle blob values with Sequel::SQL::Blob
56
+ db.translator.add_translator("blob"){|t,v| ::Sequel::SQL::Blob.new(v)}
57
+
55
58
  db
56
59
  end
57
60
 
@@ -156,7 +159,7 @@ module Sequel
156
159
  # SQLite uses a : before the name of the argument for named
157
160
  # arguments.
158
161
  def prepared_arg(k)
159
- "#{prepared_arg_placeholder}#{k}".lit
162
+ LiteralString.new("#{prepared_arg_placeholder}#{k}")
160
163
  end
161
164
  end
162
165
 
@@ -211,23 +214,6 @@ module Sequel
211
214
  end
212
215
  end
213
216
 
214
- # Use the ISO format for dates and timestamps, and quote strings
215
- # using the ::SQLite3::Database.quote method.
216
- def literal(v)
217
- case v
218
- when LiteralString
219
- v
220
- when String
221
- "'#{::SQLite3::Database.quote(v)}'"
222
- when Time
223
- literal(v.iso8601)
224
- when Date, DateTime
225
- literal(v.to_s)
226
- else
227
- super
228
- end
229
- end
230
-
231
217
  # Prepare the given type of query with the given name and store
232
218
  # it in the database. Note that a new native prepared statement is
233
219
  # created on each call to this prepared statement.
@@ -240,6 +226,10 @@ module Sequel
240
226
 
241
227
  private
242
228
 
229
+ def literal_string(v)
230
+ "'#{::SQLite3::Database.quote(v)}'"
231
+ end
232
+
243
233
  # SQLite uses a : before the name of the argument as a placeholder.
244
234
  def prepared_arg_placeholder
245
235
  PREPARED_ARG_PLACEHOLDER
@@ -0,0 +1,21 @@
1
+ # Module containing overrides for Sequel's standard date/time literalization
2
+ # to use the SQL standrd. The SQL standard is used by fewer databases than
3
+ # the defacto standard (which is just a normal string).
4
+ module Sequel::Dataset::SQLStandardDateFormat
5
+ private
6
+
7
+ # Use SQL standard syntax for Date
8
+ def literal_date(v)
9
+ v.strftime("DATE '%Y-%m-%d'")
10
+ end
11
+
12
+ # Use SQL standard syntax for DateTime
13
+ def literal_datetime(v)
14
+ v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
15
+ end
16
+
17
+ # Use SQL standard syntax for Time
18
+ def literal_time(v)
19
+ v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
20
+ end
21
+ end
@@ -56,7 +56,7 @@ class Module
56
56
  # same name, caching the result in an instance variable. Define
57
57
  # standard attr_writer method for modifying that instance variable
58
58
  def class_attr_overridable(*meths)
59
- meths.each{|meth| class_eval("def #{meth}; @#{meth}.nil? ? (@#{meth} = self.class.#{meth}) : @#{meth} end")}
59
+ meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (@#{meth} = self.class.#{meth}) : @#{meth} end")}
60
60
  attr_writer(*meths)
61
61
  public(*meths)
62
62
  public(*meths.collect{|m|"#{m}="})
@@ -121,7 +121,7 @@ class String
121
121
  include Sequel::SQL::AliasMethods
122
122
  include Sequel::SQL::CastMethods
123
123
 
124
- # Converts a string into an LiteralString, in order to override string
124
+ # Converts a string into a Sequel::LiteralString, in order to override string
125
125
  # literalization, e.g.:
126
126
  #
127
127
  # DB[:items].filter(:abc => 'def').sql #=>
@@ -130,8 +130,12 @@ class String
130
130
  # DB[:items].filter(:abc => 'def'.lit).sql #=>
131
131
  # "SELECT * FROM items WHERE (abc = def)"
132
132
  #
133
- def lit
134
- Sequel::LiteralString.new(self)
133
+ # You can also provide arguments, to create a Sequel::SQL::PlaceholderLiteralString:
134
+ #
135
+ # DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}.sql #=>
136
+ # "SELECT count(DISTINCT a) FROM items"
137
+ def lit(*args)
138
+ args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
135
139
  end
136
140
  alias_method :expr, :lit
137
141
 
@@ -149,9 +153,10 @@ class String
149
153
 
150
154
  # Returns a Blob that holds the same data as this string. Blobs provide proper
151
155
  # escaping of binary data.
152
- def to_blob
156
+ def to_sequel_blob
153
157
  ::Sequel::SQL::Blob.new self
154
158
  end
159
+ alias to_blob to_sequel_blob
155
160
  end
156
161
 
157
162
  class Symbol
@@ -75,6 +75,9 @@ module Sequel
75
75
  @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
76
76
  @prepared_statements = {}
77
77
  @transactions = []
78
+ @identifier_input_method = nil
79
+ @identifier_output_method = nil
80
+ @quote_identifiers = nil
78
81
  if opts.include?(:upcase_identifiers)
79
82
  @identifier_input_method = opts[:upcase_identifiers] ? :upcase : ""
80
83
  end
@@ -492,73 +495,71 @@ module Sequel
492
495
  # is invalid.
493
496
  def typecast_value(column_type, value)
494
497
  return nil if value.nil?
495
- case column_type
496
- when :integer
497
- begin
498
+ begin
499
+ case column_type
500
+ when :integer
498
501
  Integer(value)
499
- rescue ArgumentError => e
500
- raise Sequel::Error::InvalidValue, e.message.inspect
501
- end
502
- when :string
503
- value.to_s
504
- when :float
505
- begin
502
+ when :string
503
+ value.to_s
504
+ when :float
506
505
  Float(value)
507
- rescue ArgumentError => e
508
- raise Sequel::Error::InvalidValue, e.message.inspect
509
- end
510
- when :decimal
511
- case value
512
- when BigDecimal
513
- value
514
- when String, Float
515
- value.to_d
516
- when Integer
517
- value.to_s.to_d
518
- else
519
- raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
520
- end
521
- when :boolean
522
- case value
523
- when false, 0, "0", /\Af(alse)?\z/i
524
- false
525
- else
526
- value.blank? ? nil : true
527
- end
528
- when :date
529
- case value
530
- when Date
531
- value
532
- when DateTime, Time
533
- Date.new(value.year, value.month, value.day)
534
- when String
535
- value.to_date
536
- else
537
- raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
538
- end
539
- when :time
540
- case value
541
- when Time
542
- value
543
- when String
544
- value.to_time
506
+ when :decimal
507
+ case value
508
+ when BigDecimal
509
+ value
510
+ when String, Float
511
+ value.to_d
512
+ when Integer
513
+ value.to_s.to_d
514
+ else
515
+ raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
516
+ end
517
+ when :boolean
518
+ case value
519
+ when false, 0, "0", /\Af(alse)?\z/i
520
+ false
521
+ else
522
+ value.blank? ? nil : true
523
+ end
524
+ when :date
525
+ case value
526
+ when Date
527
+ value
528
+ when DateTime, Time
529
+ Date.new(value.year, value.month, value.day)
530
+ when String
531
+ value.to_date
532
+ else
533
+ raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
534
+ end
535
+ when :time
536
+ case value
537
+ when Time
538
+ value
539
+ when String
540
+ value.to_time
541
+ else
542
+ raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
543
+ end
544
+ when :datetime
545
+ raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
546
+ if Sequel.datetime_class === value
547
+ # Already the correct class, no need to convert
548
+ value
549
+ else
550
+ # First convert it to standard ISO 8601 time, then
551
+ # parse that string using the time class.
552
+ (Time === value ? value.iso8601 : value.to_s).to_sequel_time
553
+ end
554
+ when :blob
555
+ ::Sequel::SQL::Blob.new(value)
545
556
  else
546
- raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
547
- end
548
- when :datetime
549
- raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
550
- if Sequel.datetime_class === value
551
- # Already the correct class, no need to convert
552
557
  value
553
- else
554
- # First convert it to standard ISO 8601 time, then
555
- # parse that string using the time class.
556
- (Time === value ? value.iso8601 : value.to_s).to_sequel_time
557
558
  end
558
- when :blob
559
- value.to_blob
560
- else
561
- value
559
+ rescue ArgumentError => exp
560
+ e = Sequel::Error::InvalidValue.new("#{exp.class} #{exp.message}")
561
+ e.set_backtrace(exp.backtrace)
562
+ raise e
562
563
  end
563
564
  end
564
565
 
@@ -1,4 +1,4 @@
1
- %w'callback convenience pagination prepared_statements query schema sql unsupported'.each do |f|
1
+ %w'callback convenience pagination prepared_statements query schema sql'.each do |f|
2
2
  require "sequel_core/dataset/#{f}"
3
3
  end
4
4
 
@@ -54,7 +54,7 @@ module Sequel
54
54
  left_outer_join limit naked or order order_by order_more paginate query reject
55
55
  reverse reverse_order right_outer_join select select_all select_more
56
56
  set_defaults set_graph_aliases set_model set_overrides sort sort_by
57
- unfiltered union unordered where'.collect{|x| x.to_sym}
57
+ unfiltered union unordered where with_sql'.collect{|x| x.to_sym}
58
58
 
59
59
  NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
60
60
  STOCK_TRANSFORMS = {
@@ -467,6 +467,11 @@ module Sequel
467
467
  end
468
468
  end
469
469
 
470
+ # Set the server to use to :default unless it is already set in the passed opts
471
+ def default_server_opts(opts)
472
+ {:server=>@opts[:server] || :default}.merge(opts)
473
+ end
474
+
470
475
  # Execute the given SQL on the database using execute.
471
476
  def execute(sql, opts={}, &block)
472
477
  @db.execute(sql, {:server=>@opts[:server] || :read_only}.merge(opts), &block)
@@ -474,12 +479,12 @@ module Sequel
474
479
 
475
480
  # Execute the given SQL on the database using execute_dui.
476
481
  def execute_dui(sql, opts={}, &block)
477
- @db.execute_dui(sql, {:server=>@opts[:server] || :default}.merge(opts), &block)
482
+ @db.execute_dui(sql, default_server_opts(opts), &block)
478
483
  end
479
484
 
480
485
  # Execute the given SQL on the database using execute_insert.
481
486
  def execute_insert(sql, opts={}, &block)
482
- @db.execute_insert(sql, {:server=>@opts[:server] || :default}.merge(opts), &block)
487
+ @db.execute_insert(sql, default_server_opts(opts), &block)
483
488
  end
484
489
 
485
490
  # Modify the identifier returned from the database based on the
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Dataset
3
3
  COMMA_SEPARATOR = ', '.freeze
4
- COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, '*'.lit).as(:count)
4
+ COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
5
5
 
6
6
  # Returns the first record matching the conditions.
7
7
  def [](*conditions)
@@ -16,7 +16,7 @@ module Sequel
16
16
 
17
17
  # Returns the average value for the given column.
18
18
  def avg(column)
19
- get(SQL::Function.new(:avg, column))
19
+ get{|o| o.avg(column)}
20
20
  end
21
21
 
22
22
  # Returns true if no records exists in the dataset
@@ -61,8 +61,9 @@ module Sequel
61
61
  end
62
62
 
63
63
  # Return the column value for the first matching record in the dataset.
64
- def get(column)
65
- select(column).single_value
64
+ def get(column=nil, &block)
65
+ raise(Error, 'must provide argument or block to Dataset#get, not both') if column && block
66
+ (column ? select(column) : select(&block)).single_value
66
67
  end
67
68
 
68
69
  # Returns a dataset grouped by the given column with count by group.
@@ -73,7 +74,7 @@ module Sequel
73
74
  # Returns the interval between minimum and maximum values for the given
74
75
  # column.
75
76
  def interval(column)
76
- get("(max(#{literal(column)}) - min(#{literal(column)}))".lit)
77
+ get{|o| o.max(column) - o.min(column)}
77
78
  end
78
79
 
79
80
  # Reverses the order and then runs first. Note that this
@@ -97,12 +98,12 @@ module Sequel
97
98
 
98
99
  # Returns the maximum value for the given column.
99
100
  def max(column)
100
- get(SQL::Function.new(:max, column))
101
+ get{|o| o.max(column)}
101
102
  end
102
103
 
103
104
  # Returns the minimum value for the given column.
104
105
  def min(column)
105
- get(SQL::Function.new(:min, column))
106
+ get{|o| o.min(column)}
106
107
  end
107
108
 
108
109
  # Inserts multiple records into the associated table. This method can be
@@ -170,7 +171,7 @@ module Sequel
170
171
  # Returns a Range object made from the minimum and maximum values for the
171
172
  # given column.
172
173
  def range(column)
173
- if r = select(SQL::Function.new(:min, column).as(:v1), SQL::Function.new(:max, column).as(:v2)).first
174
+ if r = select{|o| [o.min(column).as(:v1), o.max(column).as(:v2)]}.first
174
175
  (r[:v1]..r[:v2])
175
176
  end
176
177
  end
@@ -191,7 +192,7 @@ module Sequel
191
192
 
192
193
  # Returns the sum for the given column.
193
194
  def sum(column)
194
- get(SQL::Function.new(:sum, column))
195
+ get{|o| o.sum(column)}
195
196
  end
196
197
 
197
198
  # Returns true if the table exists. Will raise an error
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  class Dataset
3
- PREPARED_ARG_PLACEHOLDER = '?'.lit.freeze
3
+ PREPARED_ARG_PLACEHOLDER = LiteralString.new('?').freeze
4
4
 
5
5
  # Default implementation of the argument mapper to allow
6
6
  # native database support for bind variables and prepared
@@ -7,13 +7,11 @@ module Sequel
7
7
  COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
8
8
  COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
9
9
  COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
10
- DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
11
10
  N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
12
11
  NULL = "NULL".freeze
13
12
  QUESTION_MARK = '?'.freeze
14
- STOCK_COUNT_OPTS = {:select => ["COUNT(*)".lit], :order => nil}.freeze
13
+ STOCK_COUNT_OPTS = {:select => [LiteralString.new("COUNT(*)").freeze], :order => nil}.freeze
15
14
  SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
16
- TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
17
15
  TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
18
16
  WILDCARD = '*'.freeze
19
17
 
@@ -45,6 +43,11 @@ module Sequel
45
43
  sql << "ELSE #{literal(ce.default)} END)"
46
44
  end
47
45
 
46
+ # SQL fragment for the SQL CAST expression.
47
+ def cast_sql(expr, type)
48
+ "CAST(#{literal(expr)} AS #{db.send(:type_literal_base, :type=>type)})"
49
+ end
50
+
48
51
  # SQL fragment for specifying all columns in a given table.
49
52
  def column_all_sql(ca)
50
53
  "#{quote_schema_table(ca.table)}.*"
@@ -126,7 +129,7 @@ module Sequel
126
129
  # DB.select(1).where(DB[:items].exists).sql
127
130
  # #=> "SELECT 1 WHERE EXISTS (SELECT * FROM items)"
128
131
  def exists(opts = nil)
129
- "EXISTS (#{select_sql(opts)})".lit
132
+ LiteralString.new("EXISTS (#{select_sql(opts)})")
130
133
  end
131
134
 
132
135
  # Returns a copy of the dataset with the given conditions imposed upon it.
@@ -184,8 +187,7 @@ module Sequel
184
187
  alias_method :where, :filter
185
188
 
186
189
  # The first source (primary table) for this dataset. If the dataset doesn't
187
- # have a table, raises an error. If the table is aliased, returns the actual
188
- # table name, not the alias.
190
+ # have a table, raises an error. If the table is aliased, returns the aliased name.
189
191
  def first_source
190
192
  source = @opts[:from]
191
193
  if source.nil? || source.empty?
@@ -464,38 +466,39 @@ module Sequel
464
466
  # If an unsupported object is given, an exception is raised.
465
467
  def literal(v)
466
468
  case v
467
- when LiteralString
468
- v
469
469
  when String
470
- "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
471
- when Integer, Float
472
- v.to_s
470
+ return v if v.is_a?(LiteralString)
471
+ v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
472
+ when Symbol
473
+ literal_symbol(v)
474
+ when Integer
475
+ literal_integer(v)
476
+ when Hash
477
+ literal_hash(v)
478
+ when SQL::Expression
479
+ literal_expression(v)
480
+ when Float
481
+ literal_float(v)
473
482
  when BigDecimal
474
- d = v.to_s("F")
475
- d = "'#{d}'" if v.nan? || v.infinite?
476
- d
483
+ literal_big_decimal(v)
477
484
  when NilClass
478
485
  NULL
479
486
  when TrueClass
480
- BOOL_TRUE
487
+ literal_true
481
488
  when FalseClass
482
- BOOL_FALSE
483
- when Symbol
484
- symbol_to_column_ref(v)
485
- when ::Sequel::SQL::Expression
486
- v.to_s(self)
489
+ literal_false
487
490
  when Array
488
- v.all_two_pairs? ? literal(v.sql_expr) : array_sql(v)
489
- when Hash
490
- literal(v.sql_expr)
491
- when Time, DateTime
492
- v.strftime(TIMESTAMP_FORMAT)
491
+ literal_array(v)
492
+ when Time
493
+ literal_time(v)
494
+ when DateTime
495
+ literal_datetime(v)
493
496
  when Date
494
- v.strftime(DATE_FORMAT)
497
+ literal_date(v)
495
498
  when Dataset
496
- "(#{subselect_sql(v)})"
499
+ literal_dataset(v)
497
500
  else
498
- raise Error, "can't express #{v.inspect} as a SQL literal"
501
+ literal_other(v)
499
502
  end
500
503
  end
501
504
 
@@ -537,15 +540,17 @@ module Sequel
537
540
  # ds.order(:name.asc).sql #=> 'SELECT * FROM items ORDER BY name ASC'
538
541
  # ds.order(:arr|1).sql #=> 'SELECT * FROM items ORDER BY arr[1]'
539
542
  # ds.order(nil).sql #=> 'SELECT * FROM items'
540
- def order(*order)
541
- clone(:order => (order.compact.empty?) ? nil : order)
543
+ def order(*columns)
544
+ columns += Array((yield SQL::VirtualRow.new)) if block_given?
545
+ clone(:order => (columns.compact.empty?) ? nil : columns)
542
546
  end
543
547
  alias_method :order_by, :order
544
548
 
545
549
  # Returns a copy of the dataset with the order columns added
546
550
  # to the existing order.
547
- def order_more(*order)
548
- order(*((@opts[:order] || []) + order))
551
+ def order_more(*columns)
552
+ columns += Array((yield SQL::VirtualRow.new)) if block_given?
553
+ order(*((@opts[:order] || []) + columns))
549
554
  end
550
555
 
551
556
  # SQL fragment for the ordered expression, used in the ORDER BY
@@ -621,6 +626,7 @@ module Sequel
621
626
  # Returns a copy of the dataset with the columns selected changed
622
627
  # to the given columns.
623
628
  def select(*columns)
629
+ columns += Array((yield SQL::VirtualRow.new)) if block_given?
624
630
  clone(:select => columns)
625
631
  end
626
632
 
@@ -632,6 +638,7 @@ module Sequel
632
638
  # Returns a copy of the dataset with the given columns added
633
639
  # to the existing selected columns.
634
640
  def select_more(*columns)
641
+ columns += Array((yield SQL::VirtualRow.new)) if block_given?
635
642
  select(*((@opts[:select] || []) + columns))
636
643
  end
637
644
 
@@ -666,9 +673,7 @@ module Sequel
666
673
  # :items__abc___a.to_column_ref(ds) #=> "items.abc AS a"
667
674
  #
668
675
  def symbol_to_column_ref(sym)
669
- c_table, column, c_alias = split_symbol(sym)
670
- qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
671
- c_alias ? as_sql(qc, c_alias) : qc
676
+ literal_symbol(sym)
672
677
  end
673
678
 
674
679
  # Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
@@ -737,6 +742,12 @@ module Sequel
737
742
  sql
738
743
  end
739
744
 
745
+ # Returns a copy of the dataset with the static SQL used. This is useful if you want
746
+ # to keep the same row_proc/transform/graph, but change the SQL used to custom SQL.
747
+ def with_sql(sql)
748
+ clone(:sql=>sql)
749
+ end
750
+
740
751
  [:inner, :full_outer, :right_outer, :left_outer].each do |jtype|
741
752
  class_eval("def #{jtype}_join(*args, &block); join_table(:#{jtype}, *args, &block) end")
742
753
  end
@@ -808,7 +819,7 @@ module Sequel
808
819
  when TrueClass, FalseClass
809
820
  SQL::BooleanExpression.new(:NOOP, expr)
810
821
  when String
811
- "(#{expr})".lit
822
+ LiteralString.new("(#{expr})")
812
823
  else
813
824
  raise(Error, 'Invalid filter argument')
814
825
  end
@@ -849,6 +860,89 @@ module Sequel
849
860
  "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
850
861
  end
851
862
 
863
+ # SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
864
+ def literal_array(v)
865
+ v.all_two_pairs? ? literal_expression(v.sql_expr) : array_sql(v)
866
+ end
867
+
868
+ # SQL fragment for BigDecimal
869
+ def literal_big_decimal(v)
870
+ d = v.to_s("F")
871
+ v.nan? || v.infinite? ? "'#{d}'" : d
872
+ end
873
+
874
+ # SQL fragment for SQL::Blob
875
+ def literal_blob(v)
876
+ literal_string(v)
877
+ end
878
+
879
+ # SQL fragment for Dataset. Does a subselect inside parantheses.
880
+ def literal_dataset(v)
881
+ "(#{subselect_sql(v)})"
882
+ end
883
+
884
+ # SQL fragment for Date, using the ISO8601 format.
885
+ def literal_date(v)
886
+ "'#{v}'"
887
+ end
888
+
889
+ # SQL fragment for DateTime, using the ISO8601 format.
890
+ def literal_datetime(v)
891
+ "'#{v}'"
892
+ end
893
+
894
+ # SQL fragment for SQL::Expression, result depends on the specific type of expression.
895
+ def literal_expression(v)
896
+ v.to_s(self)
897
+ end
898
+
899
+ # SQL fragment for false
900
+ def literal_false
901
+ BOOL_FALSE
902
+ end
903
+
904
+ # SQL fragment for Float
905
+ def literal_float(v)
906
+ v.to_s
907
+ end
908
+
909
+ # SQL fragment for Hash, treated as an expression
910
+ def literal_hash(v)
911
+ literal_expression(v.sql_expr)
912
+ end
913
+
914
+ # SQL fragment for Integer
915
+ def literal_integer(v)
916
+ v.to_s
917
+ end
918
+
919
+ # SQL fragmento for a type of object not handled by Dataset#literal. Raises an error. If a database specific type is allowed, this should be overriden in a subclass.
920
+ def literal_other(v)
921
+ raise Error, "can't express #{v.inspect} as a SQL literal"
922
+ end
923
+
924
+ # SQL fragment for String. Doubles \ and ' by default.
925
+ def literal_string(v)
926
+ "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
927
+ end
928
+
929
+ # SQL fragment for Symbol, treated as an identifier, possibly aliased and/or qualified.
930
+ def literal_symbol(v)
931
+ c_table, column, c_alias = split_symbol(v)
932
+ qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
933
+ c_alias ? as_sql(qc, c_alias) : qc
934
+ end
935
+
936
+ # SQL fragment for Time, uses the ISO8601 format.
937
+ def literal_time(v)
938
+ "'#{v.iso8601}'"
939
+ end
940
+
941
+ # SQL fragment for true.
942
+ def literal_true
943
+ BOOL_TRUE
944
+ end
945
+
852
946
  # Returns a qualified column name (including a table name) if the column
853
947
  # name isn't already qualified.
854
948
  def qualified_column_name(column, table)