// DropColumn will drop a column from the table, now we don't support drop the column with index covered. func (d *ddl) DropColumn(ctx context.Context, ti ast.Ident, colName model.CIStr) error { is := d.infoHandle.Get() schema, ok := is.SchemaByName(ti.Schema) if !ok { return errors.Trace(infoschema.ErrDatabaseNotExists) } t, err := is.TableByName(ti.Schema, ti.Name) if err != nil { return errors.Trace(infoschema.ErrTableNotExists) } // Check whether dropped column has existed. col := table.FindCol(t.Cols(), colName.L) if col == nil { return infoschema.ErrColumnNotExists.Gen("column %s doesn’t exist", colName.L) } job := &model.Job{ SchemaID: schema.ID, TableID: t.Meta().ID, Type: model.ActionDropColumn, Args: []interface{}{colName}, } err = d.doDDLJob(ctx, job) err = d.hook.OnChanged(err) return errors.Trace(err) }
// Check if column scope privilege entry exists in mysql.Columns_priv. // If unexists, insert a new one. func (e *GrantExec) checkAndInitColumnPriv(user string, host string, cols []*ast.ColumnName) error { db, tbl, err := e.getTargetSchemaAndTable() if err != nil { return errors.Trace(err) } for _, c := range cols { col := table.FindCol(tbl.Cols(), c.Name.L) if col == nil { return errors.Errorf("Unknown column: %s", c.Name.O) } ok, err := columnPrivEntryExists(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) if err != nil { return errors.Trace(err) } if ok { continue } // Entry does not exist for user-host-db-tbl-col. Insert a new entry. err = initColumnPrivEntry(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) if err != nil { return errors.Trace(err) } } return nil }
func (s *testColumnSuite) testGetColumn(c *C, t table.Table, name string, isExist bool) { col := table.FindCol(t.Cols(), name) if isExist { c.Assert(col, NotNil) } else { c.Assert(col, IsNil) } }
func findColumnByName(t table.Table, tableName, colName string) (*table.Column, error) { if len(tableName) > 0 && tableName != t.Meta().Name.O { return nil, errors.Errorf("unknown field %s.%s", tableName, colName) } c := table.FindCol(t.Cols(), colName) if c == nil { return nil, errors.Errorf("unknown field %s", colName) } return c, nil }
func (s *testColumnSuite) testGetColumn(t table.Table, name string, isExist bool) error { col := table.FindCol(t.Cols(), name) if isExist { if col == nil { return errors.Errorf("column should not be nil") } } else { if col != nil { return errors.Errorf("column should be nil") } } return nil }
// AddColumn will add a new column to the table. func (d *ddl) AddColumn(ctx context.Context, ti ast.Ident, spec *ast.AlterTableSpec) error { // Check whether the added column constraints are supported. err := checkColumnConstraint(spec.NewColumn.Options) if err != nil { return errors.Trace(err) } is := d.infoHandle.Get() schema, ok := is.SchemaByName(ti.Schema) if !ok { return errors.Trace(infoschema.ErrDatabaseNotExists) } t, err := is.TableByName(ti.Schema, ti.Name) if err != nil { return errors.Trace(infoschema.ErrTableNotExists) } // Check whether added column has existed. colName := spec.NewColumn.Name.Name.O col := table.FindCol(t.Cols(), colName) if col != nil { return infoschema.ErrColumnExists.GenByArgs(colName) } if len(colName) > mysql.MaxColumnNameLength { return ErrTooLongIdent.Gen("too long column %s", colName) } // Ingore table constraints now, maybe return error later. // We use length(t.Cols()) as the default offset firstly, later we will change the // column's offset later. col, _, err = d.buildColumnAndConstraint(ctx, len(t.Cols()), spec.NewColumn) if err != nil { return errors.Trace(err) } job := &model.Job{ SchemaID: schema.ID, TableID: t.Meta().ID, Type: model.ActionAddColumn, BinlogInfo: &model.HistoryInfo{}, Args: []interface{}{col, spec.Position, 0}, } err = d.doDDLJob(ctx, job) err = d.callHookOnChanged(err) return errors.Trace(err) }
func (s *testTypeInferrerSuite) TestColumnInfoModified(c *C) { defer testleak.AfterTest(c)() store, err := tidb.NewStore(tidb.EngineGoLevelDBMemory) c.Assert(err, IsNil) defer store.Close() testKit := testkit.NewTestKit(c, store) testKit.MustExec("use test") testKit.MustExec("drop table if exists tab0") testKit.MustExec("CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)") testKit.MustExec("SELECT + - (- CASE + col0 WHEN + CAST( col0 AS SIGNED ) THEN col1 WHEN 79 THEN NULL WHEN + - col1 THEN col0 / + col0 END ) * - 16 FROM tab0") ctx := testKit.Se.(context.Context) is := sessionctx.GetDomain(ctx).InfoSchema() tbl, _ := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tab0")) col := table.FindCol(tbl.Cols(), "col1") c.Assert(col.Tp, Equals, mysql.TypeLong) }
// AddColumn will add a new column to the table. func (d *ddl) AddColumn(ctx context.Context, ti ast.Ident, spec *ast.AlterTableSpec) error { // Check whether the added column constraints are supported. err := checkColumnConstraint(spec.Column.Options) if err != nil { return errors.Trace(err) } is := d.infoHandle.Get() schema, ok := is.SchemaByName(ti.Schema) if !ok { return errors.Trace(infoschema.ErrDatabaseNotExists) } t, err := is.TableByName(ti.Schema, ti.Name) if err != nil { return errors.Trace(infoschema.ErrTableNotExists) } // Check whether added column has existed. colName := spec.Column.Name.Name.O col := table.FindCol(t.Cols(), colName) if col != nil { return infoschema.ErrColumnExists.Gen("column %s already exists", colName) } // ingore table constraints now, maybe return error later // we use length(t.Cols()) as the default offset first, later we will change the // column's offset later. col, _, err = d.buildColumnAndConstraint(ctx, len(t.Cols()), spec.Column) if err != nil { return errors.Trace(err) } job := &model.Job{ SchemaID: schema.ID, TableID: t.Meta().ID, Type: model.ActionAddColumn, Args: []interface{}{&col.ColumnInfo, spec.Position, 0}, } err = d.doDDLJob(ctx, job) err = d.hook.OnChanged(err) return errors.Trace(err) }
// DropColumn will drop a column from the table, now we don't support drop the column with index covered. func (d *ddl) DropColumn(ctx context.Context, ti ast.Ident, colName model.CIStr) error { is := d.infoHandle.Get() schema, ok := is.SchemaByName(ti.Schema) if !ok { return errors.Trace(infoschema.ErrDatabaseNotExists) } t, err := is.TableByName(ti.Schema, ti.Name) if err != nil { return errors.Trace(infoschema.ErrTableNotExists) } // Check whether dropped column has existed. col := table.FindCol(t.Cols(), colName.L) if col == nil { return ErrCantDropFieldOrKey.Gen("column %s doesn't exist", colName) } tblInfo := t.Meta() // We don't support dropping column with index covered now. // We must drop the index first, then drop the column. if isColumnWithIndex(colName.L, tblInfo.Indices) { return errCantDropColWithIndex.Gen("can't drop column %s with index covered now", colName) } // We don't support dropping column with PK handle covered now. if col.IsPKHandleColumn(tblInfo) { return errUnsupportedPKHandle } job := &model.Job{ SchemaID: schema.ID, TableID: t.Meta().ID, Type: model.ActionDropColumn, BinlogInfo: &model.HistoryInfo{}, Args: []interface{}{colName}, } err = d.doDDLJob(ctx, job) err = d.callHookOnChanged(err) return errors.Trace(err) }
func (d *ddl) getModifiableColumnJob(ctx context.Context, ident ast.Ident, originalColName model.CIStr, spec *ast.AlterTableSpec) (*model.Job, error) { is := d.infoHandle.Get() schema, ok := is.SchemaByName(ident.Schema) if !ok { return nil, errors.Trace(infoschema.ErrDatabaseNotExists) } t, err := is.TableByName(ident.Schema, ident.Name) if err != nil { return nil, errors.Trace(infoschema.ErrTableNotExists) } col := table.FindCol(t.Cols(), originalColName.L) if col == nil { return nil, infoschema.ErrColumnNotExists.GenByArgs(originalColName, ident.Name) } if spec.Constraint != nil || (spec.Position != nil && spec.Position.Tp != ast.ColumnPositionNone) || len(spec.NewColumn.Options) != 0 || spec.NewColumn.Tp == nil { // Make sure the column definition is simple field type. return nil, errUnsupportedModifyColumn } d.setCharsetCollationFlenDecimal(spec.NewColumn.Tp) if !d.modifiable(&col.FieldType, spec.NewColumn.Tp) { return nil, errUnsupportedModifyColumn } newCol := *col newCol.FieldType = *spec.NewColumn.Tp newCol.Name = spec.NewColumn.Name.Name job := &model.Job{ SchemaID: schema.ID, TableID: t.Meta().ID, Type: model.ActionModifyColumn, BinlogInfo: &model.HistoryInfo{}, Args: []interface{}{&newCol, originalColName}, } return job, nil }
// Manipulate mysql.tables_priv table. func (e *GrantExec) grantColumnPriv(priv *ast.PrivElem, user *ast.UserSpec) error { db, tbl, err := e.getTargetSchemaAndTable() if err != nil { return errors.Trace(err) } userName, host := parseUser(user.User) for _, c := range priv.Cols { col := table.FindCol(tbl.Cols(), c.Name.L) if col == nil { return errors.Errorf("Unknown column: %s", c) } asgns, err := composeColumnPrivUpdate(e.ctx, priv.Priv, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) if err != nil { return errors.Trace(err) } sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="******" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, asgns, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) _, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) if err != nil { return errors.Trace(err) } } return nil }
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 }
func (s *testColumnSuite) TestDropColumn(c *C) { defer testleak.AfterTest(c)() d := newDDL(s.store, nil, nil, testLease) tblInfo := testTableInfo(c, d, "t", 4) ctx := testNewContext(c, d) _, err := ctx.GetTxn(true) c.Assert(err, IsNil) testCreateTable(c, ctx, d, s.dbInfo, tblInfo) t := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID) colName := "c4" defaultColValue := int64(4) row := types.MakeDatums(int64(1), int64(2), int64(3)) _, err = t.AddRecord(ctx, append(row, types.NewDatum(defaultColValue))) c.Assert(err, IsNil) err = ctx.CommitTxn() c.Assert(err, IsNil) checkOK := false var mu sync.Mutex oldCol := &table.Column{} tc := &testDDLCallback{} tc.onJobUpdated = func(job *model.Job) { if checkOK { return } t := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID).(*tables.Table) col := table.FindCol(t.Columns, colName) if col == nil { mu.Lock() checkOK = true mu.Unlock() return } oldCol = col } d.hook = tc // Use local ddl for callback test. s.d.close() d.close() d.start() job := testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, colName, false) testCheckJobDone(c, d, job, false) mu.Lock() c.Assert(checkOK, IsTrue) mu.Unlock() _, err = ctx.GetTxn(true) c.Assert(err, IsNil) job = testDropTable(c, ctx, d, s.dbInfo, tblInfo) testCheckJobDone(c, d, job, false) err = ctx.CommitTxn() c.Assert(err, IsNil) d.close() s.d.start() }
func (s *testColumnSuite) TestAddColumn(c *C) { defer testleak.AfterTest(c)() d := newDDL(s.store, nil, nil, testLease) tblInfo := testTableInfo(c, d, "t", 3) ctx := testNewContext(c, d) _, err := ctx.GetTxn(true) c.Assert(err, IsNil) testCreateTable(c, ctx, d, s.dbInfo, tblInfo) t := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID) oldRow := types.MakeDatums(int64(1), int64(2), int64(3)) handle, err := t.AddRecord(ctx, oldRow) c.Assert(err, IsNil) err = ctx.CommitTxn() c.Assert(err, IsNil) newColName := "c4" defaultColValue := int64(4) checkOK := false tc := &testDDLCallback{} var checkErr error tc.onJobUpdated = func(job *model.Job) { if checkOK { return } t := testGetTable(c, d, s.dbInfo.ID, tblInfo.ID).(*tables.Table) newCol := table.FindCol(t.Columns, newColName) if newCol == nil { return } err1 := s.checkAddColumn(c, newCol.State, d, tblInfo, handle, newCol, oldRow, defaultColValue) if err1 != nil { checkErr = errors.Trace(err1) } if newCol.State == model.StatePublic { checkOK = true } } d.hook = tc // Use local ddl for callback test. s.d.close() d.close() d.start() job := testCreateColumn(c, ctx, d, s.dbInfo, tblInfo, newColName, &ast.ColumnPosition{Tp: ast.ColumnPositionNone}, defaultColValue) c.Assert(errors.ErrorStack(checkErr), Equals, "") testCheckJobDone(c, d, job, true) _, err = ctx.GetTxn(true) c.Assert(err, IsNil) job = testDropTable(c, ctx, d, s.dbInfo, tblInfo) testCheckJobDone(c, d, job, false) err = ctx.CommitTxn() c.Assert(err, IsNil) d.close() s.d.start() }
func (s *testColumnSuite) TestColumn(c *C) { defer testleak.AfterTest(c)() tblInfo := testTableInfo(c, s.d, "t1", 3) var ctx context.Context ctx = testNewContext(c, s.d) defer ctx.RollbackTxn() testCreateTable(c, ctx, s.d, s.dbInfo, tblInfo) t := testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) num := 10 for i := 0; i < num; i++ { _, err := t.AddRecord(ctx, types.MakeDatums(i, 10*i, 100*i)) c.Assert(err, IsNil) } err := ctx.CommitTxn() c.Assert(err, IsNil) i := int64(0) t.IterRecords(ctx, t.FirstKey(), t.Cols(), func(h int64, data []types.Datum, cols []*table.Column) (bool, error) { c.Assert(data, HasLen, 3) c.Assert(data[0].GetInt64(), Equals, i) c.Assert(data[1].GetInt64(), Equals, 10*i) c.Assert(data[2].GetInt64(), Equals, 100*i) i++ return true, nil }) c.Assert(i, Equals, int64(num)) c.Assert(table.FindCol(t.Cols(), "c4"), IsNil) job := testCreateColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c4", &ast.ColumnPosition{Tp: ast.ColumnPositionAfter, RelativeColumn: &ast.ColumnName{Name: model.NewCIStr("c3")}}, 100) testCheckJobDone(c, s.d, job, true) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) c.Assert(table.FindCol(t.Cols(), "c4"), NotNil) i = int64(0) t.IterRecords(ctx, t.FirstKey(), t.Cols(), func(h int64, data []types.Datum, cols []*table.Column) (bool, error) { c.Assert(data, HasLen, 4) c.Assert(data[0].GetInt64(), Equals, i) c.Assert(data[1].GetInt64(), Equals, 10*i) c.Assert(data[2].GetInt64(), Equals, 100*i) c.Assert(data[3].GetInt64(), Equals, int64(100)) i++ return true, nil }) c.Assert(i, Equals, int64(num)) h, err := t.AddRecord(ctx, types.MakeDatums(11, 12, 13, 14)) c.Assert(err, IsNil) err = ctx.CommitTxn() c.Assert(err, IsNil) values, err := t.RowWithCols(ctx, h, t.Cols()) c.Assert(err, IsNil) c.Assert(values, HasLen, 4) c.Assert(values[3].GetInt64(), Equals, int64(14)) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c4", false) testCheckJobDone(c, s.d, job, false) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) values, err = t.RowWithCols(ctx, h, t.Cols()) c.Assert(err, IsNil) c.Assert(values, HasLen, 3) c.Assert(values[2].GetInt64(), Equals, int64(13)) job = testCreateColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c4", &ast.ColumnPosition{Tp: ast.ColumnPositionNone}, 111) testCheckJobDone(c, s.d, job, true) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) values, err = t.RowWithCols(ctx, h, t.Cols()) c.Assert(err, IsNil) c.Assert(values, HasLen, 4) c.Assert(values[3].GetInt64(), Equals, int64(111)) job = testCreateColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c5", &ast.ColumnPosition{Tp: ast.ColumnPositionNone}, 101) testCheckJobDone(c, s.d, job, true) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) values, err = t.RowWithCols(ctx, h, t.Cols()) c.Assert(err, IsNil) c.Assert(values, HasLen, 5) c.Assert(values[4].GetInt64(), Equals, int64(101)) job = testCreateColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c6", &ast.ColumnPosition{Tp: ast.ColumnPositionFirst}, 202) testCheckJobDone(c, s.d, job, true) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) cols := t.Cols() c.Assert(cols, HasLen, 6) c.Assert(cols[0].Offset, Equals, 0) c.Assert(cols[0].Name.L, Equals, "c6") c.Assert(cols[1].Offset, Equals, 1) c.Assert(cols[1].Name.L, Equals, "c1") c.Assert(cols[2].Offset, Equals, 2) c.Assert(cols[2].Name.L, Equals, "c2") c.Assert(cols[3].Offset, Equals, 3) c.Assert(cols[3].Name.L, Equals, "c3") c.Assert(cols[4].Offset, Equals, 4) c.Assert(cols[4].Name.L, Equals, "c4") c.Assert(cols[5].Offset, Equals, 5) c.Assert(cols[5].Name.L, Equals, "c5") values, err = t.RowWithCols(ctx, h, cols) c.Assert(err, IsNil) c.Assert(values, HasLen, 6) c.Assert(values[0].GetInt64(), Equals, int64(202)) c.Assert(values[5].GetInt64(), Equals, int64(101)) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c2", false) testCheckJobDone(c, s.d, job, false) t = testGetTable(c, s.d, s.dbInfo.ID, tblInfo.ID) values, err = t.RowWithCols(ctx, h, t.Cols()) c.Assert(err, IsNil) c.Assert(values, HasLen, 5) c.Assert(values[0].GetInt64(), Equals, int64(202)) c.Assert(values[4].GetInt64(), Equals, int64(101)) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c1", false) testCheckJobDone(c, s.d, job, false) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c3", false) testCheckJobDone(c, s.d, job, false) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c4", false) testCheckJobDone(c, s.d, job, false) job = testCreateIndex(c, ctx, s.d, s.dbInfo, tblInfo, false, "c5_idx", "c5") testCheckJobDone(c, s.d, job, true) testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c5", true) testDropIndex(c, ctx, s.d, s.dbInfo, tblInfo, "c5_idx") testCheckJobDone(c, s.d, job, true) job = testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c5", false) testCheckJobDone(c, s.d, job, false) testDropColumn(c, ctx, s.d, s.dbInfo, tblInfo, "c6", true) testDropTable(c, ctx, s.d, s.dbInfo, tblInfo) }