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 }