aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorRyuta Kamizono <kamipo@gmail.com>2016-05-12 07:47:28 +0900
committerRyuta Kamizono <kamipo@gmail.com>2017-01-17 08:20:10 +0900
commit24f264e9ca0f1ce3b16392b8ee6360480251f556 (patch)
treec380ae4b91b78cf1aa3b13be7be40804d7eade87 /activerecord
parentb0923e4ca2dcf9058421e14873304603586396f6 (diff)
downloadrails-24f264e9ca0f1ce3b16392b8ee6360480251f556.tar.gz
rails-24f264e9ca0f1ce3b16392b8ee6360480251f556.tar.bz2
rails-24f264e9ca0f1ce3b16392b8ee6360480251f556.zip
SQLite: Foreign Key Support
https://www.sqlite.org/foreignkeys.html
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb25
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb34
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb22
-rw-r--r--activerecord/test/schema/sqlite_specific_schema.rb18
6 files changed, 81 insertions, 30 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 322684672f..807df2184a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -15,9 +15,9 @@ module ActiveRecord
end
delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options, to: :@conn
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn
private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options
private
@@ -49,7 +49,7 @@ module ActiveRecord
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
end
- if supports_foreign_keys?
+ if supports_foreign_keys_in_create?
statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index cf2269bf12..348396de72 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -310,6 +310,12 @@ module ActiveRecord
false
end
+ # Does this adapter support creating foreign key constraints
+ # in the same statement as creating the table?
+ def supports_foreign_keys_in_create?
+ supports_foreign_keys?
+ end
+
# Does this adapter support views?
def supports_views?
false
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 2e3419991e..ca6de37a6b 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -130,6 +130,10 @@ module ActiveRecord
true
end
+ def supports_foreign_keys_in_create?
+ sqlite_version >= "3.6.19"
+ end
+
def supports_views?
true
end
@@ -439,6 +443,19 @@ module ActiveRecord
rename_column_indexes(table_name, column.name, new_column_name)
end
+ def foreign_keys(table_name)
+ fk_info = select_all("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
+ fk_info.map do |row|
+ options = {
+ column: row["from"],
+ primary_key: row["to"],
+ on_delete: extract_foreign_key_action(row["on_delete"]),
+ on_update: extract_foreign_key_action(row["on_update"])
+ }
+ ForeignKeyDefinition.new(table_name, row["table"], options)
+ end
+ end
+
private
def table_structure(table_name)
@@ -592,6 +609,14 @@ module ActiveRecord
SQLite3::TableDefinition.new(*args)
end
+ def extract_foreign_key_action(specifier)
+ case specifier
+ when "CASCADE"; :cascade
+ when "SET NULL"; :nullify
+ when "RESTRICT"; :restrict
+ end
+ end
+
def configure_connection
execute("PRAGMA foreign_keys = ON", "SCHEMA")
end
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index 9be6667aa1..96e775a58b 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -2,6 +2,26 @@ require "cases/helper"
require "support/ddl_helper"
require "support/schema_dumping_helper"
+if ActiveRecord::Base.connection.supports_foreign_keys_in_create?
+ module ActiveRecord
+ class Migration
+ class ForeignKeyInCreateTest < ActiveRecord::TestCase
+ def test_foreign_keys
+ foreign_keys = ActiveRecord::Base.connection.foreign_keys("fk_test_has_fk")
+ assert_equal 1, foreign_keys.size
+
+ fk = foreign_keys.first
+ assert_equal "fk_test_has_fk", fk.from_table
+ assert_equal "fk_test_has_pk", fk.to_table
+ assert_equal "fk_id", fk.column
+ assert_equal "pk_id", fk.primary_key
+ assert_equal "fk_name", fk.name unless current_adapter?(:SQLite3Adapter)
+ end
+ end
+ end
+ end
+end
+
if ActiveRecord::Base.connection.supports_foreign_keys?
module ActiveRecord
class Migration
@@ -29,10 +49,8 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
end
teardown do
- if defined?(@connection)
- @connection.drop_table "astronauts", if_exists: true
- @connection.drop_table "rockets", if_exists: true
- end
+ @connection.drop_table "astronauts", if_exists: true
+ @connection.drop_table "rockets", if_exists: true
end
def test_foreign_keys
@@ -305,9 +323,11 @@ else
@connection.remove_foreign_key :clubs, :categories
end
- def test_foreign_keys_should_raise_not_implemented
- assert_raises NotImplementedError do
- @connection.foreign_keys("clubs")
+ unless current_adapter?(:SQLite3Adapter)
+ def test_foreign_keys_should_raise_not_implemented
+ assert_raises NotImplementedError do
+ @connection.foreign_keys("clubs")
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 4957ab8b3d..560adcbfed 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -1,9 +1,9 @@
require "cases/helper"
-if ActiveRecord::Base.connection.supports_foreign_keys?
+if ActiveRecord::Base.connection.supports_foreign_keys_in_create?
module ActiveRecord
class Migration
- class ReferencesForeignKeyTest < ActiveRecord::TestCase
+ class ReferencesForeignKeyInCreateTest < ActiveRecord::TestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table(:testing_parents, force: true)
@@ -61,6 +61,24 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
assert_equal([["testings", "testing_parents", "parent_id"]],
fks.map { |fk| [fk.from_table, fk.to_table, fk.column] })
end
+ end
+ end
+ end
+end
+
+if ActiveRecord::Base.connection.supports_foreign_keys?
+ module ActiveRecord
+ class Migration
+ class ReferencesForeignKeyTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:testing_parents, force: true)
+ end
+
+ teardown do
+ @connection.drop_table "testings", if_exists: true
+ @connection.drop_table "testing_parents", if_exists: true
+ end
test "foreign keys cannot be added to polymorphic relations when creating the table" do
@connection.create_table :testings do |t|
diff --git a/activerecord/test/schema/sqlite_specific_schema.rb b/activerecord/test/schema/sqlite_specific_schema.rb
deleted file mode 100644
index cc7c36fe2b..0000000000
--- a/activerecord/test/schema/sqlite_specific_schema.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-ActiveRecord::Schema.define do
- execute "DROP TABLE fk_test_has_fk" rescue nil
- execute "DROP TABLE fk_test_has_pk" rescue nil
- execute <<_SQL
- CREATE TABLE 'fk_test_has_pk' (
- 'pk_id' INTEGER NOT NULL PRIMARY KEY
- );
-_SQL
-
- execute <<_SQL
- CREATE TABLE 'fk_test_has_fk' (
- 'id' INTEGER NOT NULL PRIMARY KEY,
- 'fk_id' INTEGER NOT NULL,
-
- FOREIGN KEY ('fk_id') REFERENCES 'fk_test_has_pk'('pk_id')
- );
-_SQL
-end