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