ydbi 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/ChangeLog +3699 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/Rakefile +8 -0
  7. data/TODO +44 -0
  8. data/bench/bench.rb +79 -0
  9. data/bin/dbi +518 -0
  10. data/bin/test_broken_dbi +37 -0
  11. data/build/Rakefile.dbi.rb +60 -0
  12. data/build/rake_task_lib.rb +187 -0
  13. data/doc/DBD_SPEC.rdoc +88 -0
  14. data/doc/DBI_SPEC.rdoc +157 -0
  15. data/doc/homepage/contact.html +62 -0
  16. data/doc/homepage/development.html +124 -0
  17. data/doc/homepage/index.html +83 -0
  18. data/doc/homepage/ruby-dbi.css +91 -0
  19. data/examples/test1.pl +39 -0
  20. data/examples/test1.rb +20 -0
  21. data/examples/xmltest.rb +8 -0
  22. data/lib/dbd/Mysql.rb +137 -0
  23. data/lib/dbd/ODBC.rb +89 -0
  24. data/lib/dbd/Pg.rb +188 -0
  25. data/lib/dbd/SQLite.rb +97 -0
  26. data/lib/dbd/SQLite3.rb +124 -0
  27. data/lib/dbd/mysql/database.rb +405 -0
  28. data/lib/dbd/mysql/driver.rb +125 -0
  29. data/lib/dbd/mysql/statement.rb +188 -0
  30. data/lib/dbd/odbc/database.rb +128 -0
  31. data/lib/dbd/odbc/driver.rb +38 -0
  32. data/lib/dbd/odbc/statement.rb +137 -0
  33. data/lib/dbd/pg/database.rb +516 -0
  34. data/lib/dbd/pg/exec.rb +47 -0
  35. data/lib/dbd/pg/statement.rb +160 -0
  36. data/lib/dbd/pg/tuples.rb +121 -0
  37. data/lib/dbd/pg/type.rb +209 -0
  38. data/lib/dbd/sqlite/database.rb +151 -0
  39. data/lib/dbd/sqlite/statement.rb +125 -0
  40. data/lib/dbd/sqlite3/database.rb +201 -0
  41. data/lib/dbd/sqlite3/statement.rb +78 -0
  42. data/lib/dbi.rb +336 -0
  43. data/lib/dbi/base_classes.rb +12 -0
  44. data/lib/dbi/base_classes/database.rb +135 -0
  45. data/lib/dbi/base_classes/driver.rb +47 -0
  46. data/lib/dbi/base_classes/statement.rb +171 -0
  47. data/lib/dbi/binary.rb +25 -0
  48. data/lib/dbi/columninfo.rb +107 -0
  49. data/lib/dbi/exceptions.rb +65 -0
  50. data/lib/dbi/handles.rb +49 -0
  51. data/lib/dbi/handles/database.rb +241 -0
  52. data/lib/dbi/handles/driver.rb +60 -0
  53. data/lib/dbi/handles/statement.rb +408 -0
  54. data/lib/dbi/row.rb +269 -0
  55. data/lib/dbi/sql.rb +22 -0
  56. data/lib/dbi/sql/preparedstatement.rb +115 -0
  57. data/lib/dbi/sql_type_constants.rb +75 -0
  58. data/lib/dbi/trace.rb +91 -0
  59. data/lib/dbi/types.rb +218 -0
  60. data/lib/dbi/typeutil.rb +109 -0
  61. data/lib/dbi/utils.rb +60 -0
  62. data/lib/dbi/utils/date.rb +59 -0
  63. data/lib/dbi/utils/tableformatter.rb +112 -0
  64. data/lib/dbi/utils/time.rb +52 -0
  65. data/lib/dbi/utils/timestamp.rb +96 -0
  66. data/lib/dbi/utils/xmlformatter.rb +73 -0
  67. data/lib/dbi/version.rb +3 -0
  68. data/prototypes/types2.rb +237 -0
  69. data/readme.md +274 -0
  70. data/setup.rb +1585 -0
  71. data/test/DBD_TESTS +50 -0
  72. data/test/TESTING +16 -0
  73. data/test/dbd/general/test_database.rb +206 -0
  74. data/test/dbd/general/test_statement.rb +326 -0
  75. data/test/dbd/general/test_types.rb +296 -0
  76. data/test/dbd/mysql/base.rb +26 -0
  77. data/test/dbd/mysql/down.sql +19 -0
  78. data/test/dbd/mysql/test_blob.rb +18 -0
  79. data/test/dbd/mysql/test_new_methods.rb +7 -0
  80. data/test/dbd/mysql/test_patches.rb +111 -0
  81. data/test/dbd/mysql/up.sql +28 -0
  82. data/test/dbd/odbc/base.rb +30 -0
  83. data/test/dbd/odbc/down.sql +19 -0
  84. data/test/dbd/odbc/test_new_methods.rb +12 -0
  85. data/test/dbd/odbc/test_ping.rb +10 -0
  86. data/test/dbd/odbc/test_statement.rb +44 -0
  87. data/test/dbd/odbc/test_transactions.rb +58 -0
  88. data/test/dbd/odbc/up.sql +33 -0
  89. data/test/dbd/postgresql/base.rb +31 -0
  90. data/test/dbd/postgresql/down.sql +31 -0
  91. data/test/dbd/postgresql/test_arrays.rb +179 -0
  92. data/test/dbd/postgresql/test_async.rb +121 -0
  93. data/test/dbd/postgresql/test_blob.rb +36 -0
  94. data/test/dbd/postgresql/test_bytea.rb +87 -0
  95. data/test/dbd/postgresql/test_ping.rb +10 -0
  96. data/test/dbd/postgresql/test_timestamp.rb +77 -0
  97. data/test/dbd/postgresql/test_transactions.rb +58 -0
  98. data/test/dbd/postgresql/testdbipg.rb +307 -0
  99. data/test/dbd/postgresql/up.sql +60 -0
  100. data/test/dbd/sqlite/base.rb +32 -0
  101. data/test/dbd/sqlite/test_database.rb +30 -0
  102. data/test/dbd/sqlite/test_driver.rb +68 -0
  103. data/test/dbd/sqlite/test_statement.rb +112 -0
  104. data/test/dbd/sqlite/up.sql +25 -0
  105. data/test/dbd/sqlite3/base.rb +32 -0
  106. data/test/dbd/sqlite3/test_database.rb +77 -0
  107. data/test/dbd/sqlite3/test_driver.rb +67 -0
  108. data/test/dbd/sqlite3/test_statement.rb +88 -0
  109. data/test/dbd/sqlite3/up.sql +33 -0
  110. data/test/dbi/tc_columninfo.rb +94 -0
  111. data/test/dbi/tc_date.rb +88 -0
  112. data/test/dbi/tc_dbi.rb +184 -0
  113. data/test/dbi/tc_row.rb +256 -0
  114. data/test/dbi/tc_sqlbind.rb +168 -0
  115. data/test/dbi/tc_statementhandle.rb +29 -0
  116. data/test/dbi/tc_time.rb +77 -0
  117. data/test/dbi/tc_timestamp.rb +142 -0
  118. data/test/dbi/tc_types.rb +268 -0
  119. data/test/ts_dbd.rb +131 -0
  120. data/test/ts_dbi.rb +16 -0
  121. data/ydbi.gemspec +24 -0
  122. metadata +224 -0
@@ -0,0 +1,78 @@
1
+ #
2
+ # See DBI::BaseStatement.
3
+ #
4
+ class DBI::DBD::SQLite3::Statement < DBI::BaseStatement
5
+ def initialize(sql, db)
6
+ sql.gsub!(/\\\\/) { '\\' } # sqlite underneath does this for us automatically, and it's causing trouble with the rest of the system.
7
+ @sql = sql
8
+ @db = db
9
+ @stmt = db.prepare(sql)
10
+ @result = nil
11
+ rescue ::SQLite3::Exception, RuntimeError => err
12
+ raise DBI::ProgrammingError.new(err.message)
13
+ end
14
+
15
+ #
16
+ # See DBI::BaseStatement#bind_param. This method will also raise
17
+ # DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
18
+ # binding.
19
+ #
20
+ def bind_param(param, value, attribs=nil)
21
+ raise DBI::InterfaceError, "Bound parameter must be an integer" unless param.kind_of? Fixnum
22
+ @stmt.bind_param(param, value)
23
+ end
24
+
25
+ def execute()
26
+ @result = @stmt.execute
27
+ @rows = DBI::SQL.query?(@sql) ? 0 : @db.changes
28
+ end
29
+
30
+ def finish()
31
+ @stmt.close rescue nil
32
+ @result = nil
33
+ end
34
+
35
+ def fetch()
36
+ ret = @result.next
37
+ return ret unless ret
38
+ [ret].flatten
39
+ end
40
+
41
+ def column_info()
42
+ @stmt.columns.zip(@stmt.types).map{|name, type_name|
43
+ m = DBI::DBD::SQLite3.parse_type(type_name)
44
+ h = {
45
+ 'name' => name,
46
+ 'type_name' => m[1],
47
+ 'sql_type' =>
48
+ begin
49
+ DBI.const_get('SQL_'+m[1].upcase)
50
+ rescue NameError
51
+ DBI::SQL_OTHER
52
+ end,
53
+ }
54
+ h['precision'] = m[3].to_i if m[3]
55
+ h['scale'] = m[5].to_i if m[5]
56
+
57
+ case h['type_name']
58
+ when 'double'
59
+ h['dbi_type'] = DBI::Type::Float
60
+ end
61
+
62
+ h
63
+ }
64
+ end
65
+
66
+ def rows()
67
+ @rows
68
+ end
69
+
70
+ def bind_params(*bindvars)
71
+ @stmt.bind_params(bindvars)
72
+ end
73
+
74
+ def cancel()
75
+ @result = nil
76
+ @index = 0
77
+ end
78
+ end
@@ -0,0 +1,336 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
2
+ #
3
+ # DBI - Database Interface for Ruby. Please see the files README, DBI_SPEC,
4
+ # DBD_SPEC for more information.
5
+ #
6
+ module DBI; end
7
+ #--
8
+ # Ruby/DBI
9
+ #
10
+ # Copyright (c) 2001, 2002, 2003 Michael Neumann <mneumann@ntecs.de>
11
+ # Copyright (c) 2008 Erik Hollensbe <erik@hollensbe.org>
12
+ #
13
+ # All rights reserved.
14
+ #
15
+ # Redistribution and use in source and binary forms, with or without
16
+ # modification, are permitted provided that the following conditions
17
+ # are met:
18
+ # 1. Redistributions of source code must retain the above copyright
19
+ # notice, this list of conditions and the following disclaimer.
20
+ # 2. Redistributions in binary form must reproduce the above copyright
21
+ # notice, this list of conditions and the following disclaimer in the
22
+ # documentation and/or other materials provided with the distribution.
23
+ # 3. The name of the author may not be used to endorse or promote products
24
+ # derived from this software without specific prior written permission.
25
+ #
26
+ # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
27
+ # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29
+ # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32
+ # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34
+ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35
+ # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ #
37
+
38
+ begin
39
+ require "rubygems"
40
+ gem "deprecated", "= 2.0.1"
41
+ rescue LoadError
42
+ end
43
+
44
+ #
45
+ # NOTE see the end of the file for requires that live in the DBI namespace.
46
+ #
47
+
48
+ require "deprecated"
49
+ require "dbi/row"
50
+ require "dbi/utils"
51
+ require "dbi/sql"
52
+ require "dbi/columninfo"
53
+ require 'dbi/types'
54
+ require 'dbi/typeutil'
55
+ require 'dbi/sql_type_constants'
56
+ require 'dbi/exceptions'
57
+ require 'dbi/binary'
58
+ require 'dbi/handles'
59
+ require 'dbi/base_classes'
60
+ require "date"
61
+ require "thread"
62
+ require 'monitor'
63
+
64
+ class Class
65
+ # Given a Class, returns if the object's (another Class) ancestors contain
66
+ # that class.
67
+ def inherits_from?(klass)
68
+ self.ancestors.include?(klass)
69
+ end
70
+ end
71
+
72
+ Deprecate.set_action(
73
+ proc do |call|
74
+ klass, meth = call.split(/[#.]/)
75
+ klass = klass.split(/::/).inject(Module) { |a,x| a.const_get(x) }
76
+
77
+ case klass
78
+ when DBI::Date, DBI::Time, DBI::Timestamp
79
+ warn "DBI::Date/Time/Timestamp are deprecated and will eventually be removed."
80
+ end
81
+
82
+ if klass.inherits_from?(DBI::ColumnInfo)
83
+ warn "ColumnInfo methods that do not match a component are deprecated and will eventually be removed"
84
+ end
85
+
86
+ warn "You may change the result of calling deprecated code via Deprecate.set_action; Trace follows:"
87
+ warn caller[2..-1].join("\n")
88
+ end
89
+ )
90
+
91
+ #++
92
+ module DBI
93
+
94
+ module DBD # :nodoc:
95
+ API_VERSION = "0.3"
96
+ end
97
+
98
+ # Module functions (of DBI)
99
+ DEFAULT_TRACE_MODE = 2
100
+ DEFAULT_TRACE_OUTPUT = STDERR
101
+
102
+ # TODO: Is using class variables within a module such a wise idea? - Dan B.
103
+ @@driver_map = Hash.new
104
+ @@driver_monitor = ::Monitor.new()
105
+ @@trace_mode = DEFAULT_TRACE_MODE
106
+ @@trace_output = DEFAULT_TRACE_OUTPUT
107
+ @@caseless_driver_name_map = nil
108
+ @@convert_types = true
109
+ @@last_connection = nil
110
+
111
+ # Return the last connection attempted.
112
+ def self.last_connection
113
+ @@last_connection
114
+ end
115
+
116
+ # Return the current status of type conversion at this level. This status
117
+ # will be propogated to any new DatabaseHandles created.
118
+ def self.convert_types
119
+ @@convert_types
120
+ end
121
+
122
+ # Set the current status of type conversion at this level. This status
123
+ # will be propogated to any new DatabaseHandles created.
124
+ def self.convert_types=(bool)
125
+ @@convert_types = bool
126
+ end
127
+
128
+ class << self
129
+
130
+ # Establish a database connection.
131
+ #
132
+ # Format goes as such: "dbi:Driver:database_conn_args"
133
+ #
134
+ # * "dbi" is the literal string "dbi". Case is unimportant.
135
+ # * "Driver" is the case-dependent name of your database driver class.
136
+ # The file "dbd/#{Driver}" will be required. If you are using rubygems to
137
+ # control your DBDs and DBI, you must make the gem's file path available
138
+ # via the "gem" command before this will work.
139
+ # * database_conn_args can be:
140
+ # * The database name.
141
+ # * A more complex key/value association (to indicate host, etc). This
142
+ # is driver dependent; you should consult your DBD documentation.
143
+ def connect(driver_url, user=nil, auth=nil, params=nil, &p)
144
+ dr, db_args = _get_full_driver(driver_url)
145
+ dh = dr[0] # driver-handle
146
+ dh.convert_types = @@convert_types
147
+ @@last_connection = dh.connect(db_args, user, auth, params, &p)
148
+ end
149
+
150
+ # Load a DBD and returns the DriverHandle object
151
+ def get_driver(driver_url) #:nodoc:
152
+ _get_full_driver(driver_url)[0][0] # return DriverHandle
153
+ end
154
+
155
+ # Extracts the db_args from driver_url and returns the correspondeing
156
+ # entry of the @@driver_map.
157
+ def _get_full_driver(driver_url) #:nodoc:
158
+ db_driver, db_args = parse_url(driver_url)
159
+ db_driver = load_driver(db_driver)
160
+ dr = @@driver_map[db_driver]
161
+ [dr, db_args]
162
+ end
163
+
164
+ #
165
+ # Enable tracing mode. Requires that 'dbi/trace' be required before it does anything.
166
+ #
167
+ # As of 0.4.0, this mode does not do anything either way, so this currently just
168
+ # throws an InterfaceError. This issue is expected to be resolved in the next release.
169
+ #
170
+ def trace(mode=nil, output=nil)
171
+ # FIXME trace
172
+ raise InterfaceError, "the trace module has been removed until it actually works."
173
+ @@trace_mode = mode || @@trace_mode || DBI::DEFAULT_TRACE_MODE
174
+ @@trace_output = output || @@trace_output || DBI::DEFAULT_TRACE_OUTPUT
175
+ end
176
+
177
+ #
178
+ # Return a list (of String) of the available drivers.
179
+ #
180
+ # NOTE:: This is non-functional for gem installations, due to the
181
+ # nature of how it currently works. A better solution for
182
+ # this will be provided in DBI 0.6.0.
183
+ def collect_drivers
184
+ drivers = { }
185
+ # FIXME rewrite this to leverage require and be more intelligent
186
+ path = File.join(File.dirname(__FILE__), "dbd", "*.rb")
187
+ Dir[path].each do |f|
188
+ if File.file?(f)
189
+ driver = File.basename(f, ".rb")
190
+ drivers[driver] = f
191
+ end
192
+ end
193
+
194
+ return drivers
195
+ end
196
+
197
+ # Returns a list (of String) of the currently available drivers on your system in
198
+ # 'dbi:driver:' format.
199
+ #
200
+ # This currently does not work for rubygems installations, please see
201
+ # DBI.collect_drivers for reasons.
202
+ def available_drivers
203
+ drivers = []
204
+ collect_drivers.each do |key, value|
205
+ drivers.push("dbi:#{key}:")
206
+ end
207
+ return drivers
208
+ end
209
+
210
+ # Attempt to collect the available data sources to the driver,
211
+ # specified in DBI.connect format.
212
+ #
213
+ # The result is heavily dependent on the driver's ability to enumerate
214
+ # these sources, and results will vary.
215
+ def data_sources(driver)
216
+ db_driver, = parse_url(driver)
217
+ db_driver = load_driver(db_driver)
218
+ dh = @@driver_map[db_driver][0]
219
+ dh.data_sources
220
+ end
221
+
222
+ #
223
+ # Attempt to disconnect all database handles. If a driver is provided,
224
+ # disconnections will happen under that scope. Otherwise, all loaded
225
+ # drivers (and their handles) will be attempted.
226
+ #
227
+ def disconnect_all( driver = nil )
228
+ if driver.nil?
229
+ @@driver_map.each {|k,v| v[0].disconnect_all}
230
+ else
231
+ db_driver, = parse_url(driver)
232
+ @@driver_map[db_driver][0].disconnect_all
233
+ end
234
+ end
235
+
236
+ private
237
+
238
+ # Given a driver name, locate and load the associated DBD package,
239
+ # generate a DriverHandle and return it.
240
+ def load_driver(driver_name)
241
+ @@driver_monitor.synchronize do
242
+ unless @@driver_map[driver_name]
243
+ dc = driver_name.downcase
244
+
245
+ # caseless look for drivers already loaded
246
+ found = @@driver_map.keys.find {|key| key.downcase == dc}
247
+ return found if found
248
+
249
+ begin
250
+ require "dbd/#{driver_name}"
251
+ rescue LoadError => e1
252
+ # see if you can find it in the path
253
+ unless @@caseless_driver_name_map
254
+ @@caseless_driver_name_map = { }
255
+ collect_drivers.each do |key, value|
256
+ @@caseless_driver_name_map[key.downcase] = value
257
+ end
258
+ end
259
+
260
+ begin
261
+ require @@caseless_driver_name_map[dc] if @@caseless_driver_name_map[dc]
262
+ rescue LoadError => e2
263
+ raise e1.class, "Could not find driver #{driver_name} or #{driver_name.downcase} (error: #{e1.message})"
264
+ end
265
+ end
266
+
267
+ # On a filesystem that is not case-sensitive (e.g., HFS+ on Mac OS X),
268
+ # the initial require attempt that loads the driver may succeed even
269
+ # though the lettercase of driver_name doesn't match the actual
270
+ # filename. If that happens, const_get will fail and it become
271
+ # necessary to look though the list of constants and look for a
272
+ # caseless match. The result of this match provides the constant
273
+ # with the proper lettercase -- which can be used to generate the
274
+ # driver handle.
275
+
276
+ dr = nil
277
+ dr_error = nil
278
+ begin
279
+ dr = DBI::DBD.const_get(driver_name.intern)
280
+ rescue NameError => dr_error
281
+ # caseless look for constants to find actual constant
282
+ dc = driver_name.downcase
283
+ found = DBI::DBD.constants.find { |e| e.downcase == dc }
284
+ dr = DBI::DBD.const_get(found.intern) unless found.nil?
285
+ end
286
+
287
+ # If dr is nil at this point, it means the underlying driver
288
+ # failed to load. This usually means it's not installed, but
289
+ # can fail for other reasons.
290
+ if dr.nil?
291
+ err = "Unable to load driver '#{driver_name}'"
292
+
293
+ if dr_error
294
+ err += " (underlying error: #{dr_error.message})"
295
+ else
296
+ err += " (BUG: could not determine underlying error)"
297
+ end
298
+
299
+ raise DBI::InterfaceError, err
300
+ end
301
+
302
+ dbd_dr = dr::Driver.new
303
+ drh = DBI::DriverHandle.new(dbd_dr, @@convert_types)
304
+ drh.driver_name = dr.driver_name
305
+ # FIXME trace
306
+ # drh.trace(@@trace_mode, @@trace_output)
307
+ @@driver_map[driver_name] = [drh, dbd_dr]
308
+ return driver_name
309
+ else
310
+ return driver_name
311
+ end
312
+ end
313
+ rescue LoadError, NameError
314
+ if $SAFE >= 1
315
+ raise InterfaceError, "Could not load driver (#{$!.message}). Note that in SAFE mode >= 1, driver URLs have to be case sensitive!"
316
+ else
317
+ raise InterfaceError, "Could not load driver (#{$!.message})"
318
+ end
319
+ end
320
+
321
+ # Splits a DBI URL into two components - the database driver name
322
+ # and the datasource (along with any options, if any) and returns
323
+ # a two element array, e.g. 'dbi:foo:bar' would return ['foo','bar'].
324
+ #
325
+ # A regular expression is used instead of a simple split to validate
326
+ # the proper format for the URL. If it isn't correct, an Interface
327
+ # error is raised.
328
+ def parse_url(driver_url)
329
+ if driver_url =~ /^(DBI|dbi):([^:]+)(:(.*))$/
330
+ [$2, $4]
331
+ else
332
+ raise InterfaceError, "Invalid Data Source Name"
333
+ end
334
+ end
335
+ end # self
336
+ end # module DBI
@@ -0,0 +1,12 @@
1
+ #--
2
+ # Fallback classes for default behavior of DBD driver
3
+ # must be inherited by the DBD driver classes
4
+ #++
5
+ module DBI
6
+ class Base #:nodoc:
7
+ end
8
+ end
9
+
10
+ require 'dbi/base_classes/driver'
11
+ require 'dbi/base_classes/database'
12
+ require 'dbi/base_classes/statement'
@@ -0,0 +1,135 @@
1
+ module DBI
2
+ # Provides the core-level functionality for DatabaseHandles.
3
+ #
4
+ # If the method description says "DBD Required", it's the DBD's
5
+ # responsibility to create this method.
6
+ #
7
+ # Required methods unimplemented by the DBD will raise
8
+ # DBD::NotImplementedError.
9
+ #
10
+ # "DBD Optional" methods are methods that do not have a default
11
+ # implementation but are optional due to the fact that many databases may
12
+ # not support these features (and emulating them would be prohibitive).
13
+ #
14
+ # These methods raise DBI::NotSupportedError.
15
+ #
16
+ # Otherwise, DBI will provide a general alternative which should meet the
17
+ # expectations of the documentation. However, DBDs can override every
18
+ # method in this class.
19
+ #
20
+ class BaseDatabase < Base
21
+ def initialize(handle, attr)
22
+ @handle = handle
23
+ @attr = {}
24
+ attr.each {|k,v| self[k] = v}
25
+ end
26
+
27
+ # Disconnect from the database. DBD Required.
28
+ def disconnect
29
+ raise NotImplementedError
30
+ end
31
+
32
+ # Ping the database to ensure the connection is still alive. Boolean
33
+ # return, true for success. DBD Required.
34
+ def ping
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # Prepare a cached statement, returning a StatementHandle. DBD
39
+ # Required.
40
+ def prepare(statement)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ #
45
+ # Return a map of the columns that exist in the provided table name.
46
+ # DBD Required.
47
+ #
48
+ # The result should be an array of DBI::ColumnInfo objects which have,
49
+ # at minimum, the following fields:
50
+ #
51
+ # * name:: the name of the column.
52
+ # * type:: This is not a field name in itself. You have two options:
53
+ # * type_name:: The name of the type as returned by the database
54
+ # * dbi_type:: A DBI::Type-conforming class that can be used to convert to a native type.
55
+ # * precision:: the precision (generally length) of the column
56
+ # * scale:: the scale (generally a secondary attribute to precision
57
+ # that helps indicate length) of the column
58
+ #
59
+ def columns(table)
60
+ raise NotImplementedError
61
+ end
62
+
63
+ #============================================
64
+ # OPTIONAL
65
+ #============================================
66
+
67
+ # Schedule a commit to the database immediately. DBD Optional.
68
+ def commit
69
+ raise NotSupportedError
70
+ end
71
+
72
+ # Schedule a rollback to the database immediately. DBD Optional.
73
+ def rollback
74
+ raise NotSupportedError
75
+ end
76
+
77
+ # Return the tables available to the database connection.
78
+ #
79
+ # Note:: the basic implementation returns an empty array.
80
+ def tables
81
+ []
82
+ end
83
+
84
+ #
85
+ # Execute a statement with the binds provided. Returns the statement
86
+ # handle unfinished.
87
+ #
88
+ # This is roughly equivalent to:
89
+ #
90
+ # sth = dbh.prepare("my statement")
91
+ # sth.execute(my, bind, vars)
92
+ #
93
+ def execute(statement, *bindvars)
94
+ stmt = prepare(statement)
95
+ stmt.bind_params(*bindvars)
96
+ stmt.execute
97
+ stmt
98
+ end
99
+
100
+ #
101
+ # Execute and complete the statement with the binds provided. Returns
102
+ # the row modified count (via BaseStatement#rows). Finishes the
103
+ # statement handle for you.
104
+ #
105
+ # Roughly equivalent to:
106
+ #
107
+ # sth = dbh.prepare("my statement")
108
+ # sth.execute(my, bind, vars)
109
+ # result = sth.rows
110
+ # sth.finish
111
+ #
112
+ # Returning the value stored in `result`.
113
+ def do(statement, *bindvars)
114
+ stmt = execute(statement, *bindvars)
115
+ res = stmt.rows
116
+ stmt.finish
117
+ return res
118
+ end
119
+
120
+ #
121
+ # Get an attribute from the DatabaseHandle. These are DBD specific and
122
+ # embody things like Auto-Commit support for transactional databases.
123
+ #
124
+ # DBD Authors:: This messes with @attr directly.
125
+ #
126
+ def [](attr)
127
+ @attr[attr]
128
+ end
129
+
130
+ # Set an attribute on the DatabaseHandle. DBD Optional.
131
+ def []=(attr, value)
132
+ raise NotSupportedError
133
+ end
134
+ end # class BaseDatabase
135
+ end