epugh-sequel 0.0.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.
- data/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
data/VERSION.yml
ADDED
data/bin/sequel
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require 'sequel'
|
|
6
|
+
|
|
7
|
+
db_opts = {}
|
|
8
|
+
echo = nil
|
|
9
|
+
env = nil
|
|
10
|
+
logfile = nil
|
|
11
|
+
migrate_dir = nil
|
|
12
|
+
migrate_ver = nil
|
|
13
|
+
load_dir = nil
|
|
14
|
+
|
|
15
|
+
opts = OptionParser.new do |opts|
|
|
16
|
+
opts.banner = "Sequel: The Database Toolkit for Ruby"
|
|
17
|
+
opts.define_head "Usage: sequel <uri|path> [options]"
|
|
18
|
+
opts.separator ""
|
|
19
|
+
opts.separator "Examples:"
|
|
20
|
+
opts.separator " sequel sqlite://blog.db"
|
|
21
|
+
opts.separator " sequel postgres://localhost/my_blog"
|
|
22
|
+
opts.separator " sequel config/database.yml"
|
|
23
|
+
opts.separator ""
|
|
24
|
+
opts.separator "For more information see http://sequel.rubyforge.org"
|
|
25
|
+
opts.separator ""
|
|
26
|
+
opts.separator "Options:"
|
|
27
|
+
|
|
28
|
+
opts.on_tail("-?", "--help", "Show this message") do
|
|
29
|
+
puts opts
|
|
30
|
+
exit
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on("-e", "--env ENV", "use environment config for database") do |v|
|
|
34
|
+
env = v
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opts.on("-E", "--echo", "echo SQL statements") do
|
|
38
|
+
echo = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
|
|
42
|
+
logfile = v
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
opts.on("-L", "--load-dir DIR", "loads all *.rb from specifed directory") do |v|
|
|
46
|
+
load_dir = v
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts.on("-m", "--migrate-directory DIR", "run the migrations in directory") do |v|
|
|
50
|
+
migrate_dir = v
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on("-M", "--migrate-version VER", "migrate the database to version given") do |v|
|
|
54
|
+
migrate_ver = Integer(v)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
opts.on_tail("-v", "--version", "Show version") do
|
|
58
|
+
puts "sequel #{Sequel.version}"
|
|
59
|
+
exit
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
opts.parse!
|
|
63
|
+
|
|
64
|
+
db = ARGV.shift
|
|
65
|
+
|
|
66
|
+
if db.blank?
|
|
67
|
+
puts opts
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if logfile || echo
|
|
72
|
+
require 'logger'
|
|
73
|
+
db_opts[:loggers] = []
|
|
74
|
+
db_opts[:loggers] << Logger.new(logfile) if logfile
|
|
75
|
+
db_opts[:loggers] << Logger.new($stdout) if echo
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if File.exist?(db)
|
|
79
|
+
require 'yaml'
|
|
80
|
+
env ||= "development"
|
|
81
|
+
db_config = YAML.load_file(db)
|
|
82
|
+
db_config = db_config[env] || db_config[env.to_sym] || db_config
|
|
83
|
+
db_config.each{|(k,v)| db_config[k.to_sym] = db_config.delete(k)}
|
|
84
|
+
db_config.merge!(db_opts)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
DB = Sequel.connect(*(db_config ? [db_config] : [db, db_opts]))
|
|
89
|
+
DB.test_connection
|
|
90
|
+
if migrate_dir
|
|
91
|
+
Sequel::Migrator.apply(DB, migrate_dir, migrate_ver)
|
|
92
|
+
exit
|
|
93
|
+
end
|
|
94
|
+
rescue => e
|
|
95
|
+
puts "#{e.class}: #{e.message}"
|
|
96
|
+
puts e.backtrace.first
|
|
97
|
+
exit 1
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Dir["#{load_dir}/**/*.rb"].each{|f| load(f)} if load_dir
|
|
101
|
+
|
|
102
|
+
require 'irb'
|
|
103
|
+
puts "Your database is stored in DB..."
|
|
104
|
+
IRB.start
|
data/lib/sequel.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'sequel/model'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Sequel.require 'adapters/utils/date_format'
|
|
2
|
+
require 'win32ole'
|
|
3
|
+
|
|
4
|
+
module Sequel
|
|
5
|
+
# The ADO adapter provides connectivity to ADO databases in Windows. ADO
|
|
6
|
+
# databases can be opened using a URL with the ado schema:
|
|
7
|
+
#
|
|
8
|
+
# DB = Sequel.connect('ado://mydb')
|
|
9
|
+
#
|
|
10
|
+
# or using the Sequel.ado method:
|
|
11
|
+
#
|
|
12
|
+
# DB = Sequel.ado('mydb')
|
|
13
|
+
#
|
|
14
|
+
module ADO
|
|
15
|
+
class Database < Sequel::Database
|
|
16
|
+
set_adapter_scheme :ado
|
|
17
|
+
|
|
18
|
+
def initialize(opts)
|
|
19
|
+
super(opts)
|
|
20
|
+
opts[:driver] ||= 'SQL Server'
|
|
21
|
+
case opts[:driver]
|
|
22
|
+
when 'SQL Server'
|
|
23
|
+
Sequel.require 'adapters/shared/mssql'
|
|
24
|
+
extend Sequel::MSSQL::DatabaseMethods
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def connect(server)
|
|
29
|
+
opts = server_opts(server)
|
|
30
|
+
s = "driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{";uid=#{opts[:user]};pwd=#{opts[:password]}" if opts[:user]}"
|
|
31
|
+
handle = WIN32OLE.new('ADODB.Connection')
|
|
32
|
+
handle.Open(s)
|
|
33
|
+
handle
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def dataset(opts = nil)
|
|
37
|
+
ADO::Dataset.new(self, opts)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def execute(sql, opts={})
|
|
41
|
+
log_info(sql)
|
|
42
|
+
synchronize(opts[:server]) do |conn|
|
|
43
|
+
r = conn.Execute(sql)
|
|
44
|
+
yield(r) if block_given?
|
|
45
|
+
r
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
alias_method :do, :execute
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def disconnect_connection(conn)
|
|
53
|
+
conn.Close
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Dataset < Sequel::Dataset
|
|
58
|
+
include Dataset::SQLStandardDateFormat
|
|
59
|
+
|
|
60
|
+
def fetch_rows(sql)
|
|
61
|
+
execute(sql) do |s|
|
|
62
|
+
@columns = s.Fields.extend(Enumerable).map do |column|
|
|
63
|
+
name = column.Name.empty? ? '(no column name)' : column.Name
|
|
64
|
+
output_identifier(name)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
unless s.eof
|
|
68
|
+
s.moveFirst
|
|
69
|
+
s.getRows.transpose.each {|r| yield hash_row(r)}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def hash_row(row)
|
|
78
|
+
@columns.inject({}) do |m, c|
|
|
79
|
+
m[c] = row.shift
|
|
80
|
+
m
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'db2/db2cli'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module DB2
|
|
5
|
+
class Database < Sequel::Database
|
|
6
|
+
set_adapter_scheme :db2
|
|
7
|
+
include DB2CLI
|
|
8
|
+
|
|
9
|
+
rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
|
|
10
|
+
#check_error(rc, "Could not allocate DB2 environment")
|
|
11
|
+
|
|
12
|
+
def connect(server)
|
|
13
|
+
opts = server_opts(server)
|
|
14
|
+
rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
|
|
15
|
+
check_error(rc, "Could not allocate database connection")
|
|
16
|
+
|
|
17
|
+
rc = SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
|
|
18
|
+
check_error(rc, "Could not connect to database")
|
|
19
|
+
|
|
20
|
+
dbc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_connection(server=nil)
|
|
24
|
+
synchronize(server){|conn|}
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def dataset(opts = nil)
|
|
29
|
+
DB2::Dataset.new(self, opts)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def execute(sql, opts={})
|
|
33
|
+
log_info(sql)
|
|
34
|
+
synchronize(opts[:server]) do |conn|
|
|
35
|
+
rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
|
|
36
|
+
check_error(rc, "Could not allocate statement")
|
|
37
|
+
|
|
38
|
+
begin
|
|
39
|
+
rc = SQLExecDirect(sth, sql)
|
|
40
|
+
check_error(rc, "Could not execute statement")
|
|
41
|
+
|
|
42
|
+
yield(sth) if block_given?
|
|
43
|
+
|
|
44
|
+
rc, rpc = SQLRowCount(sth)
|
|
45
|
+
check_error(rc, "Could not get RPC")
|
|
46
|
+
rpc
|
|
47
|
+
ensure
|
|
48
|
+
rc = SQLFreeHandle(SQL_HANDLE_STMT, sth)
|
|
49
|
+
check_error(rc, "Could not free statement")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
alias_method :do, :execute
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def check_error(rc, msg)
|
|
58
|
+
case rc
|
|
59
|
+
when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
|
|
60
|
+
nil
|
|
61
|
+
else
|
|
62
|
+
raise DatabaseError, msg
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def disconnect_connection(conn)
|
|
67
|
+
rc = SQLDisconnect(conn)
|
|
68
|
+
check_error(rc, "Could not disconnect from database")
|
|
69
|
+
|
|
70
|
+
rc = SQLFreeHandle(SQL_HANDLE_DBC, conn)
|
|
71
|
+
check_error(rc, "Could not free Database handle")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Dataset < Sequel::Dataset
|
|
76
|
+
MAX_COL_SIZE = 256
|
|
77
|
+
|
|
78
|
+
def fetch_rows(sql)
|
|
79
|
+
execute(sql) do |sth|
|
|
80
|
+
@column_info = get_column_info(sth)
|
|
81
|
+
@columns = @column_info.map {|c| output_identifier(c[:name])}
|
|
82
|
+
while (rc = SQLFetch(@handle)) != SQL_NO_DATA_FOUND
|
|
83
|
+
@db.check_error(rc, "Could not fetch row")
|
|
84
|
+
yield hash_row(sth)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def get_column_info(sth)
|
|
93
|
+
rc, column_count = SQLNumResultCols(sth)
|
|
94
|
+
@db.check_error(rc, "Could not get number of result columns")
|
|
95
|
+
|
|
96
|
+
(1..column_count).map do |i|
|
|
97
|
+
rc, name, buflen, datatype, size, digits, nullable = SQLDescribeCol(sth, i, MAX_COL_SIZE)
|
|
98
|
+
@b.check_error(rc, "Could not describe column")
|
|
99
|
+
|
|
100
|
+
{:name => name, :db2_type => datatype, :precision => size}
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def hash_row(sth)
|
|
105
|
+
row = {}
|
|
106
|
+
@column_info.each_with_index do |c, i|
|
|
107
|
+
rc, v = SQLGetData(sth, i+1, c[:db2_type], c[:precision])
|
|
108
|
+
@db.check_error(rc, "Could not get data")
|
|
109
|
+
|
|
110
|
+
row[output_identifier(c[:name])] = convert_type(v)
|
|
111
|
+
end
|
|
112
|
+
row
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def convert_type(v)
|
|
116
|
+
case v
|
|
117
|
+
when DB2CLI::Date
|
|
118
|
+
DBI::Date.new(v.year, v.month, v.day)
|
|
119
|
+
when DB2CLI::Time
|
|
120
|
+
DBI::Time.new(v.hour, v.minute, v.second)
|
|
121
|
+
when DB2CLI::Timestamp
|
|
122
|
+
DBI::Timestamp.new(v.year, v.month, v.day,
|
|
123
|
+
v.hour, v.minute, v.second, v.fraction)
|
|
124
|
+
when DB2CLI::Null
|
|
125
|
+
nil
|
|
126
|
+
else
|
|
127
|
+
v
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'dbi'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module DBI
|
|
5
|
+
class Database < Sequel::Database
|
|
6
|
+
set_adapter_scheme :dbi
|
|
7
|
+
|
|
8
|
+
DBI_ADAPTERS = {
|
|
9
|
+
:ado => "ADO",
|
|
10
|
+
:db2 => "DB2",
|
|
11
|
+
:frontbase => "FrontBase",
|
|
12
|
+
:interbase => "InterBase",
|
|
13
|
+
:msql => "Msql",
|
|
14
|
+
:mysql => "Mysql",
|
|
15
|
+
:odbc => "ODBC",
|
|
16
|
+
:oracle => "Oracle",
|
|
17
|
+
:pg => "pg",
|
|
18
|
+
:proxy => "Proxy",
|
|
19
|
+
:sqlite => "SQLite",
|
|
20
|
+
:sqlrelay => "SQLRelay"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Converts a uri to an options hash. These options are then passed
|
|
24
|
+
# to a newly created database object.
|
|
25
|
+
def self.uri_to_options(uri) # :nodoc:
|
|
26
|
+
database = (m = /\/(.*)/.match(uri.path)) && (m[1])
|
|
27
|
+
if m = /dbi-(.+)/.match(uri.scheme)
|
|
28
|
+
adapter = DBI_ADAPTERS[m[1].to_sym] || m[1]
|
|
29
|
+
database = "#{adapter}:dbname=#{database}"
|
|
30
|
+
end
|
|
31
|
+
{
|
|
32
|
+
:user => uri.user,
|
|
33
|
+
:password => uri.password,
|
|
34
|
+
:host => uri.host,
|
|
35
|
+
:port => uri.port,
|
|
36
|
+
:database => database
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private_class_method :uri_to_options
|
|
41
|
+
|
|
42
|
+
def connect(server)
|
|
43
|
+
opts = server_opts(server)
|
|
44
|
+
dbname = opts[:database]
|
|
45
|
+
if dbname !~ /^DBI:/ then
|
|
46
|
+
dbname = "DBI:#{dbname}"
|
|
47
|
+
[:host, :port].each{|sym| dbname += ";#{sym}=#{opts[sym]}" unless blank_object?(opts[sym])}
|
|
48
|
+
end
|
|
49
|
+
::DBI.connect(dbname, opts[:user], opts[:password])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def dataset(opts = nil)
|
|
53
|
+
DBI::Dataset.new(self, opts)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def execute(sql, opts={})
|
|
57
|
+
log_info(sql)
|
|
58
|
+
synchronize(opts[:server]) do |conn|
|
|
59
|
+
r = conn.execute(sql)
|
|
60
|
+
yield(r) if block_given?
|
|
61
|
+
r
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def do(sql, opts={})
|
|
66
|
+
log_info(sql)
|
|
67
|
+
synchronize(opts[:server]){|conn| conn.do(sql)}
|
|
68
|
+
end
|
|
69
|
+
alias_method :execute_dui, :do
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def disconnect_connection(c)
|
|
74
|
+
c.disconnect
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class Dataset < Sequel::Dataset
|
|
79
|
+
def fetch_rows(sql, &block)
|
|
80
|
+
execute(sql) do |s|
|
|
81
|
+
begin
|
|
82
|
+
@columns = s.column_names.map{|c| output_identifier(c)}
|
|
83
|
+
s.fetch{|r| yield hash_row(s, r)}
|
|
84
|
+
ensure
|
|
85
|
+
s.finish rescue nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def hash_row(stmt, row)
|
|
94
|
+
@columns.inject({}) do |m, c|
|
|
95
|
+
m[c] = row.shift
|
|
96
|
+
m
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
require 'data_objects'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
# Module holding the DataObjects support for Sequel. DataObjects is a
|
|
5
|
+
# ruby library with a standard API for accessing databases.
|
|
6
|
+
#
|
|
7
|
+
# The DataObjects adapter currently supports PostgreSQL, MySQL, and
|
|
8
|
+
# SQLite:
|
|
9
|
+
#
|
|
10
|
+
# * Sequel.connect('do:sqlite3::memory:')
|
|
11
|
+
# * Sequel.connect('do:postgres://user:password@host/database')
|
|
12
|
+
# * Sequel.connect('do:mysql://user:password@host/database')
|
|
13
|
+
module DataObjects
|
|
14
|
+
# Contains procs keyed on sub adapter type that extend the
|
|
15
|
+
# given database object so it supports the correct database type.
|
|
16
|
+
DATABASE_SETUP = {:postgres=>proc do |db|
|
|
17
|
+
require 'do_postgres'
|
|
18
|
+
Sequel.require 'adapters/do/postgres'
|
|
19
|
+
db.converted_exceptions << PostgresError
|
|
20
|
+
db.extend(Sequel::DataObjects::Postgres::DatabaseMethods)
|
|
21
|
+
end,
|
|
22
|
+
:mysql=>proc do |db|
|
|
23
|
+
require 'do_mysql'
|
|
24
|
+
Sequel.require 'adapters/do/mysql'
|
|
25
|
+
db.converted_exceptions << MysqlError
|
|
26
|
+
db.extend(Sequel::DataObjects::MySQL::DatabaseMethods)
|
|
27
|
+
end,
|
|
28
|
+
:sqlite3=>proc do |db|
|
|
29
|
+
require 'do_sqlite3'
|
|
30
|
+
Sequel.require 'adapters/do/sqlite'
|
|
31
|
+
db.converted_exceptions << Sqlite3Error
|
|
32
|
+
db.extend(Sequel::DataObjects::SQLite::DatabaseMethods)
|
|
33
|
+
end
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# DataObjects uses it's own internal connection pooling in addition to the
|
|
37
|
+
# pooling that Sequel uses. You should make sure that you don't set
|
|
38
|
+
# the connection pool size to more than 8 for a
|
|
39
|
+
# Sequel::DataObjects::Database object, or hack DataObjects (or Extlib) to
|
|
40
|
+
# use a pool size at least as large as the pool size being used by Sequel.
|
|
41
|
+
class Database < Sequel::Database
|
|
42
|
+
set_adapter_scheme :do
|
|
43
|
+
|
|
44
|
+
# Convert the given exceptions to Sequel:Errors, necessary
|
|
45
|
+
# because DO raises errors specific to database types in
|
|
46
|
+
# certain cases.
|
|
47
|
+
attr_accessor :converted_exceptions
|
|
48
|
+
|
|
49
|
+
# Call the DATABASE_SETUP proc directly after initialization,
|
|
50
|
+
# so the object always uses sub adapter specific code. Also,
|
|
51
|
+
# raise an error immediately if the connection doesn't have a
|
|
52
|
+
# uri, since DataObjects requires one.
|
|
53
|
+
def initialize(opts)
|
|
54
|
+
@opts = opts
|
|
55
|
+
@converted_exceptions = []
|
|
56
|
+
raise(Error, "No connection string specified") unless uri
|
|
57
|
+
if prok = DATABASE_SETUP[subadapter.to_sym]
|
|
58
|
+
prok.call(self)
|
|
59
|
+
end
|
|
60
|
+
super(opts)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Setup a DataObjects::Connection to the database.
|
|
64
|
+
def connect(server)
|
|
65
|
+
setup_connection(::DataObjects::Connection.new(uri(server_opts(server))))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Return a Sequel::DataObjects::Dataset object for this database.
|
|
69
|
+
def dataset(opts = nil)
|
|
70
|
+
DataObjects::Dataset.new(self, opts)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Execute the given SQL. If a block is given, the DataObjects::Reader
|
|
74
|
+
# created is yielded to it. A block should not be provided unless a
|
|
75
|
+
# a SELECT statement is being used (or something else that returns rows).
|
|
76
|
+
# Otherwise, the return value is the insert id if opts[:type] is :insert,
|
|
77
|
+
# or the number of affected rows, otherwise.
|
|
78
|
+
def execute(sql, opts={})
|
|
79
|
+
log_info(sql)
|
|
80
|
+
synchronize(opts[:server]) do |conn|
|
|
81
|
+
begin
|
|
82
|
+
command = conn.create_command(sql)
|
|
83
|
+
res = block_given? ? command.execute_reader : command.execute_non_query
|
|
84
|
+
rescue Exception => e
|
|
85
|
+
raise_error(e, :classes=>@converted_exceptions)
|
|
86
|
+
end
|
|
87
|
+
if block_given?
|
|
88
|
+
begin
|
|
89
|
+
yield(res)
|
|
90
|
+
ensure
|
|
91
|
+
res.close if res
|
|
92
|
+
end
|
|
93
|
+
elsif opts[:type] == :insert
|
|
94
|
+
res.insert_id
|
|
95
|
+
else
|
|
96
|
+
res.affected_rows
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Execute the SQL on the this database, returning the number of affected
|
|
102
|
+
# rows.
|
|
103
|
+
def execute_dui(sql, opts={})
|
|
104
|
+
execute(sql, opts)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Execute the SQL on this database, returning the primary key of the
|
|
108
|
+
# table being inserted to.
|
|
109
|
+
def execute_insert(sql, opts={})
|
|
110
|
+
execute(sql, opts.merge(:type=>:insert))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Return the subadapter type for this database, i.e. sqlite3 for
|
|
114
|
+
# do:sqlite3::memory:.
|
|
115
|
+
def subadapter
|
|
116
|
+
uri.split(":").first
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Use DataObject's transaction support for transactions. This
|
|
120
|
+
# only supports single level transactions, and it always prepares
|
|
121
|
+
# transactions and commits them immediately after. It's wasteful,
|
|
122
|
+
# but required by DataObject's API.
|
|
123
|
+
def transaction(opts={})
|
|
124
|
+
unless opts.is_a?(Hash)
|
|
125
|
+
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
|
126
|
+
opts = {:server=>opts}
|
|
127
|
+
end
|
|
128
|
+
th = Thread.current
|
|
129
|
+
synchronize(opts[:server]) do |conn|
|
|
130
|
+
return yield(conn) if @transactions.include?(th)
|
|
131
|
+
t = ::DataObjects::Transaction.create_for_uri(uri)
|
|
132
|
+
t.instance_variable_get(:@connection).close
|
|
133
|
+
t.instance_variable_set(:@connection, conn)
|
|
134
|
+
begin
|
|
135
|
+
log_info("Transaction.begin")
|
|
136
|
+
t.begin
|
|
137
|
+
@transactions << th
|
|
138
|
+
yield(conn)
|
|
139
|
+
rescue Exception => e
|
|
140
|
+
log_info("Transaction.rollback")
|
|
141
|
+
t.rollback
|
|
142
|
+
transaction_error(e)
|
|
143
|
+
ensure
|
|
144
|
+
unless e
|
|
145
|
+
log_info("Transaction.commit")
|
|
146
|
+
t.prepare
|
|
147
|
+
t.commit
|
|
148
|
+
end
|
|
149
|
+
@transactions.delete(th)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Return the DataObjects URI for the Sequel URI, removing the do:
|
|
155
|
+
# prefix.
|
|
156
|
+
def uri(opts={})
|
|
157
|
+
opts = @opts.merge(opts)
|
|
158
|
+
(opts[:uri] || opts[:url]).sub(/\Ado:/, '')
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
# Close the given database connection.
|
|
164
|
+
def disconnect_connection(c)
|
|
165
|
+
c.close
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Allow extending the given connection when it is first created.
|
|
169
|
+
# By default, just returns the connection.
|
|
170
|
+
def setup_connection(conn)
|
|
171
|
+
conn
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# The DataObjects adapter should convert exceptions by default.
|
|
175
|
+
def connection_pool_default_options
|
|
176
|
+
super.merge(:pool_convert_exceptions=>false)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Dataset class for Sequel::DataObjects::Database objects.
|
|
181
|
+
class Dataset < Sequel::Dataset
|
|
182
|
+
# Execute the SQL on the database and yield the rows as hashes
|
|
183
|
+
# with symbol keys.
|
|
184
|
+
def fetch_rows(sql)
|
|
185
|
+
execute(sql) do |reader|
|
|
186
|
+
cols = @columns = reader.fields.map{|f| output_identifier(f)}
|
|
187
|
+
while(reader.next!) do
|
|
188
|
+
h = {}
|
|
189
|
+
cols.zip(reader.values).each{|k, v| h[k] = v}
|
|
190
|
+
yield h
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
self
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|