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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/doc/advanced_associations.rdoc +132 -14
  4. data/doc/postgresql.rdoc +14 -0
  5. data/doc/release_notes/5.12.0.txt +141 -0
  6. data/lib/sequel/adapters/ado/mssql.rb +1 -1
  7. data/lib/sequel/adapters/oracle.rb +5 -6
  8. data/lib/sequel/adapters/postgres.rb +18 -5
  9. data/lib/sequel/adapters/shared/mysql.rb +5 -5
  10. data/lib/sequel/adapters/sqlite.rb +0 -5
  11. data/lib/sequel/adapters/tinytds.rb +0 -5
  12. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
  13. data/lib/sequel/core.rb +6 -1
  14. data/lib/sequel/dataset/graph.rb +25 -9
  15. data/lib/sequel/dataset/placeholder_literalizer.rb +47 -17
  16. data/lib/sequel/dataset/prepared_statements.rb +86 -18
  17. data/lib/sequel/dataset/sql.rb +5 -1
  18. data/lib/sequel/extensions/caller_logging.rb +79 -0
  19. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  20. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  21. data/lib/sequel/model/associations.rb +56 -23
  22. data/lib/sequel/model/base.rb +3 -3
  23. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  24. data/lib/sequel/plugins/static_cache.rb +9 -8
  25. data/lib/sequel/plugins/tactical_eager_loading.rb +63 -1
  26. data/lib/sequel/version.rb +1 -1
  27. data/spec/adapters/oracle_spec.rb +44 -0
  28. data/spec/adapters/postgres_spec.rb +39 -0
  29. data/spec/core/dataset_spec.rb +23 -9
  30. data/spec/core/object_graph_spec.rb +314 -284
  31. data/spec/extensions/caller_logging_spec.rb +52 -0
  32. data/spec/extensions/eager_graph_eager_spec.rb +100 -0
  33. data/spec/extensions/finder_spec.rb +1 -1
  34. data/spec/extensions/prepared_statements_spec.rb +7 -12
  35. data/spec/extensions/static_cache_spec.rb +14 -0
  36. data/spec/extensions/tactical_eager_loading_spec.rb +262 -1
  37. data/spec/integration/associations_test.rb +72 -0
  38. data/spec/integration/dataset_test.rb +3 -3
  39. data/spec/model/eager_loading_spec.rb +90 -0
  40. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 935bcf2280ce67a62dfd6e705308c85324f9d82303f5144ccbede8883d0956e5
4
- data.tar.gz: 91dc87ca6afcaa82372233829919bab98227e2f7a7555bc0229811cfc829b482
3
+ metadata.gz: 5d316ca35fb36d4b3777021b4e3d6c108036f0bb1197ab7c9efc4529012858d1
4
+ data.tar.gz: 7f0e338f35027945f84cfeb9d6d997114e098f46d74b7b2fc3e32db34c4f5925
5
5
  SHA512:
6
- metadata.gz: f0540143323390f812a543e8a28fa2ec3184faad7ad5508e9d64cf49bf6642f09dd636f0356d8bd2a4ad632a21e5a4358eda096d86b08ddaae0f62cdf8bfc9fa
7
- data.tar.gz: f85a4c4880b34f737b00affaab544473e02d70052e70ddc438597455974b411a65093f2aa511af84558a15121f3032192df191677bc7ea4b1c0b779cda1714ae
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 very powerful and flexible, but using that power and flexibility
4
- often results in complexity.
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
- == Background: Sequel::Model association options
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. There's also an :eager_block option if you want to use
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 are a whole bunch of options for changing how the association is eagerly
24
- loaded via <tt>Dataset#eager_graph</tt>: <tt>:graph_block</tt>, <tt>:graph_conditions</tt>,
25
- <tt>:graph_only_conditions</tt>, <tt>:graph_join_type</tt> (and <tt>:graph_join_table_*</tt> ones for
26
- JOINing to the join table in a many_to_many association).
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),
@@ -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}