ironruby-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.
@@ -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