sequel 3.15.0 → 3.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -0
- data/README.rdoc +1 -1
- data/Rakefile +1 -1
- data/doc/dataset_filtering.rdoc +7 -0
- data/doc/release_notes/3.16.0.txt +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +8 -5
- data/lib/sequel/adapters/shared/postgres.rb +2 -1
- data/lib/sequel/adapters/swift.rb +145 -0
- data/lib/sequel/adapters/swift/mysql.rb +48 -0
- data/lib/sequel/adapters/swift/postgres.rb +92 -0
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/dataset/sql.rb +1 -1
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/plugins/identity_map.rb +23 -13
- data/lib/sequel/plugins/rcte_tree.rb +10 -6
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +16 -6
- data/spec/core/dataset_spec.rb +3 -3
- data/spec/extensions/identity_map_spec.rb +21 -4
- data/spec/integration/dataset_test.rb +4 -2
- data/spec/integration/plugin_test.rb +22 -3
- data/spec/integration/timezone_test.rb +3 -3
- data/spec/integration/type_test.rb +3 -2
- metadata +9 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
=== 3.16.0 (2010-10-01)
|
2
|
+
|
3
|
+
* Support composite foreign keys for associations in the identity_map plugin (harukizaemon, jeremyevans) (#310)
|
4
|
+
|
5
|
+
* Handle INTERSECT and EXCEPT on Microsoft SQL Server 2005+ (jfirebaugh)
|
6
|
+
|
7
|
+
* Add :replace option to Database#create_language in the postgresql adapter (jeremyevans)
|
8
|
+
|
9
|
+
* Make rcte_tree plugin work when not all columns are selected (jeremyevans)
|
10
|
+
|
11
|
+
* Add swift adapter (jeremyevans)
|
12
|
+
|
13
|
+
* Fix literalization of DateTime objects on 1.9 for databases that support fractional seconds (jeremyevans)
|
14
|
+
|
1
15
|
=== 3.15.0 (2010-09-01)
|
2
16
|
|
3
17
|
* Make emulated alter_table tasks on SQLite correctly preserve foreign keys (DirtYiCE, jeremyevans)
|
data/README.rdoc
CHANGED
@@ -13,7 +13,7 @@ toolkit for Ruby.
|
|
13
13
|
configurations, and database sharding.
|
14
14
|
* Sequel currently has adapters for ADO, Amalgalite, DataObjects,
|
15
15
|
DB2, DBI, Firebird, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase,
|
16
|
-
Oracle, PostgreSQL and
|
16
|
+
Oracle, PostgreSQL, SQLite3, and Swift.
|
17
17
|
|
18
18
|
== Resources
|
19
19
|
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ VERS = lambda do
|
|
12
12
|
require File.expand_path("../lib/sequel/version", __FILE__)
|
13
13
|
Sequel.version
|
14
14
|
end
|
15
|
-
CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*"]
|
15
|
+
CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*", '**/*.rbc']
|
16
16
|
RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'Sequel: The Database Toolkit for Ruby']
|
17
17
|
RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
|
18
18
|
|
data/doc/dataset_filtering.rdoc
CHANGED
@@ -165,3 +165,10 @@ One of the best features of Sequel is the ability to use datasets as sub-queries
|
|
165
165
|
#=> "SELECT * FROM consumers WHERE (id IN (SELECT consumer_id FROM consumer_refs WHERE logged_in))"
|
166
166
|
|
167
167
|
Note that if you are checking for the inclusion of a single column in a subselect, the subselect should only select a single column.
|
168
|
+
|
169
|
+
== Using OR instead of AND
|
170
|
+
|
171
|
+
By default, if you chain calls to +filter+, the conditions get ANDed together. If you want to use an OR for a condition, you can use the +or+ method:
|
172
|
+
|
173
|
+
items.filter(:name=>'Food').or(:vendor=>1).sql
|
174
|
+
#=> "SELECT * FROM items WHERE ((name = 'Food') OR (vendor = 1))"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
= New Adapter
|
2
|
+
|
3
|
+
* A swift adapter was added to Sequel. Swift is a relatively new
|
4
|
+
ruby database library, built on top of a relatively new backend
|
5
|
+
called dbic++. While not yet considered production ready, it is
|
6
|
+
very fast. The swift adapter is about 33% faster and 40% more
|
7
|
+
memory efficient for selects than the postgres adapter using pg
|
8
|
+
with sequel_pg, though it is slower and less memory efficient
|
9
|
+
for inserts and updates.
|
10
|
+
|
11
|
+
Sequel's swift adapter currently supports only PostgreSQL and
|
12
|
+
MySQL, but support for other databases will probably be added in
|
13
|
+
the future.
|
14
|
+
|
15
|
+
= Other Improvements
|
16
|
+
|
17
|
+
* Sequel now correctly literalizes DateTime objects on ruby 1.9 for
|
18
|
+
databases that support fractional seconds.
|
19
|
+
|
20
|
+
* The identity_map plugin now handles composite keys in many_to_one
|
21
|
+
associations.
|
22
|
+
|
23
|
+
* The rcte_tree plugin now works when the model's dataset does not
|
24
|
+
select all columns. This can happen when using the lazy_attributes
|
25
|
+
plugin on the same model.
|
26
|
+
|
27
|
+
* Sequel now supports INTERSECT and EXCEPT on Microsoft SQL Server
|
28
|
+
2005+.
|
29
|
+
|
30
|
+
* The Database#create_language method in the shared PostgreSQL
|
31
|
+
adapter now accepts a :replace option to replace the currently
|
32
|
+
loaded procedural language if it already exists. This option
|
33
|
+
is ignored for PostgreSQL versions before 9.0.
|
34
|
+
|
35
|
+
* The identity_map plugin now handles cases where the plugin is
|
36
|
+
loaded separately by two different models.
|
37
|
+
|
38
|
+
= Backwards Compatibility
|
39
|
+
|
40
|
+
* While not technically backwards compatibility related, it was
|
41
|
+
discovered that the identity_map plugin is incompatible with
|
42
|
+
the standard eager loading of many_to_many and many_through_many
|
43
|
+
associations. If you want to eagerly load those associations and
|
44
|
+
use the identity_map plugin, you should use eager_graph instead
|
45
|
+
of eager.
|
@@ -339,9 +339,9 @@ module Sequel
|
|
339
339
|
db.server_version(@opts[:server])
|
340
340
|
end
|
341
341
|
|
342
|
-
#
|
342
|
+
# MSSQL 2005+ supports INTERSECT and EXCEPT
|
343
343
|
def supports_intersect_except?
|
344
|
-
|
344
|
+
is_2005_or_later?
|
345
345
|
end
|
346
346
|
|
347
347
|
# MSSQL does not support IS TRUE
|
@@ -356,7 +356,7 @@ module Sequel
|
|
356
356
|
|
357
357
|
# MSSQL 2005+ supports modifying joined datasets
|
358
358
|
def supports_modifying_joins?
|
359
|
-
|
359
|
+
is_2005_or_later?
|
360
360
|
end
|
361
361
|
|
362
362
|
# MSSQL does not support multiple columns for the IN/NOT IN operators
|
@@ -364,9 +364,9 @@ module Sequel
|
|
364
364
|
false
|
365
365
|
end
|
366
366
|
|
367
|
-
#
|
367
|
+
# MSSQL 2005+ supports the output clause.
|
368
368
|
def supports_output_clause?
|
369
|
-
|
369
|
+
is_2005_or_later?
|
370
370
|
end
|
371
371
|
|
372
372
|
# MSSQL 2005+ supports window functions
|
@@ -381,6 +381,9 @@ module Sequel
|
|
381
381
|
end
|
382
382
|
|
383
383
|
private
|
384
|
+
def is_2005_or_later?
|
385
|
+
server_version >= 9000000
|
386
|
+
end
|
384
387
|
|
385
388
|
# MSSQL supports the OUTPUT clause for DELETE statements.
|
386
389
|
# It also allows prepending a WITH clause.
|
@@ -196,6 +196,7 @@ module Sequel
|
|
196
196
|
# * name : Name of the procedural language (e.g. plpgsql)
|
197
197
|
# * opts : options hash:
|
198
198
|
# * :handler : The name of a previously registered function used as a call handler for this language.
|
199
|
+
# * :replace: Replace the installed language if it already exists (on PostgreSQL 9.0+).
|
199
200
|
# * :trusted : Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
|
200
201
|
# * :validator : The name of previously registered function used as a validator of functions defined in this language.
|
201
202
|
def create_language(name, opts={})
|
@@ -428,7 +429,7 @@ module Sequel
|
|
428
429
|
|
429
430
|
# SQL for creating a procedural language.
|
430
431
|
def create_language_sql(name, opts={})
|
431
|
-
"CREATE#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
432
|
+
"CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
432
433
|
end
|
433
434
|
|
434
435
|
# SQL for creating a database trigger.
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'swift'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
# Module holding the Swift support for Sequel. Swift is a
|
5
|
+
# ruby front-end for dbic++, a fast database access library
|
6
|
+
# written in C++.
|
7
|
+
#
|
8
|
+
# The Swift adapter currently supports PostgreSQL and MySQL:
|
9
|
+
#
|
10
|
+
# Sequel.connect('swift://user:password@host/database?db_type=postgres')
|
11
|
+
# Sequel.connect('swift://user:password@host/database?db_type=mysql')
|
12
|
+
module Swift
|
13
|
+
# Contains procs keyed on sub adapter type that extend the
|
14
|
+
# given database object so it supports the correct database type.
|
15
|
+
DATABASE_SETUP = {:postgres=>proc do |db|
|
16
|
+
Sequel.ts_require 'adapters/swift/postgres'
|
17
|
+
db.extend(Sequel::Swift::Postgres::DatabaseMethods)
|
18
|
+
db.swift_class = ::Swift::DB::Postgres
|
19
|
+
end,
|
20
|
+
:mysql=>proc do |db|
|
21
|
+
Sequel.ts_require 'adapters/swift/mysql'
|
22
|
+
db.extend(Sequel::Swift::MySQL::DatabaseMethods)
|
23
|
+
db.swift_class = ::Swift::DB::Mysql
|
24
|
+
end,
|
25
|
+
}
|
26
|
+
|
27
|
+
class Database < Sequel::Database
|
28
|
+
set_adapter_scheme :swift
|
29
|
+
|
30
|
+
# The Swift adapter class being used by this database. Connections
|
31
|
+
# in this database's connection pool will be instances of this class.
|
32
|
+
attr_accessor :swift_class
|
33
|
+
|
34
|
+
# Call the DATABASE_SETUP proc directly after initialization,
|
35
|
+
# so the object always uses sub adapter specific code. Also,
|
36
|
+
# raise an error immediately if the connection doesn't have a
|
37
|
+
# db_type specified, since one is required to include the correct
|
38
|
+
# subadapter.
|
39
|
+
def initialize(opts)
|
40
|
+
super
|
41
|
+
if db_type = opts[:db_type] and !db_type.to_s.empty?
|
42
|
+
if prok = DATABASE_SETUP[db_type.to_s.to_sym]
|
43
|
+
prok.call(self)
|
44
|
+
else
|
45
|
+
raise(Error, "No :db_type option specified")
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise(Error, ":db_type option not valid, should be postgres or mysql")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create an instance of swift_class for the given options.
|
53
|
+
def connect(server)
|
54
|
+
setup_connection(swift_class.new(server_opts(server)))
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return a Sequel::Swift::Dataset object for this database.
|
58
|
+
def dataset(opts = nil)
|
59
|
+
Swift::Dataset.new(self, opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Execute the given SQL, yielding a Swift::Result if a block is given.
|
63
|
+
def execute(sql, opts={})
|
64
|
+
synchronize(opts[:server]) do |conn|
|
65
|
+
begin
|
66
|
+
res = nil
|
67
|
+
log_yield(sql){res = conn.prepare(sql).execute}
|
68
|
+
yield res if block_given?
|
69
|
+
nil
|
70
|
+
rescue SwiftError => e
|
71
|
+
raise_error(e)
|
72
|
+
ensure
|
73
|
+
res.finish if res
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Execute the SQL on the this database, returning the number of affected
|
79
|
+
# rows.
|
80
|
+
def execute_dui(sql, opts={})
|
81
|
+
synchronize(opts[:server]) do |conn|
|
82
|
+
begin
|
83
|
+
log_yield(sql){conn.execute(sql)}
|
84
|
+
rescue SwiftError => e
|
85
|
+
raise_error(e)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Execute the SQL on this database, returning the primary key of the
|
91
|
+
# table being inserted to.
|
92
|
+
def execute_insert(sql, opts={})
|
93
|
+
synchronize(opts[:server]) do |conn|
|
94
|
+
begin
|
95
|
+
log_yield(sql){conn.prepare(sql).execute.insert_id}
|
96
|
+
rescue SwiftError => e
|
97
|
+
raise_error(e)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Method to call on a statement object to execute SQL that does
|
105
|
+
# not return any rows.
|
106
|
+
def connection_execute_method
|
107
|
+
:execute
|
108
|
+
end
|
109
|
+
|
110
|
+
# Close the given database connection.
|
111
|
+
def disconnect_connection(c)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Execute SQL on the connection
|
115
|
+
def log_connection_execute(conn, sql)
|
116
|
+
log_yield(sql){conn.execute(sql)}
|
117
|
+
end
|
118
|
+
|
119
|
+
# Set the :db entry to the same as the :database entry, since
|
120
|
+
# Swift uses :db.
|
121
|
+
def server_opts(o)
|
122
|
+
o = super
|
123
|
+
o[:db] ||= o[:database]
|
124
|
+
o
|
125
|
+
end
|
126
|
+
|
127
|
+
# Allow extending the given connection when it is first created.
|
128
|
+
# By default, just returns the connection.
|
129
|
+
def setup_connection(conn)
|
130
|
+
conn
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Dataset < Sequel::Dataset
|
135
|
+
# Set the columns and yield the hashes to the block.
|
136
|
+
def fetch_rows(sql, &block)
|
137
|
+
execute(sql) do |res|
|
138
|
+
@columns = res.fields
|
139
|
+
res.each(&block)
|
140
|
+
end
|
141
|
+
self
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
Sequel.require 'adapters/shared/mysql'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Swift
|
5
|
+
# Database and Dataset instance methods for MySQL specific
|
6
|
+
# support via Swift.
|
7
|
+
module MySQL
|
8
|
+
# Database instance methods for MySQL databases accessed via Swift.
|
9
|
+
module DatabaseMethods
|
10
|
+
include Sequel::MySQL::DatabaseMethods
|
11
|
+
|
12
|
+
# Return instance of Sequel::Swift::MySQL::Dataset with the given opts.
|
13
|
+
def dataset(opts=nil)
|
14
|
+
Sequel::Swift::MySQL::Dataset.new(self, opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# The database name for the given database.
|
20
|
+
def database_name
|
21
|
+
opts[:database]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Consider tinyint(1) columns as boolean.
|
25
|
+
def schema_column_type(db_type)
|
26
|
+
db_type == 'tinyint(1)' ? :boolean : super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Dataset class for MySQL datasets accessed via Swift.
|
31
|
+
class Dataset < Swift::Dataset
|
32
|
+
include Sequel::MySQL::DatasetMethods
|
33
|
+
|
34
|
+
# Use execute_insert to execute the replace_sql.
|
35
|
+
def replace(*args)
|
36
|
+
execute_insert(replace_sql(*args))
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Use Swift's escape method for quoting.
|
42
|
+
def literal_string(s)
|
43
|
+
db.synchronize{|c| "'#{c.escape(s)}'"}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
Sequel.require 'adapters/shared/postgres'
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
Postgres::CONVERTED_EXCEPTIONS << ::SwiftError
|
5
|
+
|
6
|
+
module Swift
|
7
|
+
# Adapter, Database, and Dataset support for accessing a PostgreSQL
|
8
|
+
# database via Swift.
|
9
|
+
module Postgres
|
10
|
+
# Methods to add to the Swift adapter/connection to allow it to work
|
11
|
+
# with the shared PostgreSQL code.
|
12
|
+
module AdapterMethods
|
13
|
+
include Sequel::Postgres::AdapterMethods
|
14
|
+
|
15
|
+
# Log all SQL that goes through the execute method to the related
|
16
|
+
# database object.
|
17
|
+
def execute(sql, *args, &block)
|
18
|
+
@db.log_yield(sql){super}
|
19
|
+
rescue SwiftError => e
|
20
|
+
@db.send(:raise_error, e)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Swift specific method of getting specific values from a result set.
|
26
|
+
def single_value(row)
|
27
|
+
row.values.at(0)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Methods to add to Database instances that access PostgreSQL via Swift.
|
32
|
+
module DatabaseMethods
|
33
|
+
include Sequel::Postgres::DatabaseMethods
|
34
|
+
|
35
|
+
# Add the primary_keys and primary_key_sequences instance variables,
|
36
|
+
# so we can get the correct return values for inserted rows.
|
37
|
+
def self.extended(db)
|
38
|
+
db.instance_eval do
|
39
|
+
@primary_keys = {}
|
40
|
+
@primary_key_sequences = {}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return instance of Sequel::Swift::Postgres::Dataset with the given opts.
|
45
|
+
def dataset(opts=nil)
|
46
|
+
Sequel::Swift::Postgres::Dataset.new(self, opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Run the DELETE/UPDATE SQL on the database and return the number
|
50
|
+
# of matched rows.
|
51
|
+
def execute_dui(sql, opts={})
|
52
|
+
synchronize(opts[:server]) do |conn|
|
53
|
+
begin
|
54
|
+
conn.execute(sql)
|
55
|
+
rescue SwiftError => e
|
56
|
+
raise_error(e)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Run the INSERT SQL on the database and return the primary key
|
62
|
+
# for the record.
|
63
|
+
def execute_insert(sql, opts={})
|
64
|
+
synchronize(opts[:server]) do |conn|
|
65
|
+
conn.execute(sql)
|
66
|
+
insert_result(conn, opts[:table], opts[:values])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Execute SQL on the connection.
|
73
|
+
def log_connection_execute(conn, sql)
|
74
|
+
conn.execute(sql)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Extend the adapter with the Swift PostgreSQL AdapterMethods.
|
78
|
+
def setup_connection(conn)
|
79
|
+
conn = super(conn)
|
80
|
+
conn.extend(Sequel::Swift::Postgres::AdapterMethods)
|
81
|
+
conn.db = self
|
82
|
+
conn.apply_connection_settings
|
83
|
+
conn
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Dataset < Swift::Dataset
|
88
|
+
include Sequel::Postgres::DatasetMethods
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
# ---------------------
|
7
7
|
|
8
8
|
# Array of supported database adapters
|
9
|
-
ADAPTERS = %w'ado amalgalite db2 dbi do firebird informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite'.collect{|x| x.to_sym}
|
9
|
+
ADAPTERS = %w'ado amalgalite db2 dbi do firebird informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite swift'.collect{|x| x.to_sym}
|
10
10
|
|
11
11
|
# Whether to use the single threaded connection pool by default
|
12
12
|
@@single_threaded = false
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -631,7 +631,7 @@ module Sequel
|
|
631
631
|
def select(*columns, &block)
|
632
632
|
columns += Array(Sequel.virtual_row(&block)) if block
|
633
633
|
m = []
|
634
|
-
columns.
|
634
|
+
columns.each do |i|
|
635
635
|
i.is_a?(Hash) ? m.concat(i.map{|k, v| SQL::AliasedExpression.new(k,v)}) : m << i
|
636
636
|
end
|
637
637
|
clone(:select => m)
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -576,7 +576,7 @@ module Sequel
|
|
576
576
|
v2 = Sequel.application_to_database_timestamp(v)
|
577
577
|
fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
|
578
578
|
if m == '%N'
|
579
|
-
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*86400000000 : v.usec) if supports_timestamp_usecs?
|
579
|
+
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000) : v.usec) if supports_timestamp_usecs?
|
580
580
|
else
|
581
581
|
if supports_timestamp_timezones?
|
582
582
|
# Would like to just use %z format, but it doesn't appear to work on Windows
|
data/lib/sequel/model/base.rb
CHANGED
@@ -236,8 +236,8 @@ module Sequel
|
|
236
236
|
# # INSERT INTO artists (name) VALUES ('Bob')
|
237
237
|
#
|
238
238
|
# Artist.find_or_create(:name=>'Jim'){|a| a.hometown = 'Sactown'}
|
239
|
-
# # SELECT * FROM artists WHERE (name = '
|
240
|
-
# # INSERT INTO artists (name, hometown) VALUES ('
|
239
|
+
# # SELECT * FROM artists WHERE (name = 'Jim') LIMIT 1
|
240
|
+
# # INSERT INTO artists (name, hometown) VALUES ('Jim', 'Sactown')
|
241
241
|
def find_or_create(cond, &block)
|
242
242
|
find(cond) || create(cond, &block)
|
243
243
|
end
|
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
# The identity_map plugin allows the user to create temporary identity maps
|
4
4
|
# via the with_identity_map method, which takes a block. Inside the block,
|
5
5
|
# objects have a 1-1 correspondence with rows in the database.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# For example, the following is true, and wouldn't be true if you weren't
|
8
8
|
# using the identity map:
|
9
9
|
# Sequel::Model.with_identity_map do
|
@@ -19,9 +19,13 @@ module Sequel
|
|
19
19
|
# in the identity map, the record is returned without a database query being
|
20
20
|
# issued.
|
21
21
|
#
|
22
|
-
# Identity maps are thread-local and only
|
22
|
+
# Identity maps are thread-local and only persist for the duration of the block,
|
23
23
|
# so they should only be considered as a possible performance enhancer.
|
24
|
-
#
|
24
|
+
#
|
25
|
+
# The identity_map plugin is not compatible with the standard eager loading of
|
26
|
+
# many_to_many and many_through_many associations. If you want to use the identity_map plugin,
|
27
|
+
# you should use +eager_graph+ instead of +eager+ for those associations.
|
28
|
+
#
|
25
29
|
# Usage:
|
26
30
|
#
|
27
31
|
# # Use an identity map that will affect all model classes (called before loading subclasses)
|
@@ -37,13 +41,13 @@ module Sequel
|
|
37
41
|
def identity_map
|
38
42
|
Thread.current[:sequel_identity_map]
|
39
43
|
end
|
40
|
-
|
44
|
+
|
41
45
|
# The identity map key for an object of the current class with the given pk.
|
42
46
|
# May not always be correct for a class which uses STI.
|
43
47
|
def identity_map_key(pk)
|
44
48
|
"#{self}:#{pk ? Array(pk).join(',') : "nil:#{rand}"}"
|
45
49
|
end
|
46
|
-
|
50
|
+
|
47
51
|
# If the identity map is in use, check it for a current copy of the object.
|
48
52
|
# If a copy does not exist, create a new object and add it to the identity map.
|
49
53
|
# If a copy exists, add any values in the given row that aren't currently
|
@@ -61,7 +65,7 @@ module Sequel
|
|
61
65
|
end
|
62
66
|
o
|
63
67
|
end
|
64
|
-
|
68
|
+
|
65
69
|
# Take a block and inside that block use an identity map to ensure a 1-1
|
66
70
|
# correspondence of objects to the database row they represent.
|
67
71
|
def with_identity_map
|
@@ -73,14 +77,14 @@ module Sequel
|
|
73
77
|
self.identity_map = nil
|
74
78
|
end
|
75
79
|
end
|
76
|
-
|
80
|
+
|
77
81
|
private
|
78
82
|
|
79
|
-
# Set the thread local identity map to the given value.
|
80
|
-
def identity_map=(v)
|
83
|
+
# Set the thread local identity map to the given value.
|
84
|
+
def identity_map=(v)
|
81
85
|
Thread.current[:sequel_identity_map] = v
|
82
86
|
end
|
83
|
-
|
87
|
+
|
84
88
|
# Check the current identity map if it exists for the object with
|
85
89
|
# the matching pk. If one is found, return it, otherwise call super.
|
86
90
|
def primary_key_lookup(pk)
|
@@ -105,15 +109,21 @@ module Sequel
|
|
105
109
|
end
|
106
110
|
|
107
111
|
private
|
108
|
-
|
112
|
+
|
113
|
+
# The primary keys values of the associated object, given the foreign
|
114
|
+
# key columns(s).
|
115
|
+
def _associated_object_pk(fk)
|
116
|
+
fk.is_a?(Array) ? fk.map{|c| send(c)} : send(fk)
|
117
|
+
end
|
118
|
+
|
109
119
|
# If the association is a many_to_one and it has a :key option and the
|
110
120
|
# key option has a value and the association uses the primary key of
|
111
121
|
# the associated class as the :primary_key option, check the identity
|
112
122
|
# map for the associated object and return it if present.
|
113
123
|
def _load_associated_objects(opts)
|
114
124
|
klass = opts.associated_class
|
115
|
-
if idm =
|
116
|
-
opts[:key] and pk =
|
125
|
+
if klass.respond_to?(:identity_map) && idm = klass.identity_map and opts[:type] == :many_to_one and opts[:primary_key] == klass.primary_key and
|
126
|
+
opts[:key] and pk = _associated_object_pk(opts[:key]) and o = idm[klass.identity_map_key(pk)]
|
117
127
|
o
|
118
128
|
else
|
119
129
|
super
|
@@ -125,7 +125,7 @@ module Sequel
|
|
125
125
|
end
|
126
126
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
127
127
|
model.from(t => table_alias).
|
128
|
-
with_recursive(t, base_ds,
|
128
|
+
with_recursive(t, base_ds.select_all,
|
129
129
|
recursive_ds.
|
130
130
|
select(c_all))
|
131
131
|
end
|
@@ -172,12 +172,14 @@ module Sequel
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
175
|
-
model.eager_loading_dataset(r,
|
175
|
+
elds = model.eager_loading_dataset(r,
|
176
176
|
model.from(t => table_alias).
|
177
177
|
with_recursive(t, base_case,
|
178
178
|
recursive_case),
|
179
179
|
r.select,
|
180
|
-
eo[:associations], eo)
|
180
|
+
eo[:associations], eo)
|
181
|
+
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
182
|
+
elds.all do |obj|
|
181
183
|
opk = obj[prkey]
|
182
184
|
if in_pm = parent_map.has_key?(opk)
|
183
185
|
if idm_obj = parent_map[opk]
|
@@ -220,7 +222,7 @@ module Sequel
|
|
220
222
|
end
|
221
223
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
222
224
|
model.from(t => table_alias).
|
223
|
-
with_recursive(t, base_ds,
|
225
|
+
with_recursive(t, base_ds.select_all,
|
224
226
|
recursive_ds.
|
225
227
|
select(SQL::ColumnAll.new(model.table_name)))
|
226
228
|
end
|
@@ -273,10 +275,12 @@ module Sequel
|
|
273
275
|
recursive_case = recursive_case.select_more(SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(t, la) + 1, la)).filter(SQL::QualifiedIdentifier.new(t, la) < level - 1)
|
274
276
|
end
|
275
277
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
276
|
-
model.eager_loading_dataset(r,
|
278
|
+
elds = model.eager_loading_dataset(r,
|
277
279
|
model.from(t => table_alias).with_recursive(t, base_case, recursive_case),
|
278
280
|
r.select,
|
279
|
-
associations, eo)
|
281
|
+
associations, eo)
|
282
|
+
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
283
|
+
elds.all do |obj|
|
280
284
|
if level
|
281
285
|
no_cache = no_cache_level == obj.values.delete(la)
|
282
286
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 3
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 16
|
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
@@ -905,7 +905,7 @@ context "MySQL::Dataset#complex_expression_sql" do
|
|
905
905
|
end
|
906
906
|
end
|
907
907
|
|
908
|
-
|
908
|
+
if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc
|
909
909
|
context "MySQL Stored Procedures" do
|
910
910
|
before do
|
911
911
|
MYSQL_DB.create_table(:items){Integer :id; Integer :value}
|
@@ -918,7 +918,7 @@ unless MYSQL_DB.adapter_scheme == :do or MYSQL_DB.adapter_scheme == :mysql2
|
|
918
918
|
end
|
919
919
|
|
920
920
|
specify "should be callable on the database object" do
|
921
|
-
MYSQL_DB.
|
921
|
+
MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc() BEGIN DELETE FROM items; END')
|
922
922
|
MYSQL_DB[:items].delete
|
923
923
|
MYSQL_DB[:items].insert(:value=>1)
|
924
924
|
MYSQL_DB[:items].count.should == 1
|
@@ -927,7 +927,7 @@ unless MYSQL_DB.adapter_scheme == :do or MYSQL_DB.adapter_scheme == :mysql2
|
|
927
927
|
end
|
928
928
|
|
929
929
|
specify "should be callable on the dataset object" do
|
930
|
-
MYSQL_DB.
|
930
|
+
MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
|
931
931
|
MYSQL_DB[:items].delete
|
932
932
|
@d = MYSQL_DB[:items]
|
933
933
|
@d.call_sproc(:select, :test_sproc, 3).should == []
|
@@ -938,7 +938,7 @@ unless MYSQL_DB.adapter_scheme == :do or MYSQL_DB.adapter_scheme == :mysql2
|
|
938
938
|
end
|
939
939
|
|
940
940
|
specify "should be callable on the dataset object with multiple arguments" do
|
941
|
-
MYSQL_DB.
|
941
|
+
MYSQL_DB.execute_ddl('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
|
942
942
|
MYSQL_DB[:items].delete
|
943
943
|
@d = MYSQL_DB[:items]
|
944
944
|
@d.call_sproc(:select, :test_sproc, 3, 4).should == []
|
@@ -189,7 +189,6 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
|
|
189
189
|
String :name
|
190
190
|
end
|
191
191
|
@ds = POSTGRES_DB[:items]
|
192
|
-
clear_sqls
|
193
192
|
end
|
194
193
|
after do
|
195
194
|
POSTGRES_DB.drop_table(:items)
|
@@ -241,11 +240,22 @@ context "A PostgreSQL dataset with a timestamp field" do
|
|
241
240
|
@d.delete
|
242
241
|
end
|
243
242
|
|
244
|
-
cspecify "should store milliseconds in time fields", :do do
|
243
|
+
cspecify "should store milliseconds in time fields for Time objects", :do do
|
245
244
|
t = Time.now
|
246
245
|
@d << {:value=>1, :time=>t}
|
247
|
-
@d
|
248
|
-
@d
|
246
|
+
t2 = @d[:value =>'1'][:time]
|
247
|
+
@d.literal(t2).should == @d.literal(t)
|
248
|
+
t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
|
249
|
+
t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.usec
|
250
|
+
end
|
251
|
+
|
252
|
+
cspecify "should store milliseconds in time fields for DateTime objects", :do do
|
253
|
+
t = DateTime.now
|
254
|
+
@d << {:value=>1, :time=>t}
|
255
|
+
t2 = @d[:value =>'1'][:time]
|
256
|
+
@d.literal(t2).should == @d.literal(t)
|
257
|
+
t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
|
258
|
+
t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.strftime('%N').to_i/1000
|
249
259
|
end
|
250
260
|
end
|
251
261
|
|
@@ -930,12 +940,12 @@ context "Postgres::Database functions, languages, and triggers" do
|
|
930
940
|
|
931
941
|
specify "#create_language and #drop_language should create and drop languages" do
|
932
942
|
@d.send(:create_language_sql, :plpgsql).should == 'CREATE LANGUAGE plpgsql'
|
933
|
-
@d.create_language(:plpgsql)
|
943
|
+
@d.create_language(:plpgsql, :replace=>true)
|
934
944
|
proc{@d.create_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
935
945
|
@d.send(:drop_language_sql, :plpgsql).should == 'DROP LANGUAGE plpgsql'
|
936
946
|
@d.drop_language(:plpgsql)
|
937
947
|
proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
938
|
-
@d.send(:create_language_sql, :plpgsql, :trusted=>true, :handler=>:a, :validator=>:b).should == 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b'
|
948
|
+
@d.send(:create_language_sql, :plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b).should == (@d.server_version >= 90000 ? 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b' : 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b')
|
939
949
|
@d.send(:drop_language_sql, :plpgsql, :if_exists=>true, :cascade=>true).should == 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
|
940
950
|
# Make sure if exists works
|
941
951
|
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -973,7 +973,7 @@ context "Dataset#literal" do
|
|
973
973
|
specify "should literalize DateTime properly" do
|
974
974
|
t = DateTime.now
|
975
975
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
976
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* 86400000000)}'"
|
976
|
+
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction * (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}'"
|
977
977
|
end
|
978
978
|
|
979
979
|
specify "should literalize Date properly" do
|
@@ -998,7 +998,7 @@ context "Dataset#literal" do
|
|
998
998
|
|
999
999
|
t = DateTime.now
|
1000
1000
|
s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S")
|
1001
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* 86400000000)}'"
|
1001
|
+
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}'"
|
1002
1002
|
|
1003
1003
|
d = Date.today
|
1004
1004
|
s = d.strftime("DATE '%Y-%m-%d'")
|
@@ -1014,7 +1014,7 @@ context "Dataset#literal" do
|
|
1014
1014
|
|
1015
1015
|
t = DateTime.now.new_offset(0)
|
1016
1016
|
s = t.strftime("'%Y-%m-%d %H:%M:%S")
|
1017
|
-
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* 86400000000)}+0000'"
|
1017
|
+
@dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.sec_fraction* (RUBY_VERSION < '1.9.0' ? 86400000000 : 1000000))}+0000'"
|
1018
1018
|
end
|
1019
1019
|
|
1020
1020
|
specify "should literalize Time and DateTime properly if the database doesn't support usecs in timestamps" do
|
@@ -5,7 +5,6 @@ describe "Sequel::Plugins::IdentityMap" do
|
|
5
5
|
class ::IdentityMapModel < Sequel::Model
|
6
6
|
plugin :identity_map
|
7
7
|
attr_accessor :foo
|
8
|
-
columns :id
|
9
8
|
ds = dataset
|
10
9
|
def ds.fetch_rows(sql)
|
11
10
|
c = @opts[:where].args.first
|
@@ -16,7 +15,7 @@ describe "Sequel::Plugins::IdentityMap" do
|
|
16
15
|
end
|
17
16
|
end
|
18
17
|
class ::IdentityMapAlbum < ::IdentityMapModel
|
19
|
-
columns :
|
18
|
+
columns :artist_id
|
20
19
|
end
|
21
20
|
class ::IdentityMapArtist < ::IdentityMapModel
|
22
21
|
end
|
@@ -73,7 +72,7 @@ describe "Sequel::Plugins::IdentityMap" do
|
|
73
72
|
end
|
74
73
|
end
|
75
74
|
|
76
|
-
it "#load should
|
75
|
+
it "#load should store the object in the current identity map if it isn't already there" do
|
77
76
|
@c.with_identity_map do
|
78
77
|
@c.identity_map[@c.identity_map_key(1)].should == nil
|
79
78
|
o = @c.load(:id=>1)
|
@@ -138,6 +137,24 @@ describe "Sequel::Plugins::IdentityMap" do
|
|
138
137
|
end
|
139
138
|
end
|
140
139
|
|
140
|
+
it "should use the identity map as a lookup cache when retrieving many_to_one associated records via a composite key" do
|
141
|
+
@c1.columns :another_id
|
142
|
+
@c1.many_to_one :artist, :class=>@c2, :key=>[:id, :another_id]
|
143
|
+
@c.with_identity_map do
|
144
|
+
MODEL_DB.sqls.length.should == 0
|
145
|
+
o = @c1.load(:id=>1, :another_id=>1, :artist_id=>2)
|
146
|
+
a = o.artist
|
147
|
+
a.should be_a_kind_of(@c2)
|
148
|
+
MODEL_DB.sqls.length.should == 1
|
149
|
+
o = @c1.load(:id=>1, :another_id=>2, :artist_id=>2)
|
150
|
+
o.artist.should == a
|
151
|
+
MODEL_DB.sqls.length.should == 1
|
152
|
+
o = @c1.load(:id=>3, :another_id=>3, :artist_id=>3)
|
153
|
+
o.artist.should_not == a
|
154
|
+
MODEL_DB.sqls.length.should == 2
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
141
158
|
it "should use the identity map as a lookup cache when retrieving many_to_one associated records" do
|
142
159
|
@c1.many_to_one :artist, :class=>@c2
|
143
160
|
@c.with_identity_map do
|
@@ -170,7 +187,7 @@ describe "Sequel::Plugins::IdentityMap" do
|
|
170
187
|
MODEL_DB.sqls.length.should == 3
|
171
188
|
end
|
172
189
|
end
|
173
|
-
|
190
|
+
|
174
191
|
it "should not use the identity map as a lookup cache if the assocation has a nil :key option" do
|
175
192
|
c = @c2
|
176
193
|
@c1.many_to_one :artist, :class=>@c2, :key=>nil, :dataset=>proc{c.filter(:artist_id=>artist_id)}
|
@@ -472,7 +472,9 @@ describe Sequel::SQL::Constants do
|
|
472
472
|
cspecify "should have working CURRENT_DATE", [:odbc, :mssql], [:jdbc, :sqlite] do
|
473
473
|
@db.create_table!(:constants){Date :d}
|
474
474
|
@ds.insert(:d=>Sequel::CURRENT_DATE)
|
475
|
-
|
475
|
+
d = @c2[@ds.get(:d)]
|
476
|
+
d.should be_a_kind_of(Date)
|
477
|
+
d.to_s.should == Date.today.to_s
|
476
478
|
end
|
477
479
|
|
478
480
|
cspecify "should have working CURRENT_TIME", [:do, :mysql], [:jdbc, :sqlite], [:mysql2] do
|
@@ -925,7 +927,7 @@ describe "Dataset identifier methods" do
|
|
925
927
|
@db.drop_table(:a)
|
926
928
|
end
|
927
929
|
|
928
|
-
cspecify "#identifier_output_method should change how identifiers are output", [:mysql2] do
|
930
|
+
cspecify "#identifier_output_method should change how identifiers are output", [:mysql2], [:swift] do
|
929
931
|
@ds.identifier_output_method = :upcase
|
930
932
|
@ds.first.should == {:AB=>1}
|
931
933
|
@ds.identifier_output_method = :uprev
|
@@ -413,13 +413,22 @@ describe "Touch plugin" do
|
|
413
413
|
@album.updated_at.to_i.should be_close(Time.now.to_i, 2)
|
414
414
|
end
|
415
415
|
|
416
|
-
cspecify "should update the timestamp column for associated records when the record is updated or destroyed", [:do], [:jdbc, :sqlite] do
|
416
|
+
cspecify "should update the timestamp column for associated records when the record is updated or destroyed", [:do, :sqlite], [:jdbc, :sqlite] do
|
417
417
|
@artist.updated_at.should == nil
|
418
418
|
@album.update(:name=>'B')
|
419
|
-
@artist.reload.updated_at
|
419
|
+
ua = @artist.reload.updated_at
|
420
|
+
if ua.is_a?(Time)
|
421
|
+
ua.to_i.should be_close(Time.now.to_i, 2)
|
422
|
+
else
|
423
|
+
(DateTime.now - ua).should be_close(0, 2.0/86400)
|
424
|
+
end
|
420
425
|
@artist.update(:updated_at=>nil)
|
421
426
|
@album.destroy
|
422
|
-
|
427
|
+
if ua.is_a?(Time)
|
428
|
+
ua.to_i.should be_close(Time.now.to_i, 2)
|
429
|
+
else
|
430
|
+
(DateTime.now - ua).should be_close(0, 2.0/86400)
|
431
|
+
end
|
423
432
|
end
|
424
433
|
end
|
425
434
|
|
@@ -644,6 +653,16 @@ if INTEGRATION_DB.dataset.supports_cte?
|
|
644
653
|
nodes[1].ancestors.should == [@a, @aa]
|
645
654
|
nodes[2].ancestors.should == []
|
646
655
|
end
|
656
|
+
|
657
|
+
specify "should work correctly if not all columns are selected" do
|
658
|
+
Node.plugin :lazy_attributes, :name
|
659
|
+
@aaaa.descendants.should == [Node.load(:parent_id=>11, :id=>13)]
|
660
|
+
@aa.ancestors.should == [Node.load(:parent_id=>nil, :id=>1)]
|
661
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:ancestors, :descendants).all
|
662
|
+
nodes.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>3, :id=>7}, {:parent_id=>nil, :id=>2}].map{|x| Node.load(x)}
|
663
|
+
nodes[2].descendants.should == [{:parent_id=>2, :id=>5}, {:parent_id=>2, :id=>6}].map{|x| Node.load(x)}
|
664
|
+
nodes[1].ancestors.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>1, :id=>3}].map{|x| Node.load(x)}
|
665
|
+
end
|
647
666
|
|
648
667
|
specify "should eagerly load descendants to a given level" do
|
649
668
|
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>1).all
|
@@ -37,19 +37,19 @@ describe "Sequel timezone support" do
|
|
37
37
|
Sequel.datetime_class = Time
|
38
38
|
end
|
39
39
|
|
40
|
-
cspecify "should support using UTC for database storage and local time for the application", [:do, proc{|db| db.database_type != :sqlite}] do
|
40
|
+
cspecify "should support using UTC for database storage and local time for the application", [:swift], [:do, proc{|db| db.database_type != :sqlite}] do
|
41
41
|
Sequel.database_timezone = :utc
|
42
42
|
Sequel.application_timezone = :local
|
43
43
|
test_timezone
|
44
44
|
end
|
45
45
|
|
46
|
-
cspecify "should support using local time for database storage and UTC for the application", [:do, proc{|db| db.database_type != :sqlite}] do
|
46
|
+
cspecify "should support using local time for database storage and UTC for the application", [:swift], [:do, proc{|db| db.database_type != :sqlite}] do
|
47
47
|
Sequel.database_timezone = :local
|
48
48
|
Sequel.application_timezone = :utc
|
49
49
|
test_timezone
|
50
50
|
end
|
51
51
|
|
52
|
-
cspecify "should support using UTC for both database storage and for application", [:do, proc{|db| db.database_type != :sqlite}] do
|
52
|
+
cspecify "should support using UTC for both database storage and for application", [:swift], [:do, proc{|db| db.database_type != :sqlite}] do
|
53
53
|
Sequel.default_timezone = :utc
|
54
54
|
test_timezone
|
55
55
|
end
|
@@ -64,7 +64,8 @@ describe "Supported types" do
|
|
64
64
|
ds = create_items_table_with_column(:dat, Date)
|
65
65
|
d = Date.today
|
66
66
|
ds.insert(:dat => d)
|
67
|
-
ds.first[:dat].should
|
67
|
+
ds.first[:dat].should be_a_kind_of(Date)
|
68
|
+
ds.first[:dat].to_s.should == d.to_s
|
68
69
|
end
|
69
70
|
|
70
71
|
cspecify "should support generic datetime type", [:do, :sqlite], [:jdbc, :sqlite] do
|
@@ -78,7 +79,7 @@ describe "Supported types" do
|
|
78
79
|
ds.first[:tim].strftime('%Y%m%d%H%M%S').should == t.strftime('%Y%m%d%H%M%S')
|
79
80
|
end
|
80
81
|
|
81
|
-
cspecify "should support generic file type", [:do], [:odbc, :mssql], [:mysql2] do
|
82
|
+
cspecify "should support generic file type", [:do], [:odbc, :mssql], [:mysql2], [:swift] do
|
82
83
|
ds = create_items_table_with_column(:name, File)
|
83
84
|
ds.insert(:name => ("a\0"*300).to_sequel_blob)
|
84
85
|
ds.all.should == [{:name=>("a\0"*300).to_sequel_blob}]
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 71
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
-
|
8
|
+
- 16
|
9
9
|
- 0
|
10
|
-
version: 3.
|
10
|
+
version: 3.16.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Evans
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-10-01 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -79,6 +79,7 @@ extra_rdoc_files:
|
|
79
79
|
- doc/release_notes/3.13.0.txt
|
80
80
|
- doc/release_notes/3.14.0.txt
|
81
81
|
- doc/release_notes/3.15.0.txt
|
82
|
+
- doc/release_notes/3.16.0.txt
|
82
83
|
files:
|
83
84
|
- COPYING
|
84
85
|
- CHANGELOG
|
@@ -125,6 +126,7 @@ files:
|
|
125
126
|
- doc/release_notes/3.13.0.txt
|
126
127
|
- doc/release_notes/3.14.0.txt
|
127
128
|
- doc/release_notes/3.15.0.txt
|
129
|
+
- doc/release_notes/3.16.0.txt
|
128
130
|
- doc/sharding.rdoc
|
129
131
|
- doc/sql.rdoc
|
130
132
|
- doc/virtual_rows.rdoc
|
@@ -300,6 +302,9 @@ files:
|
|
300
302
|
- lib/sequel/adapters/sqlite.rb
|
301
303
|
- lib/sequel/adapters/utils/stored_procedures.rb
|
302
304
|
- lib/sequel/adapters/mysql2.rb
|
305
|
+
- lib/sequel/adapters/swift.rb
|
306
|
+
- lib/sequel/adapters/swift/mysql.rb
|
307
|
+
- lib/sequel/adapters/swift/postgres.rb
|
303
308
|
- lib/sequel/connection_pool.rb
|
304
309
|
- lib/sequel/connection_pool/sharded_single.rb
|
305
310
|
- lib/sequel/connection_pool/sharded_threaded.rb
|