sequel 5.18.0 → 5.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/doc/opening_databases.rdoc +5 -2
- data/doc/release_notes/5.19.0.txt +28 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/sharding.rdoc +12 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/jdbc.rb +7 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +8 -8
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlanywhere.rb +33 -17
- data/lib/sequel/adapters/sqlite.rb +20 -13
- data/lib/sequel/connection_pool.rb +0 -5
- data/lib/sequel/database/misc.rb +10 -9
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset/actions.rb +6 -5
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +14 -0
- data/lib/sequel/extensions/pg_enum.rb +23 -15
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model/associations.rb +38 -12
- data/lib/sequel/model/base.rb +1 -1
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/json_serializer.rb +3 -3
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +8 -4
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
- data/lib/sequel/plugins/prepared_statements.rb +1 -1
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/plugins/validation_helpers.rb +2 -2
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +40 -0
- data/spec/core/database_spec.rb +73 -2
- data/spec/core/schema_spec.rb +7 -1
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/constraint_validations_spec.rb +20 -2
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
- data/spec/extensions/pg_enum_spec.rb +5 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/integration/plugin_test.rb +15 -0
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- metadata +7 -3
@@ -90,11 +90,6 @@ class Sequel::ConnectionPool
|
|
90
90
|
# connection object (and server argument if the callable accepts 2 arguments),
|
91
91
|
# useful for customizations that you want to apply to all connections.
|
92
92
|
# :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
93
|
-
# :preconnect :: Automatically create the maximum number of connections, so that they don't
|
94
|
-
# need to be created as needed. This is useful when connecting takes a long time
|
95
|
-
# and you want to avoid possible latency during runtime.
|
96
|
-
# Set to :concurrently to create the connections in separate threads. Otherwise
|
97
|
-
# they'll be created sequentially.
|
98
93
|
def initialize(db, opts=OPTS)
|
99
94
|
@db = db
|
100
95
|
@after_connect = opts[:after_connect]
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -106,8 +106,11 @@ module Sequel
|
|
106
106
|
# :log_connection_info :: Whether connection information should be logged when logging queries.
|
107
107
|
# :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
|
108
108
|
# :name :: A name to use for the Database object, displayed in PoolTimeout .
|
109
|
-
# :preconnect ::
|
110
|
-
#
|
109
|
+
# :preconnect :: Automatically create the maximum number of connections, so that they don't
|
110
|
+
# need to be created as needed. This is useful when connecting takes a long time
|
111
|
+
# and you want to avoid possible latency during runtime.
|
112
|
+
# Set to :concurrently to create the connections in separate threads. Otherwise
|
113
|
+
# they'll be created sequentially.
|
111
114
|
# :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
|
112
115
|
# connections are made by the :preconnect option.
|
113
116
|
# :quote_identifiers :: Whether to quote identifiers.
|
@@ -115,7 +118,9 @@ module Sequel
|
|
115
118
|
# :single_threaded :: Whether to use a single-threaded connection pool.
|
116
119
|
# :sql_log_level :: Method to use to log SQL to a logger, :info by default.
|
117
120
|
#
|
118
|
-
# All options given are also passed to the connection pool.
|
121
|
+
# All options given are also passed to the connection pool. Additional options respected by
|
122
|
+
# the connection pool are :after_connect, :connect_sqls, :max_connections, :pool_timeout,
|
123
|
+
# :servers, and :servers_hash. See the connection pool documentation for details.
|
119
124
|
def initialize(opts = OPTS)
|
120
125
|
@opts ||= opts
|
121
126
|
@opts = connection_pool_default_options.merge(@opts)
|
@@ -473,9 +478,7 @@ module Sequel
|
|
473
478
|
|
474
479
|
if RUBY_VERSION >= '2.4'
|
475
480
|
# Typecast a string to a BigDecimal
|
476
|
-
|
477
|
-
BigDecimal(value)
|
478
|
-
end
|
481
|
+
alias _typecast_value_string_to_decimal BigDecimal
|
479
482
|
else
|
480
483
|
# :nocov:
|
481
484
|
def _typecast_value_string_to_decimal(value)
|
@@ -510,9 +513,7 @@ module Sequel
|
|
510
513
|
end
|
511
514
|
|
512
515
|
# Typecast the value to a Float
|
513
|
-
|
514
|
-
Float(value)
|
515
|
-
end
|
516
|
+
alias typecast_value_float Float
|
516
517
|
|
517
518
|
# Typecast the value to an Integer
|
518
519
|
def typecast_value_integer(value)
|
@@ -331,7 +331,7 @@ module Sequel
|
|
331
331
|
:time
|
332
332
|
when /\A(bool(ean)?)\z/io
|
333
333
|
:boolean
|
334
|
-
when /\A(real|float
|
334
|
+
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
|
335
335
|
:float
|
336
336
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
|
337
337
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
@@ -634,7 +634,7 @@ module Sequel
|
|
634
634
|
|
635
635
|
# Drop a composite foreign key constraint
|
636
636
|
def drop_composite_foreign_key(columns, opts)
|
637
|
-
@operations <<
|
637
|
+
@operations << opts.merge(:op => :drop_constraint, :type => :foreign_key, :columns => columns)
|
638
638
|
nil
|
639
639
|
end
|
640
640
|
end
|
@@ -25,13 +25,19 @@ module Sequel
|
|
25
25
|
# Otherwise, add the block to the list of blocks to call after the currently
|
26
26
|
# in progress transaction commits (and only if it commits).
|
27
27
|
# Options:
|
28
|
+
# :savepoint :: If currently inside a savepoint, only run this hook on transaction
|
29
|
+
# commit if all enclosing savepoints have been released.
|
28
30
|
# :server :: The server/shard to use.
|
29
31
|
def after_commit(opts=OPTS, &block)
|
30
32
|
raise Error, "must provide block to after_commit" unless block
|
31
33
|
synchronize(opts[:server]) do |conn|
|
32
34
|
if h = _trans(conn)
|
33
35
|
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
34
|
-
|
36
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
37
|
+
add_savepoint_hook(conn, :after_commit, block)
|
38
|
+
else
|
39
|
+
add_transaction_hook(conn, :after_commit, block)
|
40
|
+
end
|
35
41
|
else
|
36
42
|
yield
|
37
43
|
end
|
@@ -42,13 +48,20 @@ module Sequel
|
|
42
48
|
# Otherwise, add the block to the list of the blocks to call after the currently
|
43
49
|
# in progress transaction rolls back (and only if it rolls back).
|
44
50
|
# Options:
|
51
|
+
# :savepoint :: If currently inside a savepoint, run this hook immediately when
|
52
|
+
# any enclosing savepoint is rolled back, which may be before the transaction
|
53
|
+
# commits or rollsback.
|
45
54
|
# :server :: The server/shard to use.
|
46
55
|
def after_rollback(opts=OPTS, &block)
|
47
56
|
raise Error, "must provide block to after_rollback" unless block
|
48
57
|
synchronize(opts[:server]) do |conn|
|
49
58
|
if h = _trans(conn)
|
50
59
|
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
51
|
-
|
60
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
61
|
+
add_savepoint_hook(conn, :after_rollback, block)
|
62
|
+
else
|
63
|
+
add_transaction_hook(conn, :after_rollback, block)
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
54
67
|
end
|
@@ -298,6 +311,13 @@ module Sequel
|
|
298
311
|
Sequel.synchronize{@transactions[conn] = hash}
|
299
312
|
end
|
300
313
|
|
314
|
+
# Set the given callable as a hook to be called. Type should be either
|
315
|
+
# :after_commit or :after_rollback.
|
316
|
+
def add_savepoint_hook(conn, type, block)
|
317
|
+
savepoint = _trans(conn)[:savepoints].last
|
318
|
+
(savepoint[type] ||= []) << block
|
319
|
+
end
|
320
|
+
|
301
321
|
# Set the given callable as a hook to be called. Type should be either
|
302
322
|
# :after_commit or :after_rollback.
|
303
323
|
def add_transaction_hook(conn, type, block)
|
@@ -401,6 +421,14 @@ module Sequel
|
|
401
421
|
supports_savepoints? && savepoint_level(conn) > 1
|
402
422
|
end
|
403
423
|
|
424
|
+
# Retrieve the savepoint hooks that should be run for the given
|
425
|
+
# connection and commit status.
|
426
|
+
def savepoint_hooks(conn, committed)
|
427
|
+
if in_savepoint?(conn)
|
428
|
+
_trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
404
432
|
# Retrieve the transaction hooks that should be run for the given
|
405
433
|
# connection and commit status.
|
406
434
|
def transaction_hooks(conn, committed)
|
@@ -411,16 +439,40 @@ module Sequel
|
|
411
439
|
|
412
440
|
# Remove the current thread from the list of active transactions
|
413
441
|
def remove_transaction(conn, committed)
|
414
|
-
|
442
|
+
if in_savepoint?(conn)
|
443
|
+
savepoint_callbacks = savepoint_hooks(conn, committed)
|
444
|
+
if committed
|
445
|
+
savepoint_rollback_callbacks = savepoint_hooks(conn, false)
|
446
|
+
end
|
447
|
+
else
|
448
|
+
callbacks = transaction_hooks(conn, committed)
|
449
|
+
end
|
415
450
|
|
416
451
|
if transaction_finished?(conn)
|
417
452
|
h = _trans(conn)
|
418
453
|
rolled_back = !committed
|
419
454
|
Sequel.synchronize{h[:rolled_back] = rolled_back}
|
420
455
|
Sequel.synchronize{@transactions.delete(conn)}
|
456
|
+
callbacks.each(&:call) if callbacks
|
457
|
+
elsif savepoint_callbacks || savepoint_rollback_callbacks
|
458
|
+
if committed
|
459
|
+
meth = in_savepoint?(conn) ? :add_savepoint_hook : :add_transaction_hook
|
460
|
+
|
461
|
+
if savepoint_callbacks
|
462
|
+
savepoint_callbacks.each do |block|
|
463
|
+
send(meth, conn, :after_commit, block)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
if savepoint_rollback_callbacks
|
468
|
+
savepoint_rollback_callbacks.each do |block|
|
469
|
+
send(meth, conn, :after_rollback, block)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
else
|
473
|
+
savepoint_callbacks.each(&:call)
|
474
|
+
end
|
421
475
|
end
|
422
|
-
|
423
|
-
callbacks.each(&:call) if callbacks
|
424
476
|
end
|
425
477
|
|
426
478
|
# SQL to rollback to a savepoint
|
@@ -18,7 +18,7 @@ module Sequel
|
|
18
18
|
where_all where_each where_single_value
|
19
19
|
METHS
|
20
20
|
|
21
|
-
# The clone options to use when
|
21
|
+
# The clone options to use when retrieving columns for a dataset.
|
22
22
|
COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
|
23
23
|
|
24
24
|
# Inserts the given argument into the database. Returns self so it
|
@@ -358,7 +358,7 @@ module Sequel
|
|
358
358
|
|
359
359
|
# Inserts values into the associated table. The returned value is generally
|
360
360
|
# the value of the autoincremented primary key for the inserted row, assuming that
|
361
|
-
#
|
361
|
+
# a single row is inserted and the table has an autoincrementing primary key.
|
362
362
|
#
|
363
363
|
# +insert+ handles a number of different argument formats:
|
364
364
|
# no arguments or single empty hash :: Uses DEFAULT VALUES
|
@@ -486,7 +486,7 @@ module Sequel
|
|
486
486
|
import(columns, hashes.map{|h| columns.map{|c| h[c]}}, opts)
|
487
487
|
end
|
488
488
|
|
489
|
-
# Yields each row in the dataset, but
|
489
|
+
# Yields each row in the dataset, but internally uses multiple queries as needed to
|
490
490
|
# process the entire result set without keeping all rows in the dataset in memory,
|
491
491
|
# even if the underlying driver buffers all query results in memory.
|
492
492
|
#
|
@@ -512,7 +512,7 @@ module Sequel
|
|
512
512
|
# NULLs. Note that some Sequel adapters have optimized implementations that will
|
513
513
|
# use cursors or streaming regardless of the :strategy option used.
|
514
514
|
# :filter_values :: If the strategy: :filter option is used, this option should be a proc
|
515
|
-
# that accepts the last
|
515
|
+
# that accepts the last retrieved row for the previous page and an array of
|
516
516
|
# ORDER BY expressions, and returns an array of values relating to those
|
517
517
|
# expressions for the last retrieved row. You will need to use this option
|
518
518
|
# if your ORDER BY expressions are not simple columns, if they contain
|
@@ -971,7 +971,8 @@ module Sequel
|
|
971
971
|
# separate insert commands for each row. Otherwise, call #multi_insert_sql
|
972
972
|
# and execute each statement it gives separately.
|
973
973
|
def _import(columns, values, opts)
|
974
|
-
trans_opts = Hash[opts]
|
974
|
+
trans_opts = Hash[opts]
|
975
|
+
trans_opts[:server] = @opts[:server]
|
975
976
|
if opts[:return] == :primary_key
|
976
977
|
@db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
|
977
978
|
else
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -21,7 +21,7 @@ module Sequel
|
|
21
21
|
raise Error, "cannot call add_graph_aliases on a dataset that has not been called with graph or set_graph_aliases"
|
22
22
|
end
|
23
23
|
columns, graph_aliases = graph_alias_columns(graph_aliases)
|
24
|
-
select_append(*columns).clone(:graph =>
|
24
|
+
select_append(*columns).clone(:graph => graph.merge(:column_aliases=>ga.merge(graph_aliases).freeze).freeze)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Similar to Dataset#join_table, but uses unambiguous aliases for selected
|
@@ -244,7 +244,7 @@ module Sequel
|
|
244
244
|
def set_graph_aliases(graph_aliases)
|
245
245
|
columns, graph_aliases = graph_alias_columns(graph_aliases)
|
246
246
|
if graph = opts[:graph]
|
247
|
-
select(*columns).clone(:graph =>
|
247
|
+
select(*columns).clone(:graph => graph.merge(:column_aliases=>graph_aliases.freeze).freeze)
|
248
248
|
else
|
249
249
|
raise Error, "cannot call #set_graph_aliases on an ungraphed dataset"
|
250
250
|
end
|
@@ -170,7 +170,10 @@ module Sequel
|
|
170
170
|
# receiver's dataset to the block, and the block should return the new dataset
|
171
171
|
# to use.
|
172
172
|
def with_dataset
|
173
|
-
|
173
|
+
dataset = yield @dataset
|
174
|
+
other = dup
|
175
|
+
other.instance_variable_set(:@dataset, dataset)
|
176
|
+
other.freeze
|
174
177
|
end
|
175
178
|
|
176
179
|
# Return an array of all objects by running the SQL query for the given arguments.
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -1091,7 +1091,7 @@ module Sequel
|
|
1091
1091
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
1092
1092
|
# # ) SELECT * FROM t
|
1093
1093
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
1094
|
-
raise(Error, 'This
|
1094
|
+
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
1095
1095
|
if hoist_cte?(nonrecursive)
|
1096
1096
|
s, ds = hoist_cte(nonrecursive)
|
1097
1097
|
s.with_recursive(name, ds, recursive, opts)
|
@@ -130,6 +130,10 @@
|
|
130
130
|
# readd all constraints you want to use inside the alter table block,
|
131
131
|
# making no other changes inside the alter_table block.
|
132
132
|
#
|
133
|
+
# Dropping a table will automatically delete all constraint validations for
|
134
|
+
# that table. However, altering a table (e.g. to drop a column) will not
|
135
|
+
# currently make any changes to the constraint validations metadata.
|
136
|
+
#
|
133
137
|
# Related module: Sequel::ConstraintValidations
|
134
138
|
|
135
139
|
#
|
@@ -264,6 +268,16 @@ module Sequel
|
|
264
268
|
end
|
265
269
|
end
|
266
270
|
|
271
|
+
# Drop all constraint validations for a table if dropping the table.
|
272
|
+
def drop_table(*names)
|
273
|
+
names.each do |name|
|
274
|
+
if !name.is_a?(Hash) && table_exists?(constraint_validations_table)
|
275
|
+
drop_constraint_validations_for(:table=>name)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
super
|
279
|
+
end
|
280
|
+
|
267
281
|
# Drop the constraint validations table.
|
268
282
|
def drop_constraint_validations_table
|
269
283
|
drop_table(constraint_validations_table)
|
@@ -17,6 +17,12 @@
|
|
17
17
|
#
|
18
18
|
# DB.rename_enum(:enum_type_name, :enum_type_another_name)
|
19
19
|
#
|
20
|
+
# If you want to rename an enum value, you can use rename_enum_value:
|
21
|
+
#
|
22
|
+
# DB.rename_enum_value(
|
23
|
+
# :enum_type_name, :enum_value_name, :enum_value_another_name
|
24
|
+
# )
|
25
|
+
#
|
20
26
|
# If you want to drop an enum type, you can use drop_enum:
|
21
27
|
#
|
22
28
|
# DB.drop_enum(:enum_type_name)
|
@@ -86,26 +92,24 @@ module Sequel
|
|
86
92
|
elsif v = opts[:after]
|
87
93
|
sql << " AFTER #{literal(v.to_s)}"
|
88
94
|
end
|
89
|
-
|
90
|
-
parse_enum_labels
|
91
|
-
nil
|
95
|
+
_process_enum_change_sql(sql)
|
92
96
|
end
|
93
97
|
|
94
98
|
# Run the SQL to create an enum type with the given name and values.
|
95
99
|
def create_enum(enum, values)
|
96
|
-
|
97
|
-
run sql
|
98
|
-
parse_enum_labels
|
99
|
-
nil
|
100
|
+
_process_enum_change_sql("CREATE TYPE #{quote_schema_table(enum)} AS ENUM (#{values.map{|v| literal(v.to_s)}.join(', ')})")
|
100
101
|
end
|
101
102
|
|
102
103
|
# Run the SQL to rename the enum type with the given name
|
103
104
|
# to the another given name.
|
104
105
|
def rename_enum(enum, new_name)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
_process_enum_change_sql("ALTER TYPE #{quote_schema_table(enum)} RENAME TO #{quote_schema_table(new_name)}")
|
107
|
+
end
|
108
|
+
|
109
|
+
# Run the SQL to rename the enum value with the given name
|
110
|
+
# to the another given name.
|
111
|
+
def rename_enum_value(enum, old_name, new_name)
|
112
|
+
_process_enum_change_sql("ALTER TYPE #{quote_schema_table(enum)} RENAME VALUE #{literal(old_name.to_s)} TO #{literal(new_name.to_s)}")
|
109
113
|
end
|
110
114
|
|
111
115
|
# Run the SQL to drop the enum type with the given name.
|
@@ -113,14 +117,18 @@ module Sequel
|
|
113
117
|
# :if_exists :: Do not raise an error if the enum type does not exist
|
114
118
|
# :cascade :: Also drop other objects that depend on the enum type
|
115
119
|
def drop_enum(enum, opts=OPTS)
|
116
|
-
|
117
|
-
run sql
|
118
|
-
parse_enum_labels
|
119
|
-
nil
|
120
|
+
_process_enum_change_sql("DROP TYPE#{' IF EXISTS' if opts[:if_exists]} #{quote_schema_table(enum)}#{' CASCADE' if opts[:cascade]}")
|
120
121
|
end
|
121
122
|
|
122
123
|
private
|
123
124
|
|
125
|
+
# Run the SQL on the database, reparsing the enum labels after it is run.
|
126
|
+
def _process_enum_change_sql(sql)
|
127
|
+
run(sql)
|
128
|
+
parse_enum_labels
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
124
132
|
# Parse the pg_enum table to get enum values, and
|
125
133
|
# the pg_type table to get names and array oids for
|
126
134
|
# enums.
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
38
38
|
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
|
39
39
|
{:type=>:Bignum}
|
40
|
-
when /\A(?:real|float
|
40
|
+
when /\A(?:real|float(?: unsigned)?|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/
|
41
41
|
{:type=>Float}
|
42
42
|
when 'boolean', 'bit', 'bool'
|
43
43
|
{:type=>TrueClass}
|
@@ -1617,9 +1617,10 @@ module Sequel
|
|
1617
1617
|
# is hash or array of two element arrays. Consider also specifying the :graph_block
|
1618
1618
|
# option if the value for this option is not a hash or array of two element arrays
|
1619
1619
|
# and you plan to use this association in eager_graph or association_join.
|
1620
|
-
# :dataset :: A proc that is
|
1620
|
+
# :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
|
1621
1621
|
# options are applied). If the proc accepts an argument, it is passed the related
|
1622
|
-
# association reflection.
|
1622
|
+
# association reflection. It is a best practice to always have the dataset accept an argument
|
1623
|
+
# and use the argument to return the appropriate dataset.
|
1623
1624
|
# :distinct :: Use the DISTINCT clause when selecting associating object, both when
|
1624
1625
|
# lazy loading and eager loading via .eager (but not when using .eager_graph).
|
1625
1626
|
# :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
|
@@ -1909,7 +1910,7 @@ module Sequel
|
|
1909
1910
|
# can be easily overridden in the class itself while allowing for
|
1910
1911
|
# super to be called.
|
1911
1912
|
def association_module_def(name, opts=OPTS, &block)
|
1912
|
-
association_module(opts).
|
1913
|
+
association_module(opts).send(:define_method, name, &block)
|
1913
1914
|
end
|
1914
1915
|
|
1915
1916
|
# Add a private method to the module included in the class.
|
@@ -1944,6 +1945,13 @@ module Sequel
|
|
1944
1945
|
end
|
1945
1946
|
|
1946
1947
|
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1948
|
+
if opts[:block]
|
1949
|
+
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
|
+
end
|
1951
|
+
if opts[:dataset]
|
1952
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1953
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1954
|
+
end
|
1947
1955
|
def_association_method(opts)
|
1948
1956
|
|
1949
1957
|
return if opts[:read_only]
|
@@ -2129,7 +2137,7 @@ module Sequel
|
|
2129
2137
|
graph_cks = opts[:graph_keys]
|
2130
2138
|
opts[:eager_grapher] ||= proc do |eo|
|
2131
2139
|
ds = eo[:self]
|
2132
|
-
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions,
|
2140
|
+
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
|
2133
2141
|
end
|
2134
2142
|
|
2135
2143
|
return if opts[:read_only]
|
@@ -2189,7 +2197,7 @@ module Sequel
|
|
2189
2197
|
graph_block = opts[:graph_block]
|
2190
2198
|
opts[:eager_grapher] ||= proc do |eo|
|
2191
2199
|
ds = eo[:self]
|
2192
|
-
ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions,
|
2200
|
+
ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
|
2193
2201
|
# We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
|
2194
2202
|
ds.opts[:eager_graph][:reciprocals][eo[:table_alias]] = opts.reciprocal
|
2195
2203
|
ds
|
@@ -2204,12 +2212,28 @@ module Sequel
|
|
2204
2212
|
if one_to_one
|
2205
2213
|
opts[:setter] ||= proc do |o|
|
2206
2214
|
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2215
|
+
|
2216
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2217
|
+
if old = up_ds.first
|
2218
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2219
|
+
end
|
2220
|
+
save_old = true
|
2221
|
+
end
|
2222
|
+
|
2207
2223
|
if o
|
2208
|
-
|
2224
|
+
if !o.new? && !save_old
|
2225
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2226
|
+
end
|
2209
2227
|
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2210
2228
|
end
|
2229
|
+
|
2211
2230
|
checked_transaction do
|
2212
|
-
|
2231
|
+
if save_old
|
2232
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2233
|
+
else
|
2234
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2235
|
+
end
|
2236
|
+
|
2213
2237
|
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2214
2238
|
end
|
2215
2239
|
end
|
@@ -2287,7 +2311,8 @@ module Sequel
|
|
2287
2311
|
end
|
2288
2312
|
ds = ds.clone(:model_object => self)
|
2289
2313
|
ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
|
2290
|
-
|
2314
|
+
# block method is private
|
2315
|
+
ds = send(opts[:block_method], ds) if opts[:block_method]
|
2291
2316
|
ds
|
2292
2317
|
end
|
2293
2318
|
|
@@ -2310,10 +2335,11 @@ module Sequel
|
|
2310
2335
|
# Return an association dataset for the given association reflection
|
2311
2336
|
def _dataset(opts)
|
2312
2337
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2313
|
-
ds = if opts[:
|
2314
|
-
|
2338
|
+
ds = if opts[:dataset_opt_arity] == 1
|
2339
|
+
# dataset_opt_method is private
|
2340
|
+
send(opts[:dataset_opt_method], opts)
|
2315
2341
|
else
|
2316
|
-
|
2342
|
+
send(opts[:dataset_opt_method])
|
2317
2343
|
end
|
2318
2344
|
_apply_association_options(opts, ds)
|
2319
2345
|
end
|
@@ -2908,7 +2934,7 @@ module Sequel
|
|
2908
2934
|
def eager(*associations)
|
2909
2935
|
opts = @opts[:eager]
|
2910
2936
|
association_opts = eager_options_for_associations(associations)
|
2911
|
-
opts = opts ?
|
2937
|
+
opts = opts ? opts.merge(association_opts) : association_opts
|
2912
2938
|
clone(:eager=>opts.freeze)
|
2913
2939
|
end
|
2914
2940
|
|