sequel 3.29.0 → 3.30.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 +35 -3
- data/Rakefile +2 -1
- data/doc/association_basics.rdoc +11 -0
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/testing.rdoc +17 -3
- data/lib/sequel/adapters/amalgalite.rb +2 -2
- data/lib/sequel/adapters/do/mysql.rb +5 -2
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +126 -43
- data/lib/sequel/adapters/jdbc/as400.rb +11 -3
- data/lib/sequel/adapters/jdbc/db2.rb +2 -1
- data/lib/sequel/adapters/jdbc/derby.rb +44 -19
- data/lib/sequel/adapters/jdbc/h2.rb +32 -19
- data/lib/sequel/adapters/jdbc/hsqldb.rb +21 -17
- data/lib/sequel/adapters/jdbc/jtds.rb +9 -4
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +21 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +48 -18
- data/lib/sequel/adapters/mock.rb +2 -1
- data/lib/sequel/adapters/mysql.rb +4 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +6 -6
- data/lib/sequel/adapters/postgres.rb +25 -12
- data/lib/sequel/adapters/shared/access.rb +14 -6
- data/lib/sequel/adapters/shared/db2.rb +36 -13
- data/lib/sequel/adapters/shared/firebird.rb +12 -5
- data/lib/sequel/adapters/shared/informix.rb +11 -3
- data/lib/sequel/adapters/shared/mssql.rb +94 -47
- data/lib/sequel/adapters/shared/mysql.rb +107 -49
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +54 -27
- data/lib/sequel/adapters/shared/postgres.rb +65 -26
- data/lib/sequel/adapters/shared/progress.rb +4 -1
- data/lib/sequel/adapters/shared/sqlite.rb +36 -20
- data/lib/sequel/adapters/sqlite.rb +2 -3
- data/lib/sequel/adapters/swift/mysql.rb +3 -2
- data/lib/sequel/adapters/swift/sqlite.rb +2 -2
- data/lib/sequel/adapters/tinytds.rb +14 -8
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -4
- data/lib/sequel/database/misc.rb +6 -2
- data/lib/sequel/dataset/graph.rb +33 -7
- data/lib/sequel/dataset/prepared_statements.rb +19 -5
- data/lib/sequel/dataset/sql.rb +611 -201
- data/lib/sequel/model/associations.rb +12 -5
- data/lib/sequel/model/base.rb +20 -5
- data/lib/sequel/plugins/sharding.rb +9 -29
- data/lib/sequel/sql.rb +2 -1
- data/lib/sequel/timezones.rb +14 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +10 -0
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/core/core_sql_spec.rb +3 -1
- data/spec/core/database_spec.rb +42 -0
- data/spec/core/dataset_spec.rb +10 -3
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +38 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +35 -39
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +30 -82
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +33 -103
- data/spec/extensions/instance_filters_spec.rb +10 -21
- data/spec/extensions/instance_hooks_spec.rb +6 -24
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +135 -277
- data/spec/extensions/migration_spec.rb +18 -15
- data/spec/extensions/named_timezones_spec.rb +1 -1
- data/spec/extensions/nested_attributes_spec.rb +97 -92
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +22 -37
- data/spec/extensions/prepared_statements_safe_spec.rb +9 -27
- data/spec/extensions/prepared_statements_spec.rb +11 -30
- data/spec/extensions/prepared_statements_with_pk_spec.rb +6 -13
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +3 -6
- data/spec/extensions/serialization_spec.rb +20 -32
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +14 -36
- data/spec/extensions/spec_helper.rb +10 -64
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +1 -2
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/integration/dataset_test.rb +16 -4
- data/spec/integration/prepared_statement_test.rb +4 -2
- data/spec/model/eager_loading_spec.rb +16 -0
- data/spec/model/model_spec.rb +15 -1
- data/spec/model/record_spec.rb +60 -0
- metadata +23 -40
|
@@ -718,6 +718,10 @@ module Sequel
|
|
|
718
718
|
# :eager_loader_key :: A symbol for the key column to use to populate the key hash
|
|
719
719
|
# for the eager loader.
|
|
720
720
|
# :extend :: A module or array of modules to extend the dataset with.
|
|
721
|
+
# :graph_alias_base :: The base name to use for the table alias when eager graphing. Defaults to the name
|
|
722
|
+
# of the association. If the alias name has already been used in the query, Sequel will create
|
|
723
|
+
# a unique alias by appending a numeric suffix (e.g. alias_0, alias_1, ...) until the alias is
|
|
724
|
+
# unique.
|
|
721
725
|
# :graph_block :: The block to pass to join_table when eagerly loading
|
|
722
726
|
# the association via +eager_graph+.
|
|
723
727
|
# :graph_conditions :: The additional conditions to use on the SQL join when eagerly loading
|
|
@@ -822,6 +826,7 @@ module Sequel
|
|
|
822
826
|
opts[:graph_join_type] ||= :left_outer
|
|
823
827
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
|
824
828
|
conds = opts[:conditions]
|
|
829
|
+
opts[:graph_alias_base] ||= name
|
|
825
830
|
opts[:graph_conditions] = conds if !opts.include?(:graph_conditions) and Sequel.condition_specifier?(conds)
|
|
826
831
|
opts[:graph_conditions] = opts.fetch(:graph_conditions, []).to_a
|
|
827
832
|
opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
|
|
@@ -1618,7 +1623,7 @@ module Sequel
|
|
|
1618
1623
|
# association and the values of the foreign/primary keys of +y+. For most association
|
|
1619
1624
|
# types, this is a simple transformation, but for +many_to_many+ associations this
|
|
1620
1625
|
# creates a subquery to the join table.
|
|
1621
|
-
def
|
|
1626
|
+
def complex_expression_sql_append(sql, op, args)
|
|
1622
1627
|
r = args.at(1)
|
|
1623
1628
|
if (((op == :'=' || op == :'!=') and r.is_a?(Sequel::Model)) ||
|
|
1624
1629
|
(multiple = ((op == :IN || op == :'NOT IN') and ((is_ds = r.is_a?(Sequel::Dataset)) or r.all?{|x| x.is_a?(Sequel::Model)}))))
|
|
@@ -1646,7 +1651,7 @@ module Sequel
|
|
|
1646
1651
|
end
|
|
1647
1652
|
|
|
1648
1653
|
if exp = association_filter_expression(op, ar, r)
|
|
1649
|
-
|
|
1654
|
+
literal_append(sql, exp)
|
|
1650
1655
|
else
|
|
1651
1656
|
raise Sequel::Error, "invalid association type #{ar[:type].inspect} for association #{l.inspect} used in dataset filter for model #{model.inspect}"
|
|
1652
1657
|
end
|
|
@@ -1725,8 +1730,10 @@ module Sequel
|
|
|
1725
1730
|
# Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
|
|
1726
1731
|
# call +each+, it will yield plain hashes, each containing all columns from all the tables.
|
|
1727
1732
|
def eager_graph(*associations)
|
|
1728
|
-
ds = if @opts[:eager_graph]
|
|
1729
|
-
|
|
1733
|
+
ds = if eg = @opts[:eager_graph]
|
|
1734
|
+
eg = eg.dup
|
|
1735
|
+
[:requirements, :reflections, :reciprocals].each{|k| eg[k] = eg[k].dup}
|
|
1736
|
+
clone(:eager_graph=>eg)
|
|
1730
1737
|
else
|
|
1731
1738
|
# Each of the following have a symbol key for the table alias, with the following values:
|
|
1732
1739
|
# :reciprocals - the reciprocal instance variable to use for this association
|
|
@@ -1761,7 +1768,7 @@ module Sequel
|
|
|
1761
1768
|
# *associations :: any associations dependent on this one
|
|
1762
1769
|
def eager_graph_association(ds, model, ta, requirements, r, *associations)
|
|
1763
1770
|
assoc_name = r[:name]
|
|
1764
|
-
assoc_table_alias = ds.unused_table_alias(
|
|
1771
|
+
assoc_table_alias = ds.unused_table_alias(r[:graph_alias_base])
|
|
1765
1772
|
loader = r[:eager_grapher]
|
|
1766
1773
|
if !associations.empty?
|
|
1767
1774
|
if associations.first.respond_to?(:call)
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -1139,13 +1139,16 @@ module Sequel
|
|
|
1139
1139
|
# Takes the following options:
|
|
1140
1140
|
#
|
|
1141
1141
|
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1142
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1143
|
+
# +raise_on_save_failure+ setting
|
|
1144
|
+
# :server :: set the server/shard on the object before saving, and use that
|
|
1145
|
+
# server/shard in any transaction.
|
|
1142
1146
|
# :transaction :: set to true or false to override the current
|
|
1143
1147
|
# +use_transactions+ setting
|
|
1144
1148
|
# :validate :: set to false to skip validation
|
|
1145
|
-
# :raise_on_failure :: set to true or false to override the current
|
|
1146
|
-
# +raise_on_save_failure+ setting
|
|
1147
1149
|
def save(*columns)
|
|
1148
1150
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1151
|
+
set_server(opts[:server]) if opts[:server]
|
|
1149
1152
|
if opts[:validate] != false
|
|
1150
1153
|
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1151
1154
|
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
@@ -1225,6 +1228,13 @@ module Sequel
|
|
|
1225
1228
|
set_restricted(hash, only.flatten, false)
|
|
1226
1229
|
end
|
|
1227
1230
|
|
|
1231
|
+
# Set the shard that this object is tied to. Returns self.
|
|
1232
|
+
def set_server(s)
|
|
1233
|
+
@server = s
|
|
1234
|
+
@this.opts[:server] = s if @this
|
|
1235
|
+
self
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1228
1238
|
# Replace the current values with hash. Should definitely not be
|
|
1229
1239
|
# used with untrusted input, and should probably not be called
|
|
1230
1240
|
# directly by user code.
|
|
@@ -1243,7 +1253,7 @@ module Sequel
|
|
|
1243
1253
|
# Artist[1].this
|
|
1244
1254
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1245
1255
|
def this
|
|
1246
|
-
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
|
1256
|
+
@this ||= use_server(model.dataset.filter(pk_hash).limit(1).naked)
|
|
1247
1257
|
end
|
|
1248
1258
|
|
|
1249
1259
|
# Runs #set with the passed hash and then runs save_changes.
|
|
@@ -1382,7 +1392,7 @@ module Sequel
|
|
|
1382
1392
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1383
1393
|
# dataset by default.
|
|
1384
1394
|
def _insert_dataset
|
|
1385
|
-
model.dataset
|
|
1395
|
+
use_server(model.dataset)
|
|
1386
1396
|
end
|
|
1387
1397
|
|
|
1388
1398
|
# Insert into the given dataset and return the primary key created (if any).
|
|
@@ -1676,6 +1686,11 @@ module Sequel
|
|
|
1676
1686
|
set_restricted(hash, only, except)
|
|
1677
1687
|
save_changes
|
|
1678
1688
|
end
|
|
1689
|
+
|
|
1690
|
+
# Set the given dataset to use the current object's shard.
|
|
1691
|
+
def use_server(ds)
|
|
1692
|
+
@server ? ds.server(@server) : ds
|
|
1693
|
+
end
|
|
1679
1694
|
|
|
1680
1695
|
# Whether to use a transaction for this action. If the :transaction
|
|
1681
1696
|
# option is present in the hash, use that, otherwise, fallback to the
|
|
@@ -1716,7 +1731,7 @@ module Sequel
|
|
|
1716
1731
|
# # ...
|
|
1717
1732
|
def destroy
|
|
1718
1733
|
pr = proc{all{|r| r.destroy}.length}
|
|
1719
|
-
model.use_transactions ? @db.transaction(&pr) : pr.call
|
|
1734
|
+
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
|
1720
1735
|
end
|
|
1721
1736
|
|
|
1722
1737
|
# This allows you to call +to_hash+ without any arguments, which will
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
# The sharding plugin
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
3
|
+
# The sharding plugin augments Sequel's default model sharding support
|
|
4
|
+
# in the following ways:
|
|
5
|
+
#
|
|
6
|
+
# 1) It automatically sets model instances to be saved back to the
|
|
7
|
+
# shard they were retreived from.
|
|
8
|
+
# 2) It makes model associations use the same shard as the model
|
|
9
|
+
# object.
|
|
10
|
+
# 3) It adds a slightly nicer API for creating model instances on
|
|
11
|
+
# specific shards.
|
|
11
12
|
#
|
|
12
13
|
# Usage:
|
|
13
14
|
#
|
|
@@ -55,12 +56,6 @@ module Sequel
|
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
module InstanceMethods
|
|
58
|
-
# Set the shard that this object is tied to. Returns self.
|
|
59
|
-
def set_server(s)
|
|
60
|
-
@server = s
|
|
61
|
-
self
|
|
62
|
-
end
|
|
63
|
-
|
|
64
59
|
# Set the server that this object is tied to, unless it has
|
|
65
60
|
# already been set. Returns self.
|
|
66
61
|
def set_server?(s)
|
|
@@ -68,11 +63,6 @@ module Sequel
|
|
|
68
63
|
self
|
|
69
64
|
end
|
|
70
65
|
|
|
71
|
-
# Ensure that the instance dataset is tied to the correct shard.
|
|
72
|
-
def this
|
|
73
|
-
use_server(super)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
66
|
private
|
|
77
67
|
|
|
78
68
|
# Ensure that association datasets are tied to the correct shard.
|
|
@@ -80,11 +70,6 @@ module Sequel
|
|
|
80
70
|
use_server(super)
|
|
81
71
|
end
|
|
82
72
|
|
|
83
|
-
# Ensure that the object is inserted into the correct shard.
|
|
84
|
-
def _insert_dataset
|
|
85
|
-
use_server(super)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
73
|
# Ensure that the join table for many_to_many associations uses the correct shard.
|
|
89
74
|
def _join_table_dataset(opts)
|
|
90
75
|
use_server(super)
|
|
@@ -97,11 +82,6 @@ module Sequel
|
|
|
97
82
|
o.set_server?(@server) if o.respond_to?(:set_server?)
|
|
98
83
|
super
|
|
99
84
|
end
|
|
100
|
-
|
|
101
|
-
# Set the given dataset to use the current object's shard.
|
|
102
|
-
def use_server(ds)
|
|
103
|
-
@server ? ds.server(@server) : ds
|
|
104
|
-
end
|
|
105
85
|
end
|
|
106
86
|
|
|
107
87
|
module DatasetMethods
|
data/lib/sequel/sql.rb
CHANGED
|
@@ -79,7 +79,8 @@ module Sequel
|
|
|
79
79
|
# the method provided on the dataset with args as the argument (self by default).
|
|
80
80
|
# Used to DRY up some code.
|
|
81
81
|
def self.to_s_method(meth, args=:self) # :nodoc:
|
|
82
|
-
class_eval("def to_s(ds)
|
|
82
|
+
class_eval("def to_s(ds) ds.#{meth}(#{args}) end", __FILE__, __LINE__)
|
|
83
|
+
class_eval("def to_s_append(ds, sql) ds.#{meth}_append(sql, #{args}) end", __FILE__, __LINE__)
|
|
83
84
|
end
|
|
84
85
|
private_class_method :to_s_method
|
|
85
86
|
|
data/lib/sequel/timezones.rb
CHANGED
|
@@ -145,14 +145,24 @@ module Sequel
|
|
|
145
145
|
v2
|
|
146
146
|
end
|
|
147
147
|
when Array
|
|
148
|
-
y, mo, d, h, mi, s = v
|
|
148
|
+
y, mo, d, h, mi, s, ns, off = v
|
|
149
149
|
if datetime_class == DateTime
|
|
150
|
-
|
|
150
|
+
s += (defined?(Rational) ? Rational(ns, 1000000000) : ns/1000000000.0) if ns
|
|
151
|
+
if off
|
|
152
|
+
DateTime.civil(y, mo, d, h, mi, s, off)
|
|
153
|
+
else
|
|
154
|
+
convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone)
|
|
155
|
+
end
|
|
151
156
|
else
|
|
152
|
-
Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
|
|
157
|
+
Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
|
|
153
158
|
end
|
|
154
159
|
when Hash
|
|
155
|
-
|
|
160
|
+
ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i}
|
|
161
|
+
if (offset = (v[:offset] || v['offset']))
|
|
162
|
+
ary << offset
|
|
163
|
+
end
|
|
164
|
+
convert_input_timestamp(ary, input_timezone)
|
|
165
|
+
convert_input_timestamp(ary, input_timezone)
|
|
156
166
|
when Time
|
|
157
167
|
if datetime_class == DateTime
|
|
158
168
|
v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
|
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 = 30
|
|
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/mysql_spec.rb
CHANGED
|
@@ -247,6 +247,16 @@ describe "A MySQL dataset" do
|
|
|
247
247
|
|
|
248
248
|
@d.first[:name].should == ':\\'
|
|
249
249
|
end
|
|
250
|
+
|
|
251
|
+
specify "should handle prepared statements with on_duplicate_key_update" do
|
|
252
|
+
@d.db.add_index :items, :value, :unique=>true
|
|
253
|
+
ds = @d.on_duplicate_key_update
|
|
254
|
+
ps = ds.prepare(:insert, :insert_user_id_feature_name, :value => :$v, :name => :$n)
|
|
255
|
+
ps.call(:v => 1, :n => 'a')
|
|
256
|
+
ds.all.should == [{:value=>1, :name=>'a'}]
|
|
257
|
+
ps.call(:v => 1, :n => 'b')
|
|
258
|
+
ds.all.should == [{:value=>1, :name=>'b'}]
|
|
259
|
+
end
|
|
250
260
|
end
|
|
251
261
|
|
|
252
262
|
describe "MySQL datasets" do
|
|
@@ -187,7 +187,7 @@ describe "An Oracle dataset" do
|
|
|
187
187
|
@d << {:name => 'def', :value => 789}
|
|
188
188
|
@d.filter('value > 500').update(:date_created => "to_timestamp('2009-09-09', 'YYYY-MM-DD')".lit)
|
|
189
189
|
|
|
190
|
-
@d[:name => 'def'][:date_created].should ==
|
|
190
|
+
@d[:name => 'def'][:date_created].strftime('%F').should == '2009-09-09'
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
specify "should delete records correctly" do
|
data/spec/core/core_sql_spec.rb
CHANGED
|
@@ -177,7 +177,9 @@ end
|
|
|
177
177
|
describe "Column references" do
|
|
178
178
|
before do
|
|
179
179
|
@ds = Sequel::Database.new.dataset
|
|
180
|
-
def @ds.
|
|
180
|
+
def @ds.quoted_identifier_append(sql, c)
|
|
181
|
+
sql << "`#{c}`"
|
|
182
|
+
end
|
|
181
183
|
@ds.quote_identifiers = true
|
|
182
184
|
end
|
|
183
185
|
|
data/spec/core/database_spec.rb
CHANGED
|
@@ -739,6 +739,13 @@ describe "Database#transaction" do
|
|
|
739
739
|
proc{@db.after_rollback}.should raise_error(Sequel::Error)
|
|
740
740
|
end
|
|
741
741
|
|
|
742
|
+
specify "should have after_commit and after_rollback respect :server option" do
|
|
743
|
+
@db.transaction(:server=>:test){@db.after_commit(:server=>:test){@db.execute('foo', :server=>:test)}}
|
|
744
|
+
@db.sqls.should == ['BEGIN -- test', 'COMMIT -- test', 'foo -- test']
|
|
745
|
+
@db.transaction(:server=>:test){@db.after_rollback(:server=>:test){@db.execute('foo', :server=>:test)}; raise Sequel::Rollback}
|
|
746
|
+
@db.sqls.should == ['BEGIN -- test', 'ROLLBACK -- test', 'foo -- test']
|
|
747
|
+
end
|
|
748
|
+
|
|
742
749
|
specify "should execute after_commit outside transactions" do
|
|
743
750
|
@db.after_commit{@db.execute('foo')}
|
|
744
751
|
@db.sqls.should == ['foo']
|
|
@@ -1774,6 +1781,41 @@ describe "Database#typecast_value" do
|
|
|
1774
1781
|
end
|
|
1775
1782
|
end
|
|
1776
1783
|
|
|
1784
|
+
specify "should handle arrays when typecasting timestamps" do
|
|
1785
|
+
begin
|
|
1786
|
+
@db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == Time.local(2011, 10, 11, 12, 13, 14)
|
|
1787
|
+
@db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
|
|
1788
|
+
|
|
1789
|
+
Sequel.datetime_class = DateTime
|
|
1790
|
+
@db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
|
|
1791
|
+
@db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
|
|
1792
|
+
@db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000, (defined?(Rational) ? Rational(1, 2) : 0.5)]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
|
|
1793
|
+
ensure
|
|
1794
|
+
Sequel.datetime_class = Time
|
|
1795
|
+
end
|
|
1796
|
+
end
|
|
1797
|
+
|
|
1798
|
+
specify "should handle hashes when typecasting timestamps" do
|
|
1799
|
+
begin
|
|
1800
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
|
|
1801
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
|
|
1802
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
|
|
1803
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
|
|
1804
|
+
|
|
1805
|
+
Sequel.datetime_class = DateTime
|
|
1806
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
|
|
1807
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
|
|
1808
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
|
|
1809
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
|
|
1810
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
|
|
1811
|
+
@db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
|
|
1812
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
|
|
1813
|
+
@db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
|
|
1814
|
+
ensure
|
|
1815
|
+
Sequel.datetime_class = Time
|
|
1816
|
+
end
|
|
1817
|
+
end
|
|
1818
|
+
|
|
1777
1819
|
specify "should typecast decimal values to BigDecimal" do
|
|
1778
1820
|
[1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
|
|
1779
1821
|
v = @db.typecast_value(:decimal, i)
|
data/spec/core/dataset_spec.rb
CHANGED
|
@@ -3066,6 +3066,13 @@ describe "Dataset prepared statements and bound variables " do
|
|
|
3066
3066
|
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
|
|
3067
3067
|
end
|
|
3068
3068
|
|
|
3069
|
+
specify "should handle columns on prepared statements correctly" do
|
|
3070
|
+
@db.columns = [:num]
|
|
3071
|
+
@ds.meta_def(:select_where_sql){|sql| super(sql); sql << " OR #{columns.first} = 1" if opts[:where]}
|
|
3072
|
+
@ds.filter(:num=>:$n).prepare(:select, :sn).sql.should == 'SELECT * FROM items WHERE (num = $n) OR num = 1'
|
|
3073
|
+
@db.sqls.should == ['SELECT * FROM items LIMIT 1']
|
|
3074
|
+
end
|
|
3075
|
+
|
|
3069
3076
|
specify "should handle datasets using static sql and placeholders" do
|
|
3070
3077
|
@db["SELECT * FROM items WHERE (num = ?)", :$n].call(:select, :n=>1)
|
|
3071
3078
|
@db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
|
|
@@ -3412,7 +3419,7 @@ describe "Sequel::Dataset #with and #with_recursive" do
|
|
|
3412
3419
|
|
|
3413
3420
|
specify "#with should work on insert, update, and delete statements if they support it" do
|
|
3414
3421
|
[:insert, :update, :delete].each do |m|
|
|
3415
|
-
@ds.meta_def(:"#{m}_clause_methods"){
|
|
3422
|
+
@ds.meta_def(:"#{m}_clause_methods"){[:"#{m}_with_sql"] + super()}
|
|
3416
3423
|
end
|
|
3417
3424
|
@ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
|
|
3418
3425
|
@ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
|
|
@@ -3888,8 +3895,8 @@ describe "Dataset emulating bitwise operator support" do
|
|
|
3888
3895
|
before do
|
|
3889
3896
|
@ds = Sequel::Database.new.dataset
|
|
3890
3897
|
@ds.quote_identifiers = true
|
|
3891
|
-
def @ds.
|
|
3892
|
-
complex_expression_arg_pairs(args){|a, b| "bitand(#{literal(a)}, #{literal(b)})"}
|
|
3898
|
+
def @ds.complex_expression_sql_append(sql, op, args)
|
|
3899
|
+
sql << complex_expression_arg_pairs(args){|a, b| "bitand(#{literal(a)}, #{literal(b)})"}
|
|
3893
3900
|
end
|
|
3894
3901
|
end
|
|
3895
3902
|
|
|
@@ -7,6 +7,10 @@ describe "Sequel Mock Adapter" do
|
|
|
7
7
|
db.adapter_scheme.should == :mock
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
specify "should have constructor accept no arguments" do
|
|
11
|
+
Sequel::Mock::Database.new.should be_a_kind_of(Sequel::Mock::Database)
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
specify "should each not return any rows by default" do
|
|
11
15
|
called = false
|
|
12
16
|
Sequel.mock[:t].each{|r| called = true}
|
|
@@ -28,11 +28,49 @@ describe Sequel::Dataset, " graphing" do
|
|
|
28
28
|
ds1.opts.should_not == o1
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
it "#graph should not modify the current dataset's opts if current dataset is already graphed" do
|
|
32
|
+
ds2 = @ds1.graph(@ds2)
|
|
33
|
+
proc{@ds1.graph(@ds2)}.should_not raise_error
|
|
34
|
+
proc{ds2.graph(@ds3)}.should_not raise_error
|
|
35
|
+
proc{ds2.graph(@ds3)}.should_not raise_error
|
|
36
|
+
end
|
|
37
|
+
|
|
31
38
|
it "#graph should accept a simple dataset and pass the table to join" do
|
|
32
39
|
ds = @ds1.graph(@ds2, :x=>:id)
|
|
33
40
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
34
41
|
end
|
|
35
42
|
|
|
43
|
+
it "#graph should use currently selected columns as the basis for the selected columns in a new graph" do
|
|
44
|
+
ds = @ds1.select(:id).graph(@ds2, :x=>:id)
|
|
45
|
+
ds.sql.should == 'SELECT points.id, lines.id AS lines_id, lines.x, lines.y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
46
|
+
ds._fetch = {:id=>1, :lines_id=>2, :x=>3, :y=>4, :graph_id=>5}
|
|
47
|
+
ds.all.should == [{:points=>{:id=>1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
|
|
48
|
+
|
|
49
|
+
ds = @ds1.select(:id, :x).graph(@ds2, :x=>:id)
|
|
50
|
+
ds.sql.should == 'SELECT points.id, points.x, lines.id AS lines_id, lines.x AS lines_x, lines.y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
51
|
+
ds._fetch = {:id=>1, :x=>-1, :lines_id=>2, :lines_x=>3, :y=>4, :graph_id=>5}
|
|
52
|
+
ds.all.should == [{:points=>{:id=>1, :x=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
|
|
53
|
+
|
|
54
|
+
ds = @ds1.select(:id.identifier, :x.qualify(:points)).graph(@ds2, :x=>:id)
|
|
55
|
+
ds.sql.should == 'SELECT points.id, points.x, lines.id AS lines_id, lines.x AS lines_x, lines.y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
56
|
+
ds._fetch = {:id=>1, :x=>-1, :lines_id=>2, :lines_x=>3, :y=>4, :graph_id=>5}
|
|
57
|
+
ds.all.should == [{:points=>{:id=>1, :x=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
|
|
58
|
+
|
|
59
|
+
ds = @ds1.select(:id.identifier.qualify(:points), :x.identifier.as(:y)).graph(@ds2, :x=>:id)
|
|
60
|
+
ds.sql.should == 'SELECT points.id, points.x AS y, lines.id AS lines_id, lines.x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
61
|
+
ds._fetch = {:id=>1, :y=>-1, :lines_id=>2, :x=>3, :lines_y=>4, :graph_id=>5}
|
|
62
|
+
ds.all.should == [{:points=>{:id=>1, :y=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
|
|
63
|
+
|
|
64
|
+
ds = @ds1.select(:id, :x.identifier.qualify(:points.identifier).as(:y.identifier)).graph(@ds2, :x=>:id)
|
|
65
|
+
ds.sql.should == 'SELECT points.id, points.x AS y, lines.id AS lines_id, lines.x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
66
|
+
ds._fetch = {:id=>1, :y=>-1, :lines_id=>2, :x=>3, :lines_y=>4, :graph_id=>5}
|
|
67
|
+
ds.all.should == [{:points=>{:id=>1, :y=>-1}, :lines=>{:id=>2, :x=>3, :y=>4, :graph_id=>5}}]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "#graph should raise error if currently selected expressions cannot be handled" do
|
|
71
|
+
proc{@ds1.select(1).graph(@ds2, :x=>:id)}.should raise_error(Sequel::Error)
|
|
72
|
+
end
|
|
73
|
+
|
|
36
74
|
it "#graph should accept a complex dataset and pass it directly to join" do
|
|
37
75
|
ds = @ds1.graph(@ds2.filter(:x=>1), {:x=>:id})
|
|
38
76
|
ds.sql.should == 'SELECT points.id, points.x, points.y, t1.id AS t1_id, t1.x AS t1_x, t1.y AS t1_y, t1.graph_id FROM points LEFT OUTER JOIN (SELECT * FROM lines WHERE (x = 1)) AS t1 ON (t1.x = points.id)'
|
|
@@ -5,11 +5,7 @@ describe "AssociationAutoreloading plugin" do
|
|
|
5
5
|
@c = Class.new(Sequel::Model)
|
|
6
6
|
@c.plugin :association_autoreloading
|
|
7
7
|
@Artist = Class.new(@c).set_dataset(:artists)
|
|
8
|
-
|
|
9
|
-
def ds1.fetch_rows(s)
|
|
10
|
-
(MODEL_DB.sqls ||= []) << s
|
|
11
|
-
yield({:id=>2, :name=>'Ar'})
|
|
12
|
-
end
|
|
8
|
+
@Artist.dataset._fetch = {:id=>2, :name=>'Ar'}
|
|
13
9
|
@Album = Class.new(@c).set_dataset(:albums)
|
|
14
10
|
@Artist.columns :id, :name
|
|
15
11
|
@Album.columns :id, :name, :artist_id
|
|
@@ -22,7 +18,6 @@ describe "AssociationAutoreloading plugin" do
|
|
|
22
18
|
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
23
19
|
album.artist
|
|
24
20
|
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
25
|
-
MODEL_DB.reset
|
|
26
21
|
|
|
27
22
|
album.artist_id = 1
|
|
28
23
|
album.artist
|
|
@@ -33,7 +28,6 @@ describe "AssociationAutoreloading plugin" do
|
|
|
33
28
|
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
34
29
|
album.artist
|
|
35
30
|
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
36
|
-
MODEL_DB.reset
|
|
37
31
|
|
|
38
32
|
album.artist_id = 2
|
|
39
33
|
album.artist
|
|
@@ -54,7 +48,6 @@ describe "AssociationAutoreloading plugin" do
|
|
|
54
48
|
album.artist_id = 1
|
|
55
49
|
album.artist
|
|
56
50
|
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
57
|
-
MODEL_DB.reset
|
|
58
51
|
|
|
59
52
|
album.other_artist
|
|
60
53
|
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
@@ -69,7 +62,6 @@ describe "AssociationAutoreloading plugin" do
|
|
|
69
62
|
album.artist_id = 1
|
|
70
63
|
album.composite_artist
|
|
71
64
|
MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al')) LIMIT 1"]
|
|
72
|
-
MODEL_DB.reset
|
|
73
65
|
|
|
74
66
|
album.name = 'Al2'
|
|
75
67
|
album.composite_artist
|
|
@@ -84,7 +76,6 @@ describe "AssociationAutoreloading plugin" do
|
|
|
84
76
|
album = salbum.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
85
77
|
album.artist
|
|
86
78
|
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
87
|
-
MODEL_DB.reset
|
|
88
79
|
|
|
89
80
|
album.artist_id = 1
|
|
90
81
|
album.artist
|