sequel 3.21.0 → 3.31.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 +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- 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/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- 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 +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- 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/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -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/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
|
@@ -11,16 +11,11 @@ module Sequel
|
|
|
11
11
|
# :dataserver and :username options.
|
|
12
12
|
def connect(server)
|
|
13
13
|
opts = server_opts(server)
|
|
14
|
-
opts[:dataserver] = opts[:host]
|
|
15
14
|
opts[:username] = opts[:user]
|
|
15
|
+
set_mssql_unicode_strings
|
|
16
16
|
TinyTds::Client.new(opts)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# Return instance of Sequel::TinyTDS::Dataset with the given options.
|
|
20
|
-
def dataset(opts = nil)
|
|
21
|
-
TinyTDS::Dataset.new(self, opts)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
19
|
# Execute the given +sql+ on the server. If the :return option
|
|
25
20
|
# is present, its value should be a method symbol that is called
|
|
26
21
|
# on the TinyTds::Result object returned from executing the
|
|
@@ -32,13 +27,36 @@ module Sequel
|
|
|
32
27
|
begin
|
|
33
28
|
m = opts[:return]
|
|
34
29
|
r = nil
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
if (args = opts[:arguments]) && !args.empty?
|
|
31
|
+
types = []
|
|
32
|
+
values = []
|
|
33
|
+
args.each_with_index do |(k, v), i|
|
|
34
|
+
v, type = ps_arg_type(v)
|
|
35
|
+
types << "@#{k} #{type}"
|
|
36
|
+
values << "@#{k} = #{v}"
|
|
37
|
+
end
|
|
38
|
+
case m
|
|
39
|
+
when :do
|
|
40
|
+
sql = "#{sql}; SELECT @@ROWCOUNT AS AffectedRows"
|
|
41
|
+
single_value = true
|
|
42
|
+
when :insert
|
|
43
|
+
sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
|
44
|
+
single_value = true
|
|
45
|
+
end
|
|
46
|
+
sql = "EXEC sp_executesql N'#{c.escape(sql)}', N'#{c.escape(types.join(', '))}', #{values.join(', ')}"
|
|
47
|
+
log_yield(sql) do
|
|
48
|
+
r = c.execute(sql)
|
|
49
|
+
r.each{|row| return row.values.first} if single_value
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
log_yield(sql) do
|
|
53
|
+
r = c.execute(sql)
|
|
54
|
+
return r.send(m) if m
|
|
55
|
+
end
|
|
38
56
|
end
|
|
39
57
|
yield(r) if block_given?
|
|
40
58
|
rescue TinyTds::Error => e
|
|
41
|
-
raise_error(e)
|
|
59
|
+
raise_error(e, :disconnect=>!c.active?)
|
|
42
60
|
ensure
|
|
43
61
|
r.cancel if r && c.sqlsent?
|
|
44
62
|
end
|
|
@@ -74,51 +92,174 @@ module Sequel
|
|
|
74
92
|
super
|
|
75
93
|
end
|
|
76
94
|
|
|
95
|
+
# tiny_tds uses TinyTds::Error as the base error class.
|
|
96
|
+
def database_error_classes
|
|
97
|
+
[TinyTds::Error]
|
|
98
|
+
end
|
|
99
|
+
|
|
77
100
|
# Close the TinyTds::Client object.
|
|
78
101
|
def disconnect_connection(c)
|
|
79
102
|
c.close
|
|
80
103
|
end
|
|
104
|
+
|
|
105
|
+
# Return true if the :conn argument is present and not active.
|
|
106
|
+
def disconnect_error?(e, opts)
|
|
107
|
+
super || (opts[:conn] && !opts[:conn].active?)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Return a 2 element array with the literal value and type to use
|
|
111
|
+
# in the prepared statement call for the given value and connection.
|
|
112
|
+
def ps_arg_type(v)
|
|
113
|
+
case v
|
|
114
|
+
when Fixnum
|
|
115
|
+
[v, 'int']
|
|
116
|
+
when Bignum
|
|
117
|
+
[v, 'bigint']
|
|
118
|
+
when Float
|
|
119
|
+
[v, 'double precision']
|
|
120
|
+
when Numeric
|
|
121
|
+
[v, 'numeric']
|
|
122
|
+
when Time
|
|
123
|
+
if v.is_a?(SQLTime)
|
|
124
|
+
[literal(v), 'time']
|
|
125
|
+
else
|
|
126
|
+
[literal(v), 'datetime']
|
|
127
|
+
end
|
|
128
|
+
when DateTime
|
|
129
|
+
[literal(v), 'datetime']
|
|
130
|
+
when Date
|
|
131
|
+
[literal(v), 'date']
|
|
132
|
+
when nil
|
|
133
|
+
['NULL', 'nvarchar(max)']
|
|
134
|
+
when true
|
|
135
|
+
['1', 'int']
|
|
136
|
+
when false
|
|
137
|
+
['0', 'int']
|
|
138
|
+
when SQL::Blob
|
|
139
|
+
[literal(v), 'varbinary(max)']
|
|
140
|
+
else
|
|
141
|
+
[literal(v), 'nvarchar(max)']
|
|
142
|
+
end
|
|
143
|
+
end
|
|
81
144
|
end
|
|
82
145
|
|
|
83
146
|
class Dataset < Sequel::Dataset
|
|
84
147
|
include Sequel::MSSQL::DatasetMethods
|
|
148
|
+
|
|
149
|
+
Database::DatasetClass = self
|
|
150
|
+
|
|
151
|
+
# SQLite already supports named bind arguments, so use directly.
|
|
152
|
+
module ArgumentMapper
|
|
153
|
+
include Sequel::Dataset::ArgumentMapper
|
|
154
|
+
|
|
155
|
+
protected
|
|
156
|
+
|
|
157
|
+
# Return a hash with the same values as the given hash,
|
|
158
|
+
# but with the keys converted to strings.
|
|
159
|
+
def map_to_prepared_args(hash)
|
|
160
|
+
args = {}
|
|
161
|
+
hash.each{|k,v| args[k.to_s.gsub('.', '__')] = v}
|
|
162
|
+
args
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
# SQLite uses a : before the name of the argument for named
|
|
168
|
+
# arguments.
|
|
169
|
+
def prepared_arg(k)
|
|
170
|
+
LiteralString.new("@#{k.to_s.gsub('.', '__')}")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Always assume a prepared argument.
|
|
174
|
+
def prepared_arg?(k)
|
|
175
|
+
true
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# SQLite prepared statement uses a new prepared statement each time
|
|
180
|
+
# it is called, but it does use the bind arguments.
|
|
181
|
+
module PreparedStatementMethods
|
|
182
|
+
include ArgumentMapper
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
# Run execute_select on the database with the given SQL and the stored
|
|
187
|
+
# bind arguments.
|
|
188
|
+
def execute(sql, opts={}, &block)
|
|
189
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
193
|
+
def execute_dui(sql, opts={}, &block)
|
|
194
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
198
|
+
def execute_insert(sql, opts={}, &block)
|
|
199
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
85
202
|
|
|
86
203
|
# Yield hashes with symbol keys, attempting to optimize for
|
|
87
204
|
# various cases.
|
|
88
205
|
def fetch_rows(sql)
|
|
89
206
|
execute(sql) do |result|
|
|
90
207
|
each_opts = {:cache_rows=>false}
|
|
91
|
-
each_opts[:timezone] = :utc if
|
|
92
|
-
offset = @opts[:offset]
|
|
93
|
-
|
|
94
|
-
if
|
|
208
|
+
each_opts[:timezone] = :utc if db.timezone == :utc
|
|
209
|
+
rn = row_number_column if offset = @opts[:offset]
|
|
210
|
+
columns = cols = result.fields.map{|c| output_identifier(c)}
|
|
211
|
+
if offset
|
|
212
|
+
rn = row_number_column
|
|
213
|
+
columns = columns.dup
|
|
214
|
+
columns.delete(rn)
|
|
215
|
+
end
|
|
216
|
+
@columns = columns
|
|
217
|
+
#if identifier_output_method
|
|
95
218
|
each_opts[:as] = :array
|
|
96
219
|
result.each(each_opts) do |r|
|
|
97
220
|
h = {}
|
|
98
221
|
cols.zip(r).each{|k, v| h[k] = v}
|
|
99
|
-
h.delete(
|
|
222
|
+
h.delete(rn) if rn
|
|
100
223
|
yield h
|
|
101
224
|
end
|
|
225
|
+
=begin
|
|
226
|
+
# Temporarily disable this optimization, as tiny_tds uses string keys
|
|
227
|
+
# if result.fields is called before result.each(:symbolize_keys=>true).
|
|
228
|
+
# See https://github.com/rails-sqlserver/tiny_tds/issues/57
|
|
102
229
|
else
|
|
103
230
|
each_opts[:symbolize_keys] = true
|
|
104
231
|
if offset
|
|
105
232
|
result.each(each_opts) do |r|
|
|
106
|
-
r.delete(
|
|
233
|
+
r.delete(rn) if rn
|
|
107
234
|
yield r
|
|
108
235
|
end
|
|
109
236
|
else
|
|
110
|
-
result.each(each_opts, &
|
|
237
|
+
result.each(each_opts, &Proc.new)
|
|
111
238
|
end
|
|
112
239
|
end
|
|
240
|
+
=end
|
|
113
241
|
end
|
|
114
242
|
self
|
|
115
243
|
end
|
|
116
244
|
|
|
245
|
+
# Create a named prepared statement that is stored in the
|
|
246
|
+
# database (and connection) for reuse.
|
|
247
|
+
def prepare(type, name=nil, *values)
|
|
248
|
+
ps = to_prepared_statement(type, values)
|
|
249
|
+
ps.extend(PreparedStatementMethods)
|
|
250
|
+
if name
|
|
251
|
+
ps.prepared_statement_name = name
|
|
252
|
+
db.prepared_statements[name] = ps
|
|
253
|
+
end
|
|
254
|
+
ps
|
|
255
|
+
end
|
|
256
|
+
|
|
117
257
|
private
|
|
118
258
|
|
|
119
259
|
# Properly escape the given string +v+.
|
|
120
|
-
def
|
|
121
|
-
|
|
260
|
+
def literal_string_append(sql, v)
|
|
261
|
+
sql << 'N' if mssql_unicode_strings
|
|
262
|
+
sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
|
|
122
263
|
end
|
|
123
264
|
end
|
|
124
265
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module EmulateOffsetWithRowNumber
|
|
3
|
+
# When a subselect that uses :offset is used in IN or NOT IN,
|
|
4
|
+
# use a nested subselect that only includes the first column
|
|
5
|
+
# instead of the ROW_NUMBER column added by the emulated offset support.
|
|
6
|
+
def complex_expression_sql_append(sql, op, args)
|
|
7
|
+
case op
|
|
8
|
+
when :IN, :"NOT IN"
|
|
9
|
+
ds = args.at(1)
|
|
10
|
+
if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
|
|
11
|
+
c = ds.opts[:select].first
|
|
12
|
+
case c
|
|
13
|
+
when Symbol
|
|
14
|
+
t, cl, a = split_symbol(c)
|
|
15
|
+
if a
|
|
16
|
+
c = SQL::Identifier.new(a)
|
|
17
|
+
elsif t
|
|
18
|
+
c = SQL::Identifier.new(cl)
|
|
19
|
+
end
|
|
20
|
+
when SQL::AliasedExpression
|
|
21
|
+
c = SQL::Identifier.new(c.aliaz)
|
|
22
|
+
when SQL::QualifiedIdentifier
|
|
23
|
+
c = SQL::Identifier.new(c.column)
|
|
24
|
+
end
|
|
25
|
+
super(sql, op, [args.at(0), ds.from_self.select(c)])
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Emulate OFFSET support with the ROW_NUMBER window function
|
|
35
|
+
#
|
|
36
|
+
# The implementation is ugly, cloning the current dataset and modifying
|
|
37
|
+
# the clone to add a ROW_NUMBER window function (and some other things),
|
|
38
|
+
# then using the modified clone in a subselect which is selected from.
|
|
39
|
+
#
|
|
40
|
+
# If offset is used, an order must be provided, because the use of ROW_NUMBER
|
|
41
|
+
# requires an order.
|
|
42
|
+
def select_sql
|
|
43
|
+
return super unless o = @opts[:offset]
|
|
44
|
+
raise(Error, "#{db.database_type} requires an order be provided if using an offset") unless order = @opts[:order]
|
|
45
|
+
dsa1 = dataset_alias(1)
|
|
46
|
+
rn = row_number_column
|
|
47
|
+
sql = @opts[:append_sql] || ''
|
|
48
|
+
subselect_sql_append(sql, unlimited.
|
|
49
|
+
unordered.
|
|
50
|
+
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
|
51
|
+
from_self(:alias=>dsa1).
|
|
52
|
+
limit(@opts[:limit]).
|
|
53
|
+
where(SQL::Identifier.new(rn) > o).
|
|
54
|
+
order(rn))
|
|
55
|
+
sql
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# This emulation adds an extra row number column that should be
|
|
61
|
+
# eliminated.
|
|
62
|
+
def offset_returns_row_number_column?
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
module StoredProcedureMethods
|
|
4
|
-
SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
|
|
5
|
-
SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
|
|
6
|
-
|
|
7
4
|
# The name of the stored procedure to call
|
|
8
5
|
attr_accessor :sproc_name
|
|
9
6
|
|
|
@@ -44,14 +41,7 @@ module Sequel
|
|
|
44
41
|
# ignored anyway).
|
|
45
42
|
def sproc_type=(type)
|
|
46
43
|
@sproc_type = type
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
# The type of query (:select, :insert, :delete, :update).
|
|
53
|
-
def sql_query_type
|
|
54
|
-
SQL_QUERY_TYPE[@sproc_type]
|
|
44
|
+
@opts[:sql] = ''
|
|
55
45
|
end
|
|
56
46
|
end
|
|
57
47
|
|
|
@@ -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,17 +228,58 @@ 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
|
|
240
241
|
end
|
|
241
242
|
|
|
243
|
+
# Uses a transaction on all given databases with the given options. This:
|
|
244
|
+
#
|
|
245
|
+
# Sequel.transaction([DB1, DB2, DB3]){...}
|
|
246
|
+
#
|
|
247
|
+
# is equivalent to:
|
|
248
|
+
#
|
|
249
|
+
# DB1.transaction do
|
|
250
|
+
# DB2.transaction do
|
|
251
|
+
# DB3.transaction do
|
|
252
|
+
# ...
|
|
253
|
+
# end
|
|
254
|
+
# end
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# except that if Sequel::Rollback is raised by the block, the transaction is
|
|
258
|
+
# rolled back on all databases instead of just the last one.
|
|
259
|
+
#
|
|
260
|
+
# Note that this method cannot guarantee that all databases will commit or
|
|
261
|
+
# rollback. For example, if DB3 commits but attempting to commit on DB2
|
|
262
|
+
# fails (maybe because foreign key checks are deferred), there is no way
|
|
263
|
+
# to uncommit the changes on DB3. For that kind of support, you need to
|
|
264
|
+
# have two-phase commit/prepared transactions (which Sequel supports on
|
|
265
|
+
# some databases).
|
|
266
|
+
def self.transaction(dbs, opts={}, &block)
|
|
267
|
+
unless opts[:rollback]
|
|
268
|
+
rescue_rollback = true
|
|
269
|
+
opts = opts.merge(:rollback=>:reraise)
|
|
270
|
+
end
|
|
271
|
+
pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
|
|
272
|
+
if rescue_rollback
|
|
273
|
+
begin
|
|
274
|
+
pr.call
|
|
275
|
+
rescue Sequel::Rollback => e
|
|
276
|
+
nil
|
|
277
|
+
end
|
|
278
|
+
else
|
|
279
|
+
pr.call
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
242
283
|
# Same as Sequel.require, but wrapped in a mutex in order to be thread safe.
|
|
243
284
|
def self.ts_require(*args)
|
|
244
285
|
check_requiring_thread{require(*args)}
|
|
@@ -292,7 +333,7 @@ module Sequel
|
|
|
292
333
|
|
|
293
334
|
private_class_method :adapter_method, :def_adapter_method
|
|
294
335
|
|
|
295
|
-
require(%w"metaprogramming sql connection_pool exceptions dataset database timezones version")
|
|
336
|
+
require(%w"metaprogramming sql connection_pool exceptions dataset database timezones ast_transformer version")
|
|
296
337
|
require('core_sql') if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
|
|
297
338
|
|
|
298
339
|
# 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 mock 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
|
#
|
|
@@ -218,7 +218,6 @@ module Sequel
|
|
|
218
218
|
# If a server option is given, acquires a connection for that specific
|
|
219
219
|
# server, instead of the :default server.
|
|
220
220
|
#
|
|
221
|
-
#
|
|
222
221
|
# DB.synchronize do |conn|
|
|
223
222
|
# ...
|
|
224
223
|
# end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Database
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that create datasets
|
|
4
|
+
# :section: 3 - Methods that create datasets
|
|
5
5
|
# These methods all return instances of this database's dataset class.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -24,10 +24,10 @@ module Sequel
|
|
|
24
24
|
#
|
|
25
25
|
# DB.dataset # SELECT *
|
|
26
26
|
# DB.dataset.from(:items) # SELECT * FROM items
|
|
27
|
-
def dataset
|
|
28
|
-
|
|
27
|
+
def dataset(opts=nil)
|
|
28
|
+
@dataset_class.new(self, opts)
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Fetches records for an arbitrary SQL statement. If a block is given,
|
|
32
32
|
# it is used to iterate over the records:
|
|
33
33
|
#
|