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.
Files changed (43) hide show
  1. data/CHANGELOG +16 -0
  2. data/README +4 -1
  3. data/Rakefile +17 -19
  4. data/doc/prepared_statements.rdoc +104 -0
  5. data/doc/sharding.rdoc +113 -0
  6. data/lib/sequel_core/adapters/ado.rb +24 -17
  7. data/lib/sequel_core/adapters/db2.rb +30 -33
  8. data/lib/sequel_core/adapters/dbi.rb +15 -13
  9. data/lib/sequel_core/adapters/informix.rb +13 -14
  10. data/lib/sequel_core/adapters/jdbc.rb +243 -60
  11. data/lib/sequel_core/adapters/jdbc/mysql.rb +32 -24
  12. data/lib/sequel_core/adapters/jdbc/postgresql.rb +32 -2
  13. data/lib/sequel_core/adapters/jdbc/sqlite.rb +16 -20
  14. data/lib/sequel_core/adapters/mysql.rb +164 -76
  15. data/lib/sequel_core/adapters/odbc.rb +21 -34
  16. data/lib/sequel_core/adapters/openbase.rb +10 -7
  17. data/lib/sequel_core/adapters/oracle.rb +17 -23
  18. data/lib/sequel_core/adapters/postgres.rb +246 -35
  19. data/lib/sequel_core/adapters/shared/mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/shared/mysql.rb +34 -26
  21. data/lib/sequel_core/adapters/shared/postgres.rb +82 -38
  22. data/lib/sequel_core/adapters/shared/sqlite.rb +48 -16
  23. data/lib/sequel_core/adapters/sqlite.rb +141 -44
  24. data/lib/sequel_core/connection_pool.rb +85 -63
  25. data/lib/sequel_core/database.rb +46 -17
  26. data/lib/sequel_core/dataset.rb +21 -40
  27. data/lib/sequel_core/dataset/convenience.rb +3 -3
  28. data/lib/sequel_core/dataset/prepared_statements.rb +218 -0
  29. data/lib/sequel_core/exceptions.rb +0 -12
  30. data/lib/sequel_model/base.rb +1 -2
  31. data/lib/sequel_model/plugins.rb +1 -1
  32. data/spec/adapters/ado_spec.rb +32 -3
  33. data/spec/adapters/mysql_spec.rb +7 -8
  34. data/spec/integration/prepared_statement_test.rb +106 -0
  35. data/spec/sequel_core/connection_pool_spec.rb +105 -3
  36. data/spec/sequel_core/database_spec.rb +41 -3
  37. data/spec/sequel_core/dataset_spec.rb +117 -7
  38. data/spec/sequel_core/spec_helper.rb +2 -2
  39. data/spec/sequel_model/model_spec.rb +0 -6
  40. data/spec/sequel_model/spec_helper.rb +1 -1
  41. metadata +11 -6
  42. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -54
  43. data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
@@ -4,21 +4,21 @@ module Sequel
4
4
  module ODBC
5
5
  class Database < Sequel::Database
6
6
  set_adapter_scheme :odbc
7
-
8
- # def connect
9
- # conn = ::ODBC::connect(@opts[:database], @opts[:user], @opts[:password])
10
- # conn.autocommit = true
11
- # conn
12
- # end
13
7
 
14
8
  GUARDED_DRV_NAME = /^\{.+\}$/.freeze
15
9
  DRV_NAME_GUARDS = '{%s}'.freeze
16
10
 
17
- def connect
18
- if @opts.include? :driver
11
+ def connect(server)
12
+ opts = server_opts(server)
13
+ case opts[:db_type]
14
+ when 'mssql'
15
+ require 'sequel_core/adapters/shared/mssql'
16
+ extend Sequel::MSSQL::DatabaseMethods
17
+ end
18
+ if opts.include? :driver
19
19
  drv = ::ODBC::Driver.new
20
20
  drv.name = 'Sequel ODBC Driver130'
21
- @opts.each do |param, value|
21
+ opts.each do |param, value|
22
22
  if :driver == param and not (value =~ GUARDED_DRV_NAME)
23
23
  value = DRV_NAME_GUARDS % value
24
24
  end
@@ -27,7 +27,7 @@ module Sequel
27
27
  db = ::ODBC::Database.new
28
28
  conn = db.drvconnect(drv)
29
29
  else
30
- conn = ::ODBC::connect(@opts[:database], @opts[:user], @opts[:password])
30
+ conn = ::ODBC::connect(opts[:database], opts[:user], opts[:password])
31
31
  end
32
32
  conn.autocommit = true
33
33
  conn
@@ -45,20 +45,20 @@ module Sequel
45
45
  # you call execute manually, or you will get warnings. See the
46
46
  # fetch_rows method source code for an example of how to drop
47
47
  # the statements.
48
- def execute(sql)
48
+ def execute(sql, opts={})
49
49
  log_info(sql)
50
- @pool.hold do |conn|
51
- conn.run(sql)
50
+ synchronize(opts[:server]) do |conn|
51
+ r = conn.run(sql)
52
+ yield(r) if block_given?
53
+ r
52
54
  end
53
55
  end
54
56
 
55
- def do(sql)
57
+ def execute_dui(sql, opts={})
56
58
  log_info(sql)
57
- @pool.hold do |conn|
58
- conn.do(sql)
59
- end
59
+ synchronize(opts[:server]){|conn| conn.do(sql)}
60
60
  end
61
- alias_method :execute_dui, :do
61
+ alias_method :do, :execute_dui
62
62
  end
63
63
 
64
64
  class Dataset < Sequel::Dataset
@@ -90,8 +90,7 @@ module Sequel
90
90
  UNTITLED_COLUMN = 'untitled_%d'.freeze
91
91
 
92
92
  def fetch_rows(sql, &block)
93
- @db.synchronize do
94
- s = @db.execute sql
93
+ execute(sql) do |s|
95
94
  begin
96
95
  untitled_count = 0
97
96
  @columns = s.columns(true).map do |c|
@@ -108,20 +107,8 @@ module Sequel
108
107
  end
109
108
  self
110
109
  end
111
-
112
- # def fetch_rows(sql, &block)
113
- # @db.synchronize do
114
- # s = @db.execute sql
115
- # begin
116
- # @columns = s.columns(true).map {|c| c.name.to_sym}
117
- # rows = s.fetch_all
118
- # rows.each {|row| yield hash_row(row)}
119
- # ensure
120
- # s.drop unless s.nil? rescue nil
121
- # end
122
- # end
123
- # self
124
- # end
110
+
111
+ private
125
112
 
126
113
  def hash_row(row)
127
114
  hash = {}
@@ -5,7 +5,8 @@ module Sequel
5
5
  class Database < Sequel::Database
6
6
  set_adapter_scheme :openbase
7
7
 
8
- def connect
8
+ def connect(server)
9
+ opts = server_opts(server)
9
10
  OpenBase.new(
10
11
  opts[:database],
11
12
  opts[:host] || 'localhost',
@@ -23,11 +24,14 @@ module Sequel
23
24
  OpenBase::Dataset.new(self, opts)
24
25
  end
25
26
 
26
- def execute(sql)
27
+ def execute(sql, opts={})
27
28
  log_info(sql)
28
- @pool.hold {|conn| conn.execute(sql)}
29
+ synchronize(opts[:server]) do |conn|
30
+ r = conn.execute(sql)
31
+ yield(r) if block_given?
32
+ r
33
+ end
29
34
  end
30
-
31
35
  alias_method :do, :execute
32
36
  end
33
37
 
@@ -43,9 +47,8 @@ module Sequel
43
47
  end
44
48
  end
45
49
 
46
- def fetch_rows(sql, &block)
47
- @db.synchronize do
48
- result = @db.execute sql
50
+ def fetch_rows(sql)
51
+ execute(sql) do |result|
49
52
  begin
50
53
  @columns = result.column_infos.map {|c| c.name.to_sym}
51
54
  result.each do |r|
@@ -5,20 +5,15 @@ module Sequel
5
5
  class Database < Sequel::Database
6
6
  set_adapter_scheme :oracle
7
7
 
8
- # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
9
- #
10
- # def auto_increment_sql
11
- # AUTO_INCREMENT
12
- # end
13
-
14
- def connect
15
- if @opts[:database]
16
- dbname = @opts[:host] ? \
17
- "//#{@opts[:host]}#{":#{@opts[:port]}" if @opts[:port]}/#{@opts[:database]}" : @opts[:database]
8
+ def connect(server)
9
+ opts = server_opts(server)
10
+ if opts[:database]
11
+ dbname = opts[:host] ? \
12
+ "//#{opts[:host]}#{":#{opts[:port]}" if opts[:port]}/#{opts[:database]}" : opts[:database]
18
13
  else
19
- dbname = @opts[:host]
14
+ dbname = opts[:host]
20
15
  end
21
- conn = OCI8.new(@opts[:user], @opts[:password], dbname, @opts[:privilege])
16
+ conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
22
17
  conn.autocommit = true
23
18
  conn.non_blocking = true
24
19
  conn
@@ -32,11 +27,14 @@ module Sequel
32
27
  Oracle::Dataset.new(self, opts)
33
28
  end
34
29
 
35
- def execute(sql)
30
+ def execute(sql, opts={})
36
31
  log_info(sql)
37
- @pool.hold {|conn| conn.exec(sql)}
32
+ synchronize(opts[:server]) do |conn|
33
+ r = conn.exec(sql)
34
+ yield(r) if block_given?
35
+ r
36
+ end
38
37
  end
39
-
40
38
  alias_method :do, :execute
41
39
 
42
40
  def tables
@@ -49,12 +47,9 @@ module Sequel
49
47
  from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
50
48
  end
51
49
 
52
- def transaction
53
- @pool.hold do |conn|
54
- @transactions ||= []
55
- if @transactions.include? Thread.current
56
- return yield(conn)
57
- end
50
+ def transaction(server=nil)
51
+ synchronize(server) do |conn|
52
+ return yield(conn) if @transactions.include?(Thread.current)
58
53
 
59
54
  conn.autocommit = false
60
55
  begin
@@ -83,8 +78,7 @@ module Sequel
83
78
  end
84
79
 
85
80
  def fetch_rows(sql, &block)
86
- @db.synchronize do
87
- cursor = @db.execute sql
81
+ execute(sql) do |cursor|
88
82
  begin
89
83
  @columns = cursor.get_col_names.map {|c| c.downcase.to_sym}
90
84
  while r = cursor.fetch
@@ -2,16 +2,23 @@ require 'sequel_core/adapters/shared/postgres'
2
2
 
3
3
  begin
4
4
  require 'pg'
5
+ SEQUEL_POSTGRES_USES_PG = true
5
6
  rescue LoadError => e
6
- begin
7
- require 'postgres'
7
+ SEQUEL_POSTGRES_USES_PG = false
8
+ begin
9
+ require 'postgres'
10
+ # Attempt to get uniform behavior for the PGconn object no matter
11
+ # if pg, postgres, or postgres-pr is used.
8
12
  class PGconn
9
13
  unless method_defined?(:escape_string)
10
14
  if self.respond_to?(:escape)
15
+ # If there is no escape_string instead method, but there is an
16
+ # escape class method, use that instead.
11
17
  def escape_string(str)
12
18
  self.class.escape(str)
13
19
  end
14
20
  else
21
+ # Raise an error if no valid string escaping method can be found.
15
22
  def escape_string(obj)
16
23
  raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
17
24
  end
@@ -19,6 +26,8 @@ rescue LoadError => e
19
26
  end
20
27
  unless method_defined?(:escape_bytea)
21
28
  if self.respond_to?(:escape_bytea)
29
+ # If there is no escape_bytea instance method, but there is an
30
+ # escape_bytea class method, use that instead.
22
31
  def escape_bytea(obj)
23
32
  self.class.escape_bytea(obj)
24
33
  end
@@ -27,14 +36,20 @@ rescue LoadError => e
27
36
  require 'postgres-pr/typeconv/conv'
28
37
  require 'postgres-pr/typeconv/bytea'
29
38
  extend Postgres::Conversion
39
+ # If we are using postgres-pr, use the encode_bytea method from
40
+ # that.
30
41
  def escape_bytea(obj)
31
42
  self.class.encode_bytea(obj)
32
43
  end
33
44
  metaalias :unescape_bytea, :decode_bytea
34
45
  rescue
46
+ # If no valid bytea escaping method can be found, create one that
47
+ # raises an error
35
48
  def escape_bytea(obj)
36
49
  raise Sequel::Error, "bytea escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
37
50
  end
51
+ # If no valid bytea unescaping method can be found, create one that
52
+ # raises an error
38
53
  def self.unescape_bytea(obj)
39
54
  raise Sequel::Error, "bytea unescaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
40
55
  end
@@ -56,8 +71,12 @@ rescue LoadError => e
56
71
  end
57
72
 
58
73
  module Sequel
74
+ # Top level module for holding all PostgreSQL-related modules and classes
75
+ # for Sequel.
59
76
  module Postgres
60
77
  CONVERTED_EXCEPTIONS << PGError
78
+
79
+ # Hash with integer keys and proc values for converting PostgreSQL types.
61
80
  PG_TYPES = {
62
81
  16 => lambda{ |s| Postgres.string_to_bool(s) }, # boolean
63
82
  17 => lambda{ |s| Adapter.unescape_bytea(s).to_blob }, # bytea
@@ -78,6 +97,7 @@ module Sequel
78
97
  1700 => lambda{ |s| s.to_d }, # numeric
79
98
  }
80
99
 
100
+ # Module method for converting a PostgreSQL string to a boolean value.
81
101
  def self.string_to_bool(s)
82
102
  if(s.blank?)
83
103
  nil
@@ -87,34 +107,43 @@ module Sequel
87
107
  false
88
108
  end
89
109
  end
90
-
110
+
111
+ # PGconn subclass for connection specific methods used with the
112
+ # pg, postgres, or postgres-pr driver.
91
113
  class Adapter < ::PGconn
92
114
  include Sequel::Postgres::AdapterMethods
93
115
  self.translate_results = false if respond_to?(:translate_results=)
94
116
 
95
- def connected?
96
- status == Adapter::CONNECTION_OK
97
- end
98
-
99
- def execute(sql, &block)
117
+ # Execute the given SQL with this connection. If a block is given,
118
+ # yield the results, otherwise, return the number of changed rows.
119
+ def execute(sql, args=nil)
100
120
  q = nil
101
121
  begin
102
- q = exec(sql)
122
+ q = args ? exec(sql, args) : exec(sql)
103
123
  rescue PGError => e
104
- unless connected?
105
- reset
106
- q = exec(sql)
107
- else
108
- raise e
109
- end
124
+ raise if status == Adapter::CONNECTION_OK
125
+ reset
126
+ q = args ? exec(sql, args) : exec(sql)
110
127
  end
111
128
  begin
112
- block ? block[q] : q.cmd_tuples
129
+ block_given? ? yield(q) : q.cmd_tuples
113
130
  ensure
114
131
  q.clear
115
132
  end
116
133
  end
117
134
 
135
+ if SEQUEL_POSTGRES_USES_PG
136
+ # Hash of prepared statements for this connection. Keys are
137
+ # string names of the server side prepared statement, and values
138
+ # are SQL strings.
139
+ def prepared_statements
140
+ @prepared_statements ||= {}
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ # Return the requested values for the given row.
118
147
  def result_set_values(r, *vals)
119
148
  return if r.nil? || (r.ntuples == 0)
120
149
  case vals.length
@@ -125,39 +154,65 @@ module Sequel
125
154
  end
126
155
  end
127
156
  end
128
-
157
+
158
+ # Database class for PostgreSQL databases used with Sequel and the
159
+ # pg, postgres, or postgres-pr driver.
129
160
  class Database < Sequel::Database
130
161
  include Sequel::Postgres::DatabaseMethods
131
162
 
132
163
  set_adapter_scheme :postgres
133
-
134
- def connect
164
+
165
+ # Connects to the database. In addition to the standard database
166
+ # options, using the :encoding or :charset option changes the
167
+ # client encoding for the connection.
168
+ def connect(server)
169
+ opts = server_opts(server)
135
170
  conn = Adapter.connect(
136
- @opts[:host] || 'localhost',
137
- @opts[:port] || 5432,
171
+ opts[:host] || 'localhost',
172
+ opts[:port] || 5432,
138
173
  '', '',
139
- @opts[:database],
140
- @opts[:user],
141
- @opts[:password]
174
+ opts[:database],
175
+ opts[:user],
176
+ opts[:password]
142
177
  )
143
- if encoding = @opts[:encoding] || @opts[:charset]
178
+ if encoding = opts[:encoding] || opts[:charset]
144
179
  conn.set_client_encoding(encoding)
145
180
  end
146
181
  conn
147
182
  end
148
183
 
184
+ # Return instance of Sequel::Postgres::Dataset with the given options.
149
185
  def dataset(opts = nil)
150
186
  Postgres::Dataset.new(self, opts)
151
187
  end
152
188
 
189
+ # Disconnect all active connections.
153
190
  def disconnect
154
191
  @pool.disconnect {|c| c.finish}
155
192
  end
156
-
157
- def execute(sql, &block)
193
+
194
+ # Execute the given SQL with the given args on an available connection.
195
+ def execute(sql, opts={}, &block)
196
+ return execute_prepared_statement(sql, opts, &block) if Symbol === sql
158
197
  begin
159
- log_info(sql)
160
- @pool.hold {|conn| conn.execute(sql, &block)}
198
+ log_info(sql, opts[:arguments])
199
+ synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
200
+ rescue => e
201
+ log_info(e.message)
202
+ raise convert_pgerror(e)
203
+ end
204
+ end
205
+
206
+ # Insert the values into the table and return the primary key (if
207
+ # automatically generated).
208
+ def execute_insert(sql, opts={})
209
+ return execute(sql, opts) if Symbol === sql
210
+ begin
211
+ log_info(sql, opts[:arguments])
212
+ synchronize(opts[:server]) do |conn|
213
+ conn.execute(sql, opts[:arguments])
214
+ insert_result(conn, opts[:table], opts[:values])
215
+ end
161
216
  rescue => e
162
217
  log_info(e.message)
163
218
  raise convert_pgerror(e)
@@ -166,18 +221,60 @@ module Sequel
166
221
 
167
222
  private
168
223
 
169
- # PostgreSQL doesn't need the pool to convert exceptions, either.
224
+ # PostgreSQL doesn't need the connection pool to convert exceptions.
170
225
  def connection_pool_default_options
171
226
  super.merge(:pool_convert_exceptions=>false)
172
227
  end
228
+
229
+ # Execute the prepared statement with the given name on an available
230
+ # connection, using the given args. If the connection has not prepared
231
+ # a statement with the given name yet, prepare it. If the connection
232
+ # has prepared a statement with the same name and different SQL,
233
+ # deallocate that statement first and then prepare this statement.
234
+ # If a block is given, yield the result, otherwise, return the number
235
+ # of rows changed. If the :insert option is passed, return the value
236
+ # of the primary key for the last inserted row.
237
+ def execute_prepared_statement(name, opts={})
238
+ ps = prepared_statements[name]
239
+ sql = ps.prepared_sql
240
+ ps_name = name.to_s
241
+ args = opts[:arguments]
242
+ synchronize(opts[:server]) do |conn|
243
+ unless conn.prepared_statements[ps_name] == sql
244
+ if conn.prepared_statements.include?(ps_name)
245
+ s = "DEALLOCATE #{ps_name}"
246
+ log_info(s)
247
+ conn.execute(s) unless conn.prepared_statements[ps_name] == sql
248
+ end
249
+ conn.prepared_statements[ps_name] = sql
250
+ log_info("PREPARE #{ps_name} AS #{sql}")
251
+ conn.prepare(ps_name, sql)
252
+ end
253
+ log_info("EXECUTE #{ps_name}", args)
254
+ q = conn.exec_prepared(ps_name, args)
255
+ if opts[:table] && opts[:values]
256
+ insert_result(conn, opts[:table], opts[:values])
257
+ else
258
+ begin
259
+ block_given? ? yield(q) : q.cmd_tuples
260
+ ensure
261
+ q.clear
262
+ end
263
+ end
264
+ end
265
+ end
173
266
  end
174
-
267
+
268
+ # Dataset class for PostgreSQL datasets that use the pg, postgres, or
269
+ # postgres-pr driver.
175
270
  class Dataset < Sequel::Dataset
176
271
  include Sequel::Postgres::DatasetMethods
177
-
178
- def fetch_rows(sql, &block)
272
+
273
+ # yield all rows returned by executing the given SQL and converting
274
+ # the types.
275
+ def fetch_rows(sql)
179
276
  @columns = []
180
- @db.execute(sql) do |res|
277
+ execute(sql) do |res|
181
278
  (0...res.ntuples).each do |recnum|
182
279
  converted_rec = {}
183
280
  (0...res.nfields).each do |fieldnum|
@@ -193,7 +290,121 @@ module Sequel
193
290
  end
194
291
  end
195
292
  end
293
+
294
+ if SEQUEL_POSTGRES_USES_PG
295
+
296
+ PREPARED_ARG_PLACEHOLDER = '$'.lit.freeze
297
+
298
+ # PostgreSQL specific argument mapper used for mapping the named
299
+ # argument hash to a array with numbered arguments. Only used with
300
+ # the pg driver.
301
+ module ArgumentMapper
302
+ include Sequel::Dataset::ArgumentMapper
303
+
304
+ protected
305
+
306
+ # Return an array of strings for each of the hash values, inserting
307
+ # them to the correct position in the array.
308
+ def map_to_prepared_args(hash)
309
+ array = []
310
+ @prepared_args.each{|k,v| array[v] = hash[k].to_s}
311
+ array
312
+ end
313
+
314
+ private
315
+
316
+ # PostgreSQL most of the time requires type information for each of
317
+ # arguments to a prepared statement. Handle this by allowing the
318
+ # named argument to have a __* suffix, with the * being the type.
319
+ # In the generated SQL, cast the bound argument to that type to
320
+ # elminate ambiguity (and PostgreSQL from raising an exception).
321
+ def prepared_arg(k)
322
+ y, type = k.to_s.split("__")
323
+ "#{prepared_arg_placeholder}#{@prepared_args[y.to_sym]}#{"::#{type}" if type}".lit
324
+ end
325
+
326
+ # If the named argument has already been used, return the position in
327
+ # the output array that it is mapped to. Otherwise, map it to the
328
+ # next position in the array.
329
+ def prepared_args_hash
330
+ max_prepared_arg = 0
331
+ Hash.new do |h,k|
332
+ h[k] = max_prepared_arg
333
+ max_prepared_arg += 1
334
+ end
335
+ end
336
+ end
337
+
338
+ # Allow use of bind arguments for PostgreSQL using the pg driver.
339
+ module BindArgumentMethods
340
+ include ArgumentMapper
341
+
342
+ private
343
+
344
+ # Execute the given SQL with the stored bind arguments.
345
+ def execute(sql, opts={}, &block)
346
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
347
+ end
348
+
349
+ # Same as execute, explicit due to intricacies of alias and super.
350
+ def execute_dui(sql, opts={}, &block)
351
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
352
+ end
353
+
354
+ # Same as execute, explicit due to intricacies of alias and super.
355
+ def execute_insert(sql, opts={}, &block)
356
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
357
+ end
358
+ end
359
+
360
+ # Allow use of server side prepared statements for PostgreSQL using the
361
+ # pg driver.
362
+ module PreparedStatementMethods
363
+ include BindArgumentMethods
364
+
365
+ private
366
+
367
+ # Execute the stored prepared statement name and the stored bind
368
+ # arguments instead of the SQL given.
369
+ def execute(sql, opts={}, &block)
370
+ super(prepared_statement_name, opts, &block)
371
+ end
372
+
373
+ # Same as execute, explicit due to intricacies of alias and super.
374
+ def execute_dui(sql, opts={}, &block)
375
+ super(prepared_statement_name, opts, &block)
376
+ end
377
+
378
+ # Same as execute, explicit due to intricacies of alias and super.
379
+ def execute_insert(sql, opts={}, &block)
380
+ super(prepared_statement_name, opts, &block)
381
+ end
382
+ end
383
+
384
+ # Execute the given type of statement with the hash of values.
385
+ def call(type, hash, values=nil, &block)
386
+ ps = to_prepared_statement(type, values)
387
+ ps.extend(BindArgumentMethods)
388
+ ps.call(hash, &block)
389
+ end
390
+
391
+ # Prepare the given type of statement with the given name, and store
392
+ # it in the database to be called later.
393
+ def prepare(type, name, values=nil)
394
+ ps = to_prepared_statement(type, values)
395
+ ps.extend(PreparedStatementMethods)
396
+ ps.prepared_statement_name = name
397
+ db.prepared_statements[name] = ps
398
+ end
399
+
400
+ private
401
+
402
+ # PostgreSQL uses $N for placeholders instead of ?, so use a $
403
+ # as the placeholder.
404
+ def prepared_arg_placeholder
405
+ PREPARED_ARG_PLACEHOLDER
406
+ end
407
+ end
196
408
  end
197
409
  end
198
410
  end
199
-