rails-dbi 0.1.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/bin/test_broken_dbi +37 -0
  6. data/build/Rakefile.dbi.rb +60 -0
  7. data/examples/test1.pl +39 -0
  8. data/examples/test1.rb +20 -0
  9. data/examples/xmltest.rb +8 -0
  10. data/lib/dbi/base_classes/database.rb +135 -0
  11. data/lib/dbi/base_classes/driver.rb +47 -0
  12. data/lib/dbi/base_classes/statement.rb +171 -0
  13. data/lib/dbi/base_classes.rb +12 -0
  14. data/lib/dbi/binary.rb +25 -0
  15. data/lib/dbi/columninfo.rb +107 -0
  16. data/lib/dbi/exceptions.rb +65 -0
  17. data/lib/dbi/handles/database.rb +241 -0
  18. data/lib/dbi/handles/driver.rb +60 -0
  19. data/lib/dbi/handles/statement.rb +408 -0
  20. data/lib/dbi/handles.rb +49 -0
  21. data/lib/dbi/row.rb +270 -0
  22. data/lib/dbi/sql/preparedstatement.rb +115 -0
  23. data/lib/dbi/sql.rb +22 -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 +218 -0
  27. data/lib/dbi/typeutil.rb +109 -0
  28. data/lib/dbi/utils/date.rb +59 -0
  29. data/lib/dbi/utils/tableformatter.rb +112 -0
  30. data/lib/dbi/utils/time.rb +52 -0
  31. data/lib/dbi/utils/timestamp.rb +96 -0
  32. data/lib/dbi/utils/xmlformatter.rb +73 -0
  33. data/lib/dbi/utils.rb +60 -0
  34. data/lib/dbi.rb +337 -0
  35. data/test/dbi/tc_columninfo.rb +94 -0
  36. data/test/dbi/tc_date.rb +88 -0
  37. data/test/dbi/tc_dbi.rb +184 -0
  38. data/test/dbi/tc_row.rb +256 -0
  39. data/test/dbi/tc_sqlbind.rb +168 -0
  40. data/test/dbi/tc_statementhandle.rb +29 -0
  41. data/test/dbi/tc_time.rb +77 -0
  42. data/test/dbi/tc_timestamp.rb +142 -0
  43. data/test/dbi/tc_types.rb +268 -0
  44. data/test/ts_dbi.rb +15 -0
  45. metadata +132 -0
@@ -0,0 +1,49 @@
1
+ #
2
+ # Dispatch classes (Handle, DriverHandle, DatabaseHandle and StatementHandle)
3
+ #
4
+
5
+ module DBI
6
+ #
7
+ # Base class for all handles.
8
+ #
9
+ class Handle
10
+ attr_reader :trace_mode, :trace_output
11
+ attr_reader :handle
12
+ attr :convert_types, true
13
+
14
+ def initialize(handle, convert_types=true)
15
+ @handle = handle
16
+ @trace_mode = @trace_output = nil
17
+ @convert_types = convert_types
18
+ end
19
+
20
+ # Please seee DBI.trace.
21
+ def trace(mode=nil, output=nil)
22
+ # FIXME trace
23
+ raise InterfaceError, "the trace module has been removed until it actually works."
24
+ @trace_mode = mode || @trace_mode || DBI::DEFAULT_TRACE_MODE
25
+ @trace_output = output || @trace_output || DBI::DEFAULT_TRACE_OUTPUT
26
+ end
27
+
28
+ #
29
+ # Leverage a driver-specific method. The method name will have "__"
30
+ # prepended to them before calling, and the DBD must define them as
31
+ # such for them to work.
32
+ #
33
+ def func(function, *values)
34
+ if @handle.respond_to?("__" + function.to_s) then
35
+ @handle.send("__" + function.to_s, *values)
36
+ else
37
+ raise InterfaceError, "Driver specific function <#{function}> not available."
38
+ end
39
+ rescue ArgumentError
40
+ raise InterfaceError, "Wrong # of arguments for driver specific function"
41
+ end
42
+
43
+ # error functions?
44
+ end
45
+ end
46
+
47
+ require 'dbi/handles/driver'
48
+ require 'dbi/handles/database'
49
+ require 'dbi/handles/statement'
data/lib/dbi/row.rb ADDED
@@ -0,0 +1,270 @@
1
+ require "delegate"
2
+
3
+ module DBI
4
+ # DBI::Row is the representation of a row in a result set returned by the
5
+ # database.
6
+ #
7
+ # It is responsible for containing and representing the result set, converting
8
+ # it to native Ruby types, and providing methods to sanely move through itself.
9
+ #
10
+ # The DBI::Row class is a delegate of Array, rather than a subclass, because
11
+ # there are times when it should act like an Array, and others when it should
12
+ # act like a Hash (and still others where it should act like String, Regexp,
13
+ # etc). It also needs to store metadata about the row, such as
14
+ # column data type and index information, that users can then access.
15
+ #
16
+ class Row < DelegateClass(Array)
17
+ attr_reader :column_names
18
+
19
+ # DBI::Row.new(columns, column_types, size_or_array=nil)
20
+ #
21
+ # Returns a new DBI::Row object using +columns+. The +size_or_array+
22
+ # argument may either be an Integer or an Array. If it is not provided,
23
+ # it defaults to the length of +columns+.
24
+ #
25
+ # Column types is a corresponding Array of Class/Module objects that
26
+ # conform to the DBI::Type interface. These will be used to convert types
27
+ # as they are returned.
28
+ #
29
+ # DBI::Row is a delegate of the Array class, so all of the Array
30
+ # instance methods are available to your DBI::Row object (keeping in
31
+ # mind that initialize, [], and []= have been explicitly overridden).
32
+ #
33
+ def initialize(columns, column_types, size_or_array=nil, convert_types=true)
34
+ @column_types = column_types
35
+ @convert_types = convert_types
36
+ size_or_array ||= columns.size
37
+
38
+ # The '@column_map' is used to map column names to integer values so
39
+ # that users can reference row values by name or number.
40
+
41
+ @column_map = {}
42
+ @column_names = columns
43
+ columns.each_with_index { |c,i| @column_map[c] = i }
44
+
45
+ case size_or_array
46
+ when Integer
47
+ super(@arr = Array.new(size_or_array))
48
+ when Array
49
+ super(@arr = size_or_array.dup)
50
+ set_values(size_or_array.dup)
51
+ else
52
+ raise TypeError, "parameter must be either Integer or Array"
53
+ end
54
+ end
55
+
56
+ # converts the types in the array to their specified representation
57
+ # from column types provided at construction time.
58
+ def convert_types(arr)
59
+ return arr.dup unless @convert_types
60
+
61
+ if arr.size != @column_types.size
62
+ raise TypeError, "Type mapping is not consistent with result"
63
+ end
64
+ new_arr = []
65
+ arr.each_with_index do |item, i|
66
+ new_arr.push((@column_types[i] || DBI::Type::Varchar).parse(item))
67
+ end
68
+
69
+ return new_arr
70
+ end
71
+
72
+ # Replaces the contents of the internal array with +new_values+.
73
+ # elements are type converted at this time.
74
+ def set_values(new_values)
75
+ @arr.replace(convert_types(new_values))
76
+ end
77
+
78
+ # Yields a column value by name (rather than index), along with the
79
+ # column name itself.
80
+ def each_with_name
81
+ @arr.each_with_index do |v, i|
82
+ yield v, @column_names[i]
83
+ end
84
+ end
85
+
86
+ # returns the underlying array (duplicated)
87
+ def to_a
88
+ @arr.dup
89
+ end
90
+
91
+ # Returns the Row object as a hash, created by #each_with_name.
92
+ def to_h
93
+ hash = {}
94
+ each_with_name{ |v, n| hash[n] = v}
95
+ hash
96
+ end
97
+
98
+ # Create a new row with 'new_values', reusing the field name hash.
99
+ # Initial cloning is done deeply, via Marshal.
100
+ def clone_with(new_values)
101
+ obj = clone
102
+ obj.set_values(new_values)
103
+
104
+ return obj
105
+ end
106
+
107
+ alias field_names column_names
108
+
109
+ # Retrieve a value by index (rather than name).
110
+ #
111
+ # Deprecated. Since Row delegates to Array, just use Row#at.
112
+ def by_index(index)
113
+ @arr[index]
114
+ end
115
+
116
+ # Value of the field named +field_name+ or nil if not found.
117
+ def by_field(field_name)
118
+ begin
119
+ @arr[@column_map[field_name.to_s]]
120
+ rescue TypeError
121
+ nil
122
+ end
123
+ end
124
+
125
+ # Row#[]
126
+ #
127
+ # row[int]
128
+ # row[array]
129
+ # row[regexp]
130
+ # row[arg, arg]
131
+ # row[arg, arg, ...]
132
+ #
133
+ # Sample: Row.new(["first","last","age"], ["Daniel", "Berger", "36"])
134
+ #
135
+ # Retrieves row elements. Exactly what it retrieves depends on the
136
+ # kind and number of arguments used.
137
+ #
138
+ # Zero arguments will raise an ArgumentError.
139
+ #
140
+ # One argument will return a single result. This can be a String,
141
+ # Symbol, Integer, Range or Regexp and the appropriate result will
142
+ # be returned. Strings, Symbols and Regexps act like hash lookups,
143
+ # while Integers and Ranges act like Array index lookups.
144
+ #
145
+ # Two arguments will act like the second form of Array#[], i.e it takes
146
+ # two integers, with the first number the starting point and the second
147
+ # number the length, and returns an array of values.
148
+ #
149
+ # If three or more arguments are provided, an array of results is
150
+ # returned. The behavior for each argument is that of a single argument,
151
+ # i.e. Strings, Symbols, and Regexps act like hash lookups, while
152
+ # Integers and Ranges act like Array index lookups.
153
+ #
154
+ # If no results are found, or an unhandled type is passed, then nil
155
+ # (or a nil element) is returned.
156
+ #
157
+ def [](*args)
158
+ begin
159
+ case args.length
160
+ when 0
161
+ err = "wrong # of arguments(#{args.size} for at least 1)"
162
+ raise ArgumentError, err
163
+ when 1
164
+ case args[0]
165
+ when Array
166
+ args[0].collect { |e| self[e] }
167
+ when Regexp
168
+ self[@column_names.grep(args[0])]
169
+ else
170
+ @arr[conv_param(args[0])]
171
+ end
172
+ # We explicitly check for a length of 2 in order to properly
173
+ # simulate the second form of Array#[].
174
+ when 2
175
+ @arr[conv_param(args[0]), conv_param(args[1])]
176
+ else
177
+ results = []
178
+ args.flatten.each{ |arg|
179
+ case arg
180
+ when Integer
181
+ results.push(@arr[arg])
182
+ when Regexp
183
+ results.push(self[@column_names.grep(arg)])
184
+ else
185
+ results.push(self[conv_param(arg)])
186
+ end
187
+ }
188
+ results.flatten
189
+ end
190
+ rescue TypeError
191
+ nil
192
+ end
193
+ end
194
+
195
+ # Assign a value to a Row object by element. You can assign using
196
+ # a single element reference, or by using a start and length similar
197
+ # to the second form of Array#[]=.
198
+ #
199
+ # row[0] = "kirk"
200
+ # row[:last] = "haines"
201
+ # row[0, 2] = "test"
202
+ #
203
+ def []=(key, value_or_length, obj=nil)
204
+ if obj
205
+ @arr[conv_param(key), conv_param(value_or_length)] = obj
206
+ else
207
+ @arr[conv_param(key)] = value_or_length
208
+ end
209
+ end
210
+
211
+
212
+ if RUBY_VERSION =~ /^1\.9/
213
+ def __getobj__
214
+ @arr
215
+ end
216
+
217
+ def __setobj__(obj)
218
+ @delegate_dc_obj = @arr = obj
219
+ end
220
+ else
221
+ #
222
+ # See Object#clone.
223
+ #
224
+ # #clone and #dup here, however, are both deep copies via Marshal.
225
+ #
226
+ def clone
227
+ Marshal.load(Marshal.dump(self))
228
+ end
229
+
230
+ def dup
231
+ row = self.class.allocate
232
+ row.instance_variable_set :@column_types, @column_types
233
+ row.instance_variable_set :@convert_types, @convert_types
234
+ row.instance_variable_set :@column_map, @column_map
235
+ row.instance_variable_set :@column_names, @column_names
236
+ # this is the only one we actually dup...
237
+ row.instance_variable_set :@arr, arr = @arr.dup
238
+ row.instance_variable_set :@_dc_obj, arr
239
+ row
240
+ end
241
+ end
242
+
243
+ private
244
+
245
+ # Simple helper method to grab the proper value from @column_map
246
+ # NOTE this does something completely different than DBI::Utils::ConvParam
247
+ def conv_param(arg) # :nodoc:
248
+ case arg
249
+ when String, Symbol
250
+ @column_map[arg.to_s]
251
+ when Range
252
+ if arg.first.kind_of?(Symbol) || arg.first.kind_of?(String)
253
+ first = @column_map[arg.first.to_s]
254
+ last = @column_map[arg.last.to_s]
255
+ else
256
+ first = arg.first
257
+ last = arg.last
258
+ end
259
+
260
+ if arg.exclude_end?
261
+ (first...last)
262
+ else
263
+ (first..last)
264
+ end
265
+ else
266
+ arg
267
+ end
268
+ end
269
+ end # class Row
270
+ end # module DBI
@@ -0,0 +1,115 @@
1
+ module DBI
2
+ module SQL
3
+ #
4
+ # The PreparedStatement class attempts to provide binding functionality
5
+ # for database systems that do not have this built-in. This package
6
+ # emulates the whole concept of a statement.
7
+ #
8
+ class PreparedStatement
9
+ attr_accessor :unbound
10
+
11
+ # Convenience method for consumers that just need the tokens
12
+ # method.
13
+ def self.tokens(sql)
14
+ self.new(nil, sql).tokens
15
+ end
16
+
17
+ #
18
+ # "prepare" a statement.
19
+ #
20
+ # +quoter+ is deprecated and will eventually disappear, it is kept
21
+ # currently for compatibility. It is safe to pass nil to this parameter.
22
+ #
23
+ # +sql+ is the statement itself.
24
+ #
25
+ def initialize(quoter, sql)
26
+ @quoter, @sql = quoter, sql
27
+ prepare
28
+ end
29
+
30
+ # Break the sql string into parts.
31
+ #
32
+ # This is NOT a full lexer for SQL. It just breaks up the SQL
33
+ # string enough so that question marks, double question marks and
34
+ # quoted strings are separated. This is used when binding
35
+ # arguments to "?" in the SQL string.
36
+ #
37
+ # C-style (/* */) and Ada-style (--) comments are handled.
38
+ # Note:: Nested C-style comments are NOT handled!
39
+ #
40
+ def tokens
41
+ @sql.scan(%r{
42
+ (
43
+ -- .* (?# matches "--" style comments to the end of line or string )
44
+ | - (?# matches single "-" )
45
+ |
46
+ /[*] .*? [*]/ (?# matches C-style comments )
47
+ | / (?# matches single slash )
48
+ |
49
+ ' ( [^'\\] | '' | \\. )* ' (?# match strings surrounded by apostophes )
50
+ |
51
+ " ( [^"\\] | "" | \\. )* " (?# match strings surrounded by " )
52
+ |
53
+ \?\?? (?# match one or two question marks )
54
+ |
55
+ [^-/'"?]+ (?# match all characters except ' " ? - and / )
56
+
57
+ )}x).collect {|t| t.first}
58
+ end
59
+
60
+ # attempts to bind the arguments in +args+ to this statement.
61
+ # Will raise StandardError if there are any extents issues.
62
+ def bind(args)
63
+ if @arg_index < args.size
64
+ raise "Too many SQL parameters"
65
+ elsif @arg_index > args.size
66
+ raise "Not enough SQL parameters"
67
+ end
68
+
69
+ @unbound.each do |res_pos, arg_pos|
70
+ @result[res_pos] = args[arg_pos]
71
+ end
72
+
73
+ @result.join("")
74
+ end
75
+
76
+ private
77
+
78
+ # prepares the statement for binding. This is done by scanning the
79
+ # statement and itemizing what needs to be bound and what does not.
80
+ #
81
+ # This information will then be used by #bind to appropriately map
82
+ # parameters to the intended destinations.
83
+ def prepare
84
+ @result = []
85
+ @unbound = {}
86
+ pos = 0
87
+ @arg_index = 0
88
+
89
+ tokens.each { |part|
90
+ case part
91
+ when '?'
92
+ @result[pos] = nil
93
+ @unbound[pos] = @arg_index
94
+ pos += 1
95
+ @arg_index += 1
96
+ when '??'
97
+ if @result[pos-1] != nil
98
+ @result[pos-1] << "?"
99
+ else
100
+ @result[pos] = "?"
101
+ pos += 1
102
+ end
103
+ else
104
+ if @result[pos-1] != nil
105
+ @result[pos-1] << part
106
+ else
107
+ @result[pos] = part
108
+ pos += 1
109
+ end
110
+ end
111
+ }
112
+ end
113
+ end # PreparedStatement
114
+ end
115
+ end
data/lib/dbi/sql.rb ADDED
@@ -0,0 +1,22 @@
1
+ #
2
+ # $Id: sql.rb,v 1.3 2006/03/27 20:25:02 francis Exp $
3
+ #
4
+ # parts extracted from Jim Weirichs DBD::Pg
5
+ #
6
+
7
+ require "dbi/utils"
8
+ require "time"
9
+
10
+ module DBI
11
+ # the SQL package contains assistance for DBDs and generally will not be
12
+ # needed outside of them.
13
+ module SQL
14
+ # Helper to determine if the statement is a query. Very crude and
15
+ # should not be relied on for accuracy.
16
+ def self.query?(sql)
17
+ sql =~ /^\s*select\b/i
18
+ end
19
+ end # module SQL
20
+ end # module DBI
21
+
22
+ require 'dbi/sql/preparedstatement'
@@ -0,0 +1,75 @@
1
+ module DBI
2
+ # Constants
3
+
4
+ # Constants for fetch_scroll
5
+ #
6
+ SQL_FETCH_NEXT = 1
7
+ SQL_FETCH_PRIOR = 2
8
+ SQL_FETCH_FIRST = 3
9
+ SQL_FETCH_LAST = 4
10
+ SQL_FETCH_ABSOLUTE = 5
11
+ SQL_FETCH_RELATIVE = 6
12
+
13
+ # SQL type constants
14
+ #
15
+ SQL_CHAR = 1
16
+ SQL_NUMERIC = 2
17
+ SQL_DECIMAL = 3
18
+ SQL_INTEGER = 4
19
+ SQL_SMALLINT = 5
20
+ SQL_FLOAT = 6
21
+ SQL_REAL = 7
22
+ SQL_DOUBLE = 8
23
+ SQL_DATE = 9 # 91
24
+ SQL_TIME = 10 # 92
25
+ SQL_TIMESTAMP = 11 # 93
26
+ SQL_VARCHAR = 12
27
+ SQL_BOOLEAN = 13
28
+
29
+ SQL_LONGVARCHAR = -1
30
+ SQL_BINARY = -2
31
+ SQL_VARBINARY = -3
32
+ SQL_LONGVARBINARY = -4
33
+ SQL_BIGINT = -5
34
+ SQL_TINYINT = -6
35
+ SQL_BIT = -7
36
+
37
+ # TODO
38
+ # Find types for these (XOPEN?)
39
+ #SQL_ARRAY =
40
+ SQL_BLOB = -10 # TODO
41
+ SQL_CLOB = -11 # TODO
42
+ #SQL_DISTINCT =
43
+ #SQL_OBJECT =
44
+ #SQL_NULL =
45
+ SQL_OTHER = 100
46
+ #SQL_REF =
47
+ #SQL_STRUCT =
48
+
49
+ SQL_TYPE_NAMES = {
50
+ SQL_BIT => 'BIT',
51
+ SQL_TINYINT => 'TINYINT',
52
+ SQL_SMALLINT => 'SMALLINT',
53
+ SQL_INTEGER => 'INTEGER',
54
+ SQL_BIGINT => 'BIGINT',
55
+ SQL_FLOAT => 'FLOAT',
56
+ SQL_REAL => 'REAL',
57
+ SQL_DOUBLE => 'DOUBLE',
58
+ SQL_NUMERIC => 'NUMERIC',
59
+ SQL_DECIMAL => 'DECIMAL',
60
+ SQL_CHAR => 'CHAR',
61
+ SQL_VARCHAR => 'VARCHAR',
62
+ SQL_LONGVARCHAR => 'LONG VARCHAR',
63
+ SQL_DATE => 'DATE',
64
+ SQL_TIME => 'TIME',
65
+ SQL_TIMESTAMP => 'TIMESTAMP',
66
+ SQL_BINARY => 'BINARY',
67
+ SQL_VARBINARY => 'VARBINARY',
68
+ SQL_LONGVARBINARY => 'LONG VARBINARY',
69
+ SQL_BLOB => 'BLOB',
70
+ SQL_CLOB => 'CLOB',
71
+ SQL_OTHER => nil,
72
+ SQL_BOOLEAN => 'BOOLEAN',
73
+
74
+ }
75
+ end
data/lib/dbi/trace.rb ADDED
@@ -0,0 +1,91 @@
1
+ # $Id: trace.rb,v 1.1 2006/01/04 02:03:22 francis Exp $
2
+ #
3
+ # Tracing for DBI programs
4
+ #
5
+ # Copyright (c) 2001 Michael Neumann
6
+ #
7
+ # This program is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License
9
+ # as published by the Free Software Foundation; either version 2
10
+ # of the License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
+
21
+ raise LoadError, "the trace module has been removed until it actually works."
22
+
23
+ # works only correct with the newest version > 0.3.3
24
+ require "aspectr"
25
+ require "dbi" # to work as "ruby -r dbi/trace myapp.rb"
26
+
27
+ module DBI
28
+
29
+ class HandleTracer < AspectR::Aspect
30
+
31
+ def initialize(klass)
32
+ @never_wrap = /^__|^send$|^id$|^class$|^$ /
33
+ self.wrap(klass, :pre, :post, methods(klass))
34
+ end
35
+
36
+ # trace methods --------------------------------------------------------------
37
+
38
+ def pre(method, object, exitstatus, *args)
39
+
40
+ par = args.collect{|a| a.inspect}.join(", ")
41
+
42
+ if object.trace_mode == 2 then
43
+ object.trace_output << "-> #{method} for #{object} (#{par})\n"
44
+ elsif object.trace_mode == 3 then
45
+ object.trace_output << "-> #{method} for #{object.inspect} (#{par})\n"
46
+ end
47
+ end
48
+
49
+ def post(method, object, exitstatus, *args)
50
+
51
+ case object.trace_mode
52
+ when 1, 2 # return values and errors
53
+ arrow = object.trace_mode == 1 ? "<=" : "<-"
54
+ if exitstatus.kind_of? Array
55
+ object.trace_output << "#{arrow} #{method} for #{object} = #{exitstatus[0] || 'nil'}\n"
56
+ else
57
+ if exitstatus == true
58
+ object.trace_output << "!! #{$!.message.chomp}\n"
59
+ end
60
+ object.trace_output << "#{arrow} #{method} for #{object}\n"
61
+ end
62
+
63
+ when 3
64
+ if exitstatus.kind_of? Array
65
+ object.trace_output << "<- #{method} for #{object.inspect} = #{exitstatus[0].inspect}\n"
66
+ else
67
+ if exitstatus == true
68
+ object.trace_output << "!! #{$!.inspect}\n"
69
+ end
70
+ object.trace_output << "<- #{method} for #{object.inspect}\n"
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ private # helper methods -----------------------------------------------------
77
+
78
+ def methods(klass)
79
+ meths = (DBI::Handle.instance_methods | klass.instance_methods) - %w(trace_mode trace_output trace)
80
+ /(#{meths.collect{|m| Regexp.quote(m)}.join('|')})/
81
+ end
82
+
83
+ end
84
+
85
+ @@tracer_driver = HandleTracer.new(DBI::DriverHandle)
86
+ @@tracer_database = HandleTracer.new(DBI::DatabaseHandle)
87
+ @@tracer_statement = HandleTracer.new(DBI::StatementHandle)
88
+
89
+
90
+ end # module DBI
91
+