sequel 0.5.0.2 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/COPYING +18 -18
  2. data/Rakefile +17 -98
  3. data/lib/sequel.rb +2 -71
  4. metadata +10 -108
  5. data/CHANGELOG +0 -989
  6. data/bin/sequel +0 -41
  7. data/lib/sequel/adapters/adapter_skeleton.rb +0 -68
  8. data/lib/sequel/adapters/ado.rb +0 -100
  9. data/lib/sequel/adapters/db2.rb +0 -158
  10. data/lib/sequel/adapters/dbi.rb +0 -126
  11. data/lib/sequel/adapters/informix.rb +0 -87
  12. data/lib/sequel/adapters/jdbc.rb +0 -108
  13. data/lib/sequel/adapters/mysql.rb +0 -269
  14. data/lib/sequel/adapters/odbc.rb +0 -145
  15. data/lib/sequel/adapters/odbc_mssql.rb +0 -93
  16. data/lib/sequel/adapters/openbase.rb +0 -90
  17. data/lib/sequel/adapters/oracle.rb +0 -99
  18. data/lib/sequel/adapters/postgres.rb +0 -519
  19. data/lib/sequel/adapters/sqlite.rb +0 -192
  20. data/lib/sequel/ado.rb +0 -6
  21. data/lib/sequel/array_keys.rb +0 -296
  22. data/lib/sequel/connection_pool.rb +0 -152
  23. data/lib/sequel/core_ext.rb +0 -59
  24. data/lib/sequel/core_sql.rb +0 -191
  25. data/lib/sequel/database.rb +0 -433
  26. data/lib/sequel/dataset.rb +0 -409
  27. data/lib/sequel/dataset/convenience.rb +0 -321
  28. data/lib/sequel/dataset/sequelizer.rb +0 -354
  29. data/lib/sequel/dataset/sql.rb +0 -586
  30. data/lib/sequel/db2.rb +0 -6
  31. data/lib/sequel/dbi.rb +0 -6
  32. data/lib/sequel/exceptions.rb +0 -45
  33. data/lib/sequel/informix.rb +0 -6
  34. data/lib/sequel/migration.rb +0 -191
  35. data/lib/sequel/model.rb +0 -8
  36. data/lib/sequel/mysql.rb +0 -6
  37. data/lib/sequel/odbc.rb +0 -6
  38. data/lib/sequel/oracle.rb +0 -6
  39. data/lib/sequel/postgres.rb +0 -6
  40. data/lib/sequel/pretty_table.rb +0 -73
  41. data/lib/sequel/schema.rb +0 -8
  42. data/lib/sequel/schema/schema_generator.rb +0 -131
  43. data/lib/sequel/schema/schema_sql.rb +0 -131
  44. data/lib/sequel/sqlite.rb +0 -6
  45. data/lib/sequel/worker.rb +0 -58
  46. data/spec/adapters/informix_spec.rb +0 -139
  47. data/spec/adapters/mysql_spec.rb +0 -330
  48. data/spec/adapters/oracle_spec.rb +0 -130
  49. data/spec/adapters/postgres_spec.rb +0 -189
  50. data/spec/adapters/sqlite_spec.rb +0 -345
  51. data/spec/array_keys_spec.rb +0 -679
  52. data/spec/connection_pool_spec.rb +0 -356
  53. data/spec/core_ext_spec.rb +0 -67
  54. data/spec/core_sql_spec.rb +0 -301
  55. data/spec/database_spec.rb +0 -811
  56. data/spec/dataset_spec.rb +0 -2381
  57. data/spec/migration_spec.rb +0 -261
  58. data/spec/pretty_table_spec.rb +0 -66
  59. data/spec/rcov.opts +0 -4
  60. data/spec/schema_generator_spec.rb +0 -86
  61. data/spec/schema_spec.rb +0 -230
  62. data/spec/sequel_spec.rb +0 -10
  63. data/spec/sequelizer_spec.rb +0 -389
  64. data/spec/spec.opts +0 -5
  65. data/spec/spec_helper.rb +0 -44
  66. data/spec/worker_spec.rb +0 -96
@@ -1,152 +0,0 @@
1
- require 'thread'
2
-
3
- module Sequel
4
- # A ConnectionPool manages access to database connections by keeping
5
- # multiple connections and giving threads exclusive access to each
6
- # connection.
7
- class ConnectionPool
8
- attr_reader :mutex
9
-
10
- # The maximum number of connections.
11
- attr_reader :max_size
12
-
13
- # The proc used to create a new connection.
14
- attr_accessor :connection_proc
15
-
16
- attr_reader :available_connections, :allocated, :created_count
17
-
18
- # Constructs a new pool with a maximum size. If a block is supplied, it
19
- # is used to create new connections as they are needed.
20
- #
21
- # pool = ConnectionPool.new(10) {MyConnection.new(opts)}
22
- #
23
- # The connection creation proc can be changed at any time by assigning a
24
- # Proc to pool#connection_proc.
25
- #
26
- # pool = ConnectionPool.new(10)
27
- # pool.connection_proc = proc {MyConnection.new(opts)}
28
- def initialize(max_size = 4, &block)
29
- @max_size = max_size
30
- @mutex = Mutex.new
31
- @connection_proc = block
32
-
33
- @available_connections = []
34
- @allocated = {}
35
- @created_count = 0
36
- end
37
-
38
- # Returns the number of created connections.
39
- def size
40
- @created_count
41
- end
42
-
43
- # Assigns a connection to the current thread, yielding the connection
44
- # to the supplied block.
45
- #
46
- # pool.hold {|conn| conn.execute('DROP TABLE posts')}
47
- #
48
- # Pool#hold is re-entrant, meaning it can be called recursively in
49
- # the same thread without blocking.
50
- #
51
- # If no connection is available, Pool#hold will block until a connection
52
- # is available.
53
- def hold
54
- t = Thread.current
55
- if (conn = owned_connection(t))
56
- return yield(conn)
57
- end
58
- while !(conn = acquire(t))
59
- sleep 0.001
60
- end
61
- begin
62
- yield conn
63
- ensure
64
- release(t)
65
- end
66
- rescue Exception => e
67
- # if the error is not a StandardError it is converted into RuntimeError.
68
- raise e.is_a?(StandardError) ? e : e.message
69
- end
70
-
71
- # Removes all connection currently available, optionally yielding each
72
- # connection to the given block. This method has the effect of
73
- # disconnecting from the database. Once a connection is requested using
74
- # #hold, the connection pool creates new connections to the database.
75
- def disconnect(&block)
76
- @mutex.synchronize do
77
- @available_connections.each {|c| block[c]} if block
78
- @available_connections = []
79
- @created_count = @allocated.size
80
- end
81
- end
82
-
83
- private
84
- # Returns the connection owned by the supplied thread, if any.
85
- def owned_connection(thread)
86
- @mutex.synchronize {@allocated[thread]}
87
- end
88
-
89
- # Assigns a connection to the supplied thread, if one is available.
90
- def acquire(thread)
91
- @mutex.synchronize do
92
- if conn = available
93
- @allocated[thread] = conn
94
- end
95
- end
96
- end
97
-
98
- # Returns an available connection. If no connection is available,
99
- # tries to create a new connection.
100
- def available
101
- @available_connections.pop || make_new
102
- end
103
-
104
- # Creates a new connection if the size of the pool is less than the
105
- # maximum size.
106
- def make_new
107
- if @created_count < @max_size
108
- @created_count += 1
109
- @connection_proc ? @connection_proc.call : \
110
- (raise Error, "No connection proc specified")
111
- end
112
- end
113
-
114
- # Releases the connection assigned to the supplied thread.
115
- def release(thread)
116
- @mutex.synchronize do
117
- @available_connections << @allocated[thread]
118
- @allocated.delete(thread)
119
- end
120
- end
121
- end
122
-
123
- # A SingleThreadedPool acts as a replacement for a ConnectionPool for use
124
- # in single-threaded applications. ConnectionPool imposes a substantial
125
- # performance penalty, so SingleThreadedPool is used to gain some speed.
126
- class SingleThreadedPool
127
- attr_reader :conn
128
- attr_writer :connection_proc
129
-
130
- # Initializes the instance with the supplied block as the connection_proc.
131
- def initialize(&block)
132
- @connection_proc = block
133
- end
134
-
135
- # Yields the connection to the supplied block. This method simulates the
136
- # ConnectionPool#hold API.
137
- def hold
138
- @conn ||= @connection_proc.call
139
- yield @conn
140
- rescue Exception => e
141
- # if the error is not a StandardError it is converted into RuntimeError.
142
- raise e.is_a?(StandardError) ? e : e.message
143
- end
144
-
145
- # Disconnects from the database. Once a connection is requested using
146
- # #hold, the connection is reestablished.
147
- def disconnect(&block)
148
- block[@conn] if block && @conn
149
- @conn = nil
150
- end
151
- end
152
- end
@@ -1,59 +0,0 @@
1
- # Enumerable extensions.
2
- module Enumerable
3
- # Invokes the specified method for each item, along with the supplied
4
- # arguments.
5
- def send_each(sym, *args)
6
- each {|i| i.send(sym, *args)}
7
- end
8
- end
9
-
10
- # Range extensions
11
- class Range
12
- # Returns the interval between the beginning and end of the range.
13
- def interval
14
- last - first
15
- end
16
- end
17
-
18
- # Object extensions
19
- class Object
20
- def is_one_of?(*classes)
21
- classes.each {|c| return c if is_a?(c)}
22
- nil
23
- end
24
- end
25
-
26
- module Sequel
27
- # Facilitates time calculations by providing methods to convert from larger
28
- # time units to seconds, and to convert relative time intervals to absolute
29
- # ones. This module duplicates some of the functionality provided by Rails'
30
- # ActiveSupport::CoreExtensions::Numeric::Time module.
31
- module NumericExtensions
32
- MINUTE = 60
33
- HOUR = 3600
34
- DAY = 86400
35
- WEEK = DAY * 7
36
-
37
- # Converts self from minutes to seconds
38
- def minutes; self * MINUTE; end; alias_method :minute, :minutes
39
- # Converts self from hours to seconds
40
- def hours; self * HOUR; end; alias_method :hour, :hours
41
- # Converts self from days to seconds
42
- def days; self * DAY; end; alias_method :day, :days
43
- # Converts self from weeks to seconds
44
- def weeks; self * WEEK; end; alias_method :week, :weeks
45
-
46
- # Returns the time at now - self.
47
- def ago(t = Time.now); t - self; end
48
- alias_method :before, :ago
49
-
50
- # Returns the time at now + self.
51
- def from_now(t = Time.now); t + self; end
52
- alias_method :since, :from_now
53
-
54
- # Extends the Numeric class with numeric extensions.
55
- def self.enable
56
- Numeric.send(:include, self)
57
- end
58
- end
59
- end
@@ -1,191 +0,0 @@
1
- # Array extensions
2
- class Array
3
- # Concatenates an array of strings into an SQL string. ANSI SQL and C-style
4
- # comments are removed, as well as excessive white-space.
5
- def to_sql
6
- map {|l| (l =~ /^(.*)--/ ? $1 : l).chomp}.join(' '). \
7
- gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
8
- end
9
- end
10
-
11
- module Sequel
12
- # LiteralString is used to represent literal SQL expressions. An
13
- # LiteralString is copied verbatim into an SQL statement. Instances of
14
- # LiteralString can be created by calling String#expr.
15
- class LiteralString < ::String
16
- end
17
- end
18
-
19
- # String extensions
20
- class String
21
- # Converts a string into an SQL string by removing comments.
22
- # See also Array#to_sql.
23
- def to_sql
24
- split($/).to_sql
25
- end
26
-
27
- # Splits a string into separate SQL statements, removing comments
28
- # and excessive white-space.
29
- def split_sql
30
- to_sql.split(';').map {|s| s.strip}
31
- end
32
-
33
- # Converts a string into an LiteralString, in order to override string
34
- # literalization, e.g.:
35
- #
36
- # DB[:items].filter(:abc => 'def').sql #=>
37
- # "SELECT * FROM items WHERE (abc = 'def')"
38
- #
39
- # DB[:items].filter(:abc => 'def'.lit).sql #=>
40
- # "SELECT * FROM items WHERE (abc = def)"
41
- #
42
- def lit
43
- Sequel::LiteralString.new(self)
44
- end
45
-
46
- alias_method :expr, :lit
47
-
48
- # Converts a string into a Time object.
49
- def to_time
50
- begin
51
- Time.parse(self)
52
- rescue Exception => e
53
- raise Sequel::Error::InvalidValue, "Invalid time value '#{self}' (#{e.message})"
54
- end
55
- # Why does Time.parse('0000-00-00') bork and not return nil or some such?
56
- end
57
- end
58
-
59
-
60
- module Sequel
61
- module SQL
62
- class Expression
63
- def lit; self; end
64
- end
65
-
66
- class ColumnExpr < Expression
67
- attr_reader :l, :op, :r
68
- def initialize(l, op, r = nil); @l, @op, @r = l, op, r; end
69
-
70
- def to_s(ds)
71
- @r ? \
72
- "#{ds.literal(@l)} #{@op} #{ds.literal(@r)}" : \
73
- "#{ds.literal(@l)} #{@op}"
74
- end
75
- end
76
-
77
- class QualifiedColumnRef < Expression
78
- def initialize(t, c); @t, @c = t, c; end
79
-
80
- def to_s(ds)
81
- "#{@t}.#{ds.literal(@c)}"
82
- end
83
- end
84
-
85
- class Function < Expression
86
- def initialize(f, *args); @f, @args = f, args; end
87
-
88
- def to_s(ds)
89
- args = @args.empty? ? '' : ds.literal(@args)
90
- "#{@f}(#{args})"
91
- end
92
- end
93
-
94
- class Subscript < Expression
95
- def initialize(f, sub); @f, @sub = f, sub; end
96
- def |(sub)
97
- unless Array === sub
98
- sub = [sub]
99
- end
100
- Subscript.new(@f, @sub << sub)
101
- end
102
-
103
- COMMA_SEPARATOR = ", ".freeze
104
-
105
- def to_s(ds)
106
- "#{@f}[#{@sub.join(COMMA_SEPARATOR)}]"
107
- end
108
- end
109
-
110
- class ColumnAll < Expression
111
- def initialize(t); @t = t; end
112
- def to_s(ds); "#{@t}.*"; end
113
- end
114
-
115
- module ColumnMethods
116
- AS = 'AS'.freeze
117
- DESC = 'DESC'.freeze
118
- ASC = 'ASC'.freeze
119
-
120
- def as(a); ColumnExpr.new(self, AS, a); end
121
- alias_method :AS, :as
122
-
123
- def desc; ColumnExpr.new(self, DESC); end
124
- alias_method :DESC, :desc
125
-
126
- def asc; ColumnExpr.new(self, ASC); end
127
- alias_method :ASC, :asc
128
-
129
- def all; Sequel::SQL::ColumnAll.new(self); end
130
- alias_method :ALL, :all
131
-
132
- def cast_as(t)
133
- if t.is_a?(Symbol)
134
- t = t.to_s.lit
135
- end
136
- Sequel::SQL::Function.new(:cast, self.as(t))
137
- end
138
- end
139
- end
140
- end
141
-
142
- class Object
143
- include Sequel::SQL::ColumnMethods
144
- end
145
-
146
- class Symbol
147
- def [](*args); Sequel::SQL::Function.new(self, *args); end
148
- def |(sub)
149
- unless Array === sub
150
- sub = [sub]
151
- end
152
- Sequel::SQL::Subscript.new(self, sub)
153
- end
154
-
155
- COLUMN_REF_RE1 = /^(\w+)__(\w+)___(\w+)/.freeze
156
- COLUMN_REF_RE2 = /^(\w+)___(\w+)$/.freeze
157
- COLUMN_REF_RE3 = /^(\w+)__(\w+)$/.freeze
158
-
159
- # Converts a symbol into a column name. This method supports underscore
160
- # notation in order to express qualified (two underscores) and aliased
161
- # (three underscores) columns:
162
- #
163
- # ds = DB[:items]
164
- # :abc.to_column_ref(ds) #=> "abc"
165
- # :abc___a.to_column_ref(ds) #=> "abc AS a"
166
- # :items__abc.to_column_ref(ds) #=> "items.abc"
167
- # :items__abc___a.to_column_ref(ds) #=> "items.abc AS a"
168
- #
169
- def to_column_ref(ds)
170
- case s = to_s
171
- when COLUMN_REF_RE1
172
- "#{$1}.#{ds.quote_column_ref($2)} AS #{ds.quote_column_ref($3)}"
173
- when COLUMN_REF_RE2
174
- "#{ds.quote_column_ref($1)} AS #{ds.quote_column_ref($2)}"
175
- when COLUMN_REF_RE3
176
- "#{$1}.#{ds.quote_column_ref($2)}"
177
- else
178
- ds.quote_column_ref(s)
179
- end
180
- end
181
-
182
- # Converts missing method calls into functions on columns, if the
183
- # method name is made of all upper case letters.
184
- def method_missing(sym)
185
- if ((s = sym.to_s) =~ /^([A-Z]+)$/)
186
- Sequel::SQL::Function.new(s.downcase, self)
187
- else
188
- super
189
- end
190
- end
191
- end
@@ -1,433 +0,0 @@
1
- require 'uri'
2
-
3
- module Sequel
4
- # A Database object represents a virtual connection to a database.
5
- # The Database class is meant to be subclassed by database adapters in order
6
- # to provide the functionality needed for executing queries.
7
- class Database
8
- attr_reader :opts, :pool
9
- attr_accessor :logger
10
-
11
- # Constructs a new instance of a database connection with the specified
12
- # options hash.
13
- #
14
- # Sequel::Database is an abstract class that is not useful by itself.
15
- def initialize(opts = {}, &block)
16
- Model.database_opened(self)
17
- @opts = opts
18
-
19
- # Determine if the DB is single threaded or multi threaded
20
- @single_threaded = opts[:single_threaded] || @@single_threaded
21
- # Construct pool
22
- if @single_threaded
23
- @pool = SingleThreadedPool.new(&block)
24
- else
25
- @pool = ConnectionPool.new(opts[:max_connections] || 4, &block)
26
- end
27
- @pool.connection_proc = block || proc {connect}
28
-
29
- @logger = opts[:logger]
30
- end
31
-
32
- # Connects to the database. This method should be overriden by descendants.
33
- def connect
34
- raise NotImplementedError, "#connect should be overriden by adapters"
35
- end
36
-
37
- # Disconnects from the database. This method should be overriden by
38
- # descendants.
39
- def disconnect
40
- raise NotImplementedError, "#disconnect should be overriden by adapters"
41
- end
42
-
43
- # Returns true if the database is using a multi-threaded connection pool.
44
- def multi_threaded?
45
- !@single_threaded
46
- end
47
-
48
- # Returns true if the database is using a single-threaded connection pool.
49
- def single_threaded?
50
- @single_threaded
51
- end
52
-
53
- # Returns the URI identifying the database.
54
- def uri
55
- uri = URI::Generic.new(
56
- self.class.adapter_scheme.to_s,
57
- nil,
58
- @opts[:host],
59
- @opts[:port],
60
- nil,
61
- "/#{@opts[:database]}",
62
- nil,
63
- nil,
64
- nil
65
- )
66
- uri.user = @opts[:user]
67
- uri.password = @opts[:password] if uri.user
68
- uri.to_s
69
- end
70
- alias url uri # Because I don't care much for the semantic difference.
71
-
72
- # Returns a blank dataset
73
- def dataset
74
- ds = Sequel::Dataset.new(self)
75
- end
76
-
77
- # Fetches records for an arbitrary SQL statement. If a block is given,
78
- # it is used to iterate over the records:
79
- #
80
- # DB.fetch('SELECT * FROM items') {|r| p r}
81
- #
82
- # If a block is not given, the method returns a dataset instance:
83
- #
84
- # DB.fetch('SELECT * FROM items').print
85
- #
86
- # Fetch can also perform parameterized queries for protection against SQL
87
- # injection:
88
- #
89
- # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
90
- #
91
- # A short-hand form for Database#fetch is Database#[]:
92
- #
93
- # DB['SELECT * FROM items'].each {|r| p r}
94
- #
95
- def fetch(sql, *args, &block)
96
- ds = dataset
97
- sql = sql.gsub('?') {|m| ds.literal(args.shift)}
98
- if block
99
- ds.fetch_rows(sql, &block)
100
- else
101
- ds.opts[:sql] = sql
102
- ds
103
- end
104
- end
105
- alias_method :>>, :fetch
106
-
107
- # Converts a query block into a dataset. For more information see
108
- # Dataset#query.
109
- def query(&block)
110
- dataset.query(&block)
111
- end
112
-
113
- # Returns a new dataset with the from method invoked. If a block is given,
114
- # it is used as a filter on the dataset.
115
- def from(*args, &block)
116
- ds = dataset.from(*args)
117
- block ? ds.filter(&block) : ds
118
- end
119
-
120
- # Returns a new dataset with the select method invoked.
121
- def select(*args); dataset.select(*args); end
122
-
123
- # Returns a dataset from the database. If the first argument is a string,
124
- # the method acts as an alias for Database#fetch, returning a dataset for
125
- # arbitrary SQL:
126
- #
127
- # DB['SELECT * FROM items WHERE name = ?', my_name].print
128
- #
129
- # Otherwise, the dataset returned has its from option set to the given
130
- # arguments:
131
- #
132
- # DB[:items].sql #=> "SELECT * FROM items"
133
- #
134
- def [](*args)
135
- (String === args.first) ? fetch(*args) : from(*args)
136
- end
137
-
138
- # Raises a Sequel::Error::NotImplemented. This method is overriden in descendants.
139
- def execute(sql)
140
- raise NotImplementedError, "#execute should be overriden by adapters"
141
- end
142
-
143
- # Executes the supplied SQL statement. The SQL can be supplied as a string
144
- # or as an array of strings. If an array is give, comments and excessive
145
- # white space are removed. See also Array#to_sql.
146
- def <<(sql); execute((Array === sql) ? sql.to_sql : sql); end
147
-
148
- # Acquires a database connection, yielding it to the passed block.
149
- def synchronize(&block)
150
- @pool.hold(&block)
151
- end
152
-
153
- # Returns true if there is a database connection
154
- def test_connection
155
- @pool.hold {|conn|}
156
- true
157
- end
158
-
159
- # include Dataset::SQL
160
- include Schema::SQL
161
-
162
- # default serial primary key definition. this should be overriden for each adapter.
163
- def serial_primary_key_options
164
- {:primary_key => true, :type => :integer, :auto_increment => true}
165
- end
166
-
167
- # Creates a table. The easiest way to use this method is to provide a
168
- # block:
169
- # DB.create_table :posts do
170
- # primary_key :id, :serial
171
- # column :title, :text
172
- # column :content, :text
173
- # index :title
174
- # end
175
- def create_table(name, &block)
176
- g = Schema::Generator.new(self, &block)
177
- create_table_sql_list(name, *g.create_info).each {|sql| execute(sql)}
178
- end
179
-
180
- # Forcibly creates a table. If the table already exists it is dropped.
181
- def create_table!(name, &block)
182
- drop_table(name) rescue nil
183
- create_table(name, &block)
184
- end
185
-
186
- # Drops one or more tables corresponding to the given table names.
187
- def drop_table(*names)
188
- names.each {|n| execute(drop_table_sql(n))}
189
- end
190
-
191
- # Renames a table:
192
- #
193
- # DB.tables #=> [:items]
194
- # DB.rename_table :items, :old_items
195
- # DB.tables #=> [:old_items]
196
- def rename_table(*args)
197
- execute(rename_table_sql(*args))
198
- end
199
-
200
- # Alters the given table with the specified block. Here are the currently
201
- # available operations:
202
- #
203
- # DB.alter_table :items do
204
- # add_column :category, :text, :default => 'ruby'
205
- # drop_column :category
206
- # rename_column :cntr, :counter
207
- # set_column_type :value, :float
208
- # set_column_default :value, :float
209
- # add_index [:group, :category]
210
- # drop_index [:group, :category]
211
- # end
212
- #
213
- # Note that #add_column accepts all the options available for column
214
- # definitions using create_table, and #add_index accepts all the options
215
- # available for index definition.
216
- def alter_table(name, &block)
217
- g = Schema::AlterTableGenerator.new(self, &block)
218
- alter_table_sql_list(name, g.operations).each {|sql| execute(sql)}
219
- end
220
-
221
- # Adds a column to the specified table. This method expects a column name,
222
- # a datatype and optionally a hash with additional constraints and options:
223
- #
224
- # DB.add_column :items, :name, :text, :unique => true, :null => false
225
- # DB.add_column :items, :category, :text, :default => 'ruby'
226
- def add_column(table, *args)
227
- alter_table(table) {add_column(*args)}
228
- end
229
-
230
- # Removes a column from the specified table:
231
- #
232
- # DB.drop_column :items, :category
233
- def drop_column(table, *args)
234
- alter_table(table) {drop_column(*args)}
235
- end
236
-
237
- # Renames a column in the specified table. This method expects the current
238
- # column name and the new column name:
239
- #
240
- # DB.rename_column :items, :cntr, :counter
241
- def rename_column(table, *args)
242
- alter_table(table) {rename_column(*args)}
243
- end
244
-
245
- # Set the data type for the given column in the given table:
246
- #
247
- # DB.set_column_type :items, :price, :float
248
- def set_column_type(table, *args)
249
- alter_table(table) {set_column_type(*args)}
250
- end
251
-
252
- # Sets the default value for the given column in the given table:
253
- #
254
- # DB.set_column_default :items, :category, 'perl!'
255
- def set_column_default(table, *args)
256
- alter_table(table) {set_column_default(*args)}
257
- end
258
-
259
- # Adds an index to a table for the given columns:
260
- #
261
- # DB.add_index :posts, :title
262
- # DB.add_index :posts, [:author, :title], :unique => true
263
- def add_index(table, *args)
264
- alter_table(table) {add_index(*args)}
265
- end
266
-
267
- # Removes an index for the given table and column/s:
268
- #
269
- # DB.drop_index :posts, :title
270
- # DB.drop_index :posts, [:author, :title]
271
- def drop_index(table, *args)
272
- alter_table(table) {drop_index(*args)}
273
- end
274
-
275
- # Returns true if the given table exists.
276
- def table_exists?(name)
277
- if respond_to?(:tables)
278
- tables.include?(name.to_sym)
279
- else
280
- from(name).first && true
281
- end
282
- rescue
283
- false
284
- end
285
-
286
- # Creates a view based on a dataset or an SQL string:
287
- #
288
- # DB.create_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
289
- # DB.create_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
290
- def create_view(name, source)
291
- source = source.sql if source.is_a?(Dataset)
292
- execute("CREATE VIEW #{name} AS #{source}")
293
- end
294
-
295
- # Creates a view, replacing it if it already exists:
296
- #
297
- # DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
298
- # DB.create_or_replace_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
299
- def create_or_replace_view(name, source)
300
- source = source.sql if source.is_a?(Dataset)
301
- execute("CREATE OR REPLACE VIEW #{name} AS #{source}")
302
- end
303
-
304
- # Drops a view:
305
- #
306
- # DB.drop_view(:cheap_items)
307
- def drop_view(name)
308
- execute("DROP VIEW #{name}")
309
- end
310
-
311
- SQL_BEGIN = 'BEGIN'.freeze
312
- SQL_COMMIT = 'COMMIT'.freeze
313
- SQL_ROLLBACK = 'ROLLBACK'.freeze
314
-
315
- # A simple implementation of SQL transactions. Nested transactions are not
316
- # supported - calling #transaction within a transaction will reuse the
317
- # current transaction. May be overridden for databases that support nested
318
- # transactions.
319
- def transaction
320
- @pool.hold do |conn|
321
- @transactions ||= []
322
- if @transactions.include? Thread.current
323
- return yield(conn)
324
- end
325
- conn.execute(SQL_BEGIN)
326
- begin
327
- @transactions << Thread.current
328
- result = yield(conn)
329
- conn.execute(SQL_COMMIT)
330
- result
331
- rescue => e
332
- conn.execute(SQL_ROLLBACK)
333
- raise e unless Error::Rollback === e
334
- ensure
335
- @transactions.delete(Thread.current)
336
- end
337
- end
338
- end
339
-
340
- @@adapters = Hash.new
341
-
342
- # Sets the adapter scheme for the Database class. Call this method in
343
- # descendnants of Database to allow connection using a URL. For example the
344
- # following:
345
- # class DB2::Database < Sequel::Database
346
- # set_adapter_scheme :db2
347
- # ...
348
- # end
349
- # would allow connection using:
350
- # Sequel.open('db2://user:password@dbserver/mydb')
351
- def self.set_adapter_scheme(scheme)
352
- @scheme = scheme
353
- @@adapters[scheme.to_sym] = self
354
- end
355
-
356
- # Returns the scheme for the Database class.
357
- def self.adapter_scheme
358
- @scheme
359
- end
360
-
361
- # Converts a uri to an options hash. These options are then passed
362
- # to a newly created database object.
363
- def self.uri_to_options(uri)
364
- {
365
- :user => uri.user,
366
- :password => uri.password,
367
- :host => uri.host,
368
- :port => uri.port,
369
- :database => (uri.path =~ /\/(.*)/) && ($1)
370
- }
371
- end
372
-
373
- def self.adapter_class(scheme)
374
- adapter_name = scheme.to_s =~ /\-/ ? scheme.to_s.gsub('-', '_').to_sym : scheme.to_sym
375
- scheme = scheme.to_sym
376
-
377
- if (klass = @@adapters[scheme]).nil?
378
- # attempt to load the adapter file
379
- begin
380
- require File.join(File.dirname(__FILE__), "adapters/#{scheme}")
381
- rescue LoadError => e
382
- raise Error::AdapterNotFound, "Could not load #{scheme} adapter:\n #{e.message}"
383
- end
384
-
385
- # make sure we actually loaded the adapter
386
- if (klass = @@adapters[scheme]).nil?
387
- raise Error::AdapterNotFound, "Could not load #{scheme} adapter"
388
- end
389
- end
390
- return klass
391
- end
392
-
393
- # call-seq:
394
- # Sequel::Database.connect(conn_string)
395
- # Sequel::Database.connect(opts)
396
- # Sequel.connect(conn_string)
397
- # Sequel.connect(opts)
398
- # Sequel.open(conn_string)
399
- # Sequel.open(opts)
400
- #
401
- # Creates a new database object based on the supplied connection string
402
- # and or options. If a URI is used, the URI scheme determines the database
403
- # class used, and the rest of the string specifies the connection options.
404
- # For example:
405
- #
406
- # DB = Sequel.open 'sqlite:///blog.db'
407
- #
408
- # The second form of this method takes an options:
409
- #
410
- # DB = Sequel.open :adapter => :sqlite, :database => 'blog.db'
411
- def self.connect(conn_string, opts = nil)
412
- if conn_string.is_a?(String)
413
- uri = URI.parse(conn_string)
414
- scheme = uri.scheme
415
- scheme = :dbi if scheme =~ /^dbi-(.+)/
416
- c = adapter_class(scheme)
417
- c.new(c.uri_to_options(uri).merge(opts || {}))
418
- else
419
- opts = conn_string.merge(opts || {})
420
- c = adapter_class(opts[:adapter])
421
- c.new(opts)
422
- end
423
- end
424
-
425
- @@single_threaded = false
426
-
427
- # Sets the default single_threaded mode for new databases.
428
- def self.single_threaded=(value)
429
- @@single_threaded = value
430
- end
431
- end
432
- end
433
-