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