sequel 3.27.0 → 3.28.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +96 -0
- data/README.rdoc +2 -2
- data/Rakefile +1 -1
- data/doc/association_basics.rdoc +48 -0
- data/doc/opening_databases.rdoc +29 -5
- data/doc/prepared_statements.rdoc +1 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/testing.rdoc +42 -0
- data/doc/transactions.rdoc +97 -0
- data/lib/sequel/adapters/db2.rb +95 -65
- data/lib/sequel/adapters/firebird.rb +25 -219
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/jdbc.rb +12 -0
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/mysql.rb +10 -15
- data/lib/sequel/adapters/odbc.rb +1 -2
- data/lib/sequel/adapters/odbc/db2.rb +5 -5
- data/lib/sequel/adapters/postgres.rb +71 -11
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/mssql.rb +18 -75
- data/lib/sequel/adapters/shared/mysql.rb +13 -0
- data/lib/sequel/adapters/shared/postgres.rb +52 -36
- data/lib/sequel/adapters/shared/sqlite.rb +32 -36
- data/lib/sequel/adapters/sqlite.rb +4 -8
- data/lib/sequel/adapters/tinytds.rb +7 -3
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +6 -5
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/dataset/actions.rb +149 -33
- data/lib/sequel/dataset/features.rb +44 -7
- data/lib/sequel/dataset/misc.rb +9 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -2
- data/lib/sequel/dataset/query.rb +63 -10
- data/lib/sequel/dataset/sql.rb +22 -5
- data/lib/sequel/model.rb +3 -3
- data/lib/sequel/model/associations.rb +250 -27
- data/lib/sequel/model/base.rb +10 -16
- data/lib/sequel/plugins/many_through_many.rb +34 -2
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/sql.rb +94 -51
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/postgres_spec.rb +74 -6
- data/spec/adapters/spec_helper.rb +1 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/database_spec.rb +7 -0
- data/spec/core/dataset_spec.rb +180 -17
- data/spec/core/expression_filters_spec.rb +107 -41
- data/spec/core/spec_helper.rb +11 -0
- data/spec/extensions/many_through_many_spec.rb +115 -1
- data/spec/extensions/prepared_statements_with_pk_spec.rb +3 -3
- data/spec/integration/associations_test.rb +193 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +215 -19
- data/spec/integration/plugin_test.rb +8 -5
- data/spec/integration/prepared_statement_test.rb +91 -98
- data/spec/integration/schema_test.rb +27 -11
- data/spec/integration/spec_helper.rb +10 -0
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +13 -0
- data/spec/model/base_spec.rb +8 -21
- data/spec/model/eager_loading_spec.rb +243 -9
- data/spec/model/model_spec.rb +15 -2
- metadata +16 -4
data/lib/sequel/model/base.rb
CHANGED
@@ -23,7 +23,7 @@ module Sequel
|
|
23
23
|
# stored so when the dataset changes, methods defined with def_dataset_method
|
24
24
|
# will be applied to the new dataset.
|
25
25
|
attr_reader :dataset_methods
|
26
|
-
|
26
|
+
|
27
27
|
# Array of plugin modules loaded by this class
|
28
28
|
#
|
29
29
|
# Sequel::Model.plugins
|
@@ -423,10 +423,12 @@ module Sequel
|
|
423
423
|
@allowed_columns = cols
|
424
424
|
end
|
425
425
|
|
426
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
427
|
-
#
|
426
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
427
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
428
|
+
# <tt>SQL::AliasedExpression</tt>
|
429
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
428
430
|
# If a dataset is used, the model's database is changed to the database of the given
|
429
|
-
# dataset. If a
|
431
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
430
432
|
# database with the table name given. Other arguments raise an +Error+.
|
431
433
|
# Returns self.
|
432
434
|
#
|
@@ -441,15 +443,15 @@ module Sequel
|
|
441
443
|
def set_dataset(ds, opts={})
|
442
444
|
inherited = opts[:inherited]
|
443
445
|
@dataset = case ds
|
444
|
-
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression
|
446
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
445
447
|
@simple_table = db.literal(ds)
|
446
|
-
db
|
448
|
+
db.from(ds)
|
447
449
|
when Dataset
|
448
450
|
@simple_table = nil
|
449
451
|
@db = ds.db
|
450
452
|
ds
|
451
453
|
else
|
452
|
-
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
454
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
453
455
|
end
|
454
456
|
@dataset.row_proc = Proc.new{|r| load(r)}
|
455
457
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
@@ -1684,15 +1686,7 @@ module Sequel
|
|
1684
1686
|
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
1685
1687
|
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
1686
1688
|
def with_pk(pk)
|
1687
|
-
|
1688
|
-
when Array
|
1689
|
-
raise(Error, "single primary key given (#{pk.inspect}) when a composite primary key is expected (#{primary_key.inspect})") unless pk.is_a?(Array)
|
1690
|
-
raise(Error, "composite primary key given (#{pk.inspect}) does not match composite primary key length (#{primary_key.inspect})") if pk.length != primary_key.length
|
1691
|
-
first(primary_key.zip(pk))
|
1692
|
-
else
|
1693
|
-
raise(Error, "composite primary key given (#{pk.inspect}) when a single primary key is expected (#{primary_key.inspect})") if pk.is_a?(Array)
|
1694
|
-
first(primary_key=>pk)
|
1695
|
-
end
|
1689
|
+
first(model.qualified_primary_key_hash(pk))
|
1696
1690
|
end
|
1697
1691
|
end
|
1698
1692
|
|
@@ -58,6 +58,19 @@ module Sequel
|
|
58
58
|
self[:uses_left_composite_keys] ? (0...self[:through].first[:left].length).map{|i| :"x_foreign_key_#{i}_x"} : :x_foreign_key_x
|
59
59
|
end
|
60
60
|
|
61
|
+
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3))
|
62
|
+
def eager_loading_predicate_key
|
63
|
+
self[:eager_loading_predicate_key] ||= begin
|
64
|
+
calculate_edges
|
65
|
+
e = self[:edges].first
|
66
|
+
if self[:uses_left_composite_keys]
|
67
|
+
e[:right].map{|k| SQL::QualifiedIdentifier.new(e[:table], k)}
|
68
|
+
else
|
69
|
+
SQL::QualifiedIdentifier.new(e[:table], e[:right])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
61
74
|
# The list of joins to use when eager graphing
|
62
75
|
def edges
|
63
76
|
self[:edges] || calculate_edges || self[:edges]
|
@@ -182,13 +195,28 @@ module Sequel
|
|
182
195
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
183
196
|
opts[:eager_loader] ||= lambda do |eo|
|
184
197
|
h = eo[:key_hash][left_pk]
|
185
|
-
|
198
|
+
rows = eo[:rows]
|
199
|
+
rows.each{|object| object.associations[name] = []}
|
186
200
|
ds = opts.associated_class
|
187
201
|
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
188
202
|
ft = opts[:final_reverse_edge]
|
189
203
|
conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, h.keys]] : [[left_key, h.keys]]
|
190
204
|
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
|
191
|
-
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo)
|
205
|
+
ds = model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo)
|
206
|
+
case opts.eager_limit_strategy
|
207
|
+
when :window_function
|
208
|
+
delete_rn = true
|
209
|
+
rn = ds.row_number_column
|
210
|
+
ds = apply_window_function_eager_limit_strategy(ds, opts)
|
211
|
+
when :correlated_subquery
|
212
|
+
ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
|
213
|
+
dsa = ds.send(:dataset_alias, 2)
|
214
|
+
opts.reverse_edges.each{|t| xds = xds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
215
|
+
xds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.map{|k| [k, SQL::QualifiedIdentifier.new(ft[:table], k)]}, :table_alias=>dsa)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
ds.all do |assoc_record|
|
219
|
+
assoc_record.values.delete(rn) if delete_rn
|
192
220
|
hash_key = if uses_lcks
|
193
221
|
left_key_alias.map{|k| assoc_record.values.delete(k)}
|
194
222
|
else
|
@@ -197,6 +225,10 @@ module Sequel
|
|
197
225
|
next unless objects = h[hash_key]
|
198
226
|
objects.each{|object| object.associations[name].push(assoc_record)}
|
199
227
|
end
|
228
|
+
if opts.eager_limit_strategy == :ruby
|
229
|
+
limit, offset = opts.limit_and_offset
|
230
|
+
rows.each{|o| o.associations[name] = o.associations[name].slice(offset||0, limit) || []}
|
231
|
+
end
|
200
232
|
end
|
201
233
|
|
202
234
|
join_type = opts[:graph_join_type]
|
@@ -31,7 +31,7 @@ module Sequel
|
|
31
31
|
# Return a prepared statement that can be used to lookup a row given a dataset for the row matching
|
32
32
|
# the primary key.
|
33
33
|
def prepared_lookup_dataset(ds)
|
34
|
-
@prepared_statements[:lookup_sql][ds.sql] ||= prepare_statement(ds.filter(prepared_statement_key_array(primary_key)), :first)
|
34
|
+
@prepared_statements[:lookup_sql][ds.sql] ||= prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
data/lib/sequel/sql.rb
CHANGED
@@ -41,6 +41,11 @@ module Sequel
|
|
41
41
|
# Time subclass that gets literalized with only the time value, so it operates
|
42
42
|
# like a standard SQL time type.
|
43
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
|
44
49
|
end
|
45
50
|
|
46
51
|
# The SQL module holds classes whose instances represent SQL fragments.
|
@@ -149,14 +154,17 @@ module Sequel
|
|
149
154
|
# Operator symbols that take exactly two arguments
|
150
155
|
TWO_ARITY_OPERATORS = [:'=', :'!=', :LIKE, :'NOT LIKE', \
|
151
156
|
:~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
|
152
|
-
INEQUALITY_OPERATORS +
|
157
|
+
INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
|
153
158
|
|
154
159
|
# Operator symbols that take one or more arguments
|
155
|
-
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS
|
160
|
+
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
|
156
161
|
|
157
162
|
# Operator symbols that take only a single argument
|
158
163
|
ONE_ARITY_OPERATORS = [:NOT, :NOOP, :'B~']
|
159
164
|
|
165
|
+
# Custom expressions that may have different syntax on different databases
|
166
|
+
CUSTOM_EXPRESSIONS = [:extract]
|
167
|
+
|
160
168
|
# An array of args for this object
|
161
169
|
attr_reader :args
|
162
170
|
|
@@ -185,6 +193,8 @@ module Sequel
|
|
185
193
|
args[1] = orig_args[1] if IN_OPERATORS.include?(op)
|
186
194
|
when *ONE_ARITY_OPERATORS
|
187
195
|
raise(Error, "The #{op} operator requires a single argument") unless args.length == 1
|
196
|
+
when *CUSTOM_EXPRESSIONS
|
197
|
+
# nothing
|
188
198
|
else
|
189
199
|
raise(Error, "Invalid operator #{op}")
|
190
200
|
end
|
@@ -224,14 +234,7 @@ module Sequel
|
|
224
234
|
# ~:a.sql_number # ~"a"
|
225
235
|
module BitwiseMethods
|
226
236
|
ComplexExpression::BITWISE_OPERATORS.each do |o|
|
227
|
-
|
228
|
-
case ce
|
229
|
-
when BooleanExpression, StringExpression
|
230
|
-
raise(Sequel::Error, "cannot apply #{o} to a non-numeric expression")
|
231
|
-
else
|
232
|
-
NumericExpression.new(o, self, ce)
|
233
|
-
end
|
234
|
-
end
|
237
|
+
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
235
238
|
end
|
236
239
|
|
237
240
|
# Do the bitwise compliment of the self
|
@@ -249,16 +252,24 @@ module Sequel
|
|
249
252
|
# :a & :b # "a" AND "b"
|
250
253
|
# :a | :b # "a" OR "b"
|
251
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)
|
252
261
|
module BooleanMethods
|
253
262
|
ComplexExpression::BOOLEAN_OPERATOR_METHODS.each do |m, o|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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
|
260
271
|
end
|
261
|
-
|
272
|
+
END
|
262
273
|
end
|
263
274
|
|
264
275
|
# Create a new BooleanExpression with NOT, representing the inversion of whatever self represents.
|
@@ -326,7 +337,7 @@ module Sequel
|
|
326
337
|
# doesn't use the standard function calling convention, and it
|
327
338
|
# doesn't work on all databases.
|
328
339
|
def extract(datetime_part)
|
329
|
-
|
340
|
+
NumericExpression.new(:extract, datetime_part, self)
|
330
341
|
end
|
331
342
|
|
332
343
|
# Return a BooleanExpression representation of +self+.
|
@@ -372,34 +383,12 @@ module Sequel
|
|
372
383
|
# 'a'.lit <= :b # a <= "b"
|
373
384
|
module InequalityMethods
|
374
385
|
ComplexExpression::INEQUALITY_OPERATORS.each do |o|
|
375
|
-
|
376
|
-
case ce
|
377
|
-
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
|
378
|
-
raise(Error, "cannot apply #{o} to a boolean expression")
|
379
|
-
else
|
380
|
-
BooleanExpression.new(o, self, ce)
|
381
|
-
end
|
382
|
-
end
|
386
|
+
module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
383
387
|
end
|
384
388
|
end
|
385
389
|
|
386
|
-
#
|
387
|
-
# +ComplexExpression+ subclass it is included in, so that
|
388
|
-
# attempting to use boolean input when initializing a +NumericExpression+
|
389
|
-
# or +StringExpression+ results in an error. It is not expected to be
|
390
|
-
# used directly.
|
390
|
+
# Only exists for backwards compatibility, ignore it.
|
391
391
|
module NoBooleanInputMethods
|
392
|
-
# Raise an +Error+ if one of the args would be boolean in an SQL
|
393
|
-
# context, otherwise call super.
|
394
|
-
def initialize(op, *args)
|
395
|
-
args.each do |a|
|
396
|
-
case a
|
397
|
-
when BooleanExpression, TrueClass, FalseClass, NilClass, Hash, ::Array
|
398
|
-
raise(Error, "cannot apply #{op} to a boolean expression")
|
399
|
-
end
|
400
|
-
end
|
401
|
-
super
|
402
|
-
end
|
403
392
|
end
|
404
393
|
|
405
394
|
# This module includes the standard mathematical methods (+, -, *, and /)
|
@@ -410,15 +399,26 @@ module Sequel
|
|
410
399
|
# :a - :b # "a" - "b"
|
411
400
|
# :a * :b # "a" * "b"
|
412
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'
|
413
407
|
module NumericMethods
|
414
408
|
ComplexExpression::MATHEMATICAL_OPERATORS.each do |o|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
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)
|
422
422
|
end
|
423
423
|
end
|
424
424
|
end
|
@@ -606,6 +606,21 @@ module Sequel
|
|
606
606
|
BooleanExpression.new(:NOT, ce)
|
607
607
|
end
|
608
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
|
609
624
|
end
|
610
625
|
|
611
626
|
# Represents an SQL CASE expression, used for conditional branching in SQL.
|
@@ -673,6 +688,21 @@ module Sequel
|
|
673
688
|
include CastMethods
|
674
689
|
include OrderMethods
|
675
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
|
676
706
|
end
|
677
707
|
|
678
708
|
# Represents constants or psuedo-constants (e.g. +CURRENT_DATE+) in SQL.
|
@@ -847,7 +877,16 @@ module Sequel
|
|
847
877
|
include BitwiseMethods
|
848
878
|
include NumericMethods
|
849
879
|
include InequalityMethods
|
850
|
-
|
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
|
851
890
|
end
|
852
891
|
|
853
892
|
# Represents a column/expression to order the result set by.
|
@@ -913,7 +952,6 @@ module Sequel
|
|
913
952
|
include StringMethods
|
914
953
|
include StringConcatenationMethods
|
915
954
|
include InequalityMethods
|
916
|
-
include NoBooleanInputMethods
|
917
955
|
|
918
956
|
# Map of [regexp, case_insenstive] to +ComplexExpression+ operator symbol
|
919
957
|
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
@@ -962,6 +1000,11 @@ module Sequel
|
|
962
1000
|
end
|
963
1001
|
end
|
964
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
|
965
1008
|
end
|
966
1009
|
|
967
1010
|
# Represents an SQL array access, with multiple possible arguments.
|
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 = 28
|
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
|