sequel 3.31.0 → 3.32.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +74 -30
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/schema_modification.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/db2.rb +7 -0
- data/lib/sequel/adapters/jdbc/derby.rb +13 -0
- data/lib/sequel/adapters/jdbc/h2.rb +10 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +7 -0
- data/lib/sequel/adapters/mock.rb +4 -0
- data/lib/sequel/adapters/mysql.rb +3 -0
- data/lib/sequel/adapters/oracle.rb +7 -3
- data/lib/sequel/adapters/shared/db2.rb +9 -2
- data/lib/sequel/adapters/shared/mssql.rb +48 -2
- data/lib/sequel/adapters/shared/mysql.rb +24 -4
- data/lib/sequel/adapters/shared/oracle.rb +7 -6
- data/lib/sequel/adapters/shared/progress.rb +1 -1
- data/lib/sequel/adapters/shared/sqlite.rb +16 -10
- data/lib/sequel/core.rb +22 -0
- data/lib/sequel/database/query.rb +13 -4
- data/lib/sequel/dataset/actions.rb +20 -11
- data/lib/sequel/dataset/mutation.rb +7 -1
- data/lib/sequel/dataset/prepared_statements.rb +11 -0
- data/lib/sequel/dataset/sql.rb +21 -24
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/model.rb +5 -2
- data/lib/sequel/model/associations.rb +70 -16
- data/lib/sequel/model/base.rb +11 -6
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/composition.rb +43 -10
- data/lib/sequel/plugins/many_through_many.rb +4 -1
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- data/lib/sequel/plugins/serialization.rb +13 -8
- data/lib/sequel/plugins/serialization_modification_detection.rb +22 -10
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +33 -10
- data/spec/adapters/mysql_spec.rb +111 -91
- data/spec/adapters/oracle_spec.rb +18 -0
- data/spec/core/database_spec.rb +1 -0
- data/spec/core/dataset_spec.rb +110 -15
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/many_through_many_spec.rb +14 -14
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -1
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/integration/associations_test.rb +278 -154
- data/spec/integration/dataset_test.rb +39 -2
- data/spec/integration/plugin_test.rb +63 -3
- data/spec/integration/prepared_statement_test.rb +10 -3
- data/spec/integration/schema_test.rb +61 -14
- data/spec/integration/transaction_test.rb +10 -0
- data/spec/model/associations_spec.rb +170 -80
- data/spec/model/hooks_spec.rb +40 -0
- metadata +4 -2
@@ -6,6 +6,8 @@ module Sequel
|
|
6
6
|
module Derby
|
7
7
|
# Instance methods for Derby Database objects accessed via JDBC.
|
8
8
|
module DatabaseMethods
|
9
|
+
PRIMARY_KEY_INDEX_RE = /\Asql\d+\z/i.freeze
|
10
|
+
|
9
11
|
include ::Sequel::JDBC::Transactions
|
10
12
|
|
11
13
|
# Derby doesn't support casting integer to varchar, only integer to char,
|
@@ -37,6 +39,12 @@ module Sequel
|
|
37
39
|
|
38
40
|
private
|
39
41
|
|
42
|
+
# Derby optimizes away Sequel's default check of SELECT NULL FROM table,
|
43
|
+
# so use a SELECT * FROM table there.
|
44
|
+
def _table_exists?(ds)
|
45
|
+
ds.first
|
46
|
+
end
|
47
|
+
|
40
48
|
# Derby-specific syntax for renaming columns and changing a columns type/nullity.
|
41
49
|
def alter_table_sql(table, op)
|
42
50
|
case op[:op]
|
@@ -86,6 +94,11 @@ module Sequel
|
|
86
94
|
"RENAME TABLE #{quote_schema_table(name)} TO #{quote_schema_table(new_name)}"
|
87
95
|
end
|
88
96
|
|
97
|
+
# Primary key indexes appear to be named sqlNNNN on Derby
|
98
|
+
def primary_key_index_re
|
99
|
+
PRIMARY_KEY_INDEX_RE
|
100
|
+
end
|
101
|
+
|
89
102
|
# If an :identity option is present in the column, add the necessary IDENTITY SQL.
|
90
103
|
def type_literal(column)
|
91
104
|
if column[:identity]
|
@@ -75,7 +75,16 @@ module Sequel
|
|
75
75
|
when :set_column_null
|
76
76
|
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET#{' NOT' unless op[:null]} NULL"
|
77
77
|
when :set_column_type
|
78
|
-
|
78
|
+
if sch = schema(table)
|
79
|
+
if cs = sch.each{|k, v| break v if k == op[:name]; nil}
|
80
|
+
cs = cs.dup
|
81
|
+
cs[:default] = cs[:ruby_default]
|
82
|
+
op = cs.merge!(op)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
sql = "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}"
|
86
|
+
column_definition_order.each{|m| send(:"column_definition_#{m}_sql", sql, op)}
|
87
|
+
sql
|
79
88
|
else
|
80
89
|
super(table, op)
|
81
90
|
end
|
@@ -6,6 +6,8 @@ module Sequel
|
|
6
6
|
module HSQLDB
|
7
7
|
# Instance methods for HSQLDB Database objects accessed via JDBC.
|
8
8
|
module DatabaseMethods
|
9
|
+
PRIMARY_KEY_INDEX_RE = /\Asys_idx_sys_pk_/i.freeze
|
10
|
+
|
9
11
|
include ::Sequel::JDBC::Transactions
|
10
12
|
|
11
13
|
# HSQLDB uses the :hsqldb database type.
|
@@ -54,6 +56,11 @@ module Sequel
|
|
54
56
|
rs.getInt(1)
|
55
57
|
end
|
56
58
|
end
|
59
|
+
|
60
|
+
# Primary key indexes appear to start with sys_idx_sys_pk_ on HSQLDB
|
61
|
+
def primary_key_index_re
|
62
|
+
PRIMARY_KEY_INDEX_RE
|
63
|
+
end
|
57
64
|
|
58
65
|
# If an :identity option is present in the column, add the necessary IDENTITY SQL.
|
59
66
|
# It's possible to use an IDENTITY type, but that defaults the sequence to start
|
@@ -7,6 +7,8 @@ module Sequel
|
|
7
7
|
module Oracle
|
8
8
|
# Instance methods for Oracle Database objects accessed via JDBC.
|
9
9
|
module DatabaseMethods
|
10
|
+
PRIMARY_KEY_INDEX_RE = /\Asys_/i.freeze
|
11
|
+
|
10
12
|
include Sequel::Oracle::DatabaseMethods
|
11
13
|
include Sequel::JDBC::Transactions
|
12
14
|
|
@@ -39,6 +41,11 @@ module Sequel
|
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
44
|
+
# Primary key indexes appear to start with sys_ on Oracle
|
45
|
+
def primary_key_index_re
|
46
|
+
PRIMARY_KEY_INDEX_RE
|
47
|
+
end
|
48
|
+
|
42
49
|
def schema_parse_table(*)
|
43
50
|
sch = super
|
44
51
|
sch.each do |c, s|
|
data/lib/sequel/adapters/mock.rb
CHANGED
@@ -29,6 +29,9 @@ module Sequel
|
|
29
29
|
|
30
30
|
class << self
|
31
31
|
# Whether to convert invalid date time values by default.
|
32
|
+
#
|
33
|
+
# Only applies to Sequel::Database instances created after this
|
34
|
+
# has been set.
|
32
35
|
attr_accessor :convert_invalid_date_time
|
33
36
|
end
|
34
37
|
self.convert_invalid_date_time = false
|
@@ -236,9 +236,13 @@ module Sequel
|
|
236
236
|
pks = ds.select_map(:cols__column_name)
|
237
237
|
|
238
238
|
# Default values
|
239
|
-
defaults =
|
240
|
-
|
241
|
-
|
239
|
+
defaults = begin
|
240
|
+
metadata_dataset.from(:dba_tab_cols).
|
241
|
+
where(:table_name=>im.call(table)).
|
242
|
+
to_hash(:column_name, :data_default)
|
243
|
+
rescue DatabaseError
|
244
|
+
{}
|
245
|
+
end
|
242
246
|
|
243
247
|
metadata = synchronize(opts[:server]) do |conn|
|
244
248
|
begin
|
@@ -60,9 +60,16 @@ module Sequel
|
|
60
60
|
|
61
61
|
# Use SYSCAT.INDEXES to get the indexes for the table
|
62
62
|
def indexes(table, opts = {})
|
63
|
+
m = output_identifier_meth
|
64
|
+
indexes = {}
|
63
65
|
metadata_dataset.
|
64
|
-
|
65
|
-
|
66
|
+
from(:syscat__indexes).
|
67
|
+
select(:indname, :uniquerule, :colnames).
|
68
|
+
where(:tabname=>input_identifier_meth.call(table), :system_required=>0).
|
69
|
+
each do |r|
|
70
|
+
indexes[m.call(r[:indname])] = {:unique=>(r[:uniquerule]=='U'), :columns=>r[:colnames][1..-1].split('+').map{|v| m.call(v)}}
|
71
|
+
end
|
72
|
+
indexes
|
66
73
|
end
|
67
74
|
|
68
75
|
private
|
@@ -28,6 +28,26 @@ module Sequel
|
|
28
28
|
:mssql
|
29
29
|
end
|
30
30
|
|
31
|
+
# Use the system tables to get index information
|
32
|
+
def indexes(table, opts={})
|
33
|
+
m = output_identifier_meth
|
34
|
+
im = input_identifier_meth
|
35
|
+
indexes = {}
|
36
|
+
metadata_dataset.from(:sys__tables___t).
|
37
|
+
join(:sys__indexes___i, :object_id=>:object_id).
|
38
|
+
join(:sys__index_columns___ic, :object_id=>:object_id, :index_id=>:index_id).
|
39
|
+
join(:sys__columns___c, :object_id=>:object_id, :column_id=>:column_id).
|
40
|
+
select(:i__name, :i__is_unique, :c__name___column).
|
41
|
+
where{{t__name=>im.call(table)}}.
|
42
|
+
where(:i__is_primary_key=>0, :i__is_disabled=>0).
|
43
|
+
order(:i__name, :ic__index_column_id).
|
44
|
+
each do |r|
|
45
|
+
index = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>(r[:is_unique] && r[:is_unique]!=0)}
|
46
|
+
index[:columns] << m.call(r[:column])
|
47
|
+
end
|
48
|
+
indexes
|
49
|
+
end
|
50
|
+
|
31
51
|
# The version of the MSSQL server, as an integer (e.g. 10001600 for
|
32
52
|
# SQL Server 2008 Express).
|
33
53
|
def server_version(server=nil)
|
@@ -79,7 +99,21 @@ module Sequel
|
|
79
99
|
when :rename_column
|
80
100
|
"sp_rename #{literal("#{quote_schema_table(table)}.#{quote_identifier(op[:name])}")}, #{literal(op[:new_name].to_s)}, 'COLUMN'"
|
81
101
|
when :set_column_type
|
82
|
-
|
102
|
+
sqls = []
|
103
|
+
if sch = schema(table)
|
104
|
+
if cs = sch.each{|k, v| break v if k == op[:name]; nil}
|
105
|
+
cs = cs.dup
|
106
|
+
if constraint = default_constraint_name(table, op[:name])
|
107
|
+
sqls << "ALTER TABLE #{quote_schema_table(table)} DROP CONSTRAINT #{constraint}"
|
108
|
+
end
|
109
|
+
cs[:default] = cs[:ruby_default]
|
110
|
+
op = cs.merge!(op)
|
111
|
+
default = op.delete(:default)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
sqls << "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{column_definition_sql(op)}"
|
115
|
+
sqls << alter_table_sql(table, op.merge(:op=>:set_column_default, :default=>default)) if default
|
116
|
+
sqls
|
83
117
|
when :set_column_null
|
84
118
|
sch = schema(table).find{|k,v| k.to_s == op[:name].to_s}.last
|
85
119
|
type = sch[:db_type]
|
@@ -122,6 +156,18 @@ module Sequel
|
|
122
156
|
"CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)} (#{column_list_sql(generator)})"
|
123
157
|
end
|
124
158
|
|
159
|
+
# The name of the constraint for setting the default value on the table and column.
|
160
|
+
def default_constraint_name(table, column)
|
161
|
+
from(:sysobjects___c_obj).
|
162
|
+
join(:syscomments___com, :id=>:id).
|
163
|
+
join(:sysobjects___t_obj, :id=>:c_obj__parent_obj).
|
164
|
+
join(:sysconstraints___con, :constid=>:c_obj__id).
|
165
|
+
join(:syscolumns___col, :id=>:t_obj__id, :colid=>:colid).
|
166
|
+
where{{c_obj__uid=>user_id{}}}.
|
167
|
+
where(:c_obj__xtype=>'D', :t_obj__name=>table.to_s, :col__name=>column.to_s).
|
168
|
+
get(:c_obj__name)
|
169
|
+
end
|
170
|
+
|
125
171
|
# The SQL to drop an index for the table.
|
126
172
|
def drop_index_sql(table, op)
|
127
173
|
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
@@ -238,7 +284,7 @@ module Sequel
|
|
238
284
|
if index[:type] == :full_text
|
239
285
|
"CREATE FULLTEXT INDEX ON #{quote_schema_table(table_name)} #{literal(index[:columns])} KEY INDEX #{literal(index[:key_index])}"
|
240
286
|
else
|
241
|
-
"CREATE #{'UNIQUE ' if index[:unique]}#{'CLUSTERED ' if index[:type] == :clustered}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
287
|
+
"CREATE #{'UNIQUE ' if index[:unique]}#{'CLUSTERED ' if index[:type] == :clustered}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}#{" INCLUDE #{literal(index[:include])}" if index[:include]}"
|
242
288
|
end
|
243
289
|
end
|
244
290
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Sequel
|
2
2
|
Dataset::NON_SQL_OPTIONS << :insert_ignore
|
3
|
+
Dataset::NON_SQL_OPTIONS << :update_ignore
|
3
4
|
Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
|
4
5
|
|
5
6
|
module MySQL
|
@@ -311,13 +312,17 @@ module Sequel
|
|
311
312
|
# MySQL has both datetime and timestamp classes, most people are going
|
312
313
|
# to want datetime
|
313
314
|
def type_literal_generic_datetime(column)
|
314
|
-
:
|
315
|
+
if column[:default] == Sequel::CURRENT_TIMESTAMP
|
316
|
+
:timestamp
|
317
|
+
else
|
318
|
+
:datetime
|
319
|
+
end
|
315
320
|
end
|
316
321
|
|
317
322
|
# MySQL has both datetime and timestamp classes, most people are going
|
318
323
|
# to want datetime
|
319
324
|
def type_literal_generic_time(column)
|
320
|
-
column[:only_time] ? :time :
|
325
|
+
column[:only_time] ? :time : type_literal_generic_datetime(column)
|
321
326
|
end
|
322
327
|
|
323
328
|
# MySQL doesn't have a true boolean class, so it uses tinyint(1)
|
@@ -336,7 +341,7 @@ module Sequel
|
|
336
341
|
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from where order limit')
|
337
342
|
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
|
338
343
|
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
|
339
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set where order limit')
|
344
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
|
340
345
|
SPACE = Dataset::SPACE
|
341
346
|
PAREN_OPEN = Dataset::PAREN_OPEN
|
342
347
|
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
@@ -467,7 +472,7 @@ module Sequel
|
|
467
472
|
def insert_ignore
|
468
473
|
clone(:insert_ignore=>true)
|
469
474
|
end
|
470
|
-
|
475
|
+
|
471
476
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
472
477
|
# If you pass no arguments, ALL fields will be
|
473
478
|
# updated with the new values. If you pass the fields you
|
@@ -549,6 +554,16 @@ module Sequel
|
|
549
554
|
false
|
550
555
|
end
|
551
556
|
|
557
|
+
# Sets up the update methods to use UPDATE IGNORE.
|
558
|
+
# Useful if you have a unique key and want to just skip
|
559
|
+
# updating rows that violate the unique key restriction.
|
560
|
+
#
|
561
|
+
# dataset.update_ignore.update({:name => 'a', :value => 1})
|
562
|
+
# # UPDATE IGNORE tablename SET name = 'a', value = 1
|
563
|
+
def update_ignore
|
564
|
+
clone(:update_ignore=>true)
|
565
|
+
end
|
566
|
+
|
552
567
|
private
|
553
568
|
|
554
569
|
# MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
|
@@ -591,6 +606,11 @@ module Sequel
|
|
591
606
|
sql << IGNORE if opts[:insert_ignore]
|
592
607
|
end
|
593
608
|
|
609
|
+
# MySQL supports UPDATE IGNORE
|
610
|
+
def update_ignore_sql(sql)
|
611
|
+
sql << IGNORE if opts[:update_ignore]
|
612
|
+
end
|
613
|
+
|
594
614
|
# If this is an replace instead of an insert, use replace instead
|
595
615
|
def insert_insert_sql(sql)
|
596
616
|
sql << (@opts[:replace] ? REPLACE : INSERT)
|
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def current_user
|
20
|
-
@current_user ||= get{sys_context('USERENV', 'CURRENT_USER')}
|
20
|
+
@current_user ||= metadata_dataset.get{sys_context('USERENV', 'CURRENT_USER')}
|
21
21
|
end
|
22
22
|
|
23
23
|
def drop_sequence(name)
|
@@ -30,17 +30,18 @@ module Sequel
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def tables(opts={})
|
33
|
-
|
34
|
-
|
33
|
+
m = output_identifier_meth
|
34
|
+
metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'TABLE').map{|r| m.call(r[:tname])}
|
35
35
|
end
|
36
36
|
|
37
37
|
def views(opts={})
|
38
|
-
|
39
|
-
|
38
|
+
m = output_identifier_meth
|
39
|
+
metadata_dataset.from(:tab).server(opts[:server]).select(:tname).filter(:tabtype => 'VIEW').map{|r| m.call(r[:tname])}
|
40
40
|
end
|
41
41
|
|
42
42
|
def view_exists?(name)
|
43
|
-
|
43
|
+
m = input_identifier_meth
|
44
|
+
metadata_dataset.from(:tab).filter(:tname =>m.call(name), :tabtype => 'VIEW').count > 0
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
@@ -9,7 +9,7 @@ module Sequel
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module DatasetMethods
|
12
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
|
12
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
|
13
13
|
|
14
14
|
# Progress requires SQL standard datetimes
|
15
15
|
def requires_sql_standard_datetimes?
|
@@ -170,6 +170,8 @@ module Sequel
|
|
170
170
|
# Run all alter_table commands in a transaction. This is technically only
|
171
171
|
# needed for drop column.
|
172
172
|
def apply_alter_table(table, ops)
|
173
|
+
fks = foreign_keys
|
174
|
+
self.foreign_keys = false if fks
|
173
175
|
transaction do
|
174
176
|
if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
|
175
177
|
# If you are just doing constraints, apply all of them at the same time,
|
@@ -181,6 +183,7 @@ module Sequel
|
|
181
183
|
ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
|
182
184
|
end
|
183
185
|
end
|
186
|
+
self.foreign_keys = true if fks
|
184
187
|
end
|
185
188
|
|
186
189
|
# SQLite supports limited table modification. You can add a column
|
@@ -208,7 +211,7 @@ module Sequel
|
|
208
211
|
when :set_column_null
|
209
212
|
duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
|
210
213
|
when :set_column_type
|
211
|
-
duplicate_table(table){|columns| columns.each{|s| s
|
214
|
+
duplicate_table(table){|columns| columns.each{|s| s.merge!(op) if s[:name].to_s == op[:name].to_s}}
|
212
215
|
when :drop_constraint
|
213
216
|
case op[:type]
|
214
217
|
when :primary_key
|
@@ -238,6 +241,11 @@ module Sequel
|
|
238
241
|
end
|
239
242
|
end
|
240
243
|
|
244
|
+
# Surround default with parens to appease SQLite
|
245
|
+
def column_definition_default_sql(sql, column)
|
246
|
+
sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
|
247
|
+
end
|
248
|
+
|
241
249
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
242
250
|
# new connections.
|
243
251
|
def connection_pragmas
|
@@ -268,15 +276,13 @@ module Sequel
|
|
268
276
|
end
|
269
277
|
|
270
278
|
begin
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
# is there any way to get deferrable status?
|
279
|
-
end
|
279
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
280
|
+
c = cols.find {|co| co[:name] == row[:from] } or next
|
281
|
+
c[:table] = row[:table]
|
282
|
+
c[:key] = row[:to]
|
283
|
+
c[:on_update] = on_delete_sql_to_sym(row[:on_update])
|
284
|
+
c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
|
285
|
+
# is there any way to get deferrable status?
|
280
286
|
end
|
281
287
|
rescue Sequel::DatabaseError
|
282
288
|
# Doesn't work correctly on some versions of JDBC SQLite,
|
data/lib/sequel/core.rb
CHANGED
@@ -25,6 +25,7 @@
|
|
25
25
|
module Sequel
|
26
26
|
@convert_two_digit_years = true
|
27
27
|
@datetime_class = Time
|
28
|
+
@empty_array_handle_nulls = true
|
28
29
|
@virtual_row_instance_eval = true
|
29
30
|
@require_thread = nil
|
30
31
|
|
@@ -54,6 +55,27 @@ module Sequel
|
|
54
55
|
# days on +DateTime+).
|
55
56
|
attr_accessor :datetime_class
|
56
57
|
|
58
|
+
# Sets whether or not to attempt to handle NULL values correctly when given
|
59
|
+
# an empty array. By default:
|
60
|
+
#
|
61
|
+
# DB[:a].filter(:b=>[])
|
62
|
+
# # SELECT * FROM a WHERE (b != b)
|
63
|
+
# DB[:a].exclude(:b=>[])
|
64
|
+
# # SELECT * FROM a WHERE (b = b)
|
65
|
+
#
|
66
|
+
# However, some databases (e.g. MySQL) will perform very poorly
|
67
|
+
# with this type of query. You can set this to false to get the
|
68
|
+
# following behavior:
|
69
|
+
#
|
70
|
+
# DB[:a].filter(:b=>[])
|
71
|
+
# # SELECT * FROM a WHERE 1 = 0
|
72
|
+
# DB[:a].exclude(:b=>[])
|
73
|
+
# # SELECT * FROM a WHERE 1 = 1
|
74
|
+
#
|
75
|
+
# This may not handle NULLs correctly, but can be much faster on
|
76
|
+
# some databases.
|
77
|
+
attr_accessor :empty_array_handle_nulls
|
78
|
+
|
57
79
|
# For backwards compatibility, has no effect.
|
58
80
|
attr_accessor :virtual_row_instance_eval
|
59
81
|
|
@@ -184,16 +184,19 @@ module Sequel
|
|
184
184
|
# to the database.
|
185
185
|
#
|
186
186
|
# DB.table_exists?(:foo) # => false
|
187
|
-
# # SELECT
|
187
|
+
# # SELECT NULL FROM foo LIMIT 1
|
188
|
+
#
|
189
|
+
# Note that since this does a SELECT from the table, it can give false negatives
|
190
|
+
# if you don't have permission to SELECT from the table.
|
188
191
|
def table_exists?(name)
|
189
192
|
sch, table_name = schema_and_table(name)
|
190
193
|
name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
|
191
|
-
from(name)
|
194
|
+
_table_exists?(from(name))
|
192
195
|
true
|
193
|
-
rescue
|
196
|
+
rescue DatabaseError
|
194
197
|
false
|
195
198
|
end
|
196
|
-
|
199
|
+
|
197
200
|
# Return all tables in the database as an array of symbols.
|
198
201
|
#
|
199
202
|
# DB.tables # => [:albums, :artists]
|
@@ -238,6 +241,12 @@ module Sequel
|
|
238
241
|
|
239
242
|
private
|
240
243
|
|
244
|
+
# Should raise an error if the table doesn't not exist,
|
245
|
+
# and not raise an error if the table does exist.
|
246
|
+
def _table_exists?(ds)
|
247
|
+
ds.get(Sequel::NULL)
|
248
|
+
end
|
249
|
+
|
241
250
|
# Internal generic transaction method. Any exception raised by the given
|
242
251
|
# block will cause the transaction to be rolled back. If the exception is
|
243
252
|
# not a Sequel::Rollback, the error will be reraised. If no exception occurs
|