ydbi 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/ChangeLog +3699 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/Rakefile +8 -0
- data/TODO +44 -0
- data/bench/bench.rb +79 -0
- data/bin/dbi +518 -0
- data/bin/test_broken_dbi +37 -0
- data/build/Rakefile.dbi.rb +60 -0
- data/build/rake_task_lib.rb +187 -0
- data/doc/DBD_SPEC.rdoc +88 -0
- data/doc/DBI_SPEC.rdoc +157 -0
- data/doc/homepage/contact.html +62 -0
- data/doc/homepage/development.html +124 -0
- data/doc/homepage/index.html +83 -0
- data/doc/homepage/ruby-dbi.css +91 -0
- data/examples/test1.pl +39 -0
- data/examples/test1.rb +20 -0
- data/examples/xmltest.rb +8 -0
- data/lib/dbd/Mysql.rb +137 -0
- data/lib/dbd/ODBC.rb +89 -0
- data/lib/dbd/Pg.rb +188 -0
- data/lib/dbd/SQLite.rb +97 -0
- data/lib/dbd/SQLite3.rb +124 -0
- data/lib/dbd/mysql/database.rb +405 -0
- data/lib/dbd/mysql/driver.rb +125 -0
- data/lib/dbd/mysql/statement.rb +188 -0
- data/lib/dbd/odbc/database.rb +128 -0
- data/lib/dbd/odbc/driver.rb +38 -0
- data/lib/dbd/odbc/statement.rb +137 -0
- data/lib/dbd/pg/database.rb +516 -0
- data/lib/dbd/pg/exec.rb +47 -0
- data/lib/dbd/pg/statement.rb +160 -0
- data/lib/dbd/pg/tuples.rb +121 -0
- data/lib/dbd/pg/type.rb +209 -0
- data/lib/dbd/sqlite/database.rb +151 -0
- data/lib/dbd/sqlite/statement.rb +125 -0
- data/lib/dbd/sqlite3/database.rb +201 -0
- data/lib/dbd/sqlite3/statement.rb +78 -0
- data/lib/dbi.rb +336 -0
- data/lib/dbi/base_classes.rb +12 -0
- data/lib/dbi/base_classes/database.rb +135 -0
- data/lib/dbi/base_classes/driver.rb +47 -0
- data/lib/dbi/base_classes/statement.rb +171 -0
- data/lib/dbi/binary.rb +25 -0
- data/lib/dbi/columninfo.rb +107 -0
- data/lib/dbi/exceptions.rb +65 -0
- data/lib/dbi/handles.rb +49 -0
- data/lib/dbi/handles/database.rb +241 -0
- data/lib/dbi/handles/driver.rb +60 -0
- data/lib/dbi/handles/statement.rb +408 -0
- data/lib/dbi/row.rb +269 -0
- data/lib/dbi/sql.rb +22 -0
- data/lib/dbi/sql/preparedstatement.rb +115 -0
- data/lib/dbi/sql_type_constants.rb +75 -0
- data/lib/dbi/trace.rb +91 -0
- data/lib/dbi/types.rb +218 -0
- data/lib/dbi/typeutil.rb +109 -0
- data/lib/dbi/utils.rb +60 -0
- data/lib/dbi/utils/date.rb +59 -0
- data/lib/dbi/utils/tableformatter.rb +112 -0
- data/lib/dbi/utils/time.rb +52 -0
- data/lib/dbi/utils/timestamp.rb +96 -0
- data/lib/dbi/utils/xmlformatter.rb +73 -0
- data/lib/dbi/version.rb +3 -0
- data/prototypes/types2.rb +237 -0
- data/readme.md +274 -0
- data/setup.rb +1585 -0
- data/test/DBD_TESTS +50 -0
- data/test/TESTING +16 -0
- data/test/dbd/general/test_database.rb +206 -0
- data/test/dbd/general/test_statement.rb +326 -0
- data/test/dbd/general/test_types.rb +296 -0
- data/test/dbd/mysql/base.rb +26 -0
- data/test/dbd/mysql/down.sql +19 -0
- data/test/dbd/mysql/test_blob.rb +18 -0
- data/test/dbd/mysql/test_new_methods.rb +7 -0
- data/test/dbd/mysql/test_patches.rb +111 -0
- data/test/dbd/mysql/up.sql +28 -0
- data/test/dbd/odbc/base.rb +30 -0
- data/test/dbd/odbc/down.sql +19 -0
- data/test/dbd/odbc/test_new_methods.rb +12 -0
- data/test/dbd/odbc/test_ping.rb +10 -0
- data/test/dbd/odbc/test_statement.rb +44 -0
- data/test/dbd/odbc/test_transactions.rb +58 -0
- data/test/dbd/odbc/up.sql +33 -0
- data/test/dbd/postgresql/base.rb +31 -0
- data/test/dbd/postgresql/down.sql +31 -0
- data/test/dbd/postgresql/test_arrays.rb +179 -0
- data/test/dbd/postgresql/test_async.rb +121 -0
- data/test/dbd/postgresql/test_blob.rb +36 -0
- data/test/dbd/postgresql/test_bytea.rb +87 -0
- data/test/dbd/postgresql/test_ping.rb +10 -0
- data/test/dbd/postgresql/test_timestamp.rb +77 -0
- data/test/dbd/postgresql/test_transactions.rb +58 -0
- data/test/dbd/postgresql/testdbipg.rb +307 -0
- data/test/dbd/postgresql/up.sql +60 -0
- data/test/dbd/sqlite/base.rb +32 -0
- data/test/dbd/sqlite/test_database.rb +30 -0
- data/test/dbd/sqlite/test_driver.rb +68 -0
- data/test/dbd/sqlite/test_statement.rb +112 -0
- data/test/dbd/sqlite/up.sql +25 -0
- data/test/dbd/sqlite3/base.rb +32 -0
- data/test/dbd/sqlite3/test_database.rb +77 -0
- data/test/dbd/sqlite3/test_driver.rb +67 -0
- data/test/dbd/sqlite3/test_statement.rb +88 -0
- data/test/dbd/sqlite3/up.sql +33 -0
- data/test/dbi/tc_columninfo.rb +94 -0
- data/test/dbi/tc_date.rb +88 -0
- data/test/dbi/tc_dbi.rb +184 -0
- data/test/dbi/tc_row.rb +256 -0
- data/test/dbi/tc_sqlbind.rb +168 -0
- data/test/dbi/tc_statementhandle.rb +29 -0
- data/test/dbi/tc_time.rb +77 -0
- data/test/dbi/tc_timestamp.rb +142 -0
- data/test/dbi/tc_types.rb +268 -0
- data/test/ts_dbd.rb +131 -0
- data/test/ts_dbi.rb +16 -0
- data/ydbi.gemspec +24 -0
- metadata +224 -0
data/bin/test_broken_dbi
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
load_path = false
|
4
|
+
gems = false
|
5
|
+
redefined = false
|
6
|
+
|
7
|
+
# these clauses are for installations which have RUBYOPT=-rubygems, etc.
|
8
|
+
if Object.const_defined? "Gem"
|
9
|
+
redefined = true
|
10
|
+
module Kernel
|
11
|
+
alias gem_require require
|
12
|
+
alias require gem_original_require
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'dbi'
|
18
|
+
load_path = true
|
19
|
+
rescue LoadError => e
|
20
|
+
end
|
21
|
+
|
22
|
+
if Object.const_defined? "Gem" and redefined
|
23
|
+
module Kernel
|
24
|
+
alias gem_original_require require
|
25
|
+
alias require gem_require
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rubygems'
|
31
|
+
gem 'dbi'
|
32
|
+
gems = true
|
33
|
+
rescue LoadError
|
34
|
+
rescue Gem::LoadError
|
35
|
+
end
|
36
|
+
|
37
|
+
puts "Your installation of DBI is broken" if gems and load_path
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rake_task_lib'
|
2
|
+
require 'dbi'
|
3
|
+
|
4
|
+
DBD_PACKAGES = Dir['lib/dbd/*.rb'].collect { |x| File.basename(x, '.rb') }
|
5
|
+
|
6
|
+
# creates a number of tasks like dbi:task_name, dbd_mysql:task_name, so on.
|
7
|
+
# Builds these out into an array that can be used as a prereq for other tasks.
|
8
|
+
def map_task(task_name)
|
9
|
+
namespaces = (['dbi'] + DBD_PACKAGES.collect { |x| dbd_namespace(x) }).flatten
|
10
|
+
namespaces.collect { |x| [x, task_name].join(":") }
|
11
|
+
end
|
12
|
+
|
13
|
+
task :package => (map_task("package") + map_task("gem"))
|
14
|
+
task :clobber_package => map_task("clobber_package")
|
15
|
+
|
16
|
+
desc 'Run interface tests (no database connectivity required)'
|
17
|
+
task :test_dbi do
|
18
|
+
ruby("test/ts_dbi.rb")
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Run database-specific tests'
|
22
|
+
task :test_dbd do
|
23
|
+
ruby("test/ts_dbd.rb")
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Run full test suite'
|
27
|
+
task :test => [:test_dbi, :test_dbd]
|
28
|
+
|
29
|
+
build_dbi_tasks
|
30
|
+
|
31
|
+
#
|
32
|
+
# There's probably a better way to do this, but here's a boilerplate spec that we dup and modify.
|
33
|
+
#
|
34
|
+
|
35
|
+
task :dbi => DEFAULT_TASKS.collect { |x| "dbi:#{x.to_s}" }
|
36
|
+
|
37
|
+
namespace :dbi do
|
38
|
+
code_files = %w(examples/**/* bin/dbi build/Rakefile.dbi.rb lib/dbi.rb lib/dbi/**/*.rb test/ts_dbi.rb test/dbi/*)
|
39
|
+
|
40
|
+
spec = boilerplate_spec
|
41
|
+
spec.name = 'dbi'
|
42
|
+
spec.version = DBI::VERSION
|
43
|
+
spec.test_file = 'test/ts_dbi.rb'
|
44
|
+
spec.executables = ['dbi', 'test_broken_dbi']
|
45
|
+
spec.files = gem_files(code_files)
|
46
|
+
spec.summary = 'A vendor independent interface for accessing databases, similar to Perl\'s DBI'
|
47
|
+
spec.description = 'A vendor independent interface for accessing databases, similar to Perl\'s DBI'
|
48
|
+
spec.add_dependency 'deprecated', '= 2.0.1'
|
49
|
+
|
50
|
+
build_package_tasks(spec, code_files)
|
51
|
+
end
|
52
|
+
|
53
|
+
DBD_PACKAGES.each do |dbd|
|
54
|
+
my_namespace = dbd_namespace(dbd)
|
55
|
+
|
56
|
+
task my_namespace => DEFAULT_TASKS.collect { |x| "#{my_namespace}:#{x.to_s}" }
|
57
|
+
namespace my_namespace do
|
58
|
+
build_dbd_tasks(dbd)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
$:.unshift 'lib'
|
2
|
+
require 'rake'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rdoc/task'
|
7
|
+
|
8
|
+
|
9
|
+
DEFAULT_TASKS = [:clobber_package, :package, :gem]
|
10
|
+
|
11
|
+
DBD_GEM_DEP_MAP = {
|
12
|
+
'pg' => 'pg',
|
13
|
+
'mysql' => 'mysql',
|
14
|
+
'sqlite' => 'sqlite-ruby',
|
15
|
+
'sqlite3' => 'sqlite3-ruby'
|
16
|
+
}
|
17
|
+
|
18
|
+
#
|
19
|
+
# Packaging
|
20
|
+
#
|
21
|
+
|
22
|
+
PACKAGE_FILES = %w(Rakefile build/rake_task_lib.rb setup.rb)
|
23
|
+
DOC_FILES = %w(readme.md LICENSE ChangeLog)
|
24
|
+
EXCLUSIONS = %w(test/sql.log)
|
25
|
+
DBD_FILES = %w(test/DBD_TESTS)
|
26
|
+
|
27
|
+
#
|
28
|
+
# some inlines
|
29
|
+
#
|
30
|
+
|
31
|
+
def gem_files(code_files)
|
32
|
+
(code_files + DOC_FILES).collect { |x| Dir[x] }.reject { |x| EXCLUSIONS.include? x }.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
def package_files(code_files)
|
36
|
+
code_files + DOC_FILES + PACKAGE_FILES
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_package_tasks(spec, code_files)
|
40
|
+
Gem::PackageTask.new(spec) do |s|
|
41
|
+
end
|
42
|
+
|
43
|
+
Rake::PackageTask.new(spec.name, spec.version) do |p|
|
44
|
+
p.need_tar_gz = true
|
45
|
+
p.need_zip = true
|
46
|
+
|
47
|
+
package_files(code_files).each do |x|
|
48
|
+
p.package_files.include(x)
|
49
|
+
end
|
50
|
+
|
51
|
+
EXCLUSIONS.each do |x|
|
52
|
+
p.package_files.exclude(x)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def boilerplate_spec
|
58
|
+
gem = Gem::Specification.new
|
59
|
+
gem.authors = ['Erik Hollensbe', 'Christopher Maujean']
|
60
|
+
gem.email = 'ruby-dbi-users@rubyforge.org'
|
61
|
+
gem.homepage = 'http://www.rubyforge.org/projects/ruby-dbi'
|
62
|
+
gem.platform = Gem::Platform::RUBY
|
63
|
+
gem.has_rdoc = true
|
64
|
+
gem.extra_rdoc_files = DOC_FILES
|
65
|
+
gem.required_ruby_version = '>= 1.8.0'
|
66
|
+
gem.rubyforge_project = 'ruby-dbi'
|
67
|
+
return gem
|
68
|
+
end
|
69
|
+
|
70
|
+
# builds a dbd namespace from the DBD_PACKAGES hash
|
71
|
+
def dbd_namespace(dbd)
|
72
|
+
"dbd-" + dbd.to_s.downcase
|
73
|
+
end
|
74
|
+
|
75
|
+
def dbd_code_files(dbd)
|
76
|
+
code_files = [
|
77
|
+
"test/dbd/general/**",
|
78
|
+
File.join("test", "dbd", dbd.downcase == "pg" ? "postgresql" : dbd.downcase, "*"),
|
79
|
+
File.join("lib", "dbd", dbd + ".rb"),
|
80
|
+
"lib/dbd/#{dbd.downcase}/*.rb",
|
81
|
+
] + DBD_FILES
|
82
|
+
end
|
83
|
+
|
84
|
+
def dbd_gem_files(code_files)
|
85
|
+
DBD_FILES + gem_files(code_files)
|
86
|
+
end
|
87
|
+
|
88
|
+
def dbd_package_files(code_files)
|
89
|
+
DBD_FILES + package_files(code_files)
|
90
|
+
end
|
91
|
+
|
92
|
+
def dbd_gem_spec(dbd, dbd_const, code_files)
|
93
|
+
spec = boilerplate_spec
|
94
|
+
spec.name = dbd_namespace(dbd)
|
95
|
+
spec.version = dbd_version(dbd_const)
|
96
|
+
spec.test_file = 'test/ts_dbd.rb'
|
97
|
+
spec.files = gem_files(code_files)
|
98
|
+
spec.summary = dbd_description(dbd_const)
|
99
|
+
spec.description = dbd_description(dbd_const)
|
100
|
+
spec.add_dependency 'dbi', '>= 0.4.0'
|
101
|
+
|
102
|
+
dcdbd = dbd.downcase
|
103
|
+
|
104
|
+
if DBD_GEM_DEP_MAP[dcdbd]
|
105
|
+
spec.add_dependency DBD_GEM_DEP_MAP[dcdbd]
|
106
|
+
end
|
107
|
+
|
108
|
+
return spec
|
109
|
+
end
|
110
|
+
|
111
|
+
def dbd_version(const)
|
112
|
+
DBI::DBD.const_get(const).const_get("VERSION")
|
113
|
+
end
|
114
|
+
|
115
|
+
def dbd_description(const)
|
116
|
+
DBI::DBD.const_get(const).const_get("DESCRIPTION")
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def build_dbd_tasks(dbd)
|
121
|
+
task :default => DEFAULT_TASKS
|
122
|
+
|
123
|
+
begin
|
124
|
+
done = false
|
125
|
+
dbd_const = nil
|
126
|
+
Dir["lib/dbd/*.rb"].each do |dbd_file|
|
127
|
+
if File.basename(dbd_file.downcase, '.rb') == dbd.to_s.downcase
|
128
|
+
dbd_const = File.basename(dbd_file, '.rb')
|
129
|
+
require "dbd/#{dbd_const}"
|
130
|
+
done = true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
abort "No DBD found even though we asked to make tasks for it" unless done
|
135
|
+
|
136
|
+
code_files = dbd_code_files(dbd_const)
|
137
|
+
|
138
|
+
spec = dbd_gem_spec(dbd, dbd_const, code_files)
|
139
|
+
|
140
|
+
build_package_tasks(spec, code_files)
|
141
|
+
|
142
|
+
# FIXME: convert to a rake_test_loader sooner or later
|
143
|
+
task :test do
|
144
|
+
ENV["DBTYPES"] = dbd
|
145
|
+
ruby "test/ts_dbd.rb"
|
146
|
+
end
|
147
|
+
rescue LoadError => e
|
148
|
+
DEFAULT_TASKS.each do |x|
|
149
|
+
task x do
|
150
|
+
end
|
151
|
+
end
|
152
|
+
warn "Skipping #{dbd_namespace(dbd)} because we can't require DBD"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def build_dbi_tasks
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# basic tasks
|
161
|
+
#
|
162
|
+
|
163
|
+
task :dist => [:distclean, :package, :rdoc]
|
164
|
+
task :distclean => [:clobber_package, :clobber_rdoc]
|
165
|
+
task :clean => [:distclean]
|
166
|
+
task :default => [:test, :dist]
|
167
|
+
|
168
|
+
task :to_blog => [:clobber_rdoc, :rdoc] do
|
169
|
+
sh "rm -r $git/blog/content/docs/ruby-dbi && mv rdoc $git/blog/content/docs/ruby-dbi"
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Documentation
|
174
|
+
#
|
175
|
+
|
176
|
+
RDoc::Task.new do |rd|
|
177
|
+
rd.rdoc_dir = "rdoc"
|
178
|
+
rd.main = "README"
|
179
|
+
rd.rdoc_files.include("./README")
|
180
|
+
rd.rdoc_files.include("./ChangeLog")
|
181
|
+
rd.rdoc_files.include("./LICENSE")
|
182
|
+
rd.rdoc_files.include("./doc/**/*.rdoc")
|
183
|
+
rd.rdoc_files.include("./lib/**/*.rb")
|
184
|
+
rd.rdoc_files.include("./ext/**/*.c")
|
185
|
+
rd.options = %w(-a)
|
186
|
+
end
|
187
|
+
|
data/doc/DBD_SPEC.rdoc
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
= DBD Specification Version 0.4.0
|
2
|
+
By Erik Hollensbe <erik@hollensbe.org>
|
3
|
+
|
4
|
+
== FOREWORD
|
5
|
+
|
6
|
+
DBI is still in a large state of flux. Previous versions of this
|
7
|
+
specification rarely reflected reality, and the 0.4.0 release is an
|
8
|
+
attempt to get the code and documentation in touch with each other to
|
9
|
+
better reflect said reality.
|
10
|
+
|
11
|
+
While this is a goal, nobody's perfect and there is still a lot of
|
12
|
+
code to check, sanitize, and otherwise clean up. If you find something
|
13
|
+
missing in these specifications while working on a new DBD or a patch
|
14
|
+
for DBI, please, do not do what everything else is doing; alert the
|
15
|
+
appropriate person to get the spec revised. Doing this will save
|
16
|
+
yourself (and the DBI authors) infinite amounts of time.
|
17
|
+
|
18
|
+
== WHAT A DBD IS
|
19
|
+
|
20
|
+
DBD stands for "DataBase Driver" and is the layer that DBI uses to interface
|
21
|
+
with the database. DBDs often employ a low level driver to do the real work
|
22
|
+
with the database, leaving the DBD itself to act as a medium between DBI and
|
23
|
+
that lower level API.
|
24
|
+
|
25
|
+
This allows a great deal of flexibility without having to sacrifice features
|
26
|
+
for compatibility. For example, instead of having one PostgreSQL DBD that
|
27
|
+
handles all version of PostgreSQL and attempts to limit it's functionality
|
28
|
+
based on what version it detects (a error-prone and time/design prohibitive
|
29
|
+
process), one can write two PostgreSQL DBD that handle the differences between
|
30
|
+
"new" and "old" postgres, all while talking to the same low-level driver (yet
|
31
|
+
leveraging different functionality). This method leads to cleaner internals and
|
32
|
+
puts the choice of which to use on the end-user, who is probably a lot more
|
33
|
+
informed about what features they want than your code.
|
34
|
+
|
35
|
+
One traditionally loads a DBD using the DBI.connect method (see DBD LOAD
|
36
|
+
PATHS below) which will attempt to load the DBD, connect to the database with
|
37
|
+
the arguments supplied and return a DatabaseHandle if successful. However, if
|
38
|
+
the DBD is written properly, requiring it directly without DBI's involvement
|
39
|
+
(or existence) should not be an issue.
|
40
|
+
|
41
|
+
== WHERE DBDs LIVE
|
42
|
+
|
43
|
+
DBDs have an expected require path to be loaded by DBI. DBI will attempt to
|
44
|
+
load the middle portion of the DBI.connect DSN provided.
|
45
|
+
|
46
|
+
Example: DBI.connect("dbi:Mysql:mydb") # requires 'dbd/Mysql'
|
47
|
+
|
48
|
+
Since rubygems potentially renders this path virtual, it is not OK to expect
|
49
|
+
this path physically exists in one spot on the filesystem. Many assuptions are
|
50
|
+
currently made about this and will be pruned in 0.6.0.
|
51
|
+
|
52
|
+
If you wish to create submodules for your DBD, create a directory in the 'dbd'
|
53
|
+
directory named the same as the DBD. (E.g., "dbd/Mysql" would have a directory
|
54
|
+
with files in it relating to the Mysql.rb file that DBI loads).
|
55
|
+
|
56
|
+
== HOW DBI INTERFACES WITH YOUR DBD
|
57
|
+
|
58
|
+
Your DBD will create classes representing a DBI::BaseDriver, DBI::BaseDatabase,
|
59
|
+
and DBI::BaseStatement. DBI will link these to DBI::DriverHandle,
|
60
|
+
DBI::DatabaseHandle, and DBI::StatementHandle respectively. Your classes will
|
61
|
+
be called by the Handle classes to retreive information to manipulate and send
|
62
|
+
to the user. This manipulation can be influenced in a number of ways.
|
63
|
+
|
64
|
+
It is strongly recommended you make the effort to read the RDoc for all six
|
65
|
+
of these classes, as they are the meat of this specification, not this
|
66
|
+
document.
|
67
|
+
|
68
|
+
== BUILDING A DBD FROM SCRATCH
|
69
|
+
|
70
|
+
For the purposes of this discussion, we'll call your driver 'Foo'.
|
71
|
+
|
72
|
+
Create your module, DBI::DBD::Foo. Store it somewhere in your load path under
|
73
|
+
dbd/Foo.rb.
|
74
|
+
|
75
|
+
Create classes Driver, Database, and Statement in this new namespace, which
|
76
|
+
inherit from DBI::BaseDriver, DBI::BaseDatabase, and DBI::BaseStatement.
|
77
|
+
Override (at mininum) the methods that return NotImplementedError in your new
|
78
|
+
classes.
|
79
|
+
|
80
|
+
Create a method in the root namespace named +driver_name+. This should return a
|
81
|
+
string with a short name for your driver, this key will be used in type
|
82
|
+
conversion.
|
83
|
+
|
84
|
+
Everything else is up to you, up to and including worrying about interacting
|
85
|
+
with the database.
|
86
|
+
|
87
|
+
At this point, you should be ready to test your driver. See test/DBD_TESTS for
|
88
|
+
information on how to configure that.
|
data/doc/DBI_SPEC.rdoc
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
= DBI Interface Spec, for version 0.4.0
|
2
|
+
|
3
|
+
by Erik Hollensbe <erik@hollensbe.org>
|
4
|
+
|
5
|
+
== Foreword
|
6
|
+
|
7
|
+
DBI is still in a large state of flux. Previous versions of this
|
8
|
+
specification rarely reflected reality, and the 0.4.0 release is an
|
9
|
+
attempt to get the code and documentation more in sync.
|
10
|
+
|
11
|
+
While this is the goal, there is still a lot of
|
12
|
+
code to check, sanitize, and otherwise clean up. If you find something
|
13
|
+
missing in these specifications while working on a new DBD or a patch
|
14
|
+
for DBI, please bring it to our attention (in IRC or on the mailing list)
|
15
|
+
to get the spec revised. Doing this will save
|
16
|
+
yourself (and the DBI authors) a lot of time.
|
17
|
+
|
18
|
+
== Design
|
19
|
+
|
20
|
+
With DBI, there are the concepts of driver, database, and statement. The core
|
21
|
+
functionality for these concepts is provided by a database driver, or
|
22
|
+
DBD. DBI controls one or more drivers at once, a driver has databases, a
|
23
|
+
database may have statements.
|
24
|
+
|
25
|
+
DBI uses a delegation model to communicate with its DBDs through a
|
26
|
+
series of handles. When a connection to a database is requested, DBI contacts
|
27
|
+
the appropriate DBD and builds a handle in
|
28
|
+
its name that it aligns with a DBI base class for that concept. The
|
29
|
+
handle provided by the DBD is the first-class method of communication,
|
30
|
+
otherwise it resorts to calling the base class methods. This allows
|
31
|
+
DBI to provide a level of consistency unless the DBD author finds it
|
32
|
+
otherwise unnecessary.
|
33
|
+
|
34
|
+
For example: DBI will provide handy methods like fetch_all and
|
35
|
+
fetch_scroll which all leverage the fetch method in the base class,
|
36
|
+
and the fetch method must be implemented by the DBD. However, the DBD
|
37
|
+
may have an internal representation of fetch_scroll (as is the case
|
38
|
+
with the ODBC driver) that may be more suited to direct use, and
|
39
|
+
therefore DBI will never see the base class method. This is similar to
|
40
|
+
inheritance, but there is a distinct disconnect between the handles
|
41
|
+
and the base classes, intentionally so. This way the DBDs have no
|
42
|
+
access to the base class and DBI does all the delegation work. Also,
|
43
|
+
DBI has no idea what the DBD is doing underneath, nor does it need to
|
44
|
+
care as long as valid data is returned.
|
45
|
+
|
46
|
+
== Classes
|
47
|
+
|
48
|
+
These are the classes that make up the core of DBI and provide
|
49
|
+
various functionality:
|
50
|
+
|
51
|
+
=== DBI
|
52
|
+
Core module, responsible for everything underneath it, kickstarting
|
53
|
+
connections and loading drivers.
|
54
|
+
|
55
|
+
=== DBI::Row
|
56
|
+
|
57
|
+
Responsible for representing the result set and managing the type
|
58
|
+
conversion of the result set.
|
59
|
+
|
60
|
+
=== DBI::Utils
|
61
|
+
|
62
|
+
Utility methods which propogate through the rest of DBI.
|
63
|
+
|
64
|
+
=== DBI::SQL
|
65
|
+
|
66
|
+
Utility methods for working with SQL queries. Includes a
|
67
|
+
driver-independent SQL bind manager.
|
68
|
+
|
69
|
+
=== DBI::ColumnInfo
|
70
|
+
|
71
|
+
Responsible for representing the information per column for both
|
72
|
+
queries and table descriptions.
|
73
|
+
|
74
|
+
=== DBI::Type
|
75
|
+
|
76
|
+
Namespace for typecasting classes. These classes are provided with a
|
77
|
+
parse method which converts them to a native Ruby type from a string.
|
78
|
+
|
79
|
+
=== DBI::TypeUtil
|
80
|
+
|
81
|
+
The inverse of DBI::Type, this provides functionality to turn native
|
82
|
+
Ruby types into a representation suitable for the DBD's queries.
|
83
|
+
|
84
|
+
=== DBI::Binary
|
85
|
+
|
86
|
+
The representation of a BLOB/CLOB in a Ruby object. This will
|
87
|
+
eventually be rolled into DBI::Type::, but remains here currently for
|
88
|
+
compatibility purposes.
|
89
|
+
|
90
|
+
=== DBI::Base* and DBI::*Handle
|
91
|
+
|
92
|
+
Please see the Design section above for the description of these modules.
|
93
|
+
|
94
|
+
== Exceptions
|
95
|
+
|
96
|
+
DBI has a slew of custom exceptions it uses to control program flow,
|
97
|
+
and alert the user to specific classes of problems.
|
98
|
+
|
99
|
+
They currently all live in the DBI namespace, although it's expected
|
100
|
+
that there will eventually be an exception namespace.
|
101
|
+
|
102
|
+
=== DBI::Warning < RuntimeError
|
103
|
+
For important warnings such as data truncation, etc.
|
104
|
+
|
105
|
+
=== DBI::Error < RuntimeError
|
106
|
+
Base class of all other error exceptions.
|
107
|
+
Rescue this to rescue all DBI errors.
|
108
|
+
|
109
|
+
=== DBI::InterfaceError < DBI::Error
|
110
|
+
Exception for errors related to the DBI interface rather
|
111
|
+
than the database itself.
|
112
|
+
|
113
|
+
=== DBI::NotImplementedError < DBI::InterfaceError
|
114
|
+
Exception raised if the DBD driver has not specified
|
115
|
+
a mandatory method.
|
116
|
+
|
117
|
+
=== DBI::DatabaseError < DBI::Error
|
118
|
+
Exception for errors related to the database.
|
119
|
+
|
120
|
+
Has three attributes: ((|err|)), ((|errstr|)) and ((|state|)).
|
121
|
+
|
122
|
+
=== DBI::DataError < DBI::DatabaseError
|
123
|
+
Exception for errors due to problems with the processed
|
124
|
+
data, such as division by zero, numeric value out of range, etc.
|
125
|
+
|
126
|
+
=== DBI::OperationalError < DBI::DatabaseError
|
127
|
+
Exception for errors related to the database's operation which
|
128
|
+
are not necessarily under the control of the programmer. This would include
|
129
|
+
such things as unexpected disconnection, failure to find a datasource name,
|
130
|
+
failure to process a transaction, memory allocation errors, etc.
|
131
|
+
|
132
|
+
=== DBI::IntegrityError < DBI::DatabaseError
|
133
|
+
Exception raised when the relational integrity of the database
|
134
|
+
is affected, such as when a foreign key constraint is violated.
|
135
|
+
|
136
|
+
=== DBI::InternalError < DBI::DatabaseError
|
137
|
+
Exception raised when the database encounters an internal error,
|
138
|
+
such as a cursor not being valid anymore, or a transaction going out of
|
139
|
+
sync.
|
140
|
+
|
141
|
+
=== DBI::ProgrammingError < DBI::DatabaseError
|
142
|
+
Exception raised for programming errors, e.g., table not found
|
143
|
+
or already exists, syntax error in SQL statement, wrong number
|
144
|
+
of parameters specified, etc.
|
145
|
+
|
146
|
+
=== DBI::NotSupportedError < DBI::DatabaseError
|
147
|
+
Raised if, e.g., ((<commit>)) is called for a database that does not
|
148
|
+
support transactions.
|
149
|
+
|
150
|
+
== API
|
151
|
+
|
152
|
+
To save my sanity, I have joined the specification and the rdoc for
|
153
|
+
DBI. Please review the specification there.
|
154
|
+
|
155
|
+
If you wish to author your own DBD, please see DBD_SPEC.rdoc,
|
156
|
+
which is a more in-depth view of the communication between DBI and
|
157
|
+
DBDs.
|