func (d *ddl) onDropForeignKey(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var ( fkName model.CIStr found bool fkInfo model.FKInfo ) err = job.DecodeArgs(&fkName) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } for _, fk := range tblInfo.ForeignKeys { if fk.Name.L == fkName.L { found = true fkInfo = *fk } } if !found { return infoschema.ErrForeignKeyNotExists.Gen("foreign key doesn't exist", fkName) } nfks := tblInfo.ForeignKeys[:0] for _, fk := range tblInfo.ForeignKeys { if fk.Name.L != fkName.L { nfks = append(nfks, fk) } } tblInfo.ForeignKeys = nfks _, err = t.GenSchemaVersion() if err != nil { return errors.Trace(err) } switch fkInfo.State { case model.StatePublic: // We just support record the foreign key, so we just make it none. // public -> none job.SchemaState = model.StateNone fkInfo.State = model.StateNone err = t.UpdateTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } // finish this job job.State = model.JobDone return nil default: return ErrInvalidForeignKeyState.Gen("invalid fk state %v", fkInfo.State) } }
func (d *ddl) buildFKInfo(fkName model.CIStr, keys []*ast.IndexColName, refer *ast.ReferenceDef) (*model.FKInfo, error) { fkID, err := d.genGlobalID() if err != nil { return nil, errors.Trace(err) } var fkInfo model.FKInfo fkInfo.ID = fkID fkInfo.Name = fkName fkInfo.RefTable = refer.Table.Name fkInfo.Cols = make([]model.CIStr, len(keys)) for i, key := range keys { fkInfo.Cols[i] = key.Column.Name } fkInfo.RefCols = make([]model.CIStr, len(refer.IndexColNames)) for i, key := range refer.IndexColNames { fkInfo.RefCols[i] = key.Column.Name } fkInfo.OnDelete = int(refer.OnDelete.ReferOpt) fkInfo.OnUpdate = int(refer.OnUpdate.ReferOpt) return &fkInfo, nil }
func (d *ddl) onCreateForeignKey(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tblInfo, err := d.getTableInfo(t, job) if err != nil { return errors.Trace(err) } var fkInfo model.FKInfo err = job.DecodeArgs(&fkInfo) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } fkInfo.ID = allocateIndexID(tblInfo) tblInfo.ForeignKeys = append(tblInfo.ForeignKeys, &fkInfo) ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch fkInfo.State { case model.StateNone: // We just support record the foreign key, so we just make it public. // none -> public job.SchemaState = model.StatePublic fkInfo.State = model.StatePublic err = t.UpdateTable(schemaID, tblInfo) if err != nil { return errors.Trace(err) } // Finish this job. job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) return nil default: return ErrInvalidForeignKeyState.Gen("invalid fk state %v", fkInfo.State) } }
func (d *ddl) buildTableInfo(tableName model.CIStr, cols []*table.Column, constraints []*ast.Constraint) (tbInfo *model.TableInfo, err error) { tbInfo = &model.TableInfo{ Name: tableName, } tbInfo.ID, err = d.genGlobalID() if err != nil { return nil, errors.Trace(err) } for _, v := range cols { v.ID = allocateColumnID(tbInfo) tbInfo.Columns = append(tbInfo.Columns, v.ToInfo()) } for _, constr := range constraints { if constr.Tp == ast.ConstraintForeignKey { for _, fk := range tbInfo.ForeignKeys { if fk.Name.L == strings.ToLower(constr.Name) { return nil, infoschema.ErrCannotAddForeign } } var fk model.FKInfo fk.Name = model.NewCIStr(constr.Name) fk.RefTable = constr.Refer.Table.Name fk.State = model.StatePublic for _, key := range constr.Keys { fk.Cols = append(fk.Cols, key.Column.Name) } for _, key := range constr.Refer.IndexColNames { fk.RefCols = append(fk.RefCols, key.Column.Name) } fk.OnDelete = int(constr.Refer.OnDelete.ReferOpt) fk.OnUpdate = int(constr.Refer.OnUpdate.ReferOpt) if len(fk.Cols) != len(fk.RefCols) { return nil, infoschema.ErrForeignKeyNotMatch } if len(fk.Cols) == 0 { // TODO: In MySQL, this case will report a parse error. return nil, infoschema.ErrCannotAddForeign } tbInfo.ForeignKeys = append(tbInfo.ForeignKeys, &fk) continue } if constr.Tp == ast.ConstraintPrimaryKey { if len(constr.Keys) == 1 { key := constr.Keys[0] col := table.FindCol(cols, key.Column.Name.O) if col == nil { return nil, errKeyColumnDoesNotExits.Gen("key column %s doesn't exist in table", key.Column.Name) } switch col.Tp { case mysql.TypeLong, mysql.TypeLonglong, mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24: tbInfo.PKIsHandle = true // Avoid creating index for PK handle column. continue } } } // 1. check if the column is exists // 2. add index indexColumns := make([]*model.IndexColumn, 0, len(constr.Keys)) for _, key := range constr.Keys { col := table.FindCol(cols, key.Column.Name.O) if col == nil { return nil, errKeyColumnDoesNotExits.Gen("key column %s doesn't exist in table", key.Column.Name) } indexColumns = append(indexColumns, &model.IndexColumn{ Name: key.Column.Name, Offset: col.Offset, Length: key.Length, }) } idxInfo := &model.IndexInfo{ Name: model.NewCIStr(constr.Name), Columns: indexColumns, State: model.StatePublic, } switch constr.Tp { case ast.ConstraintPrimaryKey: idxInfo.Unique = true idxInfo.Primary = true idxInfo.Name = model.NewCIStr(table.PrimaryKeyName) case ast.ConstraintUniq, ast.ConstraintUniqKey, ast.ConstraintUniqIndex: idxInfo.Unique = true } if constr.Option != nil { idxInfo.Comment = constr.Option.Comment idxInfo.Tp = constr.Option.Tp } else { // Use btree as default index type. idxInfo.Tp = model.IndexTypeBtree } idxInfo.ID = allocateIndexID(tbInfo) tbInfo.Indices = append(tbInfo.Indices, idxInfo) } return }