sequel 5.82.0 → 5.84.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +9 -17
  3. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  4. data/lib/sequel/adapters/shared/db2.rb +1 -1
  5. data/lib/sequel/adapters/shared/mssql.rb +14 -2
  6. data/lib/sequel/adapters/shared/postgres.rb +42 -4
  7. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  8. data/lib/sequel/database/connecting.rb +1 -4
  9. data/lib/sequel/database/misc.rb +27 -7
  10. data/lib/sequel/database/schema_methods.rb +17 -1
  11. data/lib/sequel/dataset/sql.rb +13 -0
  12. data/lib/sequel/extensions/pg_json_ops.rb +328 -1
  13. data/lib/sequel/extensions/stdio_logger.rb +48 -0
  14. data/lib/sequel/extensions/string_agg.rb +15 -2
  15. data/lib/sequel/plugins/defaults_setter.rb +16 -4
  16. data/lib/sequel/plugins/optimistic_locking.rb +2 -0
  17. data/lib/sequel/sql.rb +8 -5
  18. data/lib/sequel/version.rb +1 -1
  19. metadata +4 -235
  20. data/CHANGELOG +0 -1377
  21. data/README.rdoc +0 -936
  22. data/doc/advanced_associations.rdoc +0 -884
  23. data/doc/association_basics.rdoc +0 -1859
  24. data/doc/bin_sequel.rdoc +0 -146
  25. data/doc/cheat_sheet.rdoc +0 -255
  26. data/doc/code_order.rdoc +0 -104
  27. data/doc/core_extensions.rdoc +0 -405
  28. data/doc/dataset_basics.rdoc +0 -96
  29. data/doc/dataset_filtering.rdoc +0 -222
  30. data/doc/extensions.rdoc +0 -77
  31. data/doc/fork_safety.rdoc +0 -84
  32. data/doc/mass_assignment.rdoc +0 -98
  33. data/doc/migration.rdoc +0 -660
  34. data/doc/model_dataset_method_design.rdoc +0 -129
  35. data/doc/model_hooks.rdoc +0 -254
  36. data/doc/model_plugins.rdoc +0 -270
  37. data/doc/mssql_stored_procedures.rdoc +0 -43
  38. data/doc/object_model.rdoc +0 -563
  39. data/doc/opening_databases.rdoc +0 -439
  40. data/doc/postgresql.rdoc +0 -611
  41. data/doc/prepared_statements.rdoc +0 -144
  42. data/doc/querying.rdoc +0 -1070
  43. data/doc/reflection.rdoc +0 -120
  44. data/doc/release_notes/5.0.0.txt +0 -159
  45. data/doc/release_notes/5.1.0.txt +0 -31
  46. data/doc/release_notes/5.10.0.txt +0 -84
  47. data/doc/release_notes/5.11.0.txt +0 -83
  48. data/doc/release_notes/5.12.0.txt +0 -141
  49. data/doc/release_notes/5.13.0.txt +0 -27
  50. data/doc/release_notes/5.14.0.txt +0 -63
  51. data/doc/release_notes/5.15.0.txt +0 -39
  52. data/doc/release_notes/5.16.0.txt +0 -110
  53. data/doc/release_notes/5.17.0.txt +0 -31
  54. data/doc/release_notes/5.18.0.txt +0 -69
  55. data/doc/release_notes/5.19.0.txt +0 -28
  56. data/doc/release_notes/5.2.0.txt +0 -33
  57. data/doc/release_notes/5.20.0.txt +0 -89
  58. data/doc/release_notes/5.21.0.txt +0 -87
  59. data/doc/release_notes/5.22.0.txt +0 -48
  60. data/doc/release_notes/5.23.0.txt +0 -56
  61. data/doc/release_notes/5.24.0.txt +0 -56
  62. data/doc/release_notes/5.25.0.txt +0 -32
  63. data/doc/release_notes/5.26.0.txt +0 -35
  64. data/doc/release_notes/5.27.0.txt +0 -21
  65. data/doc/release_notes/5.28.0.txt +0 -16
  66. data/doc/release_notes/5.29.0.txt +0 -22
  67. data/doc/release_notes/5.3.0.txt +0 -121
  68. data/doc/release_notes/5.30.0.txt +0 -20
  69. data/doc/release_notes/5.31.0.txt +0 -148
  70. data/doc/release_notes/5.32.0.txt +0 -46
  71. data/doc/release_notes/5.33.0.txt +0 -24
  72. data/doc/release_notes/5.34.0.txt +0 -40
  73. data/doc/release_notes/5.35.0.txt +0 -56
  74. data/doc/release_notes/5.36.0.txt +0 -60
  75. data/doc/release_notes/5.37.0.txt +0 -30
  76. data/doc/release_notes/5.38.0.txt +0 -28
  77. data/doc/release_notes/5.39.0.txt +0 -19
  78. data/doc/release_notes/5.4.0.txt +0 -80
  79. data/doc/release_notes/5.40.0.txt +0 -40
  80. data/doc/release_notes/5.41.0.txt +0 -25
  81. data/doc/release_notes/5.42.0.txt +0 -136
  82. data/doc/release_notes/5.43.0.txt +0 -98
  83. data/doc/release_notes/5.44.0.txt +0 -32
  84. data/doc/release_notes/5.45.0.txt +0 -34
  85. data/doc/release_notes/5.46.0.txt +0 -87
  86. data/doc/release_notes/5.47.0.txt +0 -59
  87. data/doc/release_notes/5.48.0.txt +0 -14
  88. data/doc/release_notes/5.49.0.txt +0 -59
  89. data/doc/release_notes/5.5.0.txt +0 -61
  90. data/doc/release_notes/5.50.0.txt +0 -78
  91. data/doc/release_notes/5.51.0.txt +0 -47
  92. data/doc/release_notes/5.52.0.txt +0 -87
  93. data/doc/release_notes/5.53.0.txt +0 -23
  94. data/doc/release_notes/5.54.0.txt +0 -27
  95. data/doc/release_notes/5.55.0.txt +0 -21
  96. data/doc/release_notes/5.56.0.txt +0 -51
  97. data/doc/release_notes/5.57.0.txt +0 -23
  98. data/doc/release_notes/5.58.0.txt +0 -31
  99. data/doc/release_notes/5.59.0.txt +0 -73
  100. data/doc/release_notes/5.6.0.txt +0 -31
  101. data/doc/release_notes/5.60.0.txt +0 -22
  102. data/doc/release_notes/5.61.0.txt +0 -43
  103. data/doc/release_notes/5.62.0.txt +0 -132
  104. data/doc/release_notes/5.63.0.txt +0 -33
  105. data/doc/release_notes/5.64.0.txt +0 -50
  106. data/doc/release_notes/5.65.0.txt +0 -21
  107. data/doc/release_notes/5.66.0.txt +0 -24
  108. data/doc/release_notes/5.67.0.txt +0 -32
  109. data/doc/release_notes/5.68.0.txt +0 -61
  110. data/doc/release_notes/5.69.0.txt +0 -26
  111. data/doc/release_notes/5.7.0.txt +0 -108
  112. data/doc/release_notes/5.70.0.txt +0 -35
  113. data/doc/release_notes/5.71.0.txt +0 -21
  114. data/doc/release_notes/5.72.0.txt +0 -33
  115. data/doc/release_notes/5.73.0.txt +0 -66
  116. data/doc/release_notes/5.74.0.txt +0 -45
  117. data/doc/release_notes/5.75.0.txt +0 -35
  118. data/doc/release_notes/5.76.0.txt +0 -86
  119. data/doc/release_notes/5.77.0.txt +0 -63
  120. data/doc/release_notes/5.78.0.txt +0 -67
  121. data/doc/release_notes/5.79.0.txt +0 -28
  122. data/doc/release_notes/5.8.0.txt +0 -170
  123. data/doc/release_notes/5.80.0.txt +0 -40
  124. data/doc/release_notes/5.81.0.txt +0 -31
  125. data/doc/release_notes/5.82.0.txt +0 -61
  126. data/doc/release_notes/5.9.0.txt +0 -99
  127. data/doc/schema_modification.rdoc +0 -679
  128. data/doc/security.rdoc +0 -443
  129. data/doc/sharding.rdoc +0 -286
  130. data/doc/sql.rdoc +0 -648
  131. data/doc/testing.rdoc +0 -204
  132. data/doc/thread_safety.rdoc +0 -15
  133. data/doc/transactions.rdoc +0 -250
  134. data/doc/validations.rdoc +0 -558
  135. 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')
@@ -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.
@@ -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.