// Copyright 2022 Molecula Corp. All rights reserved. package planner import ( "context" "sort" "time" pilosa "github.com/featurebasedb/featurebase/v3" "github.com/featurebasedb/featurebase/v3/dax" "github.com/featurebasedb/featurebase/v3/sql3" "github.com/featurebasedb/featurebase/v3/sql3/parser" "github.com/pkg/errors" ) // Ensure type implements interface. var _ pilosa.SchemaAPI = (*systemTableDefinitionsWrapper)(nil) type systemTableDefinitionsWrapper struct { schemaAPI pilosa.SchemaAPI } func newSystemTableDefinitionsWrapper(api pilosa.SchemaAPI) *systemTableDefinitionsWrapper { return &systemTableDefinitionsWrapper{ schemaAPI: api, } } func (s *systemTableDefinitionsWrapper) CreateDatabase(ctx context.Context, db *dax.Database) error { return s.schemaAPI.CreateDatabase(ctx, db) } func (s *systemTableDefinitionsWrapper) DropDatabase(ctx context.Context, dbid dax.DatabaseID) error { return s.schemaAPI.DropDatabase(ctx, dbid) } func (s *systemTableDefinitionsWrapper) DatabaseByName(ctx context.Context, dbname dax.DatabaseName) (*dax.Database, error) { return s.schemaAPI.DatabaseByName(ctx, dbname) } func (s *systemTableDefinitionsWrapper) DatabaseByID(ctx context.Context, dbid dax.DatabaseID) (*dax.Database, error) { return s.schemaAPI.DatabaseByID(ctx, dbid) } func (s *systemTableDefinitionsWrapper) SetDatabaseOption(ctx context.Context, dbid dax.DatabaseID, option string, value string) error { return s.schemaAPI.SetDatabaseOption(ctx, dbid, option, value) } func (s *systemTableDefinitionsWrapper) Databases(ctx context.Context, dbids ...dax.DatabaseID) ([]*dax.Database, error) { return s.schemaAPI.Databases(ctx, dbids...) } func (s *systemTableDefinitionsWrapper) TableByName(ctx context.Context, tname dax.TableName) (*dax.Table, error) { tbl, err := s.schemaAPI.TableByName(ctx, tname) if err != nil { if isTableNotFoundError(err) { st, ok := systemTables.table(string(tname)) if !ok { return nil, dax.NewErrTableNameDoesNotExist(tname) } return indexInfoFromSystemTableB(st) } return nil, err } return tbl, nil } func (s *systemTableDefinitionsWrapper) TableByID(ctx context.Context, tid dax.TableID) (*dax.Table, error) { return s.schemaAPI.TableByID(ctx, tid) } func (s *systemTableDefinitionsWrapper) Tables(ctx context.Context) ([]*dax.Table, error) { tbls, err := s.schemaAPI.Tables(ctx) if err != nil { return nil, errors.Wrap(err, "getting tables") } // Append the system tables. for _, st := range systemTables.ordered() { ii, err := indexInfoFromSystemTable(st) if err != nil { return nil, errors.Wrapf(err, "converting system table to table: %s", st.name) } tbls = append(tbls, pilosa.IndexInfoToTable(ii)) } // order the result - by ID asc right now sort.Slice(tbls, func(i, j int) bool { return tbls[i].ID < tbls[j].ID }) return tbls, nil } func (s *systemTableDefinitionsWrapper) CreateTable(ctx context.Context, tbl *dax.Table) error { return s.schemaAPI.CreateTable(ctx, tbl) } func (s *systemTableDefinitionsWrapper) CreateField(ctx context.Context, tname dax.TableName, fld *dax.Field) error { return s.schemaAPI.CreateField(ctx, tname, fld) } func (s *systemTableDefinitionsWrapper) DeleteTable(ctx context.Context, tname dax.TableName) error { return s.schemaAPI.DeleteTable(ctx, tname) } func (s *systemTableDefinitionsWrapper) DeleteField(ctx context.Context, tname dax.TableName, fname dax.FieldName) error { return s.schemaAPI.DeleteField(ctx, tname, fname) } func indexInfoFromSystemTableB(st *systemTable) (*dax.Table, error) { fields := make([]*dax.Field, 0) for _, f := range st.schema { var baseType dax.BaseType var epoch time.Time switch f.Type.(type) { case *parser.DataTypeInt: baseType = dax.BaseTypeInt case *parser.DataTypeBool: baseType = dax.BaseTypeBool case *parser.DataTypeString: baseType = dax.BaseTypeString case *parser.DataTypeTimestamp: baseType = dax.BaseTypeTimestamp epoch = time.Unix(0, 0) default: return nil, sql3.NewErrInternalf("unexpected system table field type '%T'", f.Type) } _ = dax.FieldOptions{} fld := &dax.Field{ Name: dax.FieldName(f.ColumnName), Type: baseType, Options: dax.FieldOptions{Epoch: epoch}, } fields = append(fields, fld) } tbl := &dax.Table{ Name: dax.TableName(st.name), Fields: fields, } return tbl, nil } func indexInfoFromSystemTable(st *systemTable) (*pilosa.IndexInfo, error) { fields := make([]*pilosa.FieldInfo, 0) for _, f := range st.schema { var opts pilosa.FieldOptions switch f.Type.(type) { case *parser.DataTypeInt: opts.Type = pilosa.FieldTypeInt case *parser.DataTypeBool: opts.Type = pilosa.FieldTypeBool case *parser.DataTypeString: opts.Type = pilosa.FieldTypeMutex opts.Keys = true case *parser.DataTypeTimestamp: opts.Type = pilosa.FieldTypeTimestamp default: return nil, sql3.NewErrInternalf("unexpected system table field type '%T'", f.Type) } fld := &pilosa.FieldInfo{ Name: f.ColumnName, Options: opts, } fields = append(fields, fld) } i := &pilosa.IndexInfo{ Name: st.name, Fields: fields, } return i, nil }