sequel 3.28.0 → 3.29.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.
- data/CHANGELOG +119 -3
- data/Rakefile +5 -3
- data/bin/sequel +1 -5
- data/doc/model_hooks.rdoc +9 -1
- data/doc/opening_databases.rdoc +49 -40
- data/doc/prepared_statements.rdoc +27 -6
- data/doc/release_notes/3.28.0.txt +2 -2
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +18 -9
- data/doc/transactions.rdoc +41 -1
- data/lib/sequel/adapters/ado.rb +28 -17
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/amalgalite.rb +11 -7
- data/lib/sequel/adapters/db2.rb +122 -70
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do.rb +5 -36
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +3 -6
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/informix.rb +2 -4
- data/lib/sequel/adapters/jdbc.rb +47 -11
- data/lib/sequel/adapters/jdbc/as400.rb +5 -24
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +10 -12
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +64 -51
- data/lib/sequel/adapters/mysql2.rb +15 -9
- data/lib/sequel/adapters/odbc.rb +13 -6
- data/lib/sequel/adapters/odbc/db2.rb +0 -4
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +333 -51
- data/lib/sequel/adapters/postgres.rb +80 -27
- data/lib/sequel/adapters/shared/access.rb +0 -6
- data/lib/sequel/adapters/shared/db2.rb +13 -15
- data/lib/sequel/adapters/shared/firebird.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +23 -18
- data/lib/sequel/adapters/shared/mysql.rb +6 -6
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -30
- data/lib/sequel/adapters/shared/postgres.rb +35 -18
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +116 -37
- data/lib/sequel/adapters/sqlite.rb +16 -8
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/tinytds.rb +13 -10
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
- data/lib/sequel/core.rb +40 -0
- data/lib/sequel/database/connecting.rb +1 -2
- data/lib/sequel/database/dataset.rb +3 -3
- data/lib/sequel/database/dataset_defaults.rb +58 -0
- data/lib/sequel/database/misc.rb +62 -2
- data/lib/sequel/database/query.rb +113 -49
- data/lib/sequel/database/schema_methods.rb +7 -2
- data/lib/sequel/dataset/actions.rb +37 -19
- data/lib/sequel/dataset/features.rb +24 -0
- data/lib/sequel/dataset/graph.rb +7 -6
- data/lib/sequel/dataset/misc.rb +11 -3
- data/lib/sequel/dataset/mutation.rb +2 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +46 -15
- data/lib/sequel/dataset/sql.rb +28 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +115 -33
- data/lib/sequel/model/base.rb +91 -31
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +6 -10
- data/lib/sequel/plugins/prepared_statements.rb +12 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/sharding.rb +0 -5
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/timezones.rb +42 -42
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +29 -29
- data/spec/adapters/mysql_spec.rb +86 -104
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +98 -33
- data/spec/adapters/spec_helper.rb +0 -5
- data/spec/adapters/sqlite_spec.rb +24 -21
- data/spec/core/connection_pool_spec.rb +9 -15
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +491 -227
- data/spec/core/dataset_spec.rb +638 -1051
- data/spec/core/expression_filters_spec.rb +0 -1
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +51 -114
- data/spec/core/spec_helper.rb +3 -90
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +3 -0
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/spec_helper.rb +4 -0
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/integration/associations_test.rb +123 -12
- data/spec/integration/dataset_test.rb +140 -47
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +179 -128
- data/spec/integration/prepared_statement_test.rb +92 -91
- data/spec/integration/schema_test.rb +42 -23
- data/spec/integration/spec_helper.rb +25 -31
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +161 -34
- data/spec/integration/type_test.rb +3 -3
- data/spec/model/association_reflection_spec.rb +83 -7
- data/spec/model/associations_spec.rb +393 -676
- data/spec/model/base_spec.rb +186 -116
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +343 -867
- data/spec/model/hooks_spec.rb +160 -79
- data/spec/model/model_spec.rb +118 -165
- data/spec/model/plugins_spec.rb +7 -13
- data/spec/model/record_spec.rb +138 -207
- data/spec/model/spec_helper.rb +10 -73
- metadata +14 -8
|
@@ -9,11 +9,6 @@ module Sequel
|
|
|
9
9
|
module DatabaseMethods
|
|
10
10
|
include Sequel::MySQL::DatabaseMethods
|
|
11
11
|
|
|
12
|
-
# Return instance of Sequel::Swift::MySQL::Dataset with the given opts.
|
|
13
|
-
def dataset(opts=nil)
|
|
14
|
-
Sequel::Swift::MySQL::Dataset.new(self, opts)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
private
|
|
18
13
|
|
|
19
14
|
# The database name for the given database.
|
|
@@ -41,11 +41,6 @@ module Sequel
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# Return instance of Sequel::Swift::Postgres::Dataset with the given opts.
|
|
45
|
-
def dataset(opts=nil)
|
|
46
|
-
Sequel::Swift::Postgres::Dataset.new(self, opts)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
44
|
# Run the SELECT SQL on the database and yield the rows
|
|
50
45
|
def execute(sql, opts={})
|
|
51
46
|
synchronize(opts[:server]) do |conn|
|
|
@@ -8,10 +8,12 @@ module Sequel
|
|
|
8
8
|
# Database instance methods for SQLite databases accessed via Swift.
|
|
9
9
|
module DatabaseMethods
|
|
10
10
|
include Sequel::SQLite::DatabaseMethods
|
|
11
|
-
|
|
12
|
-
#
|
|
13
|
-
def
|
|
14
|
-
|
|
11
|
+
|
|
12
|
+
# Set the correct pragmas on the connection.
|
|
13
|
+
def connect(opts)
|
|
14
|
+
c = super
|
|
15
|
+
connection_pragmas.each{|s| log_yield(s){c.execute(s)}}
|
|
16
|
+
c
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
@@ -16,11 +16,6 @@ module Sequel
|
|
|
16
16
|
TinyTds::Client.new(opts)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# Return instance of Sequel::TinyTDS::Dataset with the given options.
|
|
20
|
-
def dataset(opts = nil)
|
|
21
|
-
TinyTDS::Dataset.new(self, opts)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
19
|
# Execute the given +sql+ on the server. If the :return option
|
|
25
20
|
# is present, its value should be a method symbol that is called
|
|
26
21
|
# on the TinyTds::Result object returned from executing the
|
|
@@ -150,6 +145,8 @@ module Sequel
|
|
|
150
145
|
|
|
151
146
|
class Dataset < Sequel::Dataset
|
|
152
147
|
include Sequel::MSSQL::DatasetMethods
|
|
148
|
+
|
|
149
|
+
Database::DatasetClass = self
|
|
153
150
|
|
|
154
151
|
# SQLite already supports named bind arguments, so use directly.
|
|
155
152
|
module ArgumentMapper
|
|
@@ -208,22 +205,28 @@ module Sequel
|
|
|
208
205
|
def fetch_rows(sql)
|
|
209
206
|
execute(sql) do |result|
|
|
210
207
|
each_opts = {:cache_rows=>false}
|
|
211
|
-
each_opts[:timezone] = :utc if
|
|
212
|
-
|
|
213
|
-
|
|
208
|
+
each_opts[:timezone] = :utc if db.timezone == :utc
|
|
209
|
+
rn = row_number_column if @opts[:offset]
|
|
210
|
+
columns = cols = result.fields.map{|c| output_identifier(c)}
|
|
211
|
+
if opts[:offset]
|
|
212
|
+
rn = row_number_column
|
|
213
|
+
columns = columns.dup
|
|
214
|
+
columns.delete(rn)
|
|
215
|
+
end
|
|
216
|
+
@columns = columns
|
|
214
217
|
if identifier_output_method
|
|
215
218
|
each_opts[:as] = :array
|
|
216
219
|
result.each(each_opts) do |r|
|
|
217
220
|
h = {}
|
|
218
221
|
cols.zip(r).each{|k, v| h[k] = v}
|
|
219
|
-
h.delete(
|
|
222
|
+
h.delete(rn) if rn
|
|
220
223
|
yield h
|
|
221
224
|
end
|
|
222
225
|
else
|
|
223
226
|
each_opts[:symbolize_keys] = true
|
|
224
227
|
if offset
|
|
225
228
|
result.each(each_opts) do |r|
|
|
226
|
-
r.delete(
|
|
229
|
+
r.delete(rn) if rn
|
|
227
230
|
yield r
|
|
228
231
|
end
|
|
229
232
|
else
|
|
@@ -51,5 +51,13 @@ module Sequel
|
|
|
51
51
|
limit(@opts[:limit]).
|
|
52
52
|
where(SQL::Identifier.new(rn) > o))
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# This emulation adds an extra row number column that should be
|
|
58
|
+
# eliminated.
|
|
59
|
+
def offset_returns_row_number_column?
|
|
60
|
+
true
|
|
61
|
+
end
|
|
54
62
|
end
|
|
55
63
|
end
|
data/lib/sequel/core.rb
CHANGED
|
@@ -240,6 +240,46 @@ module Sequel
|
|
|
240
240
|
end
|
|
241
241
|
end
|
|
242
242
|
|
|
243
|
+
# Uses a transaction on all given databases with the given options. This:
|
|
244
|
+
#
|
|
245
|
+
# Sequel.transaction([DB1, DB2, DB3]){...}
|
|
246
|
+
#
|
|
247
|
+
# is equivalent to:
|
|
248
|
+
#
|
|
249
|
+
# DB1.transaction do
|
|
250
|
+
# DB2.transaction do
|
|
251
|
+
# DB3.transaction do
|
|
252
|
+
# ...
|
|
253
|
+
# end
|
|
254
|
+
# end
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# except that if Sequel::Rollback is raised by the block, the transaction is
|
|
258
|
+
# rolled back on all databases instead of just the last one.
|
|
259
|
+
#
|
|
260
|
+
# Note that this method cannot guarantee that all databases will commit or
|
|
261
|
+
# rollback. For example, if DB3 commits but attempting to commit on DB2
|
|
262
|
+
# fails (maybe because foreign key checks are deferred), there is no way
|
|
263
|
+
# to uncommit the changes on DB3. For that kind of support, you need to
|
|
264
|
+
# have two-phase commit/prepared transactions (which Sequel supports on
|
|
265
|
+
# some databases).
|
|
266
|
+
def self.transaction(dbs, opts={}, &block)
|
|
267
|
+
unless opts[:rollback]
|
|
268
|
+
rescue_rollback = true
|
|
269
|
+
opts = opts.merge(:rollback=>:reraise)
|
|
270
|
+
end
|
|
271
|
+
pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
|
|
272
|
+
if rescue_rollback
|
|
273
|
+
begin
|
|
274
|
+
pr.call
|
|
275
|
+
rescue Sequel::Rollback => e
|
|
276
|
+
nil
|
|
277
|
+
end
|
|
278
|
+
else
|
|
279
|
+
pr.call
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
243
283
|
# Same as Sequel.require, but wrapped in a mutex in order to be thread safe.
|
|
244
284
|
def self.ts_require(*args)
|
|
245
285
|
check_requiring_thread{require(*args)}
|
|
@@ -6,7 +6,7 @@ module Sequel
|
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
8
|
# Array of supported database adapters
|
|
9
|
-
ADAPTERS = %w'ado amalgalite db2 dbi do firebird ibmdb informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
9
|
+
ADAPTERS = %w'ado amalgalite db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
10
10
|
|
|
11
11
|
# Whether to use the single threaded connection pool by default
|
|
12
12
|
@@single_threaded = false
|
|
@@ -218,7 +218,6 @@ module Sequel
|
|
|
218
218
|
# If a server option is given, acquires a connection for that specific
|
|
219
219
|
# server, instead of the :default server.
|
|
220
220
|
#
|
|
221
|
-
#
|
|
222
221
|
# DB.synchronize do |conn|
|
|
223
222
|
# ...
|
|
224
223
|
# end
|
|
@@ -24,10 +24,10 @@ module Sequel
|
|
|
24
24
|
#
|
|
25
25
|
# DB.dataset # SELECT *
|
|
26
26
|
# DB.dataset.from(:items) # SELECT * FROM items
|
|
27
|
-
def dataset
|
|
28
|
-
|
|
27
|
+
def dataset(opts=nil)
|
|
28
|
+
@dataset_class.new(self, opts)
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Fetches records for an arbitrary SQL statement. If a block is given,
|
|
32
32
|
# it is used to iterate over the records:
|
|
33
33
|
#
|
|
@@ -5,6 +5,9 @@ module Sequel
|
|
|
5
5
|
# This methods change the default behavior of this database's datasets.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
|
+
# The default class to use for datasets
|
|
9
|
+
DatasetClass = Sequel::Dataset
|
|
10
|
+
|
|
8
11
|
# The identifier input method to use by default
|
|
9
12
|
@@identifier_input_method = nil
|
|
10
13
|
|
|
@@ -42,9 +45,59 @@ module Sequel
|
|
|
42
45
|
@@quote_identifiers = value
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
# The class to use for creating datasets. Should respond to
|
|
49
|
+
# new with the Database argument as the first argument, and
|
|
50
|
+
# an optional options hash.
|
|
51
|
+
attr_reader :dataset_class
|
|
52
|
+
|
|
45
53
|
# The default schema to use, generally should be nil.
|
|
46
54
|
attr_accessor :default_schema
|
|
47
55
|
|
|
56
|
+
# If the database has any dataset modules associated with it,
|
|
57
|
+
# use a subclass of the given class that includes the modules
|
|
58
|
+
# as the dataset class.
|
|
59
|
+
def dataset_class=(c)
|
|
60
|
+
unless @dataset_modules.empty?
|
|
61
|
+
c = Class.new(c)
|
|
62
|
+
@dataset_modules.each{|m| c.send(:include, m)}
|
|
63
|
+
end
|
|
64
|
+
@dataset_class = c
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Equivalent to extending all datasets produced by the database with a
|
|
68
|
+
# module. What it actually does is use a subclass of the current dataset_class
|
|
69
|
+
# as the new dataset_class, and include the module in the subclass.
|
|
70
|
+
# Instead of a module, you can provide a block that is used to create an
|
|
71
|
+
# anonymous module.
|
|
72
|
+
#
|
|
73
|
+
# This allows you to override any of the dataset methods even if they are
|
|
74
|
+
# defined directly on the dataset class that this Database object uses.
|
|
75
|
+
#
|
|
76
|
+
# Examples:
|
|
77
|
+
#
|
|
78
|
+
# # Introspec columns for all of DB's datasets
|
|
79
|
+
# DB.extend_datasets(Sequel::ColumnsIntrospection)
|
|
80
|
+
#
|
|
81
|
+
# # Trace all SELECT queries by printing the SQL and the full backtrace
|
|
82
|
+
# DB.extend_datasets do
|
|
83
|
+
# def fetch_rows(sql)
|
|
84
|
+
# puts sql
|
|
85
|
+
# puts caller
|
|
86
|
+
# super
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
def extend_datasets(mod=nil, &block)
|
|
90
|
+
raise(Error, "must provide either mod or block, not both") if mod && block
|
|
91
|
+
mod = Module.new(&block) if block
|
|
92
|
+
if @dataset_modules.empty?
|
|
93
|
+
@dataset_modules = [mod]
|
|
94
|
+
@dataset_class = Class.new(@dataset_class)
|
|
95
|
+
else
|
|
96
|
+
@dataset_modules << mod
|
|
97
|
+
end
|
|
98
|
+
@dataset_class.send(:include, mod)
|
|
99
|
+
end
|
|
100
|
+
|
|
48
101
|
# The method to call on identifiers going into the database
|
|
49
102
|
def identifier_input_method
|
|
50
103
|
case @identifier_input_method
|
|
@@ -109,6 +162,11 @@ module Sequel
|
|
|
109
162
|
|
|
110
163
|
private
|
|
111
164
|
|
|
165
|
+
# The default dataset class to use for the database
|
|
166
|
+
def dataset_class_default
|
|
167
|
+
self.class.const_get(:DatasetClass)
|
|
168
|
+
end
|
|
169
|
+
|
|
112
170
|
# The default value for default_schema.
|
|
113
171
|
def default_schema_default
|
|
114
172
|
nil
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -19,6 +19,9 @@ module Sequel
|
|
|
19
19
|
# The options hash for this database
|
|
20
20
|
attr_reader :opts
|
|
21
21
|
|
|
22
|
+
# Set the timezone to use for this database, overridding <tt>Sequel.database_timezone</tt>.
|
|
23
|
+
attr_writer :timezone
|
|
24
|
+
|
|
22
25
|
# Constructs a new instance of a database connection with the specified
|
|
23
26
|
# options hash.
|
|
24
27
|
#
|
|
@@ -50,15 +53,46 @@ module Sequel
|
|
|
50
53
|
@schemas = {}
|
|
51
54
|
@default_schema = @opts.fetch(:default_schema, default_schema_default)
|
|
52
55
|
@prepared_statements = {}
|
|
53
|
-
@transactions =
|
|
56
|
+
@transactions = {}
|
|
54
57
|
@identifier_input_method = nil
|
|
55
58
|
@identifier_output_method = nil
|
|
56
59
|
@quote_identifiers = nil
|
|
60
|
+
@timezone = nil
|
|
61
|
+
@dataset_class = dataset_class_default
|
|
62
|
+
@dataset_modules = []
|
|
57
63
|
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
|
|
58
64
|
@pool = ConnectionPool.get_pool(@opts, &block)
|
|
59
65
|
|
|
60
66
|
::Sequel::DATABASES.push(self)
|
|
61
67
|
end
|
|
68
|
+
|
|
69
|
+
# If a transaction is not currently in process, yield to the block immediately.
|
|
70
|
+
# Otherwise, add the block to the list of blocks to call after the currently
|
|
71
|
+
# in progress transaction commits (and only if it commits).
|
|
72
|
+
def after_commit(opts={}, &block)
|
|
73
|
+
raise Error, "must provide block to after_commit" unless block
|
|
74
|
+
synchronize(opts) do |conn|
|
|
75
|
+
if h = @transactions[conn]
|
|
76
|
+
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
|
77
|
+
(h[:after_commit] ||= []) << block
|
|
78
|
+
else
|
|
79
|
+
yield
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# If a transaction is not currently in progress, ignore the block.
|
|
85
|
+
# Otherwise, add the block to the list of the blocks to call after the currently
|
|
86
|
+
# in progress transaction rolls back (and only if it rolls back).
|
|
87
|
+
def after_rollback(opts={}, &block)
|
|
88
|
+
raise Error, "must provide block to after_rollback" unless block
|
|
89
|
+
synchronize(opts) do |conn|
|
|
90
|
+
if h = @transactions[conn]
|
|
91
|
+
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
|
92
|
+
(h[:after_rollback] ||= []) << block
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
62
96
|
|
|
63
97
|
# Cast the given type to a literal type
|
|
64
98
|
#
|
|
@@ -68,6 +102,20 @@ module Sequel
|
|
|
68
102
|
type_literal(:type=>type)
|
|
69
103
|
end
|
|
70
104
|
|
|
105
|
+
# Convert the given timestamp from the application's timezone,
|
|
106
|
+
# to the databases's timezone or the default database timezone if
|
|
107
|
+
# the database does not have a timezone.
|
|
108
|
+
def from_application_timestamp(v)
|
|
109
|
+
Sequel.convert_output_timestamp(v, timezone)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Return true if already in a transaction given the options,
|
|
113
|
+
# false otherwise. Respects the :server option for selecting
|
|
114
|
+
# a shard.
|
|
115
|
+
def in_transaction?(opts={})
|
|
116
|
+
synchronize(opts[:server]){|conn| !!@transactions[conn]}
|
|
117
|
+
end
|
|
118
|
+
|
|
71
119
|
# Returns a string representation of the database object including the
|
|
72
120
|
# class name and the connection URI (or the opts if the URI
|
|
73
121
|
# cannot be constructed).
|
|
@@ -112,6 +160,18 @@ module Sequel
|
|
|
112
160
|
false
|
|
113
161
|
end
|
|
114
162
|
|
|
163
|
+
# The timezone to use for this database, defaulting to <tt>Sequel.database_timezone</tt>.
|
|
164
|
+
def timezone
|
|
165
|
+
@timezone || Sequel.database_timezone
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Convert the given timestamp to the application's timezone,
|
|
169
|
+
# from the databases's timezone or the default database timezone if
|
|
170
|
+
# the database does not have a timezone.
|
|
171
|
+
def to_application_timestamp(v)
|
|
172
|
+
Sequel.convert_timestamp(v, timezone)
|
|
173
|
+
end
|
|
174
|
+
|
|
115
175
|
# Typecast the value to the given column_type. Calls
|
|
116
176
|
# typecast_value_#{column_type} if the method exists,
|
|
117
177
|
# otherwise returns the value.
|
|
@@ -205,7 +265,7 @@ module Sequel
|
|
|
205
265
|
# Typecast the value to true, false, or nil
|
|
206
266
|
def typecast_value_boolean(value)
|
|
207
267
|
case value
|
|
208
|
-
when false, 0, "0", /\Af(alse)?\z/i
|
|
268
|
+
when false, 0, "0", /\Af(alse)?\z/i, /\Ano?\z/i
|
|
209
269
|
false
|
|
210
270
|
else
|
|
211
271
|
blank_object?(value) ? nil : true
|
|
@@ -37,9 +37,12 @@ module Sequel
|
|
|
37
37
|
attr_accessor :transaction_isolation_level
|
|
38
38
|
|
|
39
39
|
# Runs the supplied SQL statement string on the database server.
|
|
40
|
-
#
|
|
40
|
+
# Returns self so it can be safely chained:
|
|
41
|
+
#
|
|
42
|
+
# DB << "UPADTE albums SET artist_id = NULL" << "DROP TABLE artists"
|
|
41
43
|
def <<(sql)
|
|
42
44
|
run(sql)
|
|
45
|
+
self
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
# Call the prepared statement with the given name with the given hash
|
|
@@ -112,14 +115,15 @@ module Sequel
|
|
|
112
115
|
|
|
113
116
|
# Returns the schema for the given table as an array with all members being arrays of length 2,
|
|
114
117
|
# the first member being the column name, and the second member being a hash of column information.
|
|
118
|
+
# The table argument can also be a dataset, as long as it only has one table.
|
|
115
119
|
# Available options are:
|
|
116
120
|
#
|
|
117
121
|
# :reload :: Ignore any cached results, and get fresh information from the database.
|
|
118
122
|
# :schema :: An explicit schema to use. It may also be implicitly provided
|
|
119
123
|
# via the table name.
|
|
120
124
|
#
|
|
121
|
-
# If schema parsing is supported by the database, the column information should at least contain the
|
|
122
|
-
# following
|
|
125
|
+
# If schema parsing is supported by the database, the column information should hash at least contain the
|
|
126
|
+
# following entries:
|
|
123
127
|
#
|
|
124
128
|
# :allow_null :: Whether NULL is an allowed value for the column.
|
|
125
129
|
# :db_type :: The database type for the column, as a database specific string.
|
|
@@ -152,9 +156,20 @@ module Sequel
|
|
|
152
156
|
def schema(table, opts={})
|
|
153
157
|
raise(Error, 'schema parsing is not implemented on this database') unless respond_to?(:schema_parse_table, true)
|
|
154
158
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
opts = opts.dup
|
|
160
|
+
if table.is_a?(Dataset)
|
|
161
|
+
o = table.opts
|
|
162
|
+
from = o[:from]
|
|
163
|
+
raise(Error, "can only parse the schema for a dataset with a single from table") unless from && from.length == 1 && !o.include?(:join) && !o.include?(:sql)
|
|
164
|
+
tab = table.first_source_table
|
|
165
|
+
sch, table_name = schema_and_table(tab)
|
|
166
|
+
quoted_name = table.literal(tab)
|
|
167
|
+
opts[:dataset] = table
|
|
168
|
+
else
|
|
169
|
+
sch, table_name = schema_and_table(table)
|
|
170
|
+
quoted_name = quote_schema_table(table)
|
|
171
|
+
end
|
|
172
|
+
opts[:schema] = sch if sch && !opts.include?(:schema)
|
|
158
173
|
|
|
159
174
|
@schemas.delete(quoted_name) if opts[:reload]
|
|
160
175
|
return @schemas[quoted_name] if @schemas[quoted_name]
|
|
@@ -199,6 +214,9 @@ module Sequel
|
|
|
199
214
|
# :prepare :: A string to use as the transaction identifier for a
|
|
200
215
|
# prepared transaction (two-phase commit), if the database/adapter
|
|
201
216
|
# supports prepared transactions.
|
|
217
|
+
# :rollback :: Can the set to :reraise to reraise any Sequel::Rollback exceptions
|
|
218
|
+
# raised, or :always to always rollback even if no exceptions occur
|
|
219
|
+
# (useful for testing).
|
|
202
220
|
# :server :: The server to use for the transaction.
|
|
203
221
|
# :savepoint :: Whether to create a new savepoint for this transaction,
|
|
204
222
|
# only respected if the database/adapter supports savepoints. By
|
|
@@ -225,40 +243,65 @@ module Sequel
|
|
|
225
243
|
# not a Sequel::Rollback, the error will be reraised. If no exception occurs
|
|
226
244
|
# inside the block, the transaction is commited.
|
|
227
245
|
def _transaction(conn, opts={})
|
|
246
|
+
rollback = opts[:rollback]
|
|
228
247
|
begin
|
|
229
|
-
add_transaction
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
add_transaction(conn, opts)
|
|
249
|
+
begin_transaction(conn, opts)
|
|
250
|
+
if rollback == :always
|
|
251
|
+
begin
|
|
252
|
+
yield(conn)
|
|
253
|
+
rescue Exception => e1
|
|
254
|
+
raise e1
|
|
255
|
+
ensure
|
|
256
|
+
raise ::Sequel::Rollback unless e1
|
|
257
|
+
end
|
|
258
|
+
else
|
|
259
|
+
yield(conn)
|
|
260
|
+
end
|
|
232
261
|
rescue Exception => e
|
|
233
|
-
rollback_transaction(
|
|
234
|
-
transaction_error(e, :conn=>conn)
|
|
262
|
+
rollback_transaction(conn, opts)
|
|
263
|
+
transaction_error(e, :conn=>conn, :rollback=>rollback)
|
|
235
264
|
ensure
|
|
236
265
|
begin
|
|
237
|
-
commit_or_rollback_transaction(e,
|
|
238
|
-
rescue Exception =>
|
|
239
|
-
raise_error(
|
|
266
|
+
committed = commit_or_rollback_transaction(e, conn, opts)
|
|
267
|
+
rescue Exception => e2
|
|
268
|
+
raise_error(e2, :classes=>database_error_classes, :conn=>conn)
|
|
240
269
|
ensure
|
|
241
|
-
remove_transaction(
|
|
270
|
+
remove_transaction(conn, committed)
|
|
242
271
|
end
|
|
243
272
|
end
|
|
244
273
|
end
|
|
245
274
|
|
|
246
275
|
# Add the current thread to the list of active transactions
|
|
247
|
-
def add_transaction
|
|
248
|
-
th = Thread.current
|
|
276
|
+
def add_transaction(conn, opts)
|
|
249
277
|
if supports_savepoints?
|
|
250
|
-
unless @transactions
|
|
251
|
-
|
|
252
|
-
@transactions
|
|
278
|
+
unless @transactions[conn]
|
|
279
|
+
@transactions[conn] = {:savepoint_level=>0}
|
|
280
|
+
@transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
|
|
253
281
|
end
|
|
254
282
|
else
|
|
255
|
-
@transactions
|
|
283
|
+
@transactions[conn] = {}
|
|
284
|
+
@transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
|
|
256
285
|
end
|
|
257
286
|
end
|
|
258
287
|
|
|
288
|
+
# Call all stored after_commit blocks for the given transaction
|
|
289
|
+
def after_transaction_commit(conn)
|
|
290
|
+
if ary = @transactions[conn][:after_commit]
|
|
291
|
+
ary.each{|b| b.call}
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Call all stored after_rollback blocks for the given transaction
|
|
296
|
+
def after_transaction_rollback(conn)
|
|
297
|
+
if ary = @transactions[conn][:after_rollback]
|
|
298
|
+
ary.each{|b| b.call}
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
259
302
|
# Whether the current thread/connection is already inside a transaction
|
|
260
303
|
def already_in_transaction?(conn, opts)
|
|
261
|
-
@transactions.
|
|
304
|
+
@transactions.has_key?(conn) && (!supports_savepoints? || !opts[:savepoint])
|
|
262
305
|
end
|
|
263
306
|
|
|
264
307
|
# SQL to start a new savepoint
|
|
@@ -275,17 +318,16 @@ module Sequel
|
|
|
275
318
|
# Start a new database transaction or a new savepoint on the given connection.
|
|
276
319
|
def begin_transaction(conn, opts={})
|
|
277
320
|
if supports_savepoints?
|
|
278
|
-
th =
|
|
279
|
-
if (depth = th[:
|
|
321
|
+
th = @transactions[conn]
|
|
322
|
+
if (depth = th[:savepoint_level]) > 0
|
|
280
323
|
log_connection_execute(conn, begin_savepoint_sql(depth))
|
|
281
324
|
else
|
|
282
325
|
begin_new_transaction(conn, opts)
|
|
283
326
|
end
|
|
284
|
-
th[:
|
|
327
|
+
th[:savepoint_level] += 1
|
|
285
328
|
else
|
|
286
329
|
begin_new_transaction(conn, opts)
|
|
287
330
|
end
|
|
288
|
-
conn
|
|
289
331
|
end
|
|
290
332
|
|
|
291
333
|
# SQL to BEGIN a transaction.
|
|
@@ -348,12 +390,16 @@ module Sequel
|
|
|
348
390
|
# Thread.current.status is checked because Thread#kill skips rescue
|
|
349
391
|
# blocks (so exception would be nil), but the transaction should
|
|
350
392
|
# still be rolled back.
|
|
351
|
-
def commit_or_rollback_transaction(exception,
|
|
352
|
-
|
|
393
|
+
def commit_or_rollback_transaction(exception, conn, opts)
|
|
394
|
+
if exception
|
|
395
|
+
false
|
|
396
|
+
else
|
|
353
397
|
if Thread.current.status == 'aborting'
|
|
354
|
-
rollback_transaction(
|
|
398
|
+
rollback_transaction(conn, opts)
|
|
399
|
+
false
|
|
355
400
|
else
|
|
356
|
-
commit_transaction(
|
|
401
|
+
commit_transaction(conn, opts)
|
|
402
|
+
true
|
|
357
403
|
end
|
|
358
404
|
end
|
|
359
405
|
end
|
|
@@ -361,8 +407,13 @@ module Sequel
|
|
|
361
407
|
# Whether to commit the current transaction. On ruby 1.9 and JRuby,
|
|
362
408
|
# transactions will be committed if Thread#kill is used on an thread
|
|
363
409
|
# that has a transaction open, and there isn't a work around.
|
|
364
|
-
def commit_or_rollback_transaction(exception,
|
|
365
|
-
|
|
410
|
+
def commit_or_rollback_transaction(exception, conn, opts)
|
|
411
|
+
if exception
|
|
412
|
+
false
|
|
413
|
+
else
|
|
414
|
+
commit_transaction(conn, opts)
|
|
415
|
+
true
|
|
416
|
+
end
|
|
366
417
|
end
|
|
367
418
|
end
|
|
368
419
|
|
|
@@ -374,7 +425,7 @@ module Sequel
|
|
|
374
425
|
# Commit the active transaction on the connection
|
|
375
426
|
def commit_transaction(conn, opts={})
|
|
376
427
|
if supports_savepoints?
|
|
377
|
-
depth =
|
|
428
|
+
depth = @transactions[conn][:savepoint_level]
|
|
378
429
|
log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
|
|
379
430
|
else
|
|
380
431
|
log_connection_execute(conn, commit_transaction_sql)
|
|
@@ -395,8 +446,8 @@ module Sequel
|
|
|
395
446
|
# Return a Method object for the dataset's output_identifier_method.
|
|
396
447
|
# Used in metadata parsing to make sure the returned information is in the
|
|
397
448
|
# correct format.
|
|
398
|
-
def input_identifier_meth
|
|
399
|
-
dataset.method(:input_identifier)
|
|
449
|
+
def input_identifier_meth(ds=nil)
|
|
450
|
+
(ds || dataset).method(:input_identifier)
|
|
400
451
|
end
|
|
401
452
|
|
|
402
453
|
# Return a dataset that uses the default identifier input and output methods
|
|
@@ -413,24 +464,28 @@ module Sequel
|
|
|
413
464
|
# Return a Method object for the dataset's output_identifier_method.
|
|
414
465
|
# Used in metadata parsing to make sure the returned information is in the
|
|
415
466
|
# correct format.
|
|
416
|
-
def output_identifier_meth
|
|
417
|
-
dataset.method(:output_identifier)
|
|
467
|
+
def output_identifier_meth(ds=nil)
|
|
468
|
+
(ds || dataset).method(:output_identifier)
|
|
418
469
|
end
|
|
419
470
|
|
|
420
|
-
# SQL to ROLLBACK a transaction.
|
|
421
|
-
def rollback_transaction_sql
|
|
422
|
-
SQL_ROLLBACK
|
|
423
|
-
end
|
|
424
|
-
|
|
425
471
|
# Remove the cached schema for the given schema name
|
|
426
472
|
def remove_cached_schema(table)
|
|
427
473
|
@schemas.delete(quote_schema_table(table)) if @schemas
|
|
428
474
|
end
|
|
429
475
|
|
|
430
476
|
# Remove the current thread from the list of active transactions
|
|
431
|
-
def remove_transaction(conn)
|
|
432
|
-
|
|
433
|
-
|
|
477
|
+
def remove_transaction(conn, committed)
|
|
478
|
+
if !supports_savepoints? || ((@transactions[conn][:savepoint_level] -= 1) <= 0)
|
|
479
|
+
begin
|
|
480
|
+
if committed
|
|
481
|
+
after_transaction_commit(conn)
|
|
482
|
+
else
|
|
483
|
+
after_transaction_rollback(conn)
|
|
484
|
+
end
|
|
485
|
+
ensure
|
|
486
|
+
@transactions.delete(conn)
|
|
487
|
+
end
|
|
488
|
+
end
|
|
434
489
|
end
|
|
435
490
|
|
|
436
491
|
# SQL to rollback to a savepoint
|
|
@@ -441,13 +496,18 @@ module Sequel
|
|
|
441
496
|
# Rollback the active transaction on the connection
|
|
442
497
|
def rollback_transaction(conn, opts={})
|
|
443
498
|
if supports_savepoints?
|
|
444
|
-
depth =
|
|
499
|
+
depth = @transactions[conn][:savepoint_level]
|
|
445
500
|
log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
|
|
446
501
|
else
|
|
447
502
|
log_connection_execute(conn, rollback_transaction_sql)
|
|
448
503
|
end
|
|
449
504
|
end
|
|
450
505
|
|
|
506
|
+
# SQL to ROLLBACK a transaction.
|
|
507
|
+
def rollback_transaction_sql
|
|
508
|
+
SQL_ROLLBACK
|
|
509
|
+
end
|
|
510
|
+
|
|
451
511
|
# Match the database's column type to a ruby type via a
|
|
452
512
|
# regular expression, and return the ruby type as a symbol
|
|
453
513
|
# such as :integer or :string.
|
|
@@ -469,8 +529,8 @@ module Sequel
|
|
|
469
529
|
:boolean
|
|
470
530
|
when /\A(real|float|double( precision)?)\z/io
|
|
471
531
|
:float
|
|
472
|
-
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d
|
|
473
|
-
$1 &&
|
|
532
|
+
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?)|(?:small)?money)\z/io
|
|
533
|
+
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
|
474
534
|
when /bytea|[bc]lob|image|(var)?binary/io
|
|
475
535
|
:blob
|
|
476
536
|
when /\Aenum/io
|
|
@@ -492,7 +552,11 @@ module Sequel
|
|
|
492
552
|
|
|
493
553
|
# Raise a database error unless the exception is an Rollback.
|
|
494
554
|
def transaction_error(e, opts={})
|
|
495
|
-
|
|
555
|
+
if e.is_a?(Rollback)
|
|
556
|
+
raise e if opts[:rollback] == :reraise
|
|
557
|
+
else
|
|
558
|
+
raise_error(e, opts.merge(:classes=>database_error_classes))
|
|
559
|
+
end
|
|
496
560
|
end
|
|
497
561
|
end
|
|
498
562
|
end
|