sequel 5.18.0 → 5.20.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 +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
|
|