sequel 3.6.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|