sequel 2.3.0 → 2.4.0

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