epugh-sequel 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|