sequel 5.13.0 → 5.14.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.
- 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"
|