rails-dbi 0.1.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/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
+