sequel 5.83.1 → 5.85.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/connection_pool.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset/actions.rb +9 -1
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/pg_json_ops.rb +642 -9
- data/lib/sequel/sql.rb +8 -5
- data/lib/sequel/version.rb +2 -2
- metadata +4 -237
- data/CHANGELOG +0 -1397
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -104
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -439
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.83.0.txt +0 -56
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -204
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
data/doc/testing.rdoc
DELETED
@@ -1,204 +0,0 @@
|
|
1
|
-
= Testing with Sequel
|
2
|
-
|
3
|
-
Whether or not you use Sequel in your application, you are usually going to want to have tests that ensure that your code works. When you are using Sequel, it's helpful to integrate it into your testing framework, and it's generally best to run each test in its own transaction if possible. That keeps all tests isolated from each other, and it's simple as it handles all of the cleanup for you. Sequel doesn't ship with helpers for common libraries, as the exact code you need is often application-specific, but this page offers some examples that you can either use directly or build on.
|
4
|
-
|
5
|
-
== Transactional tests
|
6
|
-
|
7
|
-
These run each test in its own transaction, the recommended way to test.
|
8
|
-
|
9
|
-
=== minitest/spec
|
10
|
-
|
11
|
-
==== with minitest-hooks
|
12
|
-
require 'minitest/hooks/default'
|
13
|
-
|
14
|
-
DB = Sequel.postgres # change if using sqlite etc
|
15
|
-
|
16
|
-
class Minitest::HooksSpec
|
17
|
-
def around
|
18
|
-
DB.transaction(rollback: :always, auto_savepoint: true){super}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
==== without minitest-hooks
|
23
|
-
DB = Sequel.postgres # change if using sqlite etc
|
24
|
-
|
25
|
-
class Minitest::Spec
|
26
|
-
def run(*args, &block)
|
27
|
-
DB.transaction(rollback: :always, auto_savepoint: true){super}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
=== minitest/test
|
32
|
-
DB = Sequel.postgres # change if using sqlite etc
|
33
|
-
|
34
|
-
# Use this class as the base class for your tests
|
35
|
-
class SequelTestCase < Minitest::Test
|
36
|
-
def run(*args, &block)
|
37
|
-
DB.transaction(rollback: :always, auto_savepoint: true){super}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
=== rspec >= 2.8
|
42
|
-
DB = Sequel.postgres # change the database if you are using sqlite etc.
|
43
|
-
|
44
|
-
RSpec.configure do |c|
|
45
|
-
c.around(:each) do |example|
|
46
|
-
DB.transaction(rollback: :always, auto_savepoint: true){example.run}
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
== Transactional testing with multiple threads
|
51
|
-
|
52
|
-
Some tests may require executing code across multiple threads. The most common example are browser tests with Capybara, where the web server is running in a separate thread. For transactional tests to work in this case, the main thread needs to allow other threads to use its database connection while the transaction is in progress. This can be achieved with the temporarily_release_connection extension:
|
53
|
-
|
54
|
-
DB.extension :temporarily_release_connection
|
55
|
-
DB.transaction(rollback: :always, auto_savepoint: true) do |conn|
|
56
|
-
DB.temporarily_release_connection(conn) do
|
57
|
-
# Other threads can operate on connection safely inside the transaction
|
58
|
-
yield
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
This requires maximum connection pool size to be 1, so make sure to set the Database +:max_connections+ option to 1 in tests.
|
63
|
-
|
64
|
-
== Transactional testing with multiple databases
|
65
|
-
|
66
|
-
You can use the Sequel.transaction method to run a transaction on multiple databases, rolling all of them back. Instead of:
|
67
|
-
|
68
|
-
DB.transaction(rollback: :always)
|
69
|
-
|
70
|
-
Use Sequel.transaction with an array of databases:
|
71
|
-
|
72
|
-
Sequel.transaction([DB1, DB2, DB3], rollback: :always)
|
73
|
-
|
74
|
-
== Transactional testing with savepoints
|
75
|
-
|
76
|
-
Using minitest/spec and minitest-hooks, and assuming your database supports it, you can use
|
77
|
-
transactions around entire test suites, using savepoints around each test. This can sigificantly
|
78
|
-
speed up any test suite where there is a lot of shared setup in a before all hook. By using
|
79
|
-
savepoints per test, each test is isolated from each other, rolling back changes after it
|
80
|
-
completes, and by using transactions per test suite, you only pay the cost to load the data once
|
81
|
-
for the test suite, and it is automatically rolled back after the test suite completes.
|
82
|
-
|
83
|
-
Example:
|
84
|
-
|
85
|
-
require 'minitest/hooks/default'
|
86
|
-
class Minitest::HooksSpec
|
87
|
-
def around
|
88
|
-
DB.transaction(rollback: :always, savepoint: true, auto_savepoint: true){super}
|
89
|
-
end
|
90
|
-
|
91
|
-
def around_all
|
92
|
-
DB.transaction(rollback: :always){super}
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
describe "some large test suite" do
|
97
|
-
before(:all) do
|
98
|
-
DB[:table].import # Large number of rows
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
== Nontransactional tests
|
103
|
-
|
104
|
-
In some cases, it is not possible to use transactions. For example, if you are testing a web application that is running in a separate process, you don't have access to that process's database connections, so you can't run your examples in transactions. In that case, the best way to handle things is to cleanup after each test by deleting or truncating the database tables used in the test.
|
105
|
-
|
106
|
-
The order in which you delete/truncate the tables is important if you are using referential integrity in your database (which you should be doing). If you are using referential integrity, you need to make sure to delete in tables referencing other tables before the tables that are being referenced. For example, if you have an +albums+ table with an +artist_id+ field referencing the +artists+ table, you want to delete/truncate the +albums+ table before the +artists+ table. Note that if you have cyclic references in your database, you will probably need to write your own custom cleaning code.
|
107
|
-
|
108
|
-
=== minitest/spec or rspec
|
109
|
-
|
110
|
-
describe "some test suite" do
|
111
|
-
after do
|
112
|
-
[:table1, :table2].each{|x| DB.from(x).truncate}
|
113
|
-
# or
|
114
|
-
[:table1, :table2].each{|x| DB.from(x).delete}
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
=== minitest/test
|
119
|
-
|
120
|
-
class SomeTestClass < Minitest::Test
|
121
|
-
def teardown
|
122
|
-
[:table1, :table2].each{|x| DB.from(x).truncate}
|
123
|
-
# or
|
124
|
-
[:table1, :table2].each{|x| DB.from(x).delete}
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
= Testing Sequel Itself
|
129
|
-
|
130
|
-
Sequel has multiple separate test suites. All test suites use minitest/spec, with the minitest-hooks and minitest-global_expectations extensions. To install the dependencies necessary to test Sequel, run <tt>gem install --development sequel</tt>.
|
131
|
-
|
132
|
-
== rake
|
133
|
-
|
134
|
-
The default rake task runs Sequel's core, model, plugin, and extension specs, the same as <tt>rake spec</tt> or <tt>rake spec_core spec_model spec_plugin</tt>.
|
135
|
-
|
136
|
-
== rake spec_core
|
137
|
-
|
138
|
-
The +spec_core+ rake task runs Sequel's core specs. These specs use a mocked database connection, and test for specific SQL used and for generally correct behavior.
|
139
|
-
|
140
|
-
== rake spec_model
|
141
|
-
|
142
|
-
The +spec_model+ rake task runs Sequel's model specs. These specs also use a mocked database connection, and operate similar to the core tests.
|
143
|
-
|
144
|
-
== rake spec_plugin
|
145
|
-
|
146
|
-
The +spec_plugin+ rake task runs the specs for the plugins and extensions that ship with Sequel. These also use a mocked database connection, and operate very similarly to the general Sequel core and model specs.
|
147
|
-
|
148
|
-
== rake spec_core_ext
|
149
|
-
|
150
|
-
The +spec_core_ext+ rake task runs the specs for the core_extensions extension. These are run separately from the other extension tests to make sure none of the other extensions require the core_extensions.
|
151
|
-
|
152
|
-
== rake spec_bin
|
153
|
-
|
154
|
-
The +spec_bin+ rake task runs the specs for bin/sequel. These use an SQLite3 database, and require either the sqlite3 (non-JRuby) or jdbc-sqlite3 (JRuby) gem.
|
155
|
-
|
156
|
-
== rake spec_<i>adapter</i> (e.g. rake spec_postgres)
|
157
|
-
|
158
|
-
The <tt>spec_<i>adapter</i></tt> specs run against a real database connection with nothing mocked, and test for correct results. They are slower than the standard specs, but they will catch errors that are mocked out by the default specs, as well as show issues that only occur on a certain database, adapter, or a combination of the two.
|
159
|
-
|
160
|
-
These specs are broken down into two parts. For each database, there are specific specs that only apply to that database, and these are called the adapter specs. There are also shared specs that apply to all (or almost all) databases, these are called the integration specs. For database types that don't have specific adapter tests, you can use <tt>rake spec_integration</tt> to just run the shared integration tests.
|
161
|
-
|
162
|
-
Each adapter needs a specific gem installed in order to run. Please see the {connecting to a database guide}[rdoc-ref:doc/opening_databases.rdoc] for which gem you need to install for the adapter you are testing.
|
163
|
-
|
164
|
-
== Environment variables
|
165
|
-
|
166
|
-
Sequel uses environment variables when testing to specify either the database to be tested or specify how testing should be done. You can also specify the databases to test by copying <tt>spec/spec_config.rb.example</tt> to <tt>spec/spec_config.rb</tt> and modifying it. See that file for details. It may be necessary to use +spec_config.rb+ as opposed to an environment variable if your database connection cannot be specified by a connection string.
|
167
|
-
|
168
|
-
Sequel does not create test databases automatically, except for file-based databases such as SQLite/H2/HSQLDB/Derby. It's up to the user to create the test databases manually and give Sequel a valid connection string in an environment variable (or setup the connection object in +spec_config.rb+).
|
169
|
-
|
170
|
-
=== Connection Strings
|
171
|
-
|
172
|
-
The SEQUEL_INTEGRATION_URL environment variable specifies the Database connection URL to use for the adapter and integration specs. Additionally, when running the adapter specs, you can also use the SEQUEL_<i>ADAPTER</i>_URL environment variable (e.g. SEQUEL_POSTGRES_URL for spec_postgres).
|
173
|
-
|
174
|
-
=== Other
|
175
|
-
|
176
|
-
SEQUEL_AUTO_CAST_DATE_TIME :: Use the auto_cast_date_and_time extension when running the specs
|
177
|
-
SEQUEL_ASYNC_THREAD_POOL :: Use the async_thread_pool extension when running the specs
|
178
|
-
SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when running the specs, with the :preempt_async_thread option
|
179
|
-
SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
|
180
|
-
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
181
|
-
SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
|
182
|
-
SEQUEL_CONNECTION_VALIDATOR :: Use the connection_validator extension when running the adapter/integration specs
|
183
|
-
SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
|
184
|
-
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
185
|
-
SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
|
186
|
-
SEQUEL_FREEZE_DATABASE :: Freeze the database before running the integration specs
|
187
|
-
SEQUEL_IDENTIFIER_MANGLING :: Use the identifier_mangling extension when running the specs
|
188
|
-
SEQUEL_INDEX_CACHING :: Use the index_caching extension when running the specs
|
189
|
-
SEQUEL_INTEGER64 :: Use the integer64 extension when running the adapter or integration specs
|
190
|
-
SEQUEL_MODEL_PREPARED_STATEMENTS :: Use the prepared_statements plugin when running the specs
|
191
|
-
SEQUEL_MODEL_THROW_FAILURES :: Use the throw_failures plugin when running the specs
|
192
|
-
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
193
|
-
SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
|
194
|
-
SEQUEL_PG_AUTO_PARAMETERIZE :: Use the pg_auto_parameterize extension when running the postgres specs. Value can be +in_array+ to test the pg_auto_parameterize_in_array extension, and +in_array_string+ to test the pg_auto_parameterize_in_array extension with the +:treat_in_string_list_as_text_array+ Database option set.
|
195
|
-
SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
|
196
|
-
SEQUEL_PRIMARY_KEY_LOOKUP_CHECK_VALUES :: Use the primary_key_lookup_check_values extension when running the adapter or integration specs
|
197
|
-
SEQUEL_QUERY_PER_ASSOCIATION_DB_0_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
198
|
-
SEQUEL_QUERY_PER_ASSOCIATION_DB_1_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
199
|
-
SEQUEL_QUERY_PER_ASSOCIATION_DB_2_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
200
|
-
SEQUEL_QUERY_PER_ASSOCIATION_DB_3_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
201
|
-
SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
|
202
|
-
SEQUEL_SYNCHRONIZE_SQL :: Use the synchronize_sql extension when running the specs
|
203
|
-
SEQUEL_TRANSACTION_CONNECTION_VALIDATOR :: Use the transaction_connection_validator extension when running the adapter/integration specs
|
204
|
-
SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
|
data/doc/thread_safety.rdoc
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
= Thread Safety
|
2
|
-
|
3
|
-
Most Sequel usage (and all common Sequel usage) is thread safe by default. Specifically, multiple threads can operate on Database instances, Dataset instances, and Model classes concurrently without problems. In general, Database instance and Model classes are not modified after application startup, and Dataset instances are always frozen.
|
4
|
-
|
5
|
-
== Connection Pool
|
6
|
-
|
7
|
-
In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block until a connection is available or the connection pool timeout has elapsed (in which case a Sequel::PoolTimeout error will be raised).
|
8
|
-
|
9
|
-
== Exceptions
|
10
|
-
|
11
|
-
This is a small list of things that are specifically non thread-safe. This is not an exhaustive list, there may be cases not mentioned here.
|
12
|
-
|
13
|
-
1) Model instances: Model instances are not thread-safe unless they are frozen first. Multiple threads should not operate on an unfrozen model instance concurrently.
|
14
|
-
|
15
|
-
2) Model class modifications: Model class modifications, such as adding associations and loading plugins, are not designed to be thread safe. You should not modify a class in one thread if any other thread can concurrently access it. Model subclassing is designed to be thread-safe, so you create a model subclass in a thread and modify it safely.
|
data/doc/transactions.rdoc
DELETED
@@ -1,250 +0,0 @@
|
|
1
|
-
= Database Transactions
|
2
|
-
|
3
|
-
Sequel uses autocommit mode by default for all of its database adapters, so in general in Sequel if you want to use database transactions, you need to be explicit about it. There are a few cases where transactions are used implicitly by default:
|
4
|
-
|
5
|
-
* Dataset#import to insert many records at once
|
6
|
-
* Dataset#paged_each to iterate over large datasets in batches
|
7
|
-
* Model#save
|
8
|
-
* Model#destroy
|
9
|
-
* Migrations if the database supports transactional schema
|
10
|
-
* Database#use_cursor in the postgres adapter
|
11
|
-
* Dataset#lock on PostgreSQL if given a block
|
12
|
-
* setter methods created by the association_pks plugin
|
13
|
-
* move* methods in the list plugin
|
14
|
-
|
15
|
-
Everywhere else, it is up to you to use a database transaction if you want to.
|
16
|
-
|
17
|
-
== Basic Transaction Usage
|
18
|
-
|
19
|
-
In Sequel, the <tt>Database#transaction</tt> method should be called if you want to use a database transaction. This method must be called with a block. If the block does not raise an exception, the transaction is committed:
|
20
|
-
|
21
|
-
DB.transaction do # BEGIN
|
22
|
-
DB[:foo].insert(1) # INSERT
|
23
|
-
end # COMMIT
|
24
|
-
|
25
|
-
If the block raises a Sequel::Rollback exception, the transaction is rolled back, but no exception is raised outside the block:
|
26
|
-
|
27
|
-
DB.transaction do # BEGIN
|
28
|
-
raise Sequel::Rollback
|
29
|
-
end # ROLLBACK
|
30
|
-
# no exception raised
|
31
|
-
|
32
|
-
If any other exception is raised, the transaction is rolled back, and the exception is raised outside the block:
|
33
|
-
|
34
|
-
DB.transaction do # BEGIN
|
35
|
-
raise ArgumentError
|
36
|
-
end # ROLLBACK
|
37
|
-
# ArgumentError raised
|
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
|
-
|
45
|
-
If you want Sequel::Rollback exceptions to be reraised, use the <tt>rollback: :reraise</tt> option:
|
46
|
-
|
47
|
-
DB.transaction(rollback: :reraise) do # BEGIN
|
48
|
-
raise Sequel::Rollback
|
49
|
-
end # ROLLBACK
|
50
|
-
# Sequel::Rollback raised
|
51
|
-
|
52
|
-
If you always want to rollback (useful for testing), use the <tt>rollback: :always</tt> option:
|
53
|
-
|
54
|
-
DB.transaction(rollback: :always) do # BEGIN
|
55
|
-
DB[:foo].insert(1) # INSERT
|
56
|
-
end # ROLLBACK
|
57
|
-
# no exception raised
|
58
|
-
|
59
|
-
If you want to check whether you are currently in a transaction, use the Database#in_transaction? method:
|
60
|
-
|
61
|
-
DB.in_transaction? # false
|
62
|
-
DB.transaction do
|
63
|
-
DB.in_transaction? # true
|
64
|
-
end
|
65
|
-
|
66
|
-
== Transaction Hooks
|
67
|
-
|
68
|
-
You can add hooks to an in progress transaction that are called after the transaction commits or rolls back:
|
69
|
-
|
70
|
-
x = nil
|
71
|
-
DB.transaction do
|
72
|
-
DB.after_commit{x = 1}
|
73
|
-
DB.after_rollback{x = 2}
|
74
|
-
x # nil
|
75
|
-
end
|
76
|
-
x # 1
|
77
|
-
|
78
|
-
x = nil
|
79
|
-
DB.transaction do
|
80
|
-
DB.after_commit{x = 1}
|
81
|
-
DB.after_rollback{x = 2}
|
82
|
-
raise Sequel::Rollback
|
83
|
-
end
|
84
|
-
x # 2
|
85
|
-
|
86
|
-
== Nested Transaction Calls / Savepoints
|
87
|
-
|
88
|
-
You can nest calls to transaction, which by default just reuses the existing transaction:
|
89
|
-
|
90
|
-
DB.transaction do # BEGIN
|
91
|
-
DB.transaction do
|
92
|
-
DB[:foo].insert(1) # INSERT
|
93
|
-
end
|
94
|
-
end # COMMIT
|
95
|
-
|
96
|
-
You can use the <tt>savepoint: true</tt> option in the inner transaction to explicitly use a savepoint (if the database supports it):
|
97
|
-
|
98
|
-
DB.transaction do # BEGIN
|
99
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
100
|
-
DB[:foo].insert(1) # INSERT
|
101
|
-
end # RELEASE SAVEPOINT
|
102
|
-
end # COMMIT
|
103
|
-
|
104
|
-
You can use the <tt>auto_savepoint: true</tt> option in the outer transaction to explicitly use a savepoint in the inner transaction (if the database supports it):
|
105
|
-
|
106
|
-
DB.transaction(auto_savepoint: true) do # BEGIN
|
107
|
-
DB.transaction do # SAVEPOINT
|
108
|
-
DB[:foo].insert(1) # INSERT
|
109
|
-
end # RELEASE SAVEPOINT
|
110
|
-
end # COMMIT
|
111
|
-
|
112
|
-
If a Sequel::Rollback exception is raised inside the savepoint block, it will only rollback to the savepoint:
|
113
|
-
|
114
|
-
DB.transaction do # BEGIN
|
115
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
116
|
-
raise Sequel::Rollback
|
117
|
-
end # ROLLBACK TO SAVEPOINT
|
118
|
-
# no exception raised
|
119
|
-
end # COMMIT
|
120
|
-
|
121
|
-
Other exceptions, unless rescued inside the outer transaction block, will rollback the savepoint and the outer transactions, since they are reraised by the transaction code:
|
122
|
-
|
123
|
-
DB.transaction do # BEGIN
|
124
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
125
|
-
raise ArgumentError
|
126
|
-
end # ROLLBACK TO SAVEPOINT
|
127
|
-
end # ROLLBACK
|
128
|
-
# ArgumentError raised
|
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 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>
|
147
|
-
|
148
|
-
DB.transaction do # BEGIN
|
149
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
150
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
151
|
-
DB.rollback_on_exit(savepoint: 2)
|
152
|
-
end # ROLLBACK TO SAVEPOINT
|
153
|
-
end # ROLLBACK TO SAVEPOINT
|
154
|
-
end # COMMIT
|
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: 3)
|
160
|
-
end # ROLLBACK TO SAVEPOINT
|
161
|
-
end # ROLLBACK TO SAVEPOINT
|
162
|
-
end # ROLLBACK
|
163
|
-
|
164
|
-
=== Savepoint Hooks
|
165
|
-
|
166
|
-
When using savepoints, you can use the +:savepoint+ option to +after_commit+ or +after_rollback+ to use a savepoint hook. For +after_commit+, this will only run the hook after transaction commit if all enclosing savepoints are released (not rolled back). For +after_rollback+, this will run the hook after any enclosing savepoint is rolled back (before transaction commit), or after the transaction is rolled back if all enclosing savepoints are released:
|
167
|
-
|
168
|
-
x = nil
|
169
|
-
DB.transaction do # BEGIN
|
170
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
171
|
-
DB.after_commit(savepoint: true){x = 1}
|
172
|
-
DB.after_rollback(savepoint: true){x = 2}
|
173
|
-
x # nil
|
174
|
-
end # RELEASE SAVEPOINT
|
175
|
-
x # nil
|
176
|
-
end # COMMIT
|
177
|
-
x # 1
|
178
|
-
|
179
|
-
x = nil
|
180
|
-
DB.transaction do # BEGIN
|
181
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
182
|
-
DB.after_commit(savepoint: true){x = 1}
|
183
|
-
DB.after_rollback(savepoint: true){x = 2}
|
184
|
-
x # nil
|
185
|
-
raise Sequel::Rollback
|
186
|
-
end # ROLLBACK TO SAVEPOINT
|
187
|
-
x # 2
|
188
|
-
end # COMMIT
|
189
|
-
x # 2
|
190
|
-
|
191
|
-
x = nil
|
192
|
-
DB.transaction do # BEGIN
|
193
|
-
DB.transaction(savepoint: true) do # SAVEPOINT
|
194
|
-
DB.after_commit(savepoint: true){x = 1}
|
195
|
-
DB.after_rollback(savepoint: true){x = 2}
|
196
|
-
end # RELEASE SAVEPOINT
|
197
|
-
x # nil
|
198
|
-
raise Sequel::Rollback
|
199
|
-
end
|
200
|
-
x # 2
|
201
|
-
|
202
|
-
== Prepared Transactions / Two-Phase Commit
|
203
|
-
|
204
|
-
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.
|
205
|
-
|
206
|
-
To use prepared transactions in Sequel, you provide a string as the value of the :prepare option:
|
207
|
-
|
208
|
-
DB.transaction(prepare: 'foo') do # BEGIN
|
209
|
-
DB[:foo].insert(1) # INSERT
|
210
|
-
end # PREPARE TRANSACTION 'foo'
|
211
|
-
|
212
|
-
Later, you can commit the prepared transaction:
|
213
|
-
|
214
|
-
DB.commit_prepared_transaction('foo')
|
215
|
-
|
216
|
-
or roll the prepared transaction back:
|
217
|
-
|
218
|
-
DB.rollback_prepared_transaction('foo')
|
219
|
-
|
220
|
-
== Transaction Isolation Levels
|
221
|
-
|
222
|
-
The SQL standard supports 4 isolation levels: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE. Not all databases implement the levels as specified in the standard (or implement the levels at all), but on most databases, you can specify which transaction isolation level you want to use via the :isolation option to <tt>Database#transaction</tt>. The isolation level is specified as one of the following symbols: :uncommitted, :committed, :repeatable, and :serializable. Using this option makes Sequel use the correct transaction isolation syntax for your database:
|
223
|
-
|
224
|
-
DB.transaction(isolation: :serializable) do # BEGIN
|
225
|
-
# SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
|
226
|
-
DB[:foo].insert(1) # INSERT
|
227
|
-
end # COMMIT
|
228
|
-
|
229
|
-
== Automatically Restarting Transactions
|
230
|
-
|
231
|
-
Sequel offers the ability to automatically restart transactions if specific types of errors are detected. For example, if you want to automatically restart a transaction if a serialization failure is detected:
|
232
|
-
|
233
|
-
DB.transaction(isolation: :serializable, retry_on: [Sequel::SerializationFailure]) do
|
234
|
-
ModelClass.find_or_create(name: 'Foo')
|
235
|
-
end
|
236
|
-
|
237
|
-
At the serializable transaction isolation level, find_or_create may raises a Sequel::SerializationFailure exception if multiple threads simultaneously run that code. With the :retry_on option set, the transaction will be automatically retried until it succeeds.
|
238
|
-
|
239
|
-
Note that automatic retrying should not be used unless the entire transaction
|
240
|
-
block is idempotent, as otherwise it can cause non-idempotent
|
241
|
-
behavior to execute multiple times. For example, with the following code:
|
242
|
-
|
243
|
-
DB.transaction(isolation: :serializable, retry_on: [Sequel::SerializationFailure]) do
|
244
|
-
logger.info 'Ensuring existence of ModelClass with name Foo'
|
245
|
-
ModelClass.find_or_create(name: 'Foo')
|
246
|
-
end
|
247
|
-
|
248
|
-
The logger.info method will be called multiple times if there is a serialization failure.
|
249
|
-
|
250
|
-
The :num_retries option can be used to set the maximum number of times to retry. It is set to 5 times by default.
|