sequel 3.21.0 → 3.29.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 +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- 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 +72 -27
- data/doc/opening_databases.rdoc +86 -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/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -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 +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- 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 +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -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 +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -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 +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 +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- 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 +997 -331
- data/lib/sequel/model/base.rb +386 -129
- 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 +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- 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 +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- 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 +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -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_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -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 +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- 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 +200 -120
- 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 +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
|
@@ -83,13 +83,9 @@ module Sequel
|
|
|
83
83
|
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# * :server - Set the server to use
|
|
90
|
-
def tables(opts={})
|
|
91
|
-
m = output_identifier_meth
|
|
92
|
-
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
|
|
86
|
+
# MySQL supports CREATE TABLE IF NOT EXISTS syntax.
|
|
87
|
+
def supports_create_table_if_not_exists?
|
|
88
|
+
true
|
|
93
89
|
end
|
|
94
90
|
|
|
95
91
|
# MySQL supports prepared transactions (two-phase commit) using XA
|
|
@@ -107,6 +103,14 @@ module Sequel
|
|
|
107
103
|
true
|
|
108
104
|
end
|
|
109
105
|
|
|
106
|
+
# Return an array of symbols specifying table names in the current database.
|
|
107
|
+
#
|
|
108
|
+
# Options:
|
|
109
|
+
# * :server - Set the server to use
|
|
110
|
+
def tables(opts={})
|
|
111
|
+
full_tables('BASE TABLE', opts)
|
|
112
|
+
end
|
|
113
|
+
|
|
110
114
|
# Changes the database in use by issuing a USE statement. I would be
|
|
111
115
|
# very careful if I used this.
|
|
112
116
|
def use(db_name)
|
|
@@ -116,6 +120,14 @@ module Sequel
|
|
|
116
120
|
self
|
|
117
121
|
end
|
|
118
122
|
|
|
123
|
+
# Return an array of symbols specifying view names in the current database.
|
|
124
|
+
#
|
|
125
|
+
# Options:
|
|
126
|
+
# * :server - Set the server to use
|
|
127
|
+
def views(opts={})
|
|
128
|
+
full_tables('VIEW', opts)
|
|
129
|
+
end
|
|
130
|
+
|
|
119
131
|
private
|
|
120
132
|
|
|
121
133
|
# Use MySQL specific syntax for rename column, set column type, and
|
|
@@ -142,6 +154,18 @@ module Sequel
|
|
|
142
154
|
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
|
|
143
155
|
when :drop_index
|
|
144
156
|
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
|
157
|
+
when :drop_constraint
|
|
158
|
+
type = case op[:type]
|
|
159
|
+
when :primary_key
|
|
160
|
+
return "ALTER TABLE #{quote_schema_table(table)} DROP PRIMARY KEY"
|
|
161
|
+
when :foreign_key
|
|
162
|
+
'FOREIGN KEY'
|
|
163
|
+
when :unique
|
|
164
|
+
'INDEX'
|
|
165
|
+
else
|
|
166
|
+
raise(Error, "must specify constraint type via :type=>(:foreign_key|:primary_key|:unique) when dropping constraints on MySQL")
|
|
167
|
+
end
|
|
168
|
+
"ALTER TABLE #{quote_schema_table(table)} DROP #{type} #{quote_identifier(op[:name])}"
|
|
145
169
|
else
|
|
146
170
|
super(table, op)
|
|
147
171
|
end
|
|
@@ -161,9 +185,9 @@ module Sequel
|
|
|
161
185
|
# Use XA START to start a new prepared transaction if the :prepare
|
|
162
186
|
# option is given.
|
|
163
187
|
def begin_transaction(conn, opts={})
|
|
164
|
-
if s = opts[:prepare]
|
|
188
|
+
if (s = opts[:prepare]) && (th = @transactions[conn])[:savepoint_level] == 0
|
|
165
189
|
log_connection_execute(conn, "XA START #{literal(s)}")
|
|
166
|
-
|
|
190
|
+
th[:savepoint_level] += 1
|
|
167
191
|
else
|
|
168
192
|
super
|
|
169
193
|
end
|
|
@@ -184,7 +208,7 @@ module Sequel
|
|
|
184
208
|
# Prepare the XA transaction for a two-phase commit if the
|
|
185
209
|
# :prepare option is given.
|
|
186
210
|
def commit_transaction(conn, opts={})
|
|
187
|
-
if s = opts[:prepare]
|
|
211
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
188
212
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
189
213
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
190
214
|
else
|
|
@@ -205,6 +229,12 @@ module Sequel
|
|
|
205
229
|
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
|
206
230
|
end
|
|
207
231
|
|
|
232
|
+
# Backbone of the tables and views support using SHOW FULL TABLES.
|
|
233
|
+
def full_tables(type, opts)
|
|
234
|
+
m = output_identifier_meth
|
|
235
|
+
metadata_dataset.with_sql('SHOW FULL TABLES').server(opts[:server]).map{|r| m.call(r.values.first) if r.delete(:Table_type) == type}.compact
|
|
236
|
+
end
|
|
237
|
+
|
|
208
238
|
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
209
239
|
def identifier_input_method_default
|
|
210
240
|
nil
|
|
@@ -232,7 +262,7 @@ module Sequel
|
|
|
232
262
|
|
|
233
263
|
# Rollback the currently open XA transaction
|
|
234
264
|
def rollback_transaction(conn, opts={})
|
|
235
|
-
if s = opts[:prepare]
|
|
265
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
236
266
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
237
267
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
238
268
|
log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
|
|
@@ -248,8 +278,8 @@ module Sequel
|
|
|
248
278
|
|
|
249
279
|
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
|
250
280
|
def schema_parse_table(table_name, opts)
|
|
251
|
-
m = output_identifier_meth
|
|
252
|
-
im = input_identifier_meth
|
|
281
|
+
m = output_identifier_meth(opts[:dataset])
|
|
282
|
+
im = input_identifier_meth(opts[:dataset])
|
|
253
283
|
metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
|
|
254
284
|
row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
|
|
255
285
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
|
@@ -312,6 +342,13 @@ module Sequel
|
|
|
312
342
|
# string concatenation.
|
|
313
343
|
def complex_expression_sql(op, args)
|
|
314
344
|
case op
|
|
345
|
+
when :IN, :"NOT IN"
|
|
346
|
+
ds = args.at(1)
|
|
347
|
+
if ds.is_a?(Sequel::Dataset) && ds.opts[:limit]
|
|
348
|
+
super(op, [args.at(0), ds.from_self])
|
|
349
|
+
else
|
|
350
|
+
super
|
|
351
|
+
end
|
|
315
352
|
when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
|
|
316
353
|
"(#{literal(args.at(0))} #{'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)}#{[:~, :'!~', :'~*', :'!~*'].include?(op) ? 'REGEXP' : 'LIKE'} #{'BINARY ' if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)}#{literal(args.at(1))})"
|
|
317
354
|
when :'||'
|
|
@@ -451,6 +488,12 @@ module Sequel
|
|
|
451
488
|
true
|
|
452
489
|
end
|
|
453
490
|
|
|
491
|
+
# MySQL's DISTINCT ON emulation using GROUP BY does not respect the
|
|
492
|
+
# queries ORDER BY clause.
|
|
493
|
+
def supports_ordered_distinct_on?
|
|
494
|
+
false
|
|
495
|
+
end
|
|
496
|
+
|
|
454
497
|
# MySQL does support fractional timestamps in literal timestamps, but it
|
|
455
498
|
# ignores them. Also, using them seems to cause problems on 1.9. Since
|
|
456
499
|
# they are ignored anyway, not using them is probably best.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module MySQL
|
|
5
|
+
# This module is used by the mysql and mysql2 adapters to support
|
|
6
|
+
# prepared statements and stored procedures.
|
|
7
|
+
module PreparedStatements
|
|
8
|
+
module DatabaseMethods
|
|
9
|
+
# Support stored procedures on MySQL
|
|
10
|
+
def call_sproc(name, opts={}, &block)
|
|
11
|
+
args = opts[:args] || []
|
|
12
|
+
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Executes the given SQL using an available connection, yielding the
|
|
16
|
+
# connection if the block is given.
|
|
17
|
+
def execute(sql, opts={}, &block)
|
|
18
|
+
if opts[:sproc]
|
|
19
|
+
call_sproc(sql, opts, &block)
|
|
20
|
+
elsif sql.is_a?(Symbol)
|
|
21
|
+
execute_prepared_statement(sql, opts, &block)
|
|
22
|
+
else
|
|
23
|
+
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def add_prepared_statements_cache(conn)
|
|
30
|
+
class << conn
|
|
31
|
+
attr_accessor :prepared_statements
|
|
32
|
+
end
|
|
33
|
+
conn.prepared_statements = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Executes a prepared statement on an available connection. If the
|
|
37
|
+
# prepared statement already exists for the connection and has the same
|
|
38
|
+
# SQL, reuse it, otherwise, prepare the new statement. Because of the
|
|
39
|
+
# usual MySQL stupidity, we are forced to name arguments via separate
|
|
40
|
+
# SET queries. Use @sequel_arg_N (for N starting at 1) for these
|
|
41
|
+
# arguments.
|
|
42
|
+
def execute_prepared_statement(ps_name, opts, &block)
|
|
43
|
+
args = opts[:arguments]
|
|
44
|
+
ps = prepared_statements[ps_name]
|
|
45
|
+
sql = ps.prepared_sql
|
|
46
|
+
synchronize(opts[:server]) do |conn|
|
|
47
|
+
unless conn.prepared_statements[ps_name] == sql
|
|
48
|
+
conn.prepared_statements[ps_name] = sql
|
|
49
|
+
_execute(conn, "PREPARE #{ps_name} FROM #{literal(sql)}", opts)
|
|
50
|
+
end
|
|
51
|
+
i = 0
|
|
52
|
+
_execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
|
|
53
|
+
_execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
module DatasetMethods
|
|
59
|
+
include Sequel::Dataset::StoredProcedures
|
|
60
|
+
|
|
61
|
+
# Methods to add to MySQL prepared statement calls without using a
|
|
62
|
+
# real database prepared statement and bound variables.
|
|
63
|
+
module CallableStatementMethods
|
|
64
|
+
# Extend given dataset with this module so subselects inside subselects in
|
|
65
|
+
# prepared statements work.
|
|
66
|
+
def subselect_sql(ds)
|
|
67
|
+
ps = ds.to_prepared_statement(:select)
|
|
68
|
+
ps.extend(CallableStatementMethods)
|
|
69
|
+
ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
|
|
70
|
+
ps.prepared_args = prepared_args
|
|
71
|
+
ps.prepared_sql
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Methods for MySQL prepared statements using the native driver.
|
|
76
|
+
module PreparedStatementMethods
|
|
77
|
+
include Sequel::Dataset::UnnumberedArgumentMapper
|
|
78
|
+
|
|
79
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
|
80
|
+
def call(*)
|
|
81
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# Execute the prepared statement with the bind arguments instead of
|
|
88
|
+
# the given SQL.
|
|
89
|
+
def execute(sql, opts={}, &block)
|
|
90
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
94
|
+
def execute_dui(sql, opts={}, &block)
|
|
95
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Methods for MySQL stored procedures using the native driver.
|
|
100
|
+
module StoredProcedureMethods
|
|
101
|
+
include Sequel::Dataset::StoredProcedureMethods
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# Execute the database stored procedure with the stored arguments.
|
|
106
|
+
def execute(sql, opts={}, &block)
|
|
107
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
111
|
+
def execute_dui(sql, opts={}, &block)
|
|
112
|
+
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# MySQL is different in that it supports prepared statements but not bound
|
|
117
|
+
# variables outside of prepared statements. The default implementation
|
|
118
|
+
# breaks the use of subselects in prepared statements, so extend the
|
|
119
|
+
# temporary prepared statement that this creates with a module that
|
|
120
|
+
# fixes it.
|
|
121
|
+
def call(type, bind_arguments={}, *values, &block)
|
|
122
|
+
ps = to_prepared_statement(type, values)
|
|
123
|
+
ps.extend(CallableStatementMethods)
|
|
124
|
+
ps.call(bind_arguments, &block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Store the given type of prepared statement in the associated database
|
|
128
|
+
# with the given name.
|
|
129
|
+
def prepare(type, name=nil, *values)
|
|
130
|
+
ps = to_prepared_statement(type, values)
|
|
131
|
+
ps.extend(PreparedStatementMethods)
|
|
132
|
+
if name
|
|
133
|
+
ps.prepared_statement_name = name
|
|
134
|
+
db.prepared_statements[name] = ps
|
|
135
|
+
end
|
|
136
|
+
ps
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
# Extend the dataset with the MySQL stored procedure methods.
|
|
142
|
+
def prepare_extend_sproc(ds)
|
|
143
|
+
ds.extend(StoredProcedureMethods)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
Sequel.require 'adapters/utils/emulate_offset_with_row_number'
|
|
2
|
+
|
|
1
3
|
module Sequel
|
|
2
4
|
module Oracle
|
|
3
5
|
module DatabaseMethods
|
|
4
6
|
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
|
5
7
|
AUTOINCREMENT = ''.freeze
|
|
6
8
|
|
|
9
|
+
attr_accessor :autosequence
|
|
10
|
+
|
|
7
11
|
def create_sequence(name, opts={})
|
|
8
12
|
self << create_sequence_sql(name, opts)
|
|
9
13
|
end
|
|
@@ -12,6 +16,10 @@ module Sequel
|
|
|
12
16
|
self << create_trigger_sql(*args)
|
|
13
17
|
end
|
|
14
18
|
|
|
19
|
+
def current_user
|
|
20
|
+
@current_user ||= get{sys_context('USERENV', 'CURRENT_USER')}
|
|
21
|
+
end
|
|
22
|
+
|
|
15
23
|
def drop_sequence(name)
|
|
16
24
|
self << drop_sequence_sql(name)
|
|
17
25
|
end
|
|
@@ -26,10 +34,6 @@ module Sequel
|
|
|
26
34
|
ds.map{|r| ds.send(:output_identifier, r[:tname])}
|
|
27
35
|
end
|
|
28
36
|
|
|
29
|
-
def table_exists?(name)
|
|
30
|
-
from(:tab).filter(:tname =>dataset.send(:input_identifier, name), :tabtype => 'TABLE').count > 0
|
|
31
|
-
end
|
|
32
|
-
|
|
33
37
|
def views(opts={})
|
|
34
38
|
ds = from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW')
|
|
35
39
|
ds.map{|r| ds.send(:output_identifier, r[:tname])}
|
|
@@ -41,13 +45,37 @@ module Sequel
|
|
|
41
45
|
|
|
42
46
|
private
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
# Handle Oracle specific ALTER TABLE SQL
|
|
49
|
+
def alter_table_sql(table, op)
|
|
50
|
+
case op[:op]
|
|
51
|
+
when :add_column
|
|
52
|
+
if op[:primary_key]
|
|
53
|
+
sqls = []
|
|
54
|
+
sqls << alter_table_sql(table, op.merge(:primary_key=>nil))
|
|
55
|
+
if op[:auto_increment]
|
|
56
|
+
seq_name = default_sequence_name(table, op[:name])
|
|
57
|
+
sqls << drop_sequence_sql(seq_name)
|
|
58
|
+
sqls << create_sequence_sql(seq_name, op)
|
|
59
|
+
sqls << "UPDATE #{quote_schema_table(table)} SET #{quote_identifier(op[:name])} = #{seq_name}.nextval"
|
|
60
|
+
end
|
|
61
|
+
sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"
|
|
62
|
+
sqls
|
|
63
|
+
else
|
|
64
|
+
"ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
|
|
65
|
+
end
|
|
66
|
+
when :set_column_null
|
|
67
|
+
"ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{op[:null] ? 'NULL' : 'NOT NULL'}"
|
|
68
|
+
when :set_column_type
|
|
69
|
+
"ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} #{type_literal(op)}"
|
|
70
|
+
when :set_column_default
|
|
71
|
+
"ALTER TABLE #{quote_schema_table(table)} MODIFY #{quote_identifier(op[:name])} DEFAULT #{literal(op[:default])}"
|
|
72
|
+
else
|
|
73
|
+
super(table, op)
|
|
74
|
+
end
|
|
46
75
|
end
|
|
47
76
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
TEMPORARY
|
|
77
|
+
def auto_increment_sql
|
|
78
|
+
AUTOINCREMENT
|
|
51
79
|
end
|
|
52
80
|
|
|
53
81
|
def create_sequence_sql(name, opts={})
|
|
@@ -65,7 +93,7 @@ module Sequel
|
|
|
65
93
|
drop_seq_statement = nil
|
|
66
94
|
generator.columns.each do |c|
|
|
67
95
|
if c[:auto_increment]
|
|
68
|
-
c[:sequence_name] ||=
|
|
96
|
+
c[:sequence_name] ||= default_sequence_name(name, c[:name])
|
|
69
97
|
unless c[:create_sequence] == false
|
|
70
98
|
drop_seq_statement = drop_sequence_sql(c[:sequence_name])
|
|
71
99
|
statements << create_sequence_sql(c[:sequence_name], c)
|
|
@@ -97,13 +125,94 @@ module Sequel
|
|
|
97
125
|
sql
|
|
98
126
|
end
|
|
99
127
|
|
|
128
|
+
def default_sequence_name(table, column)
|
|
129
|
+
"seq_#{table}_#{column}"
|
|
130
|
+
end
|
|
131
|
+
|
|
100
132
|
def drop_sequence_sql(name)
|
|
101
133
|
"DROP SEQUENCE #{quote_identifier(name)}"
|
|
102
134
|
end
|
|
135
|
+
|
|
136
|
+
def remove_cached_schema(table)
|
|
137
|
+
@primary_key_sequences.delete(table)
|
|
138
|
+
super
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def sequence_for_table(table)
|
|
142
|
+
return nil unless autosequence
|
|
143
|
+
@primary_key_sequences.fetch(table) do |key|
|
|
144
|
+
pk = schema(table).select{|k, v| v[:primary_key]}
|
|
145
|
+
@primary_key_sequences[table] = if pk.length == 1
|
|
146
|
+
seq = "seq_#{table}_#{pk.first.first}"
|
|
147
|
+
seq.to_sym unless from(:user_sequences).filter(:sequence_name=>input_identifier_meth.call(seq)).empty?
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Oracle's integer/:number type handles larger values than
|
|
153
|
+
# most other databases's bigint types, so it should be
|
|
154
|
+
# safe to use for Bignum.
|
|
155
|
+
def type_literal_generic_bignum(column)
|
|
156
|
+
:integer
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Oracle doesn't have a time type, so use timestamp for all
|
|
160
|
+
# time columns.
|
|
161
|
+
def type_literal_generic_time(column)
|
|
162
|
+
:timestamp
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Oracle doesn't have a boolean type or even a reasonable
|
|
166
|
+
# facsimile. Using a char(1) seems to be the recommended way.
|
|
167
|
+
def type_literal_generic_trueclass(column)
|
|
168
|
+
:'char(1)'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# SQL fragment for showing a table is temporary
|
|
172
|
+
def temporary_table_sql
|
|
173
|
+
TEMPORARY
|
|
174
|
+
end
|
|
103
175
|
end
|
|
104
176
|
|
|
105
177
|
module DatasetMethods
|
|
106
|
-
|
|
178
|
+
include EmulateOffsetWithRowNumber
|
|
179
|
+
|
|
180
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order lock')
|
|
181
|
+
ROW_NUMBER_EXPRESSION = 'ROWNUM'.lit.freeze
|
|
182
|
+
|
|
183
|
+
# Oracle needs to emulate bitwise operators and ILIKE/NOT ILIKE operators.
|
|
184
|
+
def complex_expression_sql(op, args)
|
|
185
|
+
case op
|
|
186
|
+
when :&
|
|
187
|
+
complex_expression_arg_pairs(args){|a, b| "CAST(BITAND(#{literal(a)}, #{literal(b)}) AS INTEGER)"}
|
|
188
|
+
when :|
|
|
189
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} - #{complex_expression_sql(:&, [a, b])} + #{literal(b)})"}
|
|
190
|
+
when :^
|
|
191
|
+
complex_expression_arg_pairs(args){|*x| "(#{complex_expression_sql(:|, x)} - #{complex_expression_sql(:&, x)})"}
|
|
192
|
+
when :'B~'
|
|
193
|
+
"((0 - #{literal(args.at(0))}) - 1)"
|
|
194
|
+
when :<<
|
|
195
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
|
|
196
|
+
when :>>
|
|
197
|
+
complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
|
|
198
|
+
when :ILIKE, :'NOT ILIKE'
|
|
199
|
+
a, b = args
|
|
200
|
+
"(UPPER(#{literal(a)}) #{op == :ILIKE ? :LIKE : :'NOT LIKE'} UPPER(#{literal(b)}))"
|
|
201
|
+
else
|
|
202
|
+
super
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Oracle doesn't support CURRENT_TIME, as it doesn't have
|
|
207
|
+
# a type for storing just time values without a date, so
|
|
208
|
+
# use CURRENT_TIMESTAMP in its place.
|
|
209
|
+
def constant_sql(c)
|
|
210
|
+
if c == :CURRENT_TIME
|
|
211
|
+
super(:CURRENT_TIMESTAMP)
|
|
212
|
+
else
|
|
213
|
+
super
|
|
214
|
+
end
|
|
215
|
+
end
|
|
107
216
|
|
|
108
217
|
# Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
|
|
109
218
|
def except(dataset, opts={})
|
|
@@ -113,18 +222,7 @@ module Sequel
|
|
|
113
222
|
end
|
|
114
223
|
|
|
115
224
|
def empty?
|
|
116
|
-
db[:dual].where(exists).get(1) == nil
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# If this dataset is associated with a sequence, return the most recently
|
|
120
|
-
# inserted sequence value.
|
|
121
|
-
def insert(*args)
|
|
122
|
-
r = super
|
|
123
|
-
if s = opts[:sequence]
|
|
124
|
-
with_sql("SELECT #{literal(s)}.currval FROM dual").single_value.to_i
|
|
125
|
-
else
|
|
126
|
-
r
|
|
127
|
-
end
|
|
225
|
+
db[:dual].where(unordered.exists).get(1) == nil
|
|
128
226
|
end
|
|
129
227
|
|
|
130
228
|
# Oracle requires SQL standard datetimes
|
|
@@ -139,16 +237,49 @@ module Sequel
|
|
|
139
237
|
clone(:sequence=>s)
|
|
140
238
|
end
|
|
141
239
|
|
|
240
|
+
# Handle LIMIT by using a unlimited subselect filtered with ROWNUM.
|
|
241
|
+
def select_sql
|
|
242
|
+
if (limit = @opts[:limit]) && !@opts[:sql]
|
|
243
|
+
ds = clone(:limit=>nil)
|
|
244
|
+
# Lock doesn't work in subselects, so don't use a subselect when locking.
|
|
245
|
+
# Don't use a subselect if custom SQL is used, as it breaks somethings.
|
|
246
|
+
ds = ds.from_self unless @opts[:lock]
|
|
247
|
+
subselect_sql(ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
|
|
248
|
+
else
|
|
249
|
+
super
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Oracle requires recursive CTEs to have column aliases.
|
|
254
|
+
def recursive_cte_requires_column_aliases?
|
|
255
|
+
true
|
|
256
|
+
end
|
|
257
|
+
|
|
142
258
|
# Oracle does not support INTERSECT ALL or EXCEPT ALL
|
|
143
259
|
def supports_intersect_except_all?
|
|
144
260
|
false
|
|
145
261
|
end
|
|
262
|
+
|
|
263
|
+
# Oracle does not support IS TRUE.
|
|
264
|
+
def supports_is_true?
|
|
265
|
+
false
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Oracle does not support SELECT *, column
|
|
269
|
+
def supports_select_all_and_column?
|
|
270
|
+
false
|
|
271
|
+
end
|
|
146
272
|
|
|
147
273
|
# Oracle supports timezones in literal timestamps.
|
|
148
274
|
def supports_timestamp_timezones?
|
|
149
275
|
true
|
|
150
276
|
end
|
|
151
277
|
|
|
278
|
+
# Oracle does not support WHERE 'Y' for WHERE TRUE.
|
|
279
|
+
def supports_where_true?
|
|
280
|
+
false
|
|
281
|
+
end
|
|
282
|
+
|
|
152
283
|
# Oracle supports window functions
|
|
153
284
|
def supports_window_functions?
|
|
154
285
|
true
|
|
@@ -167,16 +298,47 @@ module Sequel
|
|
|
167
298
|
"TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
|
|
168
299
|
end
|
|
169
300
|
|
|
301
|
+
# If this dataset is associated with a sequence, return the most recently
|
|
302
|
+
# inserted sequence value.
|
|
303
|
+
def execute_insert(sql, opts={})
|
|
304
|
+
f = @opts[:from]
|
|
305
|
+
super(sql, {:table=>(f.first if f), :sequence=>@opts[:sequence]}.merge(opts))
|
|
306
|
+
end
|
|
307
|
+
|
|
170
308
|
# Use a colon for the timestamp offset, since Oracle appears to require it.
|
|
171
309
|
def format_timestamp_offset(hour, minute)
|
|
172
310
|
sprintf("%+03i:%02i", hour, minute)
|
|
173
311
|
end
|
|
174
312
|
|
|
313
|
+
# Oracle doesn't support empty values when inserting.
|
|
314
|
+
def insert_supports_empty_values?
|
|
315
|
+
false
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Use string in hex format for blob data.
|
|
319
|
+
def literal_blob(v)
|
|
320
|
+
blob = "'"
|
|
321
|
+
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
322
|
+
blob << "'"
|
|
323
|
+
blob
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Oracle uses 'N' for false values.
|
|
327
|
+
def literal_false
|
|
328
|
+
"'N'"
|
|
329
|
+
end
|
|
330
|
+
|
|
175
331
|
# Oracle uses the SQL standard of only doubling ' inside strings.
|
|
176
332
|
def literal_string(v)
|
|
177
333
|
"'#{v.gsub("'", "''")}'"
|
|
178
334
|
end
|
|
179
335
|
|
|
336
|
+
# Oracle uses 'Y' for true values.
|
|
337
|
+
def literal_true
|
|
338
|
+
"'Y'"
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Use the Oracle-specific SQL clauses (no limit, since it is emulated).
|
|
180
342
|
def select_clause_methods
|
|
181
343
|
SELECT_CLAUSE_METHODS
|
|
182
344
|
end
|
|
@@ -188,17 +350,6 @@ module Sequel
|
|
|
188
350
|
def select_from_sql(sql)
|
|
189
351
|
sql << " FROM #{source_list(@opts[:from] || ['DUAL'])}"
|
|
190
352
|
end
|
|
191
|
-
|
|
192
|
-
# Oracle requires a subselect to do limit and offset
|
|
193
|
-
def select_limit_sql(sql)
|
|
194
|
-
if limit = @opts[:limit]
|
|
195
|
-
if (offset = @opts[:offset]) && (offset > 0)
|
|
196
|
-
sql.replace("SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}")
|
|
197
|
-
else
|
|
198
|
-
sql.replace("SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}")
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
353
|
end
|
|
203
354
|
end
|
|
204
355
|
end
|