dbi 0.4.0

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