sequel 2.10.0 → 2.11.0

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