New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cherry-pick to 1.1-dev: add foreign key self refer #14887
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
docs: 见 https://github.com/matrixorigin/docs/pull/270/files 例子: ```sql create table t1( a int primary key, b int, constraint `c1` foreign key `fk1` (b) references t1(a) ) ``` 1. create table 增加外键自引用 - 名称的处理 mysql 会为b建立次级索引KEY `fk1` (b)。将fk1作为次级索引的名称。 约束名称为c1。 mo不会在b上建立次级索引。因此fk1不被使用。 - 新增constraint name 原先没指定constraint name时,默认是空串。没有constraint name,alter table无法删除外键。 当输入的constraint name为空串时,生成一个uuid。 当输入的constraint name为空白符串时,报错。 其它,用输入的constarint name。 在增加constraint时,会检查重复。 在删除constraint时,会检查是否存在。 - getForeignKeyData拆出来checkFkColsAreValid。 checkFkColsAreValid检查外键的列是否合法 对于非自引用外键,getForeignKeyData的逻辑不变。 对于自引用外键,getForeignKeyData仅处理外键的定义,生产fkdata。 等tableDef的pk,uk都准备好后。再由checkFkColsAreValid检查外键的列是否合法。 因为pk,uk在语法上,可能在外键定义之后。 2. alter table 增加/删除 外键自引用 - 新增alter table ... drop constraint `c1` alter table ... drop foreign key `c1` 与 alter table ... drop constraint `c1` 相同都是删除外键。 drop foreign key 的名称也要填`c1`,而不是`fk1` - alter table ... add/drop constraint/foreign key 会检查约束的存在与否。 - alter table ... add constraint/foreign key 会检查表中的数据满足外键条件。如果不满足,报错。且add失败。 3. drop/truncate table 表被外键引用,都是外键自引用。表是可以删除的。 4. 外键自引用时,父表id 和 自表id都设置为0. plan.ForeignKeyDef.ForeignTbl = 0 RefChildTableDef.Tables 中自引用的tableId也为0 因此碰到tableId为0的情况,说明与外键自引用有关,需要特殊处理。 5. 特殊的case - insert into t2 values (1,2),(2,1); - mo不报错 - mysql 报错 - insert into t2 values (2,2); 都不报错。 mo的外键自引用检查在插入数据之后,进行的。 与mysql的做法不完全相同,mysql没有深入调研。 1. 生成外键检查sql 需要检查的场景: alter table ... add foreign key alter table ... add constraint insert update load 在上述语句构建plan时,会生成一个外键检查的sql。`genSqlForCheckFKConstraints`和`genSqlsForCheckFKSelfRefer` 构造这样的sql。 构建sql的方式,检查外键约束是否满足。 单个字段情形 父表: T(a) 子表: S(b) foreign key (b) references T(a) 生成的sql : select count(*) == 0 from ( select distinct S.b from S where S.b is not null except select distinct T.a from T ) 如果结果是true,则表中的数据满足约束条件。 多个字段情形 父表: T(a,b) 子表: S(c,d) foreign key (c,d) references T(a,b) 生成的sql : select count(*) == 0 from ( select distinct S.c,S.d from S where S.c is not null and S.d is not null except select distinct T.a,T.b from T ) 如果结果是true,则表中的数据满足约束条件。 如果有多个外键,每个外键都会生成一个sql。 对于外键自引用,S和T是同一个表。 2. 在语句执行完成后,会再执行外键检查sql。 如果检查失败,会报错。 在compile.run中,defer函数会执行`detectFkSelfRefer`来检查外键约束。 乐观事务下,下面的case 与 悲观事务的行为不一致。 ``` [SCRIPT FILE]: foreign_key/fk_self_refer2.sql [ROW NUMBER]: 117 [SQL STATEMENT]: insert into t1 values (1,2,3); [EXPECT RESULT]: Duplicate entry '1' for key '__mo_index_idx_col' [ACTUAL RESULT]: Cannot add or update a child row: a foreign key constraint fails [SCRIPT FILE]: foreign_key/fk_self_refer3.sql [ROW NUMBER]: 43 [SQL STATEMENT]: update t1 set b = 3 where b = 4; [EXPECT RESULT]: Duplicate entry '(1,3)' for key '__mo_cpkey_col' [ACTUAL RESULT]: Duplicate entry '3a15013a1503' for key '__mo_cpkey_col' [SCRIPT FILE]: foreign_key/fk_self_refer3.sql [ROW NUMBER]: 58 [SQL STATEMENT]: update t1 set c = 2 where b = 5; [EXPECT RESULT]: Duplicate entry '(1,2)' for key '__mo_index_idx_col' [ACTUAL RESULT]: Duplicate entry '3a15013a1502' for key '__mo_index_idx_col' [SCRIPT FILE]: foreign_key/fk_self_refer3.sql [ROW NUMBER]: 160 [SQL STATEMENT]: update t1 set c = 4 where b = 3; [EXPECT RESULT]: Duplicate entry '(1,4)' for key '__mo_index_idx_col' [ACTUAL RESULT]: Duplicate entry '3a15013a1504' for key '__mo_index_idx_col' ``` foreign_key_checks 在次级索引上加外键 Approved by: @nnsgmsone, @ouyuanning, @iamlinjunhong, @heni02, @aunjgr, @zhangxu19830126
daviszhen
requested review from
nnsgmsone,
ouyuanning,
aunjgr,
badboynt1,
iamlinjunhong,
zhangxu19830126,
aressu1985,
heni02 and
sukki37
as code owners
March 11, 2024 07:36
nnsgmsone
approved these changes
Mar 11, 2024
heni02
approved these changes
Mar 11, 2024
ouyuanning
approved these changes
Mar 11, 2024
aunjgr
approved these changes
Mar 12, 2024
zhangxu19830126
approved these changes
Mar 12, 2024
iamlinjunhong
approved these changes
Mar 12, 2024
sukki37
approved these changes
Mar 13, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What type of PR is this?
Which issue(s) this PR fixes:
issue #https://github.com/matrixorigin/MO-Cloud/issues/2284
https://github.com/matrixorigin/MO-Cloud/issues/1449
https://github.com/matrixorigin/MO-Cloud/issues/1450
What this PR does / why we need it:
docs: 见 https://github.com/matrixorigin/docs/pull/270/files
外键逻辑的修改
create table 增加外键自引用
名称的处理
mysql 会为b建立次级索引KEY
fk1
(b)。将fk1作为次级索引的名称。约束名称为c1。
mo不会在b上建立次级索引。因此fk1不被使用。
新增constraint name
原先没指定constraint name时,默认是空串。没有constraint name,alter table无法删除外键。
当输入的constraint name为空串时,生成一个uuid。
当输入的constraint name为空白符串时,报错。
其它,用输入的constarint name。
在增加constraint时,会检查重复。
在删除constraint时,会检查是否存在。
getForeignKeyData拆出来checkFkColsAreValid。
checkFkColsAreValid检查外键的列是否合法
对于非自引用外键,getForeignKeyData的逻辑不变。
对于自引用外键,getForeignKeyData仅处理外键的定义,生产fkdata。
等tableDef的pk,uk都准备好后。再由checkFkColsAreValid检查外键的列是否合法。
因为pk,uk在语法上,可能在外键定义之后。
alter table 增加/删除 外键自引用
新增alter table ... drop constraint
c1
alter table ... drop foreign key
c1
与 alter table ... drop constraintc1
相同都是删除外键。drop foreign key 的名称也要填
c1
,而不是fk1
alter table ... add/drop constraint/foreign key 会检查约束的存在与否。
alter table ... add constraint/foreign key
会检查表中的数据满足外键条件。如果不满足,报错。且add失败。
drop/truncate table
表被外键引用,都是外键自引用。表是可以删除的。
外键自引用时,父表id 和 自表id都设置为0.
plan.ForeignKeyDef.ForeignTbl = 0
RefChildTableDef.Tables 中自引用的tableId也为0
因此碰到tableId为0的情况,说明与外键自引用有关,需要特殊处理。
特殊的case
insert into t2 values (1,2),(2,1);
insert into t2 values (2,2);
都不报错。
用sql检查外键自引用
生成外键检查sql
需要检查的场景:
构建sql的方式,检查外键约束是否满足。
如果有多个外键,每个外键都会生成一个sql。
对于外键自引用,S和T是同一个表。
在语句执行完成后,会再执行外键检查sql。
如果检查失败,会报错。
在compile.run中,defer函数会执行
detectFkSelfRefer
来检查外键约束。已知的问题
进一步