sequel 5.13.0 → 5.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/doc/postgresql.rdoc +20 -0
- data/doc/release_notes/5.14.0.txt +63 -0
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +5 -0
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +5 -0
- data/lib/sequel/adapters/shared/mysql.rb +5 -0
- data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/sql.rb +29 -6
- data/lib/sequel/extensions/constraint_validations.rb +38 -27
- data/lib/sequel/extensions/integer64.rb +1 -1
- data/lib/sequel/model/base.rb +24 -7
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/tree.rb +19 -16
- data/lib/sequel/sql.rb +42 -17
- data/lib/sequel/version.rb +1 -1
- data/spec/core/dataset_spec.rb +12 -0
- data/spec/core/expression_filters_spec.rb +22 -0
- data/spec/extensions/constraint_validations_spec.rb +28 -2
- data/spec/extensions/defaults_setter_spec.rb +9 -0
- data/spec/extensions/tree_spec.rb +3 -0
- data/spec/integration/dataset_test.rb +9 -0
- data/spec/integration/plugin_test.rb +74 -2
- data/spec/model/record_spec.rb +39 -28
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a950f8be723b867d5ecc782005c2374fa84a05a6b54c4ba55652f5442de1c90b
|
4
|
+
data.tar.gz: c5b2c92ee3580933b9fcd0525fdb67a79f5915f8b8e337e4731b9046f771dbf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4671d49935656b0ec907f10d3d4a6b25e29815b328b7661e55d95e4bfabbf84818de2b3550d92d86f444fbd69a561f099c859a5b1156303e10d58a7402641ebc
|
7
|
+
data.tar.gz: 5488428f7290a548344849919a7895dfd1e8d7cbe7990c2b261b4118e2a46845d9bde30b00673928511e2e650b3e4098f81cb2087d8f42227653607d3ae90c6f
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 5.14.0 (2018-11-01)
|
2
|
+
|
3
|
+
* Drop defaulting the :port option to 5432 in the postgres adapter, so that setting the :service option in :driver_options works (jeremyevans) (#1558)
|
4
|
+
|
5
|
+
* Do not cache values for columns without parseable defaults when using :cache option in defaults_setter plugin (jeremyevans)
|
6
|
+
|
7
|
+
* Emulate NULLS FIRST/LAST ordering on databases that do not natively support it (jeremyevans)
|
8
|
+
|
9
|
+
* Do not modify boolean expressions created from string or array if string or array is modified (jeremyevans)
|
10
|
+
|
11
|
+
* Make roots and roots_dataset dataset methods instead of class methods in the tree plugin (JelF) (#1554)
|
12
|
+
|
13
|
+
* Do not cache dataset SQL if dataset uses subquery that cannot cache SQL (jeremyevans)
|
14
|
+
|
15
|
+
* Make Model#=== work correctly for models with composite primary keys (jeremyevans)
|
16
|
+
|
17
|
+
* Add Model#pk_equal? as a more descriptive name for Model#=== (AlexWayfer) (#1550)
|
18
|
+
|
19
|
+
* Do not push down expression inversion in cases where it may result in incorrect behavior (e.g. ANY/SOME/ALL operators) (jeremyevans) (#1549)
|
20
|
+
|
1
21
|
=== 5.13.0 (2018-10-01)
|
2
22
|
|
3
23
|
* Support :single_value type in prepared statements (rintaun) (#1547)
|
data/doc/postgresql.rdoc
CHANGED
@@ -63,6 +63,14 @@ into the Database instance:
|
|
63
63
|
DB.extension :pg_array
|
64
64
|
Sequel.extension :pg_array_ops
|
65
65
|
|
66
|
+
With regard to common database types, please note that the generic String type
|
67
|
+
is +text+ on PostgreSQL and not <tt>varchar(255)</tt> as it is on some other
|
68
|
+
databases. +text+ is PostgreSQL's recommended type for storage of text data,
|
69
|
+
and is more similar to Ruby's String type as it allows for unlimited length.
|
70
|
+
If you want to set a maximum size for a text column, you must specify a
|
71
|
+
<tt>:size</tt> option. This will use a <tt>varchar($size)</tt> type and
|
72
|
+
impose a maximum size for the column.
|
73
|
+
|
66
74
|
== PostgreSQL-specific DDL Support
|
67
75
|
|
68
76
|
=== Exclusion Constraints
|
@@ -301,6 +309,18 @@ can pass an +:update+ option with a hash of values to update. You must pass eit
|
|
301
309
|
DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
|
302
310
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
303
311
|
# ON CONFLICT (a) DO UPDATE SET b = excluded.b
|
312
|
+
|
313
|
+
If you want to update existing rows but using the current value of the column, you can build
|
314
|
+
the desired calculation using <tt>Sequel[]</tt>
|
315
|
+
|
316
|
+
DB[:table]
|
317
|
+
.insert_conflict(
|
318
|
+
target: :a,
|
319
|
+
update: {b: Sequel[:excluded][:b] + Sequel[:table][:a]}
|
320
|
+
)
|
321
|
+
.import([:a, :b], [ [1, 2] ])
|
322
|
+
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
323
|
+
# ON CONFLICT (a) DO UPDATE SET b = (excluded.b + table.a)
|
304
324
|
|
305
325
|
Additionally, if you only want to do the update in certain cases, you can specify an
|
306
326
|
+:update_where+ option, which will be used as a filter. If the row doesn't match the
|
@@ -0,0 +1,63 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* The :nulls option when creating ordered expressions is now supported
|
4
|
+
on all databases that Sequel ships support for. For databases that
|
5
|
+
do not support NULLS FIRST/NULLS LAST, support is emulated.
|
6
|
+
|
7
|
+
ds.order(Sequel.asc(:name, :nulls=>:last))
|
8
|
+
# When emulated:
|
9
|
+
# ORDER BY (CASE WHEN (name IS NULL) THEN 2 ELSE 1 END), name ASC
|
10
|
+
|
11
|
+
* Model#pk_equal? has been added as a more descriptive name for
|
12
|
+
Model#===. Model#=== is now an alias of Model#pk_equal?.
|
13
|
+
|
14
|
+
* The roots and roots_dataset class methods in the tree plugin are now
|
15
|
+
also available as dataset methods.
|
16
|
+
|
17
|
+
= Other Improvements
|
18
|
+
|
19
|
+
* Inverting expressions using the ANY/SOME/ALL SQL operators now works
|
20
|
+
correctly:
|
21
|
+
|
22
|
+
# Sequel <5.14.0
|
23
|
+
Sequel.~(:a=>Sequel.function(:any, :x))
|
24
|
+
# "(a != any(x))"
|
25
|
+
|
26
|
+
# Sequel >=5.14.0
|
27
|
+
Sequel.~(:a=>Sequel.function(:any, :x))
|
28
|
+
# "NOT (a = any(x))"
|
29
|
+
|
30
|
+
Sequel has always tried to push inversion down to create SQL that is
|
31
|
+
easier to reason about. However, inversion cannot be pushed down if
|
32
|
+
an ANY/SOME/ALL SQL operator is used, because that is a different
|
33
|
+
type of operation that just happens to use the same syntax. Sequel
|
34
|
+
now avoids inversion push down for boolean operators where the
|
35
|
+
right hand side is an SQL::Function, LiteralString, or
|
36
|
+
SQL::PlaceholderLiteralString.
|
37
|
+
|
38
|
+
* When creating a boolean expression from a hash or array of pairs, if
|
39
|
+
the right hand side is an unfrozen array and string, use a frozen
|
40
|
+
copy in the expression, so that mutating the array or string
|
41
|
+
argument later does not affect the expression.
|
42
|
+
|
43
|
+
* When using the defaults_setter plugin with the :cache option, do not
|
44
|
+
cache values for columns without parseable defaults. If the default
|
45
|
+
value exists but is not parseable, caching such values could result
|
46
|
+
in incorrect behavior if the model instance is saved later.
|
47
|
+
|
48
|
+
* For models with composite primary keys, Model#=== now returns false
|
49
|
+
if any primary key value is nil, mirroring the behavior for the
|
50
|
+
scalar primary key case.
|
51
|
+
|
52
|
+
* Model datasets no longer cache SQL if they include a subquery that
|
53
|
+
cannot cache SQL.
|
54
|
+
|
55
|
+
* The SQL used for constraints in the constraint_validations
|
56
|
+
extension when the :allow_nil option is used is now clearer and
|
57
|
+
easier to understand.
|
58
|
+
|
59
|
+
* The postgres adapter no longer specifies a default port when using
|
60
|
+
the pg driver, in order to work with configurations where the
|
61
|
+
:service option is used in the :driver_options hash. The pg driver
|
62
|
+
defaults to port 5432 if no port is given, so this should not affect
|
63
|
+
backwards compatibility.
|
@@ -418,6 +418,11 @@ module Sequel
|
|
418
418
|
false
|
419
419
|
end
|
420
420
|
|
421
|
+
# At least some versions of DB do not support NULLS FIRST/LAST.
|
422
|
+
def requires_emulating_nulls_first?
|
423
|
+
true
|
424
|
+
end
|
425
|
+
|
421
426
|
# Modify the sql to limit the number of rows returned.
|
422
427
|
# Uses :offset_strategy Database option to determine how to format the
|
423
428
|
# limit and offset.
|
@@ -1046,6 +1046,11 @@ module Sequel
|
|
1046
1046
|
end
|
1047
1047
|
end
|
1048
1048
|
|
1049
|
+
# MSSQL does not natively support NULLS FIRST/LAST.
|
1050
|
+
def requires_emulating_nulls_first?
|
1051
|
+
true
|
1052
|
+
end
|
1053
|
+
|
1049
1054
|
# MSSQL supports 100-nsec precision for time columns, but ruby by
|
1050
1055
|
# default only supports usec precision.
|
1051
1056
|
def sqltime_precision
|
@@ -1008,6 +1008,11 @@ module Sequel
|
|
1008
1008
|
super || key == :insert_ignore || key == :update_ignore || key == :on_duplicate_key_update
|
1009
1009
|
end
|
1010
1010
|
|
1011
|
+
# MySQL does not natively support NULLS FIRST/LAST.
|
1012
|
+
def requires_emulating_nulls_first?
|
1013
|
+
true
|
1014
|
+
end
|
1015
|
+
|
1011
1016
|
def select_only_offset_sql(sql)
|
1012
1017
|
sql << " LIMIT "
|
1013
1018
|
literal_append(sql, @opts[:offset])
|
@@ -825,6 +825,11 @@ module Sequel
|
|
825
825
|
end
|
826
826
|
end
|
827
827
|
|
828
|
+
# SQLite does not natively support NULLS FIRST/LAST.
|
829
|
+
def requires_emulating_nulls_first?
|
830
|
+
true
|
831
|
+
end
|
832
|
+
|
828
833
|
# SQLite does not support FOR UPDATE, but silently ignore it
|
829
834
|
# instead of raising an error for compatibility with other
|
830
835
|
# databases.
|
@@ -220,6 +220,11 @@ module Sequel
|
|
220
220
|
true
|
221
221
|
end
|
222
222
|
|
223
|
+
# Whether ORDER BY col NULLS FIRST/LAST must be emulated.
|
224
|
+
def requires_emulating_nulls_first?
|
225
|
+
false
|
226
|
+
end
|
227
|
+
|
223
228
|
# Whether common table expressions are supported in UNION/INTERSECT/EXCEPT clauses.
|
224
229
|
def supports_cte_in_compounds?
|
225
230
|
supports_cte_in_subqueries?
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -559,13 +559,30 @@ module Sequel
|
|
559
559
|
|
560
560
|
# Append literalization of ordered expression to SQL string.
|
561
561
|
def ordered_expression_sql_append(sql, oe)
|
562
|
+
if emulate = requires_emulating_nulls_first?
|
563
|
+
case oe.nulls
|
564
|
+
when :first
|
565
|
+
null_order = 0
|
566
|
+
when :last
|
567
|
+
null_order = 2
|
568
|
+
end
|
569
|
+
|
570
|
+
if null_order
|
571
|
+
literal_append(sql, Sequel.case({{oe.expression=>nil}=>null_order}, 1))
|
572
|
+
sql << ", "
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
562
576
|
literal_append(sql, oe.expression)
|
563
577
|
sql << (oe.descending ? ' DESC' : ' ASC')
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
578
|
+
|
579
|
+
unless emulate
|
580
|
+
case oe.nulls
|
581
|
+
when :first
|
582
|
+
sql << " NULLS FIRST"
|
583
|
+
when :last
|
584
|
+
sql << " NULLS LAST"
|
585
|
+
end
|
569
586
|
end
|
570
587
|
end
|
571
588
|
|
@@ -1553,7 +1570,13 @@ module Sequel
|
|
1553
1570
|
|
1554
1571
|
# Append literalization of the subselect to SQL string.
|
1555
1572
|
def subselect_sql_append(sql, ds)
|
1556
|
-
subselect_sql_dataset(sql, ds)
|
1573
|
+
sds = subselect_sql_dataset(sql, ds)
|
1574
|
+
sds.sql
|
1575
|
+
unless sds.send(:cache_sql?)
|
1576
|
+
# If subquery dataset does not allow caching SQL,
|
1577
|
+
# then this dataset should not allow caching SQL.
|
1578
|
+
disable_sql_caching!
|
1579
|
+
end
|
1557
1580
|
end
|
1558
1581
|
|
1559
1582
|
def subselect_sql_dataset(sql, ds)
|
@@ -355,6 +355,18 @@ module Sequel
|
|
355
355
|
super
|
356
356
|
end
|
357
357
|
|
358
|
+
def constraint_validation_expression(cols, allow_nil)
|
359
|
+
exprs = cols.map do |c|
|
360
|
+
expr = yield c
|
361
|
+
if allow_nil
|
362
|
+
Sequel.|({c=>nil}, expr)
|
363
|
+
else
|
364
|
+
Sequel.&(Sequel.~(c=>nil), expr)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
Sequel.&(*exprs)
|
368
|
+
end
|
369
|
+
|
358
370
|
# For the given table, generator, and validations, add constraints
|
359
371
|
# to the generator for each of the validations, as well as adding
|
360
372
|
# validation metadata to the constraint validations table.
|
@@ -365,28 +377,44 @@ module Sequel
|
|
365
377
|
|
366
378
|
case validation_type
|
367
379
|
when :presence
|
368
|
-
|
369
|
-
|
380
|
+
strings, non_strings = columns.partition{|c| generator_string_column?(generator, table, c)}
|
381
|
+
if !non_strings.empty? && !allow_nil
|
382
|
+
non_strings_expr = Sequel.&(*non_strings.map{|c| Sequel.~(c=>nil)})
|
383
|
+
end
|
384
|
+
|
385
|
+
unless strings.empty?
|
386
|
+
strings_expr = constraint_validation_expression(strings, allow_nil){|c| Sequel.~(Sequel.trim(c) => blank_string_value)}
|
387
|
+
end
|
388
|
+
|
389
|
+
expr = if non_strings_expr && strings_expr
|
390
|
+
Sequel.&(strings_expr, non_strings_expr)
|
391
|
+
else
|
392
|
+
strings_expr || non_strings_expr
|
393
|
+
end
|
394
|
+
|
395
|
+
if expr
|
396
|
+
generator.constraint(constraint, expr)
|
397
|
+
end
|
370
398
|
when :exact_length
|
371
|
-
|
399
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {Sequel.char_length(c) => arg}})
|
372
400
|
when :min_length
|
373
|
-
|
401
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.char_length(c) >= arg})
|
374
402
|
when :max_length
|
375
|
-
|
403
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.char_length(c) <= arg})
|
376
404
|
when *REVERSE_OPERATOR_MAP.keys
|
377
|
-
|
405
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.identifier(c).public_send(REVERSE_OPERATOR_MAP[validation_type], arg)})
|
378
406
|
when :length_range
|
379
407
|
op = arg.exclude_end? ? :< : :<=
|
380
|
-
|
408
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| (Sequel.char_length(c) >= arg.begin) & Sequel.char_length(c).public_send(op, arg.end)})
|
381
409
|
arg = "#{arg.begin}..#{'.' if arg.exclude_end?}#{arg.end}"
|
382
410
|
when :format
|
383
|
-
|
411
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {c => arg}})
|
384
412
|
if arg.casefold?
|
385
413
|
validation_type = :iformat
|
386
414
|
end
|
387
415
|
arg = arg.source
|
388
416
|
when :includes
|
389
|
-
|
417
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {c => arg}})
|
390
418
|
if arg.is_a?(Range)
|
391
419
|
if arg.begin.is_a?(Integer) && arg.end.is_a?(Integer)
|
392
420
|
validation_type = :includes_int_range
|
@@ -407,7 +435,7 @@ module Sequel
|
|
407
435
|
raise Error, "validates includes only supports arrays and ranges currently, cannot handle: #{arg.inspect}"
|
408
436
|
end
|
409
437
|
when :like, :ilike
|
410
|
-
|
438
|
+
generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.public_send(validation_type, c, arg)})
|
411
439
|
when :unique
|
412
440
|
generator.unique(columns, :name=>constraint)
|
413
441
|
columns = [columns.join(',')]
|
@@ -438,23 +466,6 @@ module Sequel
|
|
438
466
|
ds.multi_insert(rows.flatten)
|
439
467
|
end
|
440
468
|
|
441
|
-
# Add the constraint to the generator, including a NOT NULL constraint
|
442
|
-
# for all columns unless the :allow_nil option is given.
|
443
|
-
def generator_add_constraint_from_validation(generator, val, cons)
|
444
|
-
if val[:allow_nil]
|
445
|
-
nil_cons = Sequel[val[:columns].map{|c| [c, nil]}]
|
446
|
-
cons = Sequel.|(nil_cons, cons) if cons
|
447
|
-
else
|
448
|
-
nil_cons = Sequel.negate(val[:columns].map{|c| [c, nil]})
|
449
|
-
cons = cons ? Sequel.&(nil_cons, cons) : nil_cons
|
450
|
-
end
|
451
|
-
|
452
|
-
if cons
|
453
|
-
generator.constraint(val[:name], cons)
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
|
458
469
|
# Introspect the generator to determine if column
|
459
470
|
# created is a string or not.
|
460
471
|
def generator_string_column?(generator, table, c)
|
@@ -20,7 +20,7 @@
|
|
20
20
|
#
|
21
21
|
module Sequel
|
22
22
|
module Integer64
|
23
|
-
# Use
|
23
|
+
# Use same type as used for :Bignum by default for generic integer value.
|
24
24
|
def type_literal_generic_integer(column)
|
25
25
|
type_literal_generic_bignum_symbol(column)
|
26
26
|
end
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1103,16 +1103,33 @@ module Sequel
|
|
1103
1103
|
eql?(obj)
|
1104
1104
|
end
|
1105
1105
|
|
1106
|
-
#
|
1107
|
-
#
|
1106
|
+
# Case equality. By default, checks equality of the primary key value, see
|
1107
|
+
# pk_equal?.
|
1108
1108
|
#
|
1109
|
-
# Artist[1] === Artist[1] # true
|
1110
|
-
# Artist.new === Artist.new # false
|
1111
|
-
# Artist[1].set(:name=>'Bob')
|
1109
|
+
# Artist[1] === Artist[1] # => true
|
1110
|
+
# Artist.new === Artist.new # => false
|
1111
|
+
# Artist[1].set(:name=>'Bob') === Artist[1] # => true
|
1112
1112
|
def ===(obj)
|
1113
|
-
|
1113
|
+
case pkv = pk
|
1114
|
+
when nil
|
1115
|
+
return false
|
1116
|
+
when Array
|
1117
|
+
return false if pk.any?(&:nil?)
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
(obj.class == model) && (obj.pk == pkv)
|
1114
1121
|
end
|
1115
|
-
|
1122
|
+
|
1123
|
+
# If the receiver has a primary key value, returns true if the objects have
|
1124
|
+
# the same class and primary key value.
|
1125
|
+
# If the receiver's primary key value is nil or is an array containing
|
1126
|
+
# nil, returns false.
|
1127
|
+
#
|
1128
|
+
# Artist[1].pk_equal?(Artist[1]) # => true
|
1129
|
+
# Artist.new.pk_equal?(Artist.new) # => false
|
1130
|
+
# Artist[1].set(:name=>'Bob').pk_equal?(Artist[1]) # => true
|
1131
|
+
alias pk_equal? ===
|
1132
|
+
|
1116
1133
|
# class is defined in Object, but it is also a keyword,
|
1117
1134
|
# and since a lot of instance methods call class methods,
|
1118
1135
|
# this alias makes it so you can use model instead of
|
@@ -116,7 +116,7 @@ module Sequel
|
|
116
116
|
# Use default value for a new record if values doesn't already contain an entry for it.
|
117
117
|
def [](k)
|
118
118
|
if new? && !values.has_key?(k)
|
119
|
-
v = model.default_values
|
119
|
+
v = model.default_values.fetch(k){return}
|
120
120
|
v = v.call if v.respond_to?(:call)
|
121
121
|
values[k] = v if model.cache_default_values?
|
122
122
|
v
|
data/lib/sequel/plugins/tree.rb
CHANGED
@@ -70,6 +70,7 @@ module Sequel
|
|
70
70
|
attr_reader :children_association_name
|
71
71
|
|
72
72
|
Plugins.inherited_instance_variables(self, :@parent_column=>nil, :@tree_order=>nil, :@parent_association_name=>nil, :@children_association_name=>nil)
|
73
|
+
Plugins.def_dataset_methods(self, [:roots, :roots_dataset])
|
73
74
|
|
74
75
|
# Should freeze tree order if it is an array when freezing the model class.
|
75
76
|
def freeze
|
@@ -77,22 +78,6 @@ module Sequel
|
|
77
78
|
|
78
79
|
super
|
79
80
|
end
|
80
|
-
|
81
|
-
# Returns list of all root nodes (those with no parent nodes).
|
82
|
-
#
|
83
|
-
# TreeClass.roots # => [root1, root2]
|
84
|
-
def roots
|
85
|
-
roots_dataset.all
|
86
|
-
end
|
87
|
-
|
88
|
-
# Returns the dataset for retrieval of all root nodes
|
89
|
-
#
|
90
|
-
# TreeClass.roots_dataset # => Sequel::Dataset instance
|
91
|
-
def roots_dataset
|
92
|
-
ds = where(Sequel.or(Array(parent_column).zip([])))
|
93
|
-
ds = ds.order(*tree_order) if tree_order
|
94
|
-
ds
|
95
|
-
end
|
96
81
|
end
|
97
82
|
|
98
83
|
module InstanceMethods
|
@@ -154,6 +139,24 @@ module Sequel
|
|
154
139
|
end
|
155
140
|
end
|
156
141
|
|
142
|
+
module DatasetMethods
|
143
|
+
# Returns list of all root nodes (those with no parent nodes).
|
144
|
+
#
|
145
|
+
# TreeClass.roots # => [root1, root2]
|
146
|
+
def roots
|
147
|
+
roots_dataset.all
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the dataset for retrieval of all root nodes
|
151
|
+
#
|
152
|
+
# TreeClass.roots_dataset # => Sequel::Dataset instance
|
153
|
+
def roots_dataset
|
154
|
+
ds = where(Sequel.or(Array(model.parent_column).zip([])))
|
155
|
+
ds = ds.order(*model.tree_order) if model.tree_order
|
156
|
+
ds
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
157
160
|
# Plugin included when :single_root option is passed.
|
158
161
|
module SingleRoot
|
159
162
|
module ClassMethods
|
data/lib/sequel/sql.rb
CHANGED
@@ -265,12 +265,12 @@ module Sequel
|
|
265
265
|
# methods overlap with the standard +BooleanMethods methods+, and they only
|
266
266
|
# make sense for integers, they are only included in +NumericExpression+.
|
267
267
|
#
|
268
|
-
# :a.sql_number & :b # "a" & "b"
|
269
|
-
# :a.sql_number | :b # "a" | "b"
|
270
|
-
# :a.sql_number ^ :b # "a" ^ "b"
|
271
|
-
# :a.sql_number << :b # "a" << "b"
|
272
|
-
# :a.sql_number >> :b # "a" >> "b"
|
273
|
-
#
|
268
|
+
# Sequel[:a].sql_number & :b # "a" & "b"
|
269
|
+
# Sequel[:a].sql_number | :b # "a" | "b"
|
270
|
+
# Sequel[:a].sql_number ^ :b # "a" ^ "b"
|
271
|
+
# Sequel[:a].sql_number << :b # "a" << "b"
|
272
|
+
# Sequel[:a].sql_number >> :b # "a" >> "b"
|
273
|
+
# ~Sequel[:a].sql_number # ~"a"
|
274
274
|
module BitwiseMethods
|
275
275
|
ComplexExpression::BITWISE_OPERATORS.each do |o|
|
276
276
|
module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
@@ -354,9 +354,15 @@ module Sequel
|
|
354
354
|
end
|
355
355
|
|
356
356
|
# Return an <tt>SQL::CaseExpression</tt> created with the given arguments.
|
357
|
+
# The first argument are the <tt>WHEN</tt>/<tt>THEN</tt> conditions,
|
358
|
+
# specified as an array or a hash. The second argument is the
|
359
|
+
# <tt>ELSE</tt> default value. The third optional argument is the
|
360
|
+
# <tt>CASE</tt> expression.
|
357
361
|
#
|
358
|
-
# Sequel.case(
|
362
|
+
# Sequel.case({a: 1}, 0) # SQL: CASE WHEN a THEN 1 ELSE 0 END
|
359
363
|
# Sequel.case({a: 1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
|
364
|
+
# Sequel.case({{a: [2,3]} => 1}, 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
|
365
|
+
# Sequel.case([[{a: [2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
|
360
366
|
def case(*args)
|
361
367
|
SQL::CaseExpression.new(*args)
|
362
368
|
end
|
@@ -745,10 +751,10 @@ module Sequel
|
|
745
751
|
# This module includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
|
746
752
|
# used in a numeric or string context in SQL.
|
747
753
|
#
|
748
|
-
# Sequel
|
749
|
-
# Sequel
|
750
|
-
# Sequel
|
751
|
-
# Sequel
|
754
|
+
# Sequel[:a] > :b # a > "b"
|
755
|
+
# Sequel[:a] < :b # a > "b"
|
756
|
+
# Sequel[:a] >= :b # a >= "b"
|
757
|
+
# Sequel[:a] <= :b # a <= "b"
|
752
758
|
module InequalityMethods
|
753
759
|
ComplexExpression::INEQUALITY_OPERATORS.each do |o|
|
754
760
|
module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
|
@@ -759,10 +765,10 @@ module Sequel
|
|
759
765
|
# that are defined on objects that can be used in a numeric context in SQL
|
760
766
|
# (+Symbol+, +LiteralString+, and +SQL::GenericExpression+).
|
761
767
|
#
|
762
|
-
# :a + :b # "a" + "b"
|
763
|
-
# :a - :b # "a" - "b"
|
764
|
-
# :a * :b # "a" * "b"
|
765
|
-
# :a / :b # "a" / "b"
|
768
|
+
# Sequel[:a] + :b # "a" + "b"
|
769
|
+
# Sequel[:a] - :b # "a" - "b"
|
770
|
+
# Sequel[:a] * :b # "a" * "b"
|
771
|
+
# Sequel[:a] / :b # "a" / "b"
|
766
772
|
#
|
767
773
|
# One exception to this is if + is called with a +String+ or +StringExpression+,
|
768
774
|
# in which case the || operator is used instead of the + operator:
|
@@ -1079,7 +1085,13 @@ module Sequel
|
|
1079
1085
|
expr = new(:AND, expr, new(r.exclude_end? ? :< : :<=, l, r.end))
|
1080
1086
|
end
|
1081
1087
|
expr
|
1082
|
-
when ::Array
|
1088
|
+
when ::Array
|
1089
|
+
r = r.dup.freeze unless r.frozen?
|
1090
|
+
new(:IN, l, r)
|
1091
|
+
when ::String
|
1092
|
+
r = r.dup.freeze unless r.frozen?
|
1093
|
+
new(:'=', l, r)
|
1094
|
+
when ::Sequel::Dataset
|
1083
1095
|
new(:IN, l, r)
|
1084
1096
|
when NegativeBooleanConstant
|
1085
1097
|
new(:"IS NOT", l, r.constant)
|
@@ -1111,8 +1123,21 @@ module Sequel
|
|
1111
1123
|
case op = ce.op
|
1112
1124
|
when :AND, :OR
|
1113
1125
|
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
|
1114
|
-
|
1126
|
+
when :IN, :"NOT IN"
|
1115
1127
|
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
|
1128
|
+
else
|
1129
|
+
if ce.args.length == 2
|
1130
|
+
case ce.args[1]
|
1131
|
+
when Function, LiteralString, PlaceholderLiteralString
|
1132
|
+
# Special behavior to not push down inversion in this case because doing so
|
1133
|
+
# can result in incorrect behavior for ANY/SOME/ALL operators.
|
1134
|
+
BooleanExpression.new(:NOT, ce)
|
1135
|
+
else
|
1136
|
+
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
|
1137
|
+
end
|
1138
|
+
else
|
1139
|
+
BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
|
1140
|
+
end
|
1116
1141
|
end
|
1117
1142
|
when StringExpression, NumericExpression
|
1118
1143
|
raise(Sequel::Error, "cannot invert #{ce.inspect}")
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 14
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -1411,6 +1411,10 @@ describe "Dataset#order" do
|
|
1411
1411
|
@dataset.order(Sequel.asc(:name, :nulls=>:last), Sequel.desc(:price, :nulls=>:first)).sql.must_equal 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
|
1412
1412
|
end
|
1413
1413
|
|
1414
|
+
it "should emulate :nulls options for asc and desc if not natively supported" do
|
1415
|
+
@dataset.with_extend{def requires_emulating_nulls_first?; true end}.order(Sequel.asc(:name, :nulls=>:last), Sequel.desc(:price, :nulls=>:first)).sql.must_equal 'SELECT * FROM test ORDER BY (CASE WHEN (name IS NULL) THEN 2 ELSE 1 END), name ASC, (CASE WHEN (price IS NULL) THEN 0 ELSE 1 END), price DESC'
|
1416
|
+
end
|
1417
|
+
|
1414
1418
|
it "should override a previous ordering" do
|
1415
1419
|
@dataset.order(:name).order(:stamp).sql.must_equal 'SELECT * FROM test ORDER BY stamp'
|
1416
1420
|
end
|
@@ -2344,6 +2348,14 @@ describe "Dataset#from_self" do
|
|
2344
2348
|
@ds.server(:blah).from_self(:alias=>:some_name).opts[:server].must_equal :blah
|
2345
2349
|
end
|
2346
2350
|
|
2351
|
+
it "should work correctly when a delayed evaluation is used " do
|
2352
|
+
a = true
|
2353
|
+
ds = @ds.where(Sequel.delay{a}).from_self
|
2354
|
+
ds.sql.must_equal "SELECT * FROM (SELECT name FROM test WHERE 't' LIMIT 1) AS t1"
|
2355
|
+
a = false
|
2356
|
+
ds.sql.must_equal "SELECT * FROM (SELECT name FROM test WHERE 'f' LIMIT 1) AS t1"
|
2357
|
+
end
|
2358
|
+
|
2347
2359
|
it "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
|
2348
2360
|
ds = Sequel.mock.dataset
|
2349
2361
|
ds = ds.with_extend do
|
@@ -54,6 +54,22 @@ describe "Blockless Ruby Filters" do
|
|
54
54
|
@d.l(~~Sequel.|(:x, :y)).must_equal '(x OR y)'
|
55
55
|
end
|
56
56
|
|
57
|
+
it "should not modifying boolean expression created from array if array is modified" do
|
58
|
+
a = [1]
|
59
|
+
expr = Sequel.expr(:b=>a)
|
60
|
+
@d.l(expr).must_equal '(b IN (1))'
|
61
|
+
a << 2
|
62
|
+
@d.l(expr).must_equal '(b IN (1))'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not modifying boolean expression created from string if string is modified" do
|
66
|
+
a = '1'.dup
|
67
|
+
expr = Sequel.expr(:b=>a)
|
68
|
+
@d.l(expr).must_equal "(b = '1')"
|
69
|
+
a << '2'
|
70
|
+
@d.l(expr).must_equal "(b = '1')"
|
71
|
+
end
|
72
|
+
|
57
73
|
it "should support = via Hash" do
|
58
74
|
@d.l(:x => 100).must_equal '(x = 100)'
|
59
75
|
@d.l(:x => 'a').must_equal '(x = \'a\')'
|
@@ -78,6 +94,12 @@ describe "Blockless Ruby Filters" do
|
|
78
94
|
@d.l(~Sequel.expr(:x => false)).must_equal '(x IS NOT FALSE)'
|
79
95
|
@d.l(~Sequel.expr(:x => nil)).must_equal '(x IS NOT NULL)'
|
80
96
|
end
|
97
|
+
|
98
|
+
it "should use NOT for inverting boolean expressions where right hand side is function or literal strings" do
|
99
|
+
@d.l(~Sequel.expr(:x => Sequel.function(:any))).must_equal 'NOT (x = any())'
|
100
|
+
@d.l(~Sequel.expr(:x => Sequel.lit('any()'))).must_equal 'NOT (x = any())'
|
101
|
+
@d.l(~Sequel.expr(:x => Sequel.lit('any(?)', 1))).must_equal 'NOT (x = any(1))'
|
102
|
+
end
|
81
103
|
|
82
104
|
it "should support = and similar operations via =~ method" do
|
83
105
|
@d.l{x =~ 100}.must_equal '(x = 100)'
|
@@ -107,12 +107,38 @@ describe "constraint_validations extension" do
|
|
107
107
|
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CONSTRAINT cons CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
108
108
|
end
|
109
109
|
|
110
|
-
it "should handle multiple columns when adding validations" do
|
110
|
+
it "should handle multiple string columns when adding presence validations" do
|
111
111
|
@db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar]}}
|
112
112
|
sqls = @db.sqls
|
113
113
|
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo")
|
114
114
|
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo")
|
115
|
-
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (
|
115
|
+
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '') AND (bar IS NOT NULL) AND (trim(bar) != '')))"]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should handle multiple string columns when adding presence validations with :allow_nil" do
|
119
|
+
@db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar], :allow_nil=>true}}
|
120
|
+
sqls = @db.sqls
|
121
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t')
|
122
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo", :allow_nil=>'t')
|
123
|
+
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK (((name IS NULL) OR (trim(name) != '')) AND ((bar IS NULL) OR (trim(bar) != ''))))"]
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should handle multiple string columns when adding presence validations" do
|
127
|
+
@db.create_table(:foo){String :name; Integer :x; String :bar; validate{presence [:name, :x, :bar]}}
|
128
|
+
sqls = @db.sqls
|
129
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo")
|
130
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"x", :table=>"foo")
|
131
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo")
|
132
|
+
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), x integer, bar varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '') AND (bar IS NOT NULL) AND (trim(bar) != '') AND (x IS NOT NULL)))"]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should handle multiple string columns when adding presence validations with :allow_nil" do
|
136
|
+
@db.create_table(:foo){String :name; Integer :x; String :bar; validate{presence [:name, :x, :bar], :allow_nil=>true}}
|
137
|
+
sqls = @db.sqls
|
138
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t')
|
139
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"x", :table=>"foo", :allow_nil=>'t')
|
140
|
+
parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo", :allow_nil=>'t')
|
141
|
+
sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), x integer, bar varchar(255), CHECK (((name IS NULL) OR (trim(name) != '')) AND ((bar IS NULL) OR (trim(bar) != ''))))"]
|
116
142
|
end
|
117
143
|
|
118
144
|
it "should handle presence validation on non-String columns" do
|
@@ -78,6 +78,15 @@ describe "Sequel::Plugins::DefaultsSetter" do
|
|
78
78
|
o.a.must_be_same_as(o.a)
|
79
79
|
end
|
80
80
|
|
81
|
+
it "should not cache default values if :cache plugin option is used and there is no default values" do
|
82
|
+
@c.plugin :defaults_setter, :cache => true
|
83
|
+
o = @c.new
|
84
|
+
o.a.must_be_nil
|
85
|
+
o.values.must_be_empty
|
86
|
+
o.a.must_be_nil
|
87
|
+
o.a.must_be_same_as(o.a)
|
88
|
+
end
|
89
|
+
|
81
90
|
it "should not override a given value" do
|
82
91
|
@pr.call(2)
|
83
92
|
@c.new('a'=>3).a.must_equal 3
|
@@ -63,10 +63,13 @@ describe Sequel::Model, "tree plugin" do
|
|
63
63
|
@c.dataset = @c.dataset.with_fetch([{:id=>1, :parent_id=>nil, :name=>'r'}])
|
64
64
|
@c.roots.must_equal [@c.load(:id=>1, :parent_id=>nil, :name=>'r')]
|
65
65
|
@db.sqls.must_equal ["SELECT * FROM nodes WHERE (parent_id IS NULL)"]
|
66
|
+
@c.exclude(id: 2).roots.must_equal [@c.load(:id=>1, :parent_id=>nil, :name=>'r')]
|
67
|
+
@db.sqls.must_equal ["SELECT * FROM nodes WHERE ((id != 2) AND (parent_id IS NULL))"]
|
66
68
|
end
|
67
69
|
|
68
70
|
it "should have roots_dataset be a dataset representing the tree's roots" do
|
69
71
|
@c.roots_dataset.sql.must_equal "SELECT * FROM nodes WHERE (parent_id IS NULL)"
|
72
|
+
@c.exclude(id: 2).roots_dataset.sql.must_equal "SELECT * FROM nodes WHERE ((id != 2) AND (parent_id IS NULL))"
|
70
73
|
end
|
71
74
|
|
72
75
|
it "should have ancestors return the ancestors of the current node" do
|
@@ -39,6 +39,15 @@ describe "Simple Dataset operations" do
|
|
39
39
|
@ds.order(:id).all.must_equal [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
|
40
40
|
end
|
41
41
|
|
42
|
+
it "should support ordering considering NULLS" do
|
43
|
+
@ds.insert(:number=>20)
|
44
|
+
@ds.insert(:number=>nil)
|
45
|
+
@ds.order(Sequel[:number].asc(:nulls=>:first)).select_map(:number).must_equal [nil, 10, 20]
|
46
|
+
@ds.order(Sequel[:number].asc(:nulls=>:last)).select_map(:number).must_equal [10, 20, nil]
|
47
|
+
@ds.order(Sequel[:number].desc(:nulls=>:first)).select_map(:number).must_equal [nil, 20, 10]
|
48
|
+
@ds.order(Sequel[:number].desc(:nulls=>:last)).select_map(:number).must_equal [20, 10, nil]
|
49
|
+
end
|
50
|
+
|
42
51
|
it "should have insert return primary key value" do
|
43
52
|
@ds.insert(:number=>20).must_equal 2
|
44
53
|
@ds.filter(:id=>2).first[:number].must_equal 20
|
@@ -1709,7 +1709,9 @@ describe "Sequel::Plugins::Tree" do
|
|
1709
1709
|
|
1710
1710
|
it "iterate top-level nodes in order" do
|
1711
1711
|
@Node.roots_dataset.count.must_equal 5
|
1712
|
-
@Node.roots.map
|
1712
|
+
@Node.roots.map(&:name).must_equal %w'one two three four five'
|
1713
|
+
@Node.where(:name=>%w'one two.one').roots_dataset.count.must_equal 1
|
1714
|
+
@Node.where(:name=>%w'one two.one').roots.map(&:name).must_equal %w'one'
|
1713
1715
|
end
|
1714
1716
|
|
1715
1717
|
it "should have children" do
|
@@ -2026,8 +2028,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
2026
2028
|
max_length 6, :minlen, opts.merge(:name=>:maxl2)
|
2027
2029
|
operator :<, 'm', :exactlen, opts.merge(:name=>:lt)
|
2028
2030
|
operator :>=, 5, :num, opts.merge(:name=>:gte)
|
2031
|
+
presence [:m1, :m2, :m3], opts.merge(:name=>:pm)
|
2029
2032
|
end
|
2030
|
-
@valid_row = {:pre=>'a', :exactlen=>'12345', :minlen=>'12345', :maxlen=>'12345', :lenrange=>'1234', :lik=>'fooabc', :ilik=>'FooABC', :inc=>'abc', :uniq=>'u', :num=>5}
|
2033
|
+
@valid_row = {:pre=>'a', :exactlen=>'12345', :minlen=>'12345', :maxlen=>'12345', :lenrange=>'1234', :lik=>'fooabc', :ilik=>'FooABC', :inc=>'abc', :uniq=>'u', :num=>5, :m1=>'a', :m2=>1, :m3=>'a'}
|
2031
2034
|
@violations = [
|
2032
2035
|
[:pre, [nil, '', ' ']],
|
2033
2036
|
[:exactlen, [nil, '', '1234', '123456', 'n1234']],
|
@@ -2068,6 +2071,41 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
2068
2071
|
end
|
2069
2072
|
end
|
2070
2073
|
|
2074
|
+
try = @valid_row.dup
|
2075
|
+
if @validation_opts[:allow_nil]
|
2076
|
+
[:m1, :m2, :m3].each do |c|
|
2077
|
+
@ds.insert(try.merge(c=>nil))
|
2078
|
+
@ds.delete
|
2079
|
+
end
|
2080
|
+
@ds.insert(try.merge(:m1=>nil, :m2=>nil))
|
2081
|
+
@ds.delete
|
2082
|
+
@ds.insert(try.merge(:m1=>nil, :m3=>nil))
|
2083
|
+
@ds.delete
|
2084
|
+
@ds.insert(try.merge(:m2=>nil, :m3=>nil))
|
2085
|
+
@ds.delete
|
2086
|
+
@ds.insert(try.merge(:m1=>nil, :m2=>nil, :m3=>nil))
|
2087
|
+
@ds.delete
|
2088
|
+
else
|
2089
|
+
[:m1, :m2, :m3].each do |c|
|
2090
|
+
proc{@ds.insert(try.merge(c=>nil))}.must_raise(Sequel::DatabaseError)
|
2091
|
+
end
|
2092
|
+
proc{@ds.insert(try.merge(:m1=>nil, :m2=>nil))}.must_raise(Sequel::DatabaseError)
|
2093
|
+
proc{@ds.insert(try.merge(:m1=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
|
2094
|
+
proc{@ds.insert(try.merge(:m2=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
|
2095
|
+
proc{@ds.insert(try.merge(:m1=>nil, :m2=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
|
2096
|
+
end
|
2097
|
+
|
2098
|
+
unless @db.database_type == :oracle
|
2099
|
+
[:m1, :m3].each do |c|
|
2100
|
+
proc{@ds.insert(try.merge(c=>''))}.must_raise(Sequel::DatabaseError)
|
2101
|
+
end
|
2102
|
+
proc{@ds.insert(try.merge(:m1=>'', :m3=>''))}.must_raise(Sequel::DatabaseError)
|
2103
|
+
proc{@ds.insert(try.merge(:m1=>'', :m2=>nil))}.must_raise(Sequel::DatabaseError)
|
2104
|
+
proc{@ds.insert(try.merge(:m1=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
|
2105
|
+
proc{@ds.insert(try.merge(:m2=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
|
2106
|
+
proc{@ds.insert(try.merge(:m1=>'', :m2=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
|
2107
|
+
end
|
2108
|
+
|
2071
2109
|
# Test for dropping of constraint
|
2072
2110
|
@db.alter_table(:cv_test){validate{drop :maxl2}}
|
2073
2111
|
@ds.insert(@valid_row.merge(:minlen=>'1234567'))
|
@@ -2092,6 +2130,34 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
2092
2130
|
c.new(try).wont_be :valid?
|
2093
2131
|
end
|
2094
2132
|
end
|
2133
|
+
|
2134
|
+
try = @valid_row.dup
|
2135
|
+
if @validation_opts[:allow_nil]
|
2136
|
+
[:m1, :m2, :m3].each do |col|
|
2137
|
+
c.new(try.merge(col=>nil)).must_be :valid?
|
2138
|
+
end
|
2139
|
+
c.new(try.merge(:m1=>nil, :m2=>nil)).must_be :valid?
|
2140
|
+
c.new(try.merge(:m1=>nil, :m3=>nil)).must_be :valid?
|
2141
|
+
c.new(try.merge(:m2=>nil, :m3=>nil)).must_be :valid?
|
2142
|
+
c.new(try.merge(:m1=>nil, :m2=>nil, :m3=>nil)).must_be :valid?
|
2143
|
+
else
|
2144
|
+
[:m1, :m2, :m3].each do |col|
|
2145
|
+
c.new(try.merge(col=>nil)).wont_be :valid?
|
2146
|
+
end
|
2147
|
+
c.new(try.merge(:m1=>nil, :m2=>nil)).wont_be :valid?
|
2148
|
+
c.new(try.merge(:m1=>nil, :m3=>nil)).wont_be :valid?
|
2149
|
+
c.new(try.merge(:m2=>nil, :m3=>nil)).wont_be :valid?
|
2150
|
+
c.new(try.merge(:m1=>nil, :m2=>nil, :m3=>nil)).wont_be :valid?
|
2151
|
+
end
|
2152
|
+
c.new(try.merge(:m1=>'', :m2=>nil)).wont_be :valid?
|
2153
|
+
c.new(try.merge(:m1=>nil, :m3=>'')).wont_be :valid?
|
2154
|
+
c.new(try.merge(:m2=>nil, :m3=>'')).wont_be :valid?
|
2155
|
+
c.new(try.merge(:m1=>'', :m2=>nil, :m3=>'')).wont_be :valid?
|
2156
|
+
[:m1, :m3].each do |col|
|
2157
|
+
c.new(try.merge(col=>'')).wont_be :valid?
|
2158
|
+
end
|
2159
|
+
c.new(try.merge(:m1=>'', :m3=>'')).wont_be :valid?
|
2160
|
+
|
2095
2161
|
c.db.constraint_validations = nil
|
2096
2162
|
end
|
2097
2163
|
end
|
@@ -2116,6 +2182,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
2116
2182
|
String :inc
|
2117
2183
|
String :uniq, :null=>false
|
2118
2184
|
Integer :num
|
2185
|
+
String :m1
|
2186
|
+
Integer :m2
|
2187
|
+
String :m3
|
2119
2188
|
validate(&validate_block)
|
2120
2189
|
end
|
2121
2190
|
end
|
@@ -2162,6 +2231,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
|
|
2162
2231
|
add_column :form, String
|
2163
2232
|
end
|
2164
2233
|
add_column :num, Integer
|
2234
|
+
add_column :m1, String
|
2235
|
+
add_column :m2, Integer
|
2236
|
+
add_column :m3, String
|
2165
2237
|
validate(&validate_block)
|
2166
2238
|
end
|
2167
2239
|
end
|
data/spec/model/record_spec.rb
CHANGED
@@ -1445,35 +1445,46 @@ describe Sequel::Model, "#==" do
|
|
1445
1445
|
end
|
1446
1446
|
end
|
1447
1447
|
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1448
|
+
[:===, :pk_equal?].each do |method_name|
|
1449
|
+
describe Sequel::Model, "##{method_name}" do
|
1450
|
+
it "should compare instances by class and pk if pk is not nil" do
|
1451
|
+
z = Class.new(Sequel::Model)
|
1452
|
+
z.columns :id, :x
|
1453
|
+
y = Class.new(Sequel::Model)
|
1454
|
+
y.columns :id, :x
|
1455
|
+
a = z.load(:id => 1, :x => 3)
|
1456
|
+
b = z.load(:id => 1, :x => 4)
|
1457
|
+
c = z.load(:id => 2, :x => 3)
|
1458
|
+
d = y.load(:id => 1, :x => 3)
|
1459
|
+
|
1460
|
+
a.must_be method_name, b
|
1461
|
+
a.wont_be method_name, c
|
1462
|
+
a.wont_be method_name, d
|
1463
|
+
end
|
1463
1464
|
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1465
|
+
it "should always be false if the primary key is nil" do
|
1466
|
+
z = Class.new(Sequel::Model)
|
1467
|
+
z.columns :id, :x
|
1468
|
+
y = Class.new(Sequel::Model)
|
1469
|
+
y.columns :id, :x
|
1470
|
+
a = z.new(:x => 3)
|
1471
|
+
b = z.new(:x => 4)
|
1472
|
+
c = z.new(:x => 3)
|
1473
|
+
d = y.new(:x => 3)
|
1474
|
+
|
1475
|
+
a.wont_be method_name, b
|
1476
|
+
a.wont_be method_name, c
|
1477
|
+
a.wont_be method_name, d
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
it "should always be false if the primary key is an array containing nil" do
|
1481
|
+
z = Class.new(Sequel::Model)
|
1482
|
+
z.columns :id, :x
|
1483
|
+
z.set_primary_key [:id, :x]
|
1484
|
+
z.load(:id => nil, :x => nil).wont_be method_name, z.load(:id => nil, :x => nil)
|
1485
|
+
z.load(:id => 1, :x => nil).wont_be method_name, z.load(:id => 1, :x => nil)
|
1486
|
+
z.load(:id => nil, :x => 2).wont_be method_name, z.load(:id => nil, :x => 2)
|
1487
|
+
end
|
1477
1488
|
end
|
1478
1489
|
end
|
1479
1490
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -197,6 +197,7 @@ extra_rdoc_files:
|
|
197
197
|
- doc/release_notes/5.11.0.txt
|
198
198
|
- doc/release_notes/5.12.0.txt
|
199
199
|
- doc/release_notes/5.13.0.txt
|
200
|
+
- doc/release_notes/5.14.0.txt
|
200
201
|
files:
|
201
202
|
- CHANGELOG
|
202
203
|
- MIT-LICENSE
|
@@ -280,6 +281,7 @@ files:
|
|
280
281
|
- doc/release_notes/5.11.0.txt
|
281
282
|
- doc/release_notes/5.12.0.txt
|
282
283
|
- doc/release_notes/5.13.0.txt
|
284
|
+
- doc/release_notes/5.14.0.txt
|
283
285
|
- doc/release_notes/5.2.0.txt
|
284
286
|
- doc/release_notes/5.3.0.txt
|
285
287
|
- doc/release_notes/5.4.0.txt
|
@@ -805,7 +807,12 @@ files:
|
|
805
807
|
homepage: http://sequel.jeremyevans.net
|
806
808
|
licenses:
|
807
809
|
- MIT
|
808
|
-
metadata:
|
810
|
+
metadata:
|
811
|
+
bug_tracker_uri: https://github.com/jeremyevans/sequel/issues
|
812
|
+
changelog_uri: http://sequel.jeremyevans.net/rdoc/files/CHANGELOG.html
|
813
|
+
documentation_uri: http://sequel.jeremyevans.net/documentation.html
|
814
|
+
mailing_list_uri: https://groups.google.com/forum/#!forum/sequel-talk
|
815
|
+
source_code_uri: https://github.com/jeremyevans/sequel
|
809
816
|
post_install_message:
|
810
817
|
rdoc_options:
|
811
818
|
- "--quiet"
|