sequel 4.9.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +79 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/Rakefile +2 -12
- data/bin/sequel +1 -0
- data/doc/advanced_associations.rdoc +82 -25
- data/doc/association_basics.rdoc +21 -22
- data/doc/core_extensions.rdoc +1 -1
- data/doc/opening_databases.rdoc +7 -0
- data/doc/release_notes/4.10.0.txt +226 -0
- data/doc/security.rdoc +1 -0
- data/doc/testing.rdoc +7 -7
- data/doc/transactions.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +160 -168
- data/lib/sequel/adapters/jdbc/db2.rb +17 -18
- data/lib/sequel/adapters/jdbc/derby.rb +5 -28
- data/lib/sequel/adapters/jdbc/h2.rb +11 -22
- data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
- data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
- data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
- data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
- data/lib/sequel/adapters/openbase.rb +1 -7
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +3 -6
- data/lib/sequel/adapters/shared/cubrid.rb +24 -9
- data/lib/sequel/adapters/shared/db2.rb +13 -5
- data/lib/sequel/adapters/shared/firebird.rb +16 -16
- data/lib/sequel/adapters/shared/informix.rb +2 -5
- data/lib/sequel/adapters/shared/mssql.rb +72 -63
- data/lib/sequel/adapters/shared/mysql.rb +72 -40
- data/lib/sequel/adapters/shared/oracle.rb +27 -15
- data/lib/sequel/adapters/shared/postgres.rb +24 -44
- data/lib/sequel/adapters/shared/progress.rb +1 -5
- data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
- data/lib/sequel/adapters/shared/sqlite.rb +21 -6
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
- data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
- data/lib/sequel/core.rb +14 -9
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +12 -0
- data/lib/sequel/database/query.rb +4 -1
- data/lib/sequel/database/schema_methods.rb +3 -2
- data/lib/sequel/database/transactions.rb +47 -17
- data/lib/sequel/dataset/features.rb +12 -2
- data/lib/sequel/dataset/mutation.rb +2 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
- data/lib/sequel/dataset/prepared_statements.rb +6 -0
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +132 -70
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/null_dataset.rb +8 -4
- data/lib/sequel/extensions/pg_array.rb +4 -4
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/model/associations.rb +468 -188
- data/lib/sequel/model/base.rb +88 -13
- data/lib/sequel/plugins/association_pks.rb +23 -64
- data/lib/sequel/plugins/auto_validations.rb +3 -2
- data/lib/sequel/plugins/dataset_associations.rb +1 -3
- data/lib/sequel/plugins/many_through_many.rb +18 -65
- data/lib/sequel/plugins/pg_array_associations.rb +97 -86
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
- data/lib/sequel/plugins/rcte_tree.rb +12 -16
- data/lib/sequel/plugins/sharding.rb +21 -3
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
- data/lib/sequel/plugins/subclasses.rb +1 -9
- data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
- data/lib/sequel/plugins/tree.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +57 -15
- data/spec/adapters/mysql_spec.rb +11 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +38 -4
- data/spec/core/dataset_spec.rb +45 -7
- data/spec/core/placeholder_literalizer_spec.rb +17 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/active_model_spec.rb +18 -9
- data/spec/extensions/association_pks_spec.rb +20 -18
- data/spec/extensions/association_proxies_spec.rb +9 -9
- data/spec/extensions/auto_validations_spec.rb +6 -0
- data/spec/extensions/columns_introspection_spec.rb +1 -0
- data/spec/extensions/constraint_validations_spec.rb +3 -1
- data/spec/extensions/many_through_many_spec.rb +191 -111
- data/spec/extensions/pg_array_associations_spec.rb +133 -103
- data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
- data/spec/extensions/rcte_tree_spec.rb +35 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
- data/spec/extensions/sharding_spec.rb +2 -2
- data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
- data/spec/extensions/to_dot_spec.rb +1 -0
- data/spec/extensions/touch_spec.rb +2 -2
- data/spec/integration/associations_test.rb +130 -37
- data/spec/integration/dataset_test.rb +17 -0
- data/spec/integration/model_test.rb +17 -0
- data/spec/integration/schema_test.rb +14 -0
- data/spec/integration/transaction_test.rb +25 -1
- data/spec/model/association_reflection_spec.rb +63 -24
- data/spec/model/associations_spec.rb +104 -57
- data/spec/model/base_spec.rb +14 -1
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +221 -74
- data/spec/model/model_spec.rb +119 -1
- metadata +4 -2
@@ -10,7 +10,7 @@ module Sequel
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module DatasetMethods
|
13
|
-
|
13
|
+
Dataset.def_sql_method(self, :select, %w'select limit distinct columns from join where group order having compounds')
|
14
14
|
|
15
15
|
# Progress requires SQL standard datetimes
|
16
16
|
def requires_sql_standard_datetimes?
|
@@ -24,10 +24,6 @@ module Sequel
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def select_clause_methods
|
28
|
-
SELECT_CLAUSE_METHODS
|
29
|
-
end
|
30
|
-
|
31
27
|
# Progress uses TOP for limit, but it is only supported in Progress 10.
|
32
28
|
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
33
29
|
def select_limit_sql(sql)
|
@@ -240,8 +240,6 @@ module Sequel
|
|
240
240
|
module DatasetMethods
|
241
241
|
BOOL_TRUE = '1'.freeze
|
242
242
|
BOOL_FALSE = '0'.freeze
|
243
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with insert into columns values')
|
244
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns into from join where group having order compounds lock')
|
245
243
|
WILDCARD = LiteralString.new('%').freeze
|
246
244
|
TOP = " TOP ".freeze
|
247
245
|
START_AT = " START AT ".freeze
|
@@ -261,6 +259,10 @@ module Sequel
|
|
261
259
|
HSTAR = "H*".freeze
|
262
260
|
CROSS_APPLY = 'CROSS APPLY'.freeze
|
263
261
|
OUTER_APPLY = 'OUTER APPLY'.freeze
|
262
|
+
ONLY_OFFSET = " TOP 2147483647".freeze
|
263
|
+
|
264
|
+
Dataset.def_sql_method(self, :insert, %w'with insert into columns values')
|
265
|
+
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from join where group having order compounds lock')
|
264
266
|
|
265
267
|
# Whether to convert smallint to boolean arguments for this dataset.
|
266
268
|
# Defaults to the SqlAnywhere module setting.
|
@@ -271,6 +273,10 @@ module Sequel
|
|
271
273
|
# Override the default SqlAnywhere.convert_smallint_to_bool setting for this dataset.
|
272
274
|
attr_writer :convert_smallint_to_bool
|
273
275
|
|
276
|
+
def supports_cte?(type=:select)
|
277
|
+
type == :select || type == :insert
|
278
|
+
end
|
279
|
+
|
274
280
|
def supports_multiple_column_in?
|
275
281
|
false
|
276
282
|
end
|
@@ -401,14 +407,9 @@ module Sequel
|
|
401
407
|
end
|
402
408
|
end
|
403
409
|
|
404
|
-
# Sybase supports
|
405
|
-
|
406
|
-
|
407
|
-
INSERT_CLAUSE_METHODS
|
408
|
-
end
|
409
|
-
|
410
|
-
def select_clause_methods
|
411
|
-
SELECT_CLAUSE_METHODS
|
410
|
+
# Sybase supports multiple rows in INSERT.
|
411
|
+
def multi_insert_sql_strategy
|
412
|
+
:values
|
412
413
|
end
|
413
414
|
|
414
415
|
def select_into_sql(sql)
|
@@ -425,14 +426,21 @@ module Sequel
|
|
425
426
|
# Sybase uses TOP N for limit. For Sybase TOP (N) is used
|
426
427
|
# to allow the limit to be a bound variable.
|
427
428
|
def select_limit_sql(sql)
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
429
|
+
l = @opts[:limit]
|
430
|
+
o = @opts[:offset]
|
431
|
+
if l || o
|
432
|
+
if l
|
433
|
+
sql << TOP
|
434
|
+
literal_append(sql, l)
|
435
|
+
else
|
436
|
+
sql << ONLY_OFFSET
|
437
|
+
end
|
438
|
+
|
439
|
+
if o
|
440
|
+
sql << START_AT + "("
|
441
|
+
literal_append(sql, o)
|
442
|
+
sql << " + 1)"
|
443
|
+
end
|
436
444
|
end
|
437
445
|
end
|
438
446
|
|
@@ -132,7 +132,7 @@ module Sequel
|
|
132
132
|
def sqlite_version
|
133
133
|
return @sqlite_version if defined?(@sqlite_version)
|
134
134
|
@sqlite_version = begin
|
135
|
-
v =
|
135
|
+
v = fetch('SELECT sqlite_version()').single_value
|
136
136
|
[10000, 100, 1].zip(v.split('.')).inject(0){|a, m| a + m[0] * Integer(m[1])}
|
137
137
|
rescue
|
138
138
|
0
|
@@ -497,7 +497,6 @@ module Sequel
|
|
497
497
|
module DatasetMethods
|
498
498
|
include Dataset::Replace
|
499
499
|
|
500
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
501
500
|
CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
|
502
501
|
EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
|
503
502
|
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
|
@@ -517,6 +516,11 @@ module Sequel
|
|
517
516
|
HSTAR = "H*".freeze
|
518
517
|
DATE_OPEN = "date(".freeze
|
519
518
|
DATETIME_OPEN = "datetime(".freeze
|
519
|
+
ONLY_OFFSET = " LIMIT -1 OFFSET ".freeze
|
520
|
+
|
521
|
+
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
522
|
+
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert into columns values'], ["else", %w'insert into columns values']])
|
523
|
+
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
520
524
|
|
521
525
|
def cast_sql_append(sql, expr, type)
|
522
526
|
if type == Time or type == DateTime
|
@@ -604,6 +608,11 @@ module Sequel
|
|
604
608
|
end
|
605
609
|
end
|
606
610
|
|
611
|
+
# SQLite 3.8.3+ supports common table expressions.
|
612
|
+
def supports_cte?(type=:select)
|
613
|
+
db.sqlite_version >= 30803
|
614
|
+
end
|
615
|
+
|
607
616
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
608
617
|
def supports_intersect_except_all?
|
609
618
|
false
|
@@ -677,11 +686,12 @@ module Sequel
|
|
677
686
|
@db.integer_booleans ? '1' : "'t'"
|
678
687
|
end
|
679
688
|
|
680
|
-
# SQLite
|
681
|
-
|
682
|
-
|
689
|
+
# SQLite only supporting multiple rows in the VALUES clause
|
690
|
+
# starting in 3.7.11. On older versions, fallback to using a UNION.
|
691
|
+
def multi_insert_sql_strategy
|
692
|
+
db.sqlite_version >= 30711 ? :values : :union
|
683
693
|
end
|
684
|
-
|
694
|
+
|
685
695
|
# SQLite does not support FOR UPDATE, but silently ignore it
|
686
696
|
# instead of raising an error for compatibility with other
|
687
697
|
# databases.
|
@@ -689,6 +699,11 @@ module Sequel
|
|
689
699
|
super unless @opts[:lock] == :update
|
690
700
|
end
|
691
701
|
|
702
|
+
def select_only_offset_sql(sql)
|
703
|
+
sql << ONLY_OFFSET
|
704
|
+
literal_append(sql, @opts[:offset])
|
705
|
+
end
|
706
|
+
|
692
707
|
# SQLite supports quoted function names.
|
693
708
|
def supports_quoted_function_names?
|
694
709
|
true
|
@@ -18,6 +18,7 @@ module Sequel
|
|
18
18
|
# reversed in the subselect. Note that the order needs to be unambiguous
|
19
19
|
# to work correctly, and you must select all columns that you are ordering on.
|
20
20
|
def select_sql
|
21
|
+
return super if @opts[:sql]
|
21
22
|
return super unless o = @opts[:offset]
|
22
23
|
|
23
24
|
order = @opts[:order] || default_offset_order
|
@@ -26,7 +27,7 @@ module Sequel
|
|
26
27
|
end
|
27
28
|
|
28
29
|
ds = unlimited
|
29
|
-
row_count = @opts[:offset_total_count] || ds.clone(:append_sql=>'').count
|
30
|
+
row_count = @opts[:offset_total_count] || ds.clone(:append_sql=>'', :placeholder_literal_null=>true).count
|
30
31
|
dsa1 = dataset_alias(1)
|
31
32
|
|
32
33
|
if o.is_a?(Symbol) && @opts[:bind_vars] && (match = Sequel::Dataset::PreparedStatementMethods::PLACEHOLDER_RE.match(o.to_s))
|
@@ -57,6 +58,12 @@ module Sequel
|
|
57
58
|
sql
|
58
59
|
end
|
59
60
|
|
61
|
+
# This does not support offsets in correlated subqueries, as it requires a query to get
|
62
|
+
# a count that will be invalid if a correlated subquery is used.
|
63
|
+
def supports_offsets_in_correlated_subqueries?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
60
67
|
private
|
61
68
|
|
62
69
|
# The default order to use for datasets with offsets, if no order is defined.
|
@@ -20,7 +20,7 @@ module Sequel
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
columns = clone(:append_sql=>'').columns
|
23
|
+
columns = clone(:append_sql=>'', :placeholder_literal_null=>true).columns
|
24
24
|
dsa1 = dataset_alias(1)
|
25
25
|
rn = row_number_column
|
26
26
|
sql = @opts[:append_sql] || ''
|
@@ -35,6 +35,12 @@ module Sequel
|
|
35
35
|
sql
|
36
36
|
end
|
37
37
|
|
38
|
+
# This does not support offsets in correlated subqueries, as it requires a query to get
|
39
|
+
# the columns that will be invalid if a correlated subquery is used.
|
40
|
+
def supports_offsets_in_correlated_subqueries?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
38
44
|
private
|
39
45
|
|
40
46
|
# The default order to use for datasets with offsets, if no order is defined.
|
@@ -50,7 +56,7 @@ module Sequel
|
|
50
56
|
|
51
57
|
# Whether to use ROW_NUMBER to emulate offsets
|
52
58
|
def emulate_offset_with_row_number?
|
53
|
-
@opts[:offset]
|
59
|
+
@opts[:offset] && !@opts[:sql]
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
@@ -24,6 +24,9 @@ module Sequel::Database::SplitAlterTable
|
|
24
24
|
modified_columns << op[:name] unless modified_columns.include?(op[:name])
|
25
25
|
modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name])
|
26
26
|
end
|
27
|
+
if split_alter_table_op?(op)
|
28
|
+
op_groups << []
|
29
|
+
end
|
27
30
|
op_groups.last << op
|
28
31
|
end
|
29
32
|
|
@@ -33,4 +36,9 @@ module Sequel::Database::SplitAlterTable
|
|
33
36
|
remove_cached_schema(name)
|
34
37
|
end
|
35
38
|
end
|
39
|
+
|
40
|
+
# Whether the given alter table op should start a new group.
|
41
|
+
def split_alter_table_op?(op)
|
42
|
+
false
|
43
|
+
end
|
36
44
|
end
|
data/lib/sequel/core.rb
CHANGED
@@ -219,6 +219,7 @@ module Sequel
|
|
219
219
|
COLUMN_REF_RE1 = /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/.freeze
|
220
220
|
COLUMN_REF_RE2 = /\A((?:(?!___).)+)___(.+)\z/.freeze
|
221
221
|
COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
|
222
|
+
SPLIT_SYMBOL_CACHE = {}
|
222
223
|
|
223
224
|
# Splits the symbol into three parts. Each part will
|
224
225
|
# either be a string or nil.
|
@@ -226,16 +227,20 @@ module Sequel
|
|
226
227
|
# For columns, these parts are the table, column, and alias.
|
227
228
|
# For tables, these parts are the schema, table, and alias.
|
228
229
|
def self.split_symbol(sym)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
230
|
+
unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
|
231
|
+
v = case s = sym.to_s
|
232
|
+
when COLUMN_REF_RE1
|
233
|
+
[$1.freeze, $2.freeze, $3.freeze].freeze
|
234
|
+
when COLUMN_REF_RE2
|
235
|
+
[nil, $1.freeze, $2.freeze].freeze
|
236
|
+
when COLUMN_REF_RE3
|
237
|
+
[$1.freeze, $2.freeze, nil].freeze
|
238
|
+
else
|
239
|
+
[nil, s.freeze, nil].freeze
|
240
|
+
end
|
241
|
+
Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
|
238
242
|
end
|
243
|
+
v
|
239
244
|
end
|
240
245
|
|
241
246
|
# Converts the given +string+ into a +Date+ object.
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -131,6 +131,7 @@ module Sequel
|
|
131
131
|
@dataset_class = dataset_class_default
|
132
132
|
@cache_schema = typecast_value_boolean(@opts.fetch(:cache_schema, true))
|
133
133
|
@dataset_modules = []
|
134
|
+
@symbol_literal_cache = {}
|
134
135
|
@schema_type_classes = SCHEMA_TYPE_CLASSES.dup
|
135
136
|
self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
|
136
137
|
@pool = ConnectionPool.get_pool(self, @opts)
|
@@ -235,6 +236,17 @@ module Sequel
|
|
235
236
|
schema_utility_dataset.literal(v)
|
236
237
|
end
|
237
238
|
|
239
|
+
# Return the literalized version of the symbol if cached, or
|
240
|
+
# nil if it is not cached.
|
241
|
+
def literal_symbol(sym)
|
242
|
+
Sequel.synchronize{@symbol_literal_cache[sym]}
|
243
|
+
end
|
244
|
+
|
245
|
+
# Set the cached value of the literal symbol.
|
246
|
+
def literal_symbol_set(sym, lit)
|
247
|
+
Sequel.synchronize{@symbol_literal_cache[sym] = lit}
|
248
|
+
end
|
249
|
+
|
238
250
|
# Synchronize access to the prepared statements cache.
|
239
251
|
def prepared_statement(name)
|
240
252
|
Sequel.synchronize{prepared_statements[name]}
|
@@ -279,7 +279,10 @@ module Sequel
|
|
279
279
|
|
280
280
|
# Remove the cached schema for the given schema name
|
281
281
|
def remove_cached_schema(table)
|
282
|
-
|
282
|
+
if @schemas
|
283
|
+
k = quote_schema_table(table)
|
284
|
+
Sequel.synchronize{@schemas.delete(k)}
|
285
|
+
end
|
283
286
|
end
|
284
287
|
|
285
288
|
# Match the database's column type to a ruby type via a
|
@@ -332,12 +332,13 @@ module Sequel
|
|
332
332
|
# DB.drop_view(:cheap_items)
|
333
333
|
# DB.drop_view(:cheap_items, :pricey_items)
|
334
334
|
# DB.drop_view(:cheap_items, :pricey_items, :cascade=>true)
|
335
|
+
# DB.drop_view(:cheap_items, :pricey_items, :if_exists=>true)
|
335
336
|
#
|
336
337
|
# Options:
|
337
338
|
# :cascade :: Also drop objects depending on this view.
|
339
|
+
# :if_exists :: Do not raise an error if the view does not exist.
|
338
340
|
#
|
339
341
|
# PostgreSQL specific options:
|
340
|
-
# :if_exists :: Do not raise an error if the view does not exist.
|
341
342
|
# :materialized :: Drop a materialized view.
|
342
343
|
def drop_view(*names)
|
343
344
|
options = names.last.is_a?(Hash) ? names.pop : {}
|
@@ -721,7 +722,7 @@ module Sequel
|
|
721
722
|
|
722
723
|
# SQL DDL statement to drop a view with the given name.
|
723
724
|
def drop_view_sql(name, options)
|
724
|
-
"DROP VIEW #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
725
|
+
"DROP VIEW#{' IF EXISTS' if options[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
725
726
|
end
|
726
727
|
|
727
728
|
# Proxy the filter_expr call to the dataset, used for creating constraints.
|
@@ -36,6 +36,8 @@ module Sequel
|
|
36
36
|
#
|
37
37
|
# The following general options are respected:
|
38
38
|
#
|
39
|
+
# :auto_savepoint :: Automatically use a savepoint for Database#transaction calls
|
40
|
+
# inside this transaction block.
|
39
41
|
# :isolation :: The transaction isolation level to use for this transaction,
|
40
42
|
# should be :uncommitted, :committed, :repeatable, or :serializable,
|
41
43
|
# used if given and the database/adapter supports customizable
|
@@ -59,7 +61,8 @@ module Sequel
|
|
59
61
|
# :savepoint :: Whether to create a new savepoint for this transaction,
|
60
62
|
# only respected if the database/adapter supports savepoints. By
|
61
63
|
# default Sequel will reuse an existing transaction, so if you want to
|
62
|
-
# use a savepoint you must use this option.
|
64
|
+
# use a savepoint you must use this option. If the surrounding transaction
|
65
|
+
# uses :auto_savepoint, you can set this to false to not use a savepoint.
|
63
66
|
#
|
64
67
|
# PostgreSQL specific options:
|
65
68
|
#
|
@@ -88,9 +91,14 @@ module Sequel
|
|
88
91
|
if opts[:retrying]
|
89
92
|
raise Sequel::Error, "cannot set :retry_on options if you are already inside a transaction"
|
90
93
|
end
|
91
|
-
|
94
|
+
if opts[:savepoint] != false && (stack = _trans(conn)[:savepoints]) && stack.last
|
95
|
+
_transaction(conn, opts.merge(:savepoint=>true), &block)
|
96
|
+
else
|
97
|
+
return yield(conn)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
_transaction(conn, opts, &block)
|
92
101
|
end
|
93
|
-
_transaction(conn, opts, &block)
|
94
102
|
end
|
95
103
|
end
|
96
104
|
end
|
@@ -151,18 +159,24 @@ module Sequel
|
|
151
159
|
|
152
160
|
# Add the current thread to the list of active transactions
|
153
161
|
def add_transaction(conn, opts)
|
162
|
+
hash = {}
|
163
|
+
|
154
164
|
if supports_savepoints?
|
155
|
-
|
165
|
+
if _trans(conn)
|
166
|
+
hash = nil
|
167
|
+
_trans(conn)[:savepoints].push(opts[:auto_savepoint])
|
168
|
+
else
|
169
|
+
hash[:savepoints] = [opts[:auto_savepoint]]
|
156
170
|
if (prep = opts[:prepare]) && supports_prepared_transactions?
|
157
|
-
|
158
|
-
else
|
159
|
-
Sequel.synchronize{@transactions[conn] = {:savepoint_level=>0}}
|
171
|
+
hash[:prepare] = prep
|
160
172
|
end
|
161
173
|
end
|
162
174
|
elsif (prep = opts[:prepare]) && supports_prepared_transactions?
|
163
|
-
|
164
|
-
|
165
|
-
|
175
|
+
hash[:prepare] = prep
|
176
|
+
end
|
177
|
+
|
178
|
+
if hash
|
179
|
+
Sequel.synchronize{@transactions[conn] = hash}
|
166
180
|
end
|
167
181
|
end
|
168
182
|
|
@@ -199,13 +213,12 @@ module Sequel
|
|
199
213
|
# Start a new database transaction or a new savepoint on the given connection.
|
200
214
|
def begin_transaction(conn, opts=OPTS)
|
201
215
|
if supports_savepoints?
|
202
|
-
|
203
|
-
if
|
204
|
-
log_connection_execute(conn, begin_savepoint_sql(depth))
|
216
|
+
depth = savepoint_level(conn)
|
217
|
+
if depth > 1
|
218
|
+
log_connection_execute(conn, begin_savepoint_sql(depth-1))
|
205
219
|
else
|
206
220
|
begin_new_transaction(conn, opts)
|
207
221
|
end
|
208
|
-
th[:savepoint_level] += 1
|
209
222
|
else
|
210
223
|
begin_new_transaction(conn, opts)
|
211
224
|
end
|
@@ -243,7 +256,7 @@ module Sequel
|
|
243
256
|
# Commit the active transaction on the connection
|
244
257
|
def commit_transaction(conn, opts=OPTS)
|
245
258
|
if supports_savepoints?
|
246
|
-
depth =
|
259
|
+
depth = savepoint_level(conn)
|
247
260
|
log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
|
248
261
|
else
|
249
262
|
log_connection_execute(conn, commit_transaction_sql)
|
@@ -263,7 +276,7 @@ module Sequel
|
|
263
276
|
|
264
277
|
# Remove the current thread from the list of active transactions
|
265
278
|
def remove_transaction(conn, committed)
|
266
|
-
if
|
279
|
+
if transaction_finished?(conn)
|
267
280
|
begin
|
268
281
|
if committed
|
269
282
|
after_transaction_commit(conn)
|
@@ -284,7 +297,7 @@ module Sequel
|
|
284
297
|
# Rollback the active transaction on the connection
|
285
298
|
def rollback_transaction(conn, opts=OPTS)
|
286
299
|
if supports_savepoints?
|
287
|
-
depth =
|
300
|
+
depth = savepoint_level(conn)
|
288
301
|
log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
|
289
302
|
else
|
290
303
|
log_connection_execute(conn, rollback_transaction_sql)
|
@@ -308,6 +321,11 @@ module Sequel
|
|
308
321
|
"SET TRANSACTION ISOLATION LEVEL #{TRANSACTION_ISOLATION_LEVELS[level]}"
|
309
322
|
end
|
310
323
|
|
324
|
+
# Current savepoint level.
|
325
|
+
def savepoint_level(conn)
|
326
|
+
_trans(conn)[:savepoints].length
|
327
|
+
end
|
328
|
+
|
311
329
|
# Raise a database error unless the exception is an Rollback.
|
312
330
|
def transaction_error(e, opts=OPTS)
|
313
331
|
if e.is_a?(Rollback)
|
@@ -316,5 +334,17 @@ module Sequel
|
|
316
334
|
raise_error(e, opts.merge(:classes=>database_error_classes))
|
317
335
|
end
|
318
336
|
end
|
337
|
+
|
338
|
+
# Finish a subtransaction. If savepoints are supported, pops the current
|
339
|
+
# tansaction off the savepoint stack.
|
340
|
+
def transaction_finished?(conn)
|
341
|
+
if supports_savepoints?
|
342
|
+
stack = _trans(conn)[:savepoints]
|
343
|
+
stack.pop
|
344
|
+
stack.empty?
|
345
|
+
else
|
346
|
+
true
|
347
|
+
end
|
348
|
+
end
|
319
349
|
end
|
320
350
|
end
|