sequel 5.11.0 → 5.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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}
|