sequel 3.25.0 → 3.26.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/README.rdoc +3 -3
- data/Rakefile +17 -11
- data/doc/release_notes/3.26.0.txt +88 -0
- data/lib/sequel/adapters/ado.rb +10 -0
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/jdbc.rb +6 -6
- data/lib/sequel/adapters/mysql.rb +8 -2
- data/lib/sequel/adapters/mysql2.rb +5 -1
- data/lib/sequel/adapters/odbc.rb +10 -2
- data/lib/sequel/adapters/oracle.rb +5 -1
- data/lib/sequel/adapters/postgres.rb +10 -4
- data/lib/sequel/adapters/shared/access.rb +11 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +0 -12
- data/lib/sequel/adapters/tinytds.rb +9 -0
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +3 -3
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +23 -6
- data/lib/sequel/database/query.rb +16 -15
- data/lib/sequel/database/schema_methods.rb +21 -16
- data/lib/sequel/dataset/actions.rb +19 -16
- data/lib/sequel/dataset/features.rb +8 -2
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +29 -9
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +11 -11
- data/lib/sequel/dataset/query.rb +28 -7
- data/lib/sequel/dataset/sql.rb +2 -2
- data/lib/sequel/extensions/migration.rb +1 -0
- data/lib/sequel/model.rb +5 -4
- data/lib/sequel/model/associations.rb +487 -328
- data/lib/sequel/model/base.rb +43 -26
- data/lib/sequel/model/exceptions.rb +2 -0
- data/lib/sequel/plugins/identity_map.rb +111 -4
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/xml_serializer.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +0 -6
- data/spec/core/connection_pool_spec.rb +6 -0
- data/spec/core/database_spec.rb +12 -0
- data/spec/core/schema_spec.rb +9 -2
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +3 -19
- data/spec/extensions/xml_serializer_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +7 -21
- data/spec/model/record_spec.rb +23 -0
- metadata +36 -34
data/lib/sequel/model/base.rb
CHANGED
@@ -11,7 +11,7 @@ module Sequel
|
|
11
11
|
# the Model's dataset with the method of the same name with the given arguments.
|
12
12
|
module ClassMethods
|
13
13
|
# Which columns should be the only columns allowed in a call to a mass assignment method (e.g. set)
|
14
|
-
# (default: not set, so all columns not otherwise restricted).
|
14
|
+
# (default: not set, so all columns not otherwise restricted are allowed).
|
15
15
|
attr_reader :allowed_columns
|
16
16
|
|
17
17
|
# Array of modules that extend this model's dataset. Stored
|
@@ -390,8 +390,8 @@ module Sequel
|
|
390
390
|
# setter methods (methods that end in =) that you want to be used during
|
391
391
|
# mass assignment, they need to be listed here as well (without the =).
|
392
392
|
#
|
393
|
-
# It may be better to use a method such as +set_only+
|
394
|
-
#
|
393
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
394
|
+
# the allowed fields per call.
|
395
395
|
#
|
396
396
|
# Artist.set_allowed_columns(:name, :hometown)
|
397
397
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
@@ -445,7 +445,8 @@ module Sequel
|
|
445
445
|
|
446
446
|
# Sets the primary key for this model. You can use either a regular
|
447
447
|
# or a composite primary key. To not use a primary key, set to nil
|
448
|
-
# or use +no_primary_key+.
|
448
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
449
|
+
# determine the primary key to use, so this method is not needed often.
|
449
450
|
#
|
450
451
|
# class Person < Sequel::Model
|
451
452
|
# # regular key
|
@@ -469,10 +470,9 @@ module Sequel
|
|
469
470
|
# If you have any virtual setter methods (methods that end in =) that you
|
470
471
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
471
472
|
#
|
472
|
-
# It
|
473
|
-
#
|
474
|
-
#
|
475
|
-
# method uses, where everything is allowed other than what you restrict.
|
473
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
474
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
475
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
476
476
|
#
|
477
477
|
# Artist.set_restricted_column(:records_sold)
|
478
478
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
@@ -512,6 +512,9 @@ module Sequel
|
|
512
512
|
# 7 days ago.
|
513
513
|
#
|
514
514
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
515
|
+
#
|
516
|
+
# This method creates dataset methods that do not accept arguments. To create
|
517
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
515
518
|
def subset(name, *args, &block)
|
516
519
|
def_dataset_method(name){filter(*args, &block)}
|
517
520
|
end
|
@@ -527,6 +530,7 @@ module Sequel
|
|
527
530
|
end
|
528
531
|
|
529
532
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
533
|
+
# Using this method can open up security issues, be very careful before using it.
|
530
534
|
#
|
531
535
|
# Artist.set(:id=>1) # Error
|
532
536
|
# Artist.unrestrict_primary_key
|
@@ -777,7 +781,7 @@ module Sequel
|
|
777
781
|
|
778
782
|
# Sets the value for the given column. If typecasting is enabled for
|
779
783
|
# this object, typecast the value based on the column's type.
|
780
|
-
# If this
|
784
|
+
# If this is a new record or the typecasted value isn't the same
|
781
785
|
# as the current value for the column, mark the column as changed.
|
782
786
|
#
|
783
787
|
# a = Artist.new
|
@@ -885,7 +889,7 @@ module Sequel
|
|
885
889
|
|
886
890
|
# Returns true when current instance exists, false otherwise.
|
887
891
|
# Generally an object that isn't new will exist unless it has
|
888
|
-
# been deleted.
|
892
|
+
# been deleted. Uses a database query to check for existence.
|
889
893
|
#
|
890
894
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
891
895
|
# # => true
|
@@ -982,7 +986,7 @@ module Sequel
|
|
982
986
|
end
|
983
987
|
|
984
988
|
# Returns the primary key value identifying the model instance.
|
985
|
-
# Raises an
|
989
|
+
# Raises an +Error+ if this model does not have a primary key.
|
986
990
|
# If the model has a composite primary key, returns an array of values.
|
987
991
|
#
|
988
992
|
# Artist[1].pk # => 1
|
@@ -1032,16 +1036,16 @@ module Sequel
|
|
1032
1036
|
# If it succeeds, it returns self.
|
1033
1037
|
#
|
1034
1038
|
# You can provide an optional list of columns to update, in which
|
1035
|
-
# case it only updates those columns.
|
1039
|
+
# case it only updates those columns, or a options hash.
|
1036
1040
|
#
|
1037
1041
|
# Takes the following options:
|
1038
1042
|
#
|
1039
|
-
#
|
1040
|
-
#
|
1041
|
-
#
|
1042
|
-
#
|
1043
|
-
#
|
1044
|
-
#
|
1043
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
1044
|
+
# :transaction :: set to true or false to override the current
|
1045
|
+
# +use_transactions+ setting
|
1046
|
+
# :validate :: set to false to skip validation
|
1047
|
+
# :raise_on_failure :: set to true or false to override the current
|
1048
|
+
# +raise_on_save_failure+ setting
|
1045
1049
|
def save(*columns)
|
1046
1050
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
1047
1051
|
if opts[:validate] != false
|
@@ -1088,7 +1092,8 @@ module Sequel
|
|
1088
1092
|
end
|
1089
1093
|
|
1090
1094
|
# Set all values using the entries in the hash, except for the keys
|
1091
|
-
# given in except.
|
1095
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
1096
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
1092
1097
|
#
|
1093
1098
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
1094
1099
|
# artist.name # => 'Jim'
|
@@ -1111,12 +1116,13 @@ module Sequel
|
|
1111
1116
|
end
|
1112
1117
|
|
1113
1118
|
# Set the values using the entries in the hash, only if the key
|
1114
|
-
# is included in only.
|
1119
|
+
# is included in only. It may be a better idea to use +set_fields+
|
1120
|
+
# instead of this method.
|
1115
1121
|
#
|
1116
1122
|
# artist.set_only({:name=>'Jim'}, :name)
|
1117
1123
|
# artist.name # => 'Jim'
|
1118
1124
|
#
|
1119
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
1125
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
1120
1126
|
def set_only(hash, *only)
|
1121
1127
|
set_restricted(hash, only.flatten, false)
|
1122
1128
|
end
|
@@ -1135,7 +1141,7 @@ module Sequel
|
|
1135
1141
|
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
1136
1142
|
end
|
1137
1143
|
|
1138
|
-
# Runs set with the passed hash and then runs save_changes.
|
1144
|
+
# Runs #set with the passed hash and then runs save_changes.
|
1139
1145
|
#
|
1140
1146
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1141
1147
|
def update(hash)
|
@@ -1143,7 +1149,7 @@ module Sequel
|
|
1143
1149
|
end
|
1144
1150
|
|
1145
1151
|
# Update all values using the entries in the hash, ignoring any setting of
|
1146
|
-
# allowed_columns or
|
1152
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
1147
1153
|
#
|
1148
1154
|
# Artist.set_restricted_columns(:name)
|
1149
1155
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
@@ -1152,7 +1158,8 @@ module Sequel
|
|
1152
1158
|
end
|
1153
1159
|
|
1154
1160
|
# Update all values using the entries in the hash, except for the keys
|
1155
|
-
# given in except.
|
1161
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
1162
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
1156
1163
|
#
|
1157
1164
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1158
1165
|
def update_except(hash, *except)
|
@@ -1173,7 +1180,8 @@ module Sequel
|
|
1173
1180
|
end
|
1174
1181
|
|
1175
1182
|
# Update the values using the entries in the hash, only if the key
|
1176
|
-
# is included in only.
|
1183
|
+
# is included in only. It may be a better idea to use +update_fields+
|
1184
|
+
# instead of this method.
|
1177
1185
|
#
|
1178
1186
|
# artist.update_only({:name=>'Jim'}, :name)
|
1179
1187
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
@@ -1488,7 +1496,16 @@ module Sequel
|
|
1488
1496
|
if meths.include?(m)
|
1489
1497
|
send(m, v)
|
1490
1498
|
elsif strict
|
1491
|
-
|
1499
|
+
# Avoid using respond_to? or creating symbols from user input
|
1500
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
1501
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
1502
|
+
raise Error, "#{k} is a restricted primary key"
|
1503
|
+
else
|
1504
|
+
raise Error, "#{k} is a restricted column"
|
1505
|
+
end
|
1506
|
+
else
|
1507
|
+
raise Error, "method #{m} doesn't exist"
|
1508
|
+
end
|
1492
1509
|
end
|
1493
1510
|
end
|
1494
1511
|
self
|
@@ -2,6 +2,8 @@ module Sequel
|
|
2
2
|
# Exception class raised when +raise_on_save_failure+ is set and a before hook returns false
|
3
3
|
# or an around hook doesn't call super or yield.
|
4
4
|
class HookFailed < Error; end
|
5
|
+
|
6
|
+
# Deprecated alias for HookFailed, kept for backwards compatibility
|
5
7
|
BeforeHookFailed = HookFailed
|
6
8
|
|
7
9
|
# Exception class raised when +require_modification+ is set and an UPDATE or DELETE statement to modify the dataset doesn't
|
@@ -22,10 +22,7 @@ module Sequel
|
|
22
22
|
# Identity maps are thread-local and only persist for the duration of the block,
|
23
23
|
# so they should only be considered as a possible performance enhancer.
|
24
24
|
#
|
25
|
-
# The identity_map plugin is not compatible with the
|
26
|
-
# many_to_many and many_through_many associations. If you want to use the identity_map plugin,
|
27
|
-
# you should use +eager_graph+ instead of +eager+ for those associations. It is also
|
28
|
-
# not compatible with the eager loading in the +rcte_tree+ plugin.
|
25
|
+
# The identity_map plugin is not compatible with the eager loading in the +rcte_tree+ plugin.
|
29
26
|
#
|
30
27
|
# Usage:
|
31
28
|
#
|
@@ -37,6 +34,116 @@ module Sequel
|
|
37
34
|
# # would need to do Album.with_identity_map{} to use the identity map
|
38
35
|
module IdentityMap
|
39
36
|
module ClassMethods
|
37
|
+
# Override the default :eager_loader option for many_*_many associations to work
|
38
|
+
# with an identity_map. If the :eager_graph association option is used, you'll probably have to use
|
39
|
+
# :uniq=>true on the current association amd :cartesian_product_number=>2 on the association
|
40
|
+
# mentioned by :eager_graph, otherwise you'll end up with duplicates because the row proc will be
|
41
|
+
# getting called multiple times for the same object. If you do have duplicates and you use :eager_graph,
|
42
|
+
# they'll probably be lost. Making that work correctly would require changing a lot of the core
|
43
|
+
# architecture, such as how graphing and eager graphing work.
|
44
|
+
def associate(type, name, opts = {}, &block)
|
45
|
+
if opts[:eager_loader]
|
46
|
+
super
|
47
|
+
elsif type == :many_to_many
|
48
|
+
opts = super
|
49
|
+
el = opts[:eager_loader]
|
50
|
+
model = self
|
51
|
+
left_pk = opts[:left_primary_key]
|
52
|
+
uses_lcks = opts[:uses_left_composite_keys]
|
53
|
+
uses_rcks = opts[:uses_right_composite_keys]
|
54
|
+
right = opts[:right_key]
|
55
|
+
join_table = opts[:join_table]
|
56
|
+
left = opts[:left_key]
|
57
|
+
lcks = opts[:left_keys]
|
58
|
+
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
59
|
+
opts[:eager_loader] = lambda do |eo|
|
60
|
+
return el.call(eo) unless model.identity_map
|
61
|
+
h = eo[:key_hash][left_pk]
|
62
|
+
eo[:rows].each{|object| object.associations[name] = []}
|
63
|
+
r = uses_rcks ? rcks.zip(opts.right_primary_keys) : [[right, opts.right_primary_key]]
|
64
|
+
l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)}, h.keys]] : [[left, h.keys]]
|
65
|
+
|
66
|
+
# Replace the row proc to remove the left key alias before calling the previous row proc.
|
67
|
+
# Associate the value of the left key alias with the associated object (through its object_id).
|
68
|
+
# When loading the associated objects, lookup the left key alias value and associate the
|
69
|
+
# associated objects to the main objects if the left key alias value matches the left primary key
|
70
|
+
# value of the main object.
|
71
|
+
#
|
72
|
+
# The deleting of the left key alias from the hash before calling the previous row proc
|
73
|
+
# is necessary when an identity map is used, otherwise if the same associated object is returned more than
|
74
|
+
# once for the association, it won't know which of current objects to associate it to.
|
75
|
+
ds = opts.associated_class.inner_join(join_table, r + l)
|
76
|
+
pr = ds.row_proc
|
77
|
+
h2 = {}
|
78
|
+
ds.row_proc = proc do |hash|
|
79
|
+
hash_key = if uses_lcks
|
80
|
+
left_key_alias.map{|k| hash.delete(k)}
|
81
|
+
else
|
82
|
+
hash.delete(left_key_alias)
|
83
|
+
end
|
84
|
+
obj = pr.call(hash)
|
85
|
+
(h2[obj.object_id] ||= []) << hash_key
|
86
|
+
obj
|
87
|
+
end
|
88
|
+
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo) .all do |assoc_record|
|
89
|
+
if hash_keys = h2.delete(assoc_record.object_id)
|
90
|
+
hash_keys.each do |hash_key|
|
91
|
+
if objects = h[hash_key]
|
92
|
+
objects.each{|object| object.associations[name].push(assoc_record)}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
opts
|
99
|
+
elsif type == :many_through_many
|
100
|
+
opts = super
|
101
|
+
el = opts[:eager_loader]
|
102
|
+
model = self
|
103
|
+
left_pk = opts[:left_primary_key]
|
104
|
+
left_key = opts[:left_key]
|
105
|
+
uses_lcks = opts[:uses_left_composite_keys]
|
106
|
+
left_keys = Array(left_key)
|
107
|
+
left_key_alias = opts[:left_key_alias]
|
108
|
+
opts[:eager_loader] = lambda do |eo|
|
109
|
+
return el.call(eo) unless model.identity_map
|
110
|
+
h = eo[:key_hash][left_pk]
|
111
|
+
eo[:rows].each{|object| object.associations[name] = []}
|
112
|
+
ds = opts.associated_class
|
113
|
+
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
114
|
+
ft = opts[:final_reverse_edge]
|
115
|
+
conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, h.keys]] : [[left_key, h.keys]]
|
116
|
+
|
117
|
+
# See above comment in many_to_many eager_loader
|
118
|
+
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
|
119
|
+
pr = ds.row_proc
|
120
|
+
h2 = {}
|
121
|
+
ds.row_proc = proc do |hash|
|
122
|
+
hash_key = if uses_lcks
|
123
|
+
left_key_alias.map{|k| hash.delete(k)}
|
124
|
+
else
|
125
|
+
hash.delete(left_key_alias)
|
126
|
+
end
|
127
|
+
obj = pr.call(hash)
|
128
|
+
(h2[obj.object_id] ||= []) << hash_key
|
129
|
+
obj
|
130
|
+
end
|
131
|
+
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo).all do |assoc_record|
|
132
|
+
if hash_keys = h2.delete(assoc_record.object_id)
|
133
|
+
hash_keys.each do |hash_key|
|
134
|
+
if objects = h[hash_key]
|
135
|
+
objects.each{|object| object.associations[name].push(assoc_record)}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
opts
|
142
|
+
else
|
143
|
+
super
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
40
147
|
# Returns the current thread-local identity map. Should be a hash if
|
41
148
|
# there is an active identity map, and nil otherwise.
|
42
149
|
def identity_map
|
@@ -40,6 +40,18 @@ module Sequel
|
|
40
40
|
def new_using_server(s, values={}, &block)
|
41
41
|
new(values, &block).set_server(s)
|
42
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Set the server for each graphed dataset to the current server
|
47
|
+
# unless the graphed dataset already has a server set.
|
48
|
+
def eager_graph_dataset(opts, eager_options)
|
49
|
+
ds = super
|
50
|
+
if s = eager_options[:self].opts[:server]
|
51
|
+
ds = ds.server(s) unless ds.opts[:server]
|
52
|
+
end
|
53
|
+
ds
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
57
|
module InstanceMethods
|
@@ -109,26 +121,6 @@ module Sequel
|
|
109
121
|
end
|
110
122
|
ds
|
111
123
|
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
# Set the shard of all retrieved objects to the shard of
|
116
|
-
# the table's dataset, or the current shard if the table's
|
117
|
-
# dataset does not have a shard.
|
118
|
-
def graph_each
|
119
|
-
ta = @opts[:graph][:table_aliases]
|
120
|
-
s = @opts[:server]
|
121
|
-
super do |r|
|
122
|
-
r.each do |k, v|
|
123
|
-
if ds = ta[k]
|
124
|
-
dss = ds.opts[:server]
|
125
|
-
end
|
126
|
-
vs = dss || s
|
127
|
-
v.set_server(vs) if vs && v.respond_to?(:set_server)
|
128
|
-
end
|
129
|
-
yield r
|
130
|
-
end
|
131
|
-
end
|
132
124
|
end
|
133
125
|
end
|
134
126
|
end
|
@@ -205,9 +205,9 @@ module Sequel
|
|
205
205
|
klass.from_xml_node(node)
|
206
206
|
end
|
207
207
|
elsif cols.include?(k)
|
208
|
-
self[k.to_sym] = node
|
208
|
+
self[k.to_sym] = node.key?('nil') ? nil : node.children.first.to_s
|
209
209
|
elsif meths.include?("#{k}=")
|
210
|
-
send("#{k}=", node
|
210
|
+
send("#{k}=", node.key?('nil') ? nil : node.children.first.to_s)
|
211
211
|
else
|
212
212
|
raise Error, "Entry in XML not an association or column and no setter method exists: #{k}"
|
213
213
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 3
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 26
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
@@ -606,12 +606,6 @@ describe "Postgres::Database schema qualified tables" do
|
|
606
606
|
POSTGRES_DB.drop_table(:domains)
|
607
607
|
end
|
608
608
|
|
609
|
-
specify "#table_exists? should not include tables from the default non-public schemas" do
|
610
|
-
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
611
|
-
POSTGRES_DB.table_exists?(:schema_test).should == true
|
612
|
-
POSTGRES_DB.table_exists?(:domain_udt_usage).should == false
|
613
|
-
end
|
614
|
-
|
615
609
|
specify "#table_exists? should see if the table is in a given schema" do
|
616
610
|
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
617
611
|
POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
|
@@ -245,6 +245,12 @@ shared_examples_for "A threaded connection pool" do
|
|
245
245
|
t.join
|
246
246
|
end
|
247
247
|
|
248
|
+
it "should not add a disconnected connection back to the pool if the disconnection_proc raises an error" do
|
249
|
+
pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:max_connections=>1, :pool_timeout=>0, :disconnection_proc=>proc{|c| raise Sequel::Error})) {@invoked_count += 1}
|
250
|
+
proc{pool.hold{raise Sequel::DatabaseDisconnectError}}.should raise_error(Sequel::Error)
|
251
|
+
pool.available_connections.length.should == 0
|
252
|
+
end
|
253
|
+
|
248
254
|
specify "should let five threads simultaneously access separate connections" do
|
249
255
|
cc = {}
|
250
256
|
threads = []
|
data/spec/core/database_spec.rb
CHANGED
@@ -1332,6 +1332,18 @@ describe "Database#typecast_value" do
|
|
1332
1332
|
proc{@db.typecast_value(:datetime, 4)}.should raise_error(Sequel::InvalidValue)
|
1333
1333
|
end
|
1334
1334
|
|
1335
|
+
specify "should handle integers with leading 0 as base 10" do
|
1336
|
+
@db.typecast_value(:integer, "013").should == 13
|
1337
|
+
@db.typecast_value(:integer, "08").should == 8
|
1338
|
+
@db.typecast_value(:integer, "000013").should == 13
|
1339
|
+
@db.typecast_value(:integer, "000008").should == 8
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
specify "should handle integers with leading 0x as base 16" do
|
1343
|
+
@db.typecast_value(:integer, "0x013").should == 19
|
1344
|
+
@db.typecast_value(:integer, "0x80").should == 128
|
1345
|
+
end
|
1346
|
+
|
1335
1347
|
specify "should have an underlying exception class available at wrapped_exception" do
|
1336
1348
|
begin
|
1337
1349
|
@db.typecast_value(:date, 'a')
|
data/spec/core/schema_spec.rb
CHANGED
@@ -555,8 +555,15 @@ describe "DB#create_table!" do
|
|
555
555
|
@db = SchemaDummyDatabase.new
|
556
556
|
end
|
557
557
|
|
558
|
-
specify "should
|
559
|
-
@db.
|
558
|
+
specify "should create the table if it does not exist" do
|
559
|
+
@db.meta_def(:table_exists?){|a| false}
|
560
|
+
@db.create_table!(:cats){|*a|}
|
561
|
+
@db.sqls.should == ['CREATE TABLE cats ()']
|
562
|
+
end
|
563
|
+
|
564
|
+
specify "should drop the table before creating it if it already exists" do
|
565
|
+
@db.meta_def(:table_exists?){|a| true}
|
566
|
+
@db.create_table!(:cats){|*a|}
|
560
567
|
@db.sqls.should == ['DROP TABLE cats', 'CREATE TABLE cats ()']
|
561
568
|
end
|
562
569
|
end
|