dbi 0.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 (45) hide show
  1. data/ChangeLog +3694 -0
  2. data/LICENSE +25 -0
  3. data/README +271 -0
  4. data/bin/dbi +518 -0
  5. data/build/Rakefile.dbi.rb +57 -0
  6. data/examples/test1.pl +39 -0
  7. data/examples/test1.rb +20 -0
  8. data/examples/xmltest.rb +8 -0
  9. data/lib/dbi.rb +323 -0
  10. data/lib/dbi/base_classes.rb +12 -0
  11. data/lib/dbi/base_classes/database.rb +135 -0
  12. data/lib/dbi/base_classes/driver.rb +47 -0
  13. data/lib/dbi/base_classes/statement.rb +167 -0
  14. data/lib/dbi/binary.rb +25 -0
  15. data/lib/dbi/columninfo.rb +106 -0
  16. data/lib/dbi/exceptions.rb +65 -0
  17. data/lib/dbi/handles.rb +49 -0
  18. data/lib/dbi/handles/database.rb +211 -0
  19. data/lib/dbi/handles/driver.rb +60 -0
  20. data/lib/dbi/handles/statement.rb +375 -0
  21. data/lib/dbi/row.rb +249 -0
  22. data/lib/dbi/sql.rb +23 -0
  23. data/lib/dbi/sql/preparedstatement.rb +115 -0
  24. data/lib/dbi/sql_type_constants.rb +75 -0
  25. data/lib/dbi/trace.rb +91 -0
  26. data/lib/dbi/types.rb +158 -0
  27. data/lib/dbi/typeutil.rb +108 -0
  28. data/lib/dbi/utils.rb +60 -0
  29. data/lib/dbi/utils/date.rb +59 -0
  30. data/lib/dbi/utils/tableformatter.rb +112 -0
  31. data/lib/dbi/utils/time.rb +52 -0
  32. data/lib/dbi/utils/timestamp.rb +96 -0
  33. data/lib/dbi/utils/xmlformatter.rb +73 -0
  34. data/test/dbi/tc_columninfo.rb +94 -0
  35. data/test/dbi/tc_date.rb +88 -0
  36. data/test/dbi/tc_dbi.rb +178 -0
  37. data/test/dbi/tc_row.rb +256 -0
  38. data/test/dbi/tc_sqlbind.rb +168 -0
  39. data/test/dbi/tc_statementhandle.rb +16 -0
  40. data/test/dbi/tc_time.rb +77 -0
  41. data/test/dbi/tc_timestamp.rb +142 -0
  42. data/test/dbi/tc_types.rb +220 -0
  43. data/test/dbi/trace.rb +26 -0
  44. data/test/ts_dbi.rb +15 -0
  45. metadata +108 -0
@@ -0,0 +1,57 @@
1
+ require 'rake_task_lib'
2
+ require 'dbi'
3
+
4
+ DBD_PACKAGES = Dir['lib/dbd/*.rb'].collect { |x| File.basename(x, '.rb') }
5
+
6
+ # creates a number of tasks like dbi:task_name, dbd_mysql:task_name, so on.
7
+ # Builds these out into an array that can be used as a prereq for other tasks.
8
+ def map_task(task_name)
9
+ namespaces = (['dbi'] + DBD_PACKAGES.collect { |x| dbd_namespace(x) }).flatten
10
+ namespaces.collect { |x| [x, task_name].join(":") }
11
+ end
12
+
13
+ task :package => (map_task("package") + map_task("gem"))
14
+ task :clobber_package => map_task("clobber_package")
15
+
16
+ task :test_dbi do
17
+ ruby "test/ts_dbi.rb"
18
+ end
19
+
20
+ task :test_dbd do
21
+ ruby "test/ts_dbd.rb"
22
+ end
23
+
24
+ task :test => [:test_dbi, :test_dbd]
25
+
26
+ build_dbi_tasks
27
+
28
+ #
29
+ # There's probably a better way to do this, but here's a boilerplate spec that we dup and modify.
30
+ #
31
+
32
+ task :dbi => DEFAULT_TASKS.collect { |x| "dbi:#{x.to_s}" }
33
+
34
+ namespace :dbi do
35
+ code_files = %w(examples/**/* bin/dbi build/Rakefile.dbi.rb lib/dbi.rb lib/dbi/**/*.rb test/ts_dbi.rb test/dbi/*)
36
+
37
+ spec = boilerplate_spec
38
+ spec.name = 'dbi'
39
+ spec.version = DBI::VERSION
40
+ spec.test_file = 'test/ts_dbi.rb'
41
+ spec.executables = ['dbi']
42
+ spec.files = gem_files(code_files)
43
+ spec.summary = 'A vendor independent interface for accessing databases, similar to Perl\'s DBI'
44
+ spec.description = 'A vendor independent interface for accessing databases, similar to Perl\'s DBI'
45
+ spec.add_dependency 'deprecated', '>= 2.0.0'
46
+
47
+ build_package_tasks(spec, code_files)
48
+ end
49
+
50
+ DBD_PACKAGES.each do |dbd|
51
+ my_namespace = dbd_namespace(dbd)
52
+
53
+ task my_namespace => DEFAULT_TASKS.collect { |x| "#{my_namespace}:#{x.to_s}" }
54
+ namespace my_namespace do
55
+ build_dbd_tasks(dbd)
56
+ end
57
+ end
@@ -0,0 +1,39 @@
1
+ use DBI;
2
+
3
+ $dbh = DBI->connect("dbi:Oracle:oracle.neumann", "scott", "tiger", {RaiseError => 1, AutoCommit => 0} );
4
+
5
+
6
+ $dbh->do("DROP TABLE MYTEST");
7
+ $dbh->do("CREATE TABLE MYTEST (a INT, b VARCHAR2(256), c FLOAT, d VARCHAR2(256))");
8
+
9
+ $sth = $dbh->prepare("INSERT INTO MYTEST VALUES (:1, :2, :3, :4)");
10
+
11
+ $i = 1;
12
+
13
+ while ($i <= 10000) {
14
+
15
+ $sth->execute($i, "Michael der $i. von Neumann", 5.6 * $i, "HALLO LEUTE WIE GEHTS DENN SO?");
16
+
17
+ $i = $i + 1;
18
+ #print $i, "\n";
19
+ }
20
+
21
+
22
+ $dbh->commit();
23
+
24
+
25
+
26
+
27
+
28
+ #$sth = $dbh->prepare("SELECT * FROM EMP");
29
+
30
+ #$sth->execute();
31
+
32
+ #while (@row = $sth->fetchrow_array()) {
33
+ # print(@row);
34
+ #}
35
+
36
+
37
+ $dbh->disconnect();
38
+
39
+
@@ -0,0 +1,20 @@
1
+ require "dbi"
2
+
3
+ dbh = DBI.connect("dbi:Oracle:oracle.neumann", "scott", "tiger", 'AutoCommit' => false)
4
+
5
+ dbh.do("DROP TABLE MYTEST")
6
+ dbh.do("CREATE TABLE MYTEST (a INT, b VARCHAR2(256), c FLOAT, d VARCHAR2(256))")
7
+
8
+ sth = dbh.prepare("INSERT INTO MYTEST VALUES (:1, :2, :3, :4)")
9
+
10
+ 1.upto(10000) do |i|
11
+ sth.execute(i.to_s, "Michael der #{i}. von Neumann", (5.6 * i).to_s, "HALLO LEUTE WIE GEHTS DENN SO?")
12
+ #print i, "\n"
13
+ end
14
+
15
+ sth.finish
16
+
17
+ dbh.commit
18
+
19
+ dbh.disconnect
20
+
@@ -0,0 +1,8 @@
1
+ require "dbi"
2
+
3
+ DBI.connect("dbi:Oracle:oracle.neumann") do |dbh|
4
+ dbh.execute("SELECT * FROM EMP") do |sth|
5
+ DBI::Utils::XMLFormatter.table(sth.fetch_all, "EMP")
6
+ end
7
+ end
8
+
@@ -0,0 +1,323 @@
1
+ $LOAD_PATH.unshift(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"
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
+ VERSION = "0.4.0"
94
+
95
+ module DBD # :nodoc:
96
+ API_VERSION = "0.3"
97
+ end
98
+
99
+ # Module functions (of DBI)
100
+ DEFAULT_TRACE_MODE = 2
101
+ DEFAULT_TRACE_OUTPUT = STDERR
102
+
103
+ # TODO: Is using class variables within a module such a wise idea? - Dan B.
104
+ @@driver_map = Hash.new
105
+ @@driver_monitor = ::Monitor.new()
106
+ @@trace_mode = DEFAULT_TRACE_MODE
107
+ @@trace_output = DEFAULT_TRACE_OUTPUT
108
+ @@caseless_driver_name_map = nil
109
+ @@convert_types = true
110
+
111
+ # Return the current status of type conversion at this level. This status
112
+ # will be propogated to any new DatabaseHandles created.
113
+ def self.convert_types
114
+ @@convert_types
115
+ end
116
+
117
+ # Set the current status of type conversion at this level. This status
118
+ # will be propogated to any new DatabaseHandles created.
119
+ def self.convert_types=(bool)
120
+ @@convert_types = bool
121
+ end
122
+
123
+ class << self
124
+
125
+ # Establish a database connection.
126
+ #
127
+ # Format goes as such: "dbi:Driver:database_conn_args"
128
+ #
129
+ # * "dbi" is the literal string "dbi". Case is unimportant.
130
+ # * "Driver" is the case-dependent name of your database driver class.
131
+ # The file "dbd/#{Driver}" will be required. If you are using rubygems to
132
+ # control your DBDs and DBI, you must make the gem's file path available
133
+ # via the "gem" command before this will work.
134
+ # * database_conn_args can be:
135
+ # * The database name.
136
+ # * A more complex key/value association (to indicate host, etc). This
137
+ # is driver dependent; you should consult your DBD documentation.
138
+ def connect(driver_url, user=nil, auth=nil, params=nil, &p)
139
+ dr, db_args = _get_full_driver(driver_url)
140
+ dh = dr[0] # driver-handle
141
+ dh.convert_types = @@convert_types
142
+ dh.connect(db_args, user, auth, params, &p)
143
+ end
144
+
145
+ # Load a DBD and returns the DriverHandle object
146
+ def get_driver(driver_url) #:nodoc:
147
+ _get_full_driver(driver_url)[0][0] # return DriverHandle
148
+ end
149
+
150
+ # Extracts the db_args from driver_url and returns the correspondeing
151
+ # entry of the @@driver_map.
152
+ def _get_full_driver(driver_url) #:nodoc:
153
+ db_driver, db_args = parse_url(driver_url)
154
+ db_driver = load_driver(db_driver)
155
+ dr = @@driver_map[db_driver]
156
+ [dr, db_args]
157
+ end
158
+
159
+ #
160
+ # Enable tracing mode. Requires that 'dbi/trace' be required before it does anything.
161
+ #
162
+ # As of 0.4.0, this mode does not do anything either way, so this currently just
163
+ # throws an InterfaceError. This issue is expected to be resolved in the next release.
164
+ #
165
+ def trace(mode=nil, output=nil)
166
+ # FIXME trace
167
+ raise InterfaceError, "the trace module has been removed until it actually works."
168
+ @@trace_mode = mode || @@trace_mode || DBI::DEFAULT_TRACE_MODE
169
+ @@trace_output = output || @@trace_output || DBI::DEFAULT_TRACE_OUTPUT
170
+ end
171
+
172
+ #
173
+ # Return a list (of String) of the available drivers.
174
+ #
175
+ # NOTE:: This is non-functional for gem installations, due to the
176
+ # nature of how it currently works. A better solution for
177
+ # this will be provided in DBI 0.6.0.
178
+ def collect_drivers
179
+ drivers = { }
180
+ # FIXME rewrite this to leverage require and be more intelligent
181
+ path = File.join(File.dirname(__FILE__), "dbd", "*.rb")
182
+ Dir[path].each do |f|
183
+ if File.file?(f)
184
+ driver = File.basename(f, ".rb")
185
+ drivers[driver] = f
186
+ end
187
+ end
188
+
189
+ return drivers
190
+ end
191
+
192
+ # Returns a list (of String) of the currently available drivers on your system in
193
+ # 'dbi:driver:' format.
194
+ #
195
+ # This currently does not work for rubygems installations, please see
196
+ # DBI.collect_drivers for reasons.
197
+ def available_drivers
198
+ drivers = []
199
+ collect_drivers.each do |key, value|
200
+ drivers.push("dbi:#{key}:")
201
+ end
202
+ return drivers
203
+ end
204
+
205
+ # Attempt to collect the available data sources to the driver,
206
+ # specified in DBI.connect format.
207
+ #
208
+ # The result is heavily dependent on the driver's ability to enumerate
209
+ # these sources, and results will vary.
210
+ def data_sources(driver)
211
+ db_driver, = parse_url(driver)
212
+ db_driver = load_driver(db_driver)
213
+ dh = @@driver_map[db_driver][0]
214
+ dh.data_sources
215
+ end
216
+
217
+ #
218
+ # Attempt to disconnect all database handles. If a driver is provided,
219
+ # disconnections will happen under that scope. Otherwise, all loaded
220
+ # drivers (and their handles) will be attempted.
221
+ #
222
+ def disconnect_all( driver = nil )
223
+ if driver.nil?
224
+ @@driver_map.each {|k,v| v[0].disconnect_all}
225
+ else
226
+ db_driver, = parse_url(driver)
227
+ @@driver_map[db_driver][0].disconnect_all
228
+ end
229
+ end
230
+
231
+ private
232
+
233
+ # Given a driver name, locate and load the associated DBD package,
234
+ # generate a DriverHandle and return it.
235
+ def load_driver(driver_name)
236
+ @@driver_monitor.synchronize do
237
+ unless @@driver_map[driver_name]
238
+ dc = driver_name.downcase
239
+
240
+ # caseless look for drivers already loaded
241
+ found = @@driver_map.keys.find {|key| key.downcase == dc}
242
+ return found if found
243
+
244
+ begin
245
+ require "dbd/#{driver_name}"
246
+ rescue LoadError => e1
247
+ # see if you can find it in the path
248
+ unless @@caseless_driver_name_map
249
+ @@caseless_driver_name_map = { }
250
+ collect_drivers.each do |key, value|
251
+ @@caseless_driver_name_map[key.downcase] = value
252
+ end
253
+ end
254
+
255
+ begin
256
+ require @@caseless_driver_name_map[dc] if @@caseless_driver_name_map[dc]
257
+ rescue LoadError => e2
258
+ raise e.class, "Could not find driver #{driver_name} or #{driver_name.downcase} (error: #{e1.message})"
259
+ end
260
+ end
261
+
262
+ # On a filesystem that is not case-sensitive (e.g., HFS+ on Mac OS X),
263
+ # the initial require attempt that loads the driver may succeed even
264
+ # though the lettercase of driver_name doesn't match the actual
265
+ # filename. If that happens, const_get will fail and it become
266
+ # necessary to look though the list of constants and look for a
267
+ # caseless match. The result of this match provides the constant
268
+ # with the proper lettercase -- which can be used to generate the
269
+ # driver handle.
270
+
271
+ dr = nil
272
+ begin
273
+ dr = DBI::DBD.const_get(driver_name.intern)
274
+ rescue NameError
275
+ # caseless look for constants to find actual constant
276
+ dc = driver_name.downcase
277
+ found = DBI::DBD.constants.find { |e| e.downcase == dc }
278
+ dr = DBI::DBD.const_get(found.intern) unless found.nil?
279
+ end
280
+
281
+ # If dr is nil at this point, it means the underlying driver
282
+ # failed to load. This usually means it's not installed, but
283
+ # can fail for other reasons.
284
+ if dr.nil?
285
+ err = "Unable to load driver '#{driver_name}'"
286
+ raise DBI::InterfaceError, err
287
+ end
288
+
289
+ dbd_dr = dr::Driver.new
290
+ drh = DBI::DriverHandle.new(dbd_dr, @@convert_types)
291
+ drh.driver_name = dr.driver_name
292
+ # FIXME trace
293
+ # drh.trace(@@trace_mode, @@trace_output)
294
+ @@driver_map[driver_name] = [drh, dbd_dr]
295
+ return driver_name
296
+ else
297
+ return driver_name
298
+ end
299
+ end
300
+ rescue LoadError, NameError
301
+ if $SAFE >= 1
302
+ raise InterfaceError, "Could not load driver (#{$!.message}). Note that in SAFE mode >= 1, driver URLs have to be case sensitive!"
303
+ else
304
+ raise InterfaceError, "Could not load driver (#{$!.message})"
305
+ end
306
+ end
307
+
308
+ # Splits a DBI URL into two components - the database driver name
309
+ # and the datasource (along with any options, if any) and returns
310
+ # a two element array, e.g. 'dbi:foo:bar' would return ['foo','bar'].
311
+ #
312
+ # A regular expression is used instead of a simple split to validate
313
+ # the proper format for the URL. If it isn't correct, an Interface
314
+ # error is raised.
315
+ def parse_url(driver_url)
316
+ if driver_url =~ /^(DBI|dbi):([^:]+)(:(.*))$/
317
+ [$2, $4]
318
+ else
319
+ raise InterfaceError, "Invalid Data Source Name"
320
+ end
321
+ end
322
+ end # self
323
+ end # module DBI