sequel 3.13.0 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -0
- data/doc/release_notes/3.14.0.txt +118 -0
- data/lib/sequel/adapters/oracle.rb +7 -2
- data/lib/sequel/adapters/shared/mssql.rb +9 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +3 -3
- data/lib/sequel/database/connecting.rb +47 -11
- data/lib/sequel/database/dataset.rb +17 -6
- data/lib/sequel/database/dataset_defaults.rb +15 -3
- data/lib/sequel/database/logging.rb +4 -3
- data/lib/sequel/database/misc.rb +33 -21
- data/lib/sequel/database/query.rb +61 -22
- data/lib/sequel/database/schema_generator.rb +108 -45
- data/lib/sequel/database/schema_methods.rb +8 -5
- data/lib/sequel/dataset/actions.rb +194 -45
- data/lib/sequel/dataset/features.rb +1 -1
- data/lib/sequel/dataset/graph.rb +51 -43
- data/lib/sequel/dataset/misc.rb +29 -5
- data/lib/sequel/dataset/mutation.rb +0 -1
- data/lib/sequel/dataset/prepared_statements.rb +14 -2
- data/lib/sequel/dataset/query.rb +268 -125
- data/lib/sequel/dataset/sql.rb +33 -44
- data/lib/sequel/extensions/migration.rb +3 -2
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/model/associations.rb +89 -87
- data/lib/sequel/model/base.rb +386 -109
- data/lib/sequel/model/errors.rb +15 -1
- data/lib/sequel/model/exceptions.rb +3 -3
- data/lib/sequel/model/inflections.rb +2 -2
- data/lib/sequel/model/plugins.rb +9 -5
- data/lib/sequel/plugins/rcte_tree.rb +43 -15
- data/lib/sequel/plugins/schema.rb +6 -5
- data/lib/sequel/plugins/serialization.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tree.rb +33 -1
- data/lib/sequel/timezones.rb +16 -10
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +36 -2
- data/spec/adapters/mysql_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +1 -1
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/core/database_spec.rb +8 -1
- data/spec/core/dataset_spec.rb +36 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +40 -8
- data/spec/extensions/schema_spec.rb +5 -0
- data/spec/extensions/serialization_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +7 -0
- data/spec/extensions/tree_spec.rb +36 -0
- data/spec/integration/dataset_test.rb +19 -0
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/spec_helper.rb +4 -4
- data/spec/integration/timezone_test.rb +27 -21
- data/spec/model/associations_spec.rb +5 -5
- data/spec/model/dataset_methods_spec.rb +13 -0
- data/spec/model/hooks_spec.rb +31 -0
- data/spec/model/record_spec.rb +24 -7
- data/spec/model/validations_spec.rb +9 -4
- metadata +6 -4
data/lib/sequel/model/errors.rb
CHANGED
@@ -5,17 +5,24 @@ module Sequel
|
|
5
5
|
class Errors < ::Hash
|
6
6
|
ATTRIBUTE_JOINER = ' and '.freeze
|
7
7
|
|
8
|
-
# Assign an array of messages for each attribute on access
|
8
|
+
# Assign an array of messages for each attribute on access.
|
9
|
+
# Using this message is discouraged in new code, use +add+
|
10
|
+
# to add new error messages, and +on+ to check existing
|
11
|
+
# error messages.
|
9
12
|
def [](k)
|
10
13
|
has_key?(k) ? super : (self[k] = [])
|
11
14
|
end
|
12
15
|
|
13
16
|
# Adds an error for the given attribute.
|
17
|
+
#
|
18
|
+
# errors.add(:name, 'is not valid') if name == 'invalid'
|
14
19
|
def add(att, msg)
|
15
20
|
self[att] << msg
|
16
21
|
end
|
17
22
|
|
18
23
|
# Return the total number of error messages.
|
24
|
+
#
|
25
|
+
# errors.count # => 3
|
19
26
|
def count
|
20
27
|
values.inject(0){|m, v| m + v.length}
|
21
28
|
end
|
@@ -26,6 +33,10 @@ module Sequel
|
|
26
33
|
end
|
27
34
|
|
28
35
|
# Returns an array of fully-formatted error messages.
|
36
|
+
#
|
37
|
+
# errors.full_messages
|
38
|
+
# # => ['name is not valid',
|
39
|
+
# # 'hometown is not at least 2 letters']
|
29
40
|
def full_messages
|
30
41
|
inject([]) do |m, kv|
|
31
42
|
att, errors = *kv
|
@@ -36,6 +47,9 @@ module Sequel
|
|
36
47
|
|
37
48
|
# Returns the array of errors for the given attribute, or nil
|
38
49
|
# if there are no errors for the attribute.
|
50
|
+
#
|
51
|
+
# errors.on(:name) # => ['name is not valid']
|
52
|
+
# errors.on(:id) # => nil
|
39
53
|
def on(att)
|
40
54
|
if v = fetch(att, nil) and !v.empty?
|
41
55
|
v
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Sequel
|
2
|
-
# Exception class raised when raise_on_save_failure is set and a before hook returns false
|
2
|
+
# Exception class raised when +raise_on_save_failure+ is set and a before hook returns false
|
3
3
|
class BeforeHookFailed < Error; end
|
4
4
|
|
5
|
-
# Exception class raised when require_modification is set and an UPDATE or DELETE statement to modify the dataset doesn't
|
5
|
+
# Exception class raised when +require_modification+ is set and an UPDATE or DELETE statement to modify the dataset doesn't
|
6
6
|
# modify a single row.
|
7
7
|
class NoExistingObject < Error; end
|
8
8
|
|
9
|
-
# Exception class raised when raise_on_save_failure is set and validation fails
|
9
|
+
# Exception class raised when +raise_on_save_failure+ is set and validation fails
|
10
10
|
class ValidationFailed < Error
|
11
11
|
def initialize(errors)
|
12
12
|
if errors.respond_to?(:full_messages)
|
@@ -39,10 +39,10 @@ module Sequel
|
|
39
39
|
@plurals, @singulars, @uncountables = [], [], []
|
40
40
|
|
41
41
|
class << self
|
42
|
-
# Array of
|
42
|
+
# Array of two element arrays, first containing a regex, and the second containing a substitution pattern, used for plurization.
|
43
43
|
attr_reader :plurals
|
44
44
|
|
45
|
-
# Array of
|
45
|
+
# Array of two element arrays, first containing a regex, and the second containing a substitution pattern, used for singularization.
|
46
46
|
attr_reader :singulars
|
47
47
|
|
48
48
|
# Array of strings for words were the singular form is the same as the plural form
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -6,7 +6,8 @@ module Sequel
|
|
6
6
|
# * A singleton method named apply, which takes a model,
|
7
7
|
# additional arguments, and an optional block. This is called
|
8
8
|
# the first time the plugin is loaded for this model (unless it was
|
9
|
-
# already loaded by an ancestor class),
|
9
|
+
# already loaded by an ancestor class), before including/extending
|
10
|
+
# any modules, with the arguments
|
10
11
|
# and block provided to the call to Model.plugin.
|
11
12
|
# * A module inside the plugin module named InstanceMethods,
|
12
13
|
# which will be included in the model class.
|
@@ -16,7 +17,8 @@ module Sequel
|
|
16
17
|
# which will extend the model's dataset.
|
17
18
|
# * A singleton method named configure, which takes a model,
|
18
19
|
# additional arguments, and an optional block. This is called
|
19
|
-
# every time the Model.plugin method is called
|
20
|
+
# every time the Model.plugin method is called, after including/extending
|
21
|
+
# any modules.
|
20
22
|
module Plugins
|
21
23
|
end
|
22
24
|
|
@@ -27,7 +29,6 @@ module Sequel
|
|
27
29
|
# sequel_#{plugin}, and then attempt to load the module using a
|
28
30
|
# the camelized plugin name under Sequel::Plugins.
|
29
31
|
def self.plugin(plugin, *args, &blk)
|
30
|
-
arg = args.first
|
31
32
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
32
33
|
unless @plugins.include?(m)
|
33
34
|
@plugins << m
|
@@ -45,13 +46,16 @@ module Sequel
|
|
45
46
|
end
|
46
47
|
|
47
48
|
module ClassMethods
|
48
|
-
# Array of
|
49
|
+
# Array of plugin modules loaded by this class
|
50
|
+
#
|
51
|
+
# Sequel::Model.plugins
|
52
|
+
# # => [Sequel::Model, Sequel::Model::Associations]
|
49
53
|
attr_reader :plugins
|
50
54
|
|
51
55
|
private
|
52
56
|
|
53
57
|
# Returns the module for the specified plugin. If the module is not
|
54
|
-
# defined, the corresponding plugin
|
58
|
+
# defined, the corresponding plugin required.
|
55
59
|
def plugin_module(plugin)
|
56
60
|
module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
57
61
|
if !Sequel::Plugins.const_defined?(module_name) ||
|
@@ -32,9 +32,7 @@ module Sequel
|
|
32
32
|
#
|
33
33
|
# = Usage
|
34
34
|
#
|
35
|
-
# The rcte_tree plugin
|
36
|
-
# instance, or dataset modules. It only has a single apply method, which
|
37
|
-
# adds four associations to the model: parent, children, ancestors, and
|
35
|
+
# The rcte_tree plugin adds four associations to the model: parent, children, ancestors, and
|
38
36
|
# descendants. Both the parent and children are fairly standard many_to_one
|
39
37
|
# and one_to_many associations, respectively. However, the ancestors and
|
40
38
|
# descendants associations are special. Both the ancestors and descendants
|
@@ -118,9 +116,17 @@ module Sequel
|
|
118
116
|
a[:read_only] = true unless a.has_key?(:read_only)
|
119
117
|
a[:eager_loader_key] = key
|
120
118
|
a[:dataset] ||= proc do
|
121
|
-
model.
|
122
|
-
|
123
|
-
|
119
|
+
base_ds = model.filter(prkey=>send(key))
|
120
|
+
recursive_ds = model.join(t, key=>prkey)
|
121
|
+
if c = a[:conditions]
|
122
|
+
(base_ds, recursive_ds) = [base_ds, recursive_ds].collect do |ds|
|
123
|
+
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
127
|
+
model.from(t => table_alias).
|
128
|
+
with_recursive(t, base_ds,
|
129
|
+
recursive_ds.
|
124
130
|
select(c_all))
|
125
131
|
end
|
126
132
|
aal = Array(a[:after_load])
|
@@ -156,12 +162,20 @@ module Sequel
|
|
156
162
|
obj.associations[parent] = nil
|
157
163
|
end
|
158
164
|
r = model.association_reflection(ancestors)
|
165
|
+
base_case = model.filter(prkey=>id_map.keys).
|
166
|
+
select(SQL::AliasedExpression.new(prkey, ka), c_all)
|
167
|
+
recursive_case = model.join(t, key=>prkey).
|
168
|
+
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
169
|
+
if c = r[:conditions]
|
170
|
+
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
171
|
+
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
159
175
|
model.eager_loading_dataset(r,
|
160
|
-
model.from(t).
|
161
|
-
with_recursive(t,
|
162
|
-
|
163
|
-
model.join(t, key=>prkey).
|
164
|
-
select(SQL::QualifiedIdentifier.new(t, ka), c_all)),
|
176
|
+
model.from(t => table_alias).
|
177
|
+
with_recursive(t, base_case,
|
178
|
+
recursive_case),
|
165
179
|
r.select,
|
166
180
|
eo[:associations], eo).all do |obj|
|
167
181
|
opk = obj[prkey]
|
@@ -197,9 +211,17 @@ module Sequel
|
|
197
211
|
d[:read_only] = true unless d.has_key?(:read_only)
|
198
212
|
la = d[:level_alias] ||= :x_level_x
|
199
213
|
d[:dataset] ||= proc do
|
200
|
-
model.
|
201
|
-
|
202
|
-
|
214
|
+
base_ds = model.filter(key=>send(prkey))
|
215
|
+
recursive_ds = model.join(t, prkey=>key)
|
216
|
+
if c = d[:conditions]
|
217
|
+
(base_ds, recursive_ds) = [base_ds, recursive_ds].collect do |ds|
|
218
|
+
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
222
|
+
model.from(t => table_alias).
|
223
|
+
with_recursive(t, base_ds,
|
224
|
+
recursive_ds.
|
203
225
|
select(SQL::ColumnAll.new(model.table_name)))
|
204
226
|
end
|
205
227
|
dal = Array(d[:after_load])
|
@@ -238,6 +260,11 @@ module Sequel
|
|
238
260
|
select(SQL::AliasedExpression.new(key, ka), c_all)
|
239
261
|
recursive_case = model.join(t, prkey=>key).
|
240
262
|
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
263
|
+
if c = r[:conditions]
|
264
|
+
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
265
|
+
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
266
|
+
end
|
267
|
+
end
|
241
268
|
if associations.is_a?(Integer)
|
242
269
|
level = associations
|
243
270
|
no_cache_level = level - 1
|
@@ -245,8 +272,9 @@ module Sequel
|
|
245
272
|
base_case = base_case.select_more(SQL::AliasedExpression.new(0, la))
|
246
273
|
recursive_case = recursive_case.select_more(SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(t, la) + 1, la)).filter(SQL::QualifiedIdentifier.new(t, la) < level - 1)
|
247
274
|
end
|
275
|
+
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
248
276
|
model.eager_loading_dataset(r,
|
249
|
-
model.from(t).with_recursive(t, base_case, recursive_case),
|
277
|
+
model.from(t => table_alias).with_recursive(t, base_case, recursive_case),
|
250
278
|
r.select,
|
251
279
|
associations, eo).all do |obj|
|
252
280
|
if level
|
@@ -19,7 +19,8 @@ module Sequel
|
|
19
19
|
module Schema
|
20
20
|
module ClassMethods
|
21
21
|
# Creates table, using the column information from set_schema.
|
22
|
-
def create_table
|
22
|
+
def create_table(*args, &block)
|
23
|
+
set_schema(*args, &block) if block_given?
|
23
24
|
db.create_table(table_name, :generator=>@schema)
|
24
25
|
@db_schema = get_db_schema(true)
|
25
26
|
columns
|
@@ -27,14 +28,14 @@ module Sequel
|
|
27
28
|
|
28
29
|
# Drops the table if it exists and then runs create_table. Should probably
|
29
30
|
# not be used except in testing.
|
30
|
-
def create_table!
|
31
|
+
def create_table!(*args, &block)
|
31
32
|
drop_table rescue nil
|
32
|
-
create_table
|
33
|
+
create_table(*args, &block)
|
33
34
|
end
|
34
35
|
|
35
36
|
# Creates the table unless the table already exists
|
36
|
-
def create_table?
|
37
|
-
create_table unless table_exists?
|
37
|
+
def create_table?(*args, &block)
|
38
|
+
create_table(*args, &block) unless table_exists?
|
38
39
|
end
|
39
40
|
|
40
41
|
# Drops table.
|
@@ -157,7 +157,7 @@ module Sequel
|
|
157
157
|
module InstanceMethods
|
158
158
|
# Set the sti_key column based on the sti_key_map.
|
159
159
|
def before_create
|
160
|
-
send("#{model.sti_key}=", model.sti_key_map[model]) unless
|
160
|
+
send("#{model.sti_key}=", model.sti_key_map[model]) unless self[model.sti_key]
|
161
161
|
super
|
162
162
|
end
|
163
163
|
end
|
data/lib/sequel/plugins/tree.rb
CHANGED
@@ -8,6 +8,9 @@ module Sequel
|
|
8
8
|
#
|
9
9
|
# Optionally, a column to control order of nodes returned can be specified
|
10
10
|
# by passing column name via :order.
|
11
|
+
#
|
12
|
+
# If you pass true for the :single_root option, the class will ensure there is
|
13
|
+
# only ever one root in the tree.
|
11
14
|
#
|
12
15
|
# Examples:
|
13
16
|
#
|
@@ -40,6 +43,8 @@ module Sequel
|
|
40
43
|
chi = opts.merge(opts.fetch(:children, {}))
|
41
44
|
children = chi.fetch(:name, :children)
|
42
45
|
model.one_to_many children, chi
|
46
|
+
|
47
|
+
model.plugin SingleRoot if opts[:single_root]
|
43
48
|
end
|
44
49
|
|
45
50
|
module ClassMethods
|
@@ -88,7 +93,7 @@ module Sequel
|
|
88
93
|
#
|
89
94
|
# subchild1.ancestors # => [child1, root]
|
90
95
|
def descendants
|
91
|
-
nodes =
|
96
|
+
nodes = children.dup
|
92
97
|
nodes.each{|child| nodes.concat(child.descendants)}
|
93
98
|
nodes
|
94
99
|
end
|
@@ -99,6 +104,11 @@ module Sequel
|
|
99
104
|
ancestors.last || self
|
100
105
|
end
|
101
106
|
|
107
|
+
# Returns true if this is a root node, false otherwise.
|
108
|
+
def root?
|
109
|
+
!new? && self[model.parent_column].nil?
|
110
|
+
end
|
111
|
+
|
102
112
|
# Returns all siblings and a reference to the current node.
|
103
113
|
#
|
104
114
|
# subchild1.self_and_siblings # => [subchild1, subchild2]
|
@@ -113,6 +123,28 @@ module Sequel
|
|
113
123
|
self_and_siblings - [self]
|
114
124
|
end
|
115
125
|
end
|
126
|
+
|
127
|
+
# Plugin included when :single_root option is passed
|
128
|
+
module SingleRoot
|
129
|
+
module ClassMethods
|
130
|
+
# Returns the single root node.
|
131
|
+
def root
|
132
|
+
roots_dataset.first
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module InstanceMethods
|
137
|
+
# Hook that prevents a second root from being created.
|
138
|
+
def before_save
|
139
|
+
if self[model.parent_column].nil? && (root = model.root) && pk != root.pk
|
140
|
+
raise TreeMultipleRootError, "there is already a root #{model.name} defined"
|
141
|
+
end
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class TreeMultipleRootError < Error; end
|
116
148
|
end
|
117
149
|
end
|
118
150
|
end
|
data/lib/sequel/timezones.rb
CHANGED
@@ -1,10 +1,4 @@
|
|
1
1
|
module Sequel
|
2
|
-
# The offset of the current time zone from UTC, in seconds.
|
3
|
-
LOCAL_DATETIME_OFFSET_SECS = Time.now.utc_offset
|
4
|
-
|
5
|
-
# The offset of the current time zone from UTC, as a fraction of a day.
|
6
|
-
LOCAL_DATETIME_OFFSET = respond_to?(:Rational, true) ? Rational(LOCAL_DATETIME_OFFSET_SECS, 60*60*24) : LOCAL_DATETIME_OFFSET_SECS/60/60/24.0
|
7
|
-
|
8
2
|
@application_timezone = nil
|
9
3
|
@database_timezone = nil
|
10
4
|
@typecast_timezone = nil
|
@@ -75,7 +69,8 @@ module Sequel
|
|
75
69
|
when :utc, nil
|
76
70
|
v # DateTime assumes UTC if no offset is given
|
77
71
|
when :local
|
78
|
-
v
|
72
|
+
offset = local_offset_for_datetime(v)
|
73
|
+
v.new_offset(offset) - offset
|
79
74
|
else
|
80
75
|
convert_input_datetime_other(v, input_timezone)
|
81
76
|
end
|
@@ -103,7 +98,7 @@ module Sequel
|
|
103
98
|
v2 = convert_input_datetime_no_offset(v2, input_timezone)
|
104
99
|
else
|
105
100
|
# Time assumes local time if no offset is given
|
106
|
-
v2 = v2.getutc +
|
101
|
+
v2 = v2.getutc + v2.utc_offset if input_timezone == :utc
|
107
102
|
end
|
108
103
|
v2
|
109
104
|
end
|
@@ -132,7 +127,7 @@ module Sequel
|
|
132
127
|
raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
|
133
128
|
end
|
134
129
|
end
|
135
|
-
|
130
|
+
|
136
131
|
# Convert the given +DateTime+ to the given output_timezone that is not supported
|
137
132
|
# by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
|
138
133
|
# Can be overridden in extensions.
|
@@ -148,7 +143,7 @@ module Sequel
|
|
148
143
|
when :utc
|
149
144
|
v.new_offset(0)
|
150
145
|
when :local
|
151
|
-
v.new_offset(
|
146
|
+
v.new_offset(local_offset_for_datetime(v))
|
152
147
|
else
|
153
148
|
convert_output_datetime_other(v, output_timezone)
|
154
149
|
end
|
@@ -178,6 +173,17 @@ module Sequel
|
|
178
173
|
def convert_timezone_setter_arg(tz)
|
179
174
|
tz
|
180
175
|
end
|
176
|
+
|
177
|
+
# Takes a DateTime dt, and returns the correct local offset for that dt, daylight savings included.
|
178
|
+
def local_offset_for_datetime(dt)
|
179
|
+
time_offset_to_datetime_offset Time.local(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec).utc_offset
|
180
|
+
end
|
181
|
+
|
182
|
+
# Caches offset conversions to avoid excess Rational math.
|
183
|
+
def time_offset_to_datetime_offset(offset_secs)
|
184
|
+
@local_offsets ||= {}
|
185
|
+
@local_offsets[offset_secs] ||= respond_to?(:Rational, true) ? Rational(offset_secs, 60*60*24) : offset_secs/60/60/24.0
|
186
|
+
end
|
181
187
|
end
|
182
188
|
|
183
189
|
extend Timezones
|
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 = 14
|
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
|
data/spec/adapters/mssql_spec.rb
CHANGED
@@ -63,8 +63,9 @@ context "MSSQL Dataset#join_table" do
|
|
63
63
|
specify "should emulate the USING clause with ON" do
|
64
64
|
MSSQL_DB[:items].join(:categories, [:id]).sql.should ==
|
65
65
|
'SELECT * FROM ITEMS INNER JOIN CATEGORIES ON (CATEGORIES.ID = ITEMS.ID)'
|
66
|
-
|
67
|
-
|
66
|
+
['SELECT * FROM ITEMS INNER JOIN CATEGORIES ON ((CATEGORIES.ID1 = ITEMS.ID1) AND (CATEGORIES.ID2 = ITEMS.ID2))',
|
67
|
+
'SELECT * FROM ITEMS INNER JOIN CATEGORIES ON ((CATEGORIES.ID2 = ITEMS.ID2) AND (CATEGORIES.ID1 = ITEMS.ID1))'].
|
68
|
+
should include(MSSQL_DB[:items].join(:categories, [:id1, :id2]).sql)
|
68
69
|
MSSQL_DB[:items___i].join(:categories___c, [:id]).sql.should ==
|
69
70
|
'SELECT * FROM ITEMS AS I INNER JOIN CATEGORIES AS C ON (C.ID = I.ID)'
|
70
71
|
end
|
@@ -401,3 +402,36 @@ context "A MSSQL database" do
|
|
401
402
|
(s[:max_chars] || s[:column_size]).should == 2
|
402
403
|
end
|
403
404
|
end
|
405
|
+
|
406
|
+
context "MSSQL::Database#rename_table" do
|
407
|
+
specify "should work on non-schema bound tables which need escaping" do
|
408
|
+
MSSQL_DB.quote_identifiers = true
|
409
|
+
MSSQL_DB.create_table! :'foo bar' do
|
410
|
+
text :name
|
411
|
+
end
|
412
|
+
MSSQL_DB.drop_table :baz rescue nil
|
413
|
+
proc { MSSQL_DB.rename_table 'foo bar', 'baz' }.should_not raise_error
|
414
|
+
end
|
415
|
+
|
416
|
+
specify "should workd on schema bound tables" do
|
417
|
+
MSSQL_DB.execute(<<-SQL)
|
418
|
+
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'MY')
|
419
|
+
EXECUTE sp_executesql N'create schema MY'
|
420
|
+
SQL
|
421
|
+
MSSQL_DB.create_table! :MY__foo do
|
422
|
+
text :name
|
423
|
+
end
|
424
|
+
proc { MSSQL_DB.rename_table :MY__foo, :MY__bar }.should_not raise_error
|
425
|
+
proc { MSSQL_DB.rename_table :MY__bar, :foo }.should_not raise_error
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context "MSSQL::Dataset#count" do
|
430
|
+
specify "should work with a distinct query with an order clause" do
|
431
|
+
MSSQL_DB.create_table!(:items){String :name; Integer :value}
|
432
|
+
MSSQL_DB[:items].insert(:name => "name", :value => 1)
|
433
|
+
MSSQL_DB[:items].insert(:name => "name", :value => 1)
|
434
|
+
MSSQL_DB[:items].select(:name, :value).distinct.order(:name).count.should == 1
|
435
|
+
MSSQL_DB[:items].select(:name, :value).group(:name, :value).order(:name).count.should == 1
|
436
|
+
end
|
437
|
+
end
|