sequel 3.6.0 → 3.7.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 +28 -0
- data/Rakefile +12 -15
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/virtual_rows.rdoc +3 -0
- data/lib/sequel/adapters/mysql.rb +8 -5
- data/lib/sequel/adapters/shared/mssql.rb +24 -15
- data/lib/sequel/adapters/shared/mysql.rb +16 -0
- data/lib/sequel/adapters/shared/oracle.rb +18 -0
- data/lib/sequel/adapters/shared/postgres.rb +59 -1
- data/lib/sequel/dataset/convenience.rb +58 -11
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/query.rb +3 -3
- data/lib/sequel/dataset/sql.rb +19 -2
- data/lib/sequel/extensions/schema_dumper.rb +6 -1
- data/lib/sequel/model/base.rb +40 -16
- data/lib/sequel/plugins/validation_helpers.rb +7 -2
- data/lib/sequel/sql.rb +22 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +16 -0
- data/spec/core/dataset_spec.rb +187 -0
- data/spec/core/expression_filters_spec.rb +25 -0
- data/spec/extensions/schema_dumper_spec.rb +6 -0
- data/spec/extensions/validation_helpers_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +37 -0
- data/spec/model/model_spec.rb +26 -0
- data/spec/model/record_spec.rb +65 -3
- metadata +4 -3
- data/spec/spec.opts +0 -0
@@ -107,11 +107,29 @@ module Sequel
|
|
107
107
|
db[:dual].where(exists).get(1) == nil
|
108
108
|
end
|
109
109
|
|
110
|
+
# If this dataset is associated with a sequence, return the most recently
|
111
|
+
# inserted sequence value.
|
112
|
+
def insert(*args)
|
113
|
+
r = super
|
114
|
+
if s = opts[:sequence]
|
115
|
+
with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
|
116
|
+
else
|
117
|
+
r
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
110
121
|
# Oracle requires SQL standard datetimes
|
111
122
|
def requires_sql_standard_datetimes?
|
112
123
|
true
|
113
124
|
end
|
114
125
|
|
126
|
+
# Create a copy of this dataset associated to the given sequence name,
|
127
|
+
# which will be used when calling insert to find the most recently
|
128
|
+
# inserted value for the sequence.
|
129
|
+
def sequence(s)
|
130
|
+
clone(:sequence=>s)
|
131
|
+
end
|
132
|
+
|
115
133
|
# Oracle does not support DISTINCT ON
|
116
134
|
def supports_distinct_on?
|
117
135
|
false
|
@@ -335,7 +335,7 @@ module Sequel
|
|
335
335
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
336
336
|
# managing incrementing primary keys.
|
337
337
|
def serial_primary_key_options
|
338
|
-
{:primary_key => true, :
|
338
|
+
{:primary_key => true, :serial => true, :type=>Integer}
|
339
339
|
end
|
340
340
|
|
341
341
|
# The version of the PostgreSQL server, used for determining capability.
|
@@ -551,11 +551,21 @@ module Sequel
|
|
551
551
|
"(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
|
552
552
|
end
|
553
553
|
|
554
|
+
# Handle bigserial type if :serial option is present
|
555
|
+
def type_literal_generic_bignum(column)
|
556
|
+
column[:serial] ? :bigserial : super
|
557
|
+
end
|
558
|
+
|
554
559
|
# PostgreSQL uses the bytea data type for blobs
|
555
560
|
def type_literal_generic_file(column)
|
556
561
|
:bytea
|
557
562
|
end
|
558
563
|
|
564
|
+
# Handle serial type if :serial option is present
|
565
|
+
def type_literal_generic_integer(column)
|
566
|
+
column[:serial] ? :serial : super
|
567
|
+
end
|
568
|
+
|
559
569
|
# PostgreSQL prefers the text datatype. If a fixed size is requested,
|
560
570
|
# the char type is used. If the text type is specifically
|
561
571
|
# disallowed or there is a size specified, use the varchar type.
|
@@ -578,6 +588,7 @@ module Sequel
|
|
578
588
|
BOOL_FALSE = 'false'.freeze
|
579
589
|
BOOL_TRUE = 'true'.freeze
|
580
590
|
COMMA_SEPARATOR = ', '.freeze
|
591
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
|
581
592
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
582
593
|
EXPLAIN = 'EXPLAIN '.freeze
|
583
594
|
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
@@ -595,6 +606,7 @@ module Sequel
|
|
595
606
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
596
607
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
597
608
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
609
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
|
598
610
|
|
599
611
|
# Shared methods for prepared statements when used with PostgreSQL databases.
|
600
612
|
module PreparedStatementMethods
|
@@ -696,6 +708,11 @@ module Sequel
|
|
696
708
|
[insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
|
697
709
|
end
|
698
710
|
|
711
|
+
# PostgreSQL supports modifying joined datasets
|
712
|
+
def supports_modifying_joins?
|
713
|
+
true
|
714
|
+
end
|
715
|
+
|
699
716
|
# PostgreSQL supports timezones in literal timestamps
|
700
717
|
def supports_timestamp_timezones?
|
701
718
|
true
|
@@ -713,12 +730,38 @@ module Sequel
|
|
713
730
|
|
714
731
|
private
|
715
732
|
|
733
|
+
# PostgreSQL allows deleting from joined datasets
|
734
|
+
def delete_clause_methods
|
735
|
+
DELETE_CLAUSE_METHODS
|
736
|
+
end
|
737
|
+
|
738
|
+
# Only include the primary table in the main delete clause
|
739
|
+
def delete_from_sql(sql)
|
740
|
+
sql << " FROM #{source_list(@opts[:from][0..0])}"
|
741
|
+
end
|
742
|
+
|
743
|
+
# Use USING to specify additional tables in a delete query
|
744
|
+
def delete_using_sql(sql)
|
745
|
+
join_from_sql(:USING, sql)
|
746
|
+
end
|
747
|
+
|
716
748
|
# Use the RETURNING clause to return the primary key of the inserted record, if it exists
|
717
749
|
def insert_returning_pk_sql(*values)
|
718
750
|
pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
|
719
751
|
insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
|
720
752
|
end
|
721
753
|
|
754
|
+
# For multiple table support, PostgreSQL requires at least
|
755
|
+
# two from tables, with joins allowed.
|
756
|
+
def join_from_sql(type, sql)
|
757
|
+
if(from = @opts[:from][1..-1]).empty?
|
758
|
+
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
759
|
+
else
|
760
|
+
sql << " #{type} #{source_list(from)}"
|
761
|
+
select_join_sql(sql)
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
722
765
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
723
766
|
def literal_blob(v)
|
724
767
|
"'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
|
@@ -776,6 +819,21 @@ module Sequel
|
|
776
819
|
cols.pop
|
777
820
|
literal(SQL::StringExpression.new(:'||', *cols))
|
778
821
|
end
|
822
|
+
|
823
|
+
# PostgreSQL splits the main table from the joined tables
|
824
|
+
def update_clause_methods
|
825
|
+
UPDATE_CLAUSE_METHODS
|
826
|
+
end
|
827
|
+
|
828
|
+
# Use FROM to specify additional tables in an update query
|
829
|
+
def update_from_sql(sql)
|
830
|
+
join_from_sql(:FROM, sql)
|
831
|
+
end
|
832
|
+
|
833
|
+
# Only include the primary table in the main update clause
|
834
|
+
def update_table_sql(sql)
|
835
|
+
sql << " #{source_list(@opts[:from][0..0])}"
|
836
|
+
end
|
779
837
|
end
|
780
838
|
end
|
781
839
|
end
|
@@ -3,8 +3,7 @@ module Sequel
|
|
3
3
|
COMMA_SEPARATOR = ', '.freeze
|
4
4
|
COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
|
5
5
|
ARRAY_ACCESS_ERROR_MSG = 'You cannot call Dataset#[] with an integer or with no arguments.'.freeze
|
6
|
-
|
7
|
-
GET_ERROR_MSG = 'must provide argument or block to Dataset#get, not both'.freeze
|
6
|
+
ARG_BLOCK_ERROR_MSG = 'Must use either an argument or a block, not both'.freeze
|
8
7
|
IMPORT_ERROR_MSG = 'Using Sequel::Dataset#import an empty column array is not allowed'.freeze
|
9
8
|
|
10
9
|
# Returns the first record matching the conditions. Examples:
|
@@ -74,7 +73,7 @@ module Sequel
|
|
74
73
|
# ds.get{|o| o.sum(:id)}
|
75
74
|
def get(column=nil, &block)
|
76
75
|
if column
|
77
|
-
raise(Error,
|
76
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
78
77
|
select(column).single_value
|
79
78
|
else
|
80
79
|
select(&block).single_value
|
@@ -91,13 +90,9 @@ module Sequel
|
|
91
90
|
# ds.group_and_count(:first_name, :last_name).all => [{:first_name=>'a', :last_name=>'b', :count=>1}, ...]
|
92
91
|
# ds.group_and_count(:first_name___name).all => [{:name=>'a', :count=>1}, ...]
|
93
92
|
def group_and_count(*columns)
|
94
|
-
|
95
|
-
c_table, column, _ = split_symbol(c)
|
96
|
-
c_table ? column.to_sym.qualify(c_table) : column.to_sym
|
97
|
-
end
|
98
|
-
group(*groups).select(*(columns + [COUNT_OF_ALL_AS_COUNT])).order(:count)
|
93
|
+
group(*columns.map{|c| unaliased_identifier(c)}).select(*(columns + [COUNT_OF_ALL_AS_COUNT])).order(:count)
|
99
94
|
end
|
100
|
-
|
95
|
+
|
101
96
|
# Inserts multiple records into the associated table. This method can be
|
102
97
|
# to efficiently insert a large amounts of records into a table. Inserts
|
103
98
|
# are automatically wrapped in a transaction.
|
@@ -158,7 +153,7 @@ module Sequel
|
|
158
153
|
# ds.map{|r| r[:id] * 2} => [2, 4, 6, ...]
|
159
154
|
def map(column=nil, &block)
|
160
155
|
if column
|
161
|
-
raise(Error,
|
156
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
162
157
|
super(){|r| r[column]}
|
163
158
|
else
|
164
159
|
super(&block)
|
@@ -199,6 +194,32 @@ module Sequel
|
|
199
194
|
end
|
200
195
|
end
|
201
196
|
|
197
|
+
def select_hash(key_column, value_column)
|
198
|
+
select(key_column, value_column).to_hash(hash_key_symbol(key_column), hash_key_symbol(value_column))
|
199
|
+
end
|
200
|
+
|
201
|
+
def select_map(column=nil, &block)
|
202
|
+
ds = naked.ungraphed
|
203
|
+
ds = if column
|
204
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
205
|
+
ds.select(column)
|
206
|
+
else
|
207
|
+
ds.select(&block)
|
208
|
+
end
|
209
|
+
ds.map{|r| r.values.first}
|
210
|
+
end
|
211
|
+
|
212
|
+
def select_order_map(column=nil, &block)
|
213
|
+
ds = naked.ungraphed
|
214
|
+
ds = if column
|
215
|
+
raise(Error, ARG_BLOCK_ERROR_MSG) if block
|
216
|
+
ds.select(column).order(unaliased_identifier(column))
|
217
|
+
else
|
218
|
+
ds.select(&block).order(&block)
|
219
|
+
end
|
220
|
+
ds.map{|r| r.values.first}
|
221
|
+
end
|
222
|
+
|
202
223
|
# Returns the first record in the dataset.
|
203
224
|
def single_record
|
204
225
|
clone(:limit=>1).each{|r| return r}
|
@@ -208,7 +229,7 @@ module Sequel
|
|
208
229
|
# Returns the first value of the first record in the dataset.
|
209
230
|
# Returns nil if dataset is empty.
|
210
231
|
def single_value
|
211
|
-
if r = naked.
|
232
|
+
if r = naked.ungraphed.single_record
|
212
233
|
r.values.first
|
213
234
|
end
|
214
235
|
end
|
@@ -245,5 +266,31 @@ module Sequel
|
|
245
266
|
m
|
246
267
|
end
|
247
268
|
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
# Return a plain symbol given a potentially qualified or aliased symbol,
|
273
|
+
# specifying the symbol that is likely to be used as the hash key
|
274
|
+
# for the column when records are returned.
|
275
|
+
def hash_key_symbol(s)
|
276
|
+
raise(Error, "#{s.inspect} is not a symbol") unless s.is_a?(Symbol)
|
277
|
+
_, c, a = split_symbol(s)
|
278
|
+
(a || c).to_sym
|
279
|
+
end
|
280
|
+
|
281
|
+
# Return the unaliased part of the identifier. Handles both
|
282
|
+
# implicit aliases in symbols, as well as SQL::AliasedExpression
|
283
|
+
# objects. Other objects are returned as is.
|
284
|
+
def unaliased_identifier(c)
|
285
|
+
case c
|
286
|
+
when Symbol
|
287
|
+
c_table, column, _ = split_symbol(c)
|
288
|
+
c_table ? column.to_sym.qualify(c_table) : column.to_sym
|
289
|
+
when SQL::AliasedExpression
|
290
|
+
c.expression
|
291
|
+
else
|
292
|
+
c
|
293
|
+
end
|
294
|
+
end
|
248
295
|
end
|
249
296
|
end
|
@@ -41,6 +41,11 @@ module Sequel
|
|
41
41
|
true
|
42
42
|
end
|
43
43
|
|
44
|
+
# Whether modifying joined datasets is supported.
|
45
|
+
def supports_modifying_joins?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
44
49
|
# Whether the IN/NOT IN operators support multiple columns when an
|
45
50
|
# array of values is given.
|
46
51
|
def supports_multiple_column_in?
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -144,8 +144,8 @@ module Sequel
|
|
144
144
|
#
|
145
145
|
# ds = DB[:items].order(:name).select(:id, :name)
|
146
146
|
# ds.sql #=> "SELECT id,name FROM items ORDER BY name"
|
147
|
-
# ds.from_self.sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS
|
148
|
-
# ds.from_self(:alias=>:foo).sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS
|
147
|
+
# ds.from_self.sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1"
|
148
|
+
# ds.from_self(:alias=>:foo).sql #=> "SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS foo"
|
149
149
|
def from_self(opts={})
|
150
150
|
fs = {}
|
151
151
|
@opts.keys.each{|k| fs[k] = nil unless FROM_SELF_KEEP_OPTS.include?(k)}
|
@@ -370,7 +370,7 @@ module Sequel
|
|
370
370
|
# Add the dataset to the list of compounds
|
371
371
|
def compound_clone(type, dataset, opts)
|
372
372
|
ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
|
373
|
-
opts[:from_self] == false ? ds : ds.from_self
|
373
|
+
opts[:from_self] == false ? ds : ds.from_self(opts)
|
374
374
|
end
|
375
375
|
|
376
376
|
# SQL fragment based on the expr type. See #filter.
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -52,6 +52,11 @@ module Sequel
|
|
52
52
|
a.empty? ? '(NULL)' : "(#{expression_list(a)})"
|
53
53
|
end
|
54
54
|
|
55
|
+
# SQL fragment for BooleanConstants
|
56
|
+
def boolean_constant_sql(constant)
|
57
|
+
literal(constant)
|
58
|
+
end
|
59
|
+
|
55
60
|
# SQL fragment for specifying given CaseExpression.
|
56
61
|
def case_expression_sql(ce)
|
57
62
|
sql = '(CASE '
|
@@ -386,6 +391,11 @@ module Sequel
|
|
386
391
|
values.map{|r| insert_sql(columns, r)}
|
387
392
|
end
|
388
393
|
|
394
|
+
# SQL fragment for NegativeBooleanConstants
|
395
|
+
def negative_boolean_constant_sql(constant)
|
396
|
+
"NOT #{boolean_constant_sql(constant)}"
|
397
|
+
end
|
398
|
+
|
389
399
|
# SQL fragment for the ordered expression, used in the ORDER BY
|
390
400
|
# clause.
|
391
401
|
def ordered_expression_sql(oe)
|
@@ -643,7 +653,7 @@ module Sequel
|
|
643
653
|
# for this dataset
|
644
654
|
def check_modification_allowed!
|
645
655
|
raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
|
646
|
-
raise(InvalidOperation, "Joined datasets cannot be modified") if
|
656
|
+
raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
|
647
657
|
end
|
648
658
|
|
649
659
|
# Prepare an SQL statement by calling all clause methods for the given statement type.
|
@@ -754,6 +764,11 @@ module Sequel
|
|
754
764
|
"#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
|
755
765
|
end
|
756
766
|
|
767
|
+
# Whether this dataset is a joined dataset
|
768
|
+
def joined_dataset?
|
769
|
+
(opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
|
770
|
+
end
|
771
|
+
|
757
772
|
# SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
758
773
|
def literal_array(v)
|
759
774
|
Sequel.condition_specifier?(v) ? literal_expression(SQL::BooleanExpression.from_value_pairs(v)) : array_sql(v)
|
@@ -1058,9 +1073,11 @@ module Sequel
|
|
1058
1073
|
UPDATE_CLAUSE_METHODS
|
1059
1074
|
end
|
1060
1075
|
|
1061
|
-
# SQL fragment specifying the tables from with to delete
|
1076
|
+
# SQL fragment specifying the tables from with to delete.
|
1077
|
+
# Includes join table if modifying joins is allowed.
|
1062
1078
|
def update_table_sql(sql)
|
1063
1079
|
sql << " #{source_list(@opts[:from])}"
|
1080
|
+
select_join_sql(sql) if supports_modifying_joins?
|
1064
1081
|
end
|
1065
1082
|
|
1066
1083
|
# The SQL fragment specifying the columns and values to SET.
|
@@ -86,7 +86,12 @@ END_MIG
|
|
86
86
|
# name and arguments to it to pass to a Schema::Generator to recreate the column.
|
87
87
|
def column_schema_to_generator_opts(name, schema, options)
|
88
88
|
if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
|
89
|
-
|
89
|
+
type_hash = column_schema_to_ruby_type(schema)
|
90
|
+
if type_hash == {:type=>Integer}
|
91
|
+
[:primary_key, name]
|
92
|
+
else
|
93
|
+
[:primary_key, name, type_hash]
|
94
|
+
end
|
90
95
|
else
|
91
96
|
col_opts = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
92
97
|
type = col_opts.delete(:type)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -507,7 +507,7 @@ module Sequel
|
|
507
507
|
|
508
508
|
class_attr_reader :columns, :db, :primary_key, :db_schema
|
509
509
|
class_attr_overridable :raise_on_save_failure, :raise_on_typecast_failure, :strict_param_setting, :typecast_empty_string_to_nil, :typecast_on_assignment, :use_transactions
|
510
|
-
|
510
|
+
|
511
511
|
# The hash of attribute values. Keys are symbols with the names of the
|
512
512
|
# underlying database columns.
|
513
513
|
attr_reader :values
|
@@ -607,9 +607,10 @@ module Sequel
|
|
607
607
|
# If before_destroy returns false, returns false without
|
608
608
|
# deleting the object the the database. Otherwise, deletes
|
609
609
|
# the item from the database and returns self. Uses a transaction
|
610
|
-
# if use_transactions is true
|
611
|
-
|
612
|
-
|
610
|
+
# if use_transactions is true or if the :transaction option is given and
|
611
|
+
# true.
|
612
|
+
def destroy(opts = {})
|
613
|
+
checked_save_failure{checked_transaction(opts){_destroy(opts)}}
|
613
614
|
end
|
614
615
|
|
615
616
|
# Iterates through all of the current values using each.
|
@@ -721,9 +722,11 @@ module Sequel
|
|
721
722
|
# * :validate - set to false not to validate the model before saving
|
722
723
|
def save(*columns)
|
723
724
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
724
|
-
|
725
|
-
|
726
|
-
|
725
|
+
if opts[:validate] != false and !valid?
|
726
|
+
raise(ValidationFailed, errors.full_messages.join(', ')) if raise_on_save_failure
|
727
|
+
return
|
728
|
+
end
|
729
|
+
checked_save_failure{checked_transaction(opts){_save(columns, opts)}}
|
727
730
|
end
|
728
731
|
|
729
732
|
# Saves only changed columns if the object has been modified.
|
@@ -797,7 +800,7 @@ module Sequel
|
|
797
800
|
def valid?
|
798
801
|
errors.clear
|
799
802
|
if before_validation == false
|
800
|
-
save_failure(:validation)
|
803
|
+
save_failure(:validation) if raise_on_save_failure
|
801
804
|
return false
|
802
805
|
end
|
803
806
|
validate
|
@@ -809,7 +812,7 @@ module Sequel
|
|
809
812
|
|
810
813
|
# Internal destroy method, separted from destroy to
|
811
814
|
# allow running inside a transaction
|
812
|
-
def _destroy
|
815
|
+
def _destroy(opts)
|
813
816
|
return save_failure(:destroy) if before_destroy == false
|
814
817
|
delete
|
815
818
|
after_destroy
|
@@ -880,10 +883,30 @@ module Sequel
|
|
880
883
|
self
|
881
884
|
end
|
882
885
|
|
886
|
+
# Update this instance's dataset with the supplied column hash.
|
883
887
|
def _update(columns)
|
884
888
|
this.update(columns)
|
885
889
|
end
|
890
|
+
|
891
|
+
# If raise_on_save_failure is false, check for BeforeHookFailed
|
892
|
+
# beind raised by yielding and swallow it.
|
893
|
+
def checked_save_failure
|
894
|
+
if raise_on_save_failure
|
895
|
+
yield
|
896
|
+
else
|
897
|
+
begin
|
898
|
+
yield
|
899
|
+
rescue BeforeHookFailed
|
900
|
+
nil
|
901
|
+
end
|
902
|
+
end
|
903
|
+
end
|
886
904
|
|
905
|
+
# If transactions should be used, wrap the yield in a transaction block.
|
906
|
+
def checked_transaction(opts)
|
907
|
+
use_transaction?(opts)? db.transaction(opts){yield} : yield
|
908
|
+
end
|
909
|
+
|
887
910
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
888
911
|
def inspect_values
|
889
912
|
@values.inspect
|
@@ -891,13 +914,7 @@ module Sequel
|
|
891
914
|
|
892
915
|
# Raise an error if raise_on_save_failure is true, return nil otherwise.
|
893
916
|
def save_failure(type)
|
894
|
-
|
895
|
-
if type == :invalid
|
896
|
-
raise ValidationFailed, errors.full_messages.join(', ')
|
897
|
-
else
|
898
|
-
raise BeforeHookFailed, "one of the before_#{type} hooks returned false"
|
899
|
-
end
|
900
|
-
end
|
917
|
+
raise BeforeHookFailed, "one of the before_#{type} hooks returned false"
|
901
918
|
end
|
902
919
|
|
903
920
|
# Set the columns, filtered by the only and except arrays.
|
@@ -968,6 +985,13 @@ module Sequel
|
|
968
985
|
set_restricted(hash, only, except)
|
969
986
|
save_changes
|
970
987
|
end
|
988
|
+
|
989
|
+
# Whether to use a transaction for this action. If the :transaction
|
990
|
+
# option is present in the hash, use that, otherwise, fallback to the
|
991
|
+
# object's default (if set), or class's default (if not).
|
992
|
+
def use_transaction?(opts = {})
|
993
|
+
opts.include?(:transaction) ? opts[:transaction] : use_transactions
|
994
|
+
end
|
971
995
|
end
|
972
996
|
|
973
997
|
# Dataset methods are methods that the model class extends its dataset with in
|