sequel 5.12.0 → 5.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 +10 -0
- data/README.rdoc +1 -1
- data/doc/mass_assignment.rdoc +3 -3
- data/doc/opening_databases.rdoc +1 -1
- data/doc/release_notes/5.13.0.txt +27 -0
- data/doc/sharding.rdoc +28 -28
- data/lib/sequel/adapters/ado.rb +2 -2
- data/lib/sequel/connection_pool/sharded_single.rb +3 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/misc.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +4 -3
- data/lib/sequel/dataset/prepared_statements.rb +6 -4
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/constant_sql_override.rb +65 -0
- data/lib/sequel/model/associations.rb +191 -4
- data/lib/sequel/model/base.rb +7 -7
- data/lib/sequel/plugins/static_cache.rb +6 -8
- data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/core/dataset_spec.rb +14 -2
- data/spec/extensions/constant_sql_override_spec.rb +18 -0
- data/spec/extensions/static_cache_spec.rb +12 -0
- data/spec/integration/prepared_statement_test.rb +7 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc4068290931cd51d12ec42e40a1e383ffa9cc8448501970230ec0863fc1cc21
|
4
|
+
data.tar.gz: 16c549d0adcd8700bfb498bc2a459437a37c57a85d9048099336105efff376ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f6acb6af9670774bf87b19349e778cb7b967ac529bbd192ea3ce2b6790080c21afa4445faabe18de148cfcef894e5457924782fb5462289dc38ff9af4ab5fcb
|
7
|
+
data.tar.gz: e03cf594244024d41de805ef57bcdc545bd9bb247e34fcd48e1d6fba8c558f8dacc49828b5f529e465b9a5a6089e66adc5e1a8d43cf7029225556429aa97441b
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
=== 5.13.0 (2018-10-01)
|
2
|
+
|
3
|
+
* Support :single_value type in prepared statements (rintaun) (#1547)
|
4
|
+
|
5
|
+
* Make Model.all in static_cache plugin accept a block (AlexWayfer, jeremyevans) (#1543)
|
6
|
+
|
7
|
+
* Add constant_sql_override extension for overriding SQL used for constants such as CURRENT_TIMESTAMP (celsworth) (#1538)
|
8
|
+
|
9
|
+
* Do not cache from_self datasets if options are given (jeremyevans)
|
10
|
+
|
1
11
|
=== 5.12.0 (2018-08-31)
|
2
12
|
|
3
13
|
* Make constraint_validations extension respect Database#constraint_validations_table setting (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -9,7 +9,7 @@ toolkit for Ruby.
|
|
9
9
|
records to Ruby objects and handling associated records.
|
10
10
|
* Sequel supports advanced database features such as prepared
|
11
11
|
statements, bound variables, savepoints, two-phase commit,
|
12
|
-
transaction isolation,
|
12
|
+
transaction isolation, primary/replica configurations, and
|
13
13
|
database sharding.
|
14
14
|
* Sequel currently has adapters for ADO, Amalgalite,
|
15
15
|
IBM_DB, JDBC, MySQL, Mysql2, ODBC, Oracle,
|
data/doc/mass_assignment.rdoc
CHANGED
@@ -15,13 +15,13 @@ and you call a mass assignment method with a hash:
|
|
15
15
|
|
16
16
|
the mass assignment method will go through each key in the hash, append <tt>=</tt> to it to determine the
|
17
17
|
setter method, and if the setter method is defined and access to it is not restricted, Sequel will call the
|
18
|
-
setter method with the hash value. So if we assume that the posts table has
|
18
|
+
setter method with the hash value. So if we assume that the posts table has title and body columns, what
|
19
19
|
the above mass assignment call actually does is:
|
20
|
-
|
20
|
+
|
21
21
|
post.title=('T')
|
22
22
|
post.body=('B')
|
23
23
|
|
24
|
-
By default, there are two types of setter methods that are restricted.
|
24
|
+
By default, there are two types of setter methods that are restricted.
|
25
25
|
The first is methods like <tt>typecast_on_assignment=</tt> and <tt>==</tt>, which don't affect columns.
|
26
26
|
These methods cannot be enabled for mass assignment.
|
27
27
|
The second is primary key setters.
|
data/doc/opening_databases.rdoc
CHANGED
@@ -83,7 +83,7 @@ These options are shared by all adapters unless otherwise noted.
|
|
83
83
|
:log_connection_info :: Whether to include connection information in log messages (false by default)
|
84
84
|
:log_warn_duration :: The amount of seconds after which the queries are logged at :warn level
|
85
85
|
:password :: The password for the user account
|
86
|
-
:servers :: A hash with symbol keys and hash or proc values, used with
|
86
|
+
:servers :: A hash with symbol keys and hash or proc values, used with primary/replica and sharded database configurations
|
87
87
|
:sql_log_level :: The level at which to issue queries to the loggers (:info by default)
|
88
88
|
:test :: Whether to test that a valid database connection can be made (true by default)
|
89
89
|
:user :: The user account name to use logging in
|
@@ -0,0 +1,27 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A constant_sql_override Database extension has been added, allowing
|
4
|
+
for overriding the SQL used by constants such as
|
5
|
+
Sequel::CURRENT_TIMESTAMP. This can be used to force
|
6
|
+
CURRENT_TIMESTAMP to be literalized at a particular time zone:
|
7
|
+
|
8
|
+
DB.extension :constant_sql_override
|
9
|
+
DB.set_constant_sql(Sequel::CURRENT_TIMESTAMP,
|
10
|
+
"CURRENT_TIMESTAMP AT TIME ZONE 'UTC'")
|
11
|
+
|
12
|
+
* Prepared statements now support the :single_value type, which
|
13
|
+
returns the first column value in the dataset.
|
14
|
+
|
15
|
+
prep_stmt = DB[:table].select(:column).prepare(:single_value, :ps)
|
16
|
+
prep_stmt.call
|
17
|
+
# PREPARE ps AS SELECT column FROM table LIMIT 1;
|
18
|
+
# EXECUTE ps;
|
19
|
+
# => 42
|
20
|
+
|
21
|
+
= Other Improvements
|
22
|
+
|
23
|
+
* Dataset#from_self will no longer use a cached dataset if any options
|
24
|
+
are given, as that can result in incorrect behavior.
|
25
|
+
|
26
|
+
* Model.all in the static_cache plugin now accepts a block, mirroring
|
27
|
+
the API when the static_cache plugin is not used.
|
data/doc/sharding.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
=
|
1
|
+
= Primary/Replica Configurations and Database Sharding
|
2
2
|
|
3
|
-
Sequel has support for
|
4
|
-
with
|
3
|
+
Sequel has support for primary/replica configurations (writable primary
|
4
|
+
database with read only replicas databases), as well as database sharding (where you can
|
5
5
|
pick a server to use for a given dataset). Support for both
|
6
6
|
features is database independent, and should work for all database adapters
|
7
7
|
that ship with Sequel.
|
@@ -13,7 +13,7 @@ option. Using the :servers database option makes Sequel use a connection pool
|
|
13
13
|
class that supports sharding, and the minimum required to enable sharding
|
14
14
|
support is to use the empty hash:
|
15
15
|
|
16
|
-
DB=Sequel.connect('postgres://
|
16
|
+
DB=Sequel.connect('postgres://primary_server/database', servers: {})
|
17
17
|
|
18
18
|
In most cases, you are probably not going to want to use an empty hash. Keys in the server hash are
|
19
19
|
not restricted to type, but the general recommendation is to use a symbol
|
@@ -28,69 +28,69 @@ a :host entry in each shard's hash.
|
|
28
28
|
Note that all servers should have the same schema for all
|
29
29
|
tables you are accessing, unless you really know what you are doing.
|
30
30
|
|
31
|
-
==
|
31
|
+
== Primary and Replica Database Configurations
|
32
32
|
|
33
|
-
=== Single
|
33
|
+
=== Single Primary, Single Replica
|
34
34
|
|
35
|
-
To use a single, read-only
|
35
|
+
To use a single, read-only replica that handles SELECT queries, the following
|
36
36
|
is the simplest configuration:
|
37
37
|
|
38
|
-
DB=Sequel.connect('postgres://
|
39
|
-
servers: {read_only: {host: '
|
38
|
+
DB=Sequel.connect('postgres://primary_server/database',
|
39
|
+
servers: {read_only: {host: 'replica_server'}})
|
40
40
|
|
41
|
-
This will use the
|
41
|
+
This will use the replica_server for SELECT queries and primary_server for
|
42
42
|
other queries.
|
43
43
|
|
44
44
|
If you want to ensure your queries are going to a specific database, you
|
45
45
|
can force this for a given query by using the .server method and passing
|
46
46
|
the symbol name defined in the connect options. For example:
|
47
47
|
|
48
|
-
# Force the SELECT to run on the
|
48
|
+
# Force the SELECT to run on the primary server
|
49
49
|
DB[:users].server(:default).all
|
50
50
|
|
51
|
-
# Force the DELETE to run on the read-only
|
51
|
+
# Force the DELETE to run on the read-only replica
|
52
52
|
DB[:users].server(:read_only).delete
|
53
53
|
|
54
|
-
===
|
54
|
+
=== Single Primary, Multiple Replicas
|
55
55
|
|
56
|
-
Let's say you have 4
|
57
|
-
|
56
|
+
Let's say you have 4 replica servers with names replica_server0,
|
57
|
+
replica_server1, replica_server2, and replica_server3.
|
58
58
|
|
59
59
|
num_read_only = 4
|
60
60
|
read_only_host = rand(num_read_only)
|
61
61
|
read_only_proc = proc do |db|
|
62
|
-
{host: "
|
62
|
+
{host: "replica_server#{(read_only_host+=1) % num_read_only}"}
|
63
63
|
end
|
64
|
-
DB=Sequel.connect('postgres://
|
64
|
+
DB=Sequel.connect('postgres://primary_server/database',
|
65
65
|
servers: {read_only: read_only_proc})
|
66
66
|
|
67
|
-
This will use one of the
|
68
|
-
|
67
|
+
This will use one of the replica servers for SELECT queries and use the
|
68
|
+
primary server for other queries. It's also possible to pick a random host
|
69
69
|
instead of using the round robin approach presented above, but that can result
|
70
70
|
in less optimal resource usage.
|
71
71
|
|
72
|
-
=== Multiple
|
72
|
+
=== Multiple Primary, Multiple Replicas
|
73
73
|
|
74
|
-
This involves the same basic idea as the multiple
|
75
|
-
it shows that the
|
76
|
-
4
|
74
|
+
This involves the same basic idea as the multiple replicas, single primary, but
|
75
|
+
it shows that the primary database is named :default. So for 4 primary servers and
|
76
|
+
4 replica servers:
|
77
77
|
|
78
78
|
num_read_only = 4
|
79
79
|
read_only_host = rand(num_read_only)
|
80
80
|
read_only_proc = proc do |db|
|
81
|
-
{host: "
|
81
|
+
{host: "replica_server#{(read_only_host+=1) % num_read_only}"}
|
82
82
|
end
|
83
83
|
num_default = 4
|
84
84
|
default_host = rand(num_default)
|
85
85
|
default_proc = proc do |db|
|
86
|
-
{host: "
|
86
|
+
{host: "primary_server#{(default_host+=1) % num_default}"}
|
87
87
|
end
|
88
|
-
DB=Sequel.connect('postgres://
|
88
|
+
DB=Sequel.connect('postgres://primary_server/database',
|
89
89
|
servers: {default: default_proc, read_only: read_only_proc})
|
90
90
|
|
91
91
|
== Sharding
|
92
92
|
|
93
|
-
There is specific support in Sequel for handling
|
93
|
+
There is specific support in Sequel for handling primary/replica database
|
94
94
|
combinations, with the only necessary setup being the database configuration.
|
95
95
|
However, since sharding is always going to be implementation dependent, Sequel
|
96
96
|
supplies the basic infrastructure, but you have to tell it which server to use
|
@@ -210,7 +210,7 @@ need to do that, call the server method explicitly on the dataset used to retrie
|
|
210
210
|
model objects.
|
211
211
|
|
212
212
|
The with_server method also supports a second argument for the default read_only server
|
213
|
-
to use, which can be useful if you are mixing sharding and
|
213
|
+
to use, which can be useful if you are mixing sharding and primary/replica servers:
|
214
214
|
|
215
215
|
DB.extension :server_block
|
216
216
|
DB.with_server(:a, :a_read_only) do
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -253,8 +253,8 @@ module Sequel
|
|
253
253
|
recordset.GetRows.transpose.each do |field_values|
|
254
254
|
h = {}
|
255
255
|
|
256
|
-
cols.each do |name, cp,
|
257
|
-
h[name] = if (v = field_values[
|
256
|
+
cols.each do |name, cp, index|
|
257
|
+
h[name] = if (v = field_values[index]) && cp
|
258
258
|
cp[v]
|
259
259
|
else
|
260
260
|
v
|
@@ -19,9 +19,9 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
|
|
19
19
|
add_servers(opts[:servers].keys) if opts[:servers]
|
20
20
|
end
|
21
21
|
|
22
|
-
# Adds new servers to the connection pool. Primarily used in conjunction with
|
23
|
-
# or
|
24
|
-
# at runtime. servers argument should be an array of symbols.
|
22
|
+
# Adds new servers to the connection pool. Primarily used in conjunction with primary/replica
|
23
|
+
# or sharded configurations. Allows for dynamic expansion of the potential replicas/shards
|
24
|
+
# at runtime. +servers+ argument should be an array of symbols.
|
25
25
|
def add_servers(servers)
|
26
26
|
servers.each{|s| @servers[s] = s}
|
27
27
|
end
|
@@ -28,8 +28,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
28
28
|
add_servers(opts[:servers].keys) if opts[:servers]
|
29
29
|
end
|
30
30
|
|
31
|
-
# Adds new servers to the connection pool. Allows for dynamic expansion of the potential
|
32
|
-
# at runtime. servers argument should be an array of symbols.
|
31
|
+
# Adds new servers to the connection pool. Allows for dynamic expansion of the potential replicas/shards
|
32
|
+
# at runtime. +servers+ argument should be an array of symbols.
|
33
33
|
def add_servers(servers)
|
34
34
|
sync do
|
35
35
|
servers.each do |server|
|
data/lib/sequel/core.rb
CHANGED
@@ -116,7 +116,7 @@ module Sequel
|
|
116
116
|
# terminates, or use the <tt>keep_reference: false</tt> Database option.
|
117
117
|
#
|
118
118
|
# For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
|
119
|
-
# To set up a
|
119
|
+
# To set up a primary/replica or sharded database connection, see the {"Primary/Replica Database Configurations and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
|
120
120
|
def self.connect(*args, &block)
|
121
121
|
Database.connect(*args, &block)
|
122
122
|
end
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -81,7 +81,8 @@ module Sequel
|
|
81
81
|
constraint(nil, *args, &block)
|
82
82
|
end
|
83
83
|
|
84
|
-
# Add a column with the given name, type, and opts
|
84
|
+
# Add a column with the given name, type, and opts:
|
85
|
+
#
|
85
86
|
# column :num, :integer
|
86
87
|
# # num INTEGER
|
87
88
|
#
|
@@ -158,7 +159,7 @@ module Sequel
|
|
158
159
|
nil
|
159
160
|
end
|
160
161
|
|
161
|
-
# Add a foreign key in the table that references another table. See column
|
162
|
+
# Add a foreign key in the table that references another table. See #column
|
162
163
|
# for available options.
|
163
164
|
#
|
164
165
|
# foreign_key(:artist_id) # artist_id INTEGER
|
@@ -241,7 +242,7 @@ module Sequel
|
|
241
242
|
nil
|
242
243
|
end
|
243
244
|
|
244
|
-
# Add a column with the given type, name, and opts. See
|
245
|
+
# Add a column with the given type, name, and opts. See #column for available
|
245
246
|
# options.
|
246
247
|
def method_missing(type, name = nil, opts = OPTS)
|
247
248
|
name ? column(name, type, opts) : super
|
@@ -91,7 +91,7 @@ module Sequel
|
|
91
91
|
end
|
92
92
|
|
93
93
|
# The type of prepared statement, should be one of :select, :first,
|
94
|
-
# :insert, :update, or :
|
94
|
+
# :insert, :update, :delete, or :single_value
|
95
95
|
def prepared_type
|
96
96
|
@opts[:prepared_type]
|
97
97
|
end
|
@@ -144,7 +144,7 @@ module Sequel
|
|
144
144
|
when :select, :all, :each
|
145
145
|
# Most common scenario, so listed first.
|
146
146
|
select_sql
|
147
|
-
when :first
|
147
|
+
when :first, :single_value
|
148
148
|
clone(:limit=>1).select_sql
|
149
149
|
when :insert_select
|
150
150
|
insert_select_sql(*prepared_modify_values)
|
@@ -205,6 +205,8 @@ module Sequel
|
|
205
205
|
when :map, :as_hash, :to_hash, :to_hash_groups
|
206
206
|
public_send(*prepared_type, &block)
|
207
207
|
end
|
208
|
+
when :single_value
|
209
|
+
single_value
|
208
210
|
else
|
209
211
|
raise Error, "unsupported prepared statement type used: #{prepared_type.inspect}"
|
210
212
|
end
|
@@ -290,7 +292,7 @@ module Sequel
|
|
290
292
|
ds = ds.clone(:recorder=>pl)
|
291
293
|
|
292
294
|
case type
|
293
|
-
when :first
|
295
|
+
when :first, :single_value
|
294
296
|
ds.limit(1)
|
295
297
|
when :update, :insert, :insert_select, :delete
|
296
298
|
ds.with_sql(:"#{type}_sql", *values)
|
@@ -339,7 +341,7 @@ module Sequel
|
|
339
341
|
clone(:bind_vars=>bind_vars)
|
340
342
|
end
|
341
343
|
|
342
|
-
# For the given type (:select, :first, :insert, :insert_select, :update, or :
|
344
|
+
# For the given type (:select, :first, :insert, :insert_select, :update, :delete, or :single_value),
|
343
345
|
# run the sql with the bind variables specified in the hash. +values+ is a hash passed to
|
344
346
|
# insert or update (if one of those types is used), which may contain placeholders.
|
345
347
|
#
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The constant_sql_override extension allows you to change the SQL
|
4
|
+
# generated for Sequel constants.
|
5
|
+
#
|
6
|
+
# One possible use-case for this is to have Sequel::CURRENT_TIMESTAMP use UTC time when
|
7
|
+
# you have Sequel.database_timezone = :utc, but the database uses localtime when
|
8
|
+
# generating CURRENT_TIMESTAMP.
|
9
|
+
#
|
10
|
+
# You can set SQL overrides with Database#set_constant_sql:
|
11
|
+
#
|
12
|
+
# DB.set_constant_sql(Sequel::CURRENT_TIMESTAMP, "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'")
|
13
|
+
#
|
14
|
+
# Now, using Sequel::CURRENT_TIMESTAMP will use your override instead:
|
15
|
+
#
|
16
|
+
# Album.where(released_at: Sequel::CURRENT_TIMESTAMP).sql
|
17
|
+
# # => SELECT "albums.*" FROM "albums" WHERE ("released_at" = CURRENT_TIMESTAMP AT TIME ZONE 'UTC')
|
18
|
+
#
|
19
|
+
# To use this extension, first load it into your Sequel::Database instance:
|
20
|
+
#
|
21
|
+
# DB.extension :constant_sql_override
|
22
|
+
#
|
23
|
+
# Related module: Sequel::ConstantSqlOverride
|
24
|
+
|
25
|
+
#
|
26
|
+
module Sequel
|
27
|
+
module ConstantSqlOverride
|
28
|
+
module DatabaseMethods
|
29
|
+
# Create the initial empty hash of constant sql overrides.
|
30
|
+
def self.extended(db)
|
31
|
+
db.instance_exec do
|
32
|
+
@constant_sqls ||= {}
|
33
|
+
extend_datasets(DatasetMethods)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Hash mapping constant symbols to SQL. For internal use only.
|
38
|
+
attr_reader :constant_sqls # :nodoc:
|
39
|
+
|
40
|
+
# Set the SQL to use for the given Sequel::SQL::Constant
|
41
|
+
def set_constant_sql(constant, override)
|
42
|
+
@constant_sqls[constant.constant] = override
|
43
|
+
end
|
44
|
+
|
45
|
+
# Freeze the constant_sqls hash to prevent adding new overrides.
|
46
|
+
def freeze
|
47
|
+
@constant_sqls.freeze
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module DatasetMethods
|
53
|
+
# Use overridden constant SQL
|
54
|
+
def constant_sql_append(sql, constant)
|
55
|
+
if constant_sql = db.constant_sqls[constant]
|
56
|
+
sql << constant_sql
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Database.register_extension(:constant_sql_override, ConstantSqlOverride::DatabaseMethods)
|
65
|
+
end
|
@@ -2641,7 +2641,7 @@ module Sequel
|
|
2641
2641
|
# Album.eager(:artist, :genre).all
|
2642
2642
|
# Album.eager_graph(:artist, :genre).all
|
2643
2643
|
# Album.eager(:artist).eager(:genre).all
|
2644
|
-
# Album.eager_graph(:artist).
|
2644
|
+
# Album.eager_graph(:artist).eager_graph(:genre).all
|
2645
2645
|
# Artist.eager(albums: :tracks).all
|
2646
2646
|
# Artist.eager_graph(albums: :tracks).all
|
2647
2647
|
# Artist.eager(albums: {tracks: :genre}).all
|
@@ -2674,13 +2674,79 @@ module Sequel
|
|
2674
2674
|
end
|
2675
2675
|
|
2676
2676
|
# Adds one or more INNER JOINs to the existing dataset using the keys and conditions
|
2677
|
-
# specified by the given association.
|
2678
|
-
#
|
2677
|
+
# specified by the given association(s). Take the same arguments as eager_graph, and
|
2678
|
+
# operates similarly, but only adds the joins as opposed to making the other changes
|
2679
|
+
# (such as adding selected columns and setting up eager loading).
|
2680
|
+
#
|
2681
|
+
# The following methods also exist for specifying a different type of JOIN:
|
2679
2682
|
#
|
2680
2683
|
# association_full_join :: FULL JOIN
|
2681
2684
|
# association_inner_join :: INNER JOIN
|
2682
2685
|
# association_left_join :: LEFT JOIN
|
2683
2686
|
# association_right_join :: RIGHT JOIN
|
2687
|
+
#
|
2688
|
+
# Examples:
|
2689
|
+
#
|
2690
|
+
# # For each album, association_join load the artist
|
2691
|
+
# Album.association_join(:artist).all
|
2692
|
+
# # SELECT *
|
2693
|
+
# # FROM albums
|
2694
|
+
# # INNER JOIN artists AS artist ON (artists.id = albums.artist_id)
|
2695
|
+
#
|
2696
|
+
# # For each album, association_join load the artist, using a specified alias
|
2697
|
+
# Album.association_join(Sequel[:artist].as(:a)).all
|
2698
|
+
# # SELECT *
|
2699
|
+
# # FROM albums
|
2700
|
+
# # INNER JOIN artists AS a ON (a.id = albums.artist_id)
|
2701
|
+
#
|
2702
|
+
# # For each album, association_join load the artist and genre
|
2703
|
+
# Album.association_join(:artist, :genre).all
|
2704
|
+
# Album.association_join(:artist).association_join(:genre).all
|
2705
|
+
# # SELECT *
|
2706
|
+
# # FROM albums
|
2707
|
+
# # INNER JOIN artists AS artist ON (artist.id = albums.artist_id)
|
2708
|
+
# # INNER JOIN genres AS genre ON (genre.id = albums.genre_id)
|
2709
|
+
#
|
2710
|
+
# # For each artist, association_join load albums and tracks for each album
|
2711
|
+
# Artist.association_join(albums: :tracks).all
|
2712
|
+
# # SELECT *
|
2713
|
+
# # FROM artists
|
2714
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2715
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2716
|
+
#
|
2717
|
+
# # For each artist, association_join load albums, tracks for each album, and genre for each track
|
2718
|
+
# Artist.association_join(albums: {tracks: :genre}).all
|
2719
|
+
# # SELECT *
|
2720
|
+
# # FROM artists
|
2721
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2722
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2723
|
+
# # INNER JOIN genres AS genre ON (genre.id = tracks.genre_id)
|
2724
|
+
#
|
2725
|
+
# # For each artist, association_join load albums with year > 1990
|
2726
|
+
# Artist.association_join(albums: proc{|ds| ds.where{year > 1990}}).all
|
2727
|
+
# # SELECT *
|
2728
|
+
# # FROM artists
|
2729
|
+
# # INNER JOIN (
|
2730
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
2731
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
2732
|
+
#
|
2733
|
+
# # For each artist, association_join load albums and tracks 1-10 for each album
|
2734
|
+
# Artist.association_join(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
2735
|
+
# # SELECT *
|
2736
|
+
# # FROM artists
|
2737
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2738
|
+
# # INNER JOIN (
|
2739
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
|
2740
|
+
# # ) AS tracks ON (tracks.albums_id = albums.id)
|
2741
|
+
#
|
2742
|
+
# # For each artist, association_join load albums with year > 1990, and tracks for those albums
|
2743
|
+
# Artist.association_join(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
2744
|
+
# # SELECT *
|
2745
|
+
# # FROM artists
|
2746
|
+
# # INNER JOIN (
|
2747
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
2748
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
2749
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2684
2750
|
def association_join(*associations)
|
2685
2751
|
association_inner_join(*associations)
|
2686
2752
|
end
|
@@ -2760,6 +2826,56 @@ module Sequel
|
|
2760
2826
|
#
|
2761
2827
|
# Each association's order, if defined, is respected.
|
2762
2828
|
# If the association uses a block or has an :eager_block argument, it is used.
|
2829
|
+
#
|
2830
|
+
# To modify the associated dataset that will be used for the eager load, you should use a
|
2831
|
+
# hash for the association, with the key being the association name symbol, and the value being
|
2832
|
+
# a callable object that is called with the associated dataset and should return a modified
|
2833
|
+
# dataset. If that association also has dependent associations, instead of a callable object,
|
2834
|
+
# use a hash with the callable object being the key, and the dependent association(s) as the value.
|
2835
|
+
#
|
2836
|
+
# Examples:
|
2837
|
+
#
|
2838
|
+
# # For each album, eager load the artist
|
2839
|
+
# Album.eager(:artist).all
|
2840
|
+
# # SELECT * FROM albums
|
2841
|
+
# # SELECT * FROM artists WHERE (id IN (...))
|
2842
|
+
#
|
2843
|
+
# # For each album, eager load the artist and genre
|
2844
|
+
# Album.eager(:artist, :genre).all
|
2845
|
+
# Album.eager(:artist).eager(:genre).all
|
2846
|
+
# # SELECT * FROM albums
|
2847
|
+
# # SELECT * FROM artists WHERE (id IN (...))
|
2848
|
+
# # SELECT * FROM genres WHERE (id IN (...))
|
2849
|
+
#
|
2850
|
+
# # For each artist, eager load albums and tracks for each album
|
2851
|
+
# Artist.eager(albums: :tracks).all
|
2852
|
+
# # SELECT * FROM artists
|
2853
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
2854
|
+
# # SELECT * FROM tracks WHERE (album_id IN (...))
|
2855
|
+
#
|
2856
|
+
# # For each artist, eager load albums, tracks for each album, and genre for each track
|
2857
|
+
# Artist.eager(albums: {tracks: :genre}).all
|
2858
|
+
# # SELECT * FROM artists
|
2859
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
2860
|
+
# # SELECT * FROM tracks WHERE (album_id IN (...))
|
2861
|
+
# # SELECT * FROM genre WHERE (id IN (...))
|
2862
|
+
#
|
2863
|
+
# # For each artist, eager load albums with year > 1990
|
2864
|
+
# Artist.eager(albums: proc{|ds| ds.where{year > 1990}}).all
|
2865
|
+
# # SELECT * FROM artists
|
2866
|
+
# # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
|
2867
|
+
#
|
2868
|
+
# # For each artist, eager load albums and tracks 1-10 for each album
|
2869
|
+
# Artist.eager(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
2870
|
+
# # SELECT * FROM artists
|
2871
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
2872
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10) AND (album_id IN (...)))
|
2873
|
+
#
|
2874
|
+
# # For each artist, eager load albums with year > 1990, and tracks for those albums
|
2875
|
+
# Artist.eager(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
2876
|
+
# # SELECT * FROM artists
|
2877
|
+
# # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
|
2878
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
2763
2879
|
def eager(*associations)
|
2764
2880
|
opts = @opts[:eager]
|
2765
2881
|
association_opts = eager_options_for_associations(associations)
|
@@ -2788,6 +2904,78 @@ module Sequel
|
|
2788
2904
|
#
|
2789
2905
|
# Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
|
2790
2906
|
# call +each+, it will yield plain hashes, each containing all columns from all the tables.
|
2907
|
+
#
|
2908
|
+
# To modify the associated dataset that will be joined to the current dataset, you should use a
|
2909
|
+
# hash for the association, with the key being the association name symbol, and the value being
|
2910
|
+
# a callable object that is called with the associated dataset and should return a modified
|
2911
|
+
# dataset. If that association also has dependent associations, instead of a callable object,
|
2912
|
+
# use a hash with the callable object being the key, and the dependent association(s) as the value.
|
2913
|
+
#
|
2914
|
+
# You can specify an alias by providing a Sequel::SQL::AliasedExpression object instead of
|
2915
|
+
# an a Symbol for the assocation name.
|
2916
|
+
#
|
2917
|
+
# Examples:
|
2918
|
+
#
|
2919
|
+
# # For each album, eager_graph load the artist
|
2920
|
+
# Album.eager_graph(:artist).all
|
2921
|
+
# # SELECT ...
|
2922
|
+
# # FROM albums
|
2923
|
+
# # LEFT OUTER JOIN artists AS artist ON (artists.id = albums.artist_id)
|
2924
|
+
#
|
2925
|
+
# # For each album, eager_graph load the artist, using a specified alias
|
2926
|
+
# Album.eager_graph(Sequel[:artist].as(:a)).all
|
2927
|
+
# # SELECT ...
|
2928
|
+
# # FROM albums
|
2929
|
+
# # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
|
2930
|
+
#
|
2931
|
+
# # For each album, eager_graph load the artist and genre
|
2932
|
+
# Album.eager_graph(:artist, :genre).all
|
2933
|
+
# Album.eager_graph(:artist).eager_graph(:genre).all
|
2934
|
+
# # SELECT ...
|
2935
|
+
# # FROM albums
|
2936
|
+
# # LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id)
|
2937
|
+
# # LEFT OUTER JOIN genres AS genre ON (genre.id = albums.genre_id)
|
2938
|
+
#
|
2939
|
+
# # For each artist, eager_graph load albums and tracks for each album
|
2940
|
+
# Artist.eager_graph(albums: :tracks).all
|
2941
|
+
# # SELECT ...
|
2942
|
+
# # FROM artists
|
2943
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
2944
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
2945
|
+
#
|
2946
|
+
# # For each artist, eager_graph load albums, tracks for each album, and genre for each track
|
2947
|
+
# Artist.eager_graph(albums: {tracks: :genre}).all
|
2948
|
+
# # SELECT ...
|
2949
|
+
# # FROM artists
|
2950
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
2951
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
2952
|
+
# # LEFT OUTER JOIN genres AS genre ON (genre.id = tracks.genre_id)
|
2953
|
+
#
|
2954
|
+
# # For each artist, eager_graph load albums with year > 1990
|
2955
|
+
# Artist.eager_graph(albums: proc{|ds| ds.where{year > 1990}}).all
|
2956
|
+
# # SELECT ...
|
2957
|
+
# # FROM artists
|
2958
|
+
# # LEFT OUTER JOIN (
|
2959
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
2960
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
2961
|
+
#
|
2962
|
+
# # For each artist, eager_graph load albums and tracks 1-10 for each album
|
2963
|
+
# Artist.eager_graph(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
2964
|
+
# # SELECT ...
|
2965
|
+
# # FROM artists
|
2966
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
2967
|
+
# # LEFT OUTER JOIN (
|
2968
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
|
2969
|
+
# # ) AS tracks ON (tracks.albums_id = albums.id)
|
2970
|
+
#
|
2971
|
+
# # For each artist, eager_graph load albums with year > 1990, and tracks for those albums
|
2972
|
+
# Artist.eager_graph(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
2973
|
+
# # SELECT ...
|
2974
|
+
# # FROM artists
|
2975
|
+
# # LEFT OUTER JOIN (
|
2976
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
2977
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
2978
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
2791
2979
|
def eager_graph(*associations)
|
2792
2980
|
eager_graph_with_options(associations)
|
2793
2981
|
end
|
@@ -3207,7 +3395,6 @@ module Sequel
|
|
3207
3395
|
# Hash with table alias symbol keys and [limit, offset] values
|
3208
3396
|
attr_reader :limit_map
|
3209
3397
|
|
3210
|
-
# Hash with table alias symbol keys and callable values used to create model instances
|
3211
3398
|
# The table alias symbol for the primary model
|
3212
3399
|
attr_reader :master
|
3213
3400
|
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1287,11 +1287,11 @@ module Sequel
|
|
1287
1287
|
# a.update(:name=>'A')
|
1288
1288
|
# end
|
1289
1289
|
#
|
1290
|
-
#
|
1291
|
-
#
|
1292
|
-
#
|
1293
|
-
#
|
1294
|
-
#
|
1290
|
+
# a = Artist[2]
|
1291
|
+
# Artist.db.transaction do
|
1292
|
+
# a.lock!('FOR NO KEY UPDATE')
|
1293
|
+
# a.update(:name=>'B')
|
1294
|
+
# end
|
1295
1295
|
def lock!(style=:update)
|
1296
1296
|
_refresh(this.lock_style(style)) unless new?
|
1297
1297
|
self
|
@@ -1551,8 +1551,8 @@ module Sequel
|
|
1551
1551
|
update_restricted(hash, :default)
|
1552
1552
|
end
|
1553
1553
|
|
1554
|
-
# Update the
|
1555
|
-
#
|
1554
|
+
# Update the instance's values by calling set_fields with the arguments, then
|
1555
|
+
# calls save_changes.
|
1556
1556
|
#
|
1557
1557
|
# artist.update_fields({name: 'Jim'}, [:name])
|
1558
1558
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
@@ -72,14 +72,12 @@ module Sequel
|
|
72
72
|
# A frozen ruby hash holding all of the model's frozen instances, keyed by frozen primary key.
|
73
73
|
attr_reader :cache
|
74
74
|
|
75
|
-
# An array of all of the model's
|
76
|
-
# query.
|
77
|
-
def all
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
map{|o| o}
|
82
|
-
end
|
75
|
+
# An array of all of the model's instances, without issuing a database
|
76
|
+
# query. If a block is given, yields each instance to the block.
|
77
|
+
def all(&block)
|
78
|
+
array = @static_cache_frozen ? @all.dup : to_a
|
79
|
+
array.each(&block) if block
|
80
|
+
array
|
83
81
|
end
|
84
82
|
|
85
83
|
# If a block is given, multiple arguments are given, or a single
|
@@ -60,6 +60,9 @@ module Sequel
|
|
60
60
|
# # SELECT * FROM artists WHERE name > 'N' AND id IN (...)
|
61
61
|
# albums.first.artists(eager: lambda{|ds| ds.where(Sequel[:name] > 'N')})
|
62
62
|
#
|
63
|
+
# Note that the :eager option only takes effect if the association
|
64
|
+
# has not already been loaded for the model.
|
65
|
+
#
|
63
66
|
# The tactical_eager_loading plugin also allows transparent eager
|
64
67
|
# loading when calling association methods on associated objects
|
65
68
|
# eagerly loaded via Dataset#eager_graph. This can reduce N queries
|
@@ -100,6 +103,12 @@ module Sequel
|
|
100
103
|
# loaded by eager_graph will only take place if the associated classes
|
101
104
|
# also use the tactical_eager_loading plugin.
|
102
105
|
#
|
106
|
+
# When using this plugin, calling association methods on separate
|
107
|
+
# instances of the same result set is not thread-safe, because this
|
108
|
+
# plugin attempts to modify all instances of the same result set
|
109
|
+
# to eagerly set the associated objects, and having separate threads
|
110
|
+
# modify the same model instance is not thread-safe.
|
111
|
+
#
|
103
112
|
# Usage:
|
104
113
|
#
|
105
114
|
# # Make all model subclass instances use tactical eager loading (called before loading subclasses)
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 13
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -2330,6 +2330,11 @@ describe "Dataset#from_self" do
|
|
2330
2330
|
@ds.from_self(:alias=>:some_name, :column_aliases=>[:c1, :c2]).sql.must_equal 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name(c1, c2)'
|
2331
2331
|
end
|
2332
2332
|
|
2333
|
+
it "should use the user-specified alias" do
|
2334
|
+
@ds.from_self(:alias=>:some_name).sql.must_equal 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name'
|
2335
|
+
@ds.from_self(:alias=>:some_name1).sql.must_equal 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name1'
|
2336
|
+
end
|
2337
|
+
|
2333
2338
|
it "should use the user-specified alias for joins" do
|
2334
2339
|
@ds.from_self(:alias=>:some_name).inner_join(:posts, :alias=>:name).sql.must_equal \
|
2335
2340
|
'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name INNER JOIN posts ON (posts.alias = some_name.name)'
|
@@ -3836,6 +3841,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3836
3841
|
@ds.filter(:num=>:$n).call([:to_hash, :a, :b], :n=>1)
|
3837
3842
|
@ds.filter(:num=>:$n).call([:to_hash_groups, :a, :b], :n=>1)
|
3838
3843
|
@ds.filter(:num=>:$n).call(:first, :n=>1)
|
3844
|
+
@ds.filter(:num=>:$n).call(:single_value, :n=>1)
|
3839
3845
|
@ds.filter(:num=>:$n).call(:delete, :n=>1)
|
3840
3846
|
@ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
|
3841
3847
|
@ds.call(:insert, {:n=>1}, :num=>:$n)
|
@@ -3849,6 +3855,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3849
3855
|
'SELECT * FROM items WHERE (num = 1)',
|
3850
3856
|
'SELECT * FROM items WHERE (num = 1)',
|
3851
3857
|
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
3858
|
+
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
3852
3859
|
'DELETE FROM items WHERE (num = 1)',
|
3853
3860
|
'UPDATE items SET num = 2 WHERE (num = 1)',
|
3854
3861
|
'INSERT INTO items (num) VALUES (1)',
|
@@ -3865,13 +3872,14 @@ describe "Dataset prepared statements and bound variables " do
|
|
3865
3872
|
pss << @ds.filter(:num=>:$n).prepare([:to_hash, :a, :b], :sh)
|
3866
3873
|
pss << @ds.filter(:num=>:$n).prepare([:to_hash_groups, :a, :b], :shg)
|
3867
3874
|
pss << @ds.filter(:num=>:$n).prepare(:first, :fn)
|
3875
|
+
pss << @ds.filter(:num=>:$n).prepare(:single_value, :svn)
|
3868
3876
|
pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
|
3869
3877
|
pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
|
3870
3878
|
pss << @ds.prepare(:insert, :in, :num=>:$n)
|
3871
3879
|
pss << @ds.prepare(:insert_pk, :inp, :num=>:$n)
|
3872
3880
|
pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
|
3873
|
-
@db.prepared_statements.keys.sort_by{|k| k.to_s}.must_equal [:ah, :dn, :en, :fn, :in, :inp, :ins, :sh, :shg, :sm, :sn, :un]
|
3874
|
-
[:en, :sn, :sm, :ah, :sh, :shg, :fn, :dn, :un, :in, :inp, :ins].each_with_index{|x, i| @db.prepared_statements[x].must_equal pss[i]}
|
3881
|
+
@db.prepared_statements.keys.sort_by{|k| k.to_s}.must_equal [:ah, :dn, :en, :fn, :in, :inp, :ins, :sh, :shg, :sm, :sn, :svn, :un]
|
3882
|
+
[:en, :sn, :sm, :ah, :sh, :shg, :fn, :svn, :dn, :un, :in, :inp, :ins].each_with_index{|x, i| @db.prepared_statements[x].must_equal pss[i]}
|
3875
3883
|
@db.call(:en, :n=>1){}
|
3876
3884
|
@db.call(:sn, :n=>1)
|
3877
3885
|
@db.call(:sm, :n=>1)
|
@@ -3879,6 +3887,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3879
3887
|
@db.call(:sh, :n=>1)
|
3880
3888
|
@db.call(:shg, :n=>1)
|
3881
3889
|
@db.call(:fn, :n=>1)
|
3890
|
+
@db.call(:svn, :n=>1)
|
3882
3891
|
@db.call(:dn, :n=>1)
|
3883
3892
|
@db.call(:un, :n=>1, :n2=>2)
|
3884
3893
|
@db.call(:in, :n=>1)
|
@@ -3892,6 +3901,7 @@ describe "Dataset prepared statements and bound variables " do
|
|
3892
3901
|
'SELECT * FROM items WHERE (num = 1)',
|
3893
3902
|
'SELECT * FROM items WHERE (num = 1)',
|
3894
3903
|
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
3904
|
+
'SELECT * FROM items WHERE (num = 1) LIMIT 1',
|
3895
3905
|
'DELETE FROM items WHERE (num = 1)',
|
3896
3906
|
'UPDATE items SET num = 2 WHERE (num = 1)',
|
3897
3907
|
'INSERT INTO items (num) VALUES (1)',
|
@@ -4032,6 +4042,7 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
|
|
4032
4042
|
@ps << @ds.prepare(:select, :s)
|
4033
4043
|
@ps << @ds.prepare(:all, :a)
|
4034
4044
|
@ps << @ds.prepare(:first, :f)
|
4045
|
+
@ps << @ds.prepare(:single_value, :sv)
|
4035
4046
|
@ps << @ds.prepare(:delete, :d)
|
4036
4047
|
@ps << @ds.prepare(:insert, :i, :num=>:$n)
|
4037
4048
|
@ps << @ds.prepare(:update, :u, :num=>:$n)
|
@@ -4046,6 +4057,7 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
|
|
4046
4057
|
@db.sqls.must_equal ["SELECT * FROM items WHERE (num = ?) -- args: [1]",
|
4047
4058
|
"SELECT * FROM items WHERE (num = ?) -- args: [1]",
|
4048
4059
|
"SELECT * FROM items WHERE (num = ?) LIMIT 1 -- args: [1]",
|
4060
|
+
"SELECT * FROM items WHERE (num = ?) LIMIT 1 -- args: [1]",
|
4049
4061
|
"DELETE FROM items WHERE (num = ?) -- args: [1]",
|
4050
4062
|
"INSERT INTO items (num) VALUES (?) -- args: [1]",
|
4051
4063
|
"UPDATE items SET num = ? WHERE (num = ?) -- args: [1, 1]"]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "constant_sql_override extension" do
|
4
|
+
before do
|
5
|
+
@db = Sequel.mock.extension(:constant_sql_override)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'overrides configured constants' do
|
9
|
+
@db.set_constant_sql(Sequel::CURRENT_TIMESTAMP, "CURRENT TIMESTAMP AT TIME ZONE 'UTC'")
|
10
|
+
@db[:tbl].where(foo: Sequel::CURRENT_TIMESTAMP).first
|
11
|
+
@db.sqls.must_equal ["SELECT * FROM tbl WHERE (foo = CURRENT TIMESTAMP AT TIME ZONE 'UTC') LIMIT 1"]
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'freezes the constant_sqls hash' do
|
15
|
+
@db.freeze
|
16
|
+
@db.constant_sqls.frozen?.must_equal true
|
17
|
+
end
|
18
|
+
end
|
@@ -122,6 +122,10 @@ describe "Sequel::Plugins::StaticCache" do
|
|
122
122
|
@c.map.frozen?.must_equal false
|
123
123
|
end
|
124
124
|
|
125
|
+
it "should have map without a block return an Enumerator" do
|
126
|
+
@c.map.class.must_equal Enumerator
|
127
|
+
end
|
128
|
+
|
125
129
|
it "should have map with a block and argument raise" do
|
126
130
|
proc{@c.map(:id){}}.must_raise(Sequel::Error)
|
127
131
|
end
|
@@ -148,6 +152,14 @@ describe "Sequel::Plugins::StaticCache" do
|
|
148
152
|
@c.all.must_equal [@c1, @c2]
|
149
153
|
end
|
150
154
|
|
155
|
+
it "should have all receiving block" do
|
156
|
+
a = []
|
157
|
+
b = @c.all { |o| a << o }
|
158
|
+
a.must_equal [@c1, @c2]
|
159
|
+
a.must_equal b
|
160
|
+
@db.sqls.must_equal []
|
161
|
+
end
|
162
|
+
|
151
163
|
it "should have as_hash/to_hash without arguments run without a query" do
|
152
164
|
a = @c.to_hash
|
153
165
|
a.must_equal(1=>@c1, 2=>@c2)
|
@@ -21,6 +21,7 @@ describe "Prepared Statements and Bound Arguments" do
|
|
21
21
|
@ds.filter(:numb=>:$n).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
|
22
22
|
@ds.filter(:numb=>:$n).call(:all, :n=>10).must_equal [{:id=>1, :numb=>10}]
|
23
23
|
@ds.filter(:numb=>:$n).call(:first, :n=>10).must_equal(:id=>1, :numb=>10)
|
24
|
+
@ds.select(:numb).filter(:numb=>:$n).call(:single_value, :n=>10).must_equal(10)
|
24
25
|
@ds.filter(:numb=>:$n).call([:map, :numb], :n=>10).must_equal [10]
|
25
26
|
@ds.filter(:numb=>:$n).call([:as_hash, :id, :numb], :n=>10).must_equal(1=>10)
|
26
27
|
@ds.filter(:numb=>:$n).call([:to_hash, :id, :numb], :n=>10).must_equal(1=>10)
|
@@ -39,20 +40,24 @@ describe "Prepared Statements and Bound Arguments" do
|
|
39
40
|
@ds.filter(:numb=>:$n).bind(:n=>10).call(:select).must_equal [{:id=>1, :numb=>10}]
|
40
41
|
@ds.filter(:numb=>:$n).bind(:n=>10).call(:all).must_equal [{:id=>1, :numb=>10}]
|
41
42
|
@ds.filter(:numb=>:$n).bind(:n=>10).call(:first).must_equal(:id=>1, :numb=>10)
|
43
|
+
@ds.select(:numb).filter(:numb=>:$n).bind(:n=>10).call(:single_value).must_equal(10)
|
42
44
|
|
43
45
|
@ds.bind(:n=>10).filter(:numb=>:$n).call(:select).must_equal [{:id=>1, :numb=>10}]
|
44
46
|
@ds.bind(:n=>10).filter(:numb=>:$n).call(:all).must_equal [{:id=>1, :numb=>10}]
|
45
47
|
@ds.bind(:n=>10).filter(:numb=>:$n).call(:first).must_equal(:id=>1, :numb=>10)
|
48
|
+
@ds.bind(:n=>10).select(:numb).filter(:numb=>:$n).call(:single_value).must_equal(10)
|
46
49
|
end
|
47
50
|
|
48
51
|
it "should allow overriding variables specified with #bind" do
|
49
52
|
@ds.filter(:numb=>:$n).bind(:n=>1).call(:select, :n=>10).must_equal [{:id=>1, :numb=>10}]
|
50
53
|
@ds.filter(:numb=>:$n).bind(:n=>1).call(:all, :n=>10).must_equal [{:id=>1, :numb=>10}]
|
51
54
|
@ds.filter(:numb=>:$n).bind(:n=>1).call(:first, :n=>10).must_equal(:id=>1, :numb=>10)
|
55
|
+
@ds.select(:numb).filter(:numb=>:$n).bind(:n=>1).call(:single_value, :n=>10).must_equal(10)
|
52
56
|
|
53
57
|
@ds.filter(:numb=>:$n).bind(:n=>1).bind(:n=>10).call(:select).must_equal [{:id=>1, :numb=>10}]
|
54
58
|
@ds.filter(:numb=>:$n).bind(:n=>1).bind(:n=>10).call(:all).must_equal [{:id=>1, :numb=>10}]
|
55
59
|
@ds.filter(:numb=>:$n).bind(:n=>1).bind(:n=>10).call(:first).must_equal(:id=>1, :numb=>10)
|
60
|
+
@ds.select(:numb).filter(:numb=>:$n).bind(:n=>1).bind(:n=>10).call(:single_value).must_equal(10)
|
56
61
|
end
|
57
62
|
|
58
63
|
it "should support placeholder literal strings with call" do
|
@@ -160,6 +165,8 @@ describe "Prepared Statements and Bound Arguments" do
|
|
160
165
|
@db.call(:select_n, :n=>10).must_equal [{:id=>1, :numb=>10}]
|
161
166
|
@ds.filter(:numb=>:$n).prepare(:first, :select_n)
|
162
167
|
@db.call(:select_n, :n=>10).must_equal(:id=>1, :numb=>10)
|
168
|
+
@ds.select(:numb).filter(:numb=>:$n).prepare(:single_value, :select_n)
|
169
|
+
@db.call(:select_n, :n=>10).must_equal(10)
|
163
170
|
@ds.filter(:numb=>:$n).prepare([:map, :numb], :select_n)
|
164
171
|
@db.call(:select_n, :n=>10).must_equal [10]
|
165
172
|
@ds.filter(:numb=>:$n).prepare([:as_hash, :id, :numb], :select_n)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -196,6 +196,7 @@ extra_rdoc_files:
|
|
196
196
|
- doc/release_notes/5.10.0.txt
|
197
197
|
- doc/release_notes/5.11.0.txt
|
198
198
|
- doc/release_notes/5.12.0.txt
|
199
|
+
- doc/release_notes/5.13.0.txt
|
199
200
|
files:
|
200
201
|
- CHANGELOG
|
201
202
|
- MIT-LICENSE
|
@@ -278,6 +279,7 @@ files:
|
|
278
279
|
- doc/release_notes/5.10.0.txt
|
279
280
|
- doc/release_notes/5.11.0.txt
|
280
281
|
- doc/release_notes/5.12.0.txt
|
282
|
+
- doc/release_notes/5.13.0.txt
|
281
283
|
- doc/release_notes/5.2.0.txt
|
282
284
|
- doc/release_notes/5.3.0.txt
|
283
285
|
- doc/release_notes/5.4.0.txt
|
@@ -384,6 +386,7 @@ files:
|
|
384
386
|
- lib/sequel/extensions/columns_introspection.rb
|
385
387
|
- lib/sequel/extensions/connection_expiration.rb
|
386
388
|
- lib/sequel/extensions/connection_validator.rb
|
389
|
+
- lib/sequel/extensions/constant_sql_override.rb
|
387
390
|
- lib/sequel/extensions/constraint_validations.rb
|
388
391
|
- lib/sequel/extensions/core_extensions.rb
|
389
392
|
- lib/sequel/extensions/core_refinements.rb
|
@@ -590,6 +593,7 @@ files:
|
|
590
593
|
- spec/extensions/composition_spec.rb
|
591
594
|
- spec/extensions/connection_expiration_spec.rb
|
592
595
|
- spec/extensions/connection_validator_spec.rb
|
596
|
+
- spec/extensions/constant_sql_override_spec.rb
|
593
597
|
- spec/extensions/constraint_validations_plugin_spec.rb
|
594
598
|
- spec/extensions/constraint_validations_spec.rb
|
595
599
|
- spec/extensions/core_refinements_spec.rb
|