viking-sequel 3.10.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 +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- 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.12.0.txt +534 -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/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
data/lib/sequel/sql.rb
ADDED
@@ -0,0 +1,1020 @@
|
|
1
|
+
module Sequel
|
2
|
+
if RUBY_VERSION < '1.9.0'
|
3
|
+
# If on Ruby 1.8, create a Sequel::BasicObject class that is similar to the
|
4
|
+
# the Ruby 1.9 BasicObject class. This is used in a few places where proxy
|
5
|
+
# objects are needed that respond to any method call.
|
6
|
+
class BasicObject
|
7
|
+
# The instance methods to not remove from the class when removing
|
8
|
+
# other methods.
|
9
|
+
KEEP_METHODS = %w"__id__ __send__ __metaclass__ instance_eval == equal? initialize"
|
10
|
+
|
11
|
+
# Remove all but the most basic instance methods from the class. A separate
|
12
|
+
# method so that it can be called again if necessary if you load libraries
|
13
|
+
# after Sequel that add instance methods to Object.
|
14
|
+
def self.remove_methods!
|
15
|
+
((private_instance_methods + instance_methods) - KEEP_METHODS).each{|m| undef_method(m)}
|
16
|
+
end
|
17
|
+
remove_methods!
|
18
|
+
end
|
19
|
+
else
|
20
|
+
# If on 1.9, create a Sequel::BasicObject class that is just like the
|
21
|
+
# default BasicObject class, except that missing constants are resolved in
|
22
|
+
# Object. This allows the virtual row support to work with classes
|
23
|
+
# without prefixing them with ::, such as:
|
24
|
+
#
|
25
|
+
# DB[:bonds].filter{maturity_date > Time.now}
|
26
|
+
class BasicObject < ::BasicObject
|
27
|
+
# Lookup missing constants in ::Object
|
28
|
+
def self.const_missing(name)
|
29
|
+
::Object.const_get(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
# No-op method on ruby 1.9, which has a real BasicObject class.
|
33
|
+
def self.remove_methods!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class LiteralString < ::String
|
39
|
+
end
|
40
|
+
|
41
|
+
# The SQL module holds classes whose instances represent SQL fragments.
|
42
|
+
# It also holds modules that are included in core ruby classes that
|
43
|
+
# make Sequel a friendly DSL.
|
44
|
+
module SQL
|
45
|
+
|
46
|
+
### Parent Classes ###
|
47
|
+
|
48
|
+
# Classes/Modules aren't an alphabetical order due to the fact that
|
49
|
+
# some reference constants defined in others at load time.
|
50
|
+
|
51
|
+
# Base class for all SQL fragments
|
52
|
+
class Expression
|
53
|
+
# all instance variables declared to be readers are to be used for comparison.
|
54
|
+
def self.attr_reader(*args)
|
55
|
+
super
|
56
|
+
comparison_attrs.concat args
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.comparison_attrs
|
60
|
+
@comparison_attrs ||= self == Expression ? [] : superclass.comparison_attrs.clone
|
61
|
+
end
|
62
|
+
|
63
|
+
# Create a to_s instance method that takes a dataset, and calls
|
64
|
+
# the method provided on the dataset with args as the argument (self by default).
|
65
|
+
# Used to DRY up some code.
|
66
|
+
def self.to_s_method(meth, args=:self) # :nodoc:
|
67
|
+
class_eval("def to_s(ds); ds.#{meth}(#{args}) end", __FILE__, __LINE__)
|
68
|
+
end
|
69
|
+
private_class_method :to_s_method
|
70
|
+
|
71
|
+
# Returns self, because SQL::Expression already acts like
|
72
|
+
# LiteralString.
|
73
|
+
def lit
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Alias for to_s
|
78
|
+
def sql_literal(ds)
|
79
|
+
to_s(ds)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns true if the receiver is the same expression as the
|
83
|
+
# the +other+ expression.
|
84
|
+
def eql?(other)
|
85
|
+
other.is_a?(self.class) && !self.class.comparison_attrs.find {|a| send(a) != other.send(a)}
|
86
|
+
end
|
87
|
+
alias == eql?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Represents a complex SQL expression, with a given operator and one
|
91
|
+
# or more attributes (which may also be ComplexExpressions, forming
|
92
|
+
# a tree). This class is the backbone of the blockless filter support in
|
93
|
+
# Sequel.
|
94
|
+
#
|
95
|
+
# This is an abstract class that is not that useful by itself. The
|
96
|
+
# subclasses BooleanExpression, NumericExpression, and StringExpression
|
97
|
+
# define the behavior of the DSL via operators.
|
98
|
+
class ComplexExpression < Expression
|
99
|
+
# A hash of the opposite for each operator symbol, used for inverting
|
100
|
+
# objects.
|
101
|
+
OPERTATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
|
102
|
+
:<= => :>, :>= => :<, :'=' => :'!=' , :'!=' => :'=', :LIKE => :'NOT LIKE',
|
103
|
+
:'NOT LIKE' => :LIKE, :~ => :'!~', :'!~' => :~, :IN => :'NOT IN',
|
104
|
+
:'NOT IN' => :IN, :IS => :'IS NOT', :'IS NOT' => :IS, :'~*' => :'!~*',
|
105
|
+
:'!~*' => :'~*', :NOT => :NOOP, :NOOP => :NOT, :ILIKE => :'NOT ILIKE',
|
106
|
+
:'NOT ILIKE'=>:ILIKE}
|
107
|
+
|
108
|
+
# Standard Mathematical Operators used in NumericMethods
|
109
|
+
MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
|
110
|
+
|
111
|
+
# Bitwise Mathematical Operators used in NumericMethods
|
112
|
+
BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>]
|
113
|
+
|
114
|
+
# Inequality Operators used in InequalityMethods
|
115
|
+
INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
|
116
|
+
|
117
|
+
# Hash of ruby operator symbols to SQL operators, used in BooleanMethods
|
118
|
+
BOOLEAN_OPERATOR_METHODS = {:& => :AND, :| =>:OR}
|
119
|
+
|
120
|
+
# Operators that use IS, used for special casing to override literal true/false values
|
121
|
+
IS_OPERATORS = [:IS, :'IS NOT']
|
122
|
+
|
123
|
+
# Operator symbols that take exactly two arguments
|
124
|
+
TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
|
125
|
+
:~, :'!~', :'~*', :'!~*', :IN, :'NOT IN', :ILIKE, :'NOT ILIKE'] + \
|
126
|
+
INEQUALITY_OPERATORS + BITWISE_OPERATORS + IS_OPERATORS
|
127
|
+
|
128
|
+
# Operator symbols that take one or more arguments
|
129
|
+
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
|
130
|
+
|
131
|
+
# Operator symbols that take one argument
|
132
|
+
ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
|
133
|
+
|
134
|
+
# An array of args for this object
|
135
|
+
attr_reader :args
|
136
|
+
|
137
|
+
# The operator symbol for this object
|
138
|
+
attr_reader :op
|
139
|
+
|
140
|
+
# Set the operator symbol and arguments for this object to the ones given.
|
141
|
+
# Convert all args that are hashes or arrays with all two pairs to BooleanExpressions.
|
142
|
+
# Raise an error if the operator doesn't allow boolean input and a boolean argument is given.
|
143
|
+
# Raise an error if the wrong number of arguments for a given operator is used.
|
144
|
+
def initialize(op, *args)
|
145
|
+
args.map!{|a| Sequel.condition_specifier?(a) ? SQL::BooleanExpression.from_value_pairs(a) : a}
|
146
|
+
case op
|
147
|
+
when *N_ARITY_OPERATORS
|
148
|
+
raise(Error, "The #{op} operator requires at least 1 argument") unless args.length >= 1
|
149
|
+
old_args = args
|
150
|
+
args = []
|
151
|
+
old_args.each{|a| a.is_a?(self.class) && a.op == op ? args.concat(a.args) : args.push(a)}
|
152
|
+
when *TWO_ARITY_OPERATORS
|
153
|
+
raise(Error, "The #{op} operator requires precisely 2 arguments") unless args.length == 2
|
154
|
+
when *ONE_ARITY_OPERATORS
|
155
|
+
raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
|
156
|
+
else
|
157
|
+
raise(Error, "Invalid operator #{op}")
|
158
|
+
end
|
159
|
+
@op = op
|
160
|
+
@args = args
|
161
|
+
end
|
162
|
+
|
163
|
+
to_s_method :complex_expression_sql, '@op, @args'
|
164
|
+
end
|
165
|
+
|
166
|
+
# The base class for expressions that can be used in multiple places in
|
167
|
+
# the SQL query.
|
168
|
+
class GenericExpression < Expression
|
169
|
+
end
|
170
|
+
|
171
|
+
### Modules ###
|
172
|
+
|
173
|
+
# Methods that create aliased identifiers
|
174
|
+
module AliasMethods
|
175
|
+
# Create an SQL column alias of the receiving column or expression to the given alias.
|
176
|
+
def as(aliaz)
|
177
|
+
AliasedExpression.new(self, aliaz)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# This defines the bitwise methods: &, |, ^, ~, <<, and >>. Because these
|
182
|
+
# methods overlap with the standard BooleanMethods methods, and they only
|
183
|
+
# make sense for numbers, they are only included in NumericExpression.
|
184
|
+
module BitwiseMethods
|
185
|
+
ComplexExpression::BITWISE_OPERATORS.each do |o|
|
186
|
+
define_method(o) do |ce|
|
187
|
+
case ce
|
188
|
+
when BooleanExpression, StringExpression
|
189
|
+
raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
|
190
|
+
else
|
191
|
+
NumericExpression.new(o, self, ce)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Do the bitwise compliment of the self
|
197
|
+
def ~
|
198
|
+
NumericExpression.new(:'B~', self)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# This module includes the methods that are defined on objects that can be
|
203
|
+
# used in a boolean context in SQL (Symbol, LiteralString, SQL::Function,
|
204
|
+
# and SQL::BooleanExpression).
|
205
|
+
#
|
206
|
+
# This defines the ~ (NOT), & (AND), and | (OR) methods.
|
207
|
+
module BooleanMethods
|
208
|
+
# Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
|
209
|
+
def ~
|
210
|
+
BooleanExpression.invert(self)
|
211
|
+
end
|
212
|
+
|
213
|
+
ComplexExpression::BOOLEAN_OPERATOR_METHODS.each do |m, o|
|
214
|
+
define_method(m) do |ce|
|
215
|
+
case ce
|
216
|
+
when NumericExpression, StringExpression
|
217
|
+
raise(Sequel::Error, "cannot apply #{o} to a non-boolean expression")
|
218
|
+
else
|
219
|
+
BooleanExpression.new(o, self, ce)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Holds methods that are used to cast objects to differen SQL types.
|
226
|
+
module CastMethods
|
227
|
+
# Cast the reciever to the given SQL type. You can specify a ruby class as a type,
|
228
|
+
# and it is handled similarly to using a database independent type in the schema methods.
|
229
|
+
def cast(sql_type)
|
230
|
+
Cast.new(self, sql_type)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Cast the reciever to the given SQL type (or the database's default integer type if none given),
|
234
|
+
# and return the result as a NumericExpression.
|
235
|
+
def cast_numeric(sql_type = nil)
|
236
|
+
cast(sql_type || Integer).sql_number
|
237
|
+
end
|
238
|
+
|
239
|
+
# Cast the reciever to the given SQL type (or the database's default string type if none given),
|
240
|
+
# and return the result as a StringExpression, so you can use +
|
241
|
+
# directly on the result for SQL string concatenation.
|
242
|
+
def cast_string(sql_type = nil)
|
243
|
+
cast(sql_type || String).sql_string
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Adds methods that allow you to treat an object as an instance of a specific
|
248
|
+
# ComplexExpression subclass. This is useful if another library
|
249
|
+
# overrides the methods defined by Sequel.
|
250
|
+
#
|
251
|
+
# For example, if Symbol#/ is overridden to produce a string (for
|
252
|
+
# example, to make file system path creation easier), the
|
253
|
+
# following code will not do what you want:
|
254
|
+
#
|
255
|
+
# :price/10 > 100
|
256
|
+
#
|
257
|
+
# In that case, you need to do the following:
|
258
|
+
#
|
259
|
+
# :price.sql_number/10 > 100
|
260
|
+
module ComplexExpressionMethods
|
261
|
+
# Extract a datetime_part (e.g. year, month) from self:
|
262
|
+
#
|
263
|
+
# :date.extract(:year) # SQL: extract(year FROM "date")
|
264
|
+
#
|
265
|
+
# Also has the benefit of returning the result as a
|
266
|
+
# NumericExpression instead of a generic ComplexExpression.
|
267
|
+
#
|
268
|
+
# The extract function is in the SQL standard, but it doesn't
|
269
|
+
# doesn't use the standard function calling convention.
|
270
|
+
def extract(datetime_part)
|
271
|
+
Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
|
272
|
+
end
|
273
|
+
|
274
|
+
# Return a BooleanExpression representation of self.
|
275
|
+
def sql_boolean
|
276
|
+
BooleanExpression.new(:NOOP, self)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Return a NumericExpression representation of self.
|
280
|
+
def sql_number
|
281
|
+
NumericExpression.new(:NOOP, self)
|
282
|
+
end
|
283
|
+
|
284
|
+
# Return a StringExpression representation of self.
|
285
|
+
def sql_string
|
286
|
+
StringExpression.new(:NOOP, self)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Includes a method that returns Identifiers.
|
291
|
+
module IdentifierMethods
|
292
|
+
# Return self wrapped as an identifier.
|
293
|
+
def identifier
|
294
|
+
Identifier.new(self)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# This module includes the methods that are defined on objects that can be
|
299
|
+
# used in a numeric or string context in SQL (Symbol (except on ruby 1.9), LiteralString,
|
300
|
+
# SQL::Function, and SQL::StringExpression).
|
301
|
+
#
|
302
|
+
# This defines the >, <, >=, and <= methods.
|
303
|
+
module InequalityMethods
|
304
|
+
ComplexExpression::INEQUALITY_OPERATORS.each do |o|
|
305
|
+
define_method(o) do |ce|
|
306
|
+
case ce
|
307
|
+
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, Array
|
308
|
+
raise(Error, "cannot apply #{o} to a boolean expression")
|
309
|
+
else
|
310
|
+
BooleanExpression.new(o, self, ce)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# This module augments the default initalize method for the
|
317
|
+
# ComplexExpression subclass it is included in, so that
|
318
|
+
# attempting to use boolean input when initializing a NumericExpression
|
319
|
+
# or StringExpression results in an error.
|
320
|
+
module NoBooleanInputMethods
|
321
|
+
# Raise an Error if one of the args would be boolean in an SQL
|
322
|
+
# context, otherwise call super.
|
323
|
+
def initialize(op, *args)
|
324
|
+
args.each do |a|
|
325
|
+
case a
|
326
|
+
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, Array
|
327
|
+
raise(Error, "cannot apply #{op} to a boolean expression")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
super
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# This module includes the methods that are defined on objects that can be
|
335
|
+
# used in a numeric context in SQL (Symbol, LiteralString, SQL::Function,
|
336
|
+
# and SQL::NumericExpression).
|
337
|
+
#
|
338
|
+
# This defines the +, -, *, and / methods.
|
339
|
+
module NumericMethods
|
340
|
+
ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
|
341
|
+
define_method(o) do |ce|
|
342
|
+
case ce
|
343
|
+
when BooleanExpression, StringExpression
|
344
|
+
raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
|
345
|
+
else
|
346
|
+
NumericExpression.new(o, self, ce)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Methods that create OrderedExpressions, used for sorting by columns
|
353
|
+
# or more complex expressions.
|
354
|
+
module OrderMethods
|
355
|
+
# Mark the receiving SQL column as sorting in a descending fashion.
|
356
|
+
def desc
|
357
|
+
OrderedExpression.new(self)
|
358
|
+
end
|
359
|
+
|
360
|
+
# Mark the receiving SQL column as sorting in an ascending fashion (generally a no-op).
|
361
|
+
def asc
|
362
|
+
OrderedExpression.new(self, false)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Methods that created QualifiedIdentifiers, used for qualifying column
|
367
|
+
# names with a table or table names with a schema.
|
368
|
+
module QualifyingMethods
|
369
|
+
# Qualify the current object with the given table/schema.
|
370
|
+
def qualify(ts)
|
371
|
+
QualifiedIdentifier.new(ts, self)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# This module includes the methods that are defined on objects that can be
|
376
|
+
# used in a string context in SQL (Symbol, LiteralString, SQL::Function,
|
377
|
+
# and SQL::StringExpression).
|
378
|
+
#
|
379
|
+
# This defines the like (LIKE) and ilike methods, used for pattern matching.
|
380
|
+
# like is case sensitive (if the database supports it), ilike is case insensitive.
|
381
|
+
module StringMethods
|
382
|
+
# Create a BooleanExpression case insensitive pattern match of self
|
383
|
+
# with the given patterns. See StringExpression.like.
|
384
|
+
def ilike(*ces)
|
385
|
+
StringExpression.like(self, *(ces << {:case_insensitive=>true}))
|
386
|
+
end
|
387
|
+
|
388
|
+
# Create a BooleanExpression case sensitive (if the database supports it) pattern match of self with
|
389
|
+
# the given patterns. See StringExpression.like.
|
390
|
+
def like(*ces)
|
391
|
+
StringExpression.like(self, *ces)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
# This module is included in StringExpression and can be included elsewhere
|
396
|
+
# to allow the use of the + operator to represent concatenation of SQL
|
397
|
+
# Strings:
|
398
|
+
#
|
399
|
+
# :x.sql_string + :y => # SQL: x || y
|
400
|
+
module StringConcatenationMethods
|
401
|
+
def +(ce)
|
402
|
+
StringExpression.new(:'||', self, ce)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# Methods that create Subscripts (SQL array accesses).
|
407
|
+
module SubscriptMethods
|
408
|
+
# Return an SQL array subscript with the given arguments.
|
409
|
+
#
|
410
|
+
# :array.sql_subscript(1) # SQL: array[1]
|
411
|
+
# :array.sql_subscript(1, 2) # SQL: array[1, 2]
|
412
|
+
def sql_subscript(*sub)
|
413
|
+
Subscript.new(self, sub.flatten)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
### Classes ###
|
418
|
+
|
419
|
+
# Represents an aliasing of an expression/column to a given name.
|
420
|
+
class AliasedExpression < Expression
|
421
|
+
# The expression to alias
|
422
|
+
attr_reader :expression
|
423
|
+
|
424
|
+
# The alias to use for the expression, not alias since that is
|
425
|
+
# a keyword in ruby.
|
426
|
+
attr_reader :aliaz
|
427
|
+
|
428
|
+
# Create an object with the given expression and alias.
|
429
|
+
def initialize(expression, aliaz)
|
430
|
+
@expression, @aliaz = expression, aliaz
|
431
|
+
end
|
432
|
+
|
433
|
+
to_s_method :aliased_expression_sql
|
434
|
+
end
|
435
|
+
|
436
|
+
# Blob is used to represent binary data in the Ruby environment that is
|
437
|
+
# stored as a blob type in the database. Sequel represents binary data as a Blob object because
|
438
|
+
# certain database engines require binary data to be escaped.
|
439
|
+
class Blob < ::String
|
440
|
+
# Returns self
|
441
|
+
def to_sequel_blob
|
442
|
+
self
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Subclass of ComplexExpression where the expression results
|
447
|
+
# in a boolean value in SQL.
|
448
|
+
class BooleanExpression < ComplexExpression
|
449
|
+
include BooleanMethods
|
450
|
+
|
451
|
+
# Take pairs of values (e.g. a hash or array of arrays of two pairs)
|
452
|
+
# and converts it to a BooleanExpression. The operator and args
|
453
|
+
# used depends on the case of the right (2nd) argument:
|
454
|
+
#
|
455
|
+
# * 0..10 - left >= 0 AND left <= 10
|
456
|
+
# * [1,2] - left IN (1,2)
|
457
|
+
# * nil - left IS NULL
|
458
|
+
# * /as/ - left ~ 'as'
|
459
|
+
# * :blah - left = blah
|
460
|
+
# * 'blah' - left = 'blah'
|
461
|
+
#
|
462
|
+
# If multiple arguments are given, they are joined with the op given (AND
|
463
|
+
# by default, OR possible). If negate is set to true,
|
464
|
+
# all subexpressions are inverted before used. Therefore, the following
|
465
|
+
# expressions are equivalent:
|
466
|
+
#
|
467
|
+
# ~from_value_pairs(hash)
|
468
|
+
# from_value_pairs(hash, :OR, true)
|
469
|
+
def self.from_value_pairs(pairs, op=:AND, negate=false)
|
470
|
+
pairs = pairs.collect do |l,r|
|
471
|
+
ce = case r
|
472
|
+
when Range
|
473
|
+
new(:AND, new(:>=, l, r.begin), new(r.exclude_end? ? :< : :<=, l, r.end))
|
474
|
+
when Array, ::Sequel::Dataset, SQLArray
|
475
|
+
new(:IN, l, r)
|
476
|
+
when NegativeBooleanConstant
|
477
|
+
new(:"IS NOT", l, r.constant)
|
478
|
+
when BooleanConstant
|
479
|
+
new(:IS, l, r.constant)
|
480
|
+
when NilClass, TrueClass, FalseClass
|
481
|
+
new(:IS, l, r)
|
482
|
+
when Regexp
|
483
|
+
StringExpression.like(l, r)
|
484
|
+
else
|
485
|
+
new(:'=', l, r)
|
486
|
+
end
|
487
|
+
negate ? invert(ce) : ce
|
488
|
+
end
|
489
|
+
pairs.length == 1 ? pairs.at(0) : new(op, *pairs)
|
490
|
+
end
|
491
|
+
|
492
|
+
# Invert the expression, if possible. If the expression cannot
|
493
|
+
# be inverted, raise an error. An inverted expression should match everything that the
|
494
|
+
# uninverted expression did not match, and vice-versa, except for possible issues with
|
495
|
+
# SQL NULL (i.e. 1 == NULL is NULL and 1 != NULL is also NULL).
|
496
|
+
def self.invert(ce)
|
497
|
+
case ce
|
498
|
+
when BooleanExpression
|
499
|
+
case op = ce.op
|
500
|
+
when :AND, :OR
|
501
|
+
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.collect{|a| BooleanExpression.invert(a)})
|
502
|
+
else
|
503
|
+
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
|
504
|
+
end
|
505
|
+
when StringExpression, NumericExpression
|
506
|
+
raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
507
|
+
else
|
508
|
+
BooleanExpression.new(:NOT, ce)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
# Represents an SQL CASE expression, used for conditions.
|
514
|
+
class CaseExpression < GenericExpression
|
515
|
+
# An array of all two pairs with the first element specifying the
|
516
|
+
# condition and the second element specifying the result.
|
517
|
+
attr_reader :conditions
|
518
|
+
|
519
|
+
# The default value if no conditions are true
|
520
|
+
attr_reader :default
|
521
|
+
|
522
|
+
# The expression to test the conditions against
|
523
|
+
attr_reader :expression
|
524
|
+
|
525
|
+
# Create an object with the given conditions and
|
526
|
+
# default value.
|
527
|
+
def initialize(conditions, default, expression = nil)
|
528
|
+
raise(Sequel::Error, 'CaseExpression conditions must be a hash or array of all two pairs') unless Sequel.condition_specifier?(conditions)
|
529
|
+
@conditions, @default, @expression = conditions.to_a, default, expression
|
530
|
+
end
|
531
|
+
|
532
|
+
to_s_method :case_expression_sql
|
533
|
+
end
|
534
|
+
|
535
|
+
# Represents a cast of an SQL expression to a specific type.
|
536
|
+
class Cast < GenericExpression
|
537
|
+
# The expression to cast
|
538
|
+
attr_reader :expr
|
539
|
+
|
540
|
+
# The type to which to cast the expression
|
541
|
+
attr_reader :type
|
542
|
+
|
543
|
+
# Set the attributes to the given arguments
|
544
|
+
def initialize(expr, type)
|
545
|
+
@expr = expr
|
546
|
+
@type = type
|
547
|
+
end
|
548
|
+
|
549
|
+
to_s_method :cast_sql, '@expr, @type'
|
550
|
+
end
|
551
|
+
|
552
|
+
# Represents all columns in a given table, table.* in SQL
|
553
|
+
class ColumnAll < Expression
|
554
|
+
# The table containing the columns being selected
|
555
|
+
attr_reader :table
|
556
|
+
|
557
|
+
# Create an object with the given table
|
558
|
+
def initialize(table)
|
559
|
+
@table = table
|
560
|
+
end
|
561
|
+
|
562
|
+
# ColumnAll expressions are considered equivalent if they
|
563
|
+
# have the same class and string representation
|
564
|
+
def ==(x)
|
565
|
+
x.class == self.class and @table == x.table
|
566
|
+
end
|
567
|
+
|
568
|
+
to_s_method :column_all_sql
|
569
|
+
end
|
570
|
+
|
571
|
+
class ComplexExpression
|
572
|
+
include AliasMethods
|
573
|
+
include CastMethods
|
574
|
+
include OrderMethods
|
575
|
+
include SubscriptMethods
|
576
|
+
end
|
577
|
+
|
578
|
+
# Represents constants or psuedo-constants (e.g. CURRENT_DATE) in SQL.
|
579
|
+
class Constant < GenericExpression
|
580
|
+
# Create an object with the given table
|
581
|
+
def initialize(constant)
|
582
|
+
@constant = constant
|
583
|
+
end
|
584
|
+
|
585
|
+
to_s_method :constant_sql, '@constant'
|
586
|
+
end
|
587
|
+
|
588
|
+
# Represents boolean constants such as NULL, NOTNULL, TRUE, and FALSE.
|
589
|
+
class BooleanConstant < Constant
|
590
|
+
# The underlying constant related for this object.
|
591
|
+
attr_reader :constant
|
592
|
+
|
593
|
+
to_s_method :boolean_constant_sql, '@constant'
|
594
|
+
end
|
595
|
+
|
596
|
+
# Represents inverse boolean constants (currently only NOTNULL). A
|
597
|
+
# special class to allow for special behavior.
|
598
|
+
class NegativeBooleanConstant < BooleanConstant
|
599
|
+
to_s_method :negative_boolean_constant_sql, '@constant'
|
600
|
+
end
|
601
|
+
|
602
|
+
# Holds default generic constants that can be referenced. These
|
603
|
+
# are included in the Sequel top level module and are also available
|
604
|
+
# in this module which can be required at the top level to get
|
605
|
+
# direct access to the constants.
|
606
|
+
module Constants
|
607
|
+
CURRENT_DATE = Constant.new(:CURRENT_DATE)
|
608
|
+
CURRENT_TIME = Constant.new(:CURRENT_TIME)
|
609
|
+
CURRENT_TIMESTAMP = Constant.new(:CURRENT_TIMESTAMP)
|
610
|
+
SQLTRUE = TRUE = BooleanConstant.new(true)
|
611
|
+
SQLFALSE = FALSE = BooleanConstant.new(false)
|
612
|
+
NULL = BooleanConstant.new(nil)
|
613
|
+
NOTNULL = NegativeBooleanConstant.new(nil)
|
614
|
+
end
|
615
|
+
|
616
|
+
# Represents an SQL function call.
|
617
|
+
class Function < GenericExpression
|
618
|
+
# The array of arguments to pass to the function (may be blank)
|
619
|
+
attr_reader :args
|
620
|
+
|
621
|
+
# The SQL function to call
|
622
|
+
attr_reader :f
|
623
|
+
|
624
|
+
# Set the attributes to the given arguments
|
625
|
+
def initialize(f, *args)
|
626
|
+
@f, @args = f, args
|
627
|
+
end
|
628
|
+
|
629
|
+
# Functions are considered equivalent if they
|
630
|
+
# have the same class, function, and arguments.
|
631
|
+
def ==(x)
|
632
|
+
x.class == self.class && @f == x.f && @args == x.args
|
633
|
+
end
|
634
|
+
|
635
|
+
to_s_method :function_sql
|
636
|
+
end
|
637
|
+
|
638
|
+
class GenericExpression
|
639
|
+
include AliasMethods
|
640
|
+
include BooleanMethods
|
641
|
+
include CastMethods
|
642
|
+
include ComplexExpressionMethods
|
643
|
+
include InequalityMethods
|
644
|
+
include NumericMethods
|
645
|
+
include OrderMethods
|
646
|
+
include StringMethods
|
647
|
+
include SubscriptMethods
|
648
|
+
end
|
649
|
+
|
650
|
+
# Represents an identifier (column or table). Can be used
|
651
|
+
# to specify a Symbol with multiple underscores should not be
|
652
|
+
# split, or for creating an identifier without using a symbol.
|
653
|
+
class Identifier < GenericExpression
|
654
|
+
include QualifyingMethods
|
655
|
+
|
656
|
+
# The table and column to reference
|
657
|
+
attr_reader :value
|
658
|
+
|
659
|
+
# Set the value to the given argument
|
660
|
+
def initialize(value)
|
661
|
+
@value = value
|
662
|
+
end
|
663
|
+
|
664
|
+
to_s_method :quote_identifier, '@value'
|
665
|
+
end
|
666
|
+
|
667
|
+
# Represents an SQL JOIN clause, used for joining tables.
|
668
|
+
class JoinClause < Expression
|
669
|
+
# The type of join to do
|
670
|
+
attr_reader :join_type
|
671
|
+
|
672
|
+
# The actual table to join
|
673
|
+
attr_reader :table
|
674
|
+
|
675
|
+
# The table alias to use for the join, if any
|
676
|
+
attr_reader :table_alias
|
677
|
+
|
678
|
+
# Create an object with the given join_type, table, and table alias
|
679
|
+
def initialize(join_type, table, table_alias = nil)
|
680
|
+
@join_type, @table, @table_alias = join_type, table, table_alias
|
681
|
+
end
|
682
|
+
|
683
|
+
to_s_method :join_clause_sql
|
684
|
+
end
|
685
|
+
|
686
|
+
# Represents an SQL JOIN table ON conditions clause.
|
687
|
+
class JoinOnClause < JoinClause
|
688
|
+
# The conditions for the join
|
689
|
+
attr_reader :on
|
690
|
+
|
691
|
+
# Create an object with the ON conditions and call super with the
|
692
|
+
# remaining args.
|
693
|
+
def initialize(on, *args)
|
694
|
+
@on = on
|
695
|
+
super(*args)
|
696
|
+
end
|
697
|
+
|
698
|
+
to_s_method :join_on_clause_sql
|
699
|
+
end
|
700
|
+
|
701
|
+
# Represents an SQL JOIN table USING (columns) clause.
|
702
|
+
class JoinUsingClause < JoinClause
|
703
|
+
# The columns that appear in both tables that should be equal
|
704
|
+
# for the conditions to match.
|
705
|
+
attr_reader :using
|
706
|
+
|
707
|
+
# Create an object with the given USING conditions and call super
|
708
|
+
# with the remaining args.
|
709
|
+
def initialize(using, *args)
|
710
|
+
@using = using
|
711
|
+
super(*args)
|
712
|
+
end
|
713
|
+
|
714
|
+
to_s_method :join_using_clause_sql
|
715
|
+
end
|
716
|
+
|
717
|
+
# Represents a literal string with placeholders and arguments.
|
718
|
+
# This is necessary to ensure delayed literalization of the arguments
|
719
|
+
# required for the prepared statement support
|
720
|
+
class PlaceholderLiteralString < Expression
|
721
|
+
# The arguments that will be subsituted into the placeholders.
|
722
|
+
# Either an array of unnamed placeholders (which will be substituted in
|
723
|
+
# order for ? characters), or a hash of named placeholders (which will be
|
724
|
+
# substituted for :key phrases).
|
725
|
+
attr_reader :args
|
726
|
+
|
727
|
+
# The literal string containing placeholders
|
728
|
+
attr_reader :str
|
729
|
+
|
730
|
+
# Whether to surround the expression with parantheses
|
731
|
+
attr_reader :parens
|
732
|
+
|
733
|
+
# Create an object with the given string, placeholder arguments, and parens flag.
|
734
|
+
def initialize(str, args, parens=false)
|
735
|
+
@str = str
|
736
|
+
@args = args.is_a?(Array) && args.length == 1 && (v = args.at(0)).is_a?(Hash) ? v : args
|
737
|
+
@parens = parens
|
738
|
+
end
|
739
|
+
|
740
|
+
to_s_method :placeholder_literal_string_sql
|
741
|
+
end
|
742
|
+
|
743
|
+
# Subclass of ComplexExpression where the expression results
|
744
|
+
# in a numeric value in SQL.
|
745
|
+
class NumericExpression < ComplexExpression
|
746
|
+
include BitwiseMethods
|
747
|
+
include NumericMethods
|
748
|
+
include InequalityMethods
|
749
|
+
include NoBooleanInputMethods
|
750
|
+
end
|
751
|
+
|
752
|
+
# Represents a column/expression to order the result set by.
|
753
|
+
class OrderedExpression < Expression
|
754
|
+
# The expression to order the result set by.
|
755
|
+
attr_reader :expression
|
756
|
+
|
757
|
+
# Whether the expression should order the result set in a descending manner
|
758
|
+
attr_reader :descending
|
759
|
+
|
760
|
+
# Set the expression and descending attributes to the given values.
|
761
|
+
def initialize(expression, descending = true)
|
762
|
+
@expression, @descending = expression, descending
|
763
|
+
end
|
764
|
+
|
765
|
+
# Return a copy that is ASC
|
766
|
+
def asc
|
767
|
+
OrderedExpression.new(@expression, false)
|
768
|
+
end
|
769
|
+
|
770
|
+
# Return a copy that is DESC
|
771
|
+
def desc
|
772
|
+
OrderedExpression.new(@expression)
|
773
|
+
end
|
774
|
+
|
775
|
+
# Return an inverted expression, changing ASC to DESC and vice versa
|
776
|
+
def invert
|
777
|
+
OrderedExpression.new(@expression, !@descending)
|
778
|
+
end
|
779
|
+
|
780
|
+
to_s_method :ordered_expression_sql
|
781
|
+
end
|
782
|
+
|
783
|
+
# Represents a qualified (column with table or table with schema) reference.
|
784
|
+
class QualifiedIdentifier < GenericExpression
|
785
|
+
include QualifyingMethods
|
786
|
+
|
787
|
+
# The column to reference
|
788
|
+
attr_reader :column
|
789
|
+
|
790
|
+
# The table to reference
|
791
|
+
attr_reader :table
|
792
|
+
|
793
|
+
# Set the table and column to the given arguments
|
794
|
+
def initialize(table, column)
|
795
|
+
@table, @column = table, column
|
796
|
+
end
|
797
|
+
|
798
|
+
to_s_method :qualified_identifier_sql
|
799
|
+
end
|
800
|
+
|
801
|
+
# Subclass of ComplexExpression where the expression results
|
802
|
+
# in a text/string/varchar value in SQL.
|
803
|
+
class StringExpression < ComplexExpression
|
804
|
+
include StringMethods
|
805
|
+
include StringConcatenationMethods
|
806
|
+
include InequalityMethods
|
807
|
+
include NoBooleanInputMethods
|
808
|
+
|
809
|
+
# Map of [regexp, case_insenstive] to ComplexExpression operator
|
810
|
+
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
811
|
+
|
812
|
+
# Creates a SQL pattern match exprssion. left (l) is the SQL string we
|
813
|
+
# are matching against, and ces are the patterns we are matching.
|
814
|
+
# The match succeeds if any of the patterns match (SQL OR). Patterns
|
815
|
+
# can be given as strings or regular expressions. Strings will cause
|
816
|
+
# the SQL LIKE operator to be used, and should be supported by most
|
817
|
+
# databases. Regular expressions will probably only work on MySQL
|
818
|
+
# and PostgreSQL, and SQL regular expression syntax is not fully compatible
|
819
|
+
# with ruby regular expression syntax, so be careful if using regular
|
820
|
+
# expressions.
|
821
|
+
#
|
822
|
+
# The pattern match will be case insensitive if the last argument is a hash
|
823
|
+
# with a key of :case_insensitive that is not false or nil. Also,
|
824
|
+
# if a case insensitive regular expression is used (//i), that particular
|
825
|
+
# pattern which will always be case insensitive.
|
826
|
+
def self.like(l, *ces)
|
827
|
+
l, lre, lci = like_element(l)
|
828
|
+
lci = (ces.last.is_a?(Hash) ? ces.pop : {})[:case_insensitive] ? true : lci
|
829
|
+
ces.collect! do |ce|
|
830
|
+
r, rre, rci = like_element(ce)
|
831
|
+
BooleanExpression.new(LIKE_MAP[[lre||rre, lci||rci]], l, r)
|
832
|
+
end
|
833
|
+
ces.length == 1 ? ces.at(0) : BooleanExpression.new(:OR, *ces)
|
834
|
+
end
|
835
|
+
|
836
|
+
# An array of three parts:
|
837
|
+
# * The object to use
|
838
|
+
# * Whether it is a regular expression
|
839
|
+
# * Whether it is case insensitive
|
840
|
+
def self.like_element(re) # :nodoc:
|
841
|
+
if re.is_a?(Regexp)
|
842
|
+
[re.source, true, re.casefold?]
|
843
|
+
else
|
844
|
+
[re, false, false]
|
845
|
+
end
|
846
|
+
end
|
847
|
+
private_class_method :like_element
|
848
|
+
end
|
849
|
+
|
850
|
+
# Represents an SQL array. Added so it is possible to deal with a
|
851
|
+
# ruby array of all two pairs as an SQL array instead of an ordered
|
852
|
+
# hash-like conditions specifier.
|
853
|
+
class SQLArray < Expression
|
854
|
+
# The array of objects this SQLArray wraps
|
855
|
+
attr_reader :array
|
856
|
+
alias to_a array
|
857
|
+
|
858
|
+
# Create an object with the given array.
|
859
|
+
def initialize(array)
|
860
|
+
@array = array
|
861
|
+
end
|
862
|
+
|
863
|
+
to_s_method :array_sql, '@array'
|
864
|
+
end
|
865
|
+
|
866
|
+
# Represents an SQL array access, with multiple possible arguments.
|
867
|
+
class Subscript < GenericExpression
|
868
|
+
# The SQL array column
|
869
|
+
attr_reader :f
|
870
|
+
|
871
|
+
# The array of subscripts to use (should be an array of numbers)
|
872
|
+
attr_reader :sub
|
873
|
+
|
874
|
+
# Set the array column and subscripts to the given arguments
|
875
|
+
def initialize(f, sub)
|
876
|
+
@f, @sub = f, sub
|
877
|
+
end
|
878
|
+
|
879
|
+
# Create a new subscript appending the given subscript(s)
|
880
|
+
# the the current array of subscripts.
|
881
|
+
def |(sub)
|
882
|
+
Subscript.new(@f, @sub + Array(sub))
|
883
|
+
end
|
884
|
+
|
885
|
+
to_s_method :subscript_sql
|
886
|
+
end
|
887
|
+
|
888
|
+
# The purpose of this class is to allow the easy creation of SQL identifiers and functions
|
889
|
+
# without relying on methods defined on Symbol. This is useful if another library defines
|
890
|
+
# the methods defined by Sequel, or if you are running on ruby 1.9.
|
891
|
+
#
|
892
|
+
# An instance of this class is yielded to the block supplied to filter, order, and select.
|
893
|
+
# If the block doesn't take an argument, the block is instance_evaled in the context of
|
894
|
+
# a new instance of this class.
|
895
|
+
#
|
896
|
+
# VirtualRow uses method_missing to return Identifiers, QualifiedIdentifiers, Functions, or WindowFunctions,
|
897
|
+
# depending on how it is called. If a block is not given, creates one of the following objects:
|
898
|
+
# * Function - returned if any arguments are supplied, using the method name
|
899
|
+
# as the function name, and the arguments as the function arguments.
|
900
|
+
# * QualifiedIdentifier - returned if the method name contains __, with the
|
901
|
+
# table being the part before __, and the column being the part after.
|
902
|
+
# * Identifier - returned otherwise, using the method name.
|
903
|
+
# If a block is given, it returns either a Function or WindowFunction, depending on the first
|
904
|
+
# argument to the method. Note that the block is currently not called by the code, though
|
905
|
+
# this may change in a future version. If the first argument is:
|
906
|
+
# * no arguments given - uses a Function with no arguments.
|
907
|
+
# * :* - uses a Function with a literal wildcard argument (*), mostly useful for COUNT.
|
908
|
+
# * :distinct - uses a Function that prepends DISTINCT to the rest of the arguments, mostly
|
909
|
+
# useful for aggregate functions.
|
910
|
+
# * :over - uses a WindowFunction. If a second argument is provided, it should be a hash
|
911
|
+
# of options which are passed to Window (e.g. :window, :partition, :order, :frame). The
|
912
|
+
# arguments to the function itself should be specified as :*=>true for a wildcard, or via
|
913
|
+
# the :args option.
|
914
|
+
#
|
915
|
+
# Examples:
|
916
|
+
#
|
917
|
+
# ds = DB[:t]
|
918
|
+
# # Argument yielded to block
|
919
|
+
# ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
|
920
|
+
# # Block without argument (instance_eval)
|
921
|
+
# ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
|
922
|
+
# # Qualified identifiers
|
923
|
+
# ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
|
924
|
+
# # Functions
|
925
|
+
# ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
|
926
|
+
# ds.select{version{}} # SELECT version() FROM t
|
927
|
+
# ds.select{count(:*){}} # SELECT count(*) FROM t
|
928
|
+
# ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
|
929
|
+
# # Window Functions
|
930
|
+
# ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
|
931
|
+
# ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
|
932
|
+
# ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
|
933
|
+
class VirtualRow < BasicObject
|
934
|
+
WILDCARD = LiteralString.new('*').freeze
|
935
|
+
QUESTION_MARK = LiteralString.new('?').freeze
|
936
|
+
COMMA_SEPARATOR = LiteralString.new(', ').freeze
|
937
|
+
DOUBLE_UNDERSCORE = '__'.freeze
|
938
|
+
|
939
|
+
# Return Identifiers, QualifiedIdentifiers, Functions, or WindowFunctions, depending
|
940
|
+
# on arguments and whether a block is provided. Does not currently call the block.
|
941
|
+
# See the class level documentation.
|
942
|
+
def method_missing(m, *args, &block)
|
943
|
+
if block
|
944
|
+
if args.empty?
|
945
|
+
Function.new(m)
|
946
|
+
else
|
947
|
+
case arg = args.shift
|
948
|
+
when :*
|
949
|
+
Function.new(m, WILDCARD)
|
950
|
+
when :distinct
|
951
|
+
Function.new(m, PlaceholderLiteralString.new("DISTINCT #{args.map{QUESTION_MARK}.join(COMMA_SEPARATOR)}", args))
|
952
|
+
when :over
|
953
|
+
opts = args.shift || {}
|
954
|
+
fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
|
955
|
+
WindowFunction.new(Function.new(m, *fun_args), Window.new(opts))
|
956
|
+
else
|
957
|
+
raise Error, 'unsupported VirtualRow method argument used with block'
|
958
|
+
end
|
959
|
+
end
|
960
|
+
elsif args.empty?
|
961
|
+
table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
|
962
|
+
column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
|
963
|
+
else
|
964
|
+
Function.new(m, *args)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
end
|
968
|
+
|
969
|
+
# A window is part of a window function specifying the window over which the function operates.
|
970
|
+
# It is separated from the WindowFunction class because it also can be used separately on
|
971
|
+
# some databases.
|
972
|
+
class Window < Expression
|
973
|
+
# The options for this window. Options currently used are:
|
974
|
+
# * :frame - if specified, should be :all or :rows. :all always operates over all rows in the
|
975
|
+
# partition, while :rows excludes the current row's later peers. The default is to include
|
976
|
+
# all previous rows in the partition up to the current row's last peer.
|
977
|
+
# * :order - order on the column(s) given
|
978
|
+
# * :partition - partition/group on the column(s) given
|
979
|
+
# * :window - base results on a previously specified named window
|
980
|
+
attr_reader :opts
|
981
|
+
|
982
|
+
# Set the options to the options given
|
983
|
+
def initialize(opts={})
|
984
|
+
@opts = opts
|
985
|
+
end
|
986
|
+
|
987
|
+
to_s_method :window_sql, '@opts'
|
988
|
+
end
|
989
|
+
|
990
|
+
# A WindowFunction is a grouping of a function with a window over which it operates.
|
991
|
+
class WindowFunction < GenericExpression
|
992
|
+
# The function to use, should be an SQL::Function.
|
993
|
+
attr_reader :function
|
994
|
+
|
995
|
+
# The window to use, should be an SQL::Window.
|
996
|
+
attr_reader :window
|
997
|
+
|
998
|
+
# Set the function and window.
|
999
|
+
def initialize(function, window)
|
1000
|
+
@function, @window = function, window
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
to_s_method :window_function_sql, '@function, @window'
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# LiteralString is used to represent literal SQL expressions. A
|
1008
|
+
# LiteralString is copied verbatim into an SQL statement. Instances of
|
1009
|
+
# LiteralString can be created by calling String#lit.
|
1010
|
+
class LiteralString
|
1011
|
+
include SQL::OrderMethods
|
1012
|
+
include SQL::ComplexExpressionMethods
|
1013
|
+
include SQL::BooleanMethods
|
1014
|
+
include SQL::NumericMethods
|
1015
|
+
include SQL::StringMethods
|
1016
|
+
include SQL::InequalityMethods
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
include SQL::Constants
|
1020
|
+
end
|