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