github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/compile/util.go (about)

     1  // Copyright 2023 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package compile
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"time"
    22  
    23  	plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/catalog"
    26  	"github.com/matrixorigin/matrixone/pkg/container/types"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    30  )
    31  
    32  const (
    33  	INDEX_TYPE_PRIMARY  = "PRIMARY"
    34  	INDEX_TYPE_UNIQUE   = "UNIQUE"
    35  	INDEX_TYPE_MULTIPLE = "MULTIPLE"
    36  )
    37  
    38  const (
    39  	INDEX_VISIBLE_YES = 1
    40  	INDEX_VISIBLE_NO  = 0
    41  )
    42  
    43  const (
    44  	INDEX_HIDDEN_YES = 1
    45  	INDEX_HIDDEN_NO  = 0
    46  )
    47  
    48  const (
    49  	NULL_VALUE   = "null"
    50  	EMPTY_STRING = ""
    51  )
    52  
    53  const (
    54  	ALLOCID_INDEX_KEY = "index_key"
    55  )
    56  
    57  var (
    58  	// see the comment in fuzzyCheck func genCondition for the reason why has to be two SQLs
    59  	fuzzyNonCompoundCheck = "select %s from `%s`.`%s` where %s in (%s) group by %s having count(*) > 1 limit 1;"
    60  	fuzzyCompoundCheck    = "select serial(%s) from `%s`.`%s` where %s group by serial(%s) having count(*) > 1 limit 1;"
    61  )
    62  
    63  var (
    64  	insertIntoSingleIndexTableWithPKeyFormat    = "insert into  `%s`.`%s` select (%s), %s from `%s`.`%s` where (%s) is not null;"
    65  	insertIntoUniqueIndexTableWithPKeyFormat    = "insert into  `%s`.`%s` select serial(%s), %s from `%s`.`%s` where serial(%s) is not null;"
    66  	insertIntoSecondaryIndexTableWithPKeyFormat = "insert into  `%s`.`%s` select serial_full(%s), %s from `%s`.`%s`;"
    67  	insertIntoSingleIndexTableWithoutPKeyFormat = "insert into  `%s`.`%s` select (%s) from `%s`.`%s` where (%s) is not null;"
    68  	insertIntoIndexTableWithoutPKeyFormat       = "insert into  `%s`.`%s` select serial(%s) from `%s`.`%s` where serial(%s) is not null;"
    69  	insertIntoMasterIndexTableFormat            = "insert into  `%s`.`%s` select serial_full('%s', %s, %s), %s from `%s`.`%s`;"
    70  	createIndexTableForamt                      = "create table `%s`.`%s` (%s);"
    71  )
    72  
    73  var (
    74  	deleteMoIndexesWithDatabaseIdFormat          = `delete from mo_catalog.mo_indexes where database_id = %v;`
    75  	deleteMoIndexesWithTableIdFormat             = `delete from mo_catalog.mo_indexes where table_id = %v;`
    76  	deleteMoIndexesWithTableIdAndIndexNameFormat = `delete from mo_catalog.mo_indexes where table_id = %v and name = '%s';`
    77  	updateMoIndexesVisibleFormat                 = `update mo_catalog.mo_indexes set is_visible = %v where table_id = %v and name = '%s';`
    78  	updateMoIndexesTruncateTableFormat           = `update mo_catalog.mo_indexes set table_id = %v where table_id = %v`
    79  	updateMoIndexesAlgoParams                    = `update mo_catalog.mo_indexes set algo_params = '%s' where table_id = %v and name = '%s';`
    80  )
    81  
    82  var (
    83  	deleteMoTablePartitionsWithDatabaseIdFormat = `delete from mo_catalog.mo_table_partitions where database_id = %v;`
    84  	deleteMoTablePartitionsWithTableIdFormat    = `delete from mo_catalog.mo_table_partitions where table_id = %v;`
    85  	//deleteMoTablePartitionsWithTableIdAndIndexNameFormat = `delete from mo_catalog.mo_table_partitions where table_id = %v and name = '%s';`
    86  )
    87  
    88  // genCreateIndexTableSql: Generate ddl statements for creating index table
    89  func genCreateIndexTableSql(indexTableDef *plan.TableDef, indexDef *plan.IndexDef, DBName string) string {
    90  	var sql string
    91  	planCols := indexTableDef.GetCols()
    92  	for i, planCol := range planCols {
    93  		if i >= 1 {
    94  			sql += ","
    95  		}
    96  		sql += planCol.Name + " "
    97  		typeId := types.T(planCol.Typ.Id)
    98  		switch typeId {
    99  		case types.T_bit:
   100  			sql += fmt.Sprintf("BIT(%d)", planCol.Typ.Width)
   101  		case types.T_char:
   102  			sql += fmt.Sprintf("CHAR(%d)", planCol.Typ.Width)
   103  		case types.T_varchar:
   104  			sql += fmt.Sprintf("VARCHAR(%d)", planCol.Typ.Width)
   105  		case types.T_binary:
   106  			sql += fmt.Sprintf("BINARY(%d)", planCol.Typ.Width)
   107  		case types.T_varbinary:
   108  			sql += fmt.Sprintf("VARBINARY(%d)", planCol.Typ.Width)
   109  		case types.T_decimal64:
   110  			sql += fmt.Sprintf("DECIMAL(%d,%d)", planCol.Typ.Width, planCol.Typ.Scale)
   111  		case types.T_decimal128:
   112  			sql += fmt.Sprintf("DECIMAL(%d,%d)", planCol.Typ.Width, planCol.Typ.Scale)
   113  		default:
   114  			sql += typeId.String()
   115  		}
   116  		if i == 0 {
   117  			sql += " primary key"
   118  		}
   119  	}
   120  	return fmt.Sprintf(createIndexTableForamt, DBName, indexDef.IndexTableName, sql)
   121  }
   122  
   123  // genCreateIndexTableSqlForIvfIndex: Generate ddl statements for creating ivf index table
   124  // NOTE: Here the columns are part of meta, centroids, entries table.
   125  // meta      -> key varchar(65535), value varchar(65535)
   126  // centroids -> version int64, centroid_id int64, centroid vecf32(xx)
   127  // entries   -> version int64, entry_id int64, pk xx
   128  // TODO: later on merge with genCreateIndexTableSql
   129  func genCreateIndexTableSqlForIvfIndex(indexTableDef *plan.TableDef, indexDef *plan.IndexDef, DBName string) string {
   130  	var sql string
   131  	planCols := indexTableDef.GetCols()
   132  	for i, planCol := range planCols {
   133  		if planCol.Name == catalog.CPrimaryKeyColName {
   134  			continue
   135  		}
   136  		if i >= 1 {
   137  			sql += ","
   138  		}
   139  		sql += "`" + planCol.Name + "`" + " "
   140  		typeId := types.T(planCol.Typ.Id)
   141  		switch typeId {
   142  		case types.T_char:
   143  			sql += fmt.Sprintf("CHAR(%d)", planCol.Typ.Width)
   144  		case types.T_varchar:
   145  			sql += fmt.Sprintf("VARCHAR(%d)", planCol.Typ.Width)
   146  		case types.T_binary:
   147  			sql += fmt.Sprintf("BINARY(%d)", planCol.Typ.Width)
   148  		case types.T_varbinary:
   149  			sql += fmt.Sprintf("VARBINARY(%d)", planCol.Typ.Width)
   150  		case types.T_decimal64:
   151  			sql += fmt.Sprintf("DECIMAL(%d,%d)", planCol.Typ.Width, planCol.Typ.Scale)
   152  		case types.T_decimal128:
   153  			sql += fmt.Sprintf("DECIMAL(%d,%d)", planCol.Typ.Width, planCol.Typ.Scale)
   154  		case types.T_array_float32:
   155  			sql += fmt.Sprintf("VECF32(%d)", planCol.Typ.Width)
   156  		case types.T_array_float64:
   157  			sql += fmt.Sprintf("VECF64(%d)", planCol.Typ.Width)
   158  		default:
   159  			sql += typeId.String()
   160  		}
   161  
   162  	}
   163  
   164  	if indexTableDef.Pkey != nil && indexTableDef.Pkey.Names != nil {
   165  		pkStr := fmt.Sprintf(", primary key ( %s ) ", partsToColsStr(indexTableDef.Pkey.Names))
   166  		sql += pkStr
   167  	}
   168  
   169  	return fmt.Sprintf(createIndexTableForamt, DBName, indexDef.IndexTableName, sql)
   170  }
   171  
   172  // genInsertIndexTableSql: Generate an insert statement for inserting data into the index table
   173  func genInsertIndexTableSql(originTableDef *plan.TableDef, indexDef *plan.IndexDef, DBName string, isUnique bool) string {
   174  	// insert data into index table
   175  	var insertSQL string
   176  	temp := partsToColsStr(indexDef.Parts)
   177  	if len(originTableDef.Pkey.PkeyColName) == 0 {
   178  		if len(indexDef.Parts) == 1 {
   179  			insertSQL = fmt.Sprintf(insertIntoSingleIndexTableWithoutPKeyFormat, DBName, indexDef.IndexTableName, temp, DBName, originTableDef.Name, temp)
   180  		} else {
   181  			insertSQL = fmt.Sprintf(insertIntoIndexTableWithoutPKeyFormat, DBName, indexDef.IndexTableName, temp, DBName, originTableDef.Name, temp)
   182  		}
   183  	} else {
   184  		pkeyName := originTableDef.Pkey.PkeyColName
   185  		var pKeyMsg string
   186  		if pkeyName == catalog.CPrimaryKeyColName {
   187  			pKeyMsg = "serial("
   188  			for i, part := range originTableDef.Pkey.Names {
   189  				if i == 0 {
   190  					pKeyMsg += part
   191  				} else {
   192  					pKeyMsg += "," + part
   193  				}
   194  			}
   195  			pKeyMsg += ")"
   196  		} else {
   197  			pKeyMsg = pkeyName
   198  		}
   199  		if len(indexDef.Parts) == 1 {
   200  			insertSQL = fmt.Sprintf(insertIntoSingleIndexTableWithPKeyFormat, DBName, indexDef.IndexTableName, temp, pKeyMsg, DBName, originTableDef.Name, temp)
   201  		} else {
   202  			if isUnique {
   203  				insertSQL = fmt.Sprintf(insertIntoUniqueIndexTableWithPKeyFormat, DBName, indexDef.IndexTableName, temp, pKeyMsg, DBName, originTableDef.Name, temp)
   204  			} else {
   205  				insertSQL = fmt.Sprintf(insertIntoSecondaryIndexTableWithPKeyFormat, DBName, indexDef.IndexTableName, temp, pKeyMsg, DBName, originTableDef.Name)
   206  			}
   207  		}
   208  	}
   209  	return insertSQL
   210  }
   211  
   212  // genInsertIndexTableSqlForMasterIndex: Create inserts for master index table
   213  func genInsertIndexTableSqlForMasterIndex(originTableDef *plan.TableDef, indexDef *plan.IndexDef, DBName string) []string {
   214  	// insert data into index table
   215  	var insertSQLs = make([]string, len(indexDef.Parts))
   216  
   217  	pkeyName := originTableDef.Pkey.PkeyColName
   218  	var pKeyMsg string
   219  	if pkeyName == catalog.CPrimaryKeyColName {
   220  		pKeyMsg = "serial("
   221  		for i, part := range originTableDef.Pkey.Names {
   222  			if i == 0 {
   223  				pKeyMsg += part
   224  			} else {
   225  				pKeyMsg += "," + part
   226  			}
   227  		}
   228  		pKeyMsg += ")"
   229  	} else {
   230  		pKeyMsg = pkeyName
   231  	}
   232  
   233  	colSeqNumMap := make(map[string]string)
   234  	for _, col := range originTableDef.Cols {
   235  		// NOTE:
   236  		// ColDef.ColId is not used as "after alter table, different columns may have the same colId"
   237  		// ColDef.SeqNum is used instead as it is always unique.
   238  		colSeqNumMap[col.GetName()] = fmt.Sprintf("%d", col.GetSeqnum())
   239  	}
   240  
   241  	for i, part := range indexDef.Parts {
   242  		insertSQLs[i] = fmt.Sprintf(insertIntoMasterIndexTableFormat,
   243  			DBName, indexDef.IndexTableName,
   244  			colSeqNumMap[part], part, pKeyMsg, pKeyMsg, DBName, originTableDef.Name)
   245  	}
   246  
   247  	return insertSQLs
   248  }
   249  
   250  // genInsertMOIndexesSql: Generate an insert statement for insert index metadata into `mo_catalog.mo_indexes`
   251  func genInsertMOIndexesSql(eg engine.Engine, proc *process.Process, databaseId string, tableId uint64, ct *engine.ConstraintDef) (string, error) {
   252  	buffer := bytes.NewBuffer(make([]byte, 0, 1024))
   253  	buffer.WriteString("insert into mo_catalog.mo_indexes values")
   254  
   255  	isFirst := true
   256  	for _, constraint := range ct.Cts {
   257  		switch def := constraint.(type) {
   258  		case *engine.IndexDef:
   259  			for _, indexdef := range def.Indexes {
   260  				ctx, cancelFunc := context.WithTimeout(proc.Ctx, time.Second*30)
   261  				index_id, err := eg.AllocateIDByKey(ctx, ALLOCID_INDEX_KEY)
   262  				cancelFunc()
   263  				if err != nil {
   264  					return "", err
   265  				}
   266  
   267  				for i, part := range indexdef.Parts {
   268  					// NOTE: Don't resolve the alias here.
   269  					// If we resolve it here, it will insert "OriginalPKColumnName" into the "mo_catalog.mo_indexes" table instead of
   270  					// "AliasPKColumnName". This will result is issues filter "Programmatically added PK" from the output of
   271  					// "show indexes" and "show create table" command.
   272  
   273  					//1. index id
   274  					if isFirst {
   275  						fmt.Fprintf(buffer, "(%d, ", index_id)
   276  						isFirst = false
   277  					} else {
   278  						fmt.Fprintf(buffer, ", (%d, ", index_id)
   279  					}
   280  
   281  					//2. table_id
   282  					fmt.Fprintf(buffer, "%d, ", tableId)
   283  
   284  					// 3. databaseId
   285  					fmt.Fprintf(buffer, "%s, ", databaseId)
   286  
   287  					// 4.index.IndexName
   288  					fmt.Fprintf(buffer, "'%s', ", indexdef.IndexName)
   289  
   290  					// 5. index_type
   291  					var index_type string
   292  					if indexdef.Unique {
   293  						index_type = INDEX_TYPE_UNIQUE
   294  					} else {
   295  						index_type = INDEX_TYPE_MULTIPLE
   296  					}
   297  					fmt.Fprintf(buffer, "'%s', ", index_type)
   298  
   299  					//6. algorithm
   300  					var algorithm = indexdef.IndexAlgo
   301  					fmt.Fprintf(buffer, "'%s', ", algorithm)
   302  
   303  					//7. algorithm_table_type
   304  					var algorithm_table_type = indexdef.IndexAlgoTableType
   305  					fmt.Fprintf(buffer, "'%s', ", algorithm_table_type)
   306  
   307  					//8. algorithm_params
   308  					var algorithm_params = indexdef.IndexAlgoParams
   309  					fmt.Fprintf(buffer, "'%s', ", algorithm_params)
   310  
   311  					// 9. index visible
   312  					fmt.Fprintf(buffer, "%d, ", INDEX_VISIBLE_YES)
   313  
   314  					// 10. index vec_hidden
   315  					fmt.Fprintf(buffer, "%d, ", INDEX_HIDDEN_NO)
   316  
   317  					// 11. index vec_comment
   318  					fmt.Fprintf(buffer, "'%s', ", indexdef.Comment)
   319  
   320  					// 12. index vec_column_name
   321  					fmt.Fprintf(buffer, "'%s', ", part)
   322  
   323  					// 13. index vec_ordinal_position
   324  					fmt.Fprintf(buffer, "%d, ", i+1)
   325  
   326  					// 14. index vec_options
   327  					fmt.Fprintf(buffer, "%s, ", NULL_VALUE)
   328  
   329  					// 15. index vec_index_table
   330  					if indexdef.TableExist {
   331  						fmt.Fprintf(buffer, "'%s')", indexdef.IndexTableName)
   332  					} else {
   333  						fmt.Fprintf(buffer, "%s)", NULL_VALUE)
   334  					}
   335  				}
   336  			}
   337  		case *engine.PrimaryKeyDef:
   338  			ctx, cancelFunc := context.WithTimeout(proc.Ctx, time.Second*30)
   339  			index_id, err := eg.AllocateIDByKey(ctx, ALLOCID_INDEX_KEY)
   340  			cancelFunc()
   341  			if err != nil {
   342  				return "", err
   343  			}
   344  			if def.Pkey.PkeyColName != catalog.FakePrimaryKeyColName {
   345  				for i, colName := range def.Pkey.Names {
   346  					//1. index id
   347  					if isFirst {
   348  						fmt.Fprintf(buffer, "(%d, ", index_id)
   349  						isFirst = false
   350  					} else {
   351  						fmt.Fprintf(buffer, ", (%d, ", index_id)
   352  					}
   353  
   354  					//2. table_id
   355  					fmt.Fprintf(buffer, "%d, ", tableId)
   356  
   357  					// 3. databaseId
   358  					fmt.Fprintf(buffer, "%s, ", databaseId)
   359  
   360  					// 4.index.IndexName
   361  					fmt.Fprintf(buffer, "'%s', ", "PRIMARY")
   362  
   363  					// 5.index_type
   364  					fmt.Fprintf(buffer, "'%s', ", INDEX_TYPE_PRIMARY)
   365  
   366  					//6. algorithm
   367  					fmt.Fprintf(buffer, "'%s', ", EMPTY_STRING)
   368  
   369  					//7. algorithm_table_type
   370  					fmt.Fprintf(buffer, "'%s', ", EMPTY_STRING)
   371  
   372  					//8. algorithm_params
   373  					fmt.Fprintf(buffer, "'%s', ", EMPTY_STRING)
   374  
   375  					//9. index visible
   376  					fmt.Fprintf(buffer, "%d, ", INDEX_VISIBLE_YES)
   377  
   378  					// 10. index vec_hidden
   379  					fmt.Fprintf(buffer, "%d, ", INDEX_HIDDEN_NO)
   380  
   381  					// 11. index vec_comment
   382  					fmt.Fprintf(buffer, "'%s', ", EMPTY_STRING)
   383  
   384  					// 12. index vec_column_name
   385  					fmt.Fprintf(buffer, "'%s', ", colName)
   386  
   387  					// 13. index vec_ordinal_position
   388  					fmt.Fprintf(buffer, "%d, ", i+1)
   389  
   390  					// 14. index vec_options
   391  					fmt.Fprintf(buffer, "%s, ", NULL_VALUE)
   392  
   393  					// 15. index vec_index_table
   394  					fmt.Fprintf(buffer, "%s)", NULL_VALUE)
   395  				}
   396  			}
   397  		}
   398  	}
   399  	buffer.WriteString(";")
   400  	return buffer.String(), nil
   401  }
   402  
   403  // makeInsertSingleIndexSQL: make index metadata information sql for a single index object
   404  func makeInsertSingleIndexSQL(eg engine.Engine, proc *process.Process, databaseId string, tableId uint64, idxdef *plan.IndexDef) (string, error) {
   405  	if idxdef == nil {
   406  		return "", nil
   407  	}
   408  	ct := &engine.ConstraintDef{
   409  		Cts: []engine.Constraint{
   410  			&engine.IndexDef{
   411  				Indexes: []*plan.IndexDef{idxdef},
   412  			},
   413  		},
   414  	}
   415  	insertMoIndexesSql, err := genInsertMOIndexesSql(eg, proc, databaseId, tableId, ct)
   416  	if err != nil {
   417  		return "", err
   418  	}
   419  	return insertMoIndexesSql, nil
   420  }
   421  
   422  func makeInsertTablePartitionsSQL(ctx context.Context, dbSource engine.Database, relation engine.Relation) (string, error) {
   423  	if dbSource == nil || relation == nil {
   424  		return "", nil
   425  	}
   426  	databaseId := dbSource.GetDatabaseId(ctx)
   427  	tableId := relation.GetTableID(ctx)
   428  	tableDefs, err := relation.TableDefs(ctx)
   429  	if err != nil {
   430  		return "", err
   431  	}
   432  
   433  	for _, def := range tableDefs {
   434  		if partitionDef, ok := def.(*engine.PartitionDef); ok {
   435  			partitionByDef := &plan2.PartitionByDef{}
   436  			if err = partitionByDef.UnMarshalPartitionInfo(([]byte)(partitionDef.Partition)); err != nil {
   437  				return "", nil
   438  			}
   439  
   440  			insertMoTablePartitionSql := genInsertMoTablePartitionsSql(databaseId, tableId, partitionByDef, partitionByDef.Partitions)
   441  			return insertMoTablePartitionSql, nil
   442  		}
   443  	}
   444  	return "", nil
   445  }
   446  
   447  // makeInsertMultiIndexSQL :Synchronize the index metadata information of the table to the index metadata table
   448  func makeInsertMultiIndexSQL(eg engine.Engine, ctx context.Context, proc *process.Process, dbSource engine.Database, relation engine.Relation) (string, error) {
   449  	if dbSource == nil || relation == nil {
   450  		return "", nil
   451  	}
   452  	databaseId := dbSource.GetDatabaseId(ctx)
   453  	tableId := relation.GetTableID(ctx)
   454  
   455  	ct, err := GetConstraintDef(ctx, relation)
   456  	if err != nil {
   457  		return "", err
   458  	}
   459  	if ct == nil {
   460  		return "", nil
   461  	}
   462  
   463  	hasIndex := false
   464  	for _, constraint := range ct.Cts {
   465  		if idxdef, ok := constraint.(*engine.IndexDef); ok && len(idxdef.Indexes) > 0 {
   466  			hasIndex = true
   467  			break
   468  		}
   469  		if pkdef, ok := constraint.(*engine.PrimaryKeyDef); ok {
   470  			if pkdef.Pkey.PkeyColName != catalog.FakePrimaryKeyColName {
   471  				hasIndex = true
   472  				break
   473  			}
   474  		}
   475  	}
   476  	if !hasIndex {
   477  		return "", nil
   478  	}
   479  
   480  	insertMoIndexesSql, err := genInsertMOIndexesSql(eg, proc, databaseId, tableId, ct)
   481  	if err != nil {
   482  		return "", err
   483  	}
   484  	return insertMoIndexesSql, nil
   485  }
   486  
   487  func partsToColsStr(parts []string) string {
   488  	var temp string
   489  	for i, part := range parts {
   490  		part = catalog.ResolveAlias(part)
   491  		if i == 0 {
   492  			temp += part
   493  		} else {
   494  			temp += "," + part
   495  		}
   496  	}
   497  	return temp
   498  }
   499  
   500  func haveSinkScanInPlan(nodes []*plan.Node, curNodeIdx int32) bool {
   501  	node := nodes[curNodeIdx]
   502  	if node.NodeType == plan.Node_SINK_SCAN {
   503  		return true
   504  	}
   505  	for _, newIdx := range node.Children {
   506  		flag := haveSinkScanInPlan(nodes, newIdx)
   507  		if flag {
   508  			return flag
   509  		}
   510  	}
   511  	return false
   512  }
   513  
   514  // genInsertMoTablePartitionsSql: Generate an insert statement for insert index metadata into `mo_catalog.mo_table_partitions`
   515  func genInsertMoTablePartitionsSql(databaseId string, tableId uint64, partitionByDef *plan2.PartitionByDef, partitions []*plan.PartitionItem) string {
   516  	buffer := bytes.NewBuffer(make([]byte, 0, 2048))
   517  	buffer.WriteString("insert into mo_catalog.mo_table_partitions values")
   518  
   519  	isFirst := true
   520  	for _, partition := range partitions {
   521  		// 1. tableId
   522  		if isFirst {
   523  			fmt.Fprintf(buffer, "(%d, ", tableId)
   524  			isFirst = false
   525  		} else {
   526  			fmt.Fprintf(buffer, ", (%d, ", tableId)
   527  		}
   528  
   529  		// 2. database_id
   530  		fmt.Fprintf(buffer, "%s, ", databaseId)
   531  
   532  		// 3. partition number
   533  		fmt.Fprintf(buffer, "%d, ", partition.OrdinalPosition)
   534  
   535  		// 4. partition name
   536  		fmt.Fprintf(buffer, "'%s', ", partition.PartitionName)
   537  
   538  		// 5. partition type
   539  		fmt.Fprintf(buffer, "'%s', ", partitionByDef.Type.String())
   540  
   541  		// 6. partition expression
   542  		fmt.Fprintf(buffer, "'%s', ", partitionByDef.GenPartitionExprString())
   543  
   544  		// 7. description_utf8
   545  		fmt.Fprintf(buffer, "'%s', ", partition.Description)
   546  
   547  		// 8. partition item comment
   548  		fmt.Fprintf(buffer, "'%s', ", partition.Comment)
   549  
   550  		// 9. partition item options
   551  		fmt.Fprintf(buffer, "%s, ", NULL_VALUE)
   552  
   553  		// 10. partition_table_name
   554  		fmt.Fprintf(buffer, "'%s')", partition.PartitionTableName)
   555  	}
   556  	buffer.WriteString(";")
   557  	return buffer.String()
   558  }
   559  
   560  func GetConstraintDef(ctx context.Context, rel engine.Relation) (*engine.ConstraintDef, error) {
   561  	defs, err := rel.TableDefs(ctx)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  
   566  	return GetConstraintDefFromTableDefs(defs), nil
   567  }
   568  
   569  func GetConstraintDefFromTableDefs(defs []engine.TableDef) *engine.ConstraintDef {
   570  	var cstrDef *engine.ConstraintDef
   571  	for _, def := range defs {
   572  		if ct, ok := def.(*engine.ConstraintDef); ok {
   573  			cstrDef = ct
   574  			break
   575  		}
   576  	}
   577  	if cstrDef == nil {
   578  		cstrDef = &engine.ConstraintDef{}
   579  		cstrDef.Cts = make([]engine.Constraint, 0)
   580  	}
   581  	return cstrDef
   582  }