mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
The attempt to set the TrackExistence option for fields happened before checking whether the field was created successfully or not. Credit to Rachith for spotting this. Bug was introduced with the TrackExistence stuff, but we apparently never had a test case for invalid min/max values.
143 lines
3.7 KiB
Go
143 lines
3.7 KiB
Go
// Copyright 2021 Molecula Corp. All rights reserved.
|
|
|
|
package planner
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
pilosa "github.com/featurebasedb/featurebase/v3"
|
|
"github.com/featurebasedb/featurebase/v3/dax"
|
|
"github.com/featurebasedb/featurebase/v3/sql3"
|
|
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// PlanOpCreateTable plan operator that creates a table.
|
|
type PlanOpCreateTable struct {
|
|
planner *ExecutionPlanner
|
|
tableName string
|
|
failIfExists bool
|
|
isKeyed bool
|
|
keyPartitions int
|
|
description string
|
|
columns []*createTableField
|
|
warnings []string
|
|
}
|
|
|
|
// NewPlanOpCreateTable returns a new PlanOpCreateTable planoperator
|
|
func NewPlanOpCreateTable(p *ExecutionPlanner, tableName string, failIfExists bool, isKeyed bool, keyPartitions int, description string, columns []*createTableField) *PlanOpCreateTable {
|
|
return &PlanOpCreateTable{
|
|
planner: p,
|
|
tableName: tableName,
|
|
failIfExists: failIfExists,
|
|
isKeyed: isKeyed,
|
|
keyPartitions: keyPartitions,
|
|
columns: columns,
|
|
description: description,
|
|
warnings: make([]string, 0),
|
|
}
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_op"] = fmt.Sprintf("%T", p)
|
|
result["name"] = p.tableName
|
|
result["failIfExists"] = p.failIfExists
|
|
return result
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) String() string {
|
|
return ""
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) AddWarning(warning string) {
|
|
p.warnings = append(p.warnings, warning)
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) Warnings() []string {
|
|
return p.warnings
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) Schema() types.Schema {
|
|
return types.Schema{}
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) Children() []types.PlanOperator {
|
|
return []types.PlanOperator{}
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) Iterator(ctx context.Context, row types.Row) (types.RowIterator, error) {
|
|
return &createTableRowIter{
|
|
planner: p.planner,
|
|
tableName: p.tableName,
|
|
failIfExists: p.failIfExists,
|
|
isKeyed: p.isKeyed,
|
|
keyPartitions: p.keyPartitions,
|
|
columns: p.columns,
|
|
description: p.description,
|
|
}, nil
|
|
}
|
|
|
|
func (p *PlanOpCreateTable) WithChildren(children ...types.PlanOperator) (types.PlanOperator, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type createTableRowIter struct {
|
|
planner *ExecutionPlanner
|
|
tableName string
|
|
failIfExists bool
|
|
isKeyed bool
|
|
keyPartitions int
|
|
description string
|
|
columns []*createTableField
|
|
}
|
|
|
|
var _ types.RowIterator = (*createTableRowIter)(nil)
|
|
|
|
func (i *createTableRowIter) Next(ctx context.Context) (types.Row, error) {
|
|
//create the table
|
|
|
|
fields := make([]*dax.Field, 0, len(i.columns)+1)
|
|
|
|
// add the _id column
|
|
var idType dax.BaseType = dax.BaseTypeID
|
|
if i.isKeyed {
|
|
idType = dax.BaseTypeString
|
|
}
|
|
fields = append(fields, &dax.Field{
|
|
Name: dax.PrimaryKeyFieldName,
|
|
Type: idType,
|
|
})
|
|
|
|
for _, f := range i.columns {
|
|
fld, err := pilosa.FieldFromFieldOptions(dax.FieldName(f.name), f.fos...)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "creating field from field options: %s", f.name)
|
|
}
|
|
// We unconditionally turn on TrackExistence for all newly-created fields.
|
|
fld.Options.TrackExistence = true
|
|
fields = append(fields, fld)
|
|
}
|
|
|
|
tbl := &dax.Table{
|
|
Name: dax.TableName(i.tableName),
|
|
Fields: fields,
|
|
// TODO(tlt): once we can support different partitionN's per table,
|
|
// replace dax.DefaultPartitionN with i.keyPartitions.
|
|
PartitionN: dax.DefaultPartitionN,
|
|
|
|
Description: i.description,
|
|
}
|
|
|
|
if err := i.planner.schemaAPI.CreateTable(ctx, tbl); err != nil {
|
|
if _, ok := errors.Cause(err).(pilosa.ConflictError); ok {
|
|
if i.failIfExists {
|
|
return nil, sql3.NewErrTableExists(0, 0, i.tableName)
|
|
}
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
return nil, types.ErrNoMoreRows
|
|
}
|