sequel 5.15.0 → 5.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +28 -0
- data/bin/sequel +4 -0
- data/doc/opening_databases.rdoc +1 -1
- data/doc/release_notes/5.16.0.txt +110 -0
- data/doc/transactions.rdoc +48 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +14 -28
- data/lib/sequel/adapters/mysql.rb +2 -6
- data/lib/sequel/adapters/shared/sqlite.rb +16 -2
- data/lib/sequel/database/transactions.rb +66 -13
- data/lib/sequel/dataset/sql.rb +12 -1
- data/lib/sequel/extensions/migration.rb +4 -3
- data/lib/sequel/model/associations.rb +15 -3
- data/lib/sequel/model/base.rb +5 -3
- data/lib/sequel/plugins/class_table_inheritance.rb +6 -4
- data/lib/sequel/plugins/nested_attributes.rb +4 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/bin_spec.rb +7 -0
- data/spec/core/database_spec.rb +98 -1
- data/spec/core/expression_filters_spec.rb +13 -1
- data/spec/extensions/class_table_inheritance_spec.rb +45 -1
- data/spec/extensions/migration_spec.rb +15 -4
- data/spec/extensions/nested_attributes_spec.rb +40 -0
- data/spec/extensions/pg_array_associations_spec.rb +8 -8
- data/spec/extensions/pg_array_ops_spec.rb +5 -5
- data/spec/extensions/pg_hstore_ops_spec.rb +9 -9
- data/spec/extensions/pg_row_ops_spec.rb +2 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_agg_spec.rb +1 -1
- data/spec/integration/dataset_test.rb +1 -1
- data/spec/integration/transaction_test.rb +199 -0
- data/spec/model/associations_spec.rb +31 -0
- data/spec/model/base_spec.rb +49 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50240675066a0bd3e480a52ba3ff556b962ae3d8403fb22ee210272f2fffb023
|
4
|
+
data.tar.gz: d1c86ff37811732211b00b251c49bf7c4929ca7b18bf7c6ffeda1b8d0335482f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a5a5b35a91ff83c6a0dab3e5cb88e26d79dced018f46fa78ad8be2b4da37c602adba56f849c0591c12f976006ea6e511bb3153478e70f3824ac940772f14de6
|
7
|
+
data.tar.gz: 50f5217691ec39036999623ce8d791fc93c270876d857e30b53892def7d36e0f3c99a3c69b13f97162f45b49dd066f1a5fcbf8b9660f96a1cd5c0c5a23c9ee72
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
=== 5.16.0 (2019-01-02)
|
2
|
+
|
3
|
+
* Convert integer columns to bigint columns when copying SQLite databases to other databases using bin/sequel -C (jeremyevans) (#1584)
|
4
|
+
|
5
|
+
* Use nicer error messages for missing or empty migration directories (Lavode) (#1585)
|
6
|
+
|
7
|
+
* Make alter table emulation work correctly in SQLite 3.26.0+ (jeremyevans) (#1582)
|
8
|
+
|
9
|
+
* Do not unset new one_to_one associated objects' reciprocal associations before saving associated objects in the nested_attributes plugin (jeremyevans)
|
10
|
+
|
11
|
+
* Do not validate new one_to_one associated objects twice when saving in the nested_attributes plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Fix :qualify_tables option to class_table_inheritance plugin to work correctly with subclasses of subclasses (benalavi) (#1581)
|
14
|
+
|
15
|
+
* Make class_table_inheritance plugin use the schema cache instead of sending a query to get columns for tables (kenaniah) (#1580)
|
16
|
+
|
17
|
+
* Remove loading of mysqlplus in the mysql adapter (jeremyevans)
|
18
|
+
|
19
|
+
* Make mysql adapter work correctly on ruby 2.6+ (jeremyevans)
|
20
|
+
|
21
|
+
* Add Database#rollback_on_exit to rollback transactions instead of committing them when exiting the transaction block (jeremyevans)
|
22
|
+
|
23
|
+
* Enable window functions in SQLite 3.26.0+ (jeremyevans)
|
24
|
+
|
25
|
+
* Do not override existing methods when creating Sequel::Model attribute getter/setter methods (jeremyevans) (#1578)
|
26
|
+
|
27
|
+
* Use parentheses for expressions being subscripted (e.g. (array_agg(column))[1]) (jeremyevans)
|
28
|
+
|
1
29
|
=== 5.15.0 (2018-12-01)
|
2
30
|
|
3
31
|
* Add :conn_str option in the postgres adapter for PostgreSQL connection strings, if the pg driver is used (graywolf) (#1572)
|
data/bin/sequel
CHANGED
@@ -195,6 +195,10 @@ begin
|
|
195
195
|
same_db = DB.database_type==TO_DB.database_type
|
196
196
|
index_opts = {:same_db=>same_db}
|
197
197
|
index_opts[:index_names] = :namespace if !DB.global_index_namespace? && TO_DB.global_index_namespace?
|
198
|
+
if DB.database_type == :sqlite && !same_db
|
199
|
+
# SQLite integer types allows 64-bit integers
|
200
|
+
TO_DB.extension :integer64
|
201
|
+
end
|
198
202
|
|
199
203
|
puts "Databases connections successful"
|
200
204
|
schema_migration = eval(DB.dump_schema_migration(:indexes=>false, :same_db=>same_db))
|
data/doc/opening_databases.rdoc
CHANGED
@@ -233,7 +233,7 @@ jdbc-h2 :: jdbc-h2 versions greater than 1.3.175 have issues with ORDER BY not w
|
|
233
233
|
|
234
234
|
=== mysql
|
235
235
|
|
236
|
-
Requires:
|
236
|
+
Requires: mysql
|
237
237
|
|
238
238
|
The MySQL adapter does not support the pure-ruby mysql.rb driver, it requires the C-extension driver.
|
239
239
|
|
@@ -0,0 +1,110 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Database#rollback_on_exit has been added, which allows you to
|
4
|
+
rollback transactions instead of committing them when exiting
|
5
|
+
the transaction block. Previously, the only way to rollback
|
6
|
+
a transaction from inside a transaction block was to raise
|
7
|
+
an exception. This allows you to tell Sequel to roll the
|
8
|
+
transaction back on exit, and then use return or throw to exit
|
9
|
+
the transaction block.
|
10
|
+
|
11
|
+
Database#rollback_on_exit supports savepoints, including
|
12
|
+
multiple savepoint levels, as well as canceling rollbacks:
|
13
|
+
|
14
|
+
DB.transaction do # BEGIN
|
15
|
+
DB.rollback_on_exit
|
16
|
+
end # ROLLBACK
|
17
|
+
|
18
|
+
DB.transaction do # BEGIN
|
19
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
20
|
+
DB.rollback_on_exit(savepoint: true)
|
21
|
+
end # ROLLBACK TO SAVEPOINT
|
22
|
+
end # COMMIT
|
23
|
+
|
24
|
+
DB.transaction do # BEGIN
|
25
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
26
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
27
|
+
DB.rollback_on_exit(savepoint: true)
|
28
|
+
end # ROLLBACK TO SAVEPOINT
|
29
|
+
end # RELEASE SAVEPOINT
|
30
|
+
end # COMMIT
|
31
|
+
|
32
|
+
DB.transaction do # BEGIN
|
33
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
34
|
+
DB.rollback_on_exit(savepoint: true)
|
35
|
+
end # ROLLBACK TO SAVEPOINT
|
36
|
+
end # COMMIT
|
37
|
+
|
38
|
+
DB.transaction do # BEGIN
|
39
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
40
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
41
|
+
DB.rollback_on_exit(savepoint: 2)
|
42
|
+
end # ROLLBACK TO SAVEPOINT
|
43
|
+
end # ROLLBACK TO SAVEPOINT
|
44
|
+
end # COMMIT
|
45
|
+
|
46
|
+
DB.transaction do # BEGIN
|
47
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
48
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
49
|
+
DB.rollback_on_exit(savepoint: 3)
|
50
|
+
end # ROLLBACK TO SAVEPOINT
|
51
|
+
end # ROLLBACK TO SAVEPOINT
|
52
|
+
end # ROLLBACK
|
53
|
+
|
54
|
+
DB.transaction do # BEGIN
|
55
|
+
DB.rollback_on_exit
|
56
|
+
DB.rollback_on_exit(cancel: true)
|
57
|
+
end # COMMIT
|
58
|
+
|
59
|
+
* Sequel now supports window functions on SQLite 3.26.0+. SQLite
|
60
|
+
technically supports window functions on 3.25.0+, but enabling
|
61
|
+
window function support in Sequel opens up a code path that
|
62
|
+
generates queries that cause older versions of SQLite to produce a
|
63
|
+
segmentation fault. This bug in SQLite has been fixed in 3.26.0.
|
64
|
+
|
65
|
+
= Other Improvements
|
66
|
+
|
67
|
+
* Sequel::Model no longer overrides existing methods when defining
|
68
|
+
getters and setters. Historically, it only checked for existing
|
69
|
+
method definitions for methods that could be directly expressed
|
70
|
+
(e.g. not requiring send). Sequel 5 broke the check for setter
|
71
|
+
methods that could be directly expressed. This fixes cases where
|
72
|
+
model inheritance is used and the setter methods are overridden
|
73
|
+
in a parent class.
|
74
|
+
|
75
|
+
* Alter table emulation now works correctly on SQLite 3.26.0+.
|
76
|
+
|
77
|
+
* The one_to_one association setter does not modify reciprocal
|
78
|
+
associations in cases where doing so is not necessary. This can
|
79
|
+
fix some cases where the nested_attributes plugin is used.
|
80
|
+
|
81
|
+
* The class_table_inheritance plugin can now take advantage of the
|
82
|
+
schema_caching extension to prevent database queries to determine
|
83
|
+
column information when the class is created.
|
84
|
+
|
85
|
+
* The nested_attributes plugin no longer validates one_to_one
|
86
|
+
associations twice when saving.
|
87
|
+
|
88
|
+
* The class_table_inheritance plugin :qualify_tables option now
|
89
|
+
correctly qualifies subclasses of subclasses.
|
90
|
+
|
91
|
+
* SQL expressions that are subscripted are now wrapped in parentheses.
|
92
|
+
This fixes at least subscripting a function expression on
|
93
|
+
PostgreSQL:
|
94
|
+
|
95
|
+
DB[:t].select{array_agg(column).sql_subscript(1)}
|
96
|
+
# SELECT (array_agg(column))[1] FROM t
|
97
|
+
|
98
|
+
* Sequel::Migrator now uses more descriptive error messages if a
|
99
|
+
missing or empty migration directory is given.
|
100
|
+
|
101
|
+
* bin/sequel -C when converting from SQLite to another database
|
102
|
+
type will now use 64-bit integer columns in the other database when
|
103
|
+
the SQLite column type is integer, as SQLite supports storing
|
104
|
+
64-bit values in integer columns, and most other databases only
|
105
|
+
support 32-bit values in integer columns.
|
106
|
+
|
107
|
+
= Backwards Compatibility
|
108
|
+
|
109
|
+
* The mysql adapter no longer attempts to load the mysqlplus driver,
|
110
|
+
it now only attempts to load the mysql driver.
|
data/doc/transactions.rdoc
CHANGED
@@ -36,6 +36,12 @@ If any other exception is raised, the transaction is rolled back, and the except
|
|
36
36
|
end # ROLLBACK
|
37
37
|
# ArgumentError raised
|
38
38
|
|
39
|
+
If you want the current transaction to be rolled back when the transaction block exits instead of being committed (even if an exception is not raised), use <tt>Database#rollback_on_exit</tt>
|
40
|
+
|
41
|
+
DB.transaction do # BEGIN
|
42
|
+
DB.rollback_on_exit
|
43
|
+
end # ROLLBACK
|
44
|
+
|
39
45
|
If you want Sequel::Rollback exceptions to be reraised, use the <tt>rollback: :reraise</tt> option:
|
40
46
|
|
41
47
|
DB.transaction(rollback: :reraise) do # BEGIN
|
@@ -121,6 +127,48 @@ Other exceptions, unless rescued inside the outer transaction block, will rollba
|
|
121
127
|
end # ROLLBACK
|
122
128
|
# ArgumentError raised
|
123
129
|
|
130
|
+
If you want the current savepoint to be rolled back when the savepoint block exits instead of being committed (even if an exception is not raised), use <tt>Database#rollback_on_exit(:savepoint=>true)</tt>
|
131
|
+
|
132
|
+
DB.transaction do # BEGIN
|
133
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
134
|
+
DB.rollback_on_exit(:savepoint=>true)
|
135
|
+
end # ROLLBACK TO SAVEPOINT
|
136
|
+
end # COMMIT
|
137
|
+
|
138
|
+
DB.transaction do # BEGIN
|
139
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
140
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
141
|
+
DB.rollback_on_exit(:savepoint=>true)
|
142
|
+
end # ROLLBACK TO SAVEPOINT
|
143
|
+
end # RELEASE SAVEPOINT
|
144
|
+
end # COMMIT
|
145
|
+
|
146
|
+
If you want the current savepoint to be rolled back when the savepoint block exits (even if an exception is not raised), use <tt>Database#rollback_on_exit(:savepoint=>true)</tt>
|
147
|
+
|
148
|
+
DB.transaction do # BEGIN
|
149
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
150
|
+
DB.rollback_on_exit(:savepoint=>true)
|
151
|
+
end # ROLLBACK TO SAVEPOINT
|
152
|
+
end # COMMIT
|
153
|
+
|
154
|
+
If you want the current savepoint and potentially enclosing savepoints to be rolled back when the savepoint blocks exit (even if an exception is not raised), use <tt>Database#rollback_on_exit(:savepoint=>integer)</tt>
|
155
|
+
|
156
|
+
DB.transaction do # BEGIN
|
157
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
158
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
159
|
+
DB.rollback_on_exit(:savepoint=>2)
|
160
|
+
end # ROLLBACK TO SAVEPOINT
|
161
|
+
end # ROLLBACK TO SAVEPOINT
|
162
|
+
end # COMMIT
|
163
|
+
|
164
|
+
DB.transaction do # BEGIN
|
165
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
166
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
167
|
+
DB.rollback_on_exit(:savepoint=>3)
|
168
|
+
end # ROLLBACK TO SAVEPOINT
|
169
|
+
end # ROLLBACK TO SAVEPOINT
|
170
|
+
end # ROLLBACK
|
171
|
+
|
124
172
|
== Prepared Transactions / Two-Phase Commit
|
125
173
|
|
126
174
|
Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
|
@@ -43,17 +43,15 @@ module Sequel
|
|
43
43
|
true
|
44
44
|
end
|
45
45
|
|
46
|
+
# JDBC savepoint object for the current savepoint for the connection.
|
47
|
+
def savepoint_obj(conn)
|
48
|
+
_trans(conn)[:savepoints][-1][:obj]
|
49
|
+
end
|
50
|
+
|
46
51
|
# Use JDBC connection's setAutoCommit to false to start transactions
|
47
52
|
def begin_transaction(conn, opts=OPTS)
|
48
|
-
if
|
49
|
-
|
50
|
-
if sps = th[:savepoint_objs]
|
51
|
-
sps << log_connection_yield('Transaction.savepoint', conn){conn.set_savepoint}
|
52
|
-
else
|
53
|
-
log_connection_yield('Transaction.begin', conn){conn.setAutoCommit(false)}
|
54
|
-
th[:savepoint_objs] = []
|
55
|
-
set_transaction_isolation(conn, opts)
|
56
|
-
end
|
53
|
+
if in_savepoint?(conn)
|
54
|
+
_trans(conn)[:savepoints][-1][:obj] = log_connection_yield('Transaction.savepoint', conn){conn.set_savepoint}
|
57
55
|
else
|
58
56
|
log_connection_yield('Transaction.begin', conn){conn.setAutoCommit(false)}
|
59
57
|
set_transaction_isolation(conn, opts)
|
@@ -62,12 +60,9 @@ module Sequel
|
|
62
60
|
|
63
61
|
# Use JDBC connection's commit method to commit transactions
|
64
62
|
def commit_transaction(conn, opts=OPTS)
|
65
|
-
if
|
66
|
-
|
67
|
-
|
68
|
-
log_connection_yield('Transaction.commit', conn){conn.commit}
|
69
|
-
elsif supports_releasing_savepoints?
|
70
|
-
log_connection_yield('Transaction.release_savepoint', conn){supports_releasing_savepoints? ? conn.release_savepoint(sps.last) : sps.last}
|
63
|
+
if in_savepoint?(conn)
|
64
|
+
if supports_releasing_savepoints?
|
65
|
+
log_connection_yield('Transaction.release_savepoint', conn){conn.release_savepoint(savepoint_obj(conn))}
|
71
66
|
end
|
72
67
|
else
|
73
68
|
log_connection_yield('Transaction.commit', conn){conn.commit}
|
@@ -77,13 +72,9 @@ module Sequel
|
|
77
72
|
# Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
|
78
73
|
def remove_transaction(conn, committed)
|
79
74
|
if jdbc_level = _trans(conn)[:original_jdbc_isolation_level]
|
80
|
-
conn.setTransactionIsolation(jdbc_level)
|
75
|
+
log_connection_yield("Transaction.restore_isolation_level", conn){conn.setTransactionIsolation(jdbc_level)}
|
81
76
|
end
|
82
|
-
|
83
|
-
sps = _trans(conn)[:savepoint_objs]
|
84
|
-
conn.setAutoCommit(true) if sps.empty?
|
85
|
-
sps.pop
|
86
|
-
else
|
77
|
+
unless in_savepoint?(conn)
|
87
78
|
conn.setAutoCommit(true)
|
88
79
|
end
|
89
80
|
ensure
|
@@ -92,13 +83,8 @@ module Sequel
|
|
92
83
|
|
93
84
|
# Use JDBC connection's rollback method to rollback transactions
|
94
85
|
def rollback_transaction(conn, opts=OPTS)
|
95
|
-
if
|
96
|
-
|
97
|
-
if sps.empty?
|
98
|
-
log_connection_yield('Transaction.rollback', conn){conn.rollback}
|
99
|
-
else
|
100
|
-
log_connection_yield('Transaction.rollback_savepoint', conn){conn.rollback(sps.last)}
|
101
|
-
end
|
86
|
+
if in_savepoint?(conn)
|
87
|
+
log_connection_yield('Transaction.rollback_savepoint', conn){conn.rollback(savepoint_obj(conn))}
|
102
88
|
else
|
103
89
|
log_connection_yield('Transaction.rollback', conn){conn.rollback}
|
104
90
|
end
|
@@ -1,10 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "mysqlplus"
|
5
|
-
rescue LoadError
|
6
|
-
require 'mysql'
|
7
|
-
end
|
3
|
+
require 'mysql'
|
8
4
|
raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
|
9
5
|
|
10
6
|
require_relative 'utils/mysql_mysql2'
|
@@ -21,7 +17,7 @@ module Sequel
|
|
21
17
|
# Hash with integer keys and callable values for converting MySQL types.
|
22
18
|
MYSQL_TYPES = {}
|
23
19
|
{
|
24
|
-
[0, 246] => ::
|
20
|
+
[0, 246] => ::Kernel.method(:BigDecimal),
|
25
21
|
[2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
|
26
22
|
[4, 5] => tt.method(:float),
|
27
23
|
[249, 250, 251, 252] => ::Sequel::SQL::Blob.method(:new)
|
@@ -177,7 +177,10 @@ module Sequel
|
|
177
177
|
# needed for drop column.
|
178
178
|
def apply_alter_table(table, ops)
|
179
179
|
fks = fetch("PRAGMA foreign_keys")
|
180
|
-
|
180
|
+
if fks
|
181
|
+
run "PRAGMA foreign_keys = 0"
|
182
|
+
run "PRAGMA legacy_alter_table = 1" if sqlite_version >= 32600
|
183
|
+
end
|
181
184
|
transaction do
|
182
185
|
if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint || op[:op] == :set_column_null}
|
183
186
|
null_ops, ops = ops.partition{|op| op[:op] == :set_column_null}
|
@@ -196,7 +199,10 @@ module Sequel
|
|
196
199
|
end
|
197
200
|
remove_cached_schema(table)
|
198
201
|
ensure
|
199
|
-
|
202
|
+
if fks
|
203
|
+
run "PRAGMA foreign_keys = 1"
|
204
|
+
run "PRAGMA legacy_alter_table = 0" if sqlite_version >= 32600
|
205
|
+
end
|
200
206
|
end
|
201
207
|
|
202
208
|
# SQLite supports limited table modification. You can add a column
|
@@ -725,6 +731,14 @@ module Sequel
|
|
725
731
|
false
|
726
732
|
end
|
727
733
|
|
734
|
+
# SQLite 3.25+ supports window functions. However, support is only enabled
|
735
|
+
# on SQLite 3.26.0+ because internal Sequel usage of window functions
|
736
|
+
# to implement eager loading of limited associations triggers
|
737
|
+
# an SQLite crash bug in versions 3.25.0-3.25.3.
|
738
|
+
def supports_window_functions?
|
739
|
+
db.sqlite_version >= 32600
|
740
|
+
end
|
741
|
+
|
728
742
|
private
|
729
743
|
|
730
744
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
@@ -52,7 +52,48 @@ module Sequel
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
|
+
# When exiting the transaction block through methods other than an exception
|
57
|
+
# (e.g. normal exit, non-local return, or throw), set the current transaction
|
58
|
+
# to rollback instead of committing. This is designed for use in cases where
|
59
|
+
# you want to preform a non-local return but also want to rollback instead of
|
60
|
+
# committing.
|
61
|
+
# Options:
|
62
|
+
# :cancel :: Cancel the current rollback_on_exit setting, so exiting will commit instead
|
63
|
+
# of rolling back.
|
64
|
+
# :savepoint :: Rollback only the current savepoint if inside a savepoint.
|
65
|
+
# Can also be an positive integer value to rollback that number of enclosing savepoints,
|
66
|
+
# up to and including the transaction itself.
|
67
|
+
# If the database does not support savepoints, this option is ignored and the entire
|
68
|
+
# transaction is affected.
|
69
|
+
# :server :: The server/shard the transaction is being executed on.
|
70
|
+
def rollback_on_exit(opts=OPTS)
|
71
|
+
synchronize(opts[:server]) do |conn|
|
72
|
+
raise Error, "Cannot call Sequel:: Database#rollback_on_exit! unless inside a transaction" unless h = _trans(conn)
|
73
|
+
rollback = !opts[:cancel]
|
74
|
+
|
75
|
+
if supports_savepoints?
|
76
|
+
savepoints = h[:savepoints]
|
77
|
+
|
78
|
+
if level = opts[:savepoint]
|
79
|
+
level = 1 if level == true
|
80
|
+
raise Error, "invalid :savepoint option to Database#rollback_on_exit: #{level.inspect}" unless level.is_a?(Integer)
|
81
|
+
raise Error, "cannot pass nonpositive integer (#{level.inspect}) as :savepoint option to Database#rollback_on_exit" if level < 1
|
82
|
+
level.times do |i|
|
83
|
+
break unless savepoint = savepoints[-1 - i]
|
84
|
+
savepoint[:rollback_on_exit] = rollback
|
85
|
+
end
|
86
|
+
else
|
87
|
+
savepoints[0][:rollback_on_exit] = rollback
|
88
|
+
end
|
89
|
+
else
|
90
|
+
h[:rollback_on_exit] = rollback
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
56
97
|
# Return true if already in a transaction given the options,
|
57
98
|
# false otherwise. Respects the :server option for selecting
|
58
99
|
# a shard.
|
@@ -164,7 +205,7 @@ module Sequel
|
|
164
205
|
end
|
165
206
|
end
|
166
207
|
|
167
|
-
if opts[:savepoint] != false && (stack = _trans(conn)[:savepoints]) && stack.last
|
208
|
+
if opts[:savepoint] != false && (stack = _trans(conn)[:savepoints]) && stack.last[:auto_savepoint]
|
168
209
|
opts[:savepoint] = true
|
169
210
|
end
|
170
211
|
|
@@ -242,10 +283,10 @@ module Sequel
|
|
242
283
|
|
243
284
|
if supports_savepoints?
|
244
285
|
if t = _trans(conn)
|
245
|
-
t[:savepoints].push(opts[:auto_savepoint])
|
286
|
+
t[:savepoints].push({:auto_savepoint=>opts[:auto_savepoint]})
|
246
287
|
return
|
247
288
|
else
|
248
|
-
hash[:savepoints] = [opts[:auto_savepoint]]
|
289
|
+
hash[:savepoints] = [{:auto_savepoint=>opts[:auto_savepoint]}]
|
249
290
|
if (prep = opts[:prepare]) && supports_prepared_transactions?
|
250
291
|
hash[:prepare] = prep
|
251
292
|
end
|
@@ -293,12 +334,8 @@ module Sequel
|
|
293
334
|
|
294
335
|
# Start a new database transaction or a new savepoint on the given connection.
|
295
336
|
def begin_transaction(conn, opts=OPTS)
|
296
|
-
if
|
297
|
-
|
298
|
-
begin_savepoint(conn, opts)
|
299
|
-
else
|
300
|
-
begin_new_transaction(conn, opts)
|
301
|
-
end
|
337
|
+
if in_savepoint?(conn)
|
338
|
+
begin_savepoint(conn, opts)
|
302
339
|
else
|
303
340
|
begin_new_transaction(conn, opts)
|
304
341
|
end
|
@@ -318,7 +355,7 @@ module Sequel
|
|
318
355
|
if exception
|
319
356
|
false
|
320
357
|
else
|
321
|
-
if
|
358
|
+
if rollback_on_transaction_exit?(conn, opts)
|
322
359
|
rollback_transaction(conn, opts)
|
323
360
|
false
|
324
361
|
else
|
@@ -359,10 +396,15 @@ module Sequel
|
|
359
396
|
[]
|
360
397
|
end
|
361
398
|
|
399
|
+
# Whether the connection is currently inside a savepoint.
|
400
|
+
def in_savepoint?(conn)
|
401
|
+
supports_savepoints? && savepoint_level(conn) > 1
|
402
|
+
end
|
403
|
+
|
362
404
|
# Retrieve the transaction hooks that should be run for the given
|
363
405
|
# connection and commit status.
|
364
406
|
def transaction_hooks(conn, committed)
|
365
|
-
|
407
|
+
unless in_savepoint?(conn)
|
366
408
|
_trans(conn)[committed ? :after_commit : :after_rollback]
|
367
409
|
end
|
368
410
|
end
|
@@ -372,7 +414,7 @@ module Sequel
|
|
372
414
|
callbacks = transaction_hooks(conn, committed)
|
373
415
|
|
374
416
|
if transaction_finished?(conn)
|
375
|
-
h =
|
417
|
+
h = _trans(conn)
|
376
418
|
rolled_back = !committed
|
377
419
|
Sequel.synchronize{h[:rolled_back] = rolled_back}
|
378
420
|
Sequel.synchronize{@transactions.delete(conn)}
|
@@ -386,6 +428,17 @@ module Sequel
|
|
386
428
|
"ROLLBACK TO SAVEPOINT autopoint_#{depth}"
|
387
429
|
end
|
388
430
|
|
431
|
+
# Whether to rollback the transaction when exiting the transaction.
|
432
|
+
def rollback_on_transaction_exit?(conn, opts)
|
433
|
+
return true if Thread.current.status == 'aborting'
|
434
|
+
h = _trans(conn)
|
435
|
+
if supports_savepoints?
|
436
|
+
h[:savepoints].last[:rollback_on_exit]
|
437
|
+
else
|
438
|
+
h[:rollback_on_exit]
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
389
442
|
# Rollback the active transaction on the connection
|
390
443
|
def rollback_transaction(conn, opts=OPTS)
|
391
444
|
if supports_savepoints?
|