90 Star 491 Fork 151

平凯星辰(北京)科技有限公司/tidb

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
ddl.go 19.66 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
"github.com/pingcap/tidb/model"
"github.com/pingcap/tidb/types"
)
var (
_ DDLNode = &AlterTableStmt{}
_ DDLNode = &CreateDatabaseStmt{}
_ DDLNode = &CreateIndexStmt{}
_ DDLNode = &CreateTableStmt{}
_ DDLNode = &CreateViewStmt{}
_ DDLNode = &DropDatabaseStmt{}
_ DDLNode = &DropIndexStmt{}
_ DDLNode = &DropTableStmt{}
_ DDLNode = &RenameTableStmt{}
_ DDLNode = &TruncateTableStmt{}
_ Node = &AlterTableSpec{}
_ Node = &ColumnDef{}
_ Node = &ColumnOption{}
_ Node = &ColumnPosition{}
_ Node = &Constraint{}
_ Node = &IndexColName{}
_ Node = &ReferenceDef{}
)
// CharsetOpt is used for parsing charset option from SQL.
type CharsetOpt struct {
Chs string
Col string
}
// DatabaseOptionType is the type for database options.
type DatabaseOptionType int
// Database option types.
const (
DatabaseOptionNone DatabaseOptionType = iota
DatabaseOptionCharset
DatabaseOptionCollate
)
// DatabaseOption represents database option.
type DatabaseOption struct {
Tp DatabaseOptionType
Value string
}
// CreateDatabaseStmt is a statement to create a database.
// See https://dev.mysql.com/doc/refman/5.7/en/create-database.html
type CreateDatabaseStmt struct {
ddlNode
IfNotExists bool
Name string
Options []*DatabaseOption
}
// Accept implements Node Accept interface.
func (n *CreateDatabaseStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*CreateDatabaseStmt)
return v.Leave(n)
}
// DropDatabaseStmt is a statement to drop a database and all tables in the database.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-database.html
type DropDatabaseStmt struct {
ddlNode
IfExists bool
Name string
}
// Accept implements Node Accept interface.
func (n *DropDatabaseStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropDatabaseStmt)
return v.Leave(n)
}
// IndexColName is used for parsing index column name from SQL.
type IndexColName struct {
node
Column *ColumnName
Length int
}
// Accept implements Node Accept interface.
func (n *IndexColName) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*IndexColName)
node, ok := n.Column.Accept(v)
if !ok {
return n, false
}
n.Column = node.(*ColumnName)
return v.Leave(n)
}
// ReferenceDef is used for parsing foreign key reference option from SQL.
// See http://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html
type ReferenceDef struct {
node
Table *TableName
IndexColNames []*IndexColName
OnDelete *OnDeleteOpt
OnUpdate *OnUpdateOpt
}
// Accept implements Node Accept interface.
func (n *ReferenceDef) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ReferenceDef)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
for i, val := range n.IndexColNames {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.IndexColNames[i] = node.(*IndexColName)
}
onDelete, ok := n.OnDelete.Accept(v)
if !ok {
return n, false
}
n.OnDelete = onDelete.(*OnDeleteOpt)
onUpdate, ok := n.OnUpdate.Accept(v)
if !ok {
return n, false
}
n.OnUpdate = onUpdate.(*OnUpdateOpt)
return v.Leave(n)
}
// ReferOptionType is the type for refer options.
type ReferOptionType int
// Refer option types.
const (
ReferOptionNoOption ReferOptionType = iota
ReferOptionRestrict
ReferOptionCascade
ReferOptionSetNull
ReferOptionNoAction
)
// String implements fmt.Stringer interface.
func (r ReferOptionType) String() string {
switch r {
case ReferOptionRestrict:
return "RESTRICT"
case ReferOptionCascade:
return "CASCADE"
case ReferOptionSetNull:
return "SET NULL"
case ReferOptionNoAction:
return "NO ACTION"
}
return ""
}
// OnDeleteOpt is used for optional on delete clause.
type OnDeleteOpt struct {
node
ReferOpt ReferOptionType
}
// Accept implements Node Accept interface.
func (n *OnDeleteOpt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*OnDeleteOpt)
return v.Leave(n)
}
// OnUpdateOpt is used for optional on update clause.
type OnUpdateOpt struct {
node
ReferOpt ReferOptionType
}
// Accept implements Node Accept interface.
func (n *OnUpdateOpt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*OnUpdateOpt)
return v.Leave(n)
}
// ColumnOptionType is the type for ColumnOption.
type ColumnOptionType int
// ColumnOption types.
const (
ColumnOptionNoOption ColumnOptionType = iota
ColumnOptionPrimaryKey
ColumnOptionNotNull
ColumnOptionAutoIncrement
ColumnOptionDefaultValue
ColumnOptionUniqKey
ColumnOptionNull
ColumnOptionOnUpdate // For Timestamp and Datetime only.
ColumnOptionFulltext
ColumnOptionComment
ColumnOptionGenerated
ColumnOptionReference
)
// ColumnOption is used for parsing column constraint info from SQL.
type ColumnOption struct {
node
Tp ColumnOptionType
// Expr is used for ColumnOptionDefaultValue/ColumnOptionOnUpdateColumnOptionGenerated.
// For ColumnOptionDefaultValue or ColumnOptionOnUpdate, it's the target value.
// For ColumnOptionGenerated, it's the target expression.
Expr ExprNode
// Stored is only for ColumnOptionGenerated, default is false.
Stored bool
// Refer is used for foreign key.
Refer *ReferenceDef
}
// Accept implements Node Accept interface.
func (n *ColumnOption) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ColumnOption)
if n.Expr != nil {
node, ok := n.Expr.Accept(v)
if !ok {
return n, false
}
n.Expr = node.(ExprNode)
}
return v.Leave(n)
}
// IndexOption is the index options.
// KEY_BLOCK_SIZE [=] value
// | index_type
// | WITH PARSER parser_name
// | COMMENT 'string'
// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html
type IndexOption struct {
node
KeyBlockSize uint64
Tp model.IndexType
Comment string
}
// Accept implements Node Accept interface.
func (n *IndexOption) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*IndexOption)
return v.Leave(n)
}
// ConstraintType is the type for Constraint.
type ConstraintType int
// ConstraintTypes
const (
ConstraintNoConstraint ConstraintType = iota
ConstraintPrimaryKey
ConstraintKey
ConstraintIndex
ConstraintUniq
ConstraintUniqKey
ConstraintUniqIndex
ConstraintForeignKey
ConstraintFulltext
)
// Constraint is constraint for table definition.
type Constraint struct {
node
Tp ConstraintType
Name string
Keys []*IndexColName // Used for PRIMARY KEY, UNIQUE, ......
Refer *ReferenceDef // Used for foreign key.
Option *IndexOption // Index Options
}
// Accept implements Node Accept interface.
func (n *Constraint) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*Constraint)
for i, val := range n.Keys {
node, ok := val.Accept(v)
if !ok {
return n, false
}
n.Keys[i] = node.(*IndexColName)
}
if n.Refer != nil {
node, ok := n.Refer.Accept(v)
if !ok {
return n, false
}
n.Refer = node.(*ReferenceDef)
}
if n.Option != nil {
node, ok := n.Option.Accept(v)
if !ok {
return n, false
}
n.Option = node.(*IndexOption)
}
return v.Leave(n)
}
// ColumnDef is used for parsing column definition from SQL.
type ColumnDef struct {
node
Name *ColumnName
Tp *types.FieldType
Options []*ColumnOption
}
// Accept implements Node Accept interface.
func (n *ColumnDef) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ColumnDef)
node, ok := n.Name.Accept(v)
if !ok {
return n, false
}
n.Name = node.(*ColumnName)
for i, val := range n.Options {
node, ok := val.Accept(v)
if !ok {
return n, false
}
n.Options[i] = node.(*ColumnOption)
}
return v.Leave(n)
}
// CreateTableStmt is a statement to create a table.
// See https://dev.mysql.com/doc/refman/5.7/en/create-table.html
type CreateTableStmt struct {
ddlNode
IfNotExists bool
Table *TableName
ReferTable *TableName
Cols []*ColumnDef
Constraints []*Constraint
Options []*TableOption
Partition *PartitionOptions
OnDuplicate OnDuplicateCreateTableSelectType
Select ResultSetNode
}
// Accept implements Node Accept interface.
func (n *CreateTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*CreateTableStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
if n.ReferTable != nil {
node, ok = n.ReferTable.Accept(v)
if !ok {
return n, false
}
n.ReferTable = node.(*TableName)
}
for i, val := range n.Cols {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.Cols[i] = node.(*ColumnDef)
}
for i, val := range n.Constraints {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.Constraints[i] = node.(*Constraint)
}
if n.Select != nil {
node, ok := n.Select.Accept(v)
if !ok {
return n, false
}
n.Select = node.(ResultSetNode)
}
return v.Leave(n)
}
// DropTableStmt is a statement to drop one or more tables.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-table.html
type DropTableStmt struct {
ddlNode
IfExists bool
Tables []*TableName
}
// Accept implements Node Accept interface.
func (n *DropTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropTableStmt)
for i, val := range n.Tables {
node, ok := val.Accept(v)
if !ok {
return n, false
}
n.Tables[i] = node.(*TableName)
}
return v.Leave(n)
}
// RenameTableStmt is a statement to rename a table.
// See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html
type RenameTableStmt struct {
ddlNode
OldTable *TableName
NewTable *TableName
// TableToTables is only useful for syncer which depends heavily on tidb parser to do some dirty work for now.
// TODO: Refactor this when you are going to add full support for multiple schema changes.
TableToTables []*TableToTable
}
// Accept implements Node Accept interface.
func (n *RenameTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*RenameTableStmt)
node, ok := n.OldTable.Accept(v)
if !ok {
return n, false
}
n.OldTable = node.(*TableName)
node, ok = n.NewTable.Accept(v)
if !ok {
return n, false
}
n.NewTable = node.(*TableName)
for i, t := range n.TableToTables {
node, ok := t.Accept(v)
if !ok {
return n, false
}
n.TableToTables[i] = node.(*TableToTable)
}
return v.Leave(n)
}
// TableToTable represents renaming old table to new table used in RenameTableStmt.
type TableToTable struct {
node
OldTable *TableName
NewTable *TableName
}
// Accept implements Node Accept interface.
func (n *TableToTable) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*TableToTable)
node, ok := n.OldTable.Accept(v)
if !ok {
return n, false
}
n.OldTable = node.(*TableName)
node, ok = n.NewTable.Accept(v)
if !ok {
return n, false
}
n.NewTable = node.(*TableName)
return v.Leave(n)
}
// CreateViewStmt is a statement to create a View.
// See https://dev.mysql.com/doc/refman/5.7/en/create-view.html
type CreateViewStmt struct {
ddlNode
OrReplace bool
ViewName *TableName
Cols []model.CIStr
Select StmtNode
}
// Accept implements Node Accept interface.
func (n *CreateViewStmt) Accept(v Visitor) (Node, bool) {
// TODO: implement the details.
return n, true
}
// CreateIndexStmt is a statement to create an index.
// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html
type CreateIndexStmt struct {
ddlNode
IndexName string
Table *TableName
Unique bool
IndexColNames []*IndexColName
IndexOption *IndexOption
}
// Accept implements Node Accept interface.
func (n *CreateIndexStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*CreateIndexStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
for i, val := range n.IndexColNames {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.IndexColNames[i] = node.(*IndexColName)
}
if n.IndexOption != nil {
node, ok := n.IndexOption.Accept(v)
if !ok {
return n, false
}
n.IndexOption = node.(*IndexOption)
}
return v.Leave(n)
}
// DropIndexStmt is a statement to drop the index.
// See https://dev.mysql.com/doc/refman/5.7/en/drop-index.html
type DropIndexStmt struct {
ddlNode
IfExists bool
IndexName string
Table *TableName
}
// Accept implements Node Accept interface.
func (n *DropIndexStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropIndexStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
return v.Leave(n)
}
// TableOptionType is the type for TableOption
type TableOptionType int
// TableOption types.
const (
TableOptionNone TableOptionType = iota
TableOptionEngine
TableOptionCharset
TableOptionCollate
TableOptionAutoIncrement
TableOptionComment
TableOptionAvgRowLength
TableOptionCheckSum
TableOptionCompression
TableOptionConnection
TableOptionPassword
TableOptionKeyBlockSize
TableOptionMaxRows
TableOptionMinRows
TableOptionDelayKeyWrite
TableOptionRowFormat
TableOptionStatsPersistent
TableOptionShardRowID
TableOptionPackKeys
)
// RowFormat types
const (
RowFormatDefault uint64 = iota + 1
RowFormatDynamic
RowFormatFixed
RowFormatCompressed
RowFormatRedundant
RowFormatCompact
)
// OnDuplicateCreateTableSelectType is the option that handle unique key values in 'CREATE TABLE ... SELECT'.
// See https://dev.mysql.com/doc/refman/5.7/en/create-table-select.html
type OnDuplicateCreateTableSelectType int
// OnDuplicateCreateTableSelect types
const (
OnDuplicateCreateTableSelectError OnDuplicateCreateTableSelectType = iota
OnDuplicateCreateTableSelectIgnore
OnDuplicateCreateTableSelectReplace
)
// TableOption is used for parsing table option from SQL.
type TableOption struct {
Tp TableOptionType
StrValue string
UintValue uint64
}
// ColumnPositionType is the type for ColumnPosition.
type ColumnPositionType int
// ColumnPosition Types
const (
ColumnPositionNone ColumnPositionType = iota
ColumnPositionFirst
ColumnPositionAfter
)
// ColumnPosition represent the position of the newly added column
type ColumnPosition struct {
node
// Tp is either ColumnPositionNone, ColumnPositionFirst or ColumnPositionAfter.
Tp ColumnPositionType
// RelativeColumn is the column the newly added column after if type is ColumnPositionAfter
RelativeColumn *ColumnName
}
// Accept implements Node Accept interface.
func (n *ColumnPosition) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*ColumnPosition)
if n.RelativeColumn != nil {
node, ok := n.RelativeColumn.Accept(v)
if !ok {
return n, false
}
n.RelativeColumn = node.(*ColumnName)
}
return v.Leave(n)
}
// AlterTableType is the type for AlterTableSpec.
type AlterTableType int
// AlterTable types.
const (
AlterTableOption AlterTableType = iota + 1
AlterTableAddColumns
AlterTableAddConstraint
AlterTableDropColumn
AlterTableDropPrimaryKey
AlterTableDropIndex
AlterTableDropForeignKey
AlterTableModifyColumn
AlterTableChangeColumn
AlterTableRenameTable
AlterTableAlterColumn
AlterTableLock
AlterTableAlgorithm
AlterTableRenameIndex
AlterTableForce
AlterTableAddPartitions
AlterTableDropPartition
// TODO: Add more actions
)
// LockType is the type for AlterTableSpec.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html#alter-table-concurrency
type LockType byte
// Lock Types.
const (
LockTypeNone LockType = iota + 1
LockTypeDefault
LockTypeShared
LockTypeExclusive
)
// AlterTableSpec represents alter table specification.
type AlterTableSpec struct {
node
Tp AlterTableType
Name string
Constraint *Constraint
Options []*TableOption
NewTable *TableName
NewColumns []*ColumnDef
OldColumnName *ColumnName
Position *ColumnPosition
LockType LockType
Comment string
FromKey model.CIStr
ToKey model.CIStr
PartDefinitions []*PartitionDefinition
}
// Accept implements Node Accept interface.
func (n *AlterTableSpec) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*AlterTableSpec)
if n.Constraint != nil {
node, ok := n.Constraint.Accept(v)
if !ok {
return n, false
}
n.Constraint = node.(*Constraint)
}
if n.NewTable != nil {
node, ok := n.NewTable.Accept(v)
if !ok {
return n, false
}
n.NewTable = node.(*TableName)
}
for _, col := range n.NewColumns {
node, ok := col.Accept(v)
if !ok {
return n, false
}
col = node.(*ColumnDef)
}
if n.OldColumnName != nil {
node, ok := n.OldColumnName.Accept(v)
if !ok {
return n, false
}
n.OldColumnName = node.(*ColumnName)
}
if n.Position != nil {
node, ok := n.Position.Accept(v)
if !ok {
return n, false
}
n.Position = node.(*ColumnPosition)
}
return v.Leave(n)
}
// AlterTableStmt is a statement to change the structure of a table.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html
type AlterTableStmt struct {
ddlNode
Table *TableName
Specs []*AlterTableSpec
}
// Accept implements Node Accept interface.
func (n *AlterTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*AlterTableStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
for i, val := range n.Specs {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.Specs[i] = node.(*AlterTableSpec)
}
return v.Leave(n)
}
// TruncateTableStmt is a statement to empty a table completely.
// See https://dev.mysql.com/doc/refman/5.7/en/truncate-table.html
type TruncateTableStmt struct {
ddlNode
Table *TableName
}
// Accept implements Node Accept interface.
func (n *TruncateTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*TruncateTableStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
return v.Leave(n)
}
// PartitionDefinition defines a single partition.
type PartitionDefinition struct {
Name model.CIStr
LessThan []ExprNode
MaxValue bool
Comment string
}
// PartitionOptions specifies the partition options.
type PartitionOptions struct {
Tp model.PartitionType
Expr ExprNode
ColumnNames []*ColumnName
Definitions []*PartitionDefinition
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/pingcap/tidb.git
git@gitee.com:pingcap/tidb.git
pingcap
tidb
tidb
v2.1.0-rc.3

搜索帮助

0d507c66 1850385 C8b1a773 1850385