sequel 2.12.0 → 3.0.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 +62 -0
- data/README.rdoc +3 -3
- data/Rakefile +7 -0
- data/doc/advanced_associations.rdoc +44 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/lib/sequel/adapters/amalgalite.rb +208 -0
- data/lib/sequel/adapters/db2.rb +3 -0
- data/lib/sequel/adapters/dbi.rb +9 -0
- data/lib/sequel/adapters/do.rb +0 -4
- data/lib/sequel/adapters/firebird.rb +16 -18
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +24 -20
- data/lib/sequel/adapters/jdbc/h2.rb +15 -4
- data/lib/sequel/adapters/mysql.rb +4 -8
- data/lib/sequel/adapters/odbc.rb +0 -4
- data/lib/sequel/adapters/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/mssql.rb +16 -5
- data/lib/sequel/adapters/shared/mysql.rb +87 -86
- data/lib/sequel/adapters/shared/oracle.rb +92 -3
- data/lib/sequel/adapters/shared/postgres.rb +85 -29
- data/lib/sequel/adapters/shared/progress.rb +8 -3
- data/lib/sequel/adapters/shared/sqlite.rb +53 -23
- data/lib/sequel/adapters/sqlite.rb +4 -7
- data/lib/sequel/adapters/utils/unsupported.rb +3 -3
- data/lib/sequel/connection_pool.rb +18 -25
- data/lib/sequel/core.rb +2 -21
- data/lib/sequel/database.rb +60 -44
- data/lib/sequel/database/schema_generator.rb +26 -31
- data/lib/sequel/database/schema_methods.rb +8 -3
- data/lib/sequel/database/schema_sql.rb +114 -28
- data/lib/sequel/dataset.rb +14 -41
- data/lib/sequel/dataset/convenience.rb +31 -54
- data/lib/sequel/dataset/graph.rb +7 -13
- data/lib/sequel/dataset/sql.rb +43 -54
- data/lib/sequel/extensions/inflector.rb +0 -5
- data/lib/sequel/extensions/schema_dumper.rb +238 -0
- data/lib/sequel/metaprogramming.rb +0 -20
- data/lib/sequel/model.rb +1 -2
- data/lib/sequel/model/base.rb +18 -16
- data/lib/sequel/model/inflections.rb +6 -9
- data/lib/sequel/plugins/caching.rb +0 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/firebird_spec.rb +35 -8
- data/spec/adapters/mysql_spec.rb +173 -266
- data/spec/adapters/oracle_spec.rb +13 -0
- data/spec/adapters/postgres_spec.rb +127 -227
- data/spec/adapters/sqlite_spec.rb +13 -171
- data/spec/core/connection_pool_spec.rb +15 -4
- data/spec/core/core_sql_spec.rb +14 -170
- data/spec/core/database_spec.rb +50 -132
- data/spec/core/dataset_spec.rb +47 -930
- data/spec/core/expression_filters_spec.rb +12 -0
- data/spec/core/schema_generator_spec.rb +37 -45
- data/spec/core/schema_spec.rb +26 -16
- data/spec/core/spec_helper.rb +0 -25
- data/spec/extensions/inflector_spec.rb +0 -3
- data/spec/extensions/schema_dumper_spec.rb +292 -0
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/extensions/single_table_inheritance_spec.rb +6 -1
- data/spec/extensions/spec_helper.rb +1 -3
- data/spec/extensions/validation_helpers_spec.rb +4 -4
- data/spec/integration/database_test.rb +18 -0
- data/spec/integration/dataset_test.rb +112 -1
- data/spec/integration/eager_loader_test.rb +70 -9
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +76 -27
- data/spec/integration/spec_helper.rb +0 -14
- data/spec/integration/transaction_test.rb +27 -0
- data/spec/model/associations_spec.rb +0 -36
- data/spec/model/base_spec.rb +18 -123
- data/spec/model/hooks_spec.rb +2 -235
- data/spec/model/inflector_spec.rb +15 -115
- data/spec/model/model_spec.rb +0 -120
- data/spec/model/plugins_spec.rb +0 -70
- data/spec/model/record_spec.rb +35 -93
- data/spec/model/spec_helper.rb +0 -27
- data/spec/model/validations_spec.rb +0 -931
- metadata +9 -14
- data/lib/sequel/deprecated.rb +0 -593
- data/lib/sequel/deprecated_migration.rb +0 -91
- data/lib/sequel/model/deprecated.rb +0 -204
- data/lib/sequel/model/deprecated_hooks.rb +0 -103
- data/lib/sequel/model/deprecated_inflector.rb +0 -335
- data/lib/sequel/model/deprecated_validations.rb +0 -388
- data/spec/core/core_ext_spec.rb +0 -156
- data/spec/core/migration_spec.rb +0 -263
- data/spec/core/pretty_table_spec.rb +0 -58
- data/spec/model/caching_spec.rb +0 -217
- data/spec/model/schema_spec.rb +0 -92
data/lib/sequel/adapters/db2.rb
CHANGED
@@ -4,7 +4,10 @@ module Sequel
|
|
4
4
|
module DB2
|
5
5
|
class Database < Sequel::Database
|
6
6
|
set_adapter_scheme :db2
|
7
|
+
|
7
8
|
include DB2CLI
|
9
|
+
|
10
|
+
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
8
11
|
|
9
12
|
rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
|
10
13
|
#check_error(rc, "Could not allocate DB2 environment")
|
data/lib/sequel/adapters/dbi.rb
CHANGED
@@ -19,6 +19,15 @@ module Sequel
|
|
19
19
|
:sqlite => "SQLite",
|
20
20
|
:sqlrelay => "SQLRelay"
|
21
21
|
}
|
22
|
+
|
23
|
+
def initialize(opts)
|
24
|
+
super(opts)
|
25
|
+
case opts[:db_type]
|
26
|
+
when 'mssql'
|
27
|
+
Sequel.require 'adapters/shared/mssql'
|
28
|
+
extend Sequel::MSSQL::DatabaseMethods
|
29
|
+
end
|
30
|
+
end
|
22
31
|
|
23
32
|
# Converts a uri to an options hash. These options are then passed
|
24
33
|
# to a newly created database object.
|
data/lib/sequel/adapters/do.rb
CHANGED
@@ -121,10 +121,6 @@ module Sequel
|
|
121
121
|
# transactions and commits them immediately after. It's wasteful,
|
122
122
|
# but required by DataObject's API.
|
123
123
|
def transaction(opts={})
|
124
|
-
unless opts.is_a?(Hash)
|
125
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
126
|
-
opts = {:server=>opts}
|
127
|
-
end
|
128
124
|
th = Thread.current
|
129
125
|
synchronize(opts[:server]) do |conn|
|
130
126
|
return yield(conn) if @transactions.include?(th)
|
@@ -9,6 +9,7 @@ module Sequel
|
|
9
9
|
set_adapter_scheme :firebird
|
10
10
|
|
11
11
|
AUTO_INCREMENT = ''.freeze
|
12
|
+
TEMPORARY = 'GLOBAL TEMPORARY '.freeze
|
12
13
|
|
13
14
|
# Add the primary_keys and primary_key_sequences instance variables,
|
14
15
|
# so we can get the correct return values for inserted rows.
|
@@ -44,13 +45,10 @@ module Sequel
|
|
44
45
|
# generator for auto-incrementing primary keys.
|
45
46
|
def create_table(name, options={}, &block)
|
46
47
|
options = {:generator=>options} if options.is_a?(Schema::Generator)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
nil
|
52
|
-
end if statements[1]
|
53
|
-
statements[0].flatten.each {|sql| execute_ddl(sql)}
|
48
|
+
generator = options[:generator] || Schema::Generator.new(self, &block)
|
49
|
+
drop_statement, create_statements = create_table_sql_list(name, generator, options)
|
50
|
+
(execute_ddl(drop_statement) rescue nil) if drop_statement
|
51
|
+
(create_statements + index_sql_list(name, generator.indexes)).each{|sql| execute_ddl(sql)}
|
54
52
|
end
|
55
53
|
|
56
54
|
def create_trigger(*args)
|
@@ -106,10 +104,6 @@ module Sequel
|
|
106
104
|
end
|
107
105
|
|
108
106
|
def transaction(opts={})
|
109
|
-
unless opts.is_a?(Hash)
|
110
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
111
|
-
opts = {:server=>opts}
|
112
|
-
end
|
113
107
|
synchronize(opts[:server]) do |conn|
|
114
108
|
return yield(conn) if @transactions.include?(Thread.current)
|
115
109
|
log_info("Transaction.begin")
|
@@ -157,10 +151,10 @@ module Sequel
|
|
157
151
|
"CREATE SEQUENCE #{quote_identifier(name)}"
|
158
152
|
end
|
159
153
|
|
160
|
-
def create_table_sql_list(name,
|
161
|
-
statements =
|
154
|
+
def create_table_sql_list(name, generator, options={})
|
155
|
+
statements = [create_table_sql(name, generator, options)]
|
162
156
|
drop_seq_statement = nil
|
163
|
-
columns.each do |c|
|
157
|
+
generator.columns.each do |c|
|
164
158
|
if c[:auto_increment]
|
165
159
|
c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
|
166
160
|
unless c[:create_sequence] == false
|
@@ -183,7 +177,7 @@ module Sequel
|
|
183
177
|
end
|
184
178
|
end
|
185
179
|
end
|
186
|
-
[
|
180
|
+
[drop_seq_statement, statements]
|
187
181
|
end
|
188
182
|
|
189
183
|
def create_trigger_sql(table, name, definition, opts={})
|
@@ -211,6 +205,10 @@ module Sequel
|
|
211
205
|
seq_name = quote_identifier(name)
|
212
206
|
"ALTER SEQUENCE #{seq_name} RESTART WITH #{opts[:restart_position]}"
|
213
207
|
end
|
208
|
+
|
209
|
+
def type_literal_generic_string(column)
|
210
|
+
column[:text] ? :"BLOB SUBTYPE TEXT" : super
|
211
|
+
end
|
214
212
|
end
|
215
213
|
|
216
214
|
# Dataset class for Firebird datasets
|
@@ -273,9 +271,9 @@ module Sequel
|
|
273
271
|
SELECT_CLAUSE_ORDER
|
274
272
|
end
|
275
273
|
|
276
|
-
def select_limit_sql(sql
|
277
|
-
sql << " FIRST #{opts[:limit]}" if opts[:limit]
|
278
|
-
sql << " SKIP #{opts[:offset]}" if opts[:offset]
|
274
|
+
def select_limit_sql(sql)
|
275
|
+
sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
|
276
|
+
sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
|
279
277
|
end
|
280
278
|
|
281
279
|
private
|
@@ -6,6 +6,8 @@ module Sequel
|
|
6
6
|
class Database < Sequel::Database
|
7
7
|
set_adapter_scheme :informix
|
8
8
|
|
9
|
+
TEMPORARY = 'TEMP '.freeze
|
10
|
+
|
9
11
|
def connect(server)
|
10
12
|
opts = server_opts(server)
|
11
13
|
::Informix.connect(opts[:database], opts[:user], opts[:password])
|
@@ -66,9 +68,9 @@ module Sequel
|
|
66
68
|
SELECT_CLAUSE_ORDER
|
67
69
|
end
|
68
70
|
|
69
|
-
def select_limit_sql(sql
|
70
|
-
sql << " SKIP #{opts[:offset]}" if opts[:offset]
|
71
|
-
sql << " FIRST #{opts[:limit]}" if opts[:limit]
|
71
|
+
def select_limit_sql(sql)
|
72
|
+
sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
|
73
|
+
sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -185,11 +185,27 @@ module Sequel
|
|
185
185
|
execute(sql, {:type=>:insert}.merge(opts))
|
186
186
|
end
|
187
187
|
|
188
|
+
# Return a hash containing index information. Hash keys are index name symbols.
|
189
|
+
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
190
|
+
# is an array of symbols of column names. The value of :unique is true or false
|
191
|
+
# depending on if the index is unique.
|
192
|
+
def indexes(table)
|
193
|
+
indexes = {}
|
194
|
+
m = output_identifier_meth
|
195
|
+
metadata(:getIndexInfo, nil, nil, input_identifier_meth.call(table), false, true) do |r|
|
196
|
+
next unless name = r[:column_name]
|
197
|
+
next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re
|
198
|
+
i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>!r[:non_unique]}
|
199
|
+
i[:columns] << m.call(name)
|
200
|
+
end
|
201
|
+
indexes
|
202
|
+
end
|
203
|
+
|
188
204
|
# All tables in this database
|
189
205
|
def tables
|
190
206
|
ts = []
|
191
|
-
|
192
|
-
metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts <<
|
207
|
+
m = output_identifier_meth
|
208
|
+
metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << m.call(h[:table_name])}
|
193
209
|
ts
|
194
210
|
end
|
195
211
|
|
@@ -197,10 +213,6 @@ module Sequel
|
|
197
213
|
# databases. Does not use the JDBC transaction methods, uses
|
198
214
|
# SQL BEGIN/ROLLBACK/COMMIT statements instead.
|
199
215
|
def transaction(opts={})
|
200
|
-
unless opts.is_a?(Hash)
|
201
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
202
|
-
opts = {:server=>opts}
|
203
|
-
end
|
204
216
|
synchronize(opts[:server]) do |conn|
|
205
217
|
return yield(conn) if @transactions.include?(Thread.current)
|
206
218
|
stmt = conn.createStatement
|
@@ -308,9 +320,7 @@ module Sequel
|
|
308
320
|
|
309
321
|
# Yield the metadata for this database
|
310
322
|
def metadata(*args, &block)
|
311
|
-
|
312
|
-
ds.identifier_output_method = :downcase
|
313
|
-
synchronize{|c| ds.send(:process_result_set, c.getMetaData.send(*args), &block)}
|
323
|
+
synchronize{|c| metadata_dataset.send(:process_result_set, c.getMetaData.send(*args), &block)}
|
314
324
|
end
|
315
325
|
|
316
326
|
# Java being java, you need to specify the type of each argument
|
@@ -347,16 +357,18 @@ module Sequel
|
|
347
357
|
|
348
358
|
# All tables in this database
|
349
359
|
def schema_parse_table(table, opts={})
|
360
|
+
m = output_identifier_meth
|
361
|
+
im = input_identifier_meth
|
350
362
|
ds = dataset
|
351
363
|
schema, table = schema_and_table(table)
|
352
|
-
schema =
|
353
|
-
table =
|
364
|
+
schema = im.call(schema) if schema
|
365
|
+
table = im.call(table)
|
354
366
|
pks, ts = [], []
|
355
367
|
metadata(:getPrimaryKeys, nil, schema, table) do |h|
|
356
368
|
pks << h[:column_name]
|
357
369
|
end
|
358
370
|
metadata(:getColumns, nil, schema, table, nil) do |h|
|
359
|
-
ts << [
|
371
|
+
ts << [m.call(h[:column_name]), {:type=>schema_column_type(h[:type_name]), :db_type=>h[:type_name], :default=>(h[:column_def] == '' ? nil : h[:column_def]), :allow_null=>(h[:nullable] != 0), :primary_key=>pks.include?(h[:column_name])}]
|
360
372
|
end
|
361
373
|
ts
|
362
374
|
end
|
@@ -476,11 +488,3 @@ module Sequel
|
|
476
488
|
end
|
477
489
|
end
|
478
490
|
end
|
479
|
-
|
480
|
-
class Java::JavaSQL::Timestamp
|
481
|
-
# Add a usec method in order to emulate Time values.
|
482
|
-
def usec
|
483
|
-
Deprecation.deprecate('Java::JavaSQL::Timestamp#usec', 'Use timestamp.getNanos/1000')
|
484
|
-
getNanos/1000
|
485
|
-
end
|
486
|
-
end
|
@@ -6,6 +6,13 @@ module Sequel
|
|
6
6
|
module H2
|
7
7
|
# Instance methods for H2 Database objects accessed via JDBC.
|
8
8
|
module DatabaseMethods
|
9
|
+
PRIMARY_KEY_INDEX_RE = /\Aprimary_key/i.freeze
|
10
|
+
|
11
|
+
# H2 uses the :h2 database type.
|
12
|
+
def database_type
|
13
|
+
:h2
|
14
|
+
end
|
15
|
+
|
9
16
|
# Return Sequel::JDBC::H2::Dataset object with the given opts.
|
10
17
|
def dataset(opts=nil)
|
11
18
|
Sequel::JDBC::H2::Dataset.new(self, opts)
|
@@ -32,6 +39,12 @@ module Sequel
|
|
32
39
|
super(table, op)
|
33
40
|
end
|
34
41
|
end
|
42
|
+
|
43
|
+
# Default to a single connection for a memory database.
|
44
|
+
def connection_pool_default_options
|
45
|
+
o = super
|
46
|
+
uri == 'jdbc:h2:mem:' ? o.merge(:max_connections=>1) : o
|
47
|
+
end
|
35
48
|
|
36
49
|
# Use IDENTITY() to get the last inserted id.
|
37
50
|
def last_insert_id(conn, opts={})
|
@@ -45,10 +58,8 @@ module Sequel
|
|
45
58
|
end
|
46
59
|
end
|
47
60
|
|
48
|
-
|
49
|
-
|
50
|
-
o = super
|
51
|
-
uri == 'jdbc:h2:mem:' ? o.merge(:max_connections=>1) : o
|
61
|
+
def primary_key_index_re
|
62
|
+
PRIMARY_KEY_INDEX_RE
|
52
63
|
end
|
53
64
|
end
|
54
65
|
|
@@ -135,10 +135,6 @@ module Sequel
|
|
135
135
|
|
136
136
|
# Support single level transactions on MySQL.
|
137
137
|
def transaction(opts={})
|
138
|
-
unless opts.is_a?(Hash)
|
139
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
140
|
-
opts = {:server=>opts}
|
141
|
-
end
|
142
138
|
synchronize(opts[:server]) do |conn|
|
143
139
|
return yield(conn) if @transactions.include?(Thread.current)
|
144
140
|
log_info(begin_transaction_sql)
|
@@ -297,8 +293,8 @@ module Sequel
|
|
297
293
|
end
|
298
294
|
|
299
295
|
# Delete rows matching this dataset
|
300
|
-
def delete
|
301
|
-
execute_dui(
|
296
|
+
def delete
|
297
|
+
execute_dui(delete_sql){|c| c.affected_rows}
|
302
298
|
end
|
303
299
|
|
304
300
|
# Yield all rows matching this dataset
|
@@ -338,8 +334,8 @@ module Sequel
|
|
338
334
|
end
|
339
335
|
|
340
336
|
# Update the matching rows.
|
341
|
-
def update(values={}
|
342
|
-
execute_dui(
|
337
|
+
def update(values={})
|
338
|
+
execute_dui(update_sql(values)){|c| c.affected_rows}
|
343
339
|
end
|
344
340
|
|
345
341
|
private
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -65,10 +65,6 @@ module Sequel
|
|
65
65
|
|
66
66
|
# Support single level transactions on ODBC
|
67
67
|
def transaction(opts={})
|
68
|
-
unless opts.is_a?(Hash)
|
69
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
70
|
-
opts = {:server=>opts}
|
71
|
-
end
|
72
68
|
synchronize(opts[:server]) do |conn|
|
73
69
|
return yield(conn) if @transactions.include?(Thread.current)
|
74
70
|
log_info(begin_transaction_sql)
|
@@ -78,10 +78,6 @@ module Sequel
|
|
78
78
|
alias do execute
|
79
79
|
|
80
80
|
def transaction(opts={})
|
81
|
-
unless opts.is_a?(Hash)
|
82
|
-
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
83
|
-
opts = {:server=>opts}
|
84
|
-
end
|
85
81
|
synchronize(opts[:server]) do |conn|
|
86
82
|
return yield(conn) if @transactions.include?(Thread.current)
|
87
83
|
conn.autocommit = false
|
@@ -7,7 +7,13 @@ module Sequel
|
|
7
7
|
SQL_BEGIN = "BEGIN TRANSACTION".freeze
|
8
8
|
SQL_COMMIT = "COMMIT TRANSACTION".freeze
|
9
9
|
SQL_ROLLBACK = "ROLLBACK TRANSACTION".freeze
|
10
|
+
TEMPORARY = "#".freeze
|
10
11
|
|
12
|
+
# Microsoft SQL Server uses the :mssql type.
|
13
|
+
def database_type
|
14
|
+
:mssql
|
15
|
+
end
|
16
|
+
|
11
17
|
def dataset(opts = nil)
|
12
18
|
ds = super
|
13
19
|
ds.extend(DatasetMethods)
|
@@ -34,6 +40,11 @@ module Sequel
|
|
34
40
|
def rollback_transaction_sql
|
35
41
|
SQL_ROLLBACK
|
36
42
|
end
|
43
|
+
|
44
|
+
# SQL fragment for marking a table as temporary
|
45
|
+
def temporary_table_sql
|
46
|
+
TEMPORARY
|
47
|
+
end
|
37
48
|
end
|
38
49
|
|
39
50
|
module DatasetMethods
|
@@ -79,14 +90,14 @@ module Sequel
|
|
79
90
|
end
|
80
91
|
|
81
92
|
# MSSQL uses TOP for limit, with no offset support
|
82
|
-
def select_limit_sql(sql
|
83
|
-
raise(Error, "OFFSET not supported") if opts[:offset]
|
84
|
-
sql << " TOP #{opts[:limit]}" if opts[:limit]
|
93
|
+
def select_limit_sql(sql)
|
94
|
+
raise(Error, "OFFSET not supported") if @opts[:offset]
|
95
|
+
sql << " TOP #{@opts[:limit]}" if @opts[:limit]
|
85
96
|
end
|
86
97
|
|
87
98
|
# MSSQL uses the WITH statement to lock tables
|
88
|
-
def select_with_sql(sql
|
89
|
-
sql << " WITH #{opts[:with]}" if opts[:with]
|
99
|
+
def select_with_sql(sql)
|
100
|
+
sql << " WITH #{@opts[:with]}" if @opts[:with]
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
@@ -15,14 +15,37 @@ module Sequel
|
|
15
15
|
# currently supported by the native and JDBC adapters.
|
16
16
|
module DatabaseMethods
|
17
17
|
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
18
|
-
|
19
|
-
|
20
|
-
PRIMARY_KEY = Sequel::Database::PRIMARY_KEY
|
21
|
-
TYPES = Sequel::Database::TYPES.merge(DateTime=>'datetime', \
|
22
|
-
TrueClass=>'tinyint', FalseClass=>'tinyint')
|
23
|
-
UNIQUE = Sequel::Database::UNIQUE
|
24
|
-
UNSIGNED = Sequel::Database::UNSIGNED
|
18
|
+
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
19
|
+
PRIMARY = 'PRIMARY'.freeze
|
25
20
|
|
21
|
+
# MySQL's cast rules are restrictive in that you can't just cast to any possible
|
22
|
+
# database type.
|
23
|
+
def cast_type_literal(type)
|
24
|
+
CAST_TYPES[type] || super
|
25
|
+
end
|
26
|
+
|
27
|
+
# MySQL uses the :mysql database type
|
28
|
+
def database_type
|
29
|
+
:mysql
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return a hash containing index information. Hash keys are index name symbols.
|
33
|
+
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
34
|
+
# is an array of symbols of column names. The value of :unique is true or false
|
35
|
+
# depending on if the index is unique.
|
36
|
+
def indexes(table)
|
37
|
+
indexes = {}
|
38
|
+
m = output_identifier_meth
|
39
|
+
im = input_identifier_meth
|
40
|
+
metadata_dataset.with_sql("SHOW INDEX FROM ?", SQL::Identifier.new(im.call(table))).each do |r|
|
41
|
+
name = r[:Key_name]
|
42
|
+
next if name == PRIMARY
|
43
|
+
i = indexes[m.call(name)] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
|
44
|
+
i[:columns] << m.call(r[:Column_name])
|
45
|
+
end
|
46
|
+
indexes
|
47
|
+
end
|
48
|
+
|
26
49
|
# Get version of MySQL server, used for determined capabilities.
|
27
50
|
def server_version
|
28
51
|
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
@@ -34,10 +57,8 @@ module Sequel
|
|
34
57
|
# Options:
|
35
58
|
# * :server - Set the server to use
|
36
59
|
def tables(opts={})
|
37
|
-
|
38
|
-
|
39
|
-
ds2 = dataset
|
40
|
-
ds.map{|r| ds2.send(:output_identifier, r.values.first)}
|
60
|
+
m = output_identifier_meth
|
61
|
+
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
|
41
62
|
end
|
42
63
|
|
43
64
|
# Changes the database in use by issuing a USE statement. I would be
|
@@ -63,10 +84,15 @@ module Sequel
|
|
63
84
|
else
|
64
85
|
super(table, op)
|
65
86
|
end
|
66
|
-
when :rename_column
|
67
|
-
|
68
|
-
|
69
|
-
|
87
|
+
when :rename_column, :set_column_type, :set_column_null, :set_column_default
|
88
|
+
o = op[:op]
|
89
|
+
opts = schema(table).find{|x| x.first == op[:name]}
|
90
|
+
old_opts = opts ? opts.last : {}
|
91
|
+
name = o == :rename_column ? op[:new_name] : op[:name]
|
92
|
+
type = o == :set_column_type ? op[:type] : old_opts[:db_type]
|
93
|
+
null = o == :set_column_null ? op[:null] : old_opts[:allow_null]
|
94
|
+
default = o == :set_column_default ? op[:default] : (old_opts[:default].lit if old_opts[:default])
|
95
|
+
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(:name=>name, :type=>type, :null=>null, :default=>default)}"
|
70
96
|
when :drop_index
|
71
97
|
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
72
98
|
else
|
@@ -85,13 +111,11 @@ module Sequel
|
|
85
111
|
end
|
86
112
|
|
87
113
|
# Use MySQL specific syntax for engine type and character encoding
|
88
|
-
def
|
89
|
-
options[:engine]
|
90
|
-
options[:charset]
|
91
|
-
options[:collate]
|
92
|
-
|
93
|
-
sql.concat(index_list_sql_list(name, indexes)) if indexes && !indexes.empty?
|
94
|
-
sql
|
114
|
+
def create_table_sql(name, generator, options = {})
|
115
|
+
engine = options.include?(:engine) ? options[:engine] : Sequel::MySQL.default_engine
|
116
|
+
charset = options.include?(:charset) ? options[:charset] : Sequel::MySQL.default_charset
|
117
|
+
collate = options.include?(:collate) ? options[:collate] : Sequel::MySQL.default_collate
|
118
|
+
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
95
119
|
end
|
96
120
|
|
97
121
|
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
@@ -116,15 +140,19 @@ module Sequel
|
|
116
140
|
using = " USING #{index[:type]}" unless index[:type] == nil
|
117
141
|
"UNIQUE " if index[:unique]
|
118
142
|
end
|
119
|
-
"CREATE #{index_type}INDEX #{index_name} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}
|
143
|
+
"CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
120
144
|
end
|
121
145
|
|
146
|
+
# MySQL treats integer primary keys as autoincrementing.
|
147
|
+
def schema_autoincrementing_primary_key?(schema)
|
148
|
+
super and schema[:db_type] =~ /int/io
|
149
|
+
end
|
150
|
+
|
122
151
|
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
123
152
|
def schema_parse_table(table_name, opts)
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
ds.map do |row|
|
153
|
+
m = output_identifier_meth
|
154
|
+
im = input_identifier_meth
|
155
|
+
metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
|
128
156
|
row.delete(:Extra)
|
129
157
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
130
158
|
row[:default] = row.delete(:Default)
|
@@ -132,13 +160,26 @@ module Sequel
|
|
132
160
|
row[:default] = nil if blank_object?(row[:default])
|
133
161
|
row[:db_type] = row.delete(:Type)
|
134
162
|
row[:type] = schema_column_type(row[:db_type])
|
135
|
-
[
|
163
|
+
[m.call(row.delete(:Field)), row]
|
136
164
|
end
|
137
165
|
end
|
138
166
|
|
139
|
-
#
|
140
|
-
|
141
|
-
|
167
|
+
# MySQL has both datetime and timestamp classes, most people are going
|
168
|
+
# to want datetime
|
169
|
+
def type_literal_generic_datetime(column)
|
170
|
+
:datetime
|
171
|
+
end
|
172
|
+
|
173
|
+
# MySQL has both datetime and timestamp classes, most people are going
|
174
|
+
# to want datetime
|
175
|
+
def type_literal_generic_time(column)
|
176
|
+
column[:only_time] ? :time : :datetime
|
177
|
+
end
|
178
|
+
|
179
|
+
# MySQL doesn't have a true boolean class, so it uses tinyint
|
180
|
+
# MySQL doesn't have a true boolean class, so it uses tinyint
|
181
|
+
def type_literal_generic_trueclass(column)
|
182
|
+
:tinyint
|
142
183
|
end
|
143
184
|
end
|
144
185
|
|
@@ -148,15 +189,9 @@ module Sequel
|
|
148
189
|
|
149
190
|
BOOL_TRUE = '1'.freeze
|
150
191
|
BOOL_FALSE = '0'.freeze
|
151
|
-
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
152
192
|
TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S'".freeze
|
153
193
|
COMMA_SEPARATOR = ', '.freeze
|
154
194
|
|
155
|
-
# MySQL can't use the varchar type in a cast.
|
156
|
-
def cast_sql(expr, type)
|
157
|
-
"CAST(#{literal(expr)} AS #{CAST_TYPES[type] || db.send(:type_literal_base, :type=>type)})"
|
158
|
-
end
|
159
|
-
|
160
195
|
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
161
196
|
# string concatenation.
|
162
197
|
def complex_expression_sql(op, args)
|
@@ -175,22 +210,10 @@ module Sequel
|
|
175
210
|
end
|
176
211
|
|
177
212
|
# MySQL supports ORDER and LIMIT clauses in DELETE statements.
|
178
|
-
def delete_sql
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
else
|
183
|
-
sql = super
|
184
|
-
opts = opts ? @opts.merge(opts) : @opts
|
185
|
-
end
|
186
|
-
|
187
|
-
if order = opts[:order]
|
188
|
-
sql << " ORDER BY #{expression_list(order)}"
|
189
|
-
end
|
190
|
-
if limit = opts[:limit]
|
191
|
-
sql << " LIMIT #{limit}"
|
192
|
-
end
|
193
|
-
|
213
|
+
def delete_sql
|
214
|
+
sql = super
|
215
|
+
sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
|
216
|
+
sql << " LIMIT #{opts[:limit]}" if opts[:limit]
|
194
217
|
sql
|
195
218
|
end
|
196
219
|
|
@@ -200,19 +223,14 @@ module Sequel
|
|
200
223
|
super
|
201
224
|
end
|
202
225
|
|
203
|
-
#
|
226
|
+
# Adds full text filter
|
204
227
|
def full_text_search(cols, terms, opts = {})
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
end
|
212
|
-
else
|
213
|
-
"MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)}#{mode})"
|
214
|
-
end
|
215
|
-
filter(s)
|
228
|
+
filter(full_text_sql(cols, terms, opts))
|
229
|
+
end
|
230
|
+
|
231
|
+
# MySQL specific full text search syntax.
|
232
|
+
def full_text_sql(cols, term, opts = {})
|
233
|
+
"MATCH #{literal(Array(cols))} AGAINST (#{literal(Array(term).join(' '))}#{" IN BOOLEAN MODE" if opts[:boolean]})"
|
216
234
|
end
|
217
235
|
|
218
236
|
# MySQL allows HAVING clause on ungrouped datasets.
|
@@ -311,11 +329,6 @@ module Sequel
|
|
311
329
|
else
|
312
330
|
values = values[0] if values.size == 1
|
313
331
|
|
314
|
-
# if hash or array with keys we need to transform the values
|
315
|
-
if @transform && (values.is_a?(Hash) || (values.is_a?(Array) && values.keys))
|
316
|
-
values = transform_save(values)
|
317
|
-
end
|
318
|
-
|
319
332
|
case values
|
320
333
|
when Array
|
321
334
|
if values.empty?
|
@@ -344,22 +357,10 @@ module Sequel
|
|
344
357
|
end
|
345
358
|
|
346
359
|
# MySQL supports ORDER and LIMIT clauses in UPDATE statements.
|
347
|
-
def update_sql(values
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
else
|
352
|
-
sql = super
|
353
|
-
opts = opts ? @opts.merge(opts) : @opts
|
354
|
-
end
|
355
|
-
|
356
|
-
if order = opts[:order]
|
357
|
-
sql << " ORDER BY #{expression_list(order)}"
|
358
|
-
end
|
359
|
-
if limit = opts[:limit]
|
360
|
-
sql << " LIMIT #{limit}"
|
361
|
-
end
|
362
|
-
|
360
|
+
def update_sql(values)
|
361
|
+
sql = super
|
362
|
+
sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
|
363
|
+
sql << " LIMIT #{opts[:limit]}" if opts[:limit]
|
363
364
|
sql
|
364
365
|
end
|
365
366
|
|