@@ -238,6 +238,10 @@ def supports_lazy_transactions?
238
238
true
239
239
end
240
240
241
+ def supports_deferrable_constraints?
242
+ true
243
+ end
244
+
241
245
# REFERENTIAL INTEGRITY ====================================
242
246
243
247
def disable_referential_integrity # :nodoc:
@@ -367,15 +371,31 @@ def add_reference(table_name, ref_name, **options) # :nodoc:
367
371
end
368
372
alias :add_belongs_to :add_reference
369
373
374
+ FK_REGEX = /.*FOREIGN KEY\s +\( "(\w +)"\) \s +REFERENCES\s +"(\w +)"\s +\( "(\w +)"\) /
375
+ DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w +)/
370
376
def foreign_keys ( table_name )
371
377
# SQLite returns 1 row for each column of composite foreign keys.
372
378
fk_info = internal_exec_query ( "PRAGMA foreign_key_list(#{ quote ( table_name ) } )" , "SCHEMA" )
379
+ # Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
380
+ fk_defs = table_structure_sql ( table_name )
381
+ . select do |column_string |
382
+ column_string . start_with? ( "CONSTRAINT" ) &&
383
+ column_string . include? ( "FOREIGN KEY" )
384
+ end
385
+ . to_h do |fk_string |
386
+ _ , from , table , to = fk_string . match ( FK_REGEX ) . to_a
387
+ _ , mode = fk_string . match ( DEFERRABLE_REGEX ) . to_a
388
+ deferred = mode &.downcase &.to_sym || false
389
+ [ [ table , from , to ] , deferred ]
390
+ end
391
+
373
392
grouped_fk = fk_info . group_by { |row | row [ "id" ] } . values . each { |group | group . sort_by! { |row | row [ "seq" ] } }
374
393
grouped_fk . map do |group |
375
394
row = group . first
376
395
options = {
377
396
on_delete : extract_foreign_key_action ( row [ "on_delete" ] ) ,
378
- on_update : extract_foreign_key_action ( row [ "on_update" ] )
397
+ on_update : extract_foreign_key_action ( row [ "on_update" ] ) ,
398
+ deferrable : fk_defs [ [ row [ "table" ] , row [ "from" ] , row [ "to" ] ] ]
379
399
}
380
400
381
401
if group . one?
@@ -649,24 +669,11 @@ def translate_exception(exception, message:, sql:, binds:)
649
669
def table_structure_with_collation ( table_name , basic_structure )
650
670
collation_hash = { }
651
671
auto_increments = { }
652
- sql = <<~SQL
653
- SELECT sql FROM
654
- (SELECT * FROM sqlite_master UNION ALL
655
- SELECT * FROM sqlite_temp_master)
656
- WHERE type = 'table' AND name = #{ quote ( table_name ) }
657
- SQL
658
-
659
- # Result will have following sample string
660
- # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
661
- # "password_digest" varchar COLLATE "NOCASE");
662
- result = query_value ( sql , "SCHEMA" )
663
672
664
- if result
665
- # Splitting with left parentheses and discarding the first part will return all
666
- # columns separated with comma(,).
667
- columns_string = result . split ( "(" , 2 ) . last
673
+ column_strings = table_structure_sql ( table_name )
668
674
669
- columns_string . split ( "," ) . each do |column_string |
675
+ if column_strings . any?
676
+ column_strings . each do |column_string |
670
677
# This regex will match the column name and collation type and will save
671
678
# the value in $1 and $2 respectively.
672
679
collation_hash [ $1] = $2 if COLLATE_REGEX =~ column_string
@@ -691,6 +698,28 @@ def table_structure_with_collation(table_name, basic_structure)
691
698
end
692
699
end
693
700
701
+ def table_structure_sql ( table_name )
702
+ sql = <<~SQL
703
+ SELECT sql FROM
704
+ (SELECT * FROM sqlite_master UNION ALL
705
+ SELECT * FROM sqlite_temp_master)
706
+ WHERE type = 'table' AND name = #{ quote ( table_name ) }
707
+ SQL
708
+
709
+ # Result will have following sample string
710
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
711
+ # "password_digest" varchar COLLATE "NOCASE");
712
+ result = query_value ( sql , "SCHEMA" )
713
+
714
+ return [ ] unless result
715
+
716
+ # Splitting with left parentheses and discarding the first part will return all
717
+ # columns separated with comma(,).
718
+ columns_string = result . split ( "(" , 2 ) . last
719
+
720
+ columns_string . split ( "," ) . map ( &:strip )
721
+ end
722
+
694
723
def arel_visitor
695
724
Arel ::Visitors ::SQLite . new ( self )
696
725
end
0 commit comments