sequel 5.39.0 → 5.72.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 +408 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +13 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +26 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +28 -16
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +90 -9
- data/lib/sequel/adapters/shared/mysql.rb +47 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -1
- data/lib/sequel/adapters/shared/postgres.rb +496 -178
- data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
- data/lib/sequel/adapters/shared/sqlite.rb +116 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +55 -31
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +46 -53
- data/lib/sequel/database/schema_methods.rb +18 -2
- data/lib/sequel/dataset/actions.rb +108 -14
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +171 -44
- data/lib/sequel/dataset/sql.rb +182 -47
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +439 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +11 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +32 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +11 -24
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +21 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +345 -101
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +10 -6
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +46 -12
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +132 -38
data/lib/sequel/model/base.rb
CHANGED
@@ -508,7 +508,9 @@ module Sequel
|
|
508
508
|
|
509
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
510
510
|
end
|
511
|
+
# :nocov:
|
511
512
|
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
513
|
+
# :nocov:
|
512
514
|
|
513
515
|
# Returns primary key attribute hash. If using a composite primary key
|
514
516
|
# value such be an array with values for each primary key in the correct
|
@@ -604,6 +606,7 @@ module Sequel
|
|
604
606
|
@db_schema = get_db_schema
|
605
607
|
end
|
606
608
|
|
609
|
+
@fast_pk_lookup_sql = @fast_instance_delete_sql = nil unless @dataset.supports_placeholder_literalizer?
|
607
610
|
reset_instance_dataset
|
608
611
|
self
|
609
612
|
end
|
@@ -678,24 +681,26 @@ module Sequel
|
|
678
681
|
|
679
682
|
private
|
680
683
|
|
681
|
-
# Yield to the passed block and if do_raise is false, swallow
|
684
|
+
# Yield to the passed block and if do_raise is false, swallow Sequel::Errors other than DatabaseConnectionError
|
685
|
+
# and DatabaseDisconnectError.
|
682
686
|
def check_non_connection_error(do_raise=require_valid_table)
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
raise if do_raise
|
689
|
-
end
|
687
|
+
db.transaction(:savepoint=>:only){yield}
|
688
|
+
rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
|
689
|
+
raise
|
690
|
+
rescue Sequel::Error
|
691
|
+
raise if do_raise
|
690
692
|
end
|
691
693
|
|
692
694
|
# Convert the given object to a Dataset that should be used as
|
693
695
|
# this model's dataset.
|
694
696
|
def convert_input_dataset(ds)
|
695
697
|
case ds
|
696
|
-
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
698
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
697
699
|
self.simple_table = db.literal(ds).freeze
|
698
700
|
ds = db.from(ds)
|
701
|
+
when SQL::AliasedExpression, LiteralString
|
702
|
+
self.simple_table = nil
|
703
|
+
ds = db.from(ds)
|
699
704
|
when Dataset
|
700
705
|
ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
|
701
706
|
|
@@ -727,8 +732,14 @@ module Sequel
|
|
727
732
|
im = instance_methods
|
728
733
|
overridable_methods_module.module_eval do
|
729
734
|
meth = :"#{column}="
|
730
|
-
|
731
|
-
|
735
|
+
unless im.include?(column)
|
736
|
+
define_method(column){self[column]}
|
737
|
+
alias_method(column, column)
|
738
|
+
end
|
739
|
+
unless im.include?(meth)
|
740
|
+
define_method(meth){|v| self[column] = v}
|
741
|
+
alias_method(meth, meth)
|
742
|
+
end
|
732
743
|
end
|
733
744
|
end
|
734
745
|
|
@@ -741,8 +752,14 @@ module Sequel
|
|
741
752
|
im = instance_methods
|
742
753
|
columns.each do |column|
|
743
754
|
meth = :"#{column}="
|
744
|
-
|
745
|
-
|
755
|
+
unless im.include?(column)
|
756
|
+
overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__)
|
757
|
+
overridable_methods_module.send(:alias_method, column, column)
|
758
|
+
end
|
759
|
+
unless im.include?(meth)
|
760
|
+
overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__)
|
761
|
+
overridable_methods_module.send(:alias_method, meth, meth)
|
762
|
+
end
|
746
763
|
end
|
747
764
|
end
|
748
765
|
|
@@ -757,7 +774,10 @@ module Sequel
|
|
757
774
|
else
|
758
775
|
define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
|
759
776
|
end
|
777
|
+
singleton_class.send(:alias_method, meth, meth)
|
778
|
+
# :nocov:
|
760
779
|
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
780
|
+
# :nocov:
|
761
781
|
end
|
762
782
|
|
763
783
|
# Get the schema from the database, fall back on checking the columns
|
@@ -769,7 +789,7 @@ module Sequel
|
|
769
789
|
schema_hash = {}
|
770
790
|
ds_opts = dataset.opts
|
771
791
|
get_columns = proc{check_non_connection_error{columns} || []}
|
772
|
-
schema_array =
|
792
|
+
schema_array = get_db_schema_array(reload) if db.supports_schema_parsing?
|
773
793
|
if schema_array
|
774
794
|
schema_array.each{|k,v| schema_hash[k] = v}
|
775
795
|
|
@@ -806,6 +826,12 @@ module Sequel
|
|
806
826
|
schema_hash
|
807
827
|
end
|
808
828
|
|
829
|
+
# Get the array of schema information for the dataset. Returns nil if
|
830
|
+
# the schema information cannot be determined.
|
831
|
+
def get_db_schema_array(reload)
|
832
|
+
check_non_connection_error(false){db.schema(dataset, :reload=>reload)}
|
833
|
+
end
|
834
|
+
|
809
835
|
# Uncached version of setter_methods, to be overridden by plugins
|
810
836
|
# that want to modify the methods used.
|
811
837
|
def get_setter_methods
|
@@ -1076,7 +1102,7 @@ module Sequel
|
|
1076
1102
|
@modified = true
|
1077
1103
|
initialize_set(values)
|
1078
1104
|
_clear_changed_columns(:initialize)
|
1079
|
-
yield self if
|
1105
|
+
yield self if defined?(yield)
|
1080
1106
|
end
|
1081
1107
|
|
1082
1108
|
# Returns value of the column's attribute.
|
@@ -1116,7 +1142,7 @@ module Sequel
|
|
1116
1142
|
#
|
1117
1143
|
# Artist[1] === Artist[1] # => true
|
1118
1144
|
# Artist.new === Artist.new # => false
|
1119
|
-
# Artist[1].set(:
|
1145
|
+
# Artist[1].set(name: 'Bob') === Artist[1] # => true
|
1120
1146
|
def ===(obj)
|
1121
1147
|
case pkv = pk
|
1122
1148
|
when nil
|
@@ -1135,7 +1161,7 @@ module Sequel
|
|
1135
1161
|
#
|
1136
1162
|
# Artist[1].pk_equal?(Artist[1]) # => true
|
1137
1163
|
# Artist.new.pk_equal?(Artist.new) # => false
|
1138
|
-
# Artist[1].set(:
|
1164
|
+
# Artist[1].set(name: 'Bob').pk_equal?(Artist[1]) # => true
|
1139
1165
|
alias pk_equal? ===
|
1140
1166
|
|
1141
1167
|
# class is defined in Object, but it is also a keyword,
|
@@ -1207,7 +1233,7 @@ module Sequel
|
|
1207
1233
|
#
|
1208
1234
|
# Artist[1] == Artist[1] # => true
|
1209
1235
|
# Artist.new == Artist.new # => true
|
1210
|
-
# Artist[1].set(:
|
1236
|
+
# Artist[1].set(name: 'Bob') == Artist[1] # => false
|
1211
1237
|
def eql?(obj)
|
1212
1238
|
(obj.class == model) && (obj.values == @values)
|
1213
1239
|
end
|
@@ -1243,12 +1269,12 @@ module Sequel
|
|
1243
1269
|
# Once an object is frozen, you cannot modify it's values, changed_columns,
|
1244
1270
|
# errors, or dataset.
|
1245
1271
|
def freeze
|
1246
|
-
values.freeze
|
1247
|
-
_changed_columns.freeze
|
1248
1272
|
unless errors.frozen?
|
1249
1273
|
validate
|
1250
1274
|
errors.freeze
|
1251
1275
|
end
|
1276
|
+
values.freeze
|
1277
|
+
_changed_columns.freeze
|
1252
1278
|
this if !new? && model.primary_key
|
1253
1279
|
super
|
1254
1280
|
end
|
@@ -1309,13 +1335,13 @@ module Sequel
|
|
1309
1335
|
# a = Artist[1]
|
1310
1336
|
# Artist.db.transaction do
|
1311
1337
|
# a.lock!
|
1312
|
-
# a.update(:
|
1338
|
+
# a.update(name: 'A')
|
1313
1339
|
# end
|
1314
1340
|
#
|
1315
1341
|
# a = Artist[2]
|
1316
1342
|
# Artist.db.transaction do
|
1317
1343
|
# a.lock!('FOR NO KEY UPDATE')
|
1318
|
-
# a.update(:
|
1344
|
+
# a.update(name: 'B')
|
1319
1345
|
# end
|
1320
1346
|
def lock!(style=:update)
|
1321
1347
|
_refresh(this.lock_style(style)) unless new?
|
@@ -1613,11 +1639,9 @@ module Sequel
|
|
1613
1639
|
# artist.set(name: 'Invalid').valid? # => false
|
1614
1640
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
1615
1641
|
def valid?(opts = OPTS)
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
false
|
1620
|
-
end
|
1642
|
+
_valid?(opts)
|
1643
|
+
rescue HookFailed
|
1644
|
+
false
|
1621
1645
|
end
|
1622
1646
|
|
1623
1647
|
private
|
@@ -8,6 +8,9 @@ module Sequel
|
|
8
8
|
# automatically creates class methods for public dataset
|
9
9
|
# methods.
|
10
10
|
class DatasetModule < Dataset::DatasetModule
|
11
|
+
# The model class related to this dataset module.
|
12
|
+
attr_reader :model
|
13
|
+
|
11
14
|
# Store the model related to this dataset module.
|
12
15
|
def initialize(model)
|
13
16
|
@model = model
|
data/lib/sequel/model/errors.rb
CHANGED
@@ -38,7 +38,7 @@ module Sequel
|
|
38
38
|
def full_messages
|
39
39
|
inject([]) do |m, kv|
|
40
40
|
att, errors = *kv
|
41
|
-
errors.each {|e| m << (e.is_a?(LiteralString) ? e :
|
41
|
+
errors.each {|e| m << (e.is_a?(LiteralString) ? e : full_message(att, e))}
|
42
42
|
m
|
43
43
|
end
|
44
44
|
end
|
@@ -53,6 +53,15 @@ module Sequel
|
|
53
53
|
v
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Create full error message to use for the given attribute (or array of attributes)
|
60
|
+
# and error message. This can be overridden for easier internalization.
|
61
|
+
def full_message(att, error_msg)
|
62
|
+
att = att.join(' and ') if att.is_a?(Array)
|
63
|
+
"#{att} #{error_msg}"
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -31,7 +31,9 @@ module Sequel
|
|
31
31
|
def self.def_dataset_methods(mod, meths)
|
32
32
|
Array(meths).each do |meth|
|
33
33
|
mod.class_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
|
34
|
+
# :nocov:
|
34
35
|
mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
36
|
+
# :nocov:
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -120,6 +122,7 @@ module Sequel
|
|
120
122
|
|
121
123
|
model.send(:define_method, meth, &block)
|
122
124
|
model.send(:private, meth)
|
125
|
+
model.send(:alias_method, meth, meth)
|
123
126
|
call_meth
|
124
127
|
end
|
125
128
|
|
@@ -141,6 +144,8 @@ module Sequel
|
|
141
144
|
keyword = :required
|
142
145
|
when :key, :keyrest
|
143
146
|
keyword ||= true
|
147
|
+
else
|
148
|
+
raise Error, "invalid arg_type passed to _define_sequel_method_arg_numbers: #{arg_type}"
|
144
149
|
end
|
145
150
|
end
|
146
151
|
arity = callable.arity
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
extension 'async_thread_pool'
|
5
|
+
|
6
|
+
module Plugins
|
7
|
+
# The async_thread_pool plugin makes it slightly easier to use the async_thread_pool
|
8
|
+
# Database extension with models. It makes Model.async return an async dataset for the
|
9
|
+
# model, and support async behavior for #destroy, #with_pk, and #with_pk! for model
|
10
|
+
# datasets:
|
11
|
+
#
|
12
|
+
# # Will load the artist with primary key 1 asynchronously
|
13
|
+
# artist = Artist.async.with_pk(1)
|
14
|
+
#
|
15
|
+
# You must load the async_thread_pool Database extension into the Database object the
|
16
|
+
# model class uses in order for async behavior to work.
|
17
|
+
#
|
18
|
+
# Usage:
|
19
|
+
#
|
20
|
+
# # Make all model subclass datasets support support async class methods and additional
|
21
|
+
# # async dataset methods
|
22
|
+
# Sequel::Model.plugin :async_thread_pool
|
23
|
+
#
|
24
|
+
# # Make the Album class support async class method and additional async dataset methods
|
25
|
+
# Album.plugin :async_thread_pool
|
26
|
+
module AsyncThreadPool
|
27
|
+
module ClassMethods
|
28
|
+
Plugins.def_dataset_methods(self, :async)
|
29
|
+
end
|
30
|
+
|
31
|
+
module DatasetMethods
|
32
|
+
[:destroy, :with_pk, :with_pk!].each do |meth|
|
33
|
+
::Sequel::Database::AsyncThreadPool::DatasetMethods.define_async_method(self, meth)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The auto_restrict_eager_graph plugin will automatically disallow the use
|
6
|
+
# of eager_graph for associations that have associated blocks but no :graph_*
|
7
|
+
# association options. The reason for this is the block will have an effect
|
8
|
+
# during regular and eager loading, but not loading via eager_graph, and it
|
9
|
+
# is likely that whatever the block is doing should have an equivalent done
|
10
|
+
# when eager_graphing. Most likely, not including a :graph_* option was either
|
11
|
+
# an oversight (and one should be added), or use with eager_graph was never
|
12
|
+
# intended (and usage should be forbidden). Disallowing eager_graph in this
|
13
|
+
# case prevents likely unexpected behavior during eager_graph.
|
14
|
+
#
|
15
|
+
# As an example of this, consider the following code:
|
16
|
+
#
|
17
|
+
# Album.one_to_many :popular_tracks, class: :Track do |ds|
|
18
|
+
# ds = ds.where(popular: true)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Album.eager(:popular_tracks).all
|
22
|
+
# # SELECT * FROM albums
|
23
|
+
# # SELECT * FROM tracks WHERE ((popular IS TRUE) AND (album_id IN (...)))
|
24
|
+
#
|
25
|
+
# # Notice that no condition for tracks.popular is added.
|
26
|
+
# Album.eager_graph(:popular_tracks).all
|
27
|
+
# # SELECT ... FROM albums LEFT JOIN tracks ON (tracks.album_id = albums.id)
|
28
|
+
#
|
29
|
+
# With the auto_restrict_eager_graph plugin, the eager_graph call above will
|
30
|
+
# raise an error, alerting you to the fact that you either should not be
|
31
|
+
# using eager_graph with the association, or that you should be adding an
|
32
|
+
# appropriate :graph_* option, such as:
|
33
|
+
#
|
34
|
+
# Album.one_to_many :popular_tracks, class: :Track, graph_conditions: {popular: true} do |ds|
|
35
|
+
# ds = ds.where(popular: true)
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Usage:
|
39
|
+
#
|
40
|
+
# # Automatically restrict eager_graph for associations if appropriate for all
|
41
|
+
# # model subclasses (called before loading subclasses)
|
42
|
+
# Sequel::Model.plugin :auto_restrict_eager_graph
|
43
|
+
#
|
44
|
+
# # Automatically restrict eager_graph for associations in Album class
|
45
|
+
# Album.plugin :auto_restrict_eager_graph
|
46
|
+
module AutoRestrictEagerGraph
|
47
|
+
module ClassMethods
|
48
|
+
# When defining an association, if a block is given for the association, but
|
49
|
+
# a :graph_* option is not used, disallow the use of eager_graph.
|
50
|
+
def associate(type, name, opts = OPTS, &block)
|
51
|
+
opts = super
|
52
|
+
|
53
|
+
if opts[:block] && !opts.has_key?(:allow_eager_graph) && !opts[:orig_opts].any?{|k,| /\Agraph_/ =~ k}
|
54
|
+
opts[:allow_eager_graph] = false
|
55
|
+
end
|
56
|
+
|
57
|
+
opts
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -9,12 +9,16 @@ module Sequel
|
|
9
9
|
# 2. not_null validations on NOT NULL columns (optionally, presence validations)
|
10
10
|
# 3. unique validations on columns or sets of columns with unique indexes
|
11
11
|
# 4. max length validations on string columns
|
12
|
+
# 5. no null byte validations on string columns
|
13
|
+
# 6. minimum and maximum values on columns
|
12
14
|
#
|
13
|
-
# To determine the columns to use for the type/not_null/max_length validations,
|
15
|
+
# To determine the columns to use for the type/not_null/max_length/no_null_byte/max_value/min_value validations,
|
14
16
|
# the plugin looks at the database schema for the model's table. To determine
|
15
17
|
# the unique validations, Sequel looks at the indexes on the table. In order
|
16
18
|
# for this plugin to be fully functional, the underlying database adapter needs
|
17
|
-
# to support both schema and index parsing.
|
19
|
+
# to support both schema and index parsing. Additionally, unique validations are
|
20
|
+
# only added for models that select from a simple table, they are not added for models
|
21
|
+
# that select from a subquery.
|
18
22
|
#
|
19
23
|
# This plugin uses the validation_helpers plugin underneath to implement the
|
20
24
|
# validations. It does not allow for any per-column validation message
|
@@ -48,9 +52,14 @@ module Sequel
|
|
48
52
|
#
|
49
53
|
# Model.plugin :auto_validations, unique_opts: {only_if_modified: true}
|
50
54
|
#
|
51
|
-
# This works for unique_opts, max_length_opts, schema_types_opts,
|
55
|
+
# This works for unique_opts, max_length_opts, schema_types_opts, max_value_opts, min_value_opts, no_null_byte_opts,
|
52
56
|
# explicit_not_null_opts, and not_null_opts.
|
53
57
|
#
|
58
|
+
# If you only want auto_validations to add validations to columns that do not already
|
59
|
+
# have an error associated with them, you can use the skip_invalid option:
|
60
|
+
#
|
61
|
+
# Model.plugin :auto_validations, skip_invalid: true
|
62
|
+
#
|
54
63
|
# Usage:
|
55
64
|
#
|
56
65
|
# # Make all model subclass use auto validations (called before loading subclasses)
|
@@ -64,25 +73,35 @@ module Sequel
|
|
64
73
|
MAX_LENGTH_OPTIONS = {:from=>:values, :allow_nil=>true}.freeze
|
65
74
|
SCHEMA_TYPES_OPTIONS = NOT_NULL_OPTIONS
|
66
75
|
UNIQUE_OPTIONS = NOT_NULL_OPTIONS
|
76
|
+
NO_NULL_BYTE_OPTIONS = MAX_LENGTH_OPTIONS
|
77
|
+
MAX_VALUE_OPTIONS = {:from=>:values, :allow_nil=>true, :skip_invalid=>true}.freeze
|
78
|
+
MIN_VALUE_OPTIONS = MAX_VALUE_OPTIONS
|
79
|
+
AUTO_VALIDATE_OPTIONS = {
|
80
|
+
:no_null_byte=>NO_NULL_BYTE_OPTIONS,
|
81
|
+
:not_null=>NOT_NULL_OPTIONS,
|
82
|
+
:explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
|
83
|
+
:max_length=>MAX_LENGTH_OPTIONS,
|
84
|
+
:max_value=>MAX_VALUE_OPTIONS,
|
85
|
+
:min_value=>MIN_VALUE_OPTIONS,
|
86
|
+
:schema_types=>SCHEMA_TYPES_OPTIONS,
|
87
|
+
:unique=>UNIQUE_OPTIONS
|
88
|
+
}.freeze
|
89
|
+
|
67
90
|
EMPTY_ARRAY = [].freeze
|
68
91
|
|
69
92
|
def self.apply(model, opts=OPTS)
|
70
93
|
model.instance_exec do
|
71
94
|
plugin :validation_helpers
|
72
95
|
@auto_validate_presence = false
|
96
|
+
@auto_validate_no_null_byte_columns = []
|
73
97
|
@auto_validate_not_null_columns = []
|
74
98
|
@auto_validate_explicit_not_null_columns = []
|
75
99
|
@auto_validate_max_length_columns = []
|
100
|
+
@auto_validate_max_value_columns = []
|
101
|
+
@auto_validate_min_value_columns = []
|
76
102
|
@auto_validate_unique_columns = []
|
77
103
|
@auto_validate_types = true
|
78
|
-
|
79
|
-
@auto_validate_options = {
|
80
|
-
:not_null=>NOT_NULL_OPTIONS,
|
81
|
-
:explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
|
82
|
-
:max_length=>MAX_LENGTH_OPTIONS,
|
83
|
-
:schema_types=>SCHEMA_TYPES_OPTIONS,
|
84
|
-
:unique=>UNIQUE_OPTIONS
|
85
|
-
}.freeze
|
104
|
+
@auto_validate_options = AUTO_VALIDATE_OPTIONS
|
86
105
|
end
|
87
106
|
end
|
88
107
|
|
@@ -95,16 +114,26 @@ module Sequel
|
|
95
114
|
end
|
96
115
|
|
97
116
|
h = @auto_validate_options.dup
|
98
|
-
[:not_null, :explicit_not_null, :max_length, :schema_types, :unique].each do |type|
|
117
|
+
[:not_null, :explicit_not_null, :max_length, :max_value, :min_value, :no_null_byte, :schema_types, :unique].each do |type|
|
99
118
|
if type_opts = opts[:"#{type}_opts"]
|
100
119
|
h[type] = h[type].merge(type_opts).freeze
|
101
120
|
end
|
102
121
|
end
|
122
|
+
|
123
|
+
if opts[:skip_invalid]
|
124
|
+
[:not_null, :explicit_not_null, :no_null_byte, :max_length, :schema_types].each do |type|
|
125
|
+
h[type] = h[type].merge(:skip_invalid=>true).freeze
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
103
129
|
@auto_validate_options = h.freeze
|
104
130
|
end
|
105
131
|
end
|
106
132
|
|
107
133
|
module ClassMethods
|
134
|
+
# The columns with automatic no_null_byte validations
|
135
|
+
attr_reader :auto_validate_no_null_byte_columns
|
136
|
+
|
108
137
|
# The columns with automatic not_null validations
|
109
138
|
attr_reader :auto_validate_not_null_columns
|
110
139
|
|
@@ -115,13 +144,31 @@ module Sequel
|
|
115
144
|
# pairs, with the first entry being the column name and second entry being the maximum length.
|
116
145
|
attr_reader :auto_validate_max_length_columns
|
117
146
|
|
147
|
+
# The columns with automatch max value validations, as an array of
|
148
|
+
# pairs, with the first entry being the column name and second entry being the maximum value.
|
149
|
+
attr_reader :auto_validate_max_value_columns
|
150
|
+
|
151
|
+
# The columns with automatch min value validations, as an array of
|
152
|
+
# pairs, with the first entry being the column name and second entry being the minimum value.
|
153
|
+
attr_reader :auto_validate_min_value_columns
|
154
|
+
|
118
155
|
# The columns or sets of columns with automatic unique validations
|
119
156
|
attr_reader :auto_validate_unique_columns
|
120
157
|
|
121
158
|
# Inherited options
|
122
159
|
attr_reader :auto_validate_options
|
123
160
|
|
124
|
-
Plugins.inherited_instance_variables(self,
|
161
|
+
Plugins.inherited_instance_variables(self,
|
162
|
+
:@auto_validate_presence=>nil,
|
163
|
+
:@auto_validate_types=>nil,
|
164
|
+
:@auto_validate_no_null_byte_columns=>:dup,
|
165
|
+
:@auto_validate_not_null_columns=>:dup,
|
166
|
+
:@auto_validate_explicit_not_null_columns=>:dup,
|
167
|
+
:@auto_validate_max_length_columns=>:dup,
|
168
|
+
:@auto_validate_max_value_columns=>:dup,
|
169
|
+
:@auto_validate_min_value_columns=>:dup,
|
170
|
+
:@auto_validate_unique_columns=>:dup,
|
171
|
+
:@auto_validate_options => :dup)
|
125
172
|
Plugins.after_set_dataset(self, :setup_auto_validations)
|
126
173
|
|
127
174
|
# Whether to use a presence validation for not null columns
|
@@ -136,20 +183,27 @@ module Sequel
|
|
136
183
|
|
137
184
|
# Freeze auto_validation settings when freezing model class.
|
138
185
|
def freeze
|
186
|
+
@auto_validate_no_null_byte_columns.freeze
|
139
187
|
@auto_validate_not_null_columns.freeze
|
140
188
|
@auto_validate_explicit_not_null_columns.freeze
|
141
189
|
@auto_validate_max_length_columns.freeze
|
190
|
+
@auto_validate_max_value_columns.freeze
|
191
|
+
@auto_validate_min_value_columns.freeze
|
142
192
|
@auto_validate_unique_columns.freeze
|
143
193
|
|
144
194
|
super
|
145
195
|
end
|
146
196
|
|
147
|
-
# Skip automatic validations for the given validation type
|
197
|
+
# Skip automatic validations for the given validation type
|
198
|
+
# (:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value).
|
148
199
|
# If :all is given as the type, skip all auto validations.
|
200
|
+
#
|
201
|
+
# Skipping types validation automatically skips max_value and min_value validations,
|
202
|
+
# since those validations require valid types.
|
149
203
|
def skip_auto_validations(type)
|
150
204
|
case type
|
151
205
|
when :all
|
152
|
-
[:not_null, :types, :unique, :max_length].each{|v| skip_auto_validations(v)}
|
206
|
+
[:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value].each{|v| skip_auto_validations(v)}
|
153
207
|
when :not_null
|
154
208
|
auto_validate_not_null_columns.clear
|
155
209
|
auto_validate_explicit_not_null_columns.clear
|
@@ -169,6 +223,9 @@ module Sequel
|
|
169
223
|
explicit_not_null_cols += Array(primary_key)
|
170
224
|
@auto_validate_explicit_not_null_columns = explicit_not_null_cols.uniq
|
171
225
|
@auto_validate_max_length_columns = db_schema.select{|col, sch| sch[:type] == :string && sch[:max_length].is_a?(Integer)}.map{|col, sch| [col, sch[:max_length]]}
|
226
|
+
@auto_validate_max_value_columns = db_schema.select{|col, sch| sch[:max_value]}.map{|col, sch| [col, sch[:max_value]]}
|
227
|
+
@auto_validate_min_value_columns = db_schema.select{|col, sch| sch[:min_value]}.map{|col, sch| [col, sch[:min_value]]}
|
228
|
+
@auto_validate_no_null_byte_columns = db_schema.select{|_, sch| sch[:type] == :string}.map{|col, _| col}
|
172
229
|
table = dataset.first_source_table
|
173
230
|
@auto_validate_unique_columns = if db.supports_index_parsing? && [Symbol, SQL::QualifiedIdentifier, SQL::Identifier, String].any?{|c| table.is_a?(c)}
|
174
231
|
db.indexes(table).select{|name, idx| idx[:unique] == true}.map{|name, idx| idx[:columns].length == 1 ? idx[:columns].first : idx[:columns]}
|
@@ -195,6 +252,9 @@ module Sequel
|
|
195
252
|
return if skip.include?(:all)
|
196
253
|
opts = model.auto_validate_options
|
197
254
|
|
255
|
+
unless skip.include?(:no_null_byte) || (no_null_byte_columns = model.auto_validate_no_null_byte_columns).empty?
|
256
|
+
validates_no_null_byte(no_null_byte_columns, opts[:no_null_byte])
|
257
|
+
end
|
198
258
|
|
199
259
|
unless skip.include?(:not_null)
|
200
260
|
not_null_method = model.auto_validate_presence? ? :validates_presence : :validates_not_null
|
@@ -214,6 +274,18 @@ module Sequel
|
|
214
274
|
|
215
275
|
unless skip.include?(:types) || !model.auto_validate_types?
|
216
276
|
validates_schema_types(keys, opts[:schema_types])
|
277
|
+
|
278
|
+
unless skip.include?(:max_value) || ((max_value_columns = model.auto_validate_max_value_columns).empty?)
|
279
|
+
max_value_columns.each do |col, max|
|
280
|
+
validates_max_value(max, col, opts[:max_value])
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
unless skip.include?(:min_value) || ((min_value_columns = model.auto_validate_min_value_columns).empty?)
|
285
|
+
min_value_columns.each do |col, min|
|
286
|
+
validates_min_value(min, col, opts[:min_value])
|
287
|
+
end
|
288
|
+
end
|
217
289
|
end
|
218
290
|
|
219
291
|
unless skip.include?(:unique)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The auto_validations_constraint_validations_presence_message plugin provides
|
6
|
+
# integration for the auto_validations and constraint_validations plugins in
|
7
|
+
# the following situation:
|
8
|
+
#
|
9
|
+
# * A column has a NOT NULL constraint in the database
|
10
|
+
# * A constraint validation for presence exists on the column, with a :message
|
11
|
+
# option to set a column-specific message, and with the :allow_nil option set
|
12
|
+
# to true because the CHECK constraint doesn't need to check for NULL values
|
13
|
+
# as the column itself is NOT NULL
|
14
|
+
#
|
15
|
+
# In this case, by default the validation error message on the column will
|
16
|
+
# use the more specific constraint validation error message if the column
|
17
|
+
# has a non-NULL empty value, but will use the default auto_validations
|
18
|
+
# message if the column has a NULL value. With this plugin, the column-specific
|
19
|
+
# constraint validation error message will be used in both cases.
|
20
|
+
#
|
21
|
+
# Usage:
|
22
|
+
#
|
23
|
+
# # Make all model subclasses use this auto_validations/constraint_validations
|
24
|
+
# # integration (called before loading subclasses)
|
25
|
+
# Sequel::Model.plugin :auto_validations_constraint_validations_presence_message
|
26
|
+
#
|
27
|
+
# # Make the Album class use this auto_validations/constraint_validations integration
|
28
|
+
# Album.plugin :auto_validations_constraint_validations_presence_message
|
29
|
+
module AutoValidationsConstraintValidationsPresenceMessage
|
30
|
+
def self.apply(model)
|
31
|
+
model.plugin :auto_validations
|
32
|
+
model.plugin :constraint_validations
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.configure(model, opts=OPTS)
|
36
|
+
model.send(:_adjust_auto_validations_constraint_validations_presence_message)
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
Plugins.after_set_dataset(self, :_adjust_auto_validations_constraint_validations_presence_message)
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def _adjust_auto_validations_constraint_validations_presence_message
|
45
|
+
if @dataset &&
|
46
|
+
!@auto_validate_options[:not_null][:message] &&
|
47
|
+
!@auto_validate_options[:explicit_not_null][:message]
|
48
|
+
|
49
|
+
@constraint_validations.each do |array|
|
50
|
+
meth, column, opts = array
|
51
|
+
|
52
|
+
if meth == :validates_presence &&
|
53
|
+
opts &&
|
54
|
+
opts[:message] &&
|
55
|
+
opts[:allow_nil] &&
|
56
|
+
(@auto_validate_not_null_columns.include?(column) || @auto_validate_explicit_not_null_columns.include?(column))
|
57
|
+
|
58
|
+
@auto_validate_not_null_columns.delete(column)
|
59
|
+
@auto_validate_explicit_not_null_columns.delete(column)
|
60
|
+
array[2] = array[2].merge(:allow_nil=>false)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -104,7 +104,7 @@ module Sequel
|
|
104
104
|
# should reference the subquery alias (and qualified identifiers should not be needed
|
105
105
|
# unless joining to another table):
|
106
106
|
#
|
107
|
-
# a = Executive.where(:
|
107
|
+
# a = Executive.where(id: 1).first # works
|
108
108
|
# a = Executive.where{{employees[:id]=>1}}.first # works
|
109
109
|
# a = Executive.where{{executives[:id]=>1}}.first # doesn't work
|
110
110
|
#
|
@@ -115,7 +115,7 @@ module Sequel
|
|
115
115
|
#
|
116
116
|
# pks = Executive.where{num_staff < 10}.select_map(:id)
|
117
117
|
# Executive.cti_tables.reverse_each do |table|
|
118
|
-
# DB.from(table).where(:
|
118
|
+
# DB.from(table).where(id: pks).delete
|
119
119
|
# end
|
120
120
|
#
|
121
121
|
# = Usage
|