sequel 3.38.0 → 3.39.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 +62 -0
- data/README.rdoc +2 -2
- data/bin/sequel +12 -2
- data/doc/advanced_associations.rdoc +1 -1
- data/doc/association_basics.rdoc +13 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +4 -4
- data/lib/sequel/adapters/jdbc/derby.rb +1 -0
- data/lib/sequel/adapters/mock.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +8 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/postgres.rb +72 -8
- data/lib/sequel/adapters/shared/db2.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +57 -0
- data/lib/sequel/adapters/shared/mysql.rb +95 -19
- data/lib/sequel/adapters/shared/oracle.rb +14 -0
- data/lib/sequel/adapters/shared/postgres.rb +63 -24
- data/lib/sequel/adapters/shared/sqlite.rb +6 -9
- data/lib/sequel/connection_pool/sharded_threaded.rb +8 -3
- data/lib/sequel/connection_pool/threaded.rb +9 -4
- data/lib/sequel/database/query.rb +60 -48
- data/lib/sequel/database/schema_generator.rb +13 -6
- data/lib/sequel/database/schema_methods.rb +65 -12
- data/lib/sequel/dataset/actions.rb +22 -4
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/graph.rb +2 -3
- data/lib/sequel/dataset/misc.rb +2 -2
- data/lib/sequel/dataset/query.rb +0 -2
- data/lib/sequel/dataset/sql.rb +33 -12
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/extensions/eval_inspect.rb +17 -2
- data/lib/sequel/extensions/pg_array_ops.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -2
- data/lib/sequel/extensions/pg_row_ops.rb +18 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -11
- data/lib/sequel/model/associations.rb +3 -2
- data/lib/sequel/model/base.rb +57 -13
- data/lib/sequel/model/exceptions.rb +20 -2
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/defaults_setter.rb +15 -1
- data/lib/sequel/plugins/dirty.rb +2 -2
- data/lib/sequel/plugins/identity_map.rb +12 -8
- data/lib/sequel/plugins/subclasses.rb +19 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/validation_helpers.rb +24 -4
- data/lib/sequel/sql.rb +64 -24
- data/lib/sequel/timezones.rb +10 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +26 -25
- data/spec/adapters/mysql_spec.rb +57 -23
- data/spec/adapters/oracle_spec.rb +34 -49
- data/spec/adapters/postgres_spec.rb +226 -128
- data/spec/adapters/sqlite_spec.rb +50 -49
- data/spec/core/connection_pool_spec.rb +22 -0
- data/spec/core/database_spec.rb +53 -47
- data/spec/core/dataset_spec.rb +36 -32
- data/spec/core/expression_filters_spec.rb +14 -2
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +0 -13
- data/spec/core/schema_spec.rb +64 -5
- data/spec/core_extensions_spec.rb +1 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/defaults_setter_spec.rb +24 -0
- data/spec/extensions/eval_inspect_spec.rb +9 -0
- data/spec/extensions/identity_map_spec.rb +11 -2
- data/spec/extensions/pg_array_ops_spec.rb +9 -0
- data/spec/extensions/pg_row_ops_spec.rb +11 -1
- data/spec/extensions/pg_row_plugin_spec.rb +4 -0
- data/spec/extensions/schema_dumper_spec.rb +8 -5
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/validation_helpers_spec.rb +15 -2
- data/spec/integration/dataset_test.rb +75 -1
- data/spec/integration/plugin_test.rb +146 -0
- data/spec/integration/schema_test.rb +34 -0
- data/spec/model/dataset_methods_spec.rb +38 -0
- data/spec/model/hooks_spec.rb +6 -0
- data/spec/model/validations_spec.rb +27 -2
- metadata +8 -2
|
@@ -37,11 +37,25 @@ module Sequel
|
|
|
37
37
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
|
+
# Parse the cached database schema for this model and set the default values appropriately.
|
|
40
41
|
def set_default_values
|
|
41
42
|
h = {}
|
|
42
|
-
@db_schema.each{|k, v| h[k] = v[:ruby_default]
|
|
43
|
+
@db_schema.each{|k, v| h[k] = convert_default_value(v[:ruby_default]) unless v[:ruby_default].nil?} if @db_schema
|
|
43
44
|
@default_values = h
|
|
44
45
|
end
|
|
46
|
+
|
|
47
|
+
# Handle the CURRENT_DATE and CURRENT_TIMESTAMP values specially by returning an appropriate Date or
|
|
48
|
+
# Time/DateTime value.
|
|
49
|
+
def convert_default_value(v)
|
|
50
|
+
case v
|
|
51
|
+
when Sequel::CURRENT_DATE
|
|
52
|
+
lambda{Date.today}
|
|
53
|
+
when Sequel::CURRENT_TIMESTAMP
|
|
54
|
+
lambda{Sequel.datetime_class.now}
|
|
55
|
+
else
|
|
56
|
+
v
|
|
57
|
+
end
|
|
58
|
+
end
|
|
45
59
|
end
|
|
46
60
|
|
|
47
61
|
module InstanceMethods
|
data/lib/sequel/plugins/dirty.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Sequel
|
|
|
14
14
|
# artist.name # => 'Foo'
|
|
15
15
|
# artist.column_changed?(:name) # false
|
|
16
16
|
#
|
|
17
|
-
# It
|
|
17
|
+
# It also makes changed_columns more accurate in that it
|
|
18
18
|
# can detect when a the column value is changed and then
|
|
19
19
|
# changed back:
|
|
20
20
|
#
|
|
@@ -183,7 +183,7 @@ module Sequel
|
|
|
183
183
|
|
|
184
184
|
# If the values hash does not contain the column, make sure missing_initial_values
|
|
185
185
|
# does so that it doesn't get deleted from changed_columns if changed back,
|
|
186
|
-
# and so that
|
|
186
|
+
# and so that resetting the column value can be handled correctly.
|
|
187
187
|
def check_missing_initial_value(column)
|
|
188
188
|
unless values.has_key?(column) || (miv = missing_initial_values).include?(column)
|
|
189
189
|
miv << column
|
|
@@ -153,7 +153,8 @@ module Sequel
|
|
|
153
153
|
# The identity map key for an object of the current class with the given pk.
|
|
154
154
|
# May not always be correct for a class which uses STI.
|
|
155
155
|
def identity_map_key(pk)
|
|
156
|
-
|
|
156
|
+
pk = Array(pk)
|
|
157
|
+
"#{self}:#{pk.join(',')}" unless pk.compact.empty?
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
# If the identity map is in use, check it for a current copy of the object.
|
|
@@ -164,12 +165,14 @@ module Sequel
|
|
|
164
165
|
# fields and request other, potentially overlapping fields in a new query,
|
|
165
166
|
# and not have the second query override fields you modified.
|
|
166
167
|
def call(row)
|
|
167
|
-
return super unless idm = identity_map
|
|
168
|
-
if
|
|
168
|
+
return super unless (idm = identity_map) && (pk = primary_key)
|
|
169
|
+
if (k = identity_map_key(Array(pk).map{|x| row[x]})) && (o = idm[k])
|
|
169
170
|
o.merge_db_update(row)
|
|
170
171
|
else
|
|
171
172
|
o = super
|
|
172
|
-
|
|
173
|
+
if (k = identity_map_key(o.pk))
|
|
174
|
+
idm[k] = o
|
|
175
|
+
end
|
|
173
176
|
end
|
|
174
177
|
o
|
|
175
178
|
end
|
|
@@ -196,7 +199,7 @@ module Sequel
|
|
|
196
199
|
# Check the current identity map if it exists for the object with
|
|
197
200
|
# the matching pk. If one is found, return it, otherwise call super.
|
|
198
201
|
def primary_key_lookup(pk)
|
|
199
|
-
(idm = identity_map
|
|
202
|
+
((idm = identity_map) && (k = identity_map_key(pk)) && (o = idm[k])) ? o : super
|
|
200
203
|
end
|
|
201
204
|
end
|
|
202
205
|
|
|
@@ -204,8 +207,8 @@ module Sequel
|
|
|
204
207
|
# Remove instances from the identity map cache if they are deleted.
|
|
205
208
|
def delete
|
|
206
209
|
super
|
|
207
|
-
if idm = model.identity_map
|
|
208
|
-
idm.delete(
|
|
210
|
+
if (idm = model.identity_map) && (k = model.identity_map_key(pk))
|
|
211
|
+
idm.delete(k)
|
|
209
212
|
end
|
|
210
213
|
self
|
|
211
214
|
end
|
|
@@ -239,7 +242,8 @@ module Sequel
|
|
|
239
242
|
if cache_lookup &&
|
|
240
243
|
!dynamic_opts[:callback] &&
|
|
241
244
|
(idm = klass.identity_map) &&
|
|
242
|
-
(
|
|
245
|
+
(k = klass.identity_map_key(_associated_object_pk(opts[:key]))) &&
|
|
246
|
+
(o = idm[k])
|
|
243
247
|
o
|
|
244
248
|
else
|
|
245
249
|
super
|
|
@@ -15,13 +15,27 @@ module Sequel
|
|
|
15
15
|
# sc2.subclasses # []
|
|
16
16
|
# ssc1.subclasses # []
|
|
17
17
|
# c.descendents # [sc1, ssc1, sc2]
|
|
18
|
+
#
|
|
19
|
+
# You can provide a block when loading the plugin, and it will be called
|
|
20
|
+
# with each subclass created:
|
|
21
|
+
#
|
|
22
|
+
# a = []
|
|
23
|
+
# Sequel::Model.plugin(:subclasses){|sc| a << sc}
|
|
24
|
+
# class A < Sequel::Model; end
|
|
25
|
+
# class B < Sequel::Model; end
|
|
26
|
+
# a # => [A, B]
|
|
18
27
|
module Subclasses
|
|
19
28
|
# Initialize the subclasses instance variable for the model.
|
|
20
|
-
def self.apply(model)
|
|
29
|
+
def self.apply(model, &block)
|
|
21
30
|
model.instance_variable_set(:@subclasses, [])
|
|
31
|
+
model.instance_variable_set(:@on_subclass, block)
|
|
22
32
|
end
|
|
23
33
|
|
|
24
34
|
module ClassMethods
|
|
35
|
+
# Callable object that should be called with every descendent
|
|
36
|
+
# class created.
|
|
37
|
+
attr_reader :on_subclass
|
|
38
|
+
|
|
25
39
|
# All subclasses for the current model. Does not
|
|
26
40
|
# include the model itself.
|
|
27
41
|
attr_reader :subclasses
|
|
@@ -38,6 +52,10 @@ module Sequel
|
|
|
38
52
|
super
|
|
39
53
|
Sequel.synchronize{subclasses << subclass}
|
|
40
54
|
subclass.instance_variable_set(:@subclasses, [])
|
|
55
|
+
if on_subclass
|
|
56
|
+
subclass.instance_variable_set(:@on_subclass, on_subclass)
|
|
57
|
+
on_subclass.call(subclass)
|
|
58
|
+
end
|
|
41
59
|
end
|
|
42
60
|
|
|
43
61
|
private
|
data/lib/sequel/plugins/tree.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Sequel
|
|
|
4
4
|
# treat a Model as a tree.
|
|
5
5
|
#
|
|
6
6
|
# A column for holding the parent key is required and is :parent_id by default.
|
|
7
|
-
# This may be overridden by passing column name via :key
|
|
7
|
+
# This may be overridden by passing column name via :key.
|
|
8
8
|
#
|
|
9
9
|
# Optionally, a column to control order of nodes returned can be specified
|
|
10
10
|
# by passing column name via :order.
|
|
@@ -98,7 +98,7 @@ module Sequel
|
|
|
98
98
|
nodes
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
# Returns the root node of the tree that this node descends from
|
|
101
|
+
# Returns the root node of the tree that this node descends from.
|
|
102
102
|
# This node is returned if it is a root node itself.
|
|
103
103
|
def root
|
|
104
104
|
ancestors.last || self
|
|
@@ -124,7 +124,7 @@ module Sequel
|
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
-
# Plugin included when :single_root option is passed
|
|
127
|
+
# Plugin included when :single_root option is passed.
|
|
128
128
|
module SingleRoot
|
|
129
129
|
module ClassMethods
|
|
130
130
|
# Returns the single root node.
|
|
@@ -193,19 +193,39 @@ module Sequel
|
|
|
193
193
|
# since it can deal with a grouping of multiple attributes.
|
|
194
194
|
#
|
|
195
195
|
# Possible Options:
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
#
|
|
196
|
+
# :message :: The message to use (default: 'is already taken')
|
|
197
|
+
# :only_if_modified :: Only check the uniqueness if the object is new or
|
|
198
|
+
# one of the columns has been modified.
|
|
199
|
+
# :where :: A callable object where call takes three arguments, a dataset,
|
|
200
|
+
# the current object, and an array of columns, and should return
|
|
201
|
+
# a modified dataset that is filtered to include only rows with
|
|
202
|
+
# the same values as the current object for each column in the array.
|
|
203
|
+
#
|
|
204
|
+
# If you want to to a case insensitive uniqueness validation on a database that
|
|
205
|
+
# is case sensitive by default, you can use:
|
|
206
|
+
#
|
|
207
|
+
# :where=>(proc do |ds, obj, cols|
|
|
208
|
+
# ds.where(cols.map do |c|
|
|
209
|
+
# v = obj.send(c)
|
|
210
|
+
# v = v.downcase if v
|
|
211
|
+
# [Sequel.function(:lower, c), v]
|
|
212
|
+
# end)
|
|
213
|
+
# end)
|
|
199
214
|
def validates_unique(*atts)
|
|
200
215
|
opts = default_validation_helpers_options(:unique)
|
|
201
216
|
if atts.last.is_a?(Hash)
|
|
202
217
|
opts = opts.merge(atts.pop)
|
|
203
218
|
end
|
|
204
219
|
message = validation_error_message(opts[:message])
|
|
220
|
+
where = opts[:where]
|
|
205
221
|
atts.each do |a|
|
|
206
222
|
arr = Array(a)
|
|
207
223
|
next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
|
|
208
|
-
ds =
|
|
224
|
+
ds = if where
|
|
225
|
+
where.call(model.dataset, self, arr)
|
|
226
|
+
else
|
|
227
|
+
model.where(arr.map{|x| [x, send(x)]})
|
|
228
|
+
end
|
|
209
229
|
ds = yield(ds) if block_given?
|
|
210
230
|
ds = ds.exclude(pk_hash) unless new?
|
|
211
231
|
errors.add(a, message) unless ds.count == 0
|
data/lib/sequel/sql.rb
CHANGED
|
@@ -60,29 +60,38 @@ module Sequel
|
|
|
60
60
|
|
|
61
61
|
# Base class for all SQL expression objects.
|
|
62
62
|
class Expression
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
@comparison_attrs = []
|
|
64
|
+
|
|
65
|
+
class << self
|
|
66
|
+
# All attributes used for equality and hash methods.
|
|
67
|
+
attr_reader :comparison_attrs
|
|
68
|
+
|
|
69
|
+
# Expression objects are assumed to be value objects, where their
|
|
70
|
+
# attribute values can't change after assignment. In order to make
|
|
71
|
+
# it easy to define equality and hash methods, subclass
|
|
72
|
+
# instances assume that the only values that affect the results of
|
|
73
|
+
# such methods are the values of the object's attributes.
|
|
74
|
+
def attr_reader(*args)
|
|
75
|
+
super
|
|
76
|
+
comparison_attrs.concat(args)
|
|
77
|
+
end
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
# Copy the comparison_attrs into the subclass.
|
|
80
|
+
def inherited(subclass)
|
|
81
|
+
super
|
|
82
|
+
subclass.instance_variable_set(:@comparison_attrs, comparison_attrs.dup)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
# Create a to_s instance method that takes a dataset, and calls
|
|
88
|
+
# the method provided on the dataset with args as the argument (self by default).
|
|
89
|
+
# Used to DRY up some code.
|
|
90
|
+
def to_s_method(meth, args=:self) # :nodoc:
|
|
91
|
+
class_eval("def to_s(ds) ds.#{meth}(#{args}) end", __FILE__, __LINE__)
|
|
92
|
+
class_eval("def to_s_append(ds, sql) ds.#{meth}_append(sql, #{args}) end", __FILE__, __LINE__)
|
|
93
|
+
end
|
|
84
94
|
end
|
|
85
|
-
private_class_method :to_s_method
|
|
86
95
|
|
|
87
96
|
# Alias of <tt>eql?</tt>
|
|
88
97
|
def ==(other)
|
|
@@ -140,6 +149,9 @@ module Sequel
|
|
|
140
149
|
# Bitwise mathematical operators used in +NumericMethods+
|
|
141
150
|
BITWISE_OPERATORS = [:&, :|, :^, :<<, :>>, :%]
|
|
142
151
|
|
|
152
|
+
# Operators that check for equality
|
|
153
|
+
EQUALITY_OPERATORS = [:'=', :'!=']
|
|
154
|
+
|
|
143
155
|
# Inequality operators used in +InequalityMethods+
|
|
144
156
|
INEQUALITY_OPERATORS = [:<, :>, :<=, :>=]
|
|
145
157
|
|
|
@@ -152,10 +164,14 @@ module Sequel
|
|
|
152
164
|
# Operators that use IS, used for special casing to override literal true/false values
|
|
153
165
|
IS_OPERATORS = [:IS, :'IS NOT']
|
|
154
166
|
|
|
167
|
+
# Operators that do pattern matching via regular expressions
|
|
168
|
+
REGEXP_OPERATORS = [:~, :'!~', :'~*', :'!~*']
|
|
169
|
+
|
|
170
|
+
# Operators that do pattern matching via LIKE
|
|
171
|
+
LIKE_OPERATORS = [:LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE']
|
|
172
|
+
|
|
155
173
|
# Operator symbols that take exactly two arguments
|
|
156
|
-
TWO_ARITY_OPERATORS =
|
|
157
|
-
:~, :'!~', :'~*', :'!~*', :ILIKE, :'NOT ILIKE'] + \
|
|
158
|
-
INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS
|
|
174
|
+
TWO_ARITY_OPERATORS = EQUALITY_OPERATORS + INEQUALITY_OPERATORS + IS_OPERATORS + IN_OPERATORS + REGEXP_OPERATORS + LIKE_OPERATORS
|
|
159
175
|
|
|
160
176
|
# Operator symbols that take one or more arguments
|
|
161
177
|
N_ARITY_OPERATORS = [:AND, :OR, :'||'] + MATHEMATICAL_OPERATORS + BITWISE_OPERATORS
|
|
@@ -352,6 +368,15 @@ module Sequel
|
|
|
352
368
|
cast(arg, sql_type || String).sql_string
|
|
353
369
|
end
|
|
354
370
|
|
|
371
|
+
# Return an emulated function call for getting the number of characters
|
|
372
|
+
# in the argument:
|
|
373
|
+
#
|
|
374
|
+
# Sequel.char_length(:a) # char_length(a) -- Most databases
|
|
375
|
+
# Sequel.char_length(:a) # length(a) -- SQLite
|
|
376
|
+
def char_length(arg)
|
|
377
|
+
SQL::EmulatedFunction.new(:char_length, arg)
|
|
378
|
+
end
|
|
379
|
+
|
|
355
380
|
# Order the given argument descending.
|
|
356
381
|
# Options:
|
|
357
382
|
#
|
|
@@ -567,6 +592,15 @@ module Sequel
|
|
|
567
592
|
SQL::Subscript.new(exp, subs.flatten)
|
|
568
593
|
end
|
|
569
594
|
|
|
595
|
+
# Return an emulated function call for trimming a string of spaces from
|
|
596
|
+
# both sides (similar to ruby's String#strip).
|
|
597
|
+
#
|
|
598
|
+
# Sequel.trim(:a) # trim(a) -- Most databases
|
|
599
|
+
# Sequel.trim(:a) # ltrim(rtrim(a)) -- Microsoft SQL Server
|
|
600
|
+
def trim(arg)
|
|
601
|
+
SQL::EmulatedFunction.new(:trim, arg)
|
|
602
|
+
end
|
|
603
|
+
|
|
570
604
|
# Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
|
|
571
605
|
# all two element arrays and you want it treated as an SQL value list (IN predicate)
|
|
572
606
|
# instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
|
|
@@ -1071,7 +1105,7 @@ module Sequel
|
|
|
1071
1105
|
|
|
1072
1106
|
# Represents inverse boolean constants (currently only +NOTNULL+). A
|
|
1073
1107
|
# special class to allow for special behavior.
|
|
1074
|
-
class NegativeBooleanConstant <
|
|
1108
|
+
class NegativeBooleanConstant < Constant
|
|
1075
1109
|
to_s_method :negative_boolean_constant_sql, '@constant'
|
|
1076
1110
|
end
|
|
1077
1111
|
|
|
@@ -1110,6 +1144,12 @@ module Sequel
|
|
|
1110
1144
|
|
|
1111
1145
|
to_s_method :function_sql
|
|
1112
1146
|
end
|
|
1147
|
+
|
|
1148
|
+
# Represents an SQL function call that is translated/emulated
|
|
1149
|
+
# on databases that lack support for such a function.
|
|
1150
|
+
class EmulatedFunction < Function
|
|
1151
|
+
to_s_method :emulated_function_sql
|
|
1152
|
+
end
|
|
1113
1153
|
|
|
1114
1154
|
class GenericExpression
|
|
1115
1155
|
include AliasMethods
|
data/lib/sequel/timezones.rb
CHANGED
|
@@ -165,15 +165,23 @@ module Sequel
|
|
|
165
165
|
convert_input_timestamp(ary, input_timezone)
|
|
166
166
|
when Time
|
|
167
167
|
if datetime_class == DateTime
|
|
168
|
-
v.respond_to?(:to_datetime)
|
|
168
|
+
if v.respond_to?(:to_datetime)
|
|
169
|
+
v.to_datetime
|
|
170
|
+
else
|
|
171
|
+
# Ruby 1.8 code, %N not available and %z broken on Windows
|
|
172
|
+
offset_hours, offset_minutes = (v.utc_offset/60).divmod(60)
|
|
173
|
+
string_to_datetime(v.strftime("%Y-%m-%dT%H:%M:%S") << sprintf(".%06i%+03i%02i", v.usec, offset_hours, offset_minutes))
|
|
174
|
+
end
|
|
169
175
|
else
|
|
170
176
|
v
|
|
171
177
|
end
|
|
172
178
|
when DateTime
|
|
173
179
|
if datetime_class == DateTime
|
|
174
180
|
v
|
|
181
|
+
elsif v.respond_to?(:to_time)
|
|
182
|
+
v.to_time
|
|
175
183
|
else
|
|
176
|
-
|
|
184
|
+
string_to_datetime(v.strftime("%FT%T.%N%z"))
|
|
177
185
|
end
|
|
178
186
|
else
|
|
179
187
|
raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
|
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 = 39
|
|
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
|
@@ -18,23 +18,6 @@ def logger.method_missing(m, msg)
|
|
|
18
18
|
end
|
|
19
19
|
MSSQL_DB.loggers = [logger]
|
|
20
20
|
|
|
21
|
-
MSSQL_DB.create_table! :test do
|
|
22
|
-
String :name, :type=>:text
|
|
23
|
-
Integer :value, :index => true
|
|
24
|
-
end
|
|
25
|
-
MSSQL_DB.create_table! :test2 do
|
|
26
|
-
String :name, :type=>:text
|
|
27
|
-
Integer :value
|
|
28
|
-
end
|
|
29
|
-
MSSQL_DB.create_table! :test3 do
|
|
30
|
-
Integer :value
|
|
31
|
-
Time :time
|
|
32
|
-
end
|
|
33
|
-
MSSQL_DB.create_table! :test4 do
|
|
34
|
-
String :name, :size => 20
|
|
35
|
-
column :value, 'varbinary(max)'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
21
|
describe "A MSSQL database" do
|
|
39
22
|
before do
|
|
40
23
|
@db = MSSQL_DB
|
|
@@ -54,7 +37,20 @@ describe "A MSSQL database" do
|
|
|
54
37
|
proc{@db.server_version}.should_not raise_error
|
|
55
38
|
proc{@db.dataset.server_version}.should_not raise_error
|
|
56
39
|
end
|
|
40
|
+
end
|
|
57
41
|
|
|
42
|
+
describe "A MSSQL database" do
|
|
43
|
+
before do
|
|
44
|
+
@db = MSSQL_DB
|
|
45
|
+
@db.create_table! :test3 do
|
|
46
|
+
Integer :value
|
|
47
|
+
Time :time
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
after do
|
|
51
|
+
@db.drop_table?(:test3)
|
|
52
|
+
end
|
|
53
|
+
|
|
58
54
|
specify "should work with NOLOCK" do
|
|
59
55
|
@db.transaction{@db[:test3].nolock.all.should == []}
|
|
60
56
|
end
|
|
@@ -379,12 +375,15 @@ describe "MSSSQL::Dataset#insert" do
|
|
|
379
375
|
before do
|
|
380
376
|
@db = MSSQL_DB
|
|
381
377
|
@db.create_table!(:test5){primary_key :xid; Integer :value}
|
|
378
|
+
@db.create_table! :test4 do
|
|
379
|
+
String :name, :size => 20
|
|
380
|
+
column :value, 'varbinary(max)'
|
|
381
|
+
end
|
|
382
382
|
@db.sqls.clear
|
|
383
383
|
@ds = @db[:test5]
|
|
384
384
|
end
|
|
385
385
|
after do
|
|
386
|
-
@db
|
|
387
|
-
@db.drop_table?(:test5)
|
|
386
|
+
@db.drop_table?(:test5, :test4)
|
|
388
387
|
end
|
|
389
388
|
|
|
390
389
|
specify "should have insert_select return nil if disable_insert_output is used" do
|
|
@@ -409,11 +408,9 @@ describe "MSSSQL::Dataset#insert" do
|
|
|
409
408
|
b.length.should == blob.length
|
|
410
409
|
b.should == blob
|
|
411
410
|
end
|
|
412
|
-
end
|
|
413
411
|
|
|
414
|
-
describe "MSSSQL::Dataset#disable_insert_output" do
|
|
415
412
|
specify "should play nicely with simple_select_all?" do
|
|
416
|
-
MSSQL_DB[:
|
|
413
|
+
MSSQL_DB[:test4].disable_insert_output.send(:simple_select_all?).should == true
|
|
417
414
|
end
|
|
418
415
|
end
|
|
419
416
|
|
|
@@ -462,16 +459,20 @@ describe "A MSSQL database" do
|
|
|
462
459
|
end
|
|
463
460
|
|
|
464
461
|
describe "MSSQL::Database#rename_table" do
|
|
462
|
+
after do
|
|
463
|
+
MSSQL_DB.drop_table?(:foo)
|
|
464
|
+
end
|
|
465
|
+
|
|
465
466
|
specify "should work on non-schema bound tables which need escaping" do
|
|
466
467
|
MSSQL_DB.quote_identifiers = true
|
|
467
468
|
MSSQL_DB.create_table! :'foo bar' do
|
|
468
469
|
text :name
|
|
469
470
|
end
|
|
470
|
-
MSSQL_DB.drop_table? :
|
|
471
|
-
proc { MSSQL_DB.rename_table 'foo bar', '
|
|
471
|
+
MSSQL_DB.drop_table? :foo
|
|
472
|
+
proc { MSSQL_DB.rename_table 'foo bar', 'foo' }.should_not raise_error
|
|
472
473
|
end
|
|
473
474
|
|
|
474
|
-
specify "should
|
|
475
|
+
specify "should work on schema bound tables" do
|
|
475
476
|
MSSQL_DB.execute(<<-SQL)
|
|
476
477
|
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'MY')
|
|
477
478
|
EXECUTE sp_executesql N'create schema MY'
|