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,12 @@
1
+ #--
2
+ # Fallback classes for default behavior of DBD driver
3
+ # must be inherited by the DBD driver classes
4
+ #++
5
+ module DBI
6
+ class Base #:nodoc:
7
+ end
8
+ end
9
+
10
+ require 'dbi/base_classes/driver'
11
+ require 'dbi/base_classes/database'
12
+ require 'dbi/base_classes/statement'
@@ -0,0 +1,135 @@
1
+ module DBI
2
+ # Provides the core-level functionality for DatabaseHandles.
3
+ #
4
+ # If the method description says "DBD Required", it's the DBD's
5
+ # responsibility to create this method.
6
+ #
7
+ # Required methods unimplemented by the DBD will raise
8
+ # DBD::NotImplementedError.
9
+ #
10
+ # "DBD Optional" methods are methods that do not have a default
11
+ # implementation but are optional due to the fact that many databases may
12
+ # not support these features (and emulating them would be prohibitive).
13
+ #
14
+ # These methods raise DBI::NotSupportedError.
15
+ #
16
+ # Otherwise, DBI will provide a general alternative which should meet the
17
+ # expectations of the documentation. However, DBDs can override every
18
+ # method in this class.
19
+ #
20
+ class BaseDatabase < Base
21
+ def initialize(handle, attr)
22
+ @handle = handle
23
+ @attr = {}
24
+ attr.each {|k,v| self[k] = v}
25
+ end
26
+
27
+ # Disconnect from the database. DBD Required.
28
+ def disconnect
29
+ raise NotImplementedError
30
+ end
31
+
32
+ # Ping the database to ensure the connection is still alive. Boolean
33
+ # return, true for success. DBD Required.
34
+ def ping
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # Prepare a cached statement, returning a StatementHandle. DBD
39
+ # Required.
40
+ def prepare(statement)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ #
45
+ # Return a map of the columns that exist in the provided table name.
46
+ # DBD Required.
47
+ #
48
+ # The result should be an array of DBI::ColumnInfo objects which have,
49
+ # at minimum, the following fields:
50
+ #
51
+ # * name:: the name of the column.
52
+ # * type:: This is not a field name in itself. You have two options:
53
+ # * type_name:: The name of the type as returned by the database
54
+ # * dbi_type:: A DBI::Type-conforming class that can be used to convert to a native type.
55
+ # * precision:: the precision (generally length) of the column
56
+ # * scale:: the scale (generally a secondary attribute to precision
57
+ # that helps indicate length) of the column
58
+ #
59
+ def columns(table)
60
+ raise NotImplementedError
61
+ end
62
+
63
+ #============================================
64
+ # OPTIONAL
65
+ #============================================
66
+
67
+ # Schedule a commit to the database immediately. DBD Optional.
68
+ def commit
69
+ raise NotSupportedError
70
+ end
71
+
72
+ # Schedule a rollback to the database immediately. DBD Optional.
73
+ def rollback
74
+ raise NotSupportedError
75
+ end
76
+
77
+ # Return the tables available to the database connection.
78
+ #
79
+ # Note:: the basic implementation returns an empty array.
80
+ def tables
81
+ []
82
+ end
83
+
84
+ #
85
+ # Execute a statement with the binds provided. Returns the statement
86
+ # handle unfinished.
87
+ #
88
+ # This is roughly equivalent to:
89
+ #
90
+ # sth = dbh.prepare("my statement")
91
+ # sth.execute(my, bind, vars)
92
+ #
93
+ def execute(statement, *bindvars)
94
+ stmt = prepare(statement)
95
+ stmt.bind_params(*bindvars)
96
+ stmt.execute
97
+ stmt
98
+ end
99
+
100
+ #
101
+ # Execute and complete the statement with the binds provided. Returns
102
+ # the row modified count (via BaseStatement#rows). Finishes the
103
+ # statement handle for you.
104
+ #
105
+ # Roughly equivalent to:
106
+ #
107
+ # sth = dbh.prepare("my statement)
108
+ # sth.execute(my, bind, vars)
109
+ # result = sth.rows
110
+ # sth.finish
111
+ #
112
+ # Returning the value stored in `result`.
113
+ def do(statement, *bindvars)
114
+ stmt = execute(statement, *bindvars)
115
+ res = stmt.rows
116
+ stmt.finish
117
+ return res
118
+ end
119
+
120
+ #
121
+ # Get an attribute from the DatabaseHandle. These are DBD specific and
122
+ # embody things like Auto-Commit support for transactional databases.
123
+ #
124
+ # DBD Authors:: This messes with @attr directly.
125
+ #
126
+ def [](attr)
127
+ @attr[attr]
128
+ end
129
+
130
+ # Set an attribute on the DatabaseHandle. DBD Optional.
131
+ def []=(attr, value)
132
+ raise NotSupportedError
133
+ end
134
+ end # class BaseDatabase
135
+ end
@@ -0,0 +1,47 @@
1
+ module DBI
2
+
3
+ # Implements the basic functionality that constitutes a Driver
4
+ #
5
+ # Drivers do not have a direct interface exposed to the user; these methods
6
+ # are mostly for DBD authors.
7
+ #
8
+ # As with DBI::BaseDatabase, "DBD Required" and "DBD Optional" will be used
9
+ # to explain the same requirements.
10
+ #
11
+ class BaseDriver < Base
12
+ def initialize(dbi_version)
13
+ major, minor = dbi_version.split(".").collect { |x| x.to_i }
14
+ dbi_major, dbi_minor = DBI::VERSION.split(".").collect { |x| x.to_i }
15
+ unless major == dbi_major and minor == dbi_minor
16
+ raise InterfaceError, "Wrong DBD API version used"
17
+ end
18
+ end
19
+
20
+ # Connect to the database. DBD Required.
21
+ def connect(dbname, user, auth, attr)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ # Default u/p information in an array.
26
+ def default_user
27
+ ['', '']
28
+ end
29
+
30
+ # Default attributes to set on the DatabaseHandle.
31
+ def default_attributes
32
+ {}
33
+ end
34
+
35
+ # Return the data sources available to this driver. Returns an empty
36
+ # array per default.
37
+ def data_sources
38
+ []
39
+ end
40
+
41
+ # Disconnect all DatabaseHandles. DBD Required.
42
+ def disconnect_all
43
+ raise NotImplementedError
44
+ end
45
+
46
+ end # class BaseDriver
47
+ end
@@ -0,0 +1,167 @@
1
+ module DBI
2
+ #
3
+ # StatementHandles are used to encapsulate the process of managing a
4
+ # statement (DDL or DML) and its parameters, sending it to the database,
5
+ # and gathering any results from the execution of that statement.
6
+ #
7
+ # As with the other `Base` classes, the terms "DBD Required" and "DBD
8
+ # Optional" are defined in DBI::BaseDatabase.
9
+ #
10
+ class BaseStatement < Base
11
+ def initialize(attr=nil)
12
+ @attr = attr || {}
13
+ end
14
+
15
+ #
16
+ # Bind a parameter to the statement. DBD Required.
17
+ #
18
+ # The parameter number is numeric and indexes starting at 1. This
19
+ # corresponds to the question marks (?) in the statement from the
20
+ # left-most part of the statement moving forward.
21
+ #
22
+ # The value may be any ruby type. How these are handled is
23
+ # DBD-dependent, but the vast majority of DBDs will convert these to
24
+ # string inside the query.
25
+ #
26
+ def bind_param(param, value, attribs)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ #
31
+ # Execute the statement with the known binds. DBD Required.
32
+ #
33
+ def execute
34
+ raise NotImplementedError
35
+ end
36
+
37
+ #
38
+ # Close the statement and any result cursors. DBD Required.
39
+ #
40
+ # Note:: Most implementations will fail miserably if you forget to
41
+ # finish your statement handles.
42
+ def finish
43
+ raise NotImplementedError
44
+ end
45
+
46
+ #
47
+ # Fetch the next row in the result set. DBD Required.
48
+ #
49
+ # DBI::Row is responsible for formatting the data the DBD provides it.
50
+ #
51
+ def fetch
52
+ raise NotImplementedError
53
+ end
54
+
55
+ ##
56
+ # returns result-set column information as array
57
+ # of hashs, where each hash represents one column. See
58
+ # BaseDatabase#columns. DBD Required.
59
+ #
60
+ def column_info
61
+ raise NotImplementedError
62
+ end
63
+
64
+ #============================================
65
+ # OPTIONAL
66
+ #============================================
67
+
68
+ #
69
+ # Take a list of bind variables and bind them successively using bind_param.
70
+ #
71
+ def bind_params(*bindvars)
72
+ bindvars.each_with_index {|val,i| bind_param(i+1, val, nil) }
73
+ self
74
+ end
75
+
76
+ #
77
+ # Cancel any result cursors. DBD Optional, but intentionally does not
78
+ # raise any exception as it's used internally to maintain consistency.
79
+ #
80
+ def cancel
81
+ end
82
+
83
+ #
84
+ # fetch_scroll is provided with a direction and offset and works
85
+ # similar to how seek() is used on files.
86
+ #
87
+ # The constants available for direction are as follows:
88
+ #
89
+ # * SQL_FETCH_NEXT: fetch the next result.
90
+ # * SQL_FETCH_LAST: fetch the last result, period.
91
+ # * SQL_FETCH_RELATIVE: fetch the result at the offset.
92
+ #
93
+ # Other constants can be used, but if this method is not supplied by
94
+ # the driver, they will result in a raise of DBI::NotSupportedError.
95
+ #
96
+
97
+ def fetch_scroll(direction, offset)
98
+ case direction
99
+ when SQL_FETCH_NEXT
100
+ return fetch
101
+ when SQL_FETCH_LAST
102
+ last_row = nil
103
+ while (row=fetch) != nil
104
+ last_row = row
105
+ end
106
+ return last_row
107
+ when SQL_FETCH_RELATIVE
108
+ raise NotSupportedError if offset <= 0
109
+ row = nil
110
+ offset.times { row = fetch; break if row.nil? }
111
+ return row
112
+ else
113
+ raise NotSupportedError
114
+ end
115
+ end
116
+
117
+ #
118
+ # fetch x rows. The result is Array of DBI::Row.
119
+ #
120
+ def fetch_many(cnt)
121
+ rows = []
122
+ cnt.times do
123
+ row = fetch
124
+ break if row.nil?
125
+ rows << row.dup
126
+ end
127
+
128
+ if rows.empty?
129
+ nil
130
+ else
131
+ rows
132
+ end
133
+ end
134
+
135
+ #
136
+ # Fetch all available rows. Result is Array of DBI::Row.
137
+ #
138
+ def fetch_all
139
+ rows = []
140
+ loop do
141
+ row = fetch
142
+ break if row.nil?
143
+ rows << row.dup
144
+ end
145
+
146
+ if rows.empty?
147
+ nil
148
+ else
149
+ rows
150
+ end
151
+ end
152
+
153
+ #
154
+ # Get statement attributes.
155
+ #
156
+ def [](attr)
157
+ @attr[attr]
158
+ end
159
+
160
+ #
161
+ # Set statement attributes. DBD Optional.
162
+ #
163
+ def []=(attr, value)
164
+ raise NotSupportedError
165
+ end
166
+ end # class BaseStatement
167
+ end
@@ -0,0 +1,25 @@
1
+ module DBI
2
+ #
3
+ # Encapsulates the concept of a CLOB/BLOB, which can then be passed as a
4
+ # bind via BaseStatement#bind_param.
5
+ #
6
+ # This is similar to a DBI::Type class and will eventually find its way
7
+ # there.
8
+ #
9
+ # See #new for usage.
10
+ class Binary
11
+ attr_accessor :data
12
+
13
+ # Construct a new DBI::Binary object with the data supplied as string.
14
+ # This object can then be used in bound variables to represent a CLOB
15
+ # or BLOB type.
16
+ def initialize(data)
17
+ @data = data
18
+ end
19
+
20
+ # Return the string representation of the DBI::Binary object.
21
+ def to_s
22
+ @data
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,106 @@
1
+ require 'delegate'
2
+ require 'rubygems'
3
+ begin
4
+ gem 'deprecated'
5
+ rescue LoadError => e
6
+ end
7
+
8
+ require 'deprecated'
9
+
10
+ module DBI
11
+ # This represents metadata for columns within a given table, such as the
12
+ # data type, whether or not the the column is a primary key, etc.
13
+ #
14
+ # ColumnInfo is a delegate of Hash, but represents its keys indifferently,
15
+ # coercing all strings to symbols. It also has ostruct-like features, f.e.:
16
+ #
17
+ # h = ColumnInfo.new({ "foo" => "bar" })
18
+ # h[:foo] => "bar"
19
+ # h["foo"] => "bar"
20
+ # h.foo => "bar"
21
+ #
22
+ # All of these forms have assignment forms as well.
23
+ #
24
+ class ColumnInfo < DelegateClass(Hash)
25
+
26
+ # Create a new ColumnInfo object.
27
+ #
28
+ # If no Hash is provided, one will be created for you. The hash will be
29
+ # shallow cloned for storage inside the object, and an attempt will be
30
+ # made to convert all string keys to symbols.
31
+ #
32
+ # In the event that both string and symbol keys are provided in the
33
+ # initial hash, we cannot safely route around collisions and therefore
34
+ # a TypeError is raised.
35
+ #
36
+ def initialize(hash=nil)
37
+ @hash = hash.dup rescue nil
38
+ @hash ||= Hash.new
39
+
40
+ # coerce all strings to symbols
41
+ @hash.each_key do |x|
42
+ if x.kind_of? String
43
+ sym = x.to_sym
44
+ if @hash.has_key? sym
45
+ raise ::TypeError,
46
+ "#{self.class.name} may construct from a hash keyed with strings or symbols, but not both"
47
+ end
48
+ @hash[sym] = @hash[x]
49
+ @hash.delete(x)
50
+ end
51
+ end
52
+
53
+ super(@hash)
54
+ end
55
+
56
+ def [](key)
57
+ @hash[key.to_sym]
58
+ end
59
+
60
+ def []=(key, value)
61
+ @hash[key.to_sym] = value
62
+ end
63
+
64
+ def default() # :nodoc; XXX hack to get around Hash#default
65
+ method_missing(:default)
66
+ end
67
+
68
+ def method_missing(sym, value=nil)
69
+ if sym.to_s =~ /=$/
70
+ sym = sym.to_s.sub(/=$/, '').to_sym
71
+ @hash[sym] = value
72
+ elsif sym.to_s =~ /\?$/
73
+ sym = sym.to_s.sub(/\?$/, '').to_sym
74
+ @hash[sym]
75
+ else
76
+ @hash[sym]
77
+ end
78
+ end
79
+
80
+ # Aliases - XXX soon to be deprecated
81
+ def self.deprecated_alias(target, source) # :nodoc:
82
+ define_method(target) { |*args| method_missing(source, *args) }
83
+ deprecate target
84
+ end
85
+
86
+ deprecated_alias :is_nullable?, :nullable
87
+ deprecated_alias :can_be_null?, :nullable
88
+
89
+ deprecated_alias :is_indexed?, :indexed
90
+
91
+ deprecated_alias :is_primary?, :primary
92
+
93
+ deprecated_alias :is_unique, :unique
94
+
95
+ deprecated_alias :size, :precision
96
+ deprecated_alias :size=, :precision=
97
+ deprecated_alias :length, :precision
98
+ deprecated_alias :length=, :precision=
99
+
100
+ deprecated_alias :decimal_digits, :scale
101
+ deprecated_alias :decimal_digits=, :scale=
102
+
103
+ deprecated_alias :default_value, :default
104
+ deprecated_alias :default_value=, :default=
105
+ end
106
+ end