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.
- data/CHANGELOG +51 -1
- data/README.rdoc +2 -2
- data/Rakefile +2 -2
- data/doc/advanced_associations.rdoc +6 -18
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/lib/sequel_core/adapters/ado.rb +3 -0
- data/lib/sequel_core/adapters/db2.rb +0 -11
- data/lib/sequel_core/adapters/dbi.rb +0 -11
- data/lib/sequel_core/adapters/do.rb +0 -12
- data/lib/sequel_core/adapters/firebird.rb +21 -16
- data/lib/sequel_core/adapters/informix.rb +1 -11
- data/lib/sequel_core/adapters/jdbc.rb +1 -13
- data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
- data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -17
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
- data/lib/sequel_core/adapters/mysql.rb +31 -27
- data/lib/sequel_core/adapters/odbc.rb +34 -28
- data/lib/sequel_core/adapters/openbase.rb +0 -11
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +14 -17
- data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
- data/lib/sequel_core/adapters/shared/mysql.rb +29 -14
- data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +30 -35
- data/lib/sequel_core/adapters/shared/progress.rb +4 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +73 -13
- data/lib/sequel_core/adapters/sqlite.rb +8 -18
- data/lib/sequel_core/adapters/utils/date_format.rb +21 -0
- data/lib/sequel_core/{dataset → adapters/utils}/stored_procedures.rb +0 -0
- data/lib/sequel_core/{dataset → adapters/utils}/unsupported.rb +0 -0
- data/lib/sequel_core/core_ext.rb +1 -1
- data/lib/sequel_core/core_sql.rb +9 -4
- data/lib/sequel_core/database.rb +63 -62
- data/lib/sequel_core/dataset.rb +9 -4
- data/lib/sequel_core/dataset/convenience.rb +10 -9
- data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
- data/lib/sequel_core/dataset/sql.rb +130 -36
- data/lib/sequel_core/schema/sql.rb +2 -2
- data/lib/sequel_core/sql.rb +44 -51
- data/lib/sequel_core/version.rb +1 -1
- data/lib/sequel_model/associations.rb +25 -17
- data/lib/sequel_model/base.rb +35 -7
- data/lib/sequel_model/caching.rb +1 -6
- data/lib/sequel_model/record.rb +23 -5
- data/lib/sequel_model/validations.rb +20 -5
- data/spec/adapters/firebird_spec.rb +6 -1
- data/spec/adapters/mysql_spec.rb +12 -0
- data/spec/adapters/postgres_spec.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +81 -2
- data/spec/integration/dataset_test.rb +2 -2
- data/spec/integration/type_test.rb +12 -2
- data/spec/sequel_core/core_sql_spec.rb +46 -12
- data/spec/sequel_core/database_spec.rb +24 -12
- data/spec/sequel_core/dataset_spec.rb +82 -32
- data/spec/sequel_core/schema_spec.rb +16 -0
- data/spec/sequel_model/associations_spec.rb +89 -0
- data/spec/sequel_model/base_spec.rb +66 -0
- data/spec/sequel_model/eager_loading_spec.rb +32 -0
- data/spec/sequel_model/record_spec.rb +9 -9
- data/spec/sequel_model/spec_helper.rb +3 -0
- data/spec/sequel_model/validations_spec.rb +63 -3
- 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}"
|
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
|
File without changes
|
File without changes
|
data/lib/sequel_core/core_ext.rb
CHANGED
@@ -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}
|
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}="})
|
data/lib/sequel_core/core_sql.rb
CHANGED
@@ -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
|
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
|
-
|
134
|
-
|
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
|
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
|
data/lib/sequel_core/database.rb
CHANGED
@@ -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
|
-
|
496
|
-
|
497
|
-
|
498
|
+
begin
|
499
|
+
case column_type
|
500
|
+
when :integer
|
498
501
|
Integer(value)
|
499
|
-
|
500
|
-
|
501
|
-
|
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
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
value
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
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
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
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
|
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w'callback convenience pagination prepared_statements query schema sql
|
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,
|
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,
|
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, '*'.
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
195
|
+
get{|o| o.sum(column)}
|
195
196
|
end
|
196
197
|
|
197
198
|
# Returns true if the table exists. Will raise an error
|
@@ -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(*)".
|
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)})"
|
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
|
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
|
-
|
471
|
-
|
472
|
-
|
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
|
-
|
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
|
-
|
487
|
+
literal_true
|
481
488
|
when FalseClass
|
482
|
-
|
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
|
-
|
489
|
-
when
|
490
|
-
|
491
|
-
when
|
492
|
-
v
|
491
|
+
literal_array(v)
|
492
|
+
when Time
|
493
|
+
literal_time(v)
|
494
|
+
when DateTime
|
495
|
+
literal_datetime(v)
|
493
496
|
when Date
|
494
|
-
v
|
497
|
+
literal_date(v)
|
495
498
|
when Dataset
|
496
|
-
|
499
|
+
literal_dataset(v)
|
497
500
|
else
|
498
|
-
|
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(*
|
541
|
-
|
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(*
|
548
|
-
|
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
|
-
|
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})"
|
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)
|