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,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