sequel 4.12.0 → 4.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +64 -0
- data/Rakefile +3 -1
- data/bin/sequel +13 -5
- data/doc/release_notes/4.13.0.txt +169 -0
- data/doc/sql.rdoc +3 -3
- data/lib/sequel/adapters/do.rb +11 -23
- data/lib/sequel/adapters/do/mysql.rb +8 -0
- data/lib/sequel/adapters/do/postgres.rb +8 -0
- data/lib/sequel/adapters/do/{sqlite.rb → sqlite3.rb} +9 -0
- data/lib/sequel/adapters/jdbc.rb +16 -139
- data/lib/sequel/adapters/jdbc/as400.rb +9 -0
- data/lib/sequel/adapters/jdbc/cubrid.rb +9 -0
- data/lib/sequel/adapters/jdbc/db2.rb +9 -0
- data/lib/sequel/adapters/jdbc/derby.rb +9 -0
- data/lib/sequel/adapters/jdbc/{firebird.rb → firebirdsql.rb} +9 -0
- data/lib/sequel/adapters/jdbc/h2.rb +10 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +9 -0
- data/lib/sequel/adapters/jdbc/{informix.rb → informix-sqli.rb} +9 -0
- data/lib/sequel/adapters/jdbc/{progress.rb → jdbcprogress.rb} +9 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +10 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +14 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +9 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +23 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +10 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -0
- data/lib/sequel/adapters/odbc.rb +6 -14
- data/lib/sequel/adapters/odbc/db2.rb +9 -0
- data/lib/sequel/adapters/odbc/mssql.rb +8 -0
- data/lib/sequel/adapters/odbc/progress.rb +8 -0
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/firebird.rb +8 -1
- data/lib/sequel/adapters/shared/mssql.rb +68 -27
- data/lib/sequel/adapters/shared/mysql.rb +3 -5
- data/lib/sequel/adapters/shared/oracle.rb +17 -3
- data/lib/sequel/adapters/shared/postgres.rb +9 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +6 -6
- data/lib/sequel/database/connecting.rb +38 -17
- data/lib/sequel/dataset/actions.rb +6 -2
- data/lib/sequel/dataset/graph.rb +18 -20
- data/lib/sequel/dataset/misc.rb +37 -0
- data/lib/sequel/dataset/prepared_statements.rb +1 -2
- data/lib/sequel/dataset/query.rb +1 -0
- data/lib/sequel/dataset/sql.rb +17 -10
- data/lib/sequel/extensions/dataset_source_alias.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +14 -10
- data/lib/sequel/extensions/pg_enum.rb +135 -0
- data/lib/sequel/extensions/pg_hstore.rb +4 -6
- data/lib/sequel/extensions/pg_inet.rb +4 -5
- data/lib/sequel/extensions/pg_interval.rb +3 -3
- data/lib/sequel/extensions/pg_json.rb +16 -12
- data/lib/sequel/extensions/pg_range.rb +5 -3
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/round_timestamps.rb +52 -0
- data/lib/sequel/model.rb +5 -2
- data/lib/sequel/model/associations.rb +29 -3
- data/lib/sequel/model/base.rb +68 -29
- data/lib/sequel/plugins/class_table_inheritance.rb +25 -16
- data/lib/sequel/plugins/column_select.rb +57 -0
- data/lib/sequel/plugins/composition.rb +14 -16
- data/lib/sequel/plugins/dirty.rb +9 -11
- data/lib/sequel/plugins/insert_returning_select.rb +70 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -9
- data/lib/sequel/plugins/lazy_attributes.rb +16 -4
- data/lib/sequel/plugins/list.rb +9 -0
- data/lib/sequel/plugins/modification_detection.rb +90 -0
- data/lib/sequel/plugins/serialization.rb +13 -15
- data/lib/sequel/plugins/serialization_modification_detection.rb +9 -9
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/timestamps.rb +6 -6
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +7 -0
- data/spec/adapters/postgres_spec.rb +41 -0
- data/spec/bin_spec.rb +4 -1
- data/spec/core/database_spec.rb +6 -0
- data/spec/core/dataset_spec.rb +100 -90
- data/spec/core/object_graph_spec.rb +5 -0
- data/spec/extensions/class_table_inheritance_spec.rb +18 -13
- data/spec/extensions/column_select_spec.rb +108 -0
- data/spec/extensions/composition_spec.rb +20 -0
- data/spec/extensions/dataset_source_alias_spec.rb +51 -0
- data/spec/extensions/insert_returning_select_spec.rb +46 -0
- data/spec/extensions/lazy_attributes_spec.rb +24 -20
- data/spec/extensions/list_spec.rb +5 -0
- data/spec/extensions/modification_detection_spec.rb +80 -0
- data/spec/extensions/pg_enum_spec.rb +64 -0
- data/spec/extensions/pg_json_spec.rb +7 -13
- data/spec/extensions/prepared_statements_spec.rb +6 -4
- data/spec/extensions/round_timestamps_spec.rb +43 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +10 -1
- data/spec/extensions/serialization_spec.rb +18 -0
- data/spec/extensions/single_table_inheritance_spec.rb +5 -0
- data/spec/extensions/timestamps_spec.rb +6 -0
- data/spec/integration/plugin_test.rb +14 -8
- data/spec/integration/prepared_statement_test.rb +12 -0
- data/spec/model/associations_spec.rb +24 -0
- data/spec/model/model_spec.rb +13 -3
- data/spec/model/record_spec.rb +24 -1
- metadata +22 -6
@@ -59,15 +59,6 @@ module Sequel
|
|
59
59
|
clear_instance_filters
|
60
60
|
end
|
61
61
|
|
62
|
-
# Duplicate internal structures when duplicating model instance.
|
63
|
-
def dup
|
64
|
-
ifs = instance_filters.dup
|
65
|
-
super.instance_eval do
|
66
|
-
@instance_filters = ifs
|
67
|
-
self
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
62
|
# Freeze the instance filters when freezing the object
|
72
63
|
def freeze
|
73
64
|
instance_filters.freeze
|
@@ -93,6 +84,13 @@ module Sequel
|
|
93
84
|
end
|
94
85
|
end
|
95
86
|
|
87
|
+
# Duplicate internal structures when duplicating model instance.
|
88
|
+
def initialize_copy(other)
|
89
|
+
super
|
90
|
+
@instance_filters = other.send(:instance_filters).dup
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
96
94
|
# Lazily initialize the instance filter array.
|
97
95
|
def instance_filters
|
98
96
|
@instance_filters ||= []
|
@@ -17,6 +17,13 @@ module Sequel
|
|
17
17
|
#
|
18
18
|
# # You can specify multiple columns to lazily load:
|
19
19
|
# Album.plugin :lazy_attributes, :review, :tracklist
|
20
|
+
#
|
21
|
+
# Note that by default on databases that supporting RETURNING,
|
22
|
+
# using explicit column selections will cause instance creations
|
23
|
+
# to use two queries (insert and refresh) instead of a single
|
24
|
+
# query using RETURNING. You can use the insert_returning_select
|
25
|
+
# plugin to automatically use RETURNING for instance creations
|
26
|
+
# for models using the lazy_attributes plugin.
|
20
27
|
module LazyAttributes
|
21
28
|
# Lazy attributes requires the tactical_eager_loading plugin
|
22
29
|
def self.apply(model, *attrs)
|
@@ -37,7 +44,10 @@ module Sequel
|
|
37
44
|
# For each attribute given, create an accessor method that allows a lazy
|
38
45
|
# lookup of the attribute. Each attribute should be given as a symbol.
|
39
46
|
def lazy_attributes(*attrs)
|
40
|
-
|
47
|
+
unless select = dataset.opts[:select]
|
48
|
+
select = dataset.columns.map{|c| Sequel.qualify(dataset.first_source, c)}
|
49
|
+
end
|
50
|
+
set_dataset(dataset.select(*select.reject{|c| attrs.include?(dataset.send(:_hash_key_symbol, c))}))
|
41
51
|
attrs.each{|a| define_lazy_attribute_getter(a)}
|
42
52
|
end
|
43
53
|
|
@@ -66,8 +76,9 @@ module Sequel
|
|
66
76
|
# the attribute for just the current object. Return the value of
|
67
77
|
# the attribute for the current object.
|
68
78
|
def lazy_attribute_lookup(a)
|
79
|
+
selection = Sequel.qualify(model.table_name, a)
|
69
80
|
if frozen?
|
70
|
-
return this.dup.
|
81
|
+
return this.dup.get(selection)
|
71
82
|
end
|
72
83
|
|
73
84
|
if retrieved_with
|
@@ -75,14 +86,15 @@ module Sequel
|
|
75
86
|
composite_pk = true if primary_key.is_a?(Array)
|
76
87
|
id_map = {}
|
77
88
|
retrieved_with.each{|o| id_map[o.pk] = o unless o.values.has_key?(a) || o.frozen?}
|
78
|
-
model.
|
89
|
+
predicate_key = composite_pk ? primary_key.map{|k| Sequel.qualify(model.table_name, k)} : Sequel.qualify(model.table_name, primary_key)
|
90
|
+
model.select(*(Array(primary_key).map{|k| Sequel.qualify(model.table_name, k)} + [selection])).where(predicate_key=>id_map.keys).naked.each do |row|
|
79
91
|
obj = id_map[composite_pk ? row.values_at(*primary_key) : row[primary_key]]
|
80
92
|
if obj && !obj.values.has_key?(a)
|
81
93
|
obj.values[a] = row[a]
|
82
94
|
end
|
83
95
|
end
|
84
96
|
end
|
85
|
-
values[a] = this.
|
97
|
+
values[a] = this.get(selection) unless values.has_key?(a)
|
86
98
|
values[a]
|
87
99
|
end
|
88
100
|
end
|
data/lib/sequel/plugins/list.rb
CHANGED
@@ -96,6 +96,15 @@ module Sequel
|
|
96
96
|
super
|
97
97
|
end
|
98
98
|
|
99
|
+
# When destroying an instance, move all entries after the instance down
|
100
|
+
# one position, so that there aren't any gaps
|
101
|
+
def after_destroy
|
102
|
+
super
|
103
|
+
|
104
|
+
f = Sequel.expr(position_field)
|
105
|
+
list_dataset.where(f > position_value).update(f => f - 1)
|
106
|
+
end
|
107
|
+
|
99
108
|
# Find the last position in the list containing this instance.
|
100
109
|
def last_position
|
101
110
|
list_dataset.max(position_field).to_i
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
# This plugin automatically detects in-place modifications to
|
4
|
+
# columns as well as direct modifications of the values hash.
|
5
|
+
#
|
6
|
+
# class User < Sequel::Model
|
7
|
+
# plugin :modification_detection
|
8
|
+
# end
|
9
|
+
# user = User[1]
|
10
|
+
# user.a # => 'a'
|
11
|
+
# user.a << 'b'
|
12
|
+
# user.save_changes
|
13
|
+
# # UPDATE users SET a = 'ab' WHERE (id = 1)
|
14
|
+
#
|
15
|
+
# Note that for this plugin to work correctly, the column values must
|
16
|
+
# correctly implement the #hash method, returning the same value if
|
17
|
+
# the object is equal, and a different value if the object is not equal.
|
18
|
+
#
|
19
|
+
# Note that this plugin causes a performance hit for all retrieved
|
20
|
+
# objects, so it shouldn't be used in cases where performance is a
|
21
|
+
# primary concern.
|
22
|
+
#
|
23
|
+
# Usage:
|
24
|
+
#
|
25
|
+
# # Make all model subclass automatically detect column modifications
|
26
|
+
# Sequel::Model.plugin :modification_detection
|
27
|
+
#
|
28
|
+
# # Make the Album class automatically detect column modifications
|
29
|
+
# Album.plugin :modification_detection
|
30
|
+
module ModificationDetection
|
31
|
+
module ClassMethods
|
32
|
+
# Calculate the hashes for all of the column values, so that they
|
33
|
+
# can be compared later to determine if the column value has changed.
|
34
|
+
def call(_)
|
35
|
+
v = super
|
36
|
+
v.calculate_values_hashes
|
37
|
+
v
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module InstanceMethods
|
42
|
+
# Recalculate the column value hashes after updating.
|
43
|
+
def after_update
|
44
|
+
super
|
45
|
+
recalculate_values_hashes
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calculate the column hash values if they haven't been already calculated.
|
49
|
+
def calculate_values_hashes
|
50
|
+
@values_hashes || recalculate_values_hashes
|
51
|
+
end
|
52
|
+
|
53
|
+
# Detect which columns have been modified by comparing the cached hash
|
54
|
+
# value to the hash of the current value.
|
55
|
+
def changed_columns
|
56
|
+
cc = super
|
57
|
+
changed = []
|
58
|
+
v = @values
|
59
|
+
if vh = @values_hashes
|
60
|
+
(vh.keys - cc).each{|c| changed << c unless v.has_key?(c) && vh[c] == v[c].hash}
|
61
|
+
end
|
62
|
+
cc + changed
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Recalculate the column value hashes after manually refreshing.
|
68
|
+
def _refresh(dataset)
|
69
|
+
super
|
70
|
+
recalculate_values_hashes
|
71
|
+
end
|
72
|
+
|
73
|
+
# Recalculate the column value hashes after refreshing after saving a new object.
|
74
|
+
def _save_refresh
|
75
|
+
super
|
76
|
+
recalculate_values_hashes
|
77
|
+
end
|
78
|
+
|
79
|
+
# Recalculate the column value hashes, caching them for later use.
|
80
|
+
def recalculate_values_hashes
|
81
|
+
vh = {}
|
82
|
+
@values.each do |k,v|
|
83
|
+
vh[k] = v.hash
|
84
|
+
end
|
85
|
+
@values_hashes = vh.freeze
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -175,26 +175,11 @@ module Sequel
|
|
175
175
|
end
|
176
176
|
|
177
177
|
module InstanceMethods
|
178
|
-
# Serialize deserialized values before saving
|
179
|
-
def before_save
|
180
|
-
serialize_deserialized_values
|
181
|
-
super
|
182
|
-
end
|
183
|
-
|
184
178
|
# Hash of deserialized values, used as a cache.
|
185
179
|
def deserialized_values
|
186
180
|
@deserialized_values ||= {}
|
187
181
|
end
|
188
182
|
|
189
|
-
# Freeze the deserialized values
|
190
|
-
def dup
|
191
|
-
dv = deserialized_values.dup
|
192
|
-
super.instance_eval do
|
193
|
-
@deserialized_values = dv
|
194
|
-
self
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
183
|
# Freeze the deserialized values
|
199
184
|
def freeze
|
200
185
|
deserialized_values.freeze
|
@@ -203,6 +188,12 @@ module Sequel
|
|
203
188
|
|
204
189
|
private
|
205
190
|
|
191
|
+
# Serialize deserialized values before saving
|
192
|
+
def _before_validation
|
193
|
+
serialize_deserialized_values
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
206
197
|
# Clear any cached deserialized values when doing a manual refresh.
|
207
198
|
def _refresh_set_values(hash)
|
208
199
|
@deserialized_values.clear if @deserialized_values
|
@@ -218,6 +209,13 @@ module Sequel
|
|
218
209
|
end
|
219
210
|
end
|
220
211
|
|
212
|
+
# Dup the deserialized values when duping model instance.
|
213
|
+
def initialize_copy(other)
|
214
|
+
super
|
215
|
+
@deserialized_values = other.deserialized_values.dup
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
221
219
|
# Serialize all deserialized values
|
222
220
|
def serialize_deserialized_values
|
223
221
|
deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
|
@@ -45,15 +45,6 @@ module Sequel
|
|
45
45
|
cc
|
46
46
|
end
|
47
47
|
|
48
|
-
# Duplicate the original deserialized values when duplicating instance.
|
49
|
-
def dup
|
50
|
-
o = @original_deserialized_values
|
51
|
-
super.instance_eval do
|
52
|
-
@original_deserialized_values = o.dup if o
|
53
|
-
self
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
48
|
# Freeze the original deserialized values when freezing the instance.
|
58
49
|
def freeze
|
59
50
|
@original_deserialized_values ||= {}
|
@@ -63,6 +54,15 @@ module Sequel
|
|
63
54
|
|
64
55
|
private
|
65
56
|
|
57
|
+
# Duplicate the original deserialized values when duplicating instance.
|
58
|
+
def initialize_copy(other)
|
59
|
+
super
|
60
|
+
if o = other.instance_variable_get(:@original_deserialized_values)
|
61
|
+
@original_deserialized_values = o.dup
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
66
|
# For new objects, serialize any existing deserialized values so that changes can
|
67
67
|
# be detected.
|
68
68
|
def initialize_set(values)
|
@@ -215,8 +215,10 @@ module Sequel
|
|
215
215
|
end
|
216
216
|
|
217
217
|
module InstanceMethods
|
218
|
+
private
|
219
|
+
|
218
220
|
# Set the sti_key column based on the sti_key_map.
|
219
|
-
def
|
221
|
+
def _before_validation
|
220
222
|
if new? && !self[model.sti_key]
|
221
223
|
send("#{model.sti_key}=", model.sti_key_chooser.call(self))
|
222
224
|
end
|
@@ -57,12 +57,6 @@ module Sequel
|
|
57
57
|
end
|
58
58
|
|
59
59
|
module InstanceMethods
|
60
|
-
# Set the create timestamp when creating
|
61
|
-
def before_validation
|
62
|
-
set_create_timestamp if new?
|
63
|
-
super
|
64
|
-
end
|
65
|
-
|
66
60
|
# Set the update timestamp when updating
|
67
61
|
def before_update
|
68
62
|
set_update_timestamp
|
@@ -71,6 +65,12 @@ module Sequel
|
|
71
65
|
|
72
66
|
private
|
73
67
|
|
68
|
+
# Set the create timestamp when creating
|
69
|
+
def _before_validation
|
70
|
+
set_create_timestamp if new?
|
71
|
+
super
|
72
|
+
end
|
73
|
+
|
74
74
|
# If the object has accessor methods for the create timestamp field, and
|
75
75
|
# the create timestamp value is nil or overwriting it is allowed, set the
|
76
76
|
# create timestamp field to the time given or the current time. If setting
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 4
|
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 = 13
|
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
@@ -509,6 +509,13 @@ describe "A MySQL database" do
|
|
509
509
|
end
|
510
510
|
end
|
511
511
|
|
512
|
+
specify "should correctly format CREATE TABLE statements with foreign keys, when :key != the default (:id)" do
|
513
|
+
@db.create_table(:items){primary_key :id; Integer :other_than_id; foreign_key :p_id, :items, :key => :other_than_id, :null => false, :on_delete => :cascade}
|
514
|
+
check_sqls do
|
515
|
+
@db.sqls.should == ["CREATE TABLE `items` (`id` integer PRIMARY KEY AUTO_INCREMENT, `other_than_id` integer, `p_id` integer NOT NULL, UNIQUE (`other_than_id`), FOREIGN KEY (`p_id`) REFERENCES `items`(`other_than_id`) ON DELETE CASCADE)"]
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
512
519
|
specify "should correctly format ALTER TABLE statements with foreign keys" do
|
513
520
|
@db.create_table(:items){Integer :id}
|
514
521
|
@db.create_table(:users){primary_key :id}
|
@@ -3436,3 +3436,44 @@ describe 'pg_static_cache_updater extension' do
|
|
3436
3436
|
q.pop
|
3437
3437
|
end
|
3438
3438
|
end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && DB.server_version >= 90000
|
3439
|
+
|
3440
|
+
describe 'PostgreSQL enum types' do
|
3441
|
+
before(:all) do
|
3442
|
+
@db = DB
|
3443
|
+
@db.extension :pg_array, :pg_enum
|
3444
|
+
@db.create_enum(:test_enum, %w'a b c d')
|
3445
|
+
|
3446
|
+
@db.create_table!(:test_enumt) do
|
3447
|
+
test_enum :t
|
3448
|
+
end
|
3449
|
+
end
|
3450
|
+
after(:all) do
|
3451
|
+
@db.drop_table?(:test_enumt)
|
3452
|
+
@db.drop_enum(:test_enum)
|
3453
|
+
end
|
3454
|
+
|
3455
|
+
specify "should return correct entries in the schema" do
|
3456
|
+
s = @db.schema(:test_enumt)
|
3457
|
+
s.first.last[:type].should == :enum
|
3458
|
+
s.first.last[:enum_values].should == %w'a b c d'
|
3459
|
+
end
|
3460
|
+
|
3461
|
+
it "should add array parsers for enum values" do
|
3462
|
+
@db.get(Sequel.pg_array(%w'a b', :test_enum)).should == %w'a b'
|
3463
|
+
end if DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc
|
3464
|
+
|
3465
|
+
it "should set up model typecasting correctly" do
|
3466
|
+
c = Class.new(Sequel::Model(:test_enumt))
|
3467
|
+
o = c.new
|
3468
|
+
o.t = :a
|
3469
|
+
o.t.should == 'a'
|
3470
|
+
end
|
3471
|
+
|
3472
|
+
it "should add values to existing enum" do
|
3473
|
+
@db.add_enum_value(:test_enum, 'e')
|
3474
|
+
@db.add_enum_value(:test_enum, 'f', :after=>'a')
|
3475
|
+
@db.add_enum_value(:test_enum, 'g', :before=>'b')
|
3476
|
+
@db.add_enum_value(:test_enum, 'a', :if_not_exists=>true) if @db.server_version >= 90300
|
3477
|
+
@db.schema(:test_enumt, :reload=>true).first.last[:enum_values].should == %w'a f g b c d e'
|
3478
|
+
end if DB.server_version >= 90100
|
3479
|
+
end
|
data/spec/bin_spec.rb
CHANGED
@@ -56,6 +56,7 @@ describe "bin/sequel" do
|
|
56
56
|
bin(:args=>'-c "print DB.tables.inspect"').should == '[]'
|
57
57
|
DB.create_table(:a){Integer :a}
|
58
58
|
bin(:args=>'-c "print DB.tables.inspect"').should == '[:a]'
|
59
|
+
bin(:args=>'-v -c "print DB.tables.inspect"').should == "sequel #{Sequel.version}\n[:a]"
|
59
60
|
end
|
60
61
|
|
61
62
|
it "-C should copy databases" do
|
@@ -188,7 +189,7 @@ END
|
|
188
189
|
bin(:args=>'-t -c "lambda{lambda{lambda{raise \'foo\'}.call}.call}.call"', :stderr=>true).count("\n").should > 3
|
189
190
|
end
|
190
191
|
|
191
|
-
it "-v should output the Sequel version" do
|
192
|
+
it "-v should output the Sequel version and exit if database is not given" do
|
192
193
|
bin(:args=>"-v", :no_conn=>true).should == "sequel #{Sequel.version}\n"
|
193
194
|
end
|
194
195
|
|
@@ -201,6 +202,7 @@ END
|
|
201
202
|
bin(:args=>'-D -d', :stderr=>true).should == "Error: Cannot specify -D and -d together\n"
|
202
203
|
bin(:args=>'-m foo -d', :stderr=>true).should == "Error: Cannot specify -m and -d together\n"
|
203
204
|
bin(:args=>'-S foo -d', :stderr=>true).should == "Error: Cannot specify -S and -d together\n"
|
205
|
+
bin(:args=>'-S foo -C', :stderr=>true).should == "Error: Cannot specify -S and -C together\n"
|
204
206
|
end
|
205
207
|
|
206
208
|
it "should use a mock database if no database is given" do
|
@@ -243,6 +245,7 @@ END
|
|
243
245
|
bin(:post=>TMP_FILE).should == '[]'
|
244
246
|
DB.create_table(:a){Integer :a}
|
245
247
|
bin(:post=>TMP_FILE).should == '[:a]'
|
248
|
+
bin(:post=>TMP_FILE, :args=>'-v').should == "sequel #{Sequel.version}\n[:a]"
|
246
249
|
end
|
247
250
|
|
248
251
|
it "should run code provided on stdin" do
|
data/spec/core/database_spec.rb
CHANGED
@@ -1290,6 +1290,12 @@ describe "A broken adapter (lib is there but the class is not)" do
|
|
1290
1290
|
end
|
1291
1291
|
end
|
1292
1292
|
|
1293
|
+
describe "Sequel::Database.load_adapter" do
|
1294
|
+
specify "should not raise an error if subadapter does not exist" do
|
1295
|
+
Sequel::Database.load_adapter(:foo, :subdir=>'bar').should == nil
|
1296
|
+
end
|
1297
|
+
end
|
1298
|
+
|
1293
1299
|
describe "A single threaded database" do
|
1294
1300
|
after do
|
1295
1301
|
Sequel::Database.single_threaded = false
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -982,22 +982,17 @@ describe "Dataset#literal" do
|
|
982
982
|
d.literal(d).should == "(#{d.sql})"
|
983
983
|
end
|
984
984
|
|
985
|
-
specify "should literalize
|
986
|
-
|
987
|
-
|
988
|
-
@dataset.literal(
|
985
|
+
specify "should literalize times properly" do
|
986
|
+
@dataset.literal(Sequel::SQLTime.create(1, 2, 3, 500000)).should == "'01:02:03.500000'"
|
987
|
+
@dataset.literal(Time.local(2010, 1, 2, 3, 4, 5, 500000)).should == "'2010-01-02 03:04:05.500000'"
|
988
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).should == "'2010-01-02 03:04:05.500000'"
|
989
989
|
end
|
990
990
|
|
991
|
-
specify "should literalize
|
992
|
-
|
993
|
-
|
994
|
-
@dataset.literal(
|
995
|
-
|
996
|
-
|
997
|
-
specify "should literalize DateTime properly" do
|
998
|
-
t = DateTime.now
|
999
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1000
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction * (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}'"
|
991
|
+
specify "should literalize times properly for databases supporting millisecond precision" do
|
992
|
+
meta_def(@dataset, :timestamp_precision){3}
|
993
|
+
@dataset.literal(Sequel::SQLTime.create(1, 2, 3, 500000)).should == "'01:02:03.500'"
|
994
|
+
@dataset.literal(Time.local(2010, 1, 2, 3, 4, 5, 500000)).should == "'2010-01-02 03:04:05.500'"
|
995
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).should == "'2010-01-02 03:04:05.500'"
|
1001
996
|
end
|
1002
997
|
|
1003
998
|
specify "should literalize Date properly" do
|
@@ -1015,52 +1010,19 @@ describe "Dataset#literal" do
|
|
1015
1010
|
|
1016
1011
|
specify "should literalize Time, DateTime, Date properly if SQL standard format is required" do
|
1017
1012
|
meta_def(@dataset, :requires_sql_standard_datetimes?){true}
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}'"
|
1022
|
-
|
1023
|
-
t = DateTime.now
|
1024
|
-
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S")
|
1025
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}'"
|
1026
|
-
|
1027
|
-
d = Date.today
|
1028
|
-
s = d.strftime("DATE '%Y-%m-%d'")
|
1029
|
-
@dataset.literal(d).should == s
|
1013
|
+
@dataset.literal(Time.local(2010, 1, 2, 3, 4, 5, 500000)).should == "TIMESTAMP '2010-01-02 03:04:05.500000'"
|
1014
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).should == "TIMESTAMP '2010-01-02 03:04:05.500000'"
|
1015
|
+
@dataset.literal(Date.new(2010, 1, 2)).should == "DATE '2010-01-02'"
|
1030
1016
|
end
|
1031
1017
|
|
1032
1018
|
specify "should literalize Time and DateTime properly if the database support timezones in timestamps" do
|
1033
1019
|
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
1020
|
+
@dataset.literal(Time.utc(2010, 1, 2, 3, 4, 5, 500000)).should == "'2010-01-02 03:04:05.500000+0000'"
|
1021
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, Rational(55, 10))).should == "'2010-01-02 03:04:05.500000+0000'"
|
1034
1022
|
|
1035
|
-
t = Time.now.utc
|
1036
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1037
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}+0000'"
|
1038
|
-
|
1039
|
-
t = DateTime.now.new_offset(0)
|
1040
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1041
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}+0000'"
|
1042
|
-
end
|
1043
|
-
|
1044
|
-
specify "should literalize Time and DateTime properly if the database doesn't support usecs in timestamps" do
|
1045
1023
|
meta_def(@dataset, :supports_timestamp_usecs?){false}
|
1046
|
-
|
1047
|
-
|
1048
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1049
|
-
@dataset.literal(t).should == "#{s}'"
|
1050
|
-
|
1051
|
-
t = DateTime.now.new_offset(0)
|
1052
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1053
|
-
@dataset.literal(t).should == "#{s}'"
|
1054
|
-
|
1055
|
-
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
1056
|
-
|
1057
|
-
t = Time.now.utc
|
1058
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1059
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
1060
|
-
|
1061
|
-
t = DateTime.now.new_offset(0)
|
1062
|
-
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1063
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
1024
|
+
@dataset.literal(Time.utc(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
1025
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
1064
1026
|
end
|
1065
1027
|
|
1066
1028
|
specify "should not modify literal strings" do
|
@@ -3249,6 +3211,11 @@ describe "Dataset#insert_sql" do
|
|
3249
3211
|
specify "should accept an array of columns and an LiteralString" do
|
3250
3212
|
@ds.insert_sql([:a, :b, :c], Sequel.lit('VALUES (1, 2, 3)')).should == "INSERT INTO items (a, b, c) VALUES (1, 2, 3)"
|
3251
3213
|
end
|
3214
|
+
|
3215
|
+
specify "should use unaliased table name" do
|
3216
|
+
@ds.from(:items___i).insert_sql(1).should == "INSERT INTO items VALUES (1)"
|
3217
|
+
@ds.from(Sequel.as(:items, :i)).insert_sql(1).should == "INSERT INTO items VALUES (1)"
|
3218
|
+
end
|
3252
3219
|
end
|
3253
3220
|
|
3254
3221
|
describe "Dataset#inspect" do
|
@@ -3457,7 +3424,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3457
3424
|
before do
|
3458
3425
|
@db = Sequel.mock
|
3459
3426
|
@ds = @db[:items]
|
3460
|
-
meta_def(@ds, :
|
3427
|
+
meta_def(@ds, :insert_select_sql){|*v| "#{insert_sql(*v)} RETURNING *" }
|
3461
3428
|
end
|
3462
3429
|
|
3463
3430
|
specify "#call should take a type and bind hash and interpolate it" do
|
@@ -3935,7 +3902,12 @@ describe "Sequel timezone support" do
|
|
3935
3902
|
@dataset = @db.dataset
|
3936
3903
|
meta_def(@dataset, :supports_timestamp_timezones?){true}
|
3937
3904
|
meta_def(@dataset, :supports_timestamp_usecs?){false}
|
3938
|
-
@
|
3905
|
+
@utc_time = Time.utc(2010, 1, 2, 3, 4, 5)
|
3906
|
+
@local_time = Time.local(2010, 1, 2, 3, 4, 5)
|
3907
|
+
@offset = sprintf("%+03i%02i", *(@local_time.utc_offset/60).divmod(60))
|
3908
|
+
@dt_offset = @local_time.utc_offset/Rational(86400, 1)
|
3909
|
+
@utc_datetime = DateTime.new(2010, 1, 2, 3, 4, 5)
|
3910
|
+
@local_datetime = DateTime.new(2010, 1, 2, 3, 4, 5, @dt_offset)
|
3939
3911
|
end
|
3940
3912
|
after do
|
3941
3913
|
Sequel.default_timezone = nil
|
@@ -3944,50 +3916,26 @@ describe "Sequel timezone support" do
|
|
3944
3916
|
|
3945
3917
|
specify "should handle an database timezone of :utc when literalizing values" do
|
3946
3918
|
Sequel.database_timezone = :utc
|
3947
|
-
|
3948
|
-
|
3949
|
-
s = t.getutc.strftime("'%Y-%m-%d %H:%M:%S")
|
3950
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
3951
|
-
|
3952
|
-
t = DateTime.now
|
3953
|
-
s = t.new_offset(0).strftime("'%Y-%m-%d %H:%M:%S")
|
3954
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
3919
|
+
@dataset.literal(Time.utc(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
3920
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
3955
3921
|
end
|
3956
3922
|
|
3957
3923
|
specify "should handle an database timezone of :local when literalizing values" do
|
3958
3924
|
Sequel.database_timezone = :local
|
3959
|
-
|
3960
|
-
|
3961
|
-
s = t.getlocal.strftime("'%Y-%m-%d %H:%M:%S")
|
3962
|
-
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3963
|
-
|
3964
|
-
t = DateTime.now.new_offset(0)
|
3965
|
-
s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
|
3966
|
-
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3925
|
+
@dataset.literal(Time.local(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05#{@offset}'"
|
3926
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, 5, @dt_offset)).should == "'2010-01-02 03:04:05#{@offset}'"
|
3967
3927
|
end
|
3968
3928
|
|
3969
3929
|
specify "should have Database#timezone override Sequel.database_timezone" do
|
3970
3930
|
Sequel.database_timezone = :local
|
3971
3931
|
@db.timezone = :utc
|
3972
|
-
|
3973
|
-
|
3974
|
-
s = t.getutc.strftime("'%Y-%m-%d %H:%M:%S")
|
3975
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
3976
|
-
|
3977
|
-
t = DateTime.now
|
3978
|
-
s = t.new_offset(0).strftime("'%Y-%m-%d %H:%M:%S")
|
3979
|
-
@dataset.literal(t).should == "#{s}+0000'"
|
3932
|
+
@dataset.literal(Time.utc(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
3933
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05+0000'"
|
3980
3934
|
|
3981
3935
|
Sequel.database_timezone = :utc
|
3982
3936
|
@db.timezone = :local
|
3983
|
-
|
3984
|
-
|
3985
|
-
s = t.getlocal.strftime("'%Y-%m-%d %H:%M:%S")
|
3986
|
-
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3987
|
-
|
3988
|
-
t = DateTime.now.new_offset(0)
|
3989
|
-
s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
|
3990
|
-
@dataset.literal(t).should == "#{s}#{@offset}'"
|
3937
|
+
@dataset.literal(Time.local(2010, 1, 2, 3, 4, 5)).should == "'2010-01-02 03:04:05#{@offset}'"
|
3938
|
+
@dataset.literal(DateTime.new(2010, 1, 2, 3, 4, 5, @dt_offset)).should == "'2010-01-02 03:04:05#{@offset}'"
|
3991
3939
|
end
|
3992
3940
|
|
3993
3941
|
specify "should handle converting database timestamps into application timestamps" do
|
@@ -4418,9 +4366,10 @@ end
|
|
4418
4366
|
|
4419
4367
|
describe "Dataset#returning" do
|
4420
4368
|
before do
|
4421
|
-
@
|
4369
|
+
@db = Sequel.mock(:fetch=>proc{|s| {:foo=>s}})
|
4370
|
+
@db.extend_datasets{def supports_returning?(type) true end}
|
4371
|
+
@ds = @db[:t].returning(:foo)
|
4422
4372
|
@pr = proc do
|
4423
|
-
def @ds.supports_returning?(*) true end
|
4424
4373
|
sc = class << @ds; self; end
|
4425
4374
|
Sequel::Dataset.def_sql_method(sc, :delete, %w'delete from where returning')
|
4426
4375
|
Sequel::Dataset.def_sql_method(sc, :insert, %w'insert into columns values returning')
|
@@ -4458,6 +4407,11 @@ describe "Dataset#returning" do
|
|
4458
4407
|
@ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
|
4459
4408
|
@ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
|
4460
4409
|
end
|
4410
|
+
|
4411
|
+
specify "should raise an error if RETURNING is not supported" do
|
4412
|
+
@db.extend_datasets{def supports_returning?(type) false end}
|
4413
|
+
proc{@db[:t].returning}.should raise_error(Sequel::Error)
|
4414
|
+
end
|
4461
4415
|
end
|
4462
4416
|
|
4463
4417
|
describe "Dataset emulating bitwise operator support" do
|
@@ -4943,3 +4897,59 @@ describe "Dataset emulated complex expression operators" do
|
|
4943
4897
|
@ds.literal(~@n).should == "((0 - x) - 1)"
|
4944
4898
|
end
|
4945
4899
|
end
|
4900
|
+
|
4901
|
+
describe "#joined_dataset?" do
|
4902
|
+
before do
|
4903
|
+
@ds = Sequel.mock.dataset
|
4904
|
+
end
|
4905
|
+
|
4906
|
+
it "should be false if the dataset has 0 or 1 from table" do
|
4907
|
+
@ds.joined_dataset?.should == false
|
4908
|
+
@ds.from(:a).joined_dataset?.should == false
|
4909
|
+
end
|
4910
|
+
|
4911
|
+
it "should be true if the dataset has 2 or more from tables" do
|
4912
|
+
@ds.from(:a, :b).joined_dataset?.should == true
|
4913
|
+
@ds.from(:a, :b, :c).joined_dataset?.should == true
|
4914
|
+
end
|
4915
|
+
|
4916
|
+
it "should be true if the dataset has any join tables" do
|
4917
|
+
@ds.from(:a).cross_join(:b).joined_dataset?.should == true
|
4918
|
+
end
|
4919
|
+
end
|
4920
|
+
|
4921
|
+
describe "#unqualified_column_for" do
|
4922
|
+
before do
|
4923
|
+
@ds = Sequel.mock.dataset
|
4924
|
+
end
|
4925
|
+
|
4926
|
+
it "should handle Symbols" do
|
4927
|
+
@ds.unqualified_column_for(:a).should == Sequel.identifier('a')
|
4928
|
+
@ds.unqualified_column_for(:b__a).should == Sequel.identifier('a')
|
4929
|
+
@ds.unqualified_column_for(:a___c).should == Sequel.identifier('a').as('c')
|
4930
|
+
@ds.unqualified_column_for(:b__a___c).should == Sequel.identifier('a').as('c')
|
4931
|
+
end
|
4932
|
+
|
4933
|
+
it "should handle SQL::Identifiers" do
|
4934
|
+
@ds.unqualified_column_for(Sequel.identifier(:a)).should == Sequel.identifier(:a)
|
4935
|
+
end
|
4936
|
+
|
4937
|
+
it "should handle SQL::QualifiedIdentifiers" do
|
4938
|
+
@ds.unqualified_column_for(Sequel.qualify(:b, :a)).should == Sequel.identifier('a')
|
4939
|
+
@ds.unqualified_column_for(Sequel.qualify(:b, 'a')).should == Sequel.identifier('a')
|
4940
|
+
end
|
4941
|
+
|
4942
|
+
it "should handle SQL::AliasedExpressions" do
|
4943
|
+
@ds.unqualified_column_for(Sequel.qualify(:b, :a).as(:c)).should == Sequel.identifier('a').as(:c)
|
4944
|
+
end
|
4945
|
+
|
4946
|
+
it "should return nil for other objects" do
|
4947
|
+
@ds.unqualified_column_for(Object.new).should == nil
|
4948
|
+
@ds.unqualified_column_for('a').should == nil
|
4949
|
+
end
|
4950
|
+
|
4951
|
+
it "should return nil for other objects inside SQL::AliasedExpressions" do
|
4952
|
+
@ds.unqualified_column_for(Sequel.as(Object.new, 'a')).should == nil
|
4953
|
+
@ds.unqualified_column_for(Sequel.as('a', 'b')).should == nil
|
4954
|
+
end
|
4955
|
+
end
|