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