sequel 3.21.0 → 3.29.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 +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- 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 +86 -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/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -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 +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- 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 +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
- 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 +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- 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 +997 -331
- data/lib/sequel/model/base.rb +386 -129
- 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 +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- 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 +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- 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 +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -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_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- 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 +200 -120
- 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 +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
# The update_primary_key plugin allows you to modify an
|
|
3
|
+
# The update_primary_key plugin allows you to modify an object's
|
|
4
4
|
# primary key and then save the record. Sequel does not work
|
|
5
5
|
# correctly with primary key modifications by default. Sequel
|
|
6
6
|
# is designed to work with surrogate primary keys that never need to be
|
|
@@ -22,7 +22,7 @@ module Sequel
|
|
|
22
22
|
module UpdatePrimaryKey
|
|
23
23
|
module ClassMethods
|
|
24
24
|
# Cache the pk_hash when loading records
|
|
25
|
-
def
|
|
25
|
+
def call(h)
|
|
26
26
|
r = super(h)
|
|
27
27
|
r.pk_hash
|
|
28
28
|
r
|
|
@@ -205,9 +205,9 @@ module Sequel
|
|
|
205
205
|
klass.from_xml_node(node)
|
|
206
206
|
end
|
|
207
207
|
elsif cols.include?(k)
|
|
208
|
-
self[k.to_sym] = node
|
|
208
|
+
self[k.to_sym] = node.key?('nil') ? nil : node.children.first.to_s
|
|
209
209
|
elsif meths.include?("#{k}=")
|
|
210
|
-
send("#{k}=", node
|
|
210
|
+
send("#{k}=", node.key?('nil') ? nil : node.children.first.to_s)
|
|
211
211
|
else
|
|
212
212
|
raise Error, "Entry in XML not an association or column and no setter method exists: #{k}"
|
|
213
213
|
end
|
|
@@ -266,7 +266,7 @@ module Sequel
|
|
|
266
266
|
|
|
267
267
|
name_proc = model.xml_serialize_name_proc(opts)
|
|
268
268
|
x = model.xml_builder(opts)
|
|
269
|
-
x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name)).to_s]) do |x1|
|
|
269
|
+
x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name).gsub('/', '__')).to_s]) do |x1|
|
|
270
270
|
cols.each do |c|
|
|
271
271
|
attrs = {}
|
|
272
272
|
if types
|
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.
|
|
@@ -144,14 +154,17 @@ module Sequel
|
|
|
144
154
|
# Operator symbols that take exactly two arguments
|
|
145
155
|
TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
|
|
146
156
|
:~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
|
|
147
|
-
INEQUALITY_OPERATORS +
|
|
157
|
+
INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
|
|
148
158
|
|
|
149
159
|
# Operator symbols that take one or more arguments
|
|
150
|
-
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
|
|
160
|
+
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
|
|
151
161
|
|
|
152
162
|
# Operator symbols that take only a single argument
|
|
153
163
|
ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
|
|
154
164
|
|
|
165
|
+
# Custom expressions that may have different syntax on different databases
|
|
166
|
+
CUSTOM_EXPRESSIONS = [:extract]
|
|
167
|
+
|
|
155
168
|
# An array of args for this object
|
|
156
169
|
attr_reader :args
|
|
157
170
|
|
|
@@ -180,6 +193,8 @@ module Sequel
|
|
|
180
193
|
args[1] = orig_args[1] if IN_OPERATORS.include?(op)
|
|
181
194
|
when *ONE_ARITY_OPERATORS
|
|
182
195
|
raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
|
|
196
|
+
when *CUSTOM_EXPRESSIONS
|
|
197
|
+
# nothing
|
|
183
198
|
else
|
|
184
199
|
raise(Error, "Invalid operator #{op}")
|
|
185
200
|
end
|
|
@@ -219,14 +234,7 @@ module Sequel
|
|
|
219
234
|
# ~:a.sql_number # ~"a"
|
|
220
235
|
module BitwiseMethods
|
|
221
236
|
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
|
|
237
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
|
230
238
|
end
|
|
231
239
|
|
|
232
240
|
# Do the bitwise compliment of the self
|
|
@@ -244,16 +252,24 @@ module Sequel
|
|
|
244
252
|
# :a & :b # "a" AND "b"
|
|
245
253
|
# :a | :b # "a" OR "b"
|
|
246
254
|
# ~:a # NOT "a"
|
|
255
|
+
#
|
|
256
|
+
# One exception to this is when a NumericExpression or Integer is the argument
|
|
257
|
+
# to & or |, in which case a bitwise method will be used:
|
|
258
|
+
#
|
|
259
|
+
# :a & 1 # "a" & 1
|
|
260
|
+
# :a | (:b + 1) # "a" | ("b" + 1)
|
|
247
261
|
module BooleanMethods
|
|
248
262
|
ComplexExpression::BOOLEAN_OPERATOR_METHODS.each do |m, o|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
263
|
+
module_eval(<<-END, __FILE__, __LINE__+1)
|
|
264
|
+
def #{m}(o)
|
|
265
|
+
case o
|
|
266
|
+
when NumericExpression, Integer
|
|
267
|
+
NumericExpression.new(#{m.inspect}, self, o)
|
|
268
|
+
else
|
|
269
|
+
BooleanExpression.new(#{o.inspect}, self, o)
|
|
270
|
+
end
|
|
255
271
|
end
|
|
256
|
-
|
|
272
|
+
END
|
|
257
273
|
end
|
|
258
274
|
|
|
259
275
|
# Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
|
|
@@ -321,7 +337,7 @@ module Sequel
|
|
|
321
337
|
# doesn't use the standard function calling convention, and it
|
|
322
338
|
# doesn't work on all databases.
|
|
323
339
|
def extract(datetime_part)
|
|
324
|
-
|
|
340
|
+
NumericExpression.new(:extract, datetime_part, self)
|
|
325
341
|
end
|
|
326
342
|
|
|
327
343
|
# Return a BooleanExpression representation of +self+.
|
|
@@ -367,34 +383,12 @@ module Sequel
|
|
|
367
383
|
# 'a'.lit <= :b # a <= "b"
|
|
368
384
|
module InequalityMethods
|
|
369
385
|
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
|
|
386
|
+
module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
|
378
387
|
end
|
|
379
388
|
end
|
|
380
389
|
|
|
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.
|
|
390
|
+
# Only exists for backwards compatibility, ignore it.
|
|
386
391
|
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
392
|
end
|
|
399
393
|
|
|
400
394
|
# This module includes the standard mathematical methods (+, -, *, and /)
|
|
@@ -405,15 +399,26 @@ module Sequel
|
|
|
405
399
|
# :a - :b # "a" - "b"
|
|
406
400
|
# :a * :b # "a" * "b"
|
|
407
401
|
# :a / :b # "a" / "b"
|
|
402
|
+
#
|
|
403
|
+
# One exception to this is if + is called with a +String+ or +StringExpression+,
|
|
404
|
+
# in which case the || operator is used instead of the + operator:
|
|
405
|
+
#
|
|
406
|
+
# :a + 'b' # "a" || 'b'
|
|
408
407
|
module NumericMethods
|
|
409
408
|
ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
409
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__) unless o == :+
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Use || as the operator when called with StringExpression and String instances,
|
|
413
|
+
# and the + operator for LiteralStrings and all other types.
|
|
414
|
+
def +(ce)
|
|
415
|
+
case ce
|
|
416
|
+
when LiteralString
|
|
417
|
+
NumericExpression.new(:+, self, ce)
|
|
418
|
+
when StringExpression, String
|
|
419
|
+
StringExpression.new(:'||', self, ce)
|
|
420
|
+
else
|
|
421
|
+
NumericExpression.new(:+, self, ce)
|
|
417
422
|
end
|
|
418
423
|
end
|
|
419
424
|
end
|
|
@@ -595,10 +600,27 @@ module Sequel
|
|
|
595
600
|
end
|
|
596
601
|
when StringExpression, NumericExpression
|
|
597
602
|
raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
603
|
+
when Constant
|
|
604
|
+
CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
|
598
605
|
else
|
|
599
606
|
BooleanExpression.new(:NOT, ce)
|
|
600
607
|
end
|
|
601
608
|
end
|
|
609
|
+
|
|
610
|
+
# Always use an AND operator for & on BooleanExpressions
|
|
611
|
+
def &(ce)
|
|
612
|
+
BooleanExpression.new(:AND, self, ce)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
# Always use an OR operator for | on BooleanExpressions
|
|
616
|
+
def |(ce)
|
|
617
|
+
BooleanExpression.new(:OR, self, ce)
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Return self instead of creating a new object to save on memory.
|
|
621
|
+
def sql_boolean
|
|
622
|
+
self
|
|
623
|
+
end
|
|
602
624
|
end
|
|
603
625
|
|
|
604
626
|
# Represents an SQL CASE expression, used for conditional branching in SQL.
|
|
@@ -666,6 +688,21 @@ module Sequel
|
|
|
666
688
|
include CastMethods
|
|
667
689
|
include OrderMethods
|
|
668
690
|
include SubscriptMethods
|
|
691
|
+
|
|
692
|
+
# Return a BooleanExpression with the same op and args.
|
|
693
|
+
def sql_boolean
|
|
694
|
+
BooleanExpression.new(self.op, *self.args)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
# Return a NumericExpression with the same op and args.
|
|
698
|
+
def sql_number
|
|
699
|
+
NumericExpression.new(self.op, *self.args)
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Return a StringExpression with the same op and args.
|
|
703
|
+
def sql_string
|
|
704
|
+
StringExpression.new(self.op, *self.args)
|
|
705
|
+
end
|
|
669
706
|
end
|
|
670
707
|
|
|
671
708
|
# Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
|
|
@@ -706,6 +743,12 @@ module Sequel
|
|
|
706
743
|
NOTNULL = NegativeBooleanConstant.new(nil)
|
|
707
744
|
end
|
|
708
745
|
|
|
746
|
+
class ComplexExpression
|
|
747
|
+
# A hash of the opposite for each constant, used for inverting constants.
|
|
748
|
+
CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
|
|
749
|
+
Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
|
|
750
|
+
end
|
|
751
|
+
|
|
709
752
|
# Represents an SQL function call.
|
|
710
753
|
class Function < GenericExpression
|
|
711
754
|
# The array of arguments to pass to the function (may be blank)
|
|
@@ -834,7 +877,16 @@ module Sequel
|
|
|
834
877
|
include BitwiseMethods
|
|
835
878
|
include NumericMethods
|
|
836
879
|
include InequalityMethods
|
|
837
|
-
|
|
880
|
+
|
|
881
|
+
# Always use + for + operator for NumericExpressions.
|
|
882
|
+
def +(ce)
|
|
883
|
+
NumericExpression.new(:+, self, ce)
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
# Return self instead of creating a new object to save on memory.
|
|
887
|
+
def sql_number
|
|
888
|
+
self
|
|
889
|
+
end
|
|
838
890
|
end
|
|
839
891
|
|
|
840
892
|
# Represents a column/expression to order the result set by.
|
|
@@ -900,7 +952,6 @@ module Sequel
|
|
|
900
952
|
include StringMethods
|
|
901
953
|
include StringConcatenationMethods
|
|
902
954
|
include InequalityMethods
|
|
903
|
-
include NoBooleanInputMethods
|
|
904
955
|
|
|
905
956
|
# Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
|
|
906
957
|
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
|
@@ -949,6 +1000,11 @@ module Sequel
|
|
|
949
1000
|
end
|
|
950
1001
|
end
|
|
951
1002
|
private_class_method :like_element
|
|
1003
|
+
|
|
1004
|
+
# Return self instead of creating a new object to save on memory.
|
|
1005
|
+
def sql_string
|
|
1006
|
+
self
|
|
1007
|
+
end
|
|
952
1008
|
end
|
|
953
1009
|
|
|
954
1010
|
# 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.
|
|
@@ -109,6 +151,8 @@ module Sequel
|
|
|
109
151
|
else
|
|
110
152
|
Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
|
|
111
153
|
end
|
|
154
|
+
when Hash
|
|
155
|
+
convert_input_timestamp([:year, :month, :day, :hour, :minute, :second].map{|x| (v[x] || v[x.to_s]).to_i}, input_timezone)
|
|
112
156
|
when Time
|
|
113
157
|
if datetime_class == DateTime
|
|
114
158
|
v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
|
|
@@ -121,8 +165,6 @@ module Sequel
|
|
|
121
165
|
else
|
|
122
166
|
v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
|
|
123
167
|
end
|
|
124
|
-
when Date
|
|
125
|
-
convert_input_timestamp(v.to_s, input_timezone)
|
|
126
168
|
else
|
|
127
169
|
raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
|
|
128
170
|
end
|
|
@@ -135,39 +177,6 @@ module Sequel
|
|
|
135
177
|
raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
|
|
136
178
|
end
|
|
137
179
|
|
|
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
180
|
# Convert the timezone setter argument. Returns argument given by default,
|
|
172
181
|
# exists for easier overriding in extensions.
|
|
173
182
|
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 = 29
|
|
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
|