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,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
+
@@ -0,0 +1,158 @@
1
+ require 'time'
2
+ require 'bigdecimal'
3
+
4
+ module DBI
5
+ #
6
+ # Interface to convert SQL result sets to native Ruby types.
7
+ #
8
+ # Type is used to convert result sets, which differ from bound variables
9
+ # (which generally go in the opposite direction). For those, see
10
+ # DBI::TypeUtil#convert and DBI::TypeUtil#register_conversion.
11
+ #
12
+ # Type objects have a simple interface: they implement a +parse+ method
13
+ # which takes the result from the DBD and attempts to convert it to the
14
+ # native type. In the event that they do not do this successfully, they are
15
+ # expected to return the object in its original form.
16
+ #
17
+ # As a result, many of the built-in Type classes fallback to simpler forms:
18
+ # Float falls back to Integer, Integer to Varchar, etc. It's questionable
19
+ # at this point if it's desirable to do this, but testing has so far proven
20
+ # it a non-issue.
21
+ #
22
+ # To reiterate, it is *never acceptable* to return +nil+ or some other
23
+ # placeholder when an object will not successfully parse. Return the object
24
+ # handed to you.
25
+ #
26
+ # Types must also handle +nil+ as a result to parse. In this case, the
27
+ # advisable solution is to just let the +nil+ pass through, as it's usually
28
+ # indicative of a SQL NULL result.
29
+ #
30
+ # DBI::Row handles delegation of these objects as a converter for the
31
+ # results. Typically, the type object is a class inferred from
32
+ # DBI::TypeUtil#type_name_to_module ran against the ColumnInfo field
33
+ # +type_name+. However, the the +dbi_type+ field can be used in its place
34
+ # to directly associate a Type object with the column in the DBD, and
35
+ # end-users can leverage StatementHandle#bind_coltype to manually tweak
36
+ # this transformation.
37
+ #
38
+ # As stated before, Type objects are objects. These objects may be Modules
39
+ # or Classes (and typically are), but there is no reason a traditional
40
+ # constructed object with a +parse+ method cannot be returned; in fact, it
41
+ # is used in a few spots to emulate complex types such as PostgreSQL
42
+ # arrays. Look into the +dbi_type+ ColumnInfo field to pass these types
43
+ # around.
44
+ #
45
+ module Type
46
+ #
47
+ # Represents a SQL NULL.
48
+ #
49
+ class Null
50
+ def self.parse(obj)
51
+ return nil if obj.to_s.match(/^null$/i)
52
+ return obj
53
+ end
54
+ end
55
+
56
+ #
57
+ # Represents a SQL char or varchar. General fallback class.
58
+ #
59
+ class Varchar
60
+ def self.parse(obj)
61
+ return obj unless obj
62
+ return obj.to_s if obj.respond_to? :to_s
63
+ return obj.to_str if obj.respond_to? :to_str
64
+ return obj
65
+ end
66
+ end
67
+
68
+ #
69
+ # Represents a whole number. Falls back to Varchar.
70
+ #
71
+ class Integer < Varchar
72
+ def self.parse(obj)
73
+ return nil if Null.parse(obj).nil?
74
+ return obj.to_i if obj.respond_to? :to_i
75
+ super
76
+ end
77
+ end
78
+
79
+ #
80
+ # Represents a decimal number with floating-point precision. Falls back
81
+ # to Integer.
82
+ #
83
+ class Float < Integer
84
+ def self.parse(obj)
85
+ return nil if Null.parse(obj).nil?
86
+ return obj.to_f if obj.respond_to? :to_f
87
+ super
88
+ end
89
+ end
90
+
91
+ #
92
+ # Represents a Decimal with real precision (BigDecimal). Falls back to
93
+ # Float.
94
+ #
95
+ class Decimal < Float
96
+ def self.parse(obj)
97
+ BigDecimal.new(obj) rescue super
98
+ end
99
+ end
100
+
101
+ #
102
+ # Represents a SQL TIMESTAMP and returns DateTime. Falls back to Null.
103
+ #
104
+ class Timestamp < Null
105
+ def self.parse(obj)
106
+ obj = super
107
+ return obj unless obj
108
+ case obj.class
109
+ when ::DateTime
110
+ return obj
111
+ when ::Date
112
+ return ::DateTime.parse(obj.to_s)
113
+ when ::Time
114
+ return ::DateTime.parse(obj.to_s)
115
+ else
116
+ return ::DateTime.parse(obj.to_s) if obj.respond_to? :to_s
117
+ return ::DateTime.parse(obj.to_str) if obj.respond_to? :to_str
118
+ return obj
119
+ end
120
+ end
121
+ end
122
+
123
+ #
124
+ # Represents a SQL BOOLEAN. Returns true/false. Falls back to Null.
125
+ #
126
+ class Boolean < Null
127
+ def self.parse(obj)
128
+ obj = super
129
+
130
+ return nil if obj.nil?
131
+
132
+ if obj == false or obj.kind_of? FalseClass
133
+ return false
134
+ elsif obj.kind_of? TrueClass
135
+ return true
136
+ else
137
+ case obj
138
+ when 't'
139
+ return true
140
+ when 'f'
141
+ return false
142
+ end
143
+
144
+ if obj.respond_to? :to_i
145
+ if obj.to_i == 0
146
+ return false
147
+ else
148
+ return true
149
+ end
150
+ else
151
+ # punt
152
+ return nil
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,108 @@
1
+ module DBI
2
+ #
3
+ # TypeUtil is a series of utility methods for type management.
4
+ #
5
+ class TypeUtil
6
+ @@conversions = { }
7
+
8
+ #
9
+ # Register a conversion for a DBD. This applies to bound parameters for
10
+ # outgoing statements; please look at DBI::Type for result sets.
11
+ #
12
+ # Conversions are given a driver_name, which is then used to look up
13
+ # the conversion to perform on the object. Please see #convert for more
14
+ # information. Driver names are typically provided by the DBD, but may
15
+ # be overridden at any stage temporarily by assigning to the
16
+ # +driver_name+ attribute for the various handles.
17
+ #
18
+ # A conversion block is normally a +case+ statement that identifies
19
+ # various native ruby types and converts them to string, but ultimately
20
+ # the result type is dependent on low-level driver. The resulting
21
+ # object will be fed to the query as the bound value.
22
+ #
23
+ # The result of the block is two arguments, the first being the result
24
+ # object, and the second being a +cascade+ flag, which if true
25
+ # instructs #convert to run the result through the +default+ conversion
26
+ # as well and use its result. This is advantageous when you just need
27
+ # to convert everything to string, and allow +default+ to properly escape
28
+ # it.
29
+ #
30
+ def self.register_conversion(driver_name, &block)
31
+ raise "Must provide a block" unless block_given?
32
+ @@conversions[driver_name] = block
33
+ end
34
+
35
+ #
36
+ # Convert object for +driver_name+. See #register_conversion for a
37
+ # complete explanation of how type conversion is performed.
38
+ #
39
+ # If the conversion is instructed to cascade, it will go to the special
40
+ # "default" conversion, which is a pre-defined common case (and
41
+ # mutable) ruleset for native types. Note that it will use the result
42
+ # from the first conversion, not what was originally passed. Be sure to
43
+ # leave the object untouched if that is your intent. E.g., if your DBD
44
+ # converts an Integer to String and tells it to cascade, the "default"
45
+ # conversion will get a String and quote it, not an Integer (which has
46
+ # different rules).
47
+ #
48
+ def self.convert(driver_name, obj)
49
+ if @@conversions[driver_name]
50
+ newobj, cascade = @@conversions[driver_name].call(obj)
51
+ if cascade
52
+ return @@conversions["default"].call(newobj)
53
+ end
54
+ return newobj
55
+ end
56
+
57
+ return @@conversions["default"].call(obj)
58
+ end
59
+
60
+ #
61
+ # Convenience method to match many SQL named types to DBI::Type classes. If
62
+ # none can be matched, returns DBI::Type::Varchar.
63
+ #
64
+ def self.type_name_to_module(type_name)
65
+ case type_name
66
+ when /^int(?:\d+|eger)?$/i
67
+ DBI::Type::Integer
68
+ when /^varchar$/i, /^character varying$/i
69
+ DBI::Type::Varchar
70
+ when /^(?:float|real)$/i
71
+ DBI::Type::Float
72
+ when /^bool(?:ean)?$/i, /^tinyint$/i
73
+ DBI::Type::Boolean
74
+ when /^time(?:stamp(?:tz)?)?$/i
75
+ DBI::Type::Timestamp
76
+ when /^(?:decimal|numeric)$/i
77
+ DBI::Type::Decimal
78
+ else
79
+ DBI::Type::Varchar
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ DBI::TypeUtil.register_conversion("default") do |obj|
86
+ case obj
87
+ when DBI::Binary # these need to be handled specially by the driver
88
+ obj
89
+ when ::NilClass
90
+ nil
91
+ when ::TrueClass
92
+ "'1'"
93
+ when ::FalseClass
94
+ "'0'"
95
+ when ::Time, ::Date, ::DateTime
96
+ "'#{::DateTime.parse(obj.to_s).strftime("%m/%d/%Y %H:%M:%S")}'"
97
+ when ::String
98
+ obj = obj.gsub(/\\/) { "\\\\" }
99
+ obj = obj.gsub(/'/) { "''" }
100
+ "'#{obj}'"
101
+ when ::BigDecimal
102
+ obj.to_s("F")
103
+ when ::Numeric
104
+ obj.to_s
105
+ else
106
+ "'#{obj.to_s}'"
107
+ end
108
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # $Id: utils.rb,v 1.5 2006/01/29 06:14:19 djberg96 Exp $
3
+ #
4
+
5
+ module DBI
6
+ #
7
+ # Utility classes and methods for use by both DBDs and consumers.
8
+ #
9
+ module Utils
10
+ #
11
+ # Given a block, returns the execution time for the block.
12
+ #
13
+ def self.measure
14
+ start = ::Time.now
15
+ yield
16
+ ::Time.now - start
17
+ end
18
+
19
+ #
20
+ # parse a string of the form "database=xxx;key=val;..."
21
+ # or database:host and return hash of key/value pairs
22
+ #
23
+ # Used in DBI.connect and offspring.
24
+ #
25
+ def self.parse_params(str)
26
+ # improved by John Gorman <jgorman@webbysoft.com>
27
+ params = str.split(";")
28
+ hash = {}
29
+ params.each do |param|
30
+ key, val = param.split("=")
31
+ hash[key] = val if key and val
32
+ end
33
+ if hash.empty?
34
+ database, host = str.split(":")
35
+ hash['database'] = database if database
36
+ hash['host'] = host if host
37
+ end
38
+ hash
39
+ end
40
+ end # module Utils
41
+ end # module DBI
42
+
43
+ #
44
+ # Type converter.
45
+ #
46
+ # FIXME this really needs to go into DBI::TypeUtil or similar
47
+ module DBI::Utils::ConvParam
48
+ #
49
+ # Wrapper to convert arrays of bound objects via DBI::TypeUtil#convert.
50
+ #
51
+ def self.conv_param(driver_name, *params)
52
+ params.collect { |param| DBI::TypeUtil.convert(driver_name, param) }
53
+ end
54
+ end
55
+
56
+ require 'dbi/utils/date'
57
+ require 'dbi/utils/time'
58
+ require 'dbi/utils/timestamp'
59
+ require 'dbi/utils/xmlformatter'
60
+ require 'dbi/utils/tableformatter'
@@ -0,0 +1,59 @@
1
+ module DBI
2
+ #
3
+ # Represents a Date.
4
+ #
5
+ # DEPRECATED: Please use a regular Date or DateTime object.
6
+ #
7
+ class Date
8
+ attr_accessor :year, :month, :day
9
+
10
+ # Aliases
11
+ alias :mon :month
12
+ alias :mon= :month=
13
+ alias :mday :day
14
+ alias :mday= :day=
15
+
16
+ # Returns a new Time object based on the year, month and day or, if a
17
+ # Time object was passed to the constructor, returns that object.
18
+ def to_time
19
+ @original_time || ::Time.local(@year, @month, @day, 0, 0, 0)
20
+ end
21
+
22
+ # Returns a new Date object based on the year, month and day or, if a
23
+ # Date object was passed to the constructor, returns that object.
24
+ def to_date
25
+ @original_date || ::Date.new(@year, @month, @day)
26
+ end
27
+
28
+ # Returns a DBI::Date object as a string in YYYY-MM-DD format.
29
+ def to_s
30
+ sprintf("%04d-%02d-%02d", @year, @month, @day)
31
+ end
32
+
33
+ private
34
+
35
+ # DBI::Date.new(year = 0, month = 0, day = 0)
36
+ # DBI::Date.new(Date)
37
+ # DBI::Date.new(Time)
38
+ #
39
+ # Creates and returns a new DBI::Date object. It's similar to the
40
+ # standard Date class' constructor except that it also accepts a
41
+ # Date or Time object.
42
+ def initialize(year=0, month=0, day=0)
43
+ case year
44
+ when ::Date
45
+ @year, @month, @day = year.year, year.month, year.day
46
+ @original_date = year
47
+ when ::Time
48
+ @year, @month, @day = year.year, year.month, year.day
49
+ @original_time = year
50
+ else
51
+ @year, @month, @day = year, month, day
52
+ end
53
+ end
54
+
55
+ public
56
+
57
+ deprecate :initialize, :public
58
+ end
59
+ end