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