sequel_core 2.1.0 → 2.2.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 +39 -1
- data/Rakefile +1 -1
- data/doc/dataset_filtering.rdoc +5 -87
- data/lib/sequel_core.rb +20 -13
- data/lib/sequel_core/adapters/mysql.rb +12 -11
- data/lib/sequel_core/adapters/odbc_mssql.rb +3 -3
- data/lib/sequel_core/adapters/oracle.rb +2 -2
- data/lib/sequel_core/adapters/postgres.rb +46 -26
- data/lib/sequel_core/adapters/sqlite.rb +12 -0
- data/lib/sequel_core/core_sql.rb +7 -0
- data/lib/sequel_core/database.rb +12 -14
- data/lib/sequel_core/dataset.rb +6 -3
- data/lib/sequel_core/dataset/sql.rb +28 -25
- data/lib/sequel_core/object_graph.rb +23 -21
- data/lib/sequel_core/schema/sql.rb +5 -1
- data/lib/sequel_core/sql.rb +76 -5
- data/spec/adapters/mysql_spec.rb +33 -9
- data/spec/adapters/postgres_spec.rb +35 -3
- data/spec/adapters/sqlite_spec.rb +14 -2
- data/spec/core_sql_spec.rb +38 -3
- data/spec/database_spec.rb +19 -1
- data/spec/dataset_spec.rb +60 -66
- data/spec/{blockless_filters_spec.rb → expression_filters_spec.rb} +2 -10
- data/spec/object_graph_spec.rb +5 -0
- data/spec/schema_spec.rb +6 -6
- data/spec/spec_config.rb.example +2 -1
- data/spec/spec_helper.rb +0 -6
- metadata +3 -6
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +0 -310
- data/lib/sequel_core/dataset/sequelizer.rb +0 -50
- data/spec/sequelizer_spec.rb +0 -504
@@ -116,6 +116,18 @@ module Sequel
|
|
116
116
|
case op[:op]
|
117
117
|
when :add_column
|
118
118
|
"ALTER TABLE #{table} ADD #{column_definition_sql(op)}"
|
119
|
+
when :add_index
|
120
|
+
index_definition_sql(table, op)
|
121
|
+
when :drop_column
|
122
|
+
columns_str = (schema_parse_table(table, {}).map {|c| c[0] } - (Array === op[:name] ? op[:name] : [op[:name]])).join(",")
|
123
|
+
sql = "BEGIN TRANSACTION;\n"
|
124
|
+
sql += "CREATE TEMPORARY TABLE #{table}_backup(#{columns_str});\n"
|
125
|
+
sql += "INSERT INTO #{table}_backup SELECT #{columns_str} FROM #{table};\n"
|
126
|
+
sql += "DROP TABLE #{table};\n"
|
127
|
+
sql += "CREATE TABLE #{table}(#{columns_str});\n"
|
128
|
+
sql += "INSERT INTO #{table} SELECT #{columns_str} FROM #{table}_backup;\n"
|
129
|
+
sql += "DROP TABLE #{table}_backup;\n"
|
130
|
+
sql += "COMMIT;\n"
|
119
131
|
else
|
120
132
|
raise Error, "Unsupported ALTER TABLE operation"
|
121
133
|
end
|
data/lib/sequel_core/core_sql.rb
CHANGED
@@ -139,10 +139,17 @@ class String
|
|
139
139
|
def to_sql
|
140
140
|
split("\n").to_sql
|
141
141
|
end
|
142
|
+
|
143
|
+
# Returns a Blob that holds the same data as this string. Blobs provide proper
|
144
|
+
# escaping of binary data.
|
145
|
+
def to_blob
|
146
|
+
::Sequel::SQL::Blob.new self
|
147
|
+
end
|
142
148
|
end
|
143
149
|
|
144
150
|
class Symbol
|
145
151
|
include Sequel::SQL::QualifyingMethods
|
152
|
+
include Sequel::SQL::IdentifierMethods
|
146
153
|
include Sequel::SQL::GenericExpressionMethods
|
147
154
|
|
148
155
|
# If no argument is given, returns a Sequel::SQL::ColumnAll object specifying all
|
data/lib/sequel_core/database.rb
CHANGED
@@ -48,8 +48,8 @@ module Sequel
|
|
48
48
|
def initialize(opts = {}, &block)
|
49
49
|
@opts = opts
|
50
50
|
|
51
|
-
@quote_identifiers = opts[:quote_identifiers]
|
52
|
-
@single_threaded = opts[:single_threaded]
|
51
|
+
@quote_identifiers = opts.include?(:quote_identifiers) ? opts[:quote_identifiers] : @@quote_identifiers
|
52
|
+
@single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
|
53
53
|
@schemas = nil
|
54
54
|
@pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
|
55
55
|
@pool.connection_proc = proc {connect} unless block
|
@@ -133,25 +133,21 @@ module Sequel
|
|
133
133
|
# Converts a uri to an options hash. These options are then passed
|
134
134
|
# to a newly created database object.
|
135
135
|
def self.uri_to_options(uri)
|
136
|
-
if uri.is_a?(String)
|
137
|
-
uri = URI.parse(uri)
|
138
|
-
end
|
136
|
+
uri = URI.parse(uri) if uri.is_a?(String)
|
139
137
|
# special case for sqlite
|
140
|
-
if uri.scheme == 'sqlite'
|
141
|
-
{
|
142
|
-
:user => uri.user,
|
138
|
+
opts = if uri.scheme == 'sqlite'
|
139
|
+
{ :user => uri.user,
|
143
140
|
:password => uri.password,
|
144
|
-
:database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}"
|
145
|
-
}
|
141
|
+
:database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
|
146
142
|
else
|
147
|
-
{
|
148
|
-
:user => uri.user,
|
143
|
+
{ :user => uri.user,
|
149
144
|
:password => uri.password,
|
150
145
|
:host => uri.host,
|
151
146
|
:port => uri.port,
|
152
|
-
:database => (m = /\/(.*)/.match(uri.path)) && (m[1])
|
153
|
-
}
|
147
|
+
:database => (m = /\/(.*)/.match(uri.path)) && (m[1]) }
|
154
148
|
end
|
149
|
+
uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| opts[k.to_sym] = v} unless uri.query.blank?
|
150
|
+
opts
|
155
151
|
end
|
156
152
|
|
157
153
|
### Private Class Methods ###
|
@@ -427,6 +423,8 @@ module Sequel
|
|
427
423
|
# parse that string using the time class.
|
428
424
|
(Time === value ? value.iso8601 : value.to_s).to_sequel_time
|
429
425
|
end
|
426
|
+
when :blob
|
427
|
+
value.to_blob
|
430
428
|
else
|
431
429
|
value
|
432
430
|
end
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w'callback convenience pagination query schema
|
1
|
+
%w'callback convenience pagination query schema sql'.each do |f|
|
2
2
|
require "sequel_core/dataset/#{f}"
|
3
3
|
end
|
4
4
|
|
@@ -187,7 +187,7 @@ module Sequel
|
|
187
187
|
def clone(opts = {})
|
188
188
|
c = super()
|
189
189
|
c.opts = @opts.merge(opts)
|
190
|
-
c.instance_variable_set(:@columns, nil) if
|
190
|
+
c.instance_variable_set(:@columns, nil) if opts.keys.any?{|o| COLUMN_CHANGE_OPTS.include?(o)}
|
191
191
|
c
|
192
192
|
end
|
193
193
|
|
@@ -198,7 +198,10 @@ module Sequel
|
|
198
198
|
# If the dataset does not have any rows, this will be an empty array.
|
199
199
|
# If you are looking for all columns for a single table, see Schema::SQL#schema.
|
200
200
|
def columns
|
201
|
-
|
201
|
+
return @columns if @columns
|
202
|
+
ds = unfiltered.unordered.clone(:distinct => nil)
|
203
|
+
ds.single_record
|
204
|
+
@columns = ds.instance_variable_get(:@columns)
|
202
205
|
@columns || []
|
203
206
|
end
|
204
207
|
|
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
COLUMN_REF_RE1 = /\A([\w ]+)__([\w ]+)___([\w ]+)\z/.freeze
|
7
7
|
COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
|
8
8
|
COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
|
9
|
-
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql]
|
9
|
+
COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit]
|
10
10
|
DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
|
11
11
|
N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
|
12
12
|
NULL = "NULL".freeze
|
@@ -134,8 +134,9 @@ module Sequel
|
|
134
134
|
# * Sequel::SQL::BooleanExpression - an existing condition expression,
|
135
135
|
# probably created using the Sequel blockless filter DSL.
|
136
136
|
#
|
137
|
-
# filter also takes a block,
|
138
|
-
#
|
137
|
+
# filter also takes a block, which should return one of the above argument
|
138
|
+
# types, and is treated the same way. If both a block and regular argument
|
139
|
+
# are provided, they get ANDed together.
|
139
140
|
#
|
140
141
|
# Examples:
|
141
142
|
#
|
@@ -527,7 +528,7 @@ module Sequel
|
|
527
528
|
# SQL fragment for the qualifed identifier, specifying
|
528
529
|
# a table and a column (or schema and table).
|
529
530
|
def qualified_identifier_sql(qcr)
|
530
|
-
|
531
|
+
[qcr.table, qcr.column].map{|x| x.is_one_of?(SQL::QualifiedIdentifier, SQL::Identifier) ? literal(x) : quote_identifier(x)}.join('.')
|
531
532
|
end
|
532
533
|
|
533
534
|
# Adds quoting to identifiers (columns and tables). If identifiers are not
|
@@ -588,7 +589,7 @@ module Sequel
|
|
588
589
|
select_columns = columns ? column_list(columns) : WILDCARD
|
589
590
|
|
590
591
|
if distinct = opts[:distinct]
|
591
|
-
distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{
|
592
|
+
distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{expression_list(distinct)})"
|
592
593
|
sql = "SELECT #{distinct_clause} #{select_columns}"
|
593
594
|
else
|
594
595
|
sql = "SELECT #{select_columns}"
|
@@ -607,7 +608,7 @@ module Sequel
|
|
607
608
|
end
|
608
609
|
|
609
610
|
if group = opts[:group]
|
610
|
-
sql << " GROUP BY #{
|
611
|
+
sql << " GROUP BY #{expression_list(group)}"
|
611
612
|
end
|
612
613
|
|
613
614
|
if having = opts[:having]
|
@@ -615,7 +616,7 @@ module Sequel
|
|
615
616
|
end
|
616
617
|
|
617
618
|
if order = opts[:order]
|
618
|
-
sql << " ORDER BY #{
|
619
|
+
sql << " ORDER BY #{expression_list(order)}"
|
619
620
|
end
|
620
621
|
|
621
622
|
if limit = opts[:limit]
|
@@ -694,7 +695,7 @@ module Sequel
|
|
694
695
|
#
|
695
696
|
# Raises an error if the dataset is grouped or includes more
|
696
697
|
# than one table.
|
697
|
-
def update_sql(values = {}, opts = nil
|
698
|
+
def update_sql(values = {}, opts = nil)
|
698
699
|
opts = opts ? @opts.merge(opts) : @opts
|
699
700
|
|
700
701
|
if opts[:group]
|
@@ -704,23 +705,19 @@ module Sequel
|
|
704
705
|
end
|
705
706
|
|
706
707
|
sql = "UPDATE #{source_list(@opts[:from])} SET "
|
707
|
-
if
|
708
|
-
|
708
|
+
set = if values.is_a?(Hash)
|
709
|
+
# get values from hash
|
710
|
+
values = transform_save(values) if @transform
|
711
|
+
values.map do |k, v|
|
712
|
+
# convert string key into symbol
|
713
|
+
k = k.to_sym if String === k
|
714
|
+
"#{literal(k)} = #{literal(v)}"
|
715
|
+
end.join(COMMA_SEPARATOR)
|
709
716
|
else
|
710
|
-
|
711
|
-
|
712
|
-
values = transform_save(values) if @transform
|
713
|
-
values.map do |k, v|
|
714
|
-
# convert string key into symbol
|
715
|
-
k = k.to_sym if String === k
|
716
|
-
"#{literal(k)} = #{literal(v)}"
|
717
|
-
end.join(COMMA_SEPARATOR)
|
718
|
-
else
|
719
|
-
# copy values verbatim
|
720
|
-
values
|
721
|
-
end
|
722
|
-
sql << set
|
717
|
+
# copy values verbatim
|
718
|
+
values
|
723
719
|
end
|
720
|
+
sql << set
|
724
721
|
if where = opts[:where]
|
725
722
|
sql << " WHERE #{literal(where)}"
|
726
723
|
end
|
@@ -750,12 +747,18 @@ module Sequel
|
|
750
747
|
WILDCARD
|
751
748
|
else
|
752
749
|
m = columns.map do |i|
|
753
|
-
i.is_a?(Hash) ? i.map{|
|
750
|
+
i.is_a?(Hash) ? i.map{|k, v| "#{literal(k)} AS #{quote_identifier(v)}"} : literal(i)
|
754
751
|
end
|
755
752
|
m.join(COMMA_SEPARATOR)
|
756
753
|
end
|
757
754
|
end
|
758
755
|
|
756
|
+
# Converts an array of expressions into a comma separated string of
|
757
|
+
# expressions.
|
758
|
+
def expression_list(columns)
|
759
|
+
columns.map{|i| literal(i)}.join(COMMA_SEPARATOR)
|
760
|
+
end
|
761
|
+
|
759
762
|
# SQL fragment based on the expr type. See #filter.
|
760
763
|
def filter_expr(expr = nil, &block)
|
761
764
|
expr = nil if expr == []
|
@@ -774,7 +777,7 @@ module Sequel
|
|
774
777
|
SQL::BooleanExpression.from_value_pairs(expr)
|
775
778
|
end
|
776
779
|
when Proc
|
777
|
-
|
780
|
+
filter_expr(expr.call(SQL::VirtualRow.new))
|
778
781
|
when SQL::NumericExpression, SQL::StringExpression
|
779
782
|
raise(Error, "Invalid SQL Expression type: #{expr.inspect}")
|
780
783
|
when Symbol, SQL::Expression
|
@@ -31,7 +31,7 @@ module Sequel
|
|
31
31
|
# Arguments:
|
32
32
|
# * dataset - Can be a symbol (specifying a table), another dataset,
|
33
33
|
# or an object that responds to .dataset and yields a symbol or a dataset
|
34
|
-
# * join_conditions -
|
34
|
+
# * join_conditions - Any condition(s) allowed by join_table.
|
35
35
|
# * options - A hash of graph options. The following options are currently used:
|
36
36
|
# * :table_alias - The alias to use for the table. If not specified, doesn't
|
37
37
|
# alias the table. You will get an error if the the alias (or table) name is
|
@@ -43,7 +43,8 @@ module Sequel
|
|
43
43
|
# columns and is like simply joining the tables, though graph keeps
|
44
44
|
# some metadata about join that makes it important to use graph instead
|
45
45
|
# of join.
|
46
|
-
|
46
|
+
# * block - A block that is passed to join_table.
|
47
|
+
def graph(dataset, join_conditions = nil, options = {}, &block)
|
47
48
|
# Allow the use of a model, dataset, or symbol as the first argument
|
48
49
|
# Find the table name/dataset based on the argument
|
49
50
|
dataset = dataset.dataset if dataset.respond_to?(:dataset)
|
@@ -68,7 +69,7 @@ module Sequel
|
|
68
69
|
raise_alias_error.call if @opts[:graph] && @opts[:graph][:table_aliases] && @opts[:graph][:table_aliases].include?(table_alias)
|
69
70
|
|
70
71
|
# Join the table early in order to avoid cloning the dataset twice
|
71
|
-
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, table_alias)
|
72
|
+
ds = join_table(options[:join_type] || :left_outer, table, join_conditions, table_alias, &block)
|
72
73
|
opts = ds.opts
|
73
74
|
|
74
75
|
# Whether to include the table in the result set
|
@@ -87,15 +88,15 @@ module Sequel
|
|
87
88
|
# Associates table alias (the master is never aliased)
|
88
89
|
table_aliases = graph[:table_aliases] = {master=>self}
|
89
90
|
# Keep track of the alias numbers used
|
90
|
-
ca_num = graph[:column_alias_num] =
|
91
|
+
ca_num = graph[:column_alias_num] = Hash.new(0)
|
91
92
|
# All columns in the master table are never
|
92
93
|
# aliased, but are not included if set_graph_aliases
|
93
94
|
# has been used.
|
94
95
|
if add_columns
|
95
|
-
select =
|
96
|
+
select = opts[:select] = []
|
96
97
|
columns.each do |column|
|
97
98
|
column_aliases[column] = [master, column]
|
98
|
-
select.push(
|
99
|
+
select.push(column.qualify(master))
|
99
100
|
end
|
100
101
|
end
|
101
102
|
end
|
@@ -120,23 +121,19 @@ module Sequel
|
|
120
121
|
# using the next value of N that we know hasn't been
|
121
122
|
# used
|
122
123
|
cols.each do |column|
|
123
|
-
col_alias,
|
124
|
-
|
125
|
-
if column_aliases[
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
else
|
130
|
-
ca_num[tc] = 1
|
131
|
-
tc = :"#{tc}_0"
|
132
|
-
end
|
124
|
+
col_alias, identifier = if column_aliases[column]
|
125
|
+
column_alias = :"#{table_alias}_#{column}"
|
126
|
+
if column_aliases[column_alias]
|
127
|
+
column_alias_num = ca_num[column_alias]
|
128
|
+
column_alias = :"#{column_alias}_#{column_alias_num}"
|
129
|
+
ca_num[column_alias] += 1
|
133
130
|
end
|
134
|
-
[
|
131
|
+
[column_alias, column.qualify(table_alias).as(column_alias)]
|
135
132
|
else
|
136
|
-
[column,
|
133
|
+
[column, column.qualify(table_alias)]
|
137
134
|
end
|
138
135
|
column_aliases[col_alias] = [table_alias, column]
|
139
|
-
select.push(
|
136
|
+
select.push(identifier)
|
140
137
|
end
|
141
138
|
end
|
142
139
|
ds
|
@@ -158,8 +155,13 @@ module Sequel
|
|
158
155
|
# The first element of the array should be the table alias,
|
159
156
|
# and the second should be the actual column name.
|
160
157
|
def set_graph_aliases(graph_aliases)
|
161
|
-
|
162
|
-
|
158
|
+
cols = graph_aliases.collect do |col_alias, tc|
|
159
|
+
identifier = tc[1].qualify(tc[0])
|
160
|
+
identifier = identifier.as(col_alias) unless tc[1] == col_alias
|
161
|
+
identifier
|
162
|
+
end
|
163
|
+
ds = select(*cols)
|
164
|
+
ds.opts[:graph_aliases] = graph_aliases
|
163
165
|
ds
|
164
166
|
end
|
165
167
|
|
@@ -227,6 +227,8 @@ module Sequel
|
|
227
227
|
# integer, string, date, datetime, boolean, and float.
|
228
228
|
def schema_column_type(db_type)
|
229
229
|
case db_type
|
230
|
+
when 'tinyint'
|
231
|
+
Sequel.convert_tinyint_to_bool ? :boolean : :integer
|
230
232
|
when /\A(int(eger)?|bigint|smallint)\z/
|
231
233
|
:integer
|
232
234
|
when /\A(character( varying)?|varchar|text)\z/
|
@@ -237,12 +239,14 @@ module Sequel
|
|
237
239
|
:datetime
|
238
240
|
when /\Atime( with(out)? time zone)?\z/
|
239
241
|
:time
|
240
|
-
when
|
242
|
+
when "boolean"
|
241
243
|
:boolean
|
242
244
|
when /\A(real|float|double( precision)?)\z/
|
243
245
|
:float
|
244
246
|
when /\A(numeric|decimal|money)\z/
|
245
247
|
:decimal
|
248
|
+
when "bytea"
|
249
|
+
:blob
|
246
250
|
end
|
247
251
|
end
|
248
252
|
|
data/lib/sequel_core/sql.rb
CHANGED
@@ -197,6 +197,14 @@ module Sequel
|
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
200
|
+
# Includes a method that returns Identifiers.
|
201
|
+
module IdentifierMethods
|
202
|
+
# Return self wrapped as an identifier.
|
203
|
+
def identifier
|
204
|
+
Identifier.new(self)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
200
208
|
# This module includes the methods that are defined on objects that can be
|
201
209
|
# used in a numeric or string context in SQL (Symbol, LiteralString,
|
202
210
|
# SQL::Function, and SQL::StringExpression).
|
@@ -385,7 +393,7 @@ module Sequel
|
|
385
393
|
# a keyword in ruby.
|
386
394
|
attr_reader :aliaz
|
387
395
|
|
388
|
-
#
|
396
|
+
# Create an object with the given expression and alias.
|
389
397
|
def initialize(expression, aliaz)
|
390
398
|
@expression, @aliaz = expression, aliaz
|
391
399
|
end
|
@@ -397,13 +405,25 @@ module Sequel
|
|
397
405
|
end
|
398
406
|
end
|
399
407
|
|
408
|
+
# Blob is used to represent binary data in the Ruby environment that is
|
409
|
+
# stored as a blob type in the database. In PostgreSQL, the blob type is
|
410
|
+
# called bytea. Sequel represents binary data as a Blob object because
|
411
|
+
# certain database engines, such as PostgreSQL, require binary data to be
|
412
|
+
# escaped.
|
413
|
+
class Blob < ::String
|
414
|
+
# return self.
|
415
|
+
def to_blob
|
416
|
+
self
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
400
420
|
# Subclass of ComplexExpression where the expression results
|
401
421
|
# in a boolean value in SQL.
|
402
422
|
class BooleanExpression < ComplexExpression
|
403
423
|
include BooleanMethods
|
404
424
|
|
405
425
|
# Take pairs of values (e.g. a hash or array of arrays of two pairs)
|
406
|
-
# and converts it to a
|
426
|
+
# and converts it to a BooleanExpression. The operator and args
|
407
427
|
# used depends on the case of the right (2nd) argument:
|
408
428
|
#
|
409
429
|
# * 0..10 - left >= 0 AND left <= 10
|
@@ -531,6 +551,27 @@ module Sequel
|
|
531
551
|
end
|
532
552
|
end
|
533
553
|
|
554
|
+
# Represents an identifier (column or table). Can be used
|
555
|
+
# to specify a Symbol with multiple underscores should not be
|
556
|
+
# split, or for creating an identifier without using a symbol.
|
557
|
+
class Identifier < GenericExpression
|
558
|
+
include QualifyingMethods
|
559
|
+
|
560
|
+
# The table and column to reference
|
561
|
+
attr_reader :value
|
562
|
+
|
563
|
+
# Set the value to the given argument
|
564
|
+
def initialize(value)
|
565
|
+
@value = value
|
566
|
+
end
|
567
|
+
|
568
|
+
# Delegate the creation of the resulting SQL to the given dataset,
|
569
|
+
# since it may be database dependent.
|
570
|
+
def to_s(ds)
|
571
|
+
ds.quote_identifier(@value)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
534
575
|
# IrregularFunction is used for the SQL EXTRACT and CAST functions,
|
535
576
|
# which don't use regular function calling syntax. The IrregularFunction
|
536
577
|
# replaces the commas the regular function uses with a custom
|
@@ -637,10 +678,10 @@ module Sequel
|
|
637
678
|
# The expression to order the result set by.
|
638
679
|
attr_reader :expression
|
639
680
|
|
640
|
-
# Whether the expression should order the result set in a
|
681
|
+
# Whether the expression should order the result set in a descending manner
|
641
682
|
attr_reader :descending
|
642
683
|
|
643
|
-
#
|
684
|
+
# Set the expression and descending attributes to the given values.
|
644
685
|
def initialize(expression, descending = true)
|
645
686
|
@expression, @descending = expression, descending
|
646
687
|
end
|
@@ -727,9 +768,39 @@ module Sequel
|
|
727
768
|
ds.subscript_sql(self)
|
728
769
|
end
|
729
770
|
end
|
771
|
+
|
772
|
+
# An instance of this class is yielded to the block supplied to filter.
|
773
|
+
# Useful if another library also defines the operator methods that
|
774
|
+
# Sequel defines for symbols.
|
775
|
+
#
|
776
|
+
# Examples:
|
777
|
+
#
|
778
|
+
# ds = DB[:t]
|
779
|
+
# ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
|
780
|
+
# ds.filter{|r| r.table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
|
781
|
+
# ds.filter{|r| r.is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
|
782
|
+
class VirtualRow
|
783
|
+
(instance_methods - %w"__id__ __send__").each{|m| undef_method(m)}
|
784
|
+
|
785
|
+
# Can return Identifiers, QualifiedIdentifiers, or Functions:
|
786
|
+
#
|
787
|
+
# * Function - returned if any arguments are supplied, using the method name
|
788
|
+
# as the function name, and the arguments as the function arguments.
|
789
|
+
# * QualifiedIdentifier - returned if the method name contains __, with the
|
790
|
+
# table being the part before __, and the column being the part after.
|
791
|
+
# * Identifier - returned otherwise, using the method name.
|
792
|
+
def method_missing(m, *args)
|
793
|
+
if args.empty?
|
794
|
+
table, column = m.to_s.split('__', 2)
|
795
|
+
column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
|
796
|
+
else
|
797
|
+
Function.new(m, *args)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
730
801
|
end
|
731
802
|
|
732
|
-
# LiteralString is used to represent literal SQL expressions.
|
803
|
+
# LiteralString is used to represent literal SQL expressions. A
|
733
804
|
# LiteralString is copied verbatim into an SQL statement. Instances of
|
734
805
|
# LiteralString can be created by calling String#lit.
|
735
806
|
# LiteralStrings can use all of the SQL::ColumnMethods and the
|