ironruby-dbi 0.1.0

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