sequel 5.11.0 → 5.12.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 +32 -0
- data/doc/advanced_associations.rdoc +132 -14
- data/doc/postgresql.rdoc +14 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/lib/sequel/adapters/ado/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +5 -6
- data/lib/sequel/adapters/postgres.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +5 -5
- data/lib/sequel/adapters/sqlite.rb +0 -5
- data/lib/sequel/adapters/tinytds.rb +0 -5
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
- data/lib/sequel/core.rb +6 -1
- data/lib/sequel/dataset/graph.rb +25 -9
- data/lib/sequel/dataset/placeholder_literalizer.rb +47 -17
- data/lib/sequel/dataset/prepared_statements.rb +86 -18
- data/lib/sequel/dataset/sql.rb +5 -1
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
- data/lib/sequel/model/associations.rb +56 -23
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/static_cache.rb +9 -8
- data/lib/sequel/plugins/tactical_eager_loading.rb +63 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/oracle_spec.rb +44 -0
- data/spec/adapters/postgres_spec.rb +39 -0
- data/spec/core/dataset_spec.rb +23 -9
- data/spec/core/object_graph_spec.rb +314 -284
- data/spec/extensions/caller_logging_spec.rb +52 -0
- data/spec/extensions/eager_graph_eager_spec.rb +100 -0
- data/spec/extensions/finder_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +7 -12
- data/spec/extensions/static_cache_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +262 -1
- data/spec/integration/associations_test.rb +72 -0
- data/spec/integration/dataset_test.rb +3 -3
- data/spec/model/eager_loading_spec.rb +90 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d316ca35fb36d4b3777021b4e3d6c108036f0bb1197ab7c9efc4529012858d1
|
4
|
+
data.tar.gz: 7f0e338f35027945f84cfeb9d6d997114e098f46d74b7b2fc3e32db34c4f5925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d5f3c64cf5e6361ced90c90c0cfd8e3f35cfb50e2e5acfd7b25b79728dbf207bf673e03662ab72e9208cd283675e278f126d27d656a7e2f7ec0baec7330a0b4
|
7
|
+
data.tar.gz: c741e9fd139c5e8b13c0c434f66836bbecba900f03eecddf559f1992d4155bfcb2af1e846425d9f495fc442f09e79c82e1933dc3a362f81caced379ef608e248
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
=== 5.12.0 (2018-08-31)
|
2
|
+
|
3
|
+
* Make constraint_validations extension respect Database#constraint_validations_table setting (jeremyevans)
|
4
|
+
|
5
|
+
* Make Sequel.extension load files from gems (jeremyevans)
|
6
|
+
|
7
|
+
* Map clob prepared statement argument type to OCI8::CLOB in the oracle adapter (pipistrellka) (#1534)
|
8
|
+
|
9
|
+
* Make Model.load_cache public in the static_cache plugin (AlexWayfer) (#1533)
|
10
|
+
|
11
|
+
* Enable support for NOWAIT on MariaDB 10.3+ (jeremyevans)
|
12
|
+
|
13
|
+
* Enable support for INTERSECT and EXCEPT on MariaDB 10.3+ (jeremyevans)
|
14
|
+
|
15
|
+
* Make tactical_eager_loading plugin handle automatic eager loading for associated objects created by eager_graph (jeremyevans)
|
16
|
+
|
17
|
+
* Cache eager_graph loader to speed up subsequent loads from the same dataset (jeremyevans)
|
18
|
+
|
19
|
+
* Add caller_logging database extension to log callers before queries, useful during development (jeremyevans)
|
20
|
+
|
21
|
+
* Add Database#call_procedure in the postgres adapter for calling PostgreSQL 11+ procedures (jeremyevans)
|
22
|
+
|
23
|
+
* Add eager_graph_eager plugin for chaining eager association loads after eager_graph association loads (jeremyevans)
|
24
|
+
|
25
|
+
* Support using Dataset#eager_graph in eager load callback for associations using join tables (jeremyevans)
|
26
|
+
|
27
|
+
* Make Dataset#graph handle existing selections without determinable aliases by forcing a subselect (jeremyevans)
|
28
|
+
|
29
|
+
* Freeze prepared statement arguments before returning the prepared statement (jeremyevans)
|
30
|
+
|
31
|
+
* Refactor emulated prepared statement internals to use a placeholder literalizer (jeremyevans)
|
32
|
+
|
1
33
|
=== 5.11.0 (2018-08-01)
|
2
34
|
|
3
35
|
* Fix using the jdbc/sqlserver adapter on JRuby 9.2+ (jeremyevans)
|
@@ -1,29 +1,149 @@
|
|
1
1
|
= Advanced Associations
|
2
2
|
|
3
|
-
Sequel::Model's association support is
|
4
|
-
|
3
|
+
Sequel::Model's association support is powerful and flexible, but it can be difficult for
|
4
|
+
new users to understand what the support enables. This guide shows off some of the more
|
5
|
+
advanced Sequel::Model association features.
|
5
6
|
|
6
7
|
You should probably review the {Model Associations Basics and Options guide}[rdoc-ref:doc/association_basics.rdoc]
|
7
8
|
before reviewing this guide.
|
8
9
|
|
9
|
-
==
|
10
|
+
== Sequel::Model Eager Loading
|
11
|
+
|
12
|
+
Sequel::Model offers two different ways to perform eager loading, +eager+ and
|
13
|
+
+eager_graph+. +eager+ uses an SQL query per association, +eager_graph+ uses a single
|
14
|
+
SQL query containing JOINs.
|
15
|
+
|
16
|
+
Assuming the following associations:
|
17
|
+
|
18
|
+
Artist.one_to_many :albums
|
19
|
+
Album.one_to_many :tracks
|
20
|
+
Tracks.many_to_one :lyric
|
21
|
+
|
22
|
+
Let's say you wanted to load all artists and eagerly load the related albums, tracks, and lyrics.
|
23
|
+
|
24
|
+
Artist.eager(albums: {tracks: :lyric})
|
25
|
+
# 4 Queries:
|
26
|
+
# SELECT * FROM artists;
|
27
|
+
# SELECT * FROM albums WHERE (artist_id IN (...));
|
28
|
+
# SELECT * FROM tracks WHERE (album_id IN (...));
|
29
|
+
# SELECT * FROM lyrics WHERE (id IN (...));
|
30
|
+
|
31
|
+
Artist.eager_graph(albums: {tracks: :lyric})
|
32
|
+
# 1 Query:
|
33
|
+
# SELECT artists.id, artists.name, ...
|
34
|
+
# albums.id AS albums_id, albums.name AS albums_name, ...
|
35
|
+
# tracks.id AS tracks_id, tracks.name AS tracks_name, ...
|
36
|
+
# lyric.id AS lyric_id, ...
|
37
|
+
# FROM artists
|
38
|
+
# LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
39
|
+
# LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
40
|
+
# LEFT OUTER JOIN lyrics AS lyric ON (lyric.id = tracks.lyric_id);
|
41
|
+
|
42
|
+
In general, the recommendation is to use +eager+ unless you have a reason to use +eager_graph+.
|
43
|
+
+eager_graph+ is needed when you want to reference columns in an associated table. For example,
|
44
|
+
if you want to order the loading of returned artists based on the names of the albums, you cannot
|
45
|
+
do:
|
46
|
+
|
47
|
+
Artist.eager(albums: {tracks: :lyric}).order{albums[:name]}
|
48
|
+
|
49
|
+
because the initial query Sequel will use would be:
|
50
|
+
|
51
|
+
# SELECT * FROM artists ORDER BY albums.name;
|
52
|
+
|
53
|
+
and +albums+ is not a valid qualifier in such a query. In this situation, you must use +eager_graph+:
|
54
|
+
|
55
|
+
Artist.eager_graph(albums: {tracks: :lyric}).order{albums[:name]}
|
56
|
+
|
57
|
+
Whether +eager+ or +eager_graph+ performs better is association and database dependent. If
|
58
|
+
you are concerned about performance, you should try benchmarking both cases with appropriate
|
59
|
+
data to see which performs better.
|
60
|
+
|
61
|
+
=== Mixing eager and eager_graph
|
62
|
+
|
63
|
+
Sequel offers the ability to mix +eager+ and +eager_graph+ when loading results. This can
|
64
|
+
be done at the main level by calling both +eager+ and +eager_graph+ on the same dataset:
|
65
|
+
|
66
|
+
Album.eager(:artist).eager_graph(:tracks)
|
67
|
+
# 2 Queries:
|
68
|
+
# SELECT albums.id, albums.name, ...
|
69
|
+
# artist.id AS artist_id, artist.name AS artist_name, ...
|
70
|
+
# FROM albums
|
71
|
+
# LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id);
|
72
|
+
# SELECT * FROM artists WHERE (id IN (...));
|
73
|
+
|
74
|
+
You can also use +eager+ to load initial associations, and +eager_graph+ to load
|
75
|
+
remaining associations, by using +eager_graph+ in an eager load callback:
|
76
|
+
|
77
|
+
Artist.eager(albums: {tracks: proc{|ds| ds.eager_graph(:lyric)}})
|
78
|
+
# 3 Queries:
|
79
|
+
# SELECT * FROM artists;
|
80
|
+
# SELECT * FROM albums WHERE (artist_id IN (...));
|
81
|
+
# SELECT tracks.id, tracks.name, ...
|
82
|
+
# lyric.id AS lyric_id, ...
|
83
|
+
# FROM tracks
|
84
|
+
# LEFT OUTER JOIN lyrics AS lyric ON (lyric.id = tracks.lyric_id)
|
85
|
+
# WHERE (tracks.album_id IN (...));
|
86
|
+
|
87
|
+
Using the +eager_graph_eager+ plugin, you can use +eager_graph+ to load the
|
88
|
+
initial associations, and +eager+ to load the remaining associations. When
|
89
|
+
you call +eager_graph_eager+, you must specify the dependency chain at
|
90
|
+
which to start the eager loading via +eager+:
|
91
|
+
|
92
|
+
Artist.plugin :eager_graph_eager
|
93
|
+
Artist.eager_graph(albums: :tracks).eager_graph_eager([:albums, :tracks], :lyric)
|
94
|
+
# 2 Queries:
|
95
|
+
# SELECT artists.id, artists.name, ...
|
96
|
+
# albums.id AS albums_id, albums.name AS albums_name, ...
|
97
|
+
# tracks.id AS tracks_id, tracks.name AS tracks_name, ...
|
98
|
+
# FROM artists
|
99
|
+
# LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
100
|
+
# LEFT OUTER JOIN tracks ON (tracks.album_id= albums.id);
|
101
|
+
# SELECT * FROM lyrics WHERE (id IN (...));
|
102
|
+
|
103
|
+
These two approaches can also be nested, with +eager+ -> +eager_graph+ -> +eager+:
|
104
|
+
|
105
|
+
Album.plugin :eager_graph_eager
|
106
|
+
Artist.eager(albums: proc{|ds| ds.eager_graph(:tracks).eager_graph_eager([:tracks], :lyric)})
|
107
|
+
# 3 Queries:
|
108
|
+
# SELECT * FROM artists;
|
109
|
+
# SELECT albums.id, albums.name, ...
|
110
|
+
# tracks.id AS tracks_id, tracks.name AS tracks_name, ...
|
111
|
+
# FROM albums
|
112
|
+
# LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
113
|
+
# WHERE (albums.artist_id IN (...));
|
114
|
+
# SELECT * FROM lyrics WHERE (id IN (...));
|
115
|
+
|
116
|
+
Or with 2 separate +eager_graph+ queries:
|
117
|
+
|
118
|
+
Artist.eager_graph(:albums).eager_graph_eager([:albums], :tracks=>proc{|ds| ds.eager_graph(:lyric)})
|
119
|
+
# 2 Queries:
|
120
|
+
# SELECT artists.id, artists.name, ...
|
121
|
+
# albums.id AS albums_id, albums.name AS albums_name, ...
|
122
|
+
# FROM artists
|
123
|
+
# LEFT OUTER JOIN albums ON (albums.artist_id = artists.id);
|
124
|
+
# SELECT tracks.id, tracks.name, ...
|
125
|
+
# lyric.id AS lyric_id, ...
|
126
|
+
# FROM tracks
|
127
|
+
# LEFT OUTER JOIN lyrics AS lyric ON (lyric.id = tracks.lyric_id)
|
128
|
+
# WHERE (tracks.album_id IN (...));
|
129
|
+
|
130
|
+
== Sequel::Model Association Loading Options
|
10
131
|
|
11
132
|
There are a bunch of advanced association options that are available to
|
12
133
|
handle more complex cases. First we'll go over some of the simpler ones:
|
13
134
|
|
14
135
|
All associations take a block that can be used to further filter/modify the
|
15
|
-
default dataset
|
16
|
-
a different block when eager loading via <tt>Dataset#eager</tt>. Association blocks are
|
17
|
-
useful for things like:
|
136
|
+
default dataset:
|
18
137
|
|
19
138
|
Artist.one_to_many :gold_albums, class: :Album do |ds|
|
20
139
|
ds.where{copies_sold > 500000}
|
21
140
|
end
|
22
141
|
|
23
|
-
There
|
24
|
-
|
25
|
-
|
26
|
-
|
142
|
+
There's also an :eager_block option if you want to use a different block when
|
143
|
+
eager loading via <tt>Dataset#eager</tt>.
|
144
|
+
|
145
|
+
There are many options for changing how the association is eagerly
|
146
|
+
loaded via <tt>Dataset#eager_graph</tt>:
|
27
147
|
|
28
148
|
:graph_join_type :: The type of join to do (<tt>:inner</tt>, <tt>:left</tt>, <tt>:right</tt>)
|
29
149
|
:graph_conditions :: Additional conditions to put on join (needs to be a
|
@@ -62,10 +182,6 @@ These can be used like this:
|
|
62
182
|
# Handles the case where both key columns have the name artist_name, and you want to use
|
63
183
|
# a JOIN USING
|
64
184
|
Artist.one_to_many :albums, key: :artist_name, graph_only_conditions: [:artist_name]
|
65
|
-
|
66
|
-
Remember, using +eager_graph+ is generally only necessary when you need to
|
67
|
-
filter/order based on columns in an associated table, it is recommended to
|
68
|
-
use +eager+ for eager loading if possible.
|
69
185
|
|
70
186
|
One advantage of using +eager_graph+ is that you can easily filter/order
|
71
187
|
on columns in an associated table on a per-query basis, using regular
|
@@ -83,6 +199,8 @@ For lazy loading (e.g. Model[1].association), the <tt>:dataset</tt> option can b
|
|
83
199
|
to specify an arbitrary dataset (one that uses different keys, multiple keys,
|
84
200
|
joins to other tables, etc.).
|
85
201
|
|
202
|
+
== Custom Eager Loaders
|
203
|
+
|
86
204
|
For eager loading via +eager+, the <tt>:eager_loader</tt> option can be used to specify
|
87
205
|
how to eagerly load a complex association. This is an extremely powerful
|
88
206
|
option. Though it can often be verbose (compared to other things in Sequel),
|
data/doc/postgresql.rdoc
CHANGED
@@ -337,6 +337,20 @@ rows that are distinct on just those columns:
|
|
337
337
|
DB[:table].distinct(:id).all
|
338
338
|
# SELECT DISTINCT ON ("id") * FROM "table"
|
339
339
|
|
340
|
+
=== Calling PostgreSQL 11+ Procedures <tt>postgres only</tt>
|
341
|
+
|
342
|
+
PostgreSQL 11+ added support for procedures, which are different from the user defined
|
343
|
+
functions that PostgreSQL has historically supported. These procedures are
|
344
|
+
called via a special +CALL+ syntax, and Sequel supports them via
|
345
|
+
<tt>Database#call_procedure</tt>:
|
346
|
+
|
347
|
+
DB.call_procedure(:foo, 1, "bar")
|
348
|
+
# CALL foo(1, 'bar')
|
349
|
+
|
350
|
+
<tt>Database#call_procedure</tt> will return a hash of return values if
|
351
|
+
the procedure returns a result, or +nil+ if the procedure does not return
|
352
|
+
a result.
|
353
|
+
|
340
354
|
=== Using a Cursor to Process Large Datasets <tt>postgres only</tt>
|
341
355
|
|
342
356
|
The postgres adapter offers a <tt>Dataset#use_cursor</tt> method to process large result sets
|
@@ -0,0 +1,141 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An eager_graph_eager plugin has been added, which allows you to
|
4
|
+
chain eager loads using separate queries to an existing dataset that
|
5
|
+
uses eager_graph. Given the following model associations:
|
6
|
+
|
7
|
+
Band.one_to_many :albums
|
8
|
+
Album.one_to_many :tracks
|
9
|
+
|
10
|
+
Let's say you wanted to return bands ordered by album name, and
|
11
|
+
eagerly load those albums, you can do that using:
|
12
|
+
|
13
|
+
Band.eager_graph(:albums).order{albums[:name]}
|
14
|
+
|
15
|
+
Let's say you also wanted to eagerly load the tracks for each album.
|
16
|
+
You could just add them to the eager_graph call:
|
17
|
+
|
18
|
+
Band.eager_graph(albums: :tracks).order{albums[:name]}
|
19
|
+
|
20
|
+
However, the bloats the result set, and you aren't ordering by the
|
21
|
+
track information, so a join is not required. The eager_graph_eager
|
22
|
+
plugin allows you to specify that the tracks be eagerly loaded in a
|
23
|
+
separate query after the eager_graph load of albums:
|
24
|
+
|
25
|
+
Band.eager_graph(:albums).
|
26
|
+
eager_graph_eager([:albums], :tracks).
|
27
|
+
order{albums[:name]}
|
28
|
+
|
29
|
+
eager_graph_eager's first argument is a dependency chain, specified
|
30
|
+
as an array of symbols. This specifies the point at which to
|
31
|
+
perform the eager load. The remaining arguments are arguments that
|
32
|
+
could be passed to Dataset#eager to specify what dependent
|
33
|
+
associations should be loaded at that point.
|
34
|
+
|
35
|
+
* A caller_logging Database extension has been added, which logs
|
36
|
+
caller information before queries, filtering out the internal
|
37
|
+
Sequel callers. Example:
|
38
|
+
|
39
|
+
DB.extension :caller_logging
|
40
|
+
DB[:table].first
|
41
|
+
# Logger:
|
42
|
+
# (0.000041s) (source: /path/to/app/foo/t.rb:12 in `get_first`)
|
43
|
+
# SELECT * FROM table LIMIT 1
|
44
|
+
|
45
|
+
You can further filter the caller lines by setting
|
46
|
+
Database#caller_logging_ignore to a regexp of additional caller
|
47
|
+
lines to ignore. This is useful if you have specific methods or
|
48
|
+
internal extensions/plugins that you would also like to ignore as
|
49
|
+
they obscure the code actually making the request.
|
50
|
+
|
51
|
+
DB.caller_logging_ignore = %r{/path/to/app/lib/plugins}
|
52
|
+
|
53
|
+
You can also format the caller before it is placed in the logger,
|
54
|
+
using caller_logging_formatter:
|
55
|
+
|
56
|
+
DB.caller_logging_formatter = lambda do |caller|
|
57
|
+
"(#{caller.sub(/\A\/path\/to\/app\//, '')})"
|
58
|
+
end
|
59
|
+
DB[:table].first
|
60
|
+
# Logger:
|
61
|
+
# (0.000041s) (foo/t.rb:12 in `get_first`) SELECT * FROM table LIMIT 1
|
62
|
+
|
63
|
+
* Database#call_procedure has been added to the postgres adapter, and
|
64
|
+
is usable on PostgreSQL 11+ for calling procedures created with
|
65
|
+
CREATE PROCEDURE.
|
66
|
+
|
67
|
+
DB.call_procedure(:foo, 1, "bar")
|
68
|
+
# CALL foo(1, 'bar')
|
69
|
+
|
70
|
+
This method will return a hash of results if the procedure returns
|
71
|
+
a result, or nil if it does not return a result.
|
72
|
+
|
73
|
+
= Other Improvements
|
74
|
+
|
75
|
+
* It is now possible to use Dataset#eager_graph in an eager load
|
76
|
+
callback for associations that use join tables. This allows you
|
77
|
+
to eager load some associations using separate queries and other
|
78
|
+
associations using joins. For example:
|
79
|
+
|
80
|
+
Band.eager(:albums=>proc{|ds| ds.eager_graph(:tracks)})
|
81
|
+
|
82
|
+
Will load the bands in one query, and load the albums and tracks
|
83
|
+
in a separate query using a join. Previously, this construction
|
84
|
+
worked only for associations that did not use join tables. It now
|
85
|
+
works for associations that use join tables, as long as existing
|
86
|
+
selected columns are not removed inside the callback.
|
87
|
+
|
88
|
+
* The tactical_eager_loading plugin now handles automatic eager
|
89
|
+
loading for associated objects that were created during the
|
90
|
+
load of dataset that uses eager_graph. When using the plugin,
|
91
|
+
the following code will now only execute 2 queries, instead of
|
92
|
+
issuing a separate query for each album to get the tracks for
|
93
|
+
the album.
|
94
|
+
|
95
|
+
artists = Artist.eager_graph(:albums).all
|
96
|
+
artists.each do |artist|
|
97
|
+
artist.albums.each do |album|
|
98
|
+
album.tracks
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
* Calling Dataset#graph with a dataset with existing selections where
|
103
|
+
the column aliases cannot be determined automatically now works
|
104
|
+
correctly by using a subselect. Previously, attempting to do this
|
105
|
+
would raise an exception. This allows the following code to work:
|
106
|
+
|
107
|
+
DB[:table].select_all(:table).select_append(expr).graph(...)
|
108
|
+
|
109
|
+
* Datasets now cache the EagerGraphLoader object that is generated to
|
110
|
+
convert arrays of hashes into an object graph, so that subsequent
|
111
|
+
eager loads on the same dataset do not need to recompute the same
|
112
|
+
information. Most EagerGraphLoader internal state is now frozen to
|
113
|
+
prevent unintentional modification.
|
114
|
+
|
115
|
+
* Sequel.extension now loads files from gems. Previously, it used
|
116
|
+
Kernel.require, which does not load files from gems.
|
117
|
+
|
118
|
+
* Adapters that emulate prepared statements using literalization now
|
119
|
+
use a placeholder literalizer and should execute significantly
|
120
|
+
faster. More prepared statement internal metadata is now frozen
|
121
|
+
to prevent unintentional modification.
|
122
|
+
|
123
|
+
* Dataset#intersect, #except, and #nowait are now supported on MariaDB
|
124
|
+
10.3+.
|
125
|
+
|
126
|
+
* The constraint_validations extension now respects the
|
127
|
+
constraint_validations_table setting when adding metadata for the
|
128
|
+
constraint validations.
|
129
|
+
|
130
|
+
* In the oracle adapter, the clob prepared statement argument type is
|
131
|
+
now mapped to the OCI8::CLOB class, allowing the use of Oracle
|
132
|
+
procedures with clob output parameters.
|
133
|
+
|
134
|
+
* The Model.load_cache method in the static_cache plugin is now public.
|
135
|
+
|
136
|
+
= Backwards Compatibility
|
137
|
+
|
138
|
+
* The private Dataset#prepared_arg? method has been removed. It is no
|
139
|
+
longer necessary after the refactoring to the prepared statement
|
140
|
+
code. External adapters that currently call the method should be
|
141
|
+
updated to no longer call the method.
|
@@ -50,7 +50,7 @@ module Sequel
|
|
50
50
|
# is necessary as ADO's default :provider uses a separate native
|
51
51
|
# connection for each query.
|
52
52
|
def insert(*values)
|
53
|
-
return super if @opts[:sql] || @opts[:returning]
|
53
|
+
return super if (@opts[:sql] && !@opts[:prepared_sql]) || @opts[:returning]
|
54
54
|
with_sql("SET NOCOUNT ON; #{insert_sql(*values)}; SELECT CAST(SCOPE_IDENTITY() AS INTEGER)").single_value
|
55
55
|
end
|
56
56
|
|
@@ -115,7 +115,7 @@ module Sequel
|
|
115
115
|
|
116
116
|
PS_TYPES = {'string'=>String, 'integer'=>Integer, 'float'=>Float,
|
117
117
|
'decimal'=>Float, 'date'=>Time, 'datetime'=>Time,
|
118
|
-
'time'=>Time, 'boolean'=>String, 'blob'=>OCI8::BLOB}.freeze
|
118
|
+
'time'=>Time, 'boolean'=>String, 'blob'=>OCI8::BLOB, 'clob'=>OCI8::CLOB}.freeze
|
119
119
|
def cursor_bind_params(conn, cursor, args)
|
120
120
|
i = 0
|
121
121
|
args.map do |arg, type|
|
@@ -129,6 +129,10 @@ module Sequel
|
|
129
129
|
arg = arg.to_f
|
130
130
|
when ::Sequel::SQL::Blob
|
131
131
|
arg = ::OCI8::BLOB.new(conn, arg)
|
132
|
+
when String
|
133
|
+
if type == 'clob'
|
134
|
+
arg = ::OCI8::CLOB.new(conn, arg)
|
135
|
+
end
|
132
136
|
end
|
133
137
|
cursor.bind_param(i, arg, PS_TYPES[type] || arg.class)
|
134
138
|
arg
|
@@ -347,11 +351,6 @@ module Sequel
|
|
347
351
|
i = prepared_args.length
|
348
352
|
LiteralString.new(":#{i}")
|
349
353
|
end
|
350
|
-
|
351
|
-
# Always assume a prepared argument.
|
352
|
-
def prepared_arg?(k)
|
353
|
-
true
|
354
|
-
end
|
355
354
|
end
|
356
355
|
|
357
356
|
BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
|
@@ -173,6 +173,15 @@ module Sequel
|
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
+
# Call a procedure with the given name and arguments. Returns a hash if the procedure
|
177
|
+
# returns a value, and nil otherwise. Example:
|
178
|
+
#
|
179
|
+
# DB.call_procedure(:foo, 1, 2)
|
180
|
+
# # CALL foo(1, 2)
|
181
|
+
def call_procedure(name, *args)
|
182
|
+
dataset.send(:call_procedure, name, args)
|
183
|
+
end
|
184
|
+
|
176
185
|
# Connects to the database. In addition to the standard database
|
177
186
|
# options, using the :encoding or :charset option changes the
|
178
187
|
# client encoding for the connection, :connect_timeout is a
|
@@ -672,11 +681,6 @@ module Sequel
|
|
672
681
|
end
|
673
682
|
LiteralString.new("#{prepared_arg_placeholder}#{i}")
|
674
683
|
end
|
675
|
-
|
676
|
-
# Always assume a prepared argument.
|
677
|
-
def prepared_arg?(k)
|
678
|
-
true
|
679
|
-
end
|
680
684
|
end
|
681
685
|
|
682
686
|
BindArgumentMethods = prepared_statements_module(:bind, [ArgumentMapper], %w'execute execute_dui')
|
@@ -701,6 +705,15 @@ module Sequel
|
|
701
705
|
|
702
706
|
private
|
703
707
|
|
708
|
+
# Generate and execute a procedure call.
|
709
|
+
def call_procedure(name, args)
|
710
|
+
sql = String.new
|
711
|
+
sql << "CALL "
|
712
|
+
identifier_append(sql, name)
|
713
|
+
literal_append(sql, args)
|
714
|
+
with_sql_first(sql)
|
715
|
+
end
|
716
|
+
|
704
717
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
705
718
|
def cursor_fetch_rows(sql)
|
706
719
|
server_opts = {:server=>@opts[:server] || :read_only}
|