sequel 2.3.0 → 2.4.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.
- data/CHANGELOG +16 -0
- data/README +4 -1
- data/Rakefile +17 -19
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/sharding.rdoc +113 -0
- data/lib/sequel_core/adapters/ado.rb +24 -17
- data/lib/sequel_core/adapters/db2.rb +30 -33
- data/lib/sequel_core/adapters/dbi.rb +15 -13
- data/lib/sequel_core/adapters/informix.rb +13 -14
- data/lib/sequel_core/adapters/jdbc.rb +243 -60
- data/lib/sequel_core/adapters/jdbc/mysql.rb +32 -24
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +32 -2
- data/lib/sequel_core/adapters/jdbc/sqlite.rb +16 -20
- data/lib/sequel_core/adapters/mysql.rb +164 -76
- data/lib/sequel_core/adapters/odbc.rb +21 -34
- data/lib/sequel_core/adapters/openbase.rb +10 -7
- data/lib/sequel_core/adapters/oracle.rb +17 -23
- data/lib/sequel_core/adapters/postgres.rb +246 -35
- data/lib/sequel_core/adapters/shared/mssql.rb +106 -0
- data/lib/sequel_core/adapters/shared/mysql.rb +34 -26
- data/lib/sequel_core/adapters/shared/postgres.rb +82 -38
- data/lib/sequel_core/adapters/shared/sqlite.rb +48 -16
- data/lib/sequel_core/adapters/sqlite.rb +141 -44
- data/lib/sequel_core/connection_pool.rb +85 -63
- data/lib/sequel_core/database.rb +46 -17
- data/lib/sequel_core/dataset.rb +21 -40
- data/lib/sequel_core/dataset/convenience.rb +3 -3
- data/lib/sequel_core/dataset/prepared_statements.rb +218 -0
- data/lib/sequel_core/exceptions.rb +0 -12
- data/lib/sequel_model/base.rb +1 -2
- data/lib/sequel_model/plugins.rb +1 -1
- data/spec/adapters/ado_spec.rb +32 -3
- data/spec/adapters/mysql_spec.rb +7 -8
- data/spec/integration/prepared_statement_test.rb +106 -0
- data/spec/sequel_core/connection_pool_spec.rb +105 -3
- data/spec/sequel_core/database_spec.rb +41 -3
- data/spec/sequel_core/dataset_spec.rb +117 -7
- data/spec/sequel_core/spec_helper.rb +2 -2
- data/spec/sequel_model/model_spec.rb +0 -6
- data/spec/sequel_model/spec_helper.rb +1 -1
- metadata +11 -6
- data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -54
- data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
data/lib/sequel_core/database.rb
CHANGED
@@ -40,6 +40,9 @@ module Sequel
|
|
40
40
|
|
41
41
|
# Whether to quote identifiers (columns and tables) for this database
|
42
42
|
attr_writer :quote_identifiers
|
43
|
+
|
44
|
+
# The prepared statement objects for this database, keyed by name
|
45
|
+
attr_reader :prepared_statements
|
43
46
|
|
44
47
|
# Constructs a new instance of a database connection with the specified
|
45
48
|
# options hash.
|
@@ -51,8 +54,10 @@ module Sequel
|
|
51
54
|
@quote_identifiers = opts.include?(:quote_identifiers) ? opts[:quote_identifiers] : @@quote_identifiers
|
52
55
|
@single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
|
53
56
|
@schemas = nil
|
57
|
+
@prepared_statements = {}
|
58
|
+
@transactions = []
|
54
59
|
@pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
|
55
|
-
@pool.connection_proc = proc
|
60
|
+
@pool.connection_proc = proc{|server| connect(server)} unless block
|
56
61
|
|
57
62
|
@loggers = Array(opts[:logger]) + Array(opts[:loggers])
|
58
63
|
::Sequel::DATABASES.push(self)
|
@@ -191,6 +196,12 @@ module Sequel
|
|
191
196
|
(String === args.first) ? fetch(*args, &block) : from(*args, &block)
|
192
197
|
end
|
193
198
|
|
199
|
+
# Call the prepared statement with the given name with the given hash
|
200
|
+
# of arguments.
|
201
|
+
def call(ps_name, hash={})
|
202
|
+
prepared_statements[ps_name].call(hash)
|
203
|
+
end
|
204
|
+
|
194
205
|
# Connects to the database. This method should be overridden by descendants.
|
195
206
|
def connect
|
196
207
|
raise NotImplementedError, "#connect should be overridden by adapters"
|
@@ -208,20 +219,20 @@ module Sequel
|
|
208
219
|
end
|
209
220
|
|
210
221
|
# Executes the given SQL. This method should be overridden in descendants.
|
211
|
-
def execute(sql)
|
222
|
+
def execute(sql, opts={})
|
212
223
|
raise NotImplementedError, "#execute should be overridden by adapters"
|
213
224
|
end
|
214
225
|
|
215
226
|
# Method that should be used when submitting any DDL (Data Definition
|
216
227
|
# Language) SQL. By default, calls execute_dui.
|
217
|
-
def execute_ddl(sql)
|
218
|
-
execute_dui(sql)
|
228
|
+
def execute_ddl(sql, opts={}, &block)
|
229
|
+
execute_dui(sql, opts, &block)
|
219
230
|
end
|
220
231
|
|
221
232
|
# Method that should be used when issuing a DELETE, UPDATE, or INSERT
|
222
233
|
# statement. By default, calls execute.
|
223
|
-
def execute_dui(sql)
|
224
|
-
execute(sql)
|
234
|
+
def execute_dui(sql, opts={}, &block)
|
235
|
+
execute(sql, opts, &block)
|
225
236
|
end
|
226
237
|
|
227
238
|
# Fetches records for an arbitrary SQL statement. If a block is given,
|
@@ -273,7 +284,8 @@ module Sequel
|
|
273
284
|
|
274
285
|
# Log a message at level info to all loggers. All SQL logging
|
275
286
|
# goes through this method.
|
276
|
-
def log_info(message)
|
287
|
+
def log_info(message, args=nil)
|
288
|
+
message = "#{message}; #{args.inspect}" if args
|
277
289
|
@loggers.each{|logger| logger.info(message)}
|
278
290
|
end
|
279
291
|
|
@@ -319,8 +331,8 @@ module Sequel
|
|
319
331
|
end
|
320
332
|
|
321
333
|
# Acquires a database connection, yielding it to the passed block.
|
322
|
-
def synchronize(&block)
|
323
|
-
@pool.hold(&block)
|
334
|
+
def synchronize(server=nil, &block)
|
335
|
+
@pool.hold(server || :default, &block)
|
324
336
|
end
|
325
337
|
|
326
338
|
# Returns true if a table with the given name exists.
|
@@ -339,8 +351,8 @@ module Sequel
|
|
339
351
|
|
340
352
|
# Attempts to acquire a database connection. Returns true if successful.
|
341
353
|
# Will probably raise an error if unsuccessful.
|
342
|
-
def test_connection
|
343
|
-
synchronize{|conn|}
|
354
|
+
def test_connection(server=nil)
|
355
|
+
synchronize(server){|conn|}
|
344
356
|
true
|
345
357
|
end
|
346
358
|
|
@@ -348,12 +360,9 @@ module Sequel
|
|
348
360
|
# supported - calling #transaction within a transaction will reuse the
|
349
361
|
# current transaction. Should be overridden for databases that support nested
|
350
362
|
# transactions.
|
351
|
-
def transaction
|
352
|
-
|
353
|
-
@transactions
|
354
|
-
if @transactions.include? Thread.current
|
355
|
-
return yield(conn)
|
356
|
-
end
|
363
|
+
def transaction(server=nil)
|
364
|
+
synchronize(server) do |conn|
|
365
|
+
return yield(conn) if @transactions.include?(Thread.current)
|
357
366
|
log_info(SQL_BEGIN)
|
358
367
|
conn.execute(SQL_BEGIN)
|
359
368
|
begin
|
@@ -461,6 +470,26 @@ module Sequel
|
|
461
470
|
alias_method :url, :uri
|
462
471
|
|
463
472
|
private
|
473
|
+
|
474
|
+
# Return the options for the given server by merging the generic
|
475
|
+
# options for all server with the specific options for the given
|
476
|
+
# server specified in the :servers option.
|
477
|
+
def server_opts(server)
|
478
|
+
opts = if @opts[:servers] && server_options = @opts[:servers][server]
|
479
|
+
case server_options
|
480
|
+
when Hash
|
481
|
+
@opts.merge(server_options)
|
482
|
+
when Proc
|
483
|
+
@opts.merge(server_options.call(self))
|
484
|
+
else
|
485
|
+
raise Error, 'Server opts should be a hash or proc'
|
486
|
+
end
|
487
|
+
else
|
488
|
+
@opts.dup
|
489
|
+
end
|
490
|
+
opts.delete(:servers)
|
491
|
+
opts
|
492
|
+
end
|
464
493
|
|
465
494
|
# The default options for the connection pool.
|
466
495
|
def connection_pool_default_options
|
data/lib/sequel_core/dataset.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w'callback convenience pagination query schema sql'.each do |f|
|
1
|
+
%w'callback convenience pagination prepared_statements query schema sql'.each do |f|
|
2
2
|
require "sequel_core/dataset/#{f}"
|
3
3
|
end
|
4
4
|
|
@@ -26,42 +26,6 @@ module Sequel
|
|
26
26
|
# Datasets are Enumerable objects, so they can be manipulated using any
|
27
27
|
# of the Enumerable methods, such as map, inject, etc.
|
28
28
|
#
|
29
|
-
# === The Dataset Adapter Interface
|
30
|
-
#
|
31
|
-
# Each adapter should define its own dataset class as a descendant of
|
32
|
-
# Sequel::Dataset. The following methods should be overridden by the adapter
|
33
|
-
# Dataset class (each method with the stock implementation):
|
34
|
-
#
|
35
|
-
# # Iterate over the results of the SQL query and call the supplied
|
36
|
-
# # block with each record (as a hash).
|
37
|
-
# def fetch_rows(sql, &block)
|
38
|
-
# @db.synchronize do
|
39
|
-
# r = @db.execute(sql)
|
40
|
-
# r.each(&block)
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# # Insert records.
|
45
|
-
# def insert(*values)
|
46
|
-
# @db.synchronize do
|
47
|
-
# @db.execute(insert_sql(*values)).last_insert_id
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# # Update records.
|
52
|
-
# def update(*args)
|
53
|
-
# @db.synchronize do
|
54
|
-
# @db.execute(update_sql(*args)).affected_rows
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# # Delete records.
|
59
|
-
# def delete(opts = nil)
|
60
|
-
# @db.synchronize do
|
61
|
-
# @db.execute(delete_sql(opts)).affected_rows
|
62
|
-
# end
|
63
|
-
# end
|
64
|
-
#
|
65
29
|
# === Methods added via metaprogramming
|
66
30
|
#
|
67
31
|
# Some methods are added via metaprogramming:
|
@@ -221,7 +185,7 @@ module Sequel
|
|
221
185
|
|
222
186
|
# Deletes the records in the dataset. Adapters should override this method.
|
223
187
|
def delete(*args)
|
224
|
-
|
188
|
+
execute_dui(delete_sql(*args))
|
225
189
|
end
|
226
190
|
|
227
191
|
# Iterates over the records in the dataset.
|
@@ -249,7 +213,7 @@ module Sequel
|
|
249
213
|
# Inserts values into the associated table. Adapters should override this
|
250
214
|
# method.
|
251
215
|
def insert(*values)
|
252
|
-
|
216
|
+
execute_dui(insert_sql(*values))
|
253
217
|
end
|
254
218
|
|
255
219
|
# Returns a string representation of the dataset including the class name
|
@@ -282,6 +246,13 @@ module Sequel
|
|
282
246
|
def quote_identifiers?
|
283
247
|
@quote_identifiers
|
284
248
|
end
|
249
|
+
|
250
|
+
# Set the server for this dataset to use. Used to pick a specific database
|
251
|
+
# shard to run a query against, or to override the default SELECT uses
|
252
|
+
# :read_only database and all other queries use the :default database.
|
253
|
+
def server(servr)
|
254
|
+
clone(:server=>servr)
|
255
|
+
end
|
285
256
|
|
286
257
|
# Alias for set, but not aliased directly so subclasses
|
287
258
|
# don't have to override both methods.
|
@@ -430,7 +401,7 @@ module Sequel
|
|
430
401
|
|
431
402
|
# Updates values for the dataset. Adapters should override this method.
|
432
403
|
def update(*args)
|
433
|
-
|
404
|
+
execute_dui(update_sql(*args))
|
434
405
|
end
|
435
406
|
|
436
407
|
# Add the mutation methods via metaprogramming
|
@@ -444,6 +415,16 @@ module Sequel
|
|
444
415
|
end
|
445
416
|
|
446
417
|
private
|
418
|
+
|
419
|
+
# Execute the given SQL on the database using execute.
|
420
|
+
def execute(sql, opts={}, &block)
|
421
|
+
@db.execute(sql, {:server=>@opts[:server] || :read_only}.merge(opts), &block)
|
422
|
+
end
|
423
|
+
|
424
|
+
# Execute the given SQL on the database using execute_dui.
|
425
|
+
def execute_dui(sql, opts={}, &block)
|
426
|
+
@db.execute_dui(sql, {:server=>@opts[:server] || :default}.merge(opts), &block)
|
427
|
+
end
|
447
428
|
|
448
429
|
# Modify the receiver with the results of sending the meth, args, and block
|
449
430
|
# to the receiver and merging the options of the resulting dataset into
|
@@ -136,7 +136,7 @@ module Sequel
|
|
136
136
|
table = @opts[:from].first
|
137
137
|
columns, dataset = *args
|
138
138
|
sql = "INSERT INTO #{quote_identifier(table)} #{literal(columns)} VALUES #{literal(dataset)}"
|
139
|
-
return @db.transaction
|
139
|
+
return @db.transaction{execute_dui(sql)}
|
140
140
|
else
|
141
141
|
# we assume that an array of hashes is given
|
142
142
|
hashes, opts = *args
|
@@ -153,11 +153,11 @@ module Sequel
|
|
153
153
|
if slice_size
|
154
154
|
values.each_slice(slice_size) do |slice|
|
155
155
|
statements = multi_insert_sql(columns, slice)
|
156
|
-
@db.transaction
|
156
|
+
@db.transaction{statements.each{|st| execute_dui(st)}}
|
157
157
|
end
|
158
158
|
else
|
159
159
|
statements = multi_insert_sql(columns, values)
|
160
|
-
@db.transaction
|
160
|
+
@db.transaction{statements.each{|st| execute_dui(st)}}
|
161
161
|
end
|
162
162
|
end
|
163
163
|
alias_method :import, :multi_insert
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module Sequel
|
2
|
+
class Dataset
|
3
|
+
PREPARED_ARG_PLACEHOLDER = '?'.lit.freeze
|
4
|
+
|
5
|
+
# Default implementation of the argument mapper to allow
|
6
|
+
# native database support for bind variables and prepared
|
7
|
+
# statements (as opposed to the emulated ones used by default).
|
8
|
+
module ArgumentMapper
|
9
|
+
SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
|
10
|
+
SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
|
11
|
+
|
12
|
+
# The name of the prepared statement, if any.
|
13
|
+
attr_accessor :prepared_statement_name
|
14
|
+
|
15
|
+
# The bind arguments to use for running this prepared statement
|
16
|
+
attr_accessor :bind_arguments
|
17
|
+
|
18
|
+
# Set the bind arguments based on the hash and call super.
|
19
|
+
def call(hash, &block)
|
20
|
+
ds = clone
|
21
|
+
ds.prepared_sql
|
22
|
+
ds.bind_arguments = ds.map_to_prepared_args(hash)
|
23
|
+
ds.prepared_args = hash
|
24
|
+
ds.run(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Override the given *_sql method based on the type, and
|
28
|
+
# cache the result of the sql. This requires that the object
|
29
|
+
# that includes this module implement prepared_args_hash.
|
30
|
+
def prepared_sql
|
31
|
+
return @prepared_sql if @prepared_sql
|
32
|
+
@prepared_args = prepared_args_hash
|
33
|
+
@prepared_sql = super
|
34
|
+
meta_def("#{sql_query_type}_sql"){|*args| prepared_sql}
|
35
|
+
@prepared_sql
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def sql_query_type
|
41
|
+
SQL_QUERY_TYPE[@prepared_type]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Backbone of the prepared statement support. Grafts bind variable
|
46
|
+
# support into datasets by hijacking #literal and using placeholders.
|
47
|
+
# By default, emulates prepared statements and bind variables by
|
48
|
+
# taking the hash of bind variables and directly substituting them
|
49
|
+
# into the query, which works on all databases, as it is no different
|
50
|
+
# from using the dataset without bind variables.
|
51
|
+
module PreparedStatementMethods
|
52
|
+
PLACEHOLDER_RE = /\A\$(.*)\z/
|
53
|
+
|
54
|
+
# The type of prepared statement, should be one of :select,
|
55
|
+
# :insert, :update, or :delete
|
56
|
+
attr_accessor :prepared_type
|
57
|
+
|
58
|
+
# The bind variable hash to use when substituting
|
59
|
+
attr_accessor :prepared_args
|
60
|
+
|
61
|
+
# The argument to supply to insert and update, which may use
|
62
|
+
# placeholders specified by prepared_args
|
63
|
+
attr_accessor :prepared_modify_values
|
64
|
+
|
65
|
+
# Sets the prepared_args to the given hash and runs the
|
66
|
+
# prepared statement.
|
67
|
+
def call(hash, &block)
|
68
|
+
ds = clone
|
69
|
+
ds.prepared_args = hash
|
70
|
+
ds.run(&block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the SQL for the prepared statement, depending on
|
74
|
+
# the type of the statement and the prepared_modify_values.
|
75
|
+
def prepared_sql
|
76
|
+
case @prepared_type
|
77
|
+
when :select, :all
|
78
|
+
select_sql
|
79
|
+
when :first
|
80
|
+
select_sql(:limit=>1)
|
81
|
+
when :insert
|
82
|
+
insert_sql(@prepared_modify_values)
|
83
|
+
when :update
|
84
|
+
update_sql(@prepared_modify_values)
|
85
|
+
when :delete
|
86
|
+
delete_sql
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Changes the values of symbols if they start with $ and
|
91
|
+
# prepared_args is present. If so, they are considered placeholders,
|
92
|
+
# and they are substituted using prepared_arg.
|
93
|
+
def literal(v)
|
94
|
+
case v
|
95
|
+
when Symbol
|
96
|
+
if match = PLACEHOLDER_RE.match(v.to_s) and @prepared_args
|
97
|
+
super(prepared_arg(match[1].to_sym))
|
98
|
+
else
|
99
|
+
super
|
100
|
+
end
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Programmer friendly string showing this is a prepared statement,
|
107
|
+
# with the prepared SQL it represents (which in general won't have
|
108
|
+
# substituted variables).
|
109
|
+
def inspect
|
110
|
+
"<#{self.class.name}/PreparedStatement #{prepared_sql.inspect}>"
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
# Run the method based on the type of prepared statement, with
|
116
|
+
# :select running #all to get all of the rows, and the other
|
117
|
+
# types running the method with the same name as the type.
|
118
|
+
def run(&block)
|
119
|
+
case @prepared_type
|
120
|
+
when :select, :all
|
121
|
+
all(&block)
|
122
|
+
when :first
|
123
|
+
first
|
124
|
+
when :insert
|
125
|
+
insert(@prepared_modify_values)
|
126
|
+
when :update
|
127
|
+
update(@prepared_modify_values)
|
128
|
+
when :delete
|
129
|
+
delete
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Returns the value of the prepared_args hash for the given key.
|
136
|
+
def prepared_arg(k)
|
137
|
+
@prepared_args[k]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Default implementation for an argument mapper that uses
|
142
|
+
# unnumbered SQL placeholder arguments. Keeps track of which
|
143
|
+
# arguments have been used, and allows arguments to
|
144
|
+
# be used more than once.
|
145
|
+
module UnnumberedArgumentMapper
|
146
|
+
include ArgumentMapper
|
147
|
+
|
148
|
+
protected
|
149
|
+
|
150
|
+
# Returns a single output array mapping the values of the input hash.
|
151
|
+
# Keys in the input hash that are used more than once in the query
|
152
|
+
# have multiple entries in the output array.
|
153
|
+
def map_to_prepared_args(hash)
|
154
|
+
array = []
|
155
|
+
@prepared_args.each{|k,vs| vs.each{|v| array[v] = hash[k]}}
|
156
|
+
array
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# Uses a separate array of each key, holding the positions
|
162
|
+
# in the output array (necessary to support arguments
|
163
|
+
# that are used more than once).
|
164
|
+
def prepared_args_hash
|
165
|
+
Hash.new{|h,k| h[k] = Array.new}
|
166
|
+
end
|
167
|
+
|
168
|
+
# Associates the argument with name k with the next position in
|
169
|
+
# the output array.
|
170
|
+
def prepared_arg(k)
|
171
|
+
@max_prepared_arg ||= 0
|
172
|
+
@prepared_args[k] << @max_prepared_arg
|
173
|
+
@max_prepared_arg += 1
|
174
|
+
prepared_arg_placeholder
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# For the given type (:select, :insert, :update, or :delete),
|
179
|
+
# run the sql with the bind variables
|
180
|
+
# specified in the hash. values is a hash of passed to
|
181
|
+
# insert or update (if one of those types is used),
|
182
|
+
# which may contain placeholders.
|
183
|
+
def call(type, bind_variables={}, values=nil)
|
184
|
+
to_prepared_statement(type, values).call(bind_variables)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Prepare an SQL statement for later execution. This returns
|
188
|
+
# a clone of the dataset extended with PreparedStatementMethods,
|
189
|
+
# on which you can call call with the hash of bind variables to
|
190
|
+
# do substitution. The prepared statement is also stored in
|
191
|
+
# the associated database. The following usage is identical:
|
192
|
+
#
|
193
|
+
# ps = prepare(:select, :select_by_name)
|
194
|
+
# ps.call(:name=>'Blah')
|
195
|
+
# db.call(:select_by_name, :name=>'Blah')
|
196
|
+
def prepare(type, name, values=nil)
|
197
|
+
db.prepared_statements[name] = to_prepared_statement(type, values)
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
# The argument placeholder. Most databases used unnumbered
|
203
|
+
# arguments with question marks, so that is the default.
|
204
|
+
def prepared_arg_placeholder
|
205
|
+
PREPARED_ARG_PLACEHOLDER
|
206
|
+
end
|
207
|
+
|
208
|
+
# Return a cloned copy of the current dataset extended with
|
209
|
+
# PreparedStatementMethods, setting the type and modify values.
|
210
|
+
def to_prepared_statement(type, values=nil)
|
211
|
+
ps = clone
|
212
|
+
ps.extend(PreparedStatementMethods)
|
213
|
+
ps.prepared_type = type
|
214
|
+
ps.prepared_modify_values = values
|
215
|
+
ps
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|