sequel 3.21.0 → 3.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
# The +ASTTransformer+ class is designed to handle the abstract syntax trees
|
|
3
|
+
# that Sequel uses internally and produce modified copies of them. By itself
|
|
4
|
+
# it only produces a straight copy. It's designed to be subclassed and have
|
|
5
|
+
# subclasses returned modified copies of the specific nodes that need to
|
|
6
|
+
# be modified.
|
|
7
|
+
class ASTTransformer
|
|
8
|
+
# Return +obj+ or a potentially transformed version of it.
|
|
9
|
+
def transform(obj)
|
|
10
|
+
v(obj)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Recursive version that handles all of Sequel's internal object types
|
|
16
|
+
# and produces copies of them.
|
|
17
|
+
def v(o)
|
|
18
|
+
case o
|
|
19
|
+
when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
|
|
20
|
+
o
|
|
21
|
+
when Array
|
|
22
|
+
o.map{|x| v(x)}
|
|
23
|
+
when Hash
|
|
24
|
+
h = {}
|
|
25
|
+
o.each{|k, val| h[v(k)] = v(val)}
|
|
26
|
+
h
|
|
27
|
+
when SQL::ComplexExpression
|
|
28
|
+
SQL::ComplexExpression.new(o.op, *v(o.args))
|
|
29
|
+
when SQL::Identifier
|
|
30
|
+
SQL::Identifier.new(v(o.value))
|
|
31
|
+
when SQL::QualifiedIdentifier
|
|
32
|
+
SQL::QualifiedIdentifier.new(v(o.table), v(o.column))
|
|
33
|
+
when SQL::OrderedExpression
|
|
34
|
+
SQL::OrderedExpression.new(v(o.expression), o.descending, :nulls=>o.nulls)
|
|
35
|
+
when SQL::AliasedExpression
|
|
36
|
+
SQL::AliasedExpression.new(v(o.expression), o.aliaz)
|
|
37
|
+
when SQL::CaseExpression
|
|
38
|
+
args = [v(o.conditions), v(o.default)]
|
|
39
|
+
args << v(o.expression) if o.expression?
|
|
40
|
+
SQL::CaseExpression.new(*args)
|
|
41
|
+
when SQL::Cast
|
|
42
|
+
SQL::Cast.new(v(o.expr), o.type)
|
|
43
|
+
when SQL::Function
|
|
44
|
+
SQL::Function.new(o.f, *v(o.args))
|
|
45
|
+
when SQL::Subscript
|
|
46
|
+
SQL::Subscript.new(v(o.f), v(o.sub))
|
|
47
|
+
when SQL::WindowFunction
|
|
48
|
+
SQL::WindowFunction.new(v(o.function), v(o.window))
|
|
49
|
+
when SQL::Window
|
|
50
|
+
opts = o.opts.dup
|
|
51
|
+
opts[:partition] = v(opts[:partition]) if opts[:partition]
|
|
52
|
+
opts[:order] = v(opts[:order]) if opts[:order]
|
|
53
|
+
SQL::Window.new(opts)
|
|
54
|
+
when SQL::PlaceholderLiteralString
|
|
55
|
+
args = if o.args.is_a?(Hash)
|
|
56
|
+
h = {}
|
|
57
|
+
o.args.each{|k,val| h[k] = v(val)}
|
|
58
|
+
h
|
|
59
|
+
else
|
|
60
|
+
v(o.args)
|
|
61
|
+
end
|
|
62
|
+
SQL::PlaceholderLiteralString.new(o.str, args, o.parens)
|
|
63
|
+
when SQL::JoinOnClause
|
|
64
|
+
SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.table), v(o.table_alias))
|
|
65
|
+
when SQL::JoinUsingClause
|
|
66
|
+
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
|
|
67
|
+
when SQL::JoinClause
|
|
68
|
+
SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
|
|
69
|
+
else
|
|
70
|
+
o
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Handles qualifying existing datasets, so that unqualified columns
|
|
76
|
+
# in the dataset are qualified with a given table name.
|
|
77
|
+
class Qualifier < ASTTransformer
|
|
78
|
+
# Store the dataset to use as the basis for qualification,
|
|
79
|
+
# and the table used to qualify unqualified columns.
|
|
80
|
+
def initialize(ds, table)
|
|
81
|
+
@ds = ds
|
|
82
|
+
@table = table
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Turn <tt>SQL::Identifier</tt>s and symbols that aren't implicitly
|
|
88
|
+
# qualified into <tt>SQL::QualifiedIdentifier</tt>s. For symbols that
|
|
89
|
+
# are not implicitly qualified by are implicitly aliased, return an
|
|
90
|
+
# <tt>SQL::AliasedExpression</tt>s with a qualified version of the symbol.
|
|
91
|
+
def v(o)
|
|
92
|
+
case o
|
|
93
|
+
when Symbol
|
|
94
|
+
t, column, aliaz = @ds.send(:split_symbol, o)
|
|
95
|
+
if t
|
|
96
|
+
o
|
|
97
|
+
elsif aliaz
|
|
98
|
+
SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(@table, SQL::Identifier.new(column)), aliaz)
|
|
99
|
+
else
|
|
100
|
+
SQL::QualifiedIdentifier.new(@table, o)
|
|
101
|
+
end
|
|
102
|
+
when SQL::Identifier
|
|
103
|
+
SQL::QualifiedIdentifier.new(@table, o)
|
|
104
|
+
when SQL::QualifiedIdentifier, SQL::JoinClause
|
|
105
|
+
# Return these directly, so we don't accidentally qualify symbols in them.
|
|
106
|
+
o
|
|
107
|
+
else
|
|
108
|
+
super
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# +Unbinder+ is used to take a dataset filter and return a modified version
|
|
114
|
+
# that unbinds already bound values and returns a dataset with bound value
|
|
115
|
+
# placeholders and a hash of bind values. You can then prepare the dataset
|
|
116
|
+
# and use the bound variables to execute it with the same values.
|
|
117
|
+
#
|
|
118
|
+
# This class only does a limited form of unbinding where the variable names
|
|
119
|
+
# and values can be associated unambiguously. The only cases it handles
|
|
120
|
+
# are <tt>SQL::ComplexExpression<tt> with an operator in +UNBIND_OPS+, a
|
|
121
|
+
# first argument that's an instance of a member of +UNBIND_KEY_CLASSES+, and
|
|
122
|
+
# a second argument that's an instance of a member of +UNBIND_VALUE_CLASSES+.
|
|
123
|
+
#
|
|
124
|
+
# So it can handle cases like:
|
|
125
|
+
#
|
|
126
|
+
# DB.filter(:a=>1).exclude(:b=>2).where{c > 3}
|
|
127
|
+
#
|
|
128
|
+
# But it cannot handle cases like:
|
|
129
|
+
#
|
|
130
|
+
# DB.filter(:a + 1 < 0)
|
|
131
|
+
class Unbinder < ASTTransformer
|
|
132
|
+
# The <tt>SQL::ComplexExpression<tt> operates that will be considered
|
|
133
|
+
# for transformation.
|
|
134
|
+
UNBIND_OPS = [:'=', :'!=', :<, :>, :<=, :>=]
|
|
135
|
+
|
|
136
|
+
# The key classes (first argument of the ComplexExpression) that will
|
|
137
|
+
# considered for transformation.
|
|
138
|
+
UNBIND_KEY_CLASSES = [Symbol, SQL::Identifier, SQL::QualifiedIdentifier]
|
|
139
|
+
|
|
140
|
+
# The value classes (second argument of the ComplexExpression) that
|
|
141
|
+
# will be considered for transformation.
|
|
142
|
+
UNBIND_VALUE_CLASSES = [Numeric, String, Date, Time]
|
|
143
|
+
|
|
144
|
+
# The hash of bind variables that were extracted from the dataset filter.
|
|
145
|
+
attr_reader :binds
|
|
146
|
+
|
|
147
|
+
# Intialize an empty +binds+ hash.
|
|
148
|
+
def initialize
|
|
149
|
+
@binds = {}
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
# Create a suitable bound variable key for the object, which should be
|
|
155
|
+
# an instance of one of the +UNBIND_KEY_CLASSES+.
|
|
156
|
+
def bind_key(obj)
|
|
157
|
+
case obj
|
|
158
|
+
when Symbol, String
|
|
159
|
+
obj
|
|
160
|
+
when SQL::Identifier
|
|
161
|
+
bind_key(obj.value)
|
|
162
|
+
when SQL::QualifiedIdentifier
|
|
163
|
+
:"#{bind_key(obj.table)}.#{bind_key(obj.column)}"
|
|
164
|
+
else
|
|
165
|
+
raise Error, "unhandled object in Sequel::Unbinder#bind_key: #{obj}"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Handle <tt>SQL::ComplexExpression</tt> instances with suitable ops
|
|
170
|
+
# and arguments, substituting the value with a bound variable placeholder
|
|
171
|
+
# and assigning it an entry in the +binds+ hash with a matching key.
|
|
172
|
+
def v(o)
|
|
173
|
+
if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op)
|
|
174
|
+
l, r = o.args
|
|
175
|
+
if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString)
|
|
176
|
+
key = bind_key(l)
|
|
177
|
+
if (old = binds[key]) && old != r
|
|
178
|
+
raise UnbindDuplicate, "two different values for #{key.inspect}: #{[r, old].inspect}"
|
|
179
|
+
end
|
|
180
|
+
binds[key] = r
|
|
181
|
+
SQL::ComplexExpression.new(o.op, l, :"$#{key}")
|
|
182
|
+
else
|
|
183
|
+
super
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
super
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -83,9 +83,10 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
|
83
83
|
end
|
|
84
84
|
yield conn
|
|
85
85
|
rescue Sequel::DatabaseDisconnectError
|
|
86
|
-
|
|
87
|
-
@allocated.delete(t)
|
|
86
|
+
oconn = conn
|
|
88
87
|
conn = nil
|
|
88
|
+
@disconnection_proc.call(oconn) if @disconnection_proc && oconn
|
|
89
|
+
@allocated.delete(t)
|
|
89
90
|
raise
|
|
90
91
|
ensure
|
|
91
92
|
sync{release(t)} if conn
|
|
@@ -53,7 +53,7 @@ class Sequel::ConnectionPool
|
|
|
53
53
|
|
|
54
54
|
# Return a connection pool class based on the given options.
|
|
55
55
|
def connection_pool_class(opts)
|
|
56
|
-
opts[:pool_class] || CONNECTION_POOL_MAP[[!!opts[:single_threaded], !!opts[:servers]]]
|
|
56
|
+
CONNECTION_POOL_MAP[opts[:pool_class]] || opts[:pool_class] || CONNECTION_POOL_MAP[[!!opts[:single_threaded], !!opts[:servers]]]
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
extend ClassMethods
|
data/lib/sequel/core.rb
CHANGED
|
@@ -188,7 +188,7 @@ module Sequel
|
|
|
188
188
|
# This is used to ensure that the files loaded are from the same version of
|
|
189
189
|
# Sequel as this file.
|
|
190
190
|
def self.require(files, subdir=nil)
|
|
191
|
-
Array(files).each{|f| super("#{File.dirname(__FILE__)}/#{"#{subdir}/" if subdir}#{f}")}
|
|
191
|
+
Array(files).each{|f| super("#{File.dirname(__FILE__).untaint}/#{"#{subdir}/" if subdir}#{f}")}
|
|
192
192
|
end
|
|
193
193
|
|
|
194
194
|
# Set whether to set the single threaded mode for all databases by default. By default,
|
|
@@ -228,12 +228,13 @@ module Sequel
|
|
|
228
228
|
end
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
-
# Converts the given +string+ into a
|
|
231
|
+
# Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
|
|
232
232
|
#
|
|
233
|
-
# Sequel.
|
|
233
|
+
# v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
|
|
234
|
+
# DB.literal(v) # => '10:20:30'
|
|
234
235
|
def self.string_to_time(string)
|
|
235
236
|
begin
|
|
236
|
-
|
|
237
|
+
SQLTime.parse(string)
|
|
237
238
|
rescue => e
|
|
238
239
|
raise convert_exception_class(e, InvalidValue)
|
|
239
240
|
end
|
|
@@ -292,7 +293,7 @@ module Sequel
|
|
|
292
293
|
|
|
293
294
|
private_class_method :adapter_method, :def_adapter_method
|
|
294
295
|
|
|
295
|
-
require(%w"metaprogramming sql connection_pool exceptions dataset database timezones version")
|
|
296
|
+
require(%w"metaprogramming sql connection_pool exceptions dataset database timezones ast_transformer version")
|
|
296
297
|
require('core_sql') if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
|
|
297
298
|
|
|
298
299
|
# Add the database adapter class methods to Sequel via metaprogramming
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods relating to adapters, connecting, disconnecting, and sharding
|
|
4
|
+
# :section: 4 - Methods relating to adapters, connecting, disconnecting, and sharding
|
|
5
5
|
# This methods involve the Database's connection pool.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
8
|
# Array of supported database adapters
|
|
9
|
-
ADAPTERS = %w'ado amalgalite db2 dbi do firebird informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
9
|
+
ADAPTERS = %w'ado amalgalite db2 dbi do firebird ibmdb informix jdbc mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
|
|
10
10
|
|
|
11
11
|
# Whether to use the single threaded connection pool by default
|
|
12
12
|
@@single_threaded = false
|
|
@@ -54,7 +54,7 @@ module Sequel
|
|
|
54
54
|
c = adapter_class(scheme)
|
|
55
55
|
uri_options = c.send(:uri_to_options, uri)
|
|
56
56
|
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
|
-
uri_options.
|
|
57
|
+
uri_options.to_a.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
|
|
58
58
|
opts = uri_options.merge(opts)
|
|
59
59
|
opts[:adapter] = scheme
|
|
60
60
|
end
|
|
@@ -155,8 +155,8 @@ module Sequel
|
|
|
155
155
|
|
|
156
156
|
# Disconnects all available connections from the connection pool. Any
|
|
157
157
|
# connections currently in use will not be disconnected. Options:
|
|
158
|
-
#
|
|
159
|
-
#
|
|
158
|
+
# :servers :: Should be a symbol specifing the server to disconnect from,
|
|
159
|
+
# or an array of symbols to specify multiple servers.
|
|
160
160
|
#
|
|
161
161
|
# Example:
|
|
162
162
|
#
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that set defaults for created datasets
|
|
4
|
+
# :section: 5 - Methods that set defaults for created datasets
|
|
5
5
|
# This methods change the default behavior of this database's datasets.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
data/lib/sequel/database/misc.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Miscellaneous methods
|
|
4
|
+
# :section: 7 - Miscellaneous methods
|
|
5
5
|
# These methods don't fit neatly into another category.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
8
8
|
# Converts a uri to an options hash. These options are then passed
|
|
9
9
|
# to a newly created database object.
|
|
10
|
-
def self.uri_to_options(uri)
|
|
10
|
+
def self.uri_to_options(uri)
|
|
11
11
|
{ :user => uri.user,
|
|
12
12
|
:password => uri.password,
|
|
13
13
|
:host => uri.host,
|
|
@@ -90,6 +90,12 @@ module Sequel
|
|
|
90
90
|
{:primary_key => true, :type => Integer, :auto_increment => true}
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# Whether the database supports CREATE TABLE IF NOT EXISTS syntax,
|
|
94
|
+
# false by default.
|
|
95
|
+
def supports_create_table_if_not_exists?
|
|
96
|
+
false
|
|
97
|
+
end
|
|
98
|
+
|
|
93
99
|
# Whether the database and adapter support prepared transactions
|
|
94
100
|
# (two-phase commit), false by default.
|
|
95
101
|
def supports_prepared_transactions?
|
|
@@ -175,12 +181,17 @@ module Sequel
|
|
|
175
181
|
def database_error_classes
|
|
176
182
|
[]
|
|
177
183
|
end
|
|
184
|
+
|
|
185
|
+
# Return true if exception represents a disconnect error, false otherwise.
|
|
186
|
+
def disconnect_error?(exception, opts)
|
|
187
|
+
opts[:disconnect]
|
|
188
|
+
end
|
|
178
189
|
|
|
179
190
|
# Convert the given exception to a DatabaseError, keeping message
|
|
180
191
|
# and traceback.
|
|
181
192
|
def raise_error(exception, opts={})
|
|
182
193
|
if !opts[:classes] || Array(opts[:classes]).any?{|c| exception.is_a?(c)}
|
|
183
|
-
raise Sequel.convert_exception_class(exception, opts
|
|
194
|
+
raise Sequel.convert_exception_class(exception, disconnect_error?(exception, opts) ? DatabaseDisconnectError : DatabaseError)
|
|
184
195
|
else
|
|
185
196
|
raise exception
|
|
186
197
|
end
|
|
@@ -204,10 +215,10 @@ module Sequel
|
|
|
204
215
|
# Typecast the value to a Date
|
|
205
216
|
def typecast_value_date(value)
|
|
206
217
|
case value
|
|
207
|
-
when Date
|
|
208
|
-
value
|
|
209
218
|
when DateTime, Time
|
|
210
219
|
Date.new(value.year, value.month, value.day)
|
|
220
|
+
when Date
|
|
221
|
+
value
|
|
211
222
|
when String
|
|
212
223
|
Sequel.string_to_date(value)
|
|
213
224
|
when Hash
|
|
@@ -219,12 +230,7 @@ module Sequel
|
|
|
219
230
|
|
|
220
231
|
# Typecast the value to a DateTime or Time depending on Sequel.datetime_class
|
|
221
232
|
def typecast_value_datetime(value)
|
|
222
|
-
|
|
223
|
-
if value.is_a?(Hash)
|
|
224
|
-
klass.send(klass == Time ? :mktime : :new, *[:year, :month, :day, :hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
|
|
225
|
-
else
|
|
226
|
-
Sequel.typecast_to_application_timestamp(value)
|
|
227
|
-
end
|
|
233
|
+
Sequel.typecast_to_application_timestamp(value)
|
|
228
234
|
end
|
|
229
235
|
|
|
230
236
|
# Typecast the value to a BigDecimal
|
|
@@ -244,9 +250,21 @@ module Sequel
|
|
|
244
250
|
Float(value)
|
|
245
251
|
end
|
|
246
252
|
|
|
247
|
-
#
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
# Used for checking/removing leading zeroes from strings so they don't get
|
|
254
|
+
# interpreted as octal.
|
|
255
|
+
LEADING_ZERO_RE = /\A0+(\d)/.freeze
|
|
256
|
+
if RUBY_VERSION >= '1.9'
|
|
257
|
+
# Typecast the value to an Integer
|
|
258
|
+
def typecast_value_integer(value)
|
|
259
|
+
(value.is_a?(String) && value =~ LEADING_ZERO_RE) ? Integer(value, 10) : Integer(value)
|
|
260
|
+
end
|
|
261
|
+
else
|
|
262
|
+
# Replacement string when replacing leading zeroes.
|
|
263
|
+
LEADING_ZERO_REP = "\\1".freeze
|
|
264
|
+
# Typecast the value to an Integer
|
|
265
|
+
def typecast_value_integer(value)
|
|
266
|
+
Integer(value.is_a?(String) ? value.sub(LEADING_ZERO_RE, LEADING_ZERO_REP) : value)
|
|
267
|
+
end
|
|
250
268
|
end
|
|
251
269
|
|
|
252
270
|
# Typecast the value to a String
|
|
@@ -258,12 +276,15 @@ module Sequel
|
|
|
258
276
|
def typecast_value_time(value)
|
|
259
277
|
case value
|
|
260
278
|
when Time
|
|
261
|
-
value
|
|
279
|
+
if value.is_a?(SQLTime)
|
|
280
|
+
value
|
|
281
|
+
else
|
|
282
|
+
SQLTime.create(value.hour, value.min, value.sec, value.respond_to?(:nsec) ? value.nsec/1000.0 : value.usec)
|
|
283
|
+
end
|
|
262
284
|
when String
|
|
263
285
|
Sequel.string_to_time(value)
|
|
264
286
|
when Hash
|
|
265
|
-
|
|
266
|
-
Time.mktime(t.year, t.month, t.day, *[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
|
|
287
|
+
SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
|
|
267
288
|
else
|
|
268
289
|
raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
|
|
269
290
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that execute queries and/or return results
|
|
4
|
+
# :section: 1 - Methods that execute queries and/or return results
|
|
5
5
|
# This methods generally execute SQL code on the database server.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -82,12 +82,12 @@ module Sequel
|
|
|
82
82
|
#
|
|
83
83
|
# DB.get(1) # SELECT 1
|
|
84
84
|
# # => 1
|
|
85
|
-
# DB.get{
|
|
85
|
+
# DB.get{server_version{}} # SELECT server_version()
|
|
86
86
|
def get(*args, &block)
|
|
87
87
|
dataset.get(*args, &block)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# Return a hash containing index information. Hash keys are index name symbols.
|
|
90
|
+
# Return a hash containing index information for the table. Hash keys are index name symbols.
|
|
91
91
|
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
|
92
92
|
# is an array of symbols of column names. The value of :unique is true or false
|
|
93
93
|
# depending on if the index is unique.
|
|
@@ -110,7 +110,6 @@ module Sequel
|
|
|
110
110
|
nil
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
# Parse the schema from the database.
|
|
114
113
|
# Returns the schema for the given table as an array with all members being arrays of length 2,
|
|
115
114
|
# the first member being the column name, and the second member being a hash of column information.
|
|
116
115
|
# Available options are:
|
|
@@ -129,7 +128,8 @@ module Sequel
|
|
|
129
128
|
# it means that primary key information is unavailable, not that the column
|
|
130
129
|
# is not a primary key.
|
|
131
130
|
# :ruby_default :: The database default for the column, as a ruby object. In many cases, complex
|
|
132
|
-
# database defaults cannot be parsed into ruby objects
|
|
131
|
+
# database defaults cannot be parsed into ruby objects, in which case nil will be
|
|
132
|
+
# used as the value.
|
|
133
133
|
# :type :: A symbol specifying the type, such as :integer or :string.
|
|
134
134
|
#
|
|
135
135
|
# Example:
|
|
@@ -169,13 +169,14 @@ module Sequel
|
|
|
169
169
|
# to the database.
|
|
170
170
|
#
|
|
171
171
|
# DB.table_exists?(:foo) # => false
|
|
172
|
+
# # SELECT * FROM foo LIMIT 1
|
|
172
173
|
def table_exists?(name)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
sch, table_name = schema_and_table(name)
|
|
175
|
+
name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
|
|
176
|
+
from(name).first
|
|
177
|
+
true
|
|
178
|
+
rescue
|
|
179
|
+
false
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
# Return all tables in the database as an array of symbols.
|
|
@@ -210,6 +211,13 @@ module Sequel
|
|
|
210
211
|
end
|
|
211
212
|
end
|
|
212
213
|
|
|
214
|
+
# Return all views in the database as an array of symbols.
|
|
215
|
+
#
|
|
216
|
+
# DB.views # => [:gold_albums, :artists_with_many_albums]
|
|
217
|
+
def views(opts={})
|
|
218
|
+
raise NotImplemented, "#views should be overridden by adapters"
|
|
219
|
+
end
|
|
220
|
+
|
|
213
221
|
private
|
|
214
222
|
|
|
215
223
|
# Internal generic transaction method. Any exception raised by the given
|
|
@@ -223,18 +231,18 @@ module Sequel
|
|
|
223
231
|
yield(conn)
|
|
224
232
|
rescue Exception => e
|
|
225
233
|
rollback_transaction(t, opts) if t
|
|
226
|
-
transaction_error(e)
|
|
234
|
+
transaction_error(e, :conn=>conn)
|
|
227
235
|
ensure
|
|
228
236
|
begin
|
|
229
|
-
|
|
237
|
+
commit_or_rollback_transaction(e, t, opts)
|
|
230
238
|
rescue Exception => e
|
|
231
|
-
raise_error(e, :classes=>database_error_classes)
|
|
239
|
+
raise_error(e, :classes=>database_error_classes, :conn=>conn)
|
|
232
240
|
ensure
|
|
233
241
|
remove_transaction(t)
|
|
234
242
|
end
|
|
235
243
|
end
|
|
236
244
|
end
|
|
237
|
-
|
|
245
|
+
|
|
238
246
|
# Add the current thread to the list of active transactions
|
|
239
247
|
def add_transaction
|
|
240
248
|
th = Thread.current
|
|
@@ -335,6 +343,29 @@ module Sequel
|
|
|
335
343
|
end
|
|
336
344
|
end
|
|
337
345
|
|
|
346
|
+
if (! defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
|
|
347
|
+
# Whether to commit the current transaction. On ruby 1.8 and rubinius,
|
|
348
|
+
# Thread.current.status is checked because Thread#kill skips rescue
|
|
349
|
+
# blocks (so exception would be nil), but the transaction should
|
|
350
|
+
# still be rolled back.
|
|
351
|
+
def commit_or_rollback_transaction(exception, thread, opts)
|
|
352
|
+
unless exception
|
|
353
|
+
if Thread.current.status == 'aborting'
|
|
354
|
+
rollback_transaction(thread, opts)
|
|
355
|
+
else
|
|
356
|
+
commit_transaction(thread, opts)
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
else
|
|
361
|
+
# Whether to commit the current transaction. On ruby 1.9 and JRuby,
|
|
362
|
+
# transactions will be committed if Thread#kill is used on an thread
|
|
363
|
+
# that has a transaction open, and there isn't a work around.
|
|
364
|
+
def commit_or_rollback_transaction(exception, thread, opts)
|
|
365
|
+
commit_transaction(thread, opts) unless exception
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
338
369
|
# SQL to commit a savepoint
|
|
339
370
|
def commit_savepoint_sql(depth)
|
|
340
371
|
SQL_RELEASE_SAVEPOINT % depth
|
|
@@ -434,13 +465,13 @@ module Sequel
|
|
|
434
465
|
:datetime
|
|
435
466
|
when /\Atime( with(out)? time zone)?\z/io
|
|
436
467
|
:time
|
|
437
|
-
when /\A(
|
|
468
|
+
when /\A(bool(ean)?)\z/io
|
|
438
469
|
:boolean
|
|
439
470
|
when /\A(real|float|double( precision)?)\z/io
|
|
440
471
|
:float
|
|
441
472
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+)\))?)|(?:small)?money)\z/io
|
|
442
473
|
$1 && $1 == '0' ? :integer : :decimal
|
|
443
|
-
when /bytea|
|
|
474
|
+
when /bytea|[bc]lob|image|(var)?binary/io
|
|
444
475
|
:blob
|
|
445
476
|
when /\Aenum/io
|
|
446
477
|
:enum
|
|
@@ -460,8 +491,8 @@ module Sequel
|
|
|
460
491
|
end
|
|
461
492
|
|
|
462
493
|
# Raise a database error unless the exception is an Rollback.
|
|
463
|
-
def transaction_error(e)
|
|
464
|
-
raise_error(e, :classes=>database_error_classes) unless e.is_a?(Rollback)
|
|
494
|
+
def transaction_error(e, opts={})
|
|
495
|
+
raise_error(e, opts.merge(:classes=>database_error_classes)) unless e.is_a?(Rollback)
|
|
465
496
|
end
|
|
466
497
|
end
|
|
467
498
|
end
|
|
@@ -86,7 +86,8 @@ module Sequel
|
|
|
86
86
|
# :index :: Create an index on this column.
|
|
87
87
|
# :key :: For foreign key columns, the column in the associated table
|
|
88
88
|
# that this column references. Unnecessary if this column
|
|
89
|
-
# references the primary key of the associated table
|
|
89
|
+
# references the primary key of the associated table, except if you are
|
|
90
|
+
# using MySQL.
|
|
90
91
|
# :null :: Mark the column as allowing NULL values (if true),
|
|
91
92
|
# or not allowing NULL values (if false). If unspecified, will default
|
|
92
93
|
# to whatever the database default is.
|
|
@@ -329,15 +330,17 @@ module Sequel
|
|
|
329
330
|
# Remove a column from the DDL for the table.
|
|
330
331
|
#
|
|
331
332
|
# drop_column(:artist_id) # DROP COLUMN artist_id
|
|
332
|
-
|
|
333
|
-
|
|
333
|
+
# drop_column(:artist_id, :cascade=>true) # DROP COLUMN artist_id CASCADE
|
|
334
|
+
def drop_column(name, opts={})
|
|
335
|
+
@operations << {:op => :drop_column, :name => name}.merge(opts)
|
|
334
336
|
end
|
|
335
337
|
|
|
336
338
|
# Remove a constraint from the DDL for the table.
|
|
337
339
|
#
|
|
338
340
|
# drop_constraint(:unique_name) # DROP CONSTRAINT unique_name
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
# drop_constraint(:unique_name, :cascade=>true) # DROP CONSTRAINT unique_name CASCADE
|
|
342
|
+
def drop_constraint(name, opts={})
|
|
343
|
+
@operations << {:op => :drop_constraint, :name => name}.merge(opts)
|
|
341
344
|
end
|
|
342
345
|
|
|
343
346
|
# Remove an index from the DDL for the table.
|