sequel 5.12.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|