sequel 3.21.0 → 3.36.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 +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
data/lib/sequel/sql.rb
CHANGED
|
@@ -38,6 +38,16 @@ module Sequel
|
|
|
38
38
|
class LiteralString < ::String
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# Time subclass that gets literalized with only the time value, so it operates
|
|
42
|
+
# like a standard SQL time type.
|
|
43
|
+
class SQLTime < ::Time
|
|
44
|
+
# Create a new SQLTime instance given an hour, minute, and second.
|
|
45
|
+
def self.create(hour, minute, second, usec = 0)
|
|
46
|
+
t = now
|
|
47
|
+
local(t.year, t.month, t.day, hour, minute, second, usec)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
41
51
|
# The SQL module holds classes whose instances represent SQL fragments.
|
|
42
52
|
# It also holds modules that are included in core ruby classes that
|
|
43
53
|
# make Sequel a friendly DSL.
|
|
@@ -69,7 +79,8 @@ module Sequel
|
|
|
69
79
|
# the method provided on the dataset with args as the argument (self by default).
|
|
70
80
|
# Used to DRY up some code.
|
|
71
81
|
def self.to_s_method(meth, args=:self) # :nodoc:
|
|
72
|
-
class_eval("def to_s(ds)
|
|
82
|
+
class_eval("def to_s(ds) ds.#{meth}(#{args}) end", __FILE__, __LINE__)
|
|
83
|
+
class_eval("def to_s_append(ds, sql) ds.#{meth}_append(sql, #{args}) end", __FILE__, __LINE__)
|
|
73
84
|
end
|
|
74
85
|
private_class_method :to_s_method
|
|
75
86
|
|
|
@@ -127,7 +138,7 @@ module Sequel
|
|
|
127
138
|
MATHEMATICAL_OPERATORS = [:+, :-, :/, :*]
|
|
128
139
|
|
|
129
140
|
# Bitwise mathematical operators used in +NumericMethods+
|
|
130
|
-
BITWISE_OPERATORS = [:&, :|, :^, :<<,
|
|
141
|
+
BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>, :%]
|
|
131
142
|
|
|
132
143
|
# Inequality operators used in +InequalityMethods+
|
|
133
144
|
INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
|
|
@@ -144,14 +155,17 @@ module Sequel
|
|
|
144
155
|
# Operator symbols that take exactly two arguments
|
|
145
156
|
TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
|
|
146
157
|
:~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
|
|
147
|
-
INEQUALITY_OPERATORS +
|
|
158
|
+
INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
|
|
148
159
|
|
|
149
160
|
# Operator symbols that take one or more arguments
|
|
150
|
-
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
|
|
161
|
+
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
|
|
151
162
|
|
|
152
163
|
# Operator symbols that take only a single argument
|
|
153
164
|
ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
|
|
154
165
|
|
|
166
|
+
# Custom expressions that may have different syntax on different databases
|
|
167
|
+
CUSTOM_EXPRESSIONS = [:extract]
|
|
168
|
+
|
|
155
169
|
# An array of args for this object
|
|
156
170
|
attr_reader :args
|
|
157
171
|
|
|
@@ -180,6 +194,8 @@ module Sequel
|
|
|
180
194
|
args[1] = orig_args[1] if IN_OPERATORS.include?(op)
|
|
181
195
|
when *ONE_ARITY_OPERATORS
|
|
182
196
|
raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
|
|
197
|
+
when *CUSTOM_EXPRESSIONS
|
|
198
|
+
# nothing
|
|
183
199
|
else
|
|
184
200
|
raise(Error, "Invalid operator #{op}")
|
|
185
201
|
end
|
|
@@ -219,14 +235,7 @@ module Sequel
|
|
|
219
235
|
# ~:a.sql_number # ~"a"
|
|
220
236
|
module BitwiseMethods
|
|
221
237
|
ComplexExpression::BITWISE_OPERATORS.each do |o|
|
|
222
|
-
|
|
223
|
-
case ce
|
|
224
|
-
when BooleanExpression, StringExpression
|
|
225
|
-
raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
|
|
226
|
-
else
|
|
227
|
-
NumericExpression.new(o, self, ce)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
238
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
|
230
239
|
end
|
|
231
240
|
|
|
232
241
|
# Do the bitwise compliment of the self
|
|
@@ -244,16 +253,24 @@ module Sequel
|
|
|
244
253
|
# :a & :b # "a" AND "b"
|
|
245
254
|
# :a | :b # "a" OR "b"
|
|
246
255
|
# ~:a # NOT "a"
|
|
256
|
+
#
|
|
257
|
+
# One exception to this is when a NumericExpression or Integer is the argument
|
|
258
|
+
# to & or |, in which case a bitwise method will be used:
|
|
259
|
+
#
|
|
260
|
+
# :a & 1 # "a" & 1
|
|
261
|
+
# :a | (:b + 1) # "a" | ("b" + 1)
|
|
247
262
|
module BooleanMethods
|
|
248
263
|
ComplexExpression::BOOLEAN_OPERATOR_METHODS.each do |m, o|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
264
|
+
module_eval(<<-END, __FILE__, __LINE__+1)
|
|
265
|
+
def #{m}(o)
|
|
266
|
+
case o
|
|
267
|
+
when NumericExpression, Integer
|
|
268
|
+
NumericExpression.new(#{m.inspect}, self, o)
|
|
269
|
+
else
|
|
270
|
+
BooleanExpression.new(#{o.inspect}, self, o)
|
|
271
|
+
end
|
|
255
272
|
end
|
|
256
|
-
|
|
273
|
+
END
|
|
257
274
|
end
|
|
258
275
|
|
|
259
276
|
# Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
|
|
@@ -264,6 +281,293 @@ module Sequel
|
|
|
264
281
|
end
|
|
265
282
|
end
|
|
266
283
|
|
|
284
|
+
# These methods are designed as replacements for the core extensions, so that
|
|
285
|
+
# Sequel is still easy to use if the core extensions are not enabled.
|
|
286
|
+
module Builders
|
|
287
|
+
# Create an SQL::AliasedExpression for the given expression and alias.
|
|
288
|
+
#
|
|
289
|
+
# Sequel.as(:column, :alias) # "column" AS "alias"
|
|
290
|
+
def as(exp, aliaz)
|
|
291
|
+
SQL::AliasedExpression.new(exp, aliaz)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Order the given argument ascending.
|
|
295
|
+
# Options:
|
|
296
|
+
#
|
|
297
|
+
# :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
|
|
298
|
+
# before other values), or :last to use NULLS LAST (so NULL values
|
|
299
|
+
# are ordered after other values).
|
|
300
|
+
#
|
|
301
|
+
# Sequel.asc(:a) # a ASC
|
|
302
|
+
# Sequel.asc(:b, :nulls=>:last) # b ASC NULLS LAST
|
|
303
|
+
def asc(arg, opts={})
|
|
304
|
+
SQL::OrderedExpression.new(arg, false, opts)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Return an <tt>SQL::Blob</tt> that holds the same data as this string.
|
|
308
|
+
# Blobs provide proper escaping of binary data. If given a blob, returns it
|
|
309
|
+
# directly.
|
|
310
|
+
def blob(s)
|
|
311
|
+
if s.is_a?(SQL::Blob)
|
|
312
|
+
s
|
|
313
|
+
else
|
|
314
|
+
SQL::Blob.new(s)
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Return an <tt>SQL::CaseExpression</tt> created with the given arguments.
|
|
319
|
+
#
|
|
320
|
+
# Sequel.case([[{:a=>[2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
|
|
321
|
+
# Sequel.case({:a=>1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
|
|
322
|
+
def case(*args) # core_sql ignore
|
|
323
|
+
SQL::CaseExpression.new(*args)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Cast the reciever to the given SQL type. You can specify a ruby class as a type,
|
|
327
|
+
# and it is handled similarly to using a database independent type in the schema methods.
|
|
328
|
+
#
|
|
329
|
+
# Sequel.cast(:a, :integer) # CAST(a AS integer)
|
|
330
|
+
# Sequel.cast(:a, String) # CAST(a AS varchar(255))
|
|
331
|
+
def cast(arg, sql_type)
|
|
332
|
+
SQL::Cast.new(arg, sql_type)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Cast the reciever to the given SQL type (or the database's default Integer type if none given),
|
|
336
|
+
# and return the result as a +NumericExpression+, so you can use the bitwise operators
|
|
337
|
+
# on the result.
|
|
338
|
+
#
|
|
339
|
+
# Sequel.cast_numeric(:a) # CAST(a AS integer)
|
|
340
|
+
# Sequel.cast_numeric(:a, Float) # CAST(a AS double precision)
|
|
341
|
+
def cast_numeric(arg, sql_type = nil)
|
|
342
|
+
cast(arg, sql_type || Integer).sql_number
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Cast the reciever to the given SQL type (or the database's default String type if none given),
|
|
346
|
+
# and return the result as a +StringExpression+, so you can use +
|
|
347
|
+
# directly on the result for SQL string concatenation.
|
|
348
|
+
#
|
|
349
|
+
# Sequel.cast_string(:a) # CAST(a AS varchar(255))
|
|
350
|
+
# Sequel.cast_string(:a, :text) # CAST(a AS text)
|
|
351
|
+
def cast_string(arg, sql_type = nil)
|
|
352
|
+
cast(arg, sql_type || String).sql_string
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Order the given argument descending.
|
|
356
|
+
# Options:
|
|
357
|
+
#
|
|
358
|
+
# :nulls :: Set to :first to use NULLS FIRST (so NULL values are ordered
|
|
359
|
+
# before other values), or :last to use NULLS LAST (so NULL values
|
|
360
|
+
# are ordered after other values).
|
|
361
|
+
#
|
|
362
|
+
# Sequel.desc(:a) # b DESC
|
|
363
|
+
# Sequel.desc(:b, :nulls=>:first) # b DESC NULLS FIRST
|
|
364
|
+
def desc(arg, opts={})
|
|
365
|
+
SQL::OrderedExpression.new(arg, true, opts)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Wraps the given object in an appropriate Sequel wrapper.
|
|
369
|
+
# If the given object is already a Sequel object, return it directly.
|
|
370
|
+
# For condition specifiers (hashes and arrays of two pairs), true, and false,
|
|
371
|
+
# return a boolean expressions. For numeric objects, return a numeric
|
|
372
|
+
# expression. For strings, return a string expression. For procs or when
|
|
373
|
+
# the method is passed a block, evaluate it as a virtual row and wrap it
|
|
374
|
+
# appropriately. In all other cases, use a generic wrapper.
|
|
375
|
+
#
|
|
376
|
+
# This method allows you to construct SQL expressions that are difficult
|
|
377
|
+
# to construct via other methods. For example:
|
|
378
|
+
#
|
|
379
|
+
# Sequel.expr(1) - :a # SQL: (1 - a)
|
|
380
|
+
def expr(arg=(no_arg=true), &block)
|
|
381
|
+
if block_given?
|
|
382
|
+
if no_arg
|
|
383
|
+
return expr(block)
|
|
384
|
+
else
|
|
385
|
+
raise Error, 'cannot provide both an argument and a block to Sequel.expr'
|
|
386
|
+
end
|
|
387
|
+
elsif no_arg
|
|
388
|
+
raise Error, 'must provide either an argument or a block to Sequel.expr'
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
case arg
|
|
392
|
+
when SQL::Expression, LiteralString, SQL::Blob
|
|
393
|
+
arg
|
|
394
|
+
when Hash
|
|
395
|
+
SQL::BooleanExpression.from_value_pairs(arg, :AND)
|
|
396
|
+
when Array
|
|
397
|
+
if condition_specifier?(arg)
|
|
398
|
+
SQL::BooleanExpression.from_value_pairs(arg, :AND)
|
|
399
|
+
else
|
|
400
|
+
SQL::Wrapper.new(arg)
|
|
401
|
+
end
|
|
402
|
+
when Numeric
|
|
403
|
+
SQL::NumericExpression.new(:NOOP, arg)
|
|
404
|
+
when String
|
|
405
|
+
SQL::StringExpression.new(:NOOP, arg)
|
|
406
|
+
when TrueClass, FalseClass
|
|
407
|
+
SQL::BooleanExpression.new(:NOOP, arg)
|
|
408
|
+
when Proc
|
|
409
|
+
expr(virtual_row(&arg))
|
|
410
|
+
else
|
|
411
|
+
SQL::Wrapper.new(arg)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Extract a datetime_part (e.g. year, month) from the given
|
|
416
|
+
# expression:
|
|
417
|
+
#
|
|
418
|
+
# Sequel.extract(:year, :date) # extract(year FROM "date")
|
|
419
|
+
def extract(datetime_part, exp)
|
|
420
|
+
SQL::NumericExpression.new(:extract, datetime_part, exp)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Returns a <tt>Sequel::SQL::Function</tt> with the function name
|
|
424
|
+
# and the given arguments.
|
|
425
|
+
#
|
|
426
|
+
# Sequel.function(:now) # SQL: now()
|
|
427
|
+
# Sequel.function(:substr, :a, 1) # SQL: substr(a, 1)
|
|
428
|
+
def function(name, *args)
|
|
429
|
+
SQL::Function.new(name, *args)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Return the argument wrapped as an <tt>SQL::Identifier</tt>.
|
|
433
|
+
#
|
|
434
|
+
# Sequel.identifier(:a__b) # "a__b"
|
|
435
|
+
def identifier(name)
|
|
436
|
+
SQL::Identifier.new(name)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
|
|
440
|
+
# concatenation of the given array's elements. If an argument is passed,
|
|
441
|
+
# it is used in between each element of the array in the SQL
|
|
442
|
+
# concatenation.
|
|
443
|
+
#
|
|
444
|
+
# Sequel.join([:a]) # SQL: a
|
|
445
|
+
# Sequel.join([:a, :b]) # SQL: a || b
|
|
446
|
+
# Sequel.join([:a, 'b']) # SQL: a || 'b'
|
|
447
|
+
# Sequel.join(['a', :b], ' ') # SQL: 'a' || ' ' || b
|
|
448
|
+
def join(args, joiner=nil)
|
|
449
|
+
raise Error, 'argument to Sequel.join must be an array' unless args.is_a?(Array)
|
|
450
|
+
if joiner
|
|
451
|
+
args = args.zip([joiner]*args.length).flatten
|
|
452
|
+
args.pop
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
return SQL::StringExpression.new(:NOOP, '') if args.empty?
|
|
456
|
+
|
|
457
|
+
args = args.map do |a|
|
|
458
|
+
case a
|
|
459
|
+
when Symbol, ::Sequel::SQL::Expression, ::Sequel::LiteralString, TrueClass, FalseClass, NilClass
|
|
460
|
+
a
|
|
461
|
+
else
|
|
462
|
+
a.to_s
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
SQL::StringExpression.new(:'||', *args)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Create a <tt>BooleanExpression</tt> case insensitive (if the database supports it) pattern match of the receiver with
|
|
469
|
+
# the given patterns. See <tt>SQL::StringExpression.like</tt>.
|
|
470
|
+
#
|
|
471
|
+
# Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%'
|
|
472
|
+
def ilike(*args)
|
|
473
|
+
SQL::StringExpression.like(*(args << {:case_insensitive=>true}))
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Create a <tt>SQL::BooleanExpression</tt> case sensitive (if the database supports it) pattern match of the receiver with
|
|
477
|
+
# the given patterns. See <tt>SQL::StringExpression.like</tt>.
|
|
478
|
+
#
|
|
479
|
+
# Sequel.like(:a, 'A%') # "a" LIKE 'A%'
|
|
480
|
+
def like(*args)
|
|
481
|
+
SQL::StringExpression.like(*args)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
|
|
485
|
+
# literalization, e.g.:
|
|
486
|
+
#
|
|
487
|
+
# DB[:items].filter(:abc => 'def').sql #=>
|
|
488
|
+
# "SELECT * FROM items WHERE (abc = 'def')"
|
|
489
|
+
#
|
|
490
|
+
# DB[:items].filter(:abc => Sequel.lit('def')).sql #=>
|
|
491
|
+
# "SELECT * FROM items WHERE (abc = def)"
|
|
492
|
+
#
|
|
493
|
+
# You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
|
|
494
|
+
#
|
|
495
|
+
# DB[:items].select{|o| o.count(Sequel.lit('DISTINCT ?', :a))}.sql #=>
|
|
496
|
+
# "SELECT count(DISTINCT a) FROM items"
|
|
497
|
+
def lit(s, *args) # core_sql ignore
|
|
498
|
+
if args.empty?
|
|
499
|
+
if s.is_a?(LiteralString)
|
|
500
|
+
s
|
|
501
|
+
else
|
|
502
|
+
LiteralString.new(s)
|
|
503
|
+
end
|
|
504
|
+
else
|
|
505
|
+
SQL::PlaceholderLiteralString.new(s, args)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
|
|
510
|
+
# specifier, matching none of the conditions.
|
|
511
|
+
#
|
|
512
|
+
# Sequel.negate(:a=>true) # SQL: a IS NOT TRUE
|
|
513
|
+
# Sequel.negate([[:a, true]]) # SQL: a IS NOT TRUE
|
|
514
|
+
# Sequel.negate([[:a, 1], [:b, 2]]) # SQL: ((a != 1) AND (b != 2))
|
|
515
|
+
def negate(arg)
|
|
516
|
+
if condition_specifier?(arg)
|
|
517
|
+
SQL::BooleanExpression.from_value_pairs(arg, :AND, true)
|
|
518
|
+
else
|
|
519
|
+
raise Error, 'must pass a conditions specifier to Sequel.negate'
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from the condition
|
|
524
|
+
# specifier, matching any of the conditions.
|
|
525
|
+
#
|
|
526
|
+
# Sequel.or(:a=>true) # SQL: a IS TRUE
|
|
527
|
+
# Sequel.or([[:a, true]]) # SQL: a IS TRUE
|
|
528
|
+
# Sequel.or([[:a, 1], [:b, 2]]) # SQL: ((a = 1) OR (b = 2))
|
|
529
|
+
def or(arg)
|
|
530
|
+
if condition_specifier?(arg)
|
|
531
|
+
SQL::BooleanExpression.from_value_pairs(arg, :OR, false)
|
|
532
|
+
else
|
|
533
|
+
raise Error, 'must pass a conditions specifier to Sequel.or'
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# Create a qualified identifier with the given qualifier and identifier
|
|
538
|
+
#
|
|
539
|
+
# Sequel.qualify(:table, :column) # "table"."column"
|
|
540
|
+
# Sequel.qualify(:schema, :table) # "schema"."table"
|
|
541
|
+
# Sequel.qualify(:table, :column).qualify(:schema) # "schema"."table"."column"
|
|
542
|
+
def qualify(qualifier, identifier)
|
|
543
|
+
SQL::QualifiedIdentifier.new(qualifier, identifier)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Return an <tt>SQL::Subscript</tt> with the given arguments, representing an
|
|
547
|
+
# SQL array access.
|
|
548
|
+
#
|
|
549
|
+
# Sequel.subscript(:array, 1) # array[1]
|
|
550
|
+
# Sequel.subscript(:array, 1, 2) # array[1, 2]
|
|
551
|
+
# Sequel.subscript(:array, [1, 2]) # array[1, 2]
|
|
552
|
+
def subscript(exp, *subs)
|
|
553
|
+
SQL::Subscript.new(exp, subs.flatten)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
|
|
557
|
+
# all two element arrays and you want it treated as an SQL value list (IN predicate)
|
|
558
|
+
# instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
|
|
559
|
+
# this array as a value in a filter, but may be necessary if you are using it as a
|
|
560
|
+
# value with placeholder SQL:
|
|
561
|
+
#
|
|
562
|
+
# DB[:a].filter([:a, :b]=>[[1, 2], [3, 4]]) # SQL: (a, b) IN ((1, 2), (3, 4))
|
|
563
|
+
# DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
|
|
564
|
+
# DB[:a].filter('(a, b) IN ?', Sequel.value_list([[1, 2], [3, 4]])) # SQL: (a, b) IN ((1, 2), (3, 4))
|
|
565
|
+
def value_list(arg)
|
|
566
|
+
raise Error, 'argument to Sequel.value_list must be an array' unless arg.is_a?(Array)
|
|
567
|
+
SQL::ValueList.new(arg)
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
267
571
|
# Holds methods that are used to cast objects to different SQL types.
|
|
268
572
|
module CastMethods
|
|
269
573
|
# Cast the reciever to the given SQL type. You can specify a ruby class as a type,
|
|
@@ -316,12 +620,8 @@ module Sequel
|
|
|
316
620
|
#
|
|
317
621
|
# Also has the benefit of returning the result as a
|
|
318
622
|
# NumericExpression instead of a generic ComplexExpression.
|
|
319
|
-
#
|
|
320
|
-
# The extract function is in the SQL standard, but it doesn't
|
|
321
|
-
# doesn't use the standard function calling convention, and it
|
|
322
|
-
# doesn't work on all databases.
|
|
323
623
|
def extract(datetime_part)
|
|
324
|
-
|
|
624
|
+
NumericExpression.new(:extract, datetime_part, self)
|
|
325
625
|
end
|
|
326
626
|
|
|
327
627
|
# Return a BooleanExpression representation of +self+.
|
|
@@ -367,34 +667,12 @@ module Sequel
|
|
|
367
667
|
# 'a'.lit <= :b # a <= "b"
|
|
368
668
|
module InequalityMethods
|
|
369
669
|
ComplexExpression::INEQUALITY_OPERATORS.each do |o|
|
|
370
|
-
|
|
371
|
-
case ce
|
|
372
|
-
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
|
|
373
|
-
raise(Error, "cannot apply #{o} to a boolean expression")
|
|
374
|
-
else
|
|
375
|
-
BooleanExpression.new(o, self, ce)
|
|
376
|
-
end
|
|
377
|
-
end
|
|
670
|
+
module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
|
378
671
|
end
|
|
379
672
|
end
|
|
380
673
|
|
|
381
|
-
#
|
|
382
|
-
# +ComplexExpression+ subclass it is included in, so that
|
|
383
|
-
# attempting to use boolean input when initializing a +NumericExpression+
|
|
384
|
-
# or +StringExpression+ results in an error. It is not expected to be
|
|
385
|
-
# used directly.
|
|
674
|
+
# Only exists for backwards compatibility, ignore it.
|
|
386
675
|
module NoBooleanInputMethods
|
|
387
|
-
# Raise an +Error+ if one of the args would be boolean in an SQL
|
|
388
|
-
# context, otherwise call super.
|
|
389
|
-
def initialize(op, *args)
|
|
390
|
-
args.each do |a|
|
|
391
|
-
case a
|
|
392
|
-
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
|
|
393
|
-
raise(Error, "cannot apply #{op} to a boolean expression")
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
super
|
|
397
|
-
end
|
|
398
676
|
end
|
|
399
677
|
|
|
400
678
|
# This module includes the standard mathematical methods (+, -, *, and /)
|
|
@@ -405,15 +683,66 @@ module Sequel
|
|
|
405
683
|
# :a - :b # "a" - "b"
|
|
406
684
|
# :a * :b # "a" * "b"
|
|
407
685
|
# :a / :b # "a" / "b"
|
|
686
|
+
#
|
|
687
|
+
# One exception to this is if + is called with a +String+ or +StringExpression+,
|
|
688
|
+
# in which case the || operator is used instead of the + operator:
|
|
689
|
+
#
|
|
690
|
+
# :a + 'b' # "a" || 'b'
|
|
408
691
|
module NumericMethods
|
|
409
692
|
ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
693
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Use || as the operator when called with StringExpression and String instances,
|
|
697
|
+
# and the + operator for LiteralStrings and all other types.
|
|
698
|
+
def +(ce)
|
|
699
|
+
case ce
|
|
700
|
+
when LiteralString
|
|
701
|
+
NumericExpression.new(:+, self, ce)
|
|
702
|
+
when StringExpression, String
|
|
703
|
+
StringExpression.new(:'||', self, ce)
|
|
704
|
+
else
|
|
705
|
+
NumericExpression.new(:+, self, ce)
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
# These methods are designed as replacements for the core extension operator
|
|
711
|
+
# methods, so that Sequel is still easy to use if the core extensions are not
|
|
712
|
+
# enabled.
|
|
713
|
+
#
|
|
714
|
+
# The following methods are defined via metaprogramming: +, -, *, /, &, |.
|
|
715
|
+
# The +, -, *, and / operators return numeric expressions combining all the
|
|
716
|
+
# arguments with the appropriate operator, and the & and | operators return
|
|
717
|
+
# boolean expressions combining all of the arguments with either AND or OR.
|
|
718
|
+
module OperatorBuilders
|
|
719
|
+
%w'+ - * /'.each do |op|
|
|
720
|
+
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
|
721
|
+
def #{op}(*args)
|
|
722
|
+
SQL::NumericExpression.new(:#{op}, *args)
|
|
723
|
+
end
|
|
724
|
+
END
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
{'&'=>'AND', '|'=>'OR'}.each do |m, op|
|
|
728
|
+
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
|
729
|
+
def #{m}(*args)
|
|
730
|
+
SQL::BooleanExpression.new(:#{op}, *args)
|
|
416
731
|
end
|
|
732
|
+
END
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
# Invert the given expression. Returns a <tt>Sequel::SQL::BooleanExpression</tt>
|
|
736
|
+
# created from this argument, not matching all of the conditions.
|
|
737
|
+
#
|
|
738
|
+
# Sequel.~(nil) # SQL: NOT NULL
|
|
739
|
+
# Sequel.~([[:a, true]]) # SQL: a IS NOT TRUE
|
|
740
|
+
# Sequel.~([[:a, 1], [:b, [2, 3]]]) # SQL: a != 1 OR b NOT IN (2, 3)
|
|
741
|
+
def ~(arg)
|
|
742
|
+
if condition_specifier?(arg)
|
|
743
|
+
SQL::BooleanExpression.from_value_pairs(arg, :OR, true)
|
|
744
|
+
else
|
|
745
|
+
SQL::BooleanExpression.invert(arg)
|
|
417
746
|
end
|
|
418
747
|
end
|
|
419
748
|
end
|
|
@@ -595,10 +924,27 @@ module Sequel
|
|
|
595
924
|
end
|
|
596
925
|
when StringExpression, NumericExpression
|
|
597
926
|
raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
927
|
+
when Constant
|
|
928
|
+
CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
598
929
|
else
|
|
599
930
|
BooleanExpression.new(:NOT, ce)
|
|
600
931
|
end
|
|
601
932
|
end
|
|
933
|
+
|
|
934
|
+
# Always use an AND operator for & on BooleanExpressions
|
|
935
|
+
def &(ce)
|
|
936
|
+
BooleanExpression.new(:AND, self, ce)
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# Always use an OR operator for | on BooleanExpressions
|
|
940
|
+
def |(ce)
|
|
941
|
+
BooleanExpression.new(:OR, self, ce)
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
# Return self instead of creating a new object to save on memory.
|
|
945
|
+
def sql_boolean
|
|
946
|
+
self
|
|
947
|
+
end
|
|
602
948
|
end
|
|
603
949
|
|
|
604
950
|
# Represents an SQL CASE expression, used for conditional branching in SQL.
|
|
@@ -666,6 +1012,21 @@ module Sequel
|
|
|
666
1012
|
include CastMethods
|
|
667
1013
|
include OrderMethods
|
|
668
1014
|
include SubscriptMethods
|
|
1015
|
+
|
|
1016
|
+
# Return a BooleanExpression with the same op and args.
|
|
1017
|
+
def sql_boolean
|
|
1018
|
+
BooleanExpression.new(self.op, *self.args)
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
# Return a NumericExpression with the same op and args.
|
|
1022
|
+
def sql_number
|
|
1023
|
+
NumericExpression.new(self.op, *self.args)
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
# Return a StringExpression with the same op and args.
|
|
1027
|
+
def sql_string
|
|
1028
|
+
StringExpression.new(self.op, *self.args)
|
|
1029
|
+
end
|
|
669
1030
|
end
|
|
670
1031
|
|
|
671
1032
|
# Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
|
|
@@ -706,6 +1067,12 @@ module Sequel
|
|
|
706
1067
|
NOTNULL = NegativeBooleanConstant.new(nil)
|
|
707
1068
|
end
|
|
708
1069
|
|
|
1070
|
+
class ComplexExpression
|
|
1071
|
+
# A hash of the opposite for each constant, used for inverting constants.
|
|
1072
|
+
CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
|
|
1073
|
+
Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
|
|
1074
|
+
end
|
|
1075
|
+
|
|
709
1076
|
# Represents an SQL function call.
|
|
710
1077
|
class Function < GenericExpression
|
|
711
1078
|
# The array of arguments to pass to the function (may be blank)
|
|
@@ -812,7 +1179,8 @@ module Sequel
|
|
|
812
1179
|
# substituted for :key phrases).
|
|
813
1180
|
attr_reader :args
|
|
814
1181
|
|
|
815
|
-
# The literal string containing placeholders
|
|
1182
|
+
# The literal string containing placeholders. This can also be an array
|
|
1183
|
+
# of strings, where each arg in args goes between the string elements.
|
|
816
1184
|
attr_reader :str
|
|
817
1185
|
|
|
818
1186
|
# Whether to surround the expression with parantheses
|
|
@@ -834,7 +1202,16 @@ module Sequel
|
|
|
834
1202
|
include BitwiseMethods
|
|
835
1203
|
include NumericMethods
|
|
836
1204
|
include InequalityMethods
|
|
837
|
-
|
|
1205
|
+
|
|
1206
|
+
# Always use + for + operator for NumericExpressions.
|
|
1207
|
+
def +(ce)
|
|
1208
|
+
NumericExpression.new(:+, self, ce)
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
# Return self instead of creating a new object to save on memory.
|
|
1212
|
+
def sql_number
|
|
1213
|
+
self
|
|
1214
|
+
end
|
|
838
1215
|
end
|
|
839
1216
|
|
|
840
1217
|
# Represents a column/expression to order the result set by.
|
|
@@ -891,7 +1268,7 @@ module Sequel
|
|
|
891
1268
|
@table, @column = table, column
|
|
892
1269
|
end
|
|
893
1270
|
|
|
894
|
-
to_s_method :qualified_identifier_sql
|
|
1271
|
+
to_s_method :qualified_identifier_sql, "@table, @column"
|
|
895
1272
|
end
|
|
896
1273
|
|
|
897
1274
|
# Subclass of +ComplexExpression+ where the expression results
|
|
@@ -900,7 +1277,6 @@ module Sequel
|
|
|
900
1277
|
include StringMethods
|
|
901
1278
|
include StringConcatenationMethods
|
|
902
1279
|
include InequalityMethods
|
|
903
|
-
include NoBooleanInputMethods
|
|
904
1280
|
|
|
905
1281
|
# Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
|
|
906
1282
|
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
|
@@ -949,6 +1325,11 @@ module Sequel
|
|
|
949
1325
|
end
|
|
950
1326
|
end
|
|
951
1327
|
private_class_method :like_element
|
|
1328
|
+
|
|
1329
|
+
# Return self instead of creating a new object to save on memory.
|
|
1330
|
+
def sql_string
|
|
1331
|
+
self
|
|
1332
|
+
end
|
|
952
1333
|
end
|
|
953
1334
|
|
|
954
1335
|
# Represents an SQL array access, with multiple possible arguments.
|
|
@@ -966,9 +1347,21 @@ module Sequel
|
|
|
966
1347
|
|
|
967
1348
|
# Create a new +Subscript+ appending the given subscript(s)
|
|
968
1349
|
# the the current array of subscripts.
|
|
1350
|
+
#
|
|
1351
|
+
# :a.sql_subscript(2) # a[2]
|
|
1352
|
+
# :a.sql_subscript(2) | 1 # a[2, 1]
|
|
969
1353
|
def |(sub)
|
|
970
1354
|
Subscript.new(@f, @sub + Array(sub))
|
|
971
1355
|
end
|
|
1356
|
+
|
|
1357
|
+
# Create a new +Subscript+ by accessing a subarray of a multidimensional
|
|
1358
|
+
# array.
|
|
1359
|
+
#
|
|
1360
|
+
# :a.sql_subscript(2) # a[2]
|
|
1361
|
+
# :a.sql_subscript(2)[1] # a[2][1]
|
|
1362
|
+
def [](sub)
|
|
1363
|
+
Subscript.new(self, Array(sub))
|
|
1364
|
+
end
|
|
972
1365
|
|
|
973
1366
|
to_s_method :subscript_sql
|
|
974
1367
|
end
|
|
@@ -1019,28 +1412,71 @@ module Sequel
|
|
|
1019
1412
|
# Examples:
|
|
1020
1413
|
#
|
|
1021
1414
|
# ds = DB[:t]
|
|
1415
|
+
#
|
|
1022
1416
|
# # Argument yielded to block
|
|
1023
1417
|
# ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
|
|
1418
|
+
#
|
|
1024
1419
|
# # Block without argument (instance_eval)
|
|
1025
1420
|
# ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
|
|
1421
|
+
#
|
|
1026
1422
|
# # Qualified identifiers
|
|
1027
1423
|
# ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
|
|
1424
|
+
#
|
|
1028
1425
|
# # Functions
|
|
1029
1426
|
# ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
|
|
1030
1427
|
# ds.select{version{}} # SELECT version() FROM t
|
|
1031
1428
|
# ds.select{count(:*){}} # SELECT count(*) FROM t
|
|
1032
1429
|
# ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
|
|
1430
|
+
#
|
|
1033
1431
|
# # Window Functions
|
|
1034
1432
|
# ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
|
|
1035
1433
|
# ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
|
|
1036
1434
|
# ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
|
|
1037
1435
|
#
|
|
1436
|
+
# # Math Operators
|
|
1437
|
+
# ds.select{|o| o.+(1, :a).as(:b)} # SELECT (1 + a) AS b FROM t
|
|
1438
|
+
# ds.select{|o| o.-(2, :a).as(:b)} # SELECT (2 - a) AS b FROM t
|
|
1439
|
+
# ds.select{|o| o.*(3, :a).as(:b)} # SELECT (3 * a) AS b FROM t
|
|
1440
|
+
# ds.select{|o| o./(4, :a).as(:b)} # SELECT (4 / a) AS b FROM t
|
|
1441
|
+
#
|
|
1442
|
+
# # Boolean Operators
|
|
1443
|
+
# ds.filter{|o| o.&({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) AND b)
|
|
1444
|
+
# ds.filter{|o| o.|({:a=>1}, :b)} # SELECT * FROM t WHERE ((a = 1) OR b)
|
|
1445
|
+
# ds.filter{|o| o.~({:a=>1})} # SELECT * FROM t WHERE (a != 1)
|
|
1446
|
+
# ds.filter{|o| o.~({:a=>1, :b=>2})} # SELECT * FROM t WHERE ((a != 1) OR (b != 2))
|
|
1447
|
+
#
|
|
1448
|
+
# # Inequality Operators
|
|
1449
|
+
# ds.filter{|o| o.>(1, :a)} # SELECT * FROM t WHERE (1 > a)
|
|
1450
|
+
# ds.filter{|o| o.<(2, :a)} # SELECT * FROM t WHERE (2 < a)
|
|
1451
|
+
# ds.filter{|o| o.>=(3, :a)} # SELECT * FROM t WHERE (3 >= a)
|
|
1452
|
+
# ds.filter{|o| o.<=(4, :a)} # SELECT * FROM t WHERE (4 <= a)
|
|
1453
|
+
#
|
|
1454
|
+
# # Literal Strings
|
|
1455
|
+
# ds.filter{{a=>`some SQL`}} # SELECT * FROM t WHERE (a = some SQL)
|
|
1456
|
+
#
|
|
1038
1457
|
# For a more detailed explanation, see the {Virtual Rows guide}[link:files/doc/virtual_rows_rdoc.html].
|
|
1039
1458
|
class VirtualRow < BasicObject
|
|
1040
1459
|
WILDCARD = LiteralString.new('*').freeze
|
|
1041
1460
|
QUESTION_MARK = LiteralString.new('?').freeze
|
|
1042
1461
|
COMMA_SEPARATOR = LiteralString.new(', ').freeze
|
|
1043
1462
|
DOUBLE_UNDERSCORE = '__'.freeze
|
|
1463
|
+
DISTINCT = ["DISTINCT ".freeze].freeze
|
|
1464
|
+
COMMA_ARRAY = [COMMA_SEPARATOR].freeze
|
|
1465
|
+
|
|
1466
|
+
include OperatorBuilders
|
|
1467
|
+
|
|
1468
|
+
%w'> < >= <='.each do |op|
|
|
1469
|
+
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
|
1470
|
+
def #{op}(*args)
|
|
1471
|
+
SQL::BooleanExpression.new(:#{op}, *args)
|
|
1472
|
+
end
|
|
1473
|
+
END
|
|
1474
|
+
end
|
|
1475
|
+
|
|
1476
|
+
# Return a literal string created with the given string.
|
|
1477
|
+
def `(s)
|
|
1478
|
+
Sequel::LiteralString.new(s)
|
|
1479
|
+
end
|
|
1044
1480
|
|
|
1045
1481
|
# Return an +Identifier+, +QualifiedIdentifier+, +Function+, or +WindowFunction+, depending
|
|
1046
1482
|
# on arguments and whether a block is provided. Does not currently call the block.
|
|
@@ -1054,7 +1490,7 @@ module Sequel
|
|
|
1054
1490
|
when :*
|
|
1055
1491
|
Function.new(m, WILDCARD)
|
|
1056
1492
|
when :distinct
|
|
1057
|
-
Function.new(m, PlaceholderLiteralString.new(
|
|
1493
|
+
Function.new(m, PlaceholderLiteralString.new(DISTINCT + COMMA_ARRAY * (args.length-1), args))
|
|
1058
1494
|
when :over
|
|
1059
1495
|
opts = args.shift || {}
|
|
1060
1496
|
fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
|
|
@@ -1108,6 +1544,20 @@ module Sequel
|
|
|
1108
1544
|
|
|
1109
1545
|
to_s_method :window_function_sql, '@function, @window'
|
|
1110
1546
|
end
|
|
1547
|
+
|
|
1548
|
+
# A +Wrapper+ is a simple way to wrap an existing object so that it supports
|
|
1549
|
+
# the Sequel DSL.
|
|
1550
|
+
class Wrapper < GenericExpression
|
|
1551
|
+
# The underlying value wrapped by this object.
|
|
1552
|
+
attr_reader :value
|
|
1553
|
+
|
|
1554
|
+
# Set the value wrapped by the object.
|
|
1555
|
+
def initialize(value)
|
|
1556
|
+
@value = value
|
|
1557
|
+
end
|
|
1558
|
+
|
|
1559
|
+
to_s_method :literal, '@value'
|
|
1560
|
+
end
|
|
1111
1561
|
end
|
|
1112
1562
|
|
|
1113
1563
|
# +LiteralString+ is used to represent literal SQL expressions. A
|
|
@@ -1120,7 +1570,24 @@ module Sequel
|
|
|
1120
1570
|
include SQL::NumericMethods
|
|
1121
1571
|
include SQL::StringMethods
|
|
1122
1572
|
include SQL::InequalityMethods
|
|
1573
|
+
|
|
1574
|
+
# If the core extensions are enabled, these will already be included
|
|
1575
|
+
# in String, so we don't need to include/define them here.
|
|
1576
|
+
unless Sequel.core_extensions?
|
|
1577
|
+
include Sequel::SQL::AliasMethods
|
|
1578
|
+
include Sequel::SQL::CastMethods
|
|
1579
|
+
|
|
1580
|
+
def lit(*args)
|
|
1581
|
+
args.empty? ? self : SQL::PlaceholderLiteralString.new(self, args)
|
|
1582
|
+
end
|
|
1583
|
+
|
|
1584
|
+
def to_sequel_blob
|
|
1585
|
+
SQL::Blob.new(self)
|
|
1586
|
+
end
|
|
1587
|
+
end
|
|
1123
1588
|
end
|
|
1124
1589
|
|
|
1125
1590
|
include SQL::Constants
|
|
1591
|
+
extend SQL::Builders
|
|
1592
|
+
extend SQL::OperatorBuilders
|
|
1126
1593
|
end
|