github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/partition_list.go (about)

     1  // Copyright 2022 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 plan
    16  
    17  import (
    18  	"context"
    19  
    20  	"github.com/matrixorigin/matrixone/pkg/catalog"
    21  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    22  	"github.com/matrixorigin/matrixone/pkg/container/types"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    24  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    25  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql"
    26  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    27  	"github.com/matrixorigin/matrixone/pkg/sql/util"
    28  )
    29  
    30  type listPartitionBuilder struct {
    31  	addPartitions            []*plan.PartitionItem
    32  	addPartitionSubTableDefs []*TableDef
    33  	newPartitionDef          *PartitionByDef
    34  }
    35  
    36  // buildListPartitiion handle List Partitioning and List columns partitioning
    37  func (lpb *listPartitionBuilder) build(ctx context.Context, partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error {
    38  	partitionSyntaxDef := stmt.PartitionOption
    39  	partitionCount, err := getValidPartitionCount(ctx, true, partitionSyntaxDef)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	partitionType := partitionSyntaxDef.PartBy.PType.(*tree.ListType)
    44  
    45  	partitionDef := &plan.PartitionByDef{
    46  		IsSubPartition: partitionSyntaxDef.PartBy.IsSubPartition,
    47  		Partitions:     make([]*plan.PartitionItem, partitionCount),
    48  		PartitionNum:   partitionCount,
    49  	}
    50  
    51  	if len(partitionType.ColumnList) == 0 {
    52  		//PARTITION BY LIST(expr)
    53  		partitionDef.Type = plan.PartitionType_LIST
    54  		err := buildPartitionExpr(ctx, tableDef, partitionBinder, partitionDef, partitionType.Expr)
    55  		if err != nil {
    56  			return err
    57  		}
    58  	} else {
    59  		//PARTITION BY LIST COLUMNS(col1,col2,...)
    60  		partitionDef.Type = plan.PartitionType_LIST_COLUMNS
    61  		err = buildPartitionColumns(ctx, tableDef, partitionBinder, partitionDef, partitionType.ColumnList)
    62  		if err != nil {
    63  			return err
    64  		}
    65  	}
    66  
    67  	err = lpb.buildPartitionDefs(ctx, partitionBinder, partitionDef, partitionSyntaxDef.Partitions)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	err = lpb.checkPartitionIntegrity(ctx, partitionBinder, tableDef, partitionDef)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	err = lpb.buildEvalPartitionExpression(ctx, partitionBinder, stmt.PartitionOption, partitionDef)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	//partitionDef.PartitionMsg = tree.String(partitionSyntaxDef, dialect.MYSQL)
    83  	partitionDef.PartitionMsg = tree.StringWithOpts(partitionSyntaxDef, dialect.MYSQL, tree.WithSingleQuoteString())
    84  	tableDef.Partition = partitionDef
    85  	return nil
    86  }
    87  
    88  func (lpb *listPartitionBuilder) buildPartitionDefs(ctx context.Context, partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, defs []*tree.Partition) (err error) {
    89  	for i, partition := range defs {
    90  		name := string(partition.Name)
    91  		partitionItem := &plan.PartitionItem{
    92  			PartitionName:   name,
    93  			OrdinalPosition: uint32(i + 1),
    94  		}
    95  
    96  		if valuesIn, ok := partition.Values.(*tree.ValuesIn); ok {
    97  			valueList := valuesIn.ValueList
    98  			if len(valueList) == 0 {
    99  				return moerr.NewInvalidInput(ctx, "there is no value in value list")
   100  			}
   101  			binder := NewPartitionBinder(nil, nil)
   102  			inValues := make([]*plan.Expr, len(valueList))
   103  
   104  			for j, value := range valueList {
   105  				tuple, err := binder.BindExpr(value, 0, false)
   106  				if err != nil {
   107  					return err
   108  				}
   109  				inValues[j] = tuple
   110  			}
   111  			partitionItem.InValues = inValues
   112  			partitionItem.Description = tree.String(valuesIn, dialect.MYSQL)
   113  		} else {
   114  			return moerr.NewInternalError(ctx, "LIST PARTITIONING can only use VALUES IN definition")
   115  		}
   116  		for _, tableOption := range partition.Options {
   117  			if opComment, ok := tableOption.(*tree.TableOptionComment); ok {
   118  				partitionItem.Comment = opComment.Comment
   119  			}
   120  		}
   121  		partitionDef.Partitions[i] = partitionItem
   122  	}
   123  	return buildListPartitionItem(partitionBinder, partitionDef, defs)
   124  }
   125  
   126  func (lpb *listPartitionBuilder) checkPartitionIntegrity(ctx context.Context, partitionBinder *PartitionBinder, tableDef *TableDef, partitionDef *plan.PartitionByDef) error {
   127  	if err := checkPartitionExprType(ctx, partitionBinder, tableDef, partitionDef); err != nil {
   128  		return err
   129  	}
   130  	if err := checkPartitionDefines(ctx, partitionBinder, partitionDef, tableDef); err != nil {
   131  		return err
   132  	}
   133  	if err := checkPartitionKeys(ctx, partitionBinder.builder.nameByColRef, tableDef, partitionDef); err != nil {
   134  		return err
   135  	}
   136  	if partitionDef.Type == plan.PartitionType_KEY || partitionDef.Type == plan.PartitionType_LINEAR_KEY {
   137  		if len(partitionDef.PartitionColumns.Columns) == 0 {
   138  			return handleEmptyKeyPartition(partitionBinder, tableDef, partitionDef)
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  func (lpb *listPartitionBuilder) buildEvalPartitionExpression(ctx context.Context, partitionBinder *PartitionBinder, stmt *tree.PartitionOption, partitionDef *plan.PartitionByDef) error {
   145  	partitionType := stmt.PartBy.PType.(*tree.ListType)
   146  	// For the List partition, convert the partition information into the expression, such as:
   147  	// case when expr in (1, 5, 9, 13, 17) then 0 when expr in (2, 6, 10, 14, 18) then 1 else -1 end
   148  	if partitionType.ColumnList == nil {
   149  		listExpr := partitionType.Expr
   150  		partitionExprAst, err := buildListCaseWhenExpr(listExpr, stmt.Partitions)
   151  		if err != nil {
   152  			return err
   153  		}
   154  		tempExpr, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true)
   155  		if err != nil {
   156  			return err
   157  		}
   158  		partitionExpression, err := appendCastBeforeExpr(ctx, tempExpr, plan.Type{
   159  			Id:          int32(types.T_int32),
   160  			NotNullable: true,
   161  		})
   162  		if err != nil {
   163  			return err
   164  		}
   165  		partitionDef.PartitionExpression = partitionExpression
   166  	} else {
   167  		// For the List Columns partition, convert the partition information into the expression, such as:
   168  		// case when col1 = 0 and col2 = 0 or col1 = null and col2 = null then 0
   169  		// when col1 = 0 and col2 = 1 or col1 = 0 and col2 = 2 or col1 = 0 and col2 = 3 then 1
   170  		// when col1 = 1 and col2 = 0 or col1 = 2 and col2 = 0 or col1 = 2 and col2 = 1 then 2
   171  		// else -1 end
   172  		columnsExpr := partitionType.ColumnList
   173  		partitionExprAst, err := buildListColumnsCaseWhenExpr(columnsExpr, stmt.Partitions)
   174  		if err != nil {
   175  			return err
   176  		}
   177  		tempExpr, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true)
   178  		if err != nil {
   179  			return err
   180  		}
   181  		partitionExpression, err := appendCastBeforeExpr(ctx, tempExpr, plan.Type{
   182  			Id:          int32(types.T_int32),
   183  			NotNullable: true,
   184  		})
   185  		if err != nil {
   186  			return err
   187  		}
   188  		partitionDef.PartitionExpression = partitionExpression
   189  	}
   190  	return nil
   191  }
   192  
   193  // buildAddPartition Perform `ADD PARTITION` semantic check on the list partition table
   194  func (lpb *listPartitionBuilder) buildAddPartition(ctx context.Context, partitionBinder *PartitionBinder, stmt *tree.AlterPartitionAddPartitionClause, tableDef *TableDef) error {
   195  	partitionInfo := DeepCopyPartitionByDef(tableDef.Partition)
   196  
   197  	err := lpb.buildAddPartitionDefinitions(ctx, partitionBinder, partitionInfo, stmt.Partitions)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	err = lpb.checkPartitionIntegrity(ctx, partitionBinder, tableDef, partitionInfo)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	//------------------------------------------------------------------------------------------------------------------
   208  	// Regenerate the syntax tree for the partition by clause
   209  	ast, err := mysql.ParseOne(ctx, "create table t1() "+partitionInfo.PartitionMsg, 1, 0)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	createTable, ok := ast.(*tree.CreateTable)
   215  	if !ok {
   216  		panic("Partition definition statement Parser restore failed")
   217  	}
   218  
   219  	// Regenerate partition calculation expression
   220  	partitionBy := createTable.PartitionOption
   221  	partitionBy.Partitions = append(partitionBy.Partitions, stmt.Partitions...)
   222  	err = lpb.buildEvalPartitionExpression(ctx, partitionBinder, partitionBy, partitionInfo)
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	// Generate a new partition by clause's restore string
   228  	partitionInfo.PartitionMsg = tree.StringWithOpts(partitionBy, dialect.MYSQL, tree.WithSingleQuoteString())
   229  
   230  	// Construct partition subtable `TableDef` for newly added partitions
   231  	err = lpb.makePartitionSubTableDef(ctx, tableDef, partitionInfo, lpb.addPartitions)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	return nil
   236  }
   237  
   238  // Perform semantic check for newly added partition definitions
   239  func (lpb *listPartitionBuilder) buildAddPartitionDefinitions(ctx context.Context, partitionBinder *PartitionBinder, newPartitionDef *plan.PartitionByDef, defs []*tree.Partition) (err error) {
   240  	srcLen := len(newPartitionDef.Partitions)
   241  	addPartitions := make([]*plan.PartitionItem, len(defs))
   242  
   243  	for i, partition := range defs {
   244  		name := string(partition.Name)
   245  		partitionItem := &plan.PartitionItem{
   246  			PartitionName:   name,
   247  			OrdinalPosition: uint32(srcLen + i + 1),
   248  		}
   249  
   250  		if valuesIn, ok := partition.Values.(*tree.ValuesIn); ok {
   251  			valueList := valuesIn.ValueList
   252  			if len(valueList) == 0 {
   253  				return moerr.NewInvalidInput(ctx, "there is no value in value list")
   254  			}
   255  			binder := NewPartitionBinder(nil, nil)
   256  			inValues := make([]*plan.Expr, len(valueList))
   257  
   258  			for j, value := range valueList {
   259  				tuple, err := binder.BindExpr(value, 0, false)
   260  				if err != nil {
   261  					return err
   262  				}
   263  				inValues[j] = tuple
   264  			}
   265  			partitionItem.InValues = inValues
   266  			partitionItem.Description = tree.String(valuesIn, dialect.MYSQL)
   267  		} else {
   268  			return moerr.NewInternalError(ctx, "LIST PARTITIONING can only use VALUES IN definition")
   269  		}
   270  		for _, tableOption := range partition.Options {
   271  			if opComment, ok := tableOption.(*tree.TableOptionComment); ok {
   272  				partitionItem.Comment = opComment.Comment
   273  			}
   274  		}
   275  		addPartitions[i] = partitionItem
   276  		newPartitionDef.Partitions = append(newPartitionDef.Partitions, partitionItem)
   277  	}
   278  	lpb.addPartitions = addPartitions
   279  	return buildListPartitionItem(partitionBinder, newPartitionDef, defs)
   280  }
   281  
   282  // makePartitionSubTableDef Construct List partition subtable `TableDef` for newly added partitions
   283  func (lpb *listPartitionBuilder) makePartitionSubTableDef(ctx context.Context, tableDef *TableDef, newPartitionDef *plan.PartitionByDef, alterAddPartition []*plan.PartitionItem) error {
   284  	//add partition table
   285  	//there is no index for the partition table
   286  	//there is no foreign key for the partition table
   287  	mainTableName := tableDef.Name
   288  	if !util.IsValidNameForPartitionTable(mainTableName) {
   289  		return moerr.NewInvalidInput(ctx, "invalid main table name %s", mainTableName)
   290  	}
   291  
   292  	// common properties
   293  	partitionProps := []*plan.Property{
   294  		{
   295  			Key:   catalog.SystemRelAttr_Kind,
   296  			Value: catalog.SystemPartitionRel,
   297  		},
   298  		{
   299  			Key:   catalog.SystemRelAttr_CreateSQL,
   300  			Value: "",
   301  		},
   302  	}
   303  	partitionPropsDef := &plan.TableDef_DefType{
   304  		Def: &plan.TableDef_DefType_Properties{
   305  			Properties: &plan.PropertiesDef{
   306  				Properties: partitionProps,
   307  			},
   308  		}}
   309  
   310  	partitionTableDefs := make([]*TableDef, len(alterAddPartition))
   311  	partitionTableNames := make([]string, len(alterAddPartition))
   312  
   313  	for i := 0; i < len(alterAddPartition); i++ {
   314  		part := alterAddPartition[i]
   315  		ok, partitionTableName := util.MakeNameOfPartitionTable(part.GetPartitionName(), mainTableName)
   316  		if !ok {
   317  			return moerr.NewInvalidInput(ctx, "invalid partition table name %s", partitionTableName)
   318  		}
   319  
   320  		// save the table name for a partition
   321  		part.PartitionTableName = partitionTableName
   322  		partitionTableNames[i] = partitionTableName
   323  
   324  		partitionTableDefs[i] = &TableDef{
   325  			Name: partitionTableName,
   326  			Cols: deepCopyTableCols(tableDef.Cols, true), //same as the main table's column defs
   327  		}
   328  
   329  		partitionTableDefs[i].Pkey = tableDef.GetPkey()
   330  		partitionTableDefs[i].Defs = append(partitionTableDefs[i].Defs, partitionPropsDef)
   331  	}
   332  	newPartitionDef.PartitionTableNames = append(newPartitionDef.PartitionTableNames, partitionTableNames...)
   333  	newPartitionDef.PartitionNum = newPartitionDef.PartitionNum + uint64(len(alterAddPartition))
   334  	lpb.newPartitionDef = newPartitionDef
   335  	lpb.addPartitionSubTableDefs = partitionTableDefs
   336  	return nil
   337  }