sequel 3.33.0 → 3.34.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +140 -0
- data/Rakefile +7 -0
- data/bin/sequel +22 -2
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +3 -1
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +23 -3
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/schema_modification.rdoc +18 -2
- data/doc/virtual_rows.rdoc +49 -0
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/ibmdb.rb +9 -4
- data/lib/sequel/adapters/jdbc.rb +9 -4
- data/lib/sequel/adapters/jdbc/h2.rb +8 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
- data/lib/sequel/adapters/mock.rb +24 -3
- data/lib/sequel/adapters/mysql.rb +29 -50
- data/lib/sequel/adapters/mysql2.rb +13 -28
- data/lib/sequel/adapters/oracle.rb +8 -2
- data/lib/sequel/adapters/postgres.rb +115 -20
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -3
- data/lib/sequel/adapters/shared/mysql.rb +59 -11
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +127 -30
- data/lib/sequel/adapters/shared/sqlite.rb +55 -38
- data/lib/sequel/adapters/sqlite.rb +9 -3
- data/lib/sequel/adapters/swift.rb +2 -2
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +10 -0
- data/lib/sequel/ast_transformer.rb +4 -0
- data/lib/sequel/connection_pool.rb +8 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -0
- data/lib/sequel/core.rb +24 -3
- data/lib/sequel/database/connecting.rb +24 -14
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +16 -25
- data/lib/sequel/database/query.rb +20 -2
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +120 -23
- data/lib/sequel/dataset/actions.rb +91 -18
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/prepared_statements.rb +6 -2
- data/lib/sequel/dataset/sql.rb +68 -51
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
- data/lib/sequel/extensions/migration.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +227 -31
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +11 -2
- data/lib/sequel/model/associations.rb +35 -7
- data/lib/sequel/model/base.rb +159 -36
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +11 -3
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +4 -3
- data/lib/sequel/plugins/prepared_statements.rb +3 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +417 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +12 -15
- data/spec/adapters/mysql_spec.rb +81 -23
- data/spec/adapters/postgres_spec.rb +444 -77
- data/spec/adapters/spec_helper.rb +2 -0
- data/spec/adapters/sqlite_spec.rb +8 -8
- data/spec/core/connection_pool_spec.rb +85 -0
- data/spec/core/database_spec.rb +29 -5
- data/spec/core/dataset_spec.rb +171 -3
- data/spec/core/expression_filters_spec.rb +364 -0
- data/spec/core/mock_adapter_spec.rb +17 -3
- data/spec/core/schema_spec.rb +133 -0
- data/spec/extensions/association_dependencies_spec.rb +13 -13
- data/spec/extensions/caching_spec.rb +26 -3
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +5 -2
- data/spec/extensions/identity_map_spec.rb +17 -0
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +2 -2
- data/spec/extensions/list_spec.rb +4 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +6 -2
- data/spec/extensions/nested_attributes_spec.rb +20 -0
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +2 -2
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_spec.rb +4 -0
- data/spec/extensions/pretty_table_spec.rb +6 -0
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +231 -11
- data/spec/extensions/schema_spec.rb +14 -2
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/sharding_spec.rb +6 -6
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +2 -1
- data/spec/extensions/sql_expr_spec.rb +28 -19
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +9 -1
- data/spec/integration/associations_test.rb +6 -6
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +89 -26
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +3 -3
- data/spec/integration/plugin_test.rb +85 -22
- data/spec/integration/prepared_statement_test.rb +28 -8
- data/spec/integration/schema_test.rb +78 -7
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +4 -6
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/associations_spec.rb +94 -8
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/hooks_spec.rb +2 -2
- data/spec/model/model_spec.rb +19 -7
- data/spec/model/record_spec.rb +135 -58
- data/spec/model/spec_helper.rb +1 -0
- metadata +35 -7
@@ -229,13 +229,19 @@ module Sequel
|
|
229
229
|
end
|
230
230
|
end
|
231
231
|
unless cps
|
232
|
-
cps = log_yield("
|
232
|
+
cps = log_yield("PREPARE #{name}: #{sql}"){conn.prepare(sql)}
|
233
233
|
conn.prepared_statements[name] = [cps, sql]
|
234
234
|
end
|
235
|
+
log_sql = "EXECUTE #{name}"
|
236
|
+
if ps.log_sql
|
237
|
+
log_sql << " ("
|
238
|
+
log_sql << sql
|
239
|
+
log_sql << ")"
|
240
|
+
end
|
235
241
|
if block
|
236
|
-
log_yield(
|
242
|
+
log_yield(log_sql, args){cps.execute(ps_args, &block)}
|
237
243
|
else
|
238
|
-
log_yield(
|
244
|
+
log_yield(log_sql, args){cps.execute!(ps_args){|r|}}
|
239
245
|
case type
|
240
246
|
when :insert
|
241
247
|
conn.last_insert_row_id
|
@@ -136,10 +136,10 @@ module Sequel
|
|
136
136
|
Database::DatasetClass = self
|
137
137
|
|
138
138
|
# Set the columns and yield the hashes to the block.
|
139
|
-
def fetch_rows(sql
|
139
|
+
def fetch_rows(sql)
|
140
140
|
execute(sql) do |res|
|
141
141
|
@columns = res.fields
|
142
|
-
res.each
|
142
|
+
res.each{|h| yield h}
|
143
143
|
end
|
144
144
|
self
|
145
145
|
end
|
@@ -36,11 +36,6 @@ module Sequel
|
|
36
36
|
include Sequel::MySQL::DatasetMethods
|
37
37
|
APOS = Dataset::APOS
|
38
38
|
|
39
|
-
# Use execute_insert to execute the replace_sql.
|
40
|
-
def replace(*args)
|
41
|
-
execute_insert(replace_sql(*args))
|
42
|
-
end
|
43
|
-
|
44
39
|
private
|
45
40
|
|
46
41
|
# Use Swift's escape method for quoting.
|
@@ -85,6 +85,16 @@ module Sequel
|
|
85
85
|
def log_connection_execute(conn, sql)
|
86
86
|
conn.execute(sql)
|
87
87
|
end
|
88
|
+
|
89
|
+
# Remove all other options except for ones specifically handled, as
|
90
|
+
# otherwise swift passes them to dbic++ which passes them to PostgreSQL
|
91
|
+
# which can raise an error.
|
92
|
+
def server_opts(o)
|
93
|
+
o = super
|
94
|
+
so = {}
|
95
|
+
[:db, :user, :password, :host, :port].each{|s| so[s] = o[s] if o.has_key?(s)}
|
96
|
+
so
|
97
|
+
end
|
88
98
|
|
89
99
|
# Extend the adapter with the Swift PostgreSQL AdapterMethods.
|
90
100
|
def setup_connection(conn)
|
@@ -66,6 +66,8 @@ module Sequel
|
|
66
66
|
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
|
67
67
|
when SQL::JoinClause
|
68
68
|
SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
|
69
|
+
when SQL::Wrapper
|
70
|
+
SQL::Wrapper.new(v(o.value))
|
69
71
|
else
|
70
72
|
o
|
71
73
|
end
|
@@ -172,6 +174,8 @@ module Sequel
|
|
172
174
|
def v(o)
|
173
175
|
if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op)
|
174
176
|
l, r = o.args
|
177
|
+
l = l.value if l.is_a?(Sequel::SQL::Wrapper)
|
178
|
+
r = r.value if r.is_a?(Sequel::SQL::Wrapper)
|
175
179
|
if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString)
|
176
180
|
key = bind_key(l)
|
177
181
|
if (old = binds[key]) && old != r
|
@@ -57,6 +57,14 @@ class Sequel::ConnectionPool
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
extend ClassMethods
|
60
|
+
|
61
|
+
# The after_connect proc used for this pool. This is called with each new
|
62
|
+
# connection made, and is usually used to set custom per-connection settings.
|
63
|
+
attr_accessor :after_connect
|
64
|
+
|
65
|
+
# The disconnect_proc used for the pool. This is called with each connection
|
66
|
+
# that is disconnected, usually to clean up related resources.
|
67
|
+
attr_accessor :disconnection_proc
|
60
68
|
|
61
69
|
# Instantiates a connection pool with the given options. The block is called
|
62
70
|
# with a single symbol (specifying the server/shard to use) every time a new
|
@@ -26,6 +26,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
|
|
26
26
|
def add_servers(servers)
|
27
27
|
servers.each{|s| @servers[s] = s}
|
28
28
|
end
|
29
|
+
|
30
|
+
# Yield all of the currently established connections
|
31
|
+
def all_connections
|
32
|
+
@conns.values.each{|c| yield c}
|
33
|
+
end
|
29
34
|
|
30
35
|
# The connection for the given server.
|
31
36
|
def conn(server=:default)
|
@@ -45,6 +45,23 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
45
45
|
@allocated[server]
|
46
46
|
end
|
47
47
|
|
48
|
+
# Yield all of the available connections, and the ones currently allocated to
|
49
|
+
# this thread. This will not yield connections currently allocated to other
|
50
|
+
# threads, as it is not safe to operate on them. This holds the mutex while
|
51
|
+
# it is yielding all of the connections, which means that until
|
52
|
+
# the method's block returns, the pool is locked.
|
53
|
+
def all_connections
|
54
|
+
t = Thread.current
|
55
|
+
sync do
|
56
|
+
@allocated.values.each do |threads|
|
57
|
+
threads.each do |thread, conn|
|
58
|
+
yield conn if t == thread
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@available_connections.values.each{|v| v.each{|c| yield c}}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
48
65
|
# An array of connections opened but not currently used, for the given
|
49
66
|
# server. Nonexistent servers will return nil. Treat this as read only, do
|
50
67
|
# not modify the resulting object.
|
@@ -8,6 +8,11 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
8
8
|
@conn ? 1 : 0
|
9
9
|
end
|
10
10
|
|
11
|
+
# Yield the connection if one has been made.
|
12
|
+
def all_connections
|
13
|
+
yield @conn if @conn
|
14
|
+
end
|
15
|
+
|
11
16
|
# Disconnect the connection from the database.
|
12
17
|
def disconnect(opts=nil, &block)
|
13
18
|
return unless @conn
|
@@ -36,6 +36,20 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
36
36
|
@allocated.length + @available_connections.length
|
37
37
|
end
|
38
38
|
|
39
|
+
# Yield all of the available connections, and the one currently allocated to
|
40
|
+
# this thread. This will not yield connections currently allocated to other
|
41
|
+
# threads, as it is not safe to operate on them. This holds the mutex while
|
42
|
+
# it is yielding all of the available connections, which means that until
|
43
|
+
# the method's block returns, the pool is locked.
|
44
|
+
def all_connections
|
45
|
+
hold do |c|
|
46
|
+
sync do
|
47
|
+
yield c
|
48
|
+
@available_connections.each{|c| yield c}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
39
53
|
# Removes all connections currently available, optionally
|
40
54
|
# yielding each connection to the given block. This method has the effect of
|
41
55
|
# disconnecting from the database, assuming that no connections are currently
|
data/lib/sequel/core.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
warn 'Sequel support for ruby <1.8.7 is deprecated and will be removed in 3.35.0' if RUBY_VERSION < '1.8.7'
|
1
2
|
%w'bigdecimal date thread time uri'.each{|f| require f}
|
2
3
|
|
3
4
|
# Top level module for Sequel
|
@@ -17,8 +18,10 @@
|
|
17
18
|
#
|
18
19
|
# Sequel.sqlite('blog.db'){|db| puts db[:users].count}
|
19
20
|
#
|
20
|
-
#
|
21
|
-
#
|
21
|
+
# Sequel currently adds methods to the Array, Hash, String and Symbol classes by
|
22
|
+
# default. You can either require 'sequel/no_core_ext' or set the
|
23
|
+
# +SEQUEL_NO_CORE_EXTENSIONS+ constant or environment variable before requiring
|
24
|
+
# sequel to have # Sequel not add methods to those classes.
|
22
25
|
#
|
23
26
|
# For a more expanded introduction, see the {README}[link:files/README_rdoc.html].
|
24
27
|
# For a quicker introduction, see the {cheat sheet}[link:files/doc/cheat_sheet_rdoc.html].
|
@@ -140,6 +143,24 @@ module Sequel
|
|
140
143
|
def self.connect(*args, &block)
|
141
144
|
Database.connect(*args, &block)
|
142
145
|
end
|
146
|
+
|
147
|
+
if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
|
148
|
+
# Whether the core extensions are enabled. The core extensions are enabled by
|
149
|
+
# default for backwards compatibility, but can be disabled using the SEQUEL_NO_CORE_EXTENSIONS
|
150
|
+
# constant or environment variable.
|
151
|
+
def self.core_extensions?
|
152
|
+
# We override this method to return true inside the core_extensions.rb file,
|
153
|
+
# but we also set it here because that file is not loaded until most of Sequel
|
154
|
+
# is finished loading, and parts of Sequel check whether the core extensions
|
155
|
+
# are loaded.
|
156
|
+
true
|
157
|
+
end
|
158
|
+
else
|
159
|
+
def self.core_extensions?
|
160
|
+
false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
143
164
|
|
144
165
|
# Convert the +exception+ to the given class. The given class should be
|
145
166
|
# <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
|
@@ -356,7 +377,7 @@ module Sequel
|
|
356
377
|
private_class_method :adapter_method, :def_adapter_method
|
357
378
|
|
358
379
|
require(%w"metaprogramming sql connection_pool exceptions dataset database timezones ast_transformer version")
|
359
|
-
|
380
|
+
extension(:core_extensions) if Sequel.core_extensions?
|
360
381
|
|
361
382
|
# Add the database adapter class methods to Sequel via metaprogramming
|
362
383
|
def_adapter_method(*Database::ADAPTERS)
|
@@ -46,6 +46,7 @@ module Sequel
|
|
46
46
|
when String
|
47
47
|
if match = /\A(jdbc|do):/o.match(conn_string)
|
48
48
|
c = adapter_class(match[1].to_sym)
|
49
|
+
opts = opts.merge(:orig_opts=>opts.dup)
|
49
50
|
opts = {:uri=>conn_string}.merge(opts)
|
50
51
|
else
|
51
52
|
uri = URI.parse(conn_string)
|
@@ -55,11 +56,14 @@ module Sequel
|
|
55
56
|
uri_options = c.send(:uri_to_options, uri)
|
56
57
|
uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
|
57
58
|
uri_options.to_a.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
|
59
|
+
opts = opts.merge(:orig_opts=>opts.dup)
|
60
|
+
opts[:uri] = conn_string
|
58
61
|
opts = uri_options.merge(opts)
|
59
62
|
opts[:adapter] = scheme
|
60
63
|
end
|
61
64
|
when Hash
|
62
65
|
opts = conn_string.merge(opts)
|
66
|
+
opts = opts.merge(:orig_opts=>opts.dup)
|
63
67
|
c = adapter_class(opts[:adapter_class] || opts[:adapter] || opts['adapter'])
|
64
68
|
else
|
65
69
|
raise Error, "Sequel::Database.connect takes either a Hash or a String, given: #{conn_string.inspect}"
|
@@ -209,20 +213,26 @@ module Sequel
|
|
209
213
|
@single_threaded
|
210
214
|
end
|
211
215
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
216
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
|
217
|
+
# Acquires a database connection, yielding it to the passed block. This is
|
218
|
+
# useful if you want to make sure the same connection is used for all
|
219
|
+
# database queries in the block. It is also useful if you want to gain
|
220
|
+
# direct access to the underlying connection object if you need to do
|
221
|
+
# something Sequel does not natively support.
|
222
|
+
#
|
223
|
+
# If a server option is given, acquires a connection for that specific
|
224
|
+
# server, instead of the :default server.
|
225
|
+
#
|
226
|
+
# DB.synchronize do |conn|
|
227
|
+
# ...
|
228
|
+
# end
|
229
|
+
def synchronize(server=nil)
|
230
|
+
@pool.hold(server || :default){|conn| yield conn}
|
231
|
+
end
|
232
|
+
else
|
233
|
+
def synchronize(server=nil, &block)
|
234
|
+
@pool.hold(server || :default, &block)
|
235
|
+
end
|
226
236
|
end
|
227
237
|
|
228
238
|
# Attempts to acquire a database connection. Returns true if successful.
|
@@ -88,6 +88,7 @@ module Sequel
|
|
88
88
|
# end
|
89
89
|
def extend_datasets(mod=nil, &block)
|
90
90
|
raise(Error, "must provide either mod or block, not both") if mod && block
|
91
|
+
reset_schema_utility_dataset
|
91
92
|
mod = Module.new(&block) if block
|
92
93
|
if @dataset_modules.empty?
|
93
94
|
@dataset_modules = [mod]
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -121,10 +121,14 @@ module Sequel
|
|
121
121
|
end
|
122
122
|
|
123
123
|
# Returns a string representation of the database object including the
|
124
|
-
# class name and
|
125
|
-
# cannot be constructed).
|
124
|
+
# class name and connection URI and options used when connecting (if any).
|
126
125
|
def inspect
|
127
|
-
|
126
|
+
a = []
|
127
|
+
a << uri.inspect if uri
|
128
|
+
if (oo = opts[:orig_opts]) && !oo.empty?
|
129
|
+
a << oo.inspect
|
130
|
+
end
|
131
|
+
"#<#{self.class}: #{a.join(' ')}>"
|
128
132
|
end
|
129
133
|
|
130
134
|
# Proxy the literal call to the dataset.
|
@@ -148,6 +152,12 @@ module Sequel
|
|
148
152
|
false
|
149
153
|
end
|
150
154
|
|
155
|
+
# Whether the database supports DROP TABLE IF EXISTS syntax,
|
156
|
+
# default is the same as #supports_create_table_if_not_exists?.
|
157
|
+
def supports_drop_table_if_exists?
|
158
|
+
supports_create_table_if_not_exists?
|
159
|
+
end
|
160
|
+
|
151
161
|
# Whether the database and adapter support prepared transactions
|
152
162
|
# (two-phase commit), false by default.
|
153
163
|
def supports_prepared_transactions?
|
@@ -197,29 +207,10 @@ module Sequel
|
|
197
207
|
end
|
198
208
|
end
|
199
209
|
|
200
|
-
# Returns the URI
|
201
|
-
#
|
202
|
-
# This method can raise an error if the database used options
|
203
|
-
# instead of a connection string, and will not include uri
|
204
|
-
# parameters.
|
205
|
-
#
|
206
|
-
# Sequel.connect('postgres://localhost/db?user=billg').url
|
207
|
-
# # => "postgres://billg@localhost/db"
|
210
|
+
# Returns the URI use to connect to the database. If a URI
|
211
|
+
# was not used when connecting, returns nil.
|
208
212
|
def uri
|
209
|
-
uri
|
210
|
-
adapter_scheme.to_s,
|
211
|
-
nil,
|
212
|
-
@opts[:host],
|
213
|
-
@opts[:port],
|
214
|
-
nil,
|
215
|
-
"/#{@opts[:database]}",
|
216
|
-
nil,
|
217
|
-
nil,
|
218
|
-
nil
|
219
|
-
)
|
220
|
-
uri.user = @opts[:user]
|
221
|
-
uri.password = @opts[:password] if uri.user
|
222
|
-
uri.to_s
|
213
|
+
opts[:uri]
|
223
214
|
end
|
224
215
|
|
225
216
|
# Explicit alias of uri for easier subclassing.
|
@@ -81,6 +81,24 @@ module Sequel
|
|
81
81
|
execute_dui(sql, opts, &block)
|
82
82
|
end
|
83
83
|
|
84
|
+
# Returns an array of hashes containing foreign key information from the
|
85
|
+
# table. Each hash will contain at least the following fields:
|
86
|
+
#
|
87
|
+
# :columns :: An array of columns in the given table
|
88
|
+
# :table :: The table referenced by the columns
|
89
|
+
# :key :: An array of columns referenced (in the table specified by :table),
|
90
|
+
# but can be nil on certain adapters if the primary key is referenced.
|
91
|
+
#
|
92
|
+
# The hash may also contain entries for:
|
93
|
+
#
|
94
|
+
# :deferrable :: Whether the constraint is deferrable
|
95
|
+
# :name :: The name of the constraint
|
96
|
+
# :on_delete :: The action to take ON DELETE
|
97
|
+
# :on_update :: The action to take ON UPDATE
|
98
|
+
def foreign_key_list(table, opts={})
|
99
|
+
raise NotImplemented, "#foreign_key_list should be overridden by adapters"
|
100
|
+
end
|
101
|
+
|
84
102
|
# Returns a single value from the database, e.g.:
|
85
103
|
#
|
86
104
|
# DB.get(1) # SELECT 1
|
@@ -286,11 +304,11 @@ module Sequel
|
|
286
304
|
if supports_savepoints?
|
287
305
|
unless @transactions[conn]
|
288
306
|
@transactions[conn] = {:savepoint_level=>0}
|
289
|
-
@transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
|
307
|
+
@transactions[conn][:prepare] = opts[:prepare] if opts[:prepare] && supports_prepared_transactions?
|
290
308
|
end
|
291
309
|
else
|
292
310
|
@transactions[conn] = {}
|
293
|
-
@transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
|
311
|
+
@transactions[conn][:prepare] = opts[:prepare] if opts[:prepare] && supports_prepared_transactions?
|
294
312
|
end
|
295
313
|
end
|
296
314
|
|
@@ -92,9 +92,9 @@ module Sequel
|
|
92
92
|
# or not allowing NULL values (if false). If unspecified, will default
|
93
93
|
# to whatever the database default is.
|
94
94
|
# :on_delete :: Specify the behavior of this column when being deleted
|
95
|
-
# (:restrict, cascade, :set_null, :set_default, :no_action).
|
95
|
+
# (:restrict, :cascade, :set_null, :set_default, :no_action).
|
96
96
|
# :on_update :: Specify the behavior of this column when being updated
|
97
|
-
# (:restrict, cascade, :set_null, :set_default, :no_action).
|
97
|
+
# (:restrict, :cascade, :set_null, :set_default, :no_action).
|
98
98
|
# :primary_key :: Make the column as a single primary key column. This should only
|
99
99
|
# be used if you have a single, nonautoincrementing primary key column.
|
100
100
|
# :size :: The size of the column, generally used with string
|
@@ -6,15 +6,10 @@ module Sequel
|
|
6
6
|
# ---------------------
|
7
7
|
|
8
8
|
AUTOINCREMENT = 'AUTOINCREMENT'.freeze
|
9
|
-
CASCADE = 'CASCADE'.freeze
|
10
9
|
COMMA_SEPARATOR = ', '.freeze
|
11
|
-
NO_ACTION = 'NO ACTION'.freeze
|
12
10
|
NOT_NULL = ' NOT NULL'.freeze
|
13
11
|
NULL = ' NULL'.freeze
|
14
12
|
PRIMARY_KEY = ' PRIMARY KEY'.freeze
|
15
|
-
RESTRICT = 'RESTRICT'.freeze
|
16
|
-
SET_DEFAULT = 'SET DEFAULT'.freeze
|
17
|
-
SET_NULL = 'SET NULL'.freeze
|
18
13
|
TEMPORARY = 'TEMPORARY '.freeze
|
19
14
|
UNDERSCORE = '_'.freeze
|
20
15
|
UNIQUE = ' UNIQUE'.freeze
|
@@ -23,6 +18,9 @@ module Sequel
|
|
23
18
|
# The order of column modifiers to use when defining a column.
|
24
19
|
COLUMN_DEFINITION_ORDER = [:collate, :default, :null, :unique, :primary_key, :auto_increment, :references]
|
25
20
|
|
21
|
+
# The default options for join table columns.
|
22
|
+
DEFAULT_JOIN_TABLE_COLUMN_OPTIONS = {:null=>false}
|
23
|
+
|
26
24
|
# Adds a column to the specified table. This method expects a column name,
|
27
25
|
# a datatype and optionally a hash with additional constraints and options:
|
28
26
|
#
|
@@ -76,6 +74,54 @@ module Sequel
|
|
76
74
|
nil
|
77
75
|
end
|
78
76
|
|
77
|
+
# Create a join table using a hash of foreign keys to referenced
|
78
|
+
# table names. Example:
|
79
|
+
#
|
80
|
+
# create_join_table(:cat_id=>:cats, :dog_id=>:dogs)
|
81
|
+
# # CREATE TABLE cats_dogs (
|
82
|
+
# # cat_id integer NOT NULL REFERENCES cats,
|
83
|
+
# # dog_id integer NOT NULL REFERENCES dogs,
|
84
|
+
# # PRIMARY KEY (cat_id, dog_id)
|
85
|
+
# # )
|
86
|
+
# # CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs(dog_id, cat_id)
|
87
|
+
#
|
88
|
+
# The primary key and index are used so that almost all operations
|
89
|
+
# on the table can benefit from one of the two indexes, and the primary
|
90
|
+
# key ensures that entries in the table are unique, which is the typical
|
91
|
+
# desire for a join table.
|
92
|
+
#
|
93
|
+
# You can provide column options by making the values in the hash
|
94
|
+
# be option hashes, so long as the option hashes have a :table
|
95
|
+
# entry giving the table referenced:
|
96
|
+
#
|
97
|
+
# create_join_table(:cat_id=>{:table=>:cats, :type=>Bignum}, :dog_id=>:dogs)
|
98
|
+
#
|
99
|
+
# You can provide a second argument which is a table options hash:
|
100
|
+
#
|
101
|
+
# create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :temp=>true)
|
102
|
+
#
|
103
|
+
# Some table options are handled specially:
|
104
|
+
#
|
105
|
+
# :index_options :: The options to pass to the index
|
106
|
+
# :name :: The name of the table to create
|
107
|
+
# :no_index :: Set to true not to create the second index.
|
108
|
+
# :no_primary_key :: Set to true to not create the primary key.
|
109
|
+
def create_join_table(hash, options={})
|
110
|
+
keys = hash.keys.sort_by{|k| k.to_s}
|
111
|
+
create_table(join_table_name(hash, options), options) do
|
112
|
+
keys.each do |key|
|
113
|
+
v = hash[key]
|
114
|
+
unless v.is_a?(Hash)
|
115
|
+
v = {:table=>v}
|
116
|
+
end
|
117
|
+
v = DEFAULT_JOIN_TABLE_COLUMN_OPTIONS.merge(v)
|
118
|
+
foreign_key(key, v)
|
119
|
+
end
|
120
|
+
primary_key(keys) unless options[:no_primary_key]
|
121
|
+
index(keys.reverse, options[:index_options] || {}) unless options[:no_index]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
79
125
|
# Creates a table with the columns given in the provided block:
|
80
126
|
#
|
81
127
|
# DB.create_table :posts do
|
@@ -102,18 +148,18 @@ module Sequel
|
|
102
148
|
# Forcibly create a table, attempting to drop it if it already exists, then creating it.
|
103
149
|
#
|
104
150
|
# DB.create_table!(:a){Integer :a}
|
105
|
-
# # SELECT
|
151
|
+
# # SELECT NULL FROM a LIMIT 1 -- check existence
|
106
152
|
# # DROP TABLE a -- drop table if already exists
|
107
153
|
# # CREATE TABLE a (a integer)
|
108
154
|
def create_table!(name, options={}, &block)
|
109
|
-
drop_table
|
155
|
+
drop_table?(name)
|
110
156
|
create_table(name, options, &block)
|
111
157
|
end
|
112
158
|
|
113
159
|
# Creates the table unless the table already exists.
|
114
160
|
#
|
115
161
|
# DB.create_table?(:a){Integer :a}
|
116
|
-
# # SELECT
|
162
|
+
# # SELECT NULL FROM a LIMIT 1 -- check existence
|
117
163
|
# # CREATE TABLE a (a integer) -- if it doesn't already exist
|
118
164
|
def create_table?(name, options={}, &block)
|
119
165
|
if supports_create_table_if_not_exists?
|
@@ -161,10 +207,19 @@ module Sequel
|
|
161
207
|
def drop_index(table, columns, options={})
|
162
208
|
alter_table(table){drop_index(columns, options)}
|
163
209
|
end
|
210
|
+
|
211
|
+
# Drop the join table that would have been created with the
|
212
|
+
# same arguments to create_join_table:
|
213
|
+
#
|
214
|
+
# drop_join_table(:cat_id=>:cats, :dog_id=>:dogs)
|
215
|
+
# # DROP TABLE cats_dogs
|
216
|
+
def drop_join_table(hash, options={})
|
217
|
+
drop_table(join_table_name(hash, options), options)
|
218
|
+
end
|
164
219
|
|
165
220
|
# Drops one or more tables corresponding to the given names:
|
166
221
|
#
|
167
|
-
# DB.drop_table(:posts)
|
222
|
+
# DB.drop_table(:posts) # DROP TABLE posts
|
168
223
|
# DB.drop_table(:posts, :comments)
|
169
224
|
# DB.drop_table(:posts, :comments, :cascade=>true)
|
170
225
|
def drop_table(*names)
|
@@ -176,6 +231,26 @@ module Sequel
|
|
176
231
|
nil
|
177
232
|
end
|
178
233
|
|
234
|
+
# Drops the table if it already exists. If it doesn't exist,
|
235
|
+
# does nothing.
|
236
|
+
#
|
237
|
+
# DB.drop_table?(:a)
|
238
|
+
# # SELECT NULL FROM a LIMIT 1 -- check existence
|
239
|
+
# # DROP TABLE a -- if it already exists
|
240
|
+
def drop_table?(*names)
|
241
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
242
|
+
if supports_drop_table_if_exists?
|
243
|
+
options = options.merge(:if_exists=>true)
|
244
|
+
names.each do |name|
|
245
|
+
drop_table(name, options)
|
246
|
+
end
|
247
|
+
else
|
248
|
+
names.each do |name|
|
249
|
+
drop_table(name, options) if table_exists?(name)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
179
254
|
# Drops one or more views corresponding to the given names:
|
180
255
|
#
|
181
256
|
# DB.drop_view(:cheap_items)
|
@@ -343,7 +418,7 @@ module Sequel
|
|
343
418
|
sql = " REFERENCES #{quote_schema_table(column[:table])}"
|
344
419
|
sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
|
345
420
|
sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
|
346
|
-
sql << " ON UPDATE #{
|
421
|
+
sql << " ON UPDATE #{on_update_clause(column[:on_update])}" if column[:on_update]
|
347
422
|
sql << " DEFERRABLE INITIALLY DEFERRED" if column[:deferrable]
|
348
423
|
sql
|
349
424
|
end
|
@@ -408,7 +483,7 @@ module Sequel
|
|
408
483
|
|
409
484
|
# SQL DDL statement to drop the table with the given name.
|
410
485
|
def drop_table_sql(name, options)
|
411
|
-
"DROP TABLE #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
486
|
+
"DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
|
412
487
|
end
|
413
488
|
|
414
489
|
# SQL DDL statement to drop a view with the given name.
|
@@ -440,6 +515,32 @@ module Sequel
|
|
440
515
|
indexes.map{|i| index_definition_sql(table_name, i)}
|
441
516
|
end
|
442
517
|
|
518
|
+
# Extract the join table name from the arguments given to create_join_table.
|
519
|
+
# Also does argument validation for the create_join_table method.
|
520
|
+
def join_table_name(hash, options)
|
521
|
+
entries = hash.values
|
522
|
+
raise Error, "must have 2 entries in hash given to (create|drop)_join_table" unless entries.length == 2
|
523
|
+
if options[:name]
|
524
|
+
options[:name]
|
525
|
+
else
|
526
|
+
table_names = entries.map{|e| join_table_name_extract(e)}
|
527
|
+
table_names.map{|t| t.to_s}.sort.join('_')
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
# Extract an individual join table name, which should either be a string
|
532
|
+
# or symbol, or a hash containing one of those as the value for :table.
|
533
|
+
def join_table_name_extract(entry)
|
534
|
+
case entry
|
535
|
+
when Symbol, String
|
536
|
+
entry
|
537
|
+
when Hash
|
538
|
+
join_table_name_extract(entry[:table])
|
539
|
+
else
|
540
|
+
raise Error, "can't extract table name from #{entry.inspect}"
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
443
544
|
# SQL DDL ON DELETE fragment to use, based on the given action.
|
444
545
|
# The following actions are recognized:
|
445
546
|
#
|
@@ -450,19 +551,15 @@ module Sequel
|
|
450
551
|
# but do not allow deferring the integrity check.
|
451
552
|
# * :set_default - Set columns referencing this row to their default value.
|
452
553
|
# * :set_null - Set columns referencing this row to NULL.
|
554
|
+
#
|
555
|
+
# Any other object given is just converted to a string, with "_" converted to " " and upcased.
|
453
556
|
def on_delete_clause(action)
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
SET_NULL
|
461
|
-
when :set_default
|
462
|
-
SET_DEFAULT
|
463
|
-
else
|
464
|
-
NO_ACTION
|
465
|
-
end
|
557
|
+
action.to_s.gsub("_", " ").upcase
|
558
|
+
end
|
559
|
+
|
560
|
+
# Alias of #on_delete_clause, since the two usually behave the same.
|
561
|
+
def on_update_clause(action)
|
562
|
+
on_delete_clause(action)
|
466
563
|
end
|
467
564
|
|
468
565
|
# Proxy the quote_schema_table method to the dataset
|