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.
Files changed (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 0
@@ -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
@@ -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