sequel 3.21.0 → 3.31.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 +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- 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/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -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 +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -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 +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -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 +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- 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 +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- 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 +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -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/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -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/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- 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/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- 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/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- 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 +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -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 +1 -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 +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -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 +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
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
|
|
|
@@ -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.
|
|
@@ -321,7 +338,7 @@ module Sequel
|
|
|
321
338
|
# doesn't use the standard function calling convention, and it
|
|
322
339
|
# doesn't work on all databases.
|
|
323
340
|
def extract(datetime_part)
|
|
324
|
-
|
|
341
|
+
NumericExpression.new(:extract, datetime_part, self)
|
|
325
342
|
end
|
|
326
343
|
|
|
327
344
|
# Return a BooleanExpression representation of +self+.
|
|
@@ -367,34 +384,12 @@ module Sequel
|
|
|
367
384
|
# 'a'.lit <= :b # a <= "b"
|
|
368
385
|
module InequalityMethods
|
|
369
386
|
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
|
|
387
|
+
module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
|
378
388
|
end
|
|
379
389
|
end
|
|
380
390
|
|
|
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.
|
|
391
|
+
# Only exists for backwards compatibility, ignore it.
|
|
386
392
|
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
393
|
end
|
|
399
394
|
|
|
400
395
|
# This module includes the standard mathematical methods (+, -, *, and /)
|
|
@@ -405,15 +400,26 @@ module Sequel
|
|
|
405
400
|
# :a - :b # "a" - "b"
|
|
406
401
|
# :a * :b # "a" * "b"
|
|
407
402
|
# :a / :b # "a" / "b"
|
|
403
|
+
#
|
|
404
|
+
# One exception to this is if + is called with a +String+ or +StringExpression+,
|
|
405
|
+
# in which case the || operator is used instead of the + operator:
|
|
406
|
+
#
|
|
407
|
+
# :a + 'b' # "a" || 'b'
|
|
408
408
|
module NumericMethods
|
|
409
409
|
ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
410
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Use || as the operator when called with StringExpression and String instances,
|
|
414
|
+
# and the + operator for LiteralStrings and all other types.
|
|
415
|
+
def +(ce)
|
|
416
|
+
case ce
|
|
417
|
+
when LiteralString
|
|
418
|
+
NumericExpression.new(:+, self, ce)
|
|
419
|
+
when StringExpression, String
|
|
420
|
+
StringExpression.new(:'||', self, ce)
|
|
421
|
+
else
|
|
422
|
+
NumericExpression.new(:+, self, ce)
|
|
417
423
|
end
|
|
418
424
|
end
|
|
419
425
|
end
|
|
@@ -595,10 +601,27 @@ module Sequel
|
|
|
595
601
|
end
|
|
596
602
|
when StringExpression, NumericExpression
|
|
597
603
|
raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
604
|
+
when Constant
|
|
605
|
+
CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
598
606
|
else
|
|
599
607
|
BooleanExpression.new(:NOT, ce)
|
|
600
608
|
end
|
|
601
609
|
end
|
|
610
|
+
|
|
611
|
+
# Always use an AND operator for & on BooleanExpressions
|
|
612
|
+
def &(ce)
|
|
613
|
+
BooleanExpression.new(:AND, self, ce)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# Always use an OR operator for | on BooleanExpressions
|
|
617
|
+
def |(ce)
|
|
618
|
+
BooleanExpression.new(:OR, self, ce)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
# Return self instead of creating a new object to save on memory.
|
|
622
|
+
def sql_boolean
|
|
623
|
+
self
|
|
624
|
+
end
|
|
602
625
|
end
|
|
603
626
|
|
|
604
627
|
# Represents an SQL CASE expression, used for conditional branching in SQL.
|
|
@@ -666,6 +689,21 @@ module Sequel
|
|
|
666
689
|
include CastMethods
|
|
667
690
|
include OrderMethods
|
|
668
691
|
include SubscriptMethods
|
|
692
|
+
|
|
693
|
+
# Return a BooleanExpression with the same op and args.
|
|
694
|
+
def sql_boolean
|
|
695
|
+
BooleanExpression.new(self.op, *self.args)
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
# Return a NumericExpression with the same op and args.
|
|
699
|
+
def sql_number
|
|
700
|
+
NumericExpression.new(self.op, *self.args)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
# Return a StringExpression with the same op and args.
|
|
704
|
+
def sql_string
|
|
705
|
+
StringExpression.new(self.op, *self.args)
|
|
706
|
+
end
|
|
669
707
|
end
|
|
670
708
|
|
|
671
709
|
# Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
|
|
@@ -706,6 +744,12 @@ module Sequel
|
|
|
706
744
|
NOTNULL = NegativeBooleanConstant.new(nil)
|
|
707
745
|
end
|
|
708
746
|
|
|
747
|
+
class ComplexExpression
|
|
748
|
+
# A hash of the opposite for each constant, used for inverting constants.
|
|
749
|
+
CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
|
|
750
|
+
Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
|
|
751
|
+
end
|
|
752
|
+
|
|
709
753
|
# Represents an SQL function call.
|
|
710
754
|
class Function < GenericExpression
|
|
711
755
|
# The array of arguments to pass to the function (may be blank)
|
|
@@ -834,7 +878,16 @@ module Sequel
|
|
|
834
878
|
include BitwiseMethods
|
|
835
879
|
include NumericMethods
|
|
836
880
|
include InequalityMethods
|
|
837
|
-
|
|
881
|
+
|
|
882
|
+
# Always use + for + operator for NumericExpressions.
|
|
883
|
+
def +(ce)
|
|
884
|
+
NumericExpression.new(:+, self, ce)
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
# Return self instead of creating a new object to save on memory.
|
|
888
|
+
def sql_number
|
|
889
|
+
self
|
|
890
|
+
end
|
|
838
891
|
end
|
|
839
892
|
|
|
840
893
|
# Represents a column/expression to order the result set by.
|
|
@@ -900,7 +953,6 @@ module Sequel
|
|
|
900
953
|
include StringMethods
|
|
901
954
|
include StringConcatenationMethods
|
|
902
955
|
include InequalityMethods
|
|
903
|
-
include NoBooleanInputMethods
|
|
904
956
|
|
|
905
957
|
# Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
|
|
906
958
|
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
|
@@ -949,6 +1001,11 @@ module Sequel
|
|
|
949
1001
|
end
|
|
950
1002
|
end
|
|
951
1003
|
private_class_method :like_element
|
|
1004
|
+
|
|
1005
|
+
# Return self instead of creating a new object to save on memory.
|
|
1006
|
+
def sql_string
|
|
1007
|
+
self
|
|
1008
|
+
end
|
|
952
1009
|
end
|
|
953
1010
|
|
|
954
1011
|
# Represents an SQL array access, with multiple possible arguments.
|
data/lib/sequel/timezones.rb
CHANGED
|
@@ -39,6 +39,48 @@ module Sequel
|
|
|
39
39
|
convert_output_timestamp(v, Sequel.database_timezone)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# Converts the object to the given +output_timezone+.
|
|
43
|
+
def convert_output_timestamp(v, output_timezone)
|
|
44
|
+
if output_timezone
|
|
45
|
+
if v.is_a?(DateTime)
|
|
46
|
+
case output_timezone
|
|
47
|
+
when :utc
|
|
48
|
+
v.new_offset(0)
|
|
49
|
+
when :local
|
|
50
|
+
v.new_offset(local_offset_for_datetime(v))
|
|
51
|
+
else
|
|
52
|
+
convert_output_datetime_other(v, output_timezone)
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
v.send(output_timezone == :utc ? :getutc : :getlocal)
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
v
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Converts the given object from the given input timezone to the
|
|
63
|
+
# +application_timezone+ using +convert_input_timestamp+ and
|
|
64
|
+
# +convert_output_timestamp+.
|
|
65
|
+
def convert_timestamp(v, input_timezone)
|
|
66
|
+
begin
|
|
67
|
+
if v.is_a?(Date) && !v.is_a?(DateTime)
|
|
68
|
+
# Dates handled specially as they are assumed to already be in the application_timezone
|
|
69
|
+
if datetime_class == DateTime
|
|
70
|
+
DateTime.civil(v.year, v.month, v.day, 0, 0, 0, application_timezone == :local ? (defined?(Rational) ? Rational(Time.local(v.year, v.month, v.day).utc_offset, 86400) : Time.local(v.year, v.month, v.day).utc_offset/86400.0) : 0)
|
|
71
|
+
else
|
|
72
|
+
Time.send(application_timezone == :utc ? :utc : :local, v.year, v.month, v.day)
|
|
73
|
+
end
|
|
74
|
+
else
|
|
75
|
+
convert_output_timestamp(convert_input_timestamp(v, input_timezone), application_timezone)
|
|
76
|
+
end
|
|
77
|
+
rescue InvalidValue
|
|
78
|
+
raise
|
|
79
|
+
rescue => e
|
|
80
|
+
raise convert_exception_class(e, InvalidValue)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
42
84
|
# Convert the given object into an object of <tt>Sequel.datetime_class</tt> in the
|
|
43
85
|
# +application_timezone+. Used when coverting datetime/timestamp columns
|
|
44
86
|
# returned by the database.
|
|
@@ -103,12 +145,24 @@ module Sequel
|
|
|
103
145
|
v2
|
|
104
146
|
end
|
|
105
147
|
when Array
|
|
106
|
-
y, mo, d, h, mi, s = v
|
|
148
|
+
y, mo, d, h, mi, s, ns, off = v
|
|
107
149
|
if datetime_class == DateTime
|
|
108
|
-
|
|
150
|
+
s += (defined?(Rational) ? Rational(ns, 1000000000) : ns/1000000000.0) if ns
|
|
151
|
+
if off
|
|
152
|
+
DateTime.civil(y, mo, d, h, mi, s, off)
|
|
153
|
+
else
|
|
154
|
+
convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone)
|
|
155
|
+
end
|
|
109
156
|
else
|
|
110
|
-
Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
|
|
157
|
+
Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
|
|
158
|
+
end
|
|
159
|
+
when Hash
|
|
160
|
+
ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i}
|
|
161
|
+
if (offset = (v[:offset] || v['offset']))
|
|
162
|
+
ary << offset
|
|
111
163
|
end
|
|
164
|
+
convert_input_timestamp(ary, input_timezone)
|
|
165
|
+
convert_input_timestamp(ary, input_timezone)
|
|
112
166
|
when Time
|
|
113
167
|
if datetime_class == DateTime
|
|
114
168
|
v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
|
|
@@ -121,8 +175,6 @@ module Sequel
|
|
|
121
175
|
else
|
|
122
176
|
v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
|
|
123
177
|
end
|
|
124
|
-
when Date
|
|
125
|
-
convert_input_timestamp(v.to_s, input_timezone)
|
|
126
178
|
else
|
|
127
179
|
raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
|
|
128
180
|
end
|
|
@@ -135,39 +187,6 @@ module Sequel
|
|
|
135
187
|
raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
|
|
136
188
|
end
|
|
137
189
|
|
|
138
|
-
# Converts the object to the given +output_timezone+.
|
|
139
|
-
def convert_output_timestamp(v, output_timezone)
|
|
140
|
-
if output_timezone
|
|
141
|
-
if v.is_a?(DateTime)
|
|
142
|
-
case output_timezone
|
|
143
|
-
when :utc
|
|
144
|
-
v.new_offset(0)
|
|
145
|
-
when :local
|
|
146
|
-
v.new_offset(local_offset_for_datetime(v))
|
|
147
|
-
else
|
|
148
|
-
convert_output_datetime_other(v, output_timezone)
|
|
149
|
-
end
|
|
150
|
-
else
|
|
151
|
-
v.send(output_timezone == :utc ? :getutc : :getlocal)
|
|
152
|
-
end
|
|
153
|
-
else
|
|
154
|
-
v
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Converts the given object from the given input timezone to the
|
|
159
|
-
# +application_timezone+ using +convert_input_timestamp+ and
|
|
160
|
-
# +convert_output_timestamp+.
|
|
161
|
-
def convert_timestamp(v, input_timezone)
|
|
162
|
-
begin
|
|
163
|
-
convert_output_timestamp(convert_input_timestamp(v, input_timezone), Sequel.application_timezone)
|
|
164
|
-
rescue InvalidValue
|
|
165
|
-
raise
|
|
166
|
-
rescue => e
|
|
167
|
-
raise convert_exception_class(e, InvalidValue)
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
190
|
# Convert the timezone setter argument. Returns argument given by default,
|
|
172
191
|
# exists for easier overriding in extensions.
|
|
173
192
|
def convert_timezone_setter_arg(tz)
|
data/lib/sequel/version.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Sequel
|
|
|
3
3
|
MAJOR = 3
|
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
5
5
|
# release, generally around once a month.
|
|
6
|
-
MINOR =
|
|
6
|
+
MINOR = 31
|
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
8
8
|
# releases that fix regressions from previous versions.
|
|
9
9
|
TINY = 0
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
#Author: Roy L Zuo (roylzuo at gmail dot com)
|
|
4
|
+
#Description:
|
|
5
|
+
|
|
6
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
|
|
7
|
+
|
|
8
|
+
require ENV['SEQUEL_DB2_SPEC_REQUIRE'] if ENV['SEQUEL_DB2_SPEC_REQUIRE']
|
|
9
|
+
|
|
10
|
+
unless defined?(DB2_DB)
|
|
11
|
+
DB2_DB = Sequel.connect(ENV['SEQUEL_DB2_SPEC_DB']||DB2_URL)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if DB2_DB.table_exists?(:test)
|
|
15
|
+
DB2_DB.drop_table :test
|
|
16
|
+
end
|
|
17
|
+
INTEGRATION_DB = DB2_DB unless defined?(INTEGRATION_DB)
|
|
18
|
+
|
|
19
|
+
describe Sequel::Database do
|
|
20
|
+
before do
|
|
21
|
+
@db = DB2_DB
|
|
22
|
+
@db.create_table(:test){String :a}
|
|
23
|
+
@ds = @db[:test]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
after do
|
|
27
|
+
@db.drop_table(:test)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
specify "should provide disconnect functionality after preparing a connection" do
|
|
31
|
+
@ds.prepare(:first, :a).call
|
|
32
|
+
@db.disconnect
|
|
33
|
+
@db.pool.size.should == 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify "should return version correctly" do
|
|
37
|
+
@db.db2_version.should match(/DB2 v/i)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "Simple Dataset operations" do
|
|
42
|
+
before do
|
|
43
|
+
DB2_DB.create_table!(:items) do
|
|
44
|
+
Integer :id, :primary_key => true
|
|
45
|
+
Integer :number
|
|
46
|
+
end
|
|
47
|
+
@ds = DB2_DB[:items]
|
|
48
|
+
@ds.insert(:number=>10, :id => 1 )
|
|
49
|
+
end
|
|
50
|
+
after do
|
|
51
|
+
DB2_DB.drop_table(:items)
|
|
52
|
+
end
|
|
53
|
+
cspecify "should insert with a primary key specified", :mssql do
|
|
54
|
+
@ds.insert(:id=>100, :number=>20)
|
|
55
|
+
@ds.count.should == 2
|
|
56
|
+
@ds.order(:id).all.should == [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe Sequel::Database do
|
|
61
|
+
before do
|
|
62
|
+
@db = DB2_DB
|
|
63
|
+
end
|
|
64
|
+
after do
|
|
65
|
+
@db.drop_table(:items)
|
|
66
|
+
end
|
|
67
|
+
specify "should parse primary keys from the schema properly" do
|
|
68
|
+
@db.create_table!(:items){Integer :number}
|
|
69
|
+
@db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == []
|
|
70
|
+
@db.create_table!(:items){primary_key :number}
|
|
71
|
+
@db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number]
|
|
72
|
+
@db.create_table!(:items){Integer :number1, :null => false; Integer :number2, :null => false; primary_key [:number1, :number2]}
|
|
73
|
+
@db.schema(:items).collect{|k,v| k if v[:primary_key]}.compact.should == [:number1, :number2]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe "Sequel::IBMDB.convert_smallint_to_bool" do
|
|
78
|
+
before do
|
|
79
|
+
@db = DB2_DB
|
|
80
|
+
@db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
|
|
81
|
+
@ds = @db[:booltest]
|
|
82
|
+
end
|
|
83
|
+
after do
|
|
84
|
+
Sequel::IBMDB.convert_smallint_to_bool = true
|
|
85
|
+
@db.drop_table(:booltest)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
specify "should consider smallint datatypes as boolean if set, but not larger smallints" do
|
|
89
|
+
@db.schema(:booltest, :reload=>true).first.last[:type].should == :boolean
|
|
90
|
+
@db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
|
|
91
|
+
Sequel::IBMDB.convert_smallint_to_bool = false
|
|
92
|
+
@db.schema(:booltest, :reload=>true).first.last[:type].should == :integer
|
|
93
|
+
@db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
specify "should return smallints as bools and integers as integers when set" do
|
|
97
|
+
Sequel::IBMDB.convert_smallint_to_bool = true
|
|
98
|
+
@ds.delete
|
|
99
|
+
@ds << {:b=>true, :i=>10}
|
|
100
|
+
@ds.all.should == [{:b=>true, :i=>10}]
|
|
101
|
+
@ds.delete
|
|
102
|
+
@ds << {:b=>false, :i=>0}
|
|
103
|
+
@ds.all.should == [{:b=>false, :i=>0}]
|
|
104
|
+
@ds.delete
|
|
105
|
+
@ds << {:b=>true, :i=>1}
|
|
106
|
+
@ds.all.should == [{:b=>true, :i=>1}]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
specify "should return all smallints as integers when unset" do
|
|
110
|
+
Sequel::IBMDB.convert_smallint_to_bool = false
|
|
111
|
+
@ds.delete
|
|
112
|
+
@ds << {:b=>true, :i=>10}
|
|
113
|
+
@ds.all.should == [{:b=>1, :i=>10}]
|
|
114
|
+
@ds.delete
|
|
115
|
+
@ds << {:b=>false, :i=>0}
|
|
116
|
+
@ds.all.should == [{:b=>0, :i=>0}]
|
|
117
|
+
|
|
118
|
+
@ds.delete
|
|
119
|
+
@ds << {:b=>1, :i=>10}
|
|
120
|
+
@ds.all.should == [{:b=>1, :i=>10}]
|
|
121
|
+
@ds.delete
|
|
122
|
+
@ds << {:b=>0, :i=>0}
|
|
123
|
+
@ds.all.should == [{:b=>0, :i=>0}]
|
|
124
|
+
end
|
|
125
|
+
end if DB2_DB.adapter_scheme == :ibmdb
|
|
126
|
+
|
|
127
|
+
describe "Simple Dataset operations in transactions" do
|
|
128
|
+
before do
|
|
129
|
+
DB2_DB.create_table!(:items_insert_in_transaction) do
|
|
130
|
+
Integer :id, :primary_key => true
|
|
131
|
+
integer :number
|
|
132
|
+
end
|
|
133
|
+
@ds = DB2_DB[:items_insert_in_transaction]
|
|
134
|
+
end
|
|
135
|
+
after do
|
|
136
|
+
DB2_DB.drop_table(:items_insert_in_transaction)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
specify "should insert correctly with a primary key specified inside a transaction" do
|
|
140
|
+
DB2_DB.transaction do
|
|
141
|
+
@ds.insert(:id=>100, :number=>20)
|
|
142
|
+
@ds.count.should == 1
|
|
143
|
+
@ds.order(:id).all.should == [{:id=>100, :number=>20}]
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|