ydbi 0.5.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 (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