sequel 5.93.0 → 5.95.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/lib/sequel/adapters/ado/access.rb +2 -2
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +21 -7
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/shared/db2.rb +8 -3
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +191 -22
- data/lib/sequel/adapters/shared/sqlite.rb +3 -3
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +1 -1
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +6 -0
- data/lib/sequel/connection_pool/timed_queue.rb +6 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/misc.rb +3 -3
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +66 -10
- data/lib/sequel/database/schema_methods.rb +79 -28
- data/lib/sequel/dataset/query.rb +10 -2
- data/lib/sequel/dataset/sql.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +1 -3
- data/lib/sequel/extensions/eval_inspect.rb +1 -1
- data/lib/sequel/extensions/inflector.rb +2 -2
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/provenance.rb +1 -3
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
- data/lib/sequel/plugins/constraint_validations.rb +15 -10
- data/lib/sequel/plugins/json_serializer.rb +3 -3
- data/lib/sequel/plugins/unused_associations.rb +4 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +3 -2
data/lib/sequel/database/misc.rb
CHANGED
@@ -36,13 +36,13 @@ module Sequel
|
|
36
36
|
|
37
37
|
# Register a hook that will be run when a new Database is instantiated. It is
|
38
38
|
# called with the new database handle.
|
39
|
-
def self.after_initialize
|
40
|
-
raise Error, "must provide block to after_initialize" unless
|
39
|
+
def self.after_initialize
|
40
|
+
raise Error, "must provide block to after_initialize" unless defined?(yield)
|
41
41
|
Sequel.synchronize do
|
42
42
|
previous = @initialize_hook
|
43
43
|
@initialize_hook = proc do |db|
|
44
44
|
previous.call(db)
|
45
|
-
|
45
|
+
yield(db)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -269,7 +269,7 @@ module Sequel
|
|
269
269
|
def column_schema_to_ruby_default(default, type)
|
270
270
|
return default unless default.is_a?(String)
|
271
271
|
if COLUMN_SCHEMA_DATETIME_TYPES.include?(type)
|
272
|
-
if /now|today|CURRENT|getdate|\ADate\(\)\z/i
|
272
|
+
if /now|today|CURRENT|getdate|\ADate\(\)\z/i =~ default
|
273
273
|
if type == :date
|
274
274
|
return Sequel::CURRENT_DATE
|
275
275
|
else
|
@@ -118,6 +118,11 @@ module Sequel
|
|
118
118
|
# that this column references. Unnecessary if this column
|
119
119
|
# references the primary key of the associated table, except if you are
|
120
120
|
# using MySQL.
|
121
|
+
# :not_null :: Similar to setting <tt>null: false</tt>, but you can provide a hash value
|
122
|
+
# to specify options for the NOT NULL constraint on PostgreSQL 18+:
|
123
|
+
# :name :: The name of the NOT NULL constraint
|
124
|
+
# :no_inherit :: Set NO INHERIT on the constraint, so it will not propogate to
|
125
|
+
# child tables.
|
121
126
|
# :null :: Mark the column as allowing NULL values (if true),
|
122
127
|
# or not allowing NULL values (if false). The default is to allow NULL values.
|
123
128
|
# :on_delete :: Specify the behavior of this column when being deleted
|
@@ -126,17 +131,27 @@ module Sequel
|
|
126
131
|
# (:restrict, :cascade, :set_null, :set_default, :no_action).
|
127
132
|
# :primary_key :: Make the column as a single primary key column. This should not
|
128
133
|
# be used if you want a single autoincrementing primary key column
|
129
|
-
# (use the primary_key method in that case).
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
134
|
+
# (use the primary_key method in that case). Can be a hash to provide
|
135
|
+
# options for the constraint:
|
136
|
+
# :name :: The name to give the primary key constraint.
|
137
|
+
# :deferrable :: Sets whether the primary key constraint is deferrable.
|
138
|
+
# :include :: Include additional columns in the underlying index, to
|
139
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
140
|
+
# :primary_key_constraint_name :: Older option to name primary key constraint.
|
141
|
+
# :primary_key_deferrable :: Older option to set primary key constraint as deferrable.
|
133
142
|
# :type :: Overrides the type given as the argument. Generally not used by column
|
134
143
|
# itself, but can be passed as an option to other methods that call column.
|
135
144
|
# :unique :: Mark the column as unique, generally has the same effect as
|
136
|
-
# creating a unique index on the column.
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
145
|
+
# creating a unique index on the column. Can be a hash to provide options for
|
146
|
+
# the constraint:
|
147
|
+
# :name :: The name to give the unique constraint.
|
148
|
+
# :deferrable :: Sets whether the unique constraint is deferrable.
|
149
|
+
# :include :: Include additional columns in the underlying index, to
|
150
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
151
|
+
# :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
|
152
|
+
# entries are considered distinct (PostgreSQL 15+)
|
153
|
+
# :unique_constraint_name :: Older option to name unique constraint.
|
154
|
+
# :unique_deferrable :: Older option to set unique constraint as deferrable.
|
140
155
|
#
|
141
156
|
# PostgreSQL specific options:
|
142
157
|
#
|
@@ -178,6 +193,9 @@ module Sequel
|
|
178
193
|
# :deferrable :: Whether the CHECK constraint should be marked DEFERRABLE.
|
179
194
|
#
|
180
195
|
# PostgreSQL specific options:
|
196
|
+
# :no_inherit :: Set NO INHERIT on the constraint, so it will not propogate to
|
197
|
+
# child tables.
|
198
|
+
# :not_enforced :: Whether the CHECK constraint should be marked NOT ENFORCED.
|
181
199
|
# :not_valid :: Whether the CHECK constraint should be marked NOT VALID.
|
182
200
|
def constraint(name, *args, &block)
|
183
201
|
opts = name.is_a?(Hash) ? name : {:name=>name}
|
@@ -197,9 +215,21 @@ module Sequel
|
|
197
215
|
#
|
198
216
|
# :foreign_key_constraint_name :: The name to give the foreign key constraint
|
199
217
|
#
|
218
|
+
# PostgreSQL specific options:
|
219
|
+
#
|
220
|
+
# :not_enforced :: Whether the foreign key constraint should be marked NOT ENFORCED.
|
221
|
+
#
|
200
222
|
# If you want a foreign key constraint without adding a column (usually because it is a
|
201
|
-
# composite foreign key), you can provide an array of columns as the first argument
|
202
|
-
#
|
223
|
+
# composite foreign key), you can provide an array of columns as the first argument.
|
224
|
+
# This changes the method to accept constraint options instead of column options.
|
225
|
+
# You can provide the :name option to name the constraint.
|
226
|
+
#
|
227
|
+
# PostgreSQL specific options:
|
228
|
+
#
|
229
|
+
# :not_enforced :: Whether the foreign key constraint should be marked NOT ENFORCED.
|
230
|
+
# :period :: Use PERIOD to setup a constraint where the final column is a range
|
231
|
+
# or multirange column, and the range or multirange is covered by
|
232
|
+
# existing ranges in the referenced table (which can be in separate rows).
|
203
233
|
#
|
204
234
|
# foreign_key([:artist_name, :artist_location], :artists, name: :artist_fk)
|
205
235
|
# # ADD CONSTRAINT artist_fk FOREIGN KEY (artist_name, artist_location) REFERENCES artists
|
@@ -303,6 +333,13 @@ module Sequel
|
|
303
333
|
# :keep_order :: For non-composite primary keys, respects the existing order of
|
304
334
|
# columns, overriding the default behavior of making the primary
|
305
335
|
# key the first column.
|
336
|
+
#
|
337
|
+
# PostgreSQL specific options:
|
338
|
+
#
|
339
|
+
# :include :: Include additional columns in the underlying index, to
|
340
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
341
|
+
# :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
|
342
|
+
# on the final column (PostgreSQL 18+, composite primary keys only).
|
306
343
|
#
|
307
344
|
# Examples:
|
308
345
|
# primary_key(:id)
|
@@ -346,6 +383,15 @@ module Sequel
|
|
346
383
|
#
|
347
384
|
# Supports the same :deferrable option as #column. The :name option can be used
|
348
385
|
# to name the constraint.
|
386
|
+
#
|
387
|
+
# PostgreSQL specific options:
|
388
|
+
#
|
389
|
+
# :include :: Include additional columns in the underlying index, to
|
390
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
391
|
+
# :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
|
392
|
+
# entries are considered distinct (PostgreSQL 15+)
|
393
|
+
# :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
|
394
|
+
# on the final column (PostgreSQL 18+, composite unique only).
|
349
395
|
def unique(columns, opts = OPTS)
|
350
396
|
constraints << {:type => :unique, :columns => Array(columns)}.merge!(opts)
|
351
397
|
nil
|
@@ -433,7 +479,13 @@ module Sequel
|
|
433
479
|
#
|
434
480
|
# PostgreSQL specific options:
|
435
481
|
#
|
482
|
+
# :include :: Include additional columns in the underlying index, to
|
483
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
484
|
+
# :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
|
485
|
+
# entries are considered distinct (PostgreSQL 15+)
|
436
486
|
# :using_index :: Use the USING INDEX clause to specify an existing unique index
|
487
|
+
# :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
|
488
|
+
# on the final column (PostgreSQL 18+, composite unique constraints only).
|
437
489
|
def add_unique_constraint(columns, opts = OPTS)
|
438
490
|
@operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
|
439
491
|
nil
|
@@ -490,7 +542,11 @@ module Sequel
|
|
490
542
|
#
|
491
543
|
# PostgreSQL specific options:
|
492
544
|
#
|
545
|
+
# :include :: Include additional columns in the underlying index, to
|
546
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
493
547
|
# :using_index :: Use the USING INDEX clause to specify an existing unique index
|
548
|
+
# :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
|
549
|
+
# on the final column (PostgreSQL 18+, composite primary keys only).
|
494
550
|
def add_primary_key(name, opts = OPTS)
|
495
551
|
return add_composite_primary_key(name, opts) if name.is_a?(Array)
|
496
552
|
opts = @db.serial_primary_key_options.merge(opts)
|
@@ -592,7 +592,12 @@ module Sequel
|
|
592
592
|
|
593
593
|
# Add null/not null SQL fragment to column creation SQL.
|
594
594
|
def column_definition_null_sql(sql, column)
|
595
|
-
null =
|
595
|
+
null = if column[:not_null]
|
596
|
+
false
|
597
|
+
else
|
598
|
+
column.fetch(:null, column[:allow_null])
|
599
|
+
end
|
600
|
+
|
596
601
|
if null.nil? && !can_add_primary_key_constraint_on_nullable_columns? && column[:primary_key]
|
597
602
|
null = false
|
598
603
|
end
|
@@ -605,36 +610,71 @@ module Sequel
|
|
605
610
|
end
|
606
611
|
end
|
607
612
|
|
608
|
-
# Add primary key SQL fragment to column creation SQL.
|
613
|
+
# Add primary key SQL fragment to column creation SQL if column is a primary key.
|
609
614
|
def column_definition_primary_key_sql(sql, column)
|
610
|
-
if column[:primary_key]
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
615
|
+
column_definition_add_primary_key_sql(sql, column) if column[:primary_key]
|
616
|
+
end
|
617
|
+
|
618
|
+
# Add primary key SQL fragment to column creation SQL (column should be a primary key).
|
619
|
+
def column_definition_add_primary_key_sql(sql, column)
|
620
|
+
constraint = column_definition_constraint_hash(column, :primary_key)
|
621
|
+
append_named_constraint_prefix_sql(sql, constraint[:name])
|
622
|
+
column_definition_append_primary_key_sql(sql, constraint)
|
623
|
+
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
624
|
+
end
|
625
|
+
|
626
|
+
def column_definition_append_primary_key_sql(sql, constraint)
|
627
|
+
sql << " " << primary_key_constraint_sql_fragment(constraint)
|
617
628
|
end
|
618
629
|
|
619
|
-
# Add foreign key reference SQL fragment to column creation SQL.
|
630
|
+
# Add foreign key reference SQL fragment to column creation SQL if column is a foreign key.
|
620
631
|
def column_definition_references_sql(sql, column)
|
621
|
-
if column[:table]
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
632
|
+
column_definition_add_references_sql(sql, column) if column[:table]
|
633
|
+
end
|
634
|
+
|
635
|
+
# Add foreign key reference SQL fragment to column creation SQL (column should be a foreign key).
|
636
|
+
def column_definition_add_references_sql(sql, column)
|
637
|
+
append_named_constraint_prefix_sql(sql, column[:foreign_key_constraint_name])
|
638
|
+
sql << column_references_column_constraint_sql(column)
|
627
639
|
end
|
628
640
|
|
629
|
-
# Add unique constraint SQL fragment to column creation SQL.
|
641
|
+
# Add unique constraint SQL fragment to column creation SQL if column has a unique constraint.
|
630
642
|
def column_definition_unique_sql(sql, column)
|
631
|
-
if column[:unique]
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
643
|
+
column_definition_add_unique_sql(sql, column) if column[:unique]
|
644
|
+
end
|
645
|
+
|
646
|
+
# Add unique constraint SQL fragment to column creation SQL (column should have unique constraint).
|
647
|
+
def column_definition_add_unique_sql(sql, column)
|
648
|
+
constraint = column_definition_constraint_hash(column, :unique)
|
649
|
+
append_named_constraint_prefix_sql(sql, constraint[:name])
|
650
|
+
column_definition_append_unique_sql(sql, constraint)
|
651
|
+
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
652
|
+
end
|
653
|
+
|
654
|
+
def column_definition_append_unique_sql(sql, constraint)
|
655
|
+
sql << ' ' << unique_constraint_sql_fragment(constraint)
|
656
|
+
end
|
657
|
+
|
658
|
+
# Add the name of the constraint to the column creation SQL.
|
659
|
+
def append_named_constraint_prefix_sql(sql, name)
|
660
|
+
sql << " CONSTRAINT #{quote_identifier(name)}" if name
|
661
|
+
end
|
662
|
+
|
663
|
+
# Return a hash of constraint options for the primary key or column
|
664
|
+
# unique constraint.
|
665
|
+
def column_definition_constraint_hash(column, prefix)
|
666
|
+
constraint = column[prefix]
|
667
|
+
constraint = constraint.is_a?(Hash) ? constraint.dup : {}
|
668
|
+
|
669
|
+
if name = column[:"#{prefix}_constraint_name"]
|
670
|
+
constraint[:name] = name
|
671
|
+
end
|
672
|
+
|
673
|
+
if column.has_key?(:"#{prefix}_deferrable")
|
674
|
+
constraint[:deferrable] = column[:"#{prefix}_deferrable"]
|
637
675
|
end
|
676
|
+
|
677
|
+
constraint
|
638
678
|
end
|
639
679
|
|
640
680
|
# SQL for all given columns, used inside a CREATE TABLE block.
|
@@ -651,12 +691,16 @@ module Sequel
|
|
651
691
|
def column_references_sql(column)
|
652
692
|
sql = String.new
|
653
693
|
sql << " REFERENCES #{quote_schema_table(column[:table])}"
|
654
|
-
sql
|
694
|
+
column_references_append_key_sql(sql, column) if column[:key]
|
655
695
|
sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
|
656
696
|
sql << " ON UPDATE #{on_update_clause(column[:on_update])}" if column[:on_update]
|
657
697
|
constraint_deferrable_sql_append(sql, column[:deferrable])
|
658
698
|
sql
|
659
699
|
end
|
700
|
+
|
701
|
+
def column_references_append_key_sql(sql, column)
|
702
|
+
sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(', ')})"
|
703
|
+
end
|
660
704
|
|
661
705
|
# SQL fragment for table foreign key references (table constraints)
|
662
706
|
def column_references_table_constraint_sql(constraint)
|
@@ -677,7 +721,7 @@ module Sequel
|
|
677
721
|
check = constraint[:check]
|
678
722
|
check = check.first if check.is_a?(Array) && check.length == 1
|
679
723
|
check = filter_expr(check)
|
680
|
-
check = "(#{check})" unless check
|
724
|
+
check = "(#{check})" unless check.start_with?('(') && check.end_with?(')')
|
681
725
|
sql << "CHECK #{check}"
|
682
726
|
when :primary_key
|
683
727
|
sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
|
@@ -733,17 +777,24 @@ module Sequel
|
|
733
777
|
def create_table_sql(name, generator, options)
|
734
778
|
unless supports_named_column_constraints?
|
735
779
|
# Split column constraints into table constraints if they have a name
|
780
|
+
fk_opt_keys = [:key, :on_delete, :on_update, :deferrable]
|
736
781
|
generator.columns.each do |c|
|
737
782
|
if (constraint_name = c.delete(:foreign_key_constraint_name)) && (table = c.delete(:table))
|
738
783
|
opts = {}
|
739
784
|
opts[:name] = constraint_name
|
740
|
-
|
785
|
+
fk_opt_keys.each{|k| opts[k] = c[k]}
|
741
786
|
generator.foreign_key([c[:name]], table, opts)
|
742
787
|
end
|
743
|
-
|
788
|
+
|
789
|
+
if c[:unique].is_a?(Hash) && c[:unique][:name]
|
790
|
+
generator.unique(c[:name], c.delete(:unique))
|
791
|
+
elsif (constraint_name = c.delete(:unique_constraint_name)) && c.delete(:unique)
|
744
792
|
generator.unique(c[:name], :name=>constraint_name)
|
745
793
|
end
|
746
|
-
|
794
|
+
|
795
|
+
if c[:primary_key].is_a?(Hash) && c[:primary_key][:name]
|
796
|
+
generator.primary_key([c[:name]], c.delete(:primary_key))
|
797
|
+
elsif (constraint_name = c.delete(:primary_key_constraint_name)) && c.delete(:primary_key)
|
747
798
|
generator.primary_key([c[:name]], :name=>constraint_name)
|
748
799
|
end
|
749
800
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -231,8 +231,7 @@ module Sequel
|
|
231
231
|
#
|
232
232
|
# DB[:table].for_update # SELECT * FROM table FOR UPDATE
|
233
233
|
def for_update
|
234
|
-
|
235
|
-
cached_dataset(:_for_update_ds){lock_style(:update)}
|
234
|
+
cached_lock_style_dataset(:_for_update_ds, :update)
|
236
235
|
end
|
237
236
|
|
238
237
|
# Returns a copy of the dataset with the source changed. If no
|
@@ -1462,6 +1461,15 @@ module Sequel
|
|
1462
1461
|
end
|
1463
1462
|
end
|
1464
1463
|
|
1464
|
+
# Internals of for_update and adapter-specific lock methods.
|
1465
|
+
# Returns receiver if it already uses this lock style, and a cached
|
1466
|
+
# dataset using the given key otherwise. The key could be derived from
|
1467
|
+
# the style, but doing so would require allocation, so pass it in as
|
1468
|
+
# an argument.
|
1469
|
+
def cached_lock_style_dataset(key, style)
|
1470
|
+
opts[:lock] == style ? self : cached_dataset(key){lock_style(style)}
|
1471
|
+
end
|
1472
|
+
|
1465
1473
|
# The default :qualify option to use for join tables if one is not specified.
|
1466
1474
|
def default_join_table_qualification
|
1467
1475
|
:symbol
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -1339,7 +1339,7 @@ module Sequel
|
|
1339
1339
|
# SQL fragment specifying a JOIN type, converts underscores to
|
1340
1340
|
# spaces and upcases.
|
1341
1341
|
def join_type_sql(join_type)
|
1342
|
-
"#{join_type.to_s.
|
1342
|
+
"#{join_type.to_s.tr('_', ' ').upcase} JOIN"
|
1343
1343
|
end
|
1344
1344
|
|
1345
1345
|
# Append USING clause for JOIN USING
|
@@ -357,7 +357,7 @@ module Sequel
|
|
357
357
|
define_singleton_method(:async_job_class){proxy_klass}
|
358
358
|
|
359
359
|
queue = @async_thread_queue = Queue.new
|
360
|
-
pool = @async_thread_pool = num_async_threads
|
360
|
+
pool = @async_thread_pool = Array.new(num_async_threads){JobProcessor.new(queue)}
|
361
361
|
ObjectSpace.define_finalizer(db, JobProcessor.create_finalizer(queue, pool))
|
362
362
|
|
363
363
|
extend_datasets(DatasetMethods)
|
@@ -60,9 +60,7 @@ module Sequel
|
|
60
60
|
def external_caller_for_log
|
61
61
|
ignore = caller_logging_ignore
|
62
62
|
c = caller.find do |line|
|
63
|
-
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
64
|
-
line.start_with?(RUBY_STDLIB) ||
|
65
|
-
line.start_with?(INTERNAL) ||
|
63
|
+
!(line.start_with?(SEQUEL_LIB_PATH, RUBY_STDLIB, INTERNAL) ||
|
66
64
|
(ignore && line =~ ignore))
|
67
65
|
end
|
68
66
|
|
@@ -66,7 +66,7 @@ module Sequel
|
|
66
66
|
# in the order they were defined.
|
67
67
|
klass = self.class
|
68
68
|
args = inspect_args.map do |arg|
|
69
|
-
if arg.is_a?(String) && arg
|
69
|
+
if arg.is_a?(String) && arg.start_with?('*')
|
70
70
|
# Special case string arguments starting with *, indicating that
|
71
71
|
# they should return an array to be splatted as the remaining arguments.
|
72
72
|
# Allow calling private methods to get inspect output.
|
@@ -159,7 +159,7 @@ class String
|
|
159
159
|
# Example
|
160
160
|
# "puni_puni".dasherize #=> "puni-puni"
|
161
161
|
def dasherize
|
162
|
-
|
162
|
+
tr('_', '-')
|
163
163
|
end
|
164
164
|
|
165
165
|
# Removes the module part from the expression in the string
|
@@ -189,7 +189,7 @@ class String
|
|
189
189
|
# "employee_salary" #=> "Employee salary"
|
190
190
|
# "author_id" #=> "Author"
|
191
191
|
def humanize
|
192
|
-
gsub(/_id$/, "").
|
192
|
+
gsub(/_id$/, "").tr('_', " ").capitalize
|
193
193
|
end
|
194
194
|
|
195
195
|
# Returns the plural form of the word in the string.
|
@@ -845,7 +845,7 @@ module Sequel
|
|
845
845
|
begin
|
846
846
|
db.create_table(table){String c, :primary_key=>true}
|
847
847
|
rescue Sequel::DatabaseError => e
|
848
|
-
if db.database_type == :mysql && e.message
|
848
|
+
if db.database_type == :mysql && e.message.include?('max key length')
|
849
849
|
# Handle case where MySQL is used with utf8mb4 charset default, which
|
850
850
|
# only allows a maximum length of about 190 characters for string
|
851
851
|
# primary keys due to InnoDB limitations.
|
@@ -136,7 +136,7 @@ module Sequel
|
|
136
136
|
module DatabaseMethods
|
137
137
|
def self.extended(db)
|
138
138
|
db.instance_exec do
|
139
|
-
add_named_conversion_proc(:hstore
|
139
|
+
add_named_conversion_proc(:hstore){|v| HStore.parse(v)}
|
140
140
|
@schema_type_classes[:hstore] = HStore
|
141
141
|
end
|
142
142
|
end
|
@@ -97,9 +97,7 @@ module Sequel
|
|
97
97
|
def provenance_source
|
98
98
|
ignore = db.opts[:provenance_caller_ignore]
|
99
99
|
caller.find do |line|
|
100
|
-
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
101
|
-
line.start_with?(RUBY_STDLIB) ||
|
102
|
-
line.start_with?(INTERNAL) ||
|
100
|
+
!(line.start_with?(SEQUEL_LIB_PATH, RUBY_STDLIB, INTERNAL) ||
|
103
101
|
(ignore && line =~ ignore))
|
104
102
|
end
|
105
103
|
end
|
@@ -221,7 +221,7 @@ END_MIG
|
|
221
221
|
else
|
222
222
|
col_opts = if options[:same_db]
|
223
223
|
h = {:type=>schema[:db_type]}
|
224
|
-
if database_type == :mysql && h[:type]
|
224
|
+
if database_type == :mysql && h[:type].start_with?("timestamp")
|
225
225
|
h[:null] = true
|
226
226
|
end
|
227
227
|
if database_type == :mssql && schema[:max_length]
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'constraint_validations'
|
4
|
+
require_relative 'class_table_inheritance'
|
5
|
+
|
6
|
+
module Sequel
|
7
|
+
module Plugins
|
8
|
+
# = Overview
|
9
|
+
#
|
10
|
+
# The class_table_inheritance_constraint_validations plugin extends the
|
11
|
+
# constraint_validations plugin to work correctly with the
|
12
|
+
# class_table_inheritance plugin. It ensures that constraint_validations
|
13
|
+
# are loaded from all tables in the class table inheritance hierarchy,
|
14
|
+
# not just the base table.
|
15
|
+
#
|
16
|
+
# = Example
|
17
|
+
#
|
18
|
+
# For example, with this hierarchy, where each model has its own table with
|
19
|
+
# constraint validations:
|
20
|
+
#
|
21
|
+
# Employee
|
22
|
+
# / \
|
23
|
+
# Staff Manager
|
24
|
+
# |
|
25
|
+
# Executive
|
26
|
+
#
|
27
|
+
# # Loads constraint_validations from the employees table
|
28
|
+
# class Employee < Sequel::Model
|
29
|
+
# plugin :class_table_inheritance
|
30
|
+
# plugin :constraint_validations
|
31
|
+
# plugin :class_table_inheritance_constraint_validations
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # Loads constraint_validations from managers and employees tables
|
35
|
+
# class Manager < Employee
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # Loads constraint_validations from executives, managers, and
|
39
|
+
# # employees tables
|
40
|
+
# class Executive < Manager
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # Loads constraint_validations from staff and employees tables
|
44
|
+
# class Staff < Employee
|
45
|
+
# end
|
46
|
+
module ClassTableInheritanceConstraintValidations
|
47
|
+
def self.apply(model)
|
48
|
+
unless ConstraintValidations::InstanceMethods > model && ClassTableInheritance::InstanceMethods > model
|
49
|
+
raise Error, "must load the constraint_validations and class_table_inheritance plugins into #{model} before loading class_table_inheritance_constraint_validations plugin"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
private
|
55
|
+
|
56
|
+
def inherited(subclass)
|
57
|
+
super
|
58
|
+
|
59
|
+
# constraint_validations will parse_constraint_validations in the
|
60
|
+
# classes after_set_dataset hook. That runs before cti_tables are
|
61
|
+
# updated for subclasses in class_table_inheritance's inherited
|
62
|
+
# so re-parsing them here.
|
63
|
+
subclass.send(:parse_constraint_validations)
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_constraint_validations_dataset
|
67
|
+
reflections = {}
|
68
|
+
allow_missing_columns = db_schema.select{|col, sch| sch[:allow_null] == false && nil != sch[:default]}.map(&:first)
|
69
|
+
hash = Sequel.synchronize{db.constraint_validations}
|
70
|
+
cv = []
|
71
|
+
ds = @dataset.with_quote_identifiers(false)
|
72
|
+
cti_tables.each do |table_name|
|
73
|
+
table_name = ds.literal(table_name)
|
74
|
+
cv += (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections, allow_missing_columns)}
|
75
|
+
end
|
76
|
+
@constraint_validations = cv
|
77
|
+
@constraint_validation_reflections = reflections
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -113,7 +113,7 @@ module Sequel
|
|
113
113
|
def parse_constraint_validations
|
114
114
|
db.extension(:_model_constraint_validations)
|
115
115
|
|
116
|
-
unless
|
116
|
+
unless Sequel.synchronize{db.constraint_validations}
|
117
117
|
hash = {}
|
118
118
|
db.from(constraint_validations_table).each do |r|
|
119
119
|
(hash[r[:table]] ||= []) << r
|
@@ -121,14 +121,7 @@ module Sequel
|
|
121
121
|
Sequel.synchronize{db.constraint_validations = hash}
|
122
122
|
end
|
123
123
|
|
124
|
-
if @dataset
|
125
|
-
ds = @dataset.with_quote_identifiers(false)
|
126
|
-
table_name = ds.literal(ds.first_source_table)
|
127
|
-
reflections = {}
|
128
|
-
allow_missing_columns = db_schema.select{|col, sch| sch[:allow_null] == false && nil != sch[:default]}.map(&:first)
|
129
|
-
@constraint_validations = (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections, allow_missing_columns)}
|
130
|
-
@constraint_validation_reflections = reflections
|
131
|
-
end
|
124
|
+
parse_constraint_validations_dataset if @dataset
|
132
125
|
end
|
133
126
|
|
134
127
|
# Given a specific database constraint validation metadata row hash, transform
|
@@ -164,7 +157,7 @@ module Sequel
|
|
164
157
|
arg = constraint_validation_int_range(arg)
|
165
158
|
type = :includes
|
166
159
|
when *OPERATOR_MAP.keys
|
167
|
-
arg = arg.to_i if type.to_s
|
160
|
+
arg = arg.to_i if type.to_s.start_with?('int_')
|
168
161
|
operator = OPERATOR_MAP[type]
|
169
162
|
type = :operator
|
170
163
|
end
|
@@ -236,6 +229,18 @@ module Sequel
|
|
236
229
|
Regexp.new(arg)
|
237
230
|
end
|
238
231
|
end
|
232
|
+
|
233
|
+
# If this model has associated dataset, use the model's table name
|
234
|
+
# to get the validations for just this model.
|
235
|
+
def parse_constraint_validations_dataset
|
236
|
+
ds = @dataset.with_quote_identifiers(false)
|
237
|
+
table_name = ds.literal(ds.first_source_table)
|
238
|
+
reflections = {}
|
239
|
+
allow_missing_columns = db_schema.select{|col, sch| sch[:allow_null] == false && nil != sch[:default]}.map(&:first)
|
240
|
+
hash = Sequel.synchronize{db.constraint_validations}
|
241
|
+
@constraint_validations = (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections, allow_missing_columns)}
|
242
|
+
@constraint_validation_reflections = reflections
|
243
|
+
end
|
239
244
|
end
|
240
245
|
|
241
246
|
module InstanceMethods
|
@@ -370,9 +370,9 @@ module Sequel
|
|
370
370
|
end
|
371
371
|
|
372
372
|
# Convert the receiver to a JSON data structure using the given arguments.
|
373
|
-
def to_json_data(*args
|
374
|
-
if
|
375
|
-
to_json(*args){|x| return
|
373
|
+
def to_json_data(*args)
|
374
|
+
if defined?(yield)
|
375
|
+
to_json(*args){|x| return yield(x)}
|
376
376
|
else
|
377
377
|
to_json(*args){|x| return x}
|
378
378
|
end
|
@@ -347,6 +347,8 @@ module Sequel
|
|
347
347
|
coverage_data = options[:coverage_data] || Sequel.parse_json(File.binread(@unused_associations_coverage_file))
|
348
348
|
|
349
349
|
unused_associations_data = {}
|
350
|
+
to_many_modification_methods = [:adder, :remover, :clearer]
|
351
|
+
modification_methods = [:setter, :adder, :remover, :clearer]
|
350
352
|
|
351
353
|
([self] + descendants).each do |sc|
|
352
354
|
next unless cov_data = coverage_data[sc.name]
|
@@ -383,8 +385,8 @@ module Sequel
|
|
383
385
|
if !info[:used]
|
384
386
|
(unused_associations_data[sc.name] ||= {})[assoc.to_s] = 'unused'
|
385
387
|
elsif unused = info[:unused]
|
386
|
-
if unused.include?(:setter) ||
|
387
|
-
|
388
|
+
if unused.include?(:setter) || to_many_modification_methods.all?{|k| unused.include?(k)}
|
389
|
+
modification_methods.each do |k|
|
388
390
|
unused.delete(k)
|
389
391
|
end
|
390
392
|
unused << :read_only
|
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 = 95
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|