From 0bf9e9d812c99048af9682a9b7e52e5acf83abd5 Mon Sep 17 00:00:00 2001 From: Alex Robinson Date: Thu, 15 Sep 2016 09:55:02 -0400 Subject: [PATCH 1/2] sql: Don't create indexes / column families for views / virtual tables --- sql/information_schema.go | 14 +-- sql/show.go | 2 +- sql/sqlbase/structured.go | 203 +++++++++++++++++++++++--------------- 3 files changed, 130 insertions(+), 89 deletions(-) diff --git a/sql/information_schema.go b/sql/information_schema.go index 183f094541b8..eccd09b71006 100644 --- a/sql/information_schema.go +++ b/sql/information_schema.go @@ -99,7 +99,7 @@ CREATE TABLE information_schema.columns ( DATETIME_PRECISION INT ); `, - desc: sqlbase.TableDescriptor{Name: "columns", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "TABLE_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "COLUMN_NAME", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "ORDINAL_POSITION", ID: 0x5, Type: sqlbase.ColumnType{Kind: 1}, DefaultExpr: &zeroStr}, {Name: "COLUMN_DEFAULT", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "IS_NULLABLE", ID: 0x7, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "DATA_TYPE", ID: 0x8, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CHARACTER_MAXIMUM_LENGTH", ID: 0x9, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "CHARACTER_OCTET_LENGTH", ID: 0xa, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "NUMERIC_PRECISION", ID: 0xb, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "NUMERIC_SCALE", ID: 0xc, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "DATETIME_PRECISION", ID: 0xd, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0xe, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1, Unique: false, ColumnNames: []string(nil), ColumnDirections: []sqlbase.IndexDescriptor_Direction(nil), StoreColumnNames: []string(nil), ColumnIDs: []sqlbase.ColumnID(nil), ImplicitColumnIDs: []sqlbase.ColumnID(nil), Interleave: sqlbase.InterleaveDescriptor{Ancestors: []sqlbase.InterleaveDescriptor_Ancestor(nil)}, InterleavedBy: []sqlbase.ForeignKeyReference(nil)}, Indexes: []sqlbase.IndexDescriptor(nil), NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "columns", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "TABLE_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "COLUMN_NAME", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "ORDINAL_POSITION", ID: 0x5, Type: sqlbase.ColumnType{Kind: 1}, DefaultExpr: &zeroStr}, {Name: "COLUMN_DEFAULT", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "IS_NULLABLE", ID: 0x7, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "DATA_TYPE", ID: 0x8, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CHARACTER_MAXIMUM_LENGTH", ID: 0x9, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "CHARACTER_OCTET_LENGTH", ID: 0xa, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "NUMERIC_PRECISION", ID: 0xb, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "NUMERIC_SCALE", ID: 0xc, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}, {Name: "DATETIME_PRECISION", ID: 0xd, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0xe, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0, Unique: false, ColumnNames: []string(nil), ColumnDirections: []sqlbase.IndexDescriptor_Direction(nil), StoreColumnNames: []string(nil), ColumnIDs: []sqlbase.ColumnID(nil), ImplicitColumnIDs: []sqlbase.ColumnID(nil), Interleave: sqlbase.InterleaveDescriptor{Ancestors: []sqlbase.InterleaveDescriptor_Ancestor(nil)}, InterleavedBy: []sqlbase.ForeignKeyReference(nil)}, Indexes: []sqlbase.IndexDescriptor(nil), NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachTableDesc(p, func(db *sqlbase.DatabaseDescriptor, table *sqlbase.TableDescriptor) error { @@ -168,7 +168,7 @@ CREATE TABLE information_schema.table_privileges ( WITH_HIERARCHY BOOL NOT NULL DEFAULT FALSE ); `, - desc: sqlbase.TableDescriptor{Name: "table_privileges", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "GRANTOR", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "GRANTEE", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_CATALOG", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x5, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "PRIVILEGE_TYPE", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "IS_GRANTABLE", ID: 0x7, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}, {Name: "WITH_HIERARCHY", ID: 0x8, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}}, NextColumnID: 0x9, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1}, NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "table_privileges", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "GRANTOR", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "GRANTEE", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_CATALOG", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x5, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "PRIVILEGE_TYPE", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "IS_GRANTABLE", ID: 0x7, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}, {Name: "WITH_HIERARCHY", ID: 0x8, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}}, NextColumnID: 0x9, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0}, NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachTableDesc(p, func(db *sqlbase.DatabaseDescriptor, table *sqlbase.TableDescriptor) error { @@ -202,7 +202,7 @@ CREATE TABLE information_schema.schemata ( DEFAULT_CHARACTER_SET_NAME STRING NOT NULL DEFAULT '', SQL_PATH STRING );`, - desc: sqlbase.TableDescriptor{Name: "schemata", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "CATALOG_NAME", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "SCHEMA_NAME", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "DEFAULT_CHARACTER_SET_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "SQL_PATH", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0x5, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1}, NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "schemata", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "CATALOG_NAME", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "SCHEMA_NAME", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "DEFAULT_CHARACTER_SET_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "SQL_PATH", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0x5, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0}, NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachDatabaseDesc(p, func(db *sqlbase.DatabaseDescriptor) error { return addRow( @@ -225,7 +225,7 @@ CREATE TABLE information_schema.schema_privileges ( IS_GRANTABLE BOOL NOT NULL DEFAULT FALSE ); `, - desc: sqlbase.TableDescriptor{Name: "schema_privileges", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "GRANTEE", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_CATALOG", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "PRIVILEGE_TYPE", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "IS_GRANTABLE", ID: 0x5, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}}, NextColumnID: 0x6, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1}, NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "schema_privileges", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "GRANTEE", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_CATALOG", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "PRIVILEGE_TYPE", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "IS_GRANTABLE", ID: 0x5, Type: sqlbase.ColumnType{Kind: 0}, DefaultExpr: &falseStr}}, NextColumnID: 0x6, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0}, NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachDatabaseDesc(p, func(db *sqlbase.DatabaseDescriptor) error { for _, u := range db.Privileges.Show() { @@ -264,7 +264,7 @@ CREATE TABLE information_schema.table_constraints ( TABLE_NAME STRING NOT NULL DEFAULT '', CONSTRAINT_TYPE STRING NOT NULL DEFAULT '' );`, - desc: sqlbase.TableDescriptor{Name: "table_constraints", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "CONSTRAINT_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x5, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_TYPE", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}}, NextColumnID: 0x7, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1}, NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "table_constraints", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "CONSTRAINT_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x5, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "CONSTRAINT_TYPE", ID: 0x6, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}}, NextColumnID: 0x7, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0}, NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachTableDesc(p, func(db *sqlbase.DatabaseDescriptor, table *sqlbase.TableDescriptor) error { @@ -273,7 +273,7 @@ CREATE TABLE information_schema.table_constraints ( typ parser.Datum } var constraints []constraint - if table.HasPrimaryKey() { + if table.IsPhysicalTable() { constraints = append(constraints, constraint{ name: table.PrimaryIndex.Name, typ: constraintTypePrimaryKey, @@ -329,7 +329,7 @@ CREATE TABLE information_schema.tables ( TABLE_TYPE STRING NOT NULL DEFAULT '', VERSION INT );`, - desc: sqlbase.TableDescriptor{Name: "tables", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "TABLE_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_TYPE", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "VERSION", ID: 0x5, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0x6, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x1}, NextIndexID: 0x2, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, + desc: sqlbase.TableDescriptor{Name: "tables", ID: 0xffffffff, Version: 0x1, Columns: []sqlbase.ColumnDescriptor{{Name: "TABLE_CATALOG", ID: 0x1, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_SCHEMA", ID: 0x2, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_NAME", ID: 0x3, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "TABLE_TYPE", ID: 0x4, Type: sqlbase.ColumnType{Kind: 7}, DefaultExpr: &emptyStr}, {Name: "VERSION", ID: 0x5, Type: sqlbase.ColumnType{Kind: 1}, Nullable: true, DefaultExpr: (*string)(nil)}}, NextColumnID: 0x6, PrimaryIndex: sqlbase.IndexDescriptor{Name: "", ID: 0x0}, NextIndexID: 0x0, Privileges: emptyPrivileges, NextMutationID: 0x1, FormatVersion: 0x3}, populate: func(p *planner, addRow func(...parser.Datum) error) error { return forEachTableDesc(p, func(db *sqlbase.DatabaseDescriptor, table *sqlbase.TableDescriptor) error { diff --git a/sql/show.go b/sql/show.go index 78d46cb227a7..ebd1aa126982 100644 --- a/sql/show.go +++ b/sql/show.go @@ -167,7 +167,7 @@ func (p *planner) ShowCreateTable(n *parser.ShowCreateTable) (planNode, error) { if col.DefaultExpr != nil { fmt.Fprintf(&buf, " DEFAULT %s", *col.DefaultExpr) } - if desc.HasPrimaryKey() && desc.PrimaryIndex.ColumnIDs[0] == col.ID { + if desc.IsPhysicalTable() && desc.PrimaryIndex.ColumnIDs[0] == col.ID { // Only set primary if the primary key is on a visible column (not rowid). primary = fmt.Sprintf(",\n\tCONSTRAINT %s PRIMARY KEY (%s)", quoteNames(desc.PrimaryIndex.Name), diff --git a/sql/sqlbase/structured.go b/sql/sqlbase/structured.go index 071a46789cdf..21bf288a658d 100644 --- a/sql/sqlbase/structured.go +++ b/sql/sqlbase/structured.go @@ -245,6 +245,12 @@ func (desc *TableDescriptor) SetName(name string) { desc.Name = name } +// IsEmpty checks if the descriptor is uninitialized. +func (desc *TableDescriptor) IsEmpty() bool { + // Valid tables cannot have an ID of 0. + return desc.ID == 0 +} + // IsTable returns true if the TableDescriptor actually describes a // Table resource, as opposed to a different resource (like a View). func (desc *TableDescriptor) IsTable() bool { @@ -257,6 +263,21 @@ func (desc *TableDescriptor) IsView() bool { return desc.ViewQuery != "" } +// IsVirtualTable returns true if the TableDescriptor describes a +// virtual Table (like the information_schema tables) and thus doesn't +// need to be physically stored. +func (desc *TableDescriptor) IsVirtualTable() bool { + return desc.ID == keys.VirtualDescriptorID +} + +// IsPhysicalTable returns true if the TableDescriptor actually describes a +// physical Table that needs to be stored in the kv layer, as opposed to a +// different resource like a view or a virtual table. Physical tables have +// primary keys, column families, and indexes (unlike virtual tables). +func (desc *TableDescriptor) IsPhysicalTable() bool { + return desc.IsTable() && !desc.IsVirtualTable() +} + // allNonDropColumns returns all the columns, including those being added // in the mutations. func (desc *TableDescriptor) allNonDropColumns() []ColumnDescriptor { @@ -361,25 +382,9 @@ func (desc *TableDescriptor) maybeUpgradeToFamilyFormatVersion() bool { // AllocateIDs allocates column, family, and index ids for any column, family, // or index which has an ID of 0. func (desc *TableDescriptor) AllocateIDs() error { - if len(desc.PrimaryIndex.ColumnNames) == 0 && desc.HasPrimaryKey() { - // Ensure a Primary Key exists. - s := "unique_rowid()" - col := ColumnDescriptor{ - Name: "rowid", - Type: ColumnType{ - Kind: ColumnType_INT, - }, - DefaultExpr: &s, - Hidden: true, - Nullable: false, - } - desc.AddColumn(col) - idx := IndexDescriptor{ - Unique: true, - ColumnNames: []string{col.Name}, - ColumnDirections: []IndexDescriptor_Direction{IndexDescriptor_ASC}, - } - if err := desc.AddIndex(idx, true); err != nil { + // Only physical tables can have / need a primary key. + if desc.IsPhysicalTable() { + if err := desc.ensurePrimaryKey(); err != nil { return err } } @@ -387,17 +392,6 @@ func (desc *TableDescriptor) AllocateIDs() error { if desc.NextColumnID == 0 { desc.NextColumnID = 1 } - if desc.NextFamilyID == 0 { - if len(desc.Families) == 0 { - desc.Families = []ColumnFamilyDescriptor{ - {ID: 0, Name: "primary"}, - } - } - desc.NextFamilyID = 1 - } - if desc.NextIndexID == 0 { - desc.NextIndexID = 1 - } if desc.Version == 0 { desc.Version = 1 } @@ -424,6 +418,57 @@ func (desc *TableDescriptor) AllocateIDs() error { } } + // Only physical tables can have / need indexes and column families. + if desc.IsPhysicalTable() { + if err := desc.allocateIndexIDs(columnNames); err != nil { + return err + } + desc.allocateColumnFamilyIDs(columnNames) + } + + // This is sort of ugly. If the descriptor does not have an ID, we hack one in + // to pass the table ID check. We use a non-reserved ID, reserved ones being set + // before AllocateIDs. + savedID := desc.ID + if desc.ID == 0 { + desc.ID = keys.MaxReservedDescID + 1 + } + err := desc.ValidateTable() + desc.ID = savedID + return err +} + +func (desc *TableDescriptor) ensurePrimaryKey() error { + if len(desc.PrimaryIndex.ColumnNames) == 0 && desc.IsPhysicalTable() { + // Ensure a Primary Key exists. + s := "unique_rowid()" + col := ColumnDescriptor{ + Name: "rowid", + Type: ColumnType{ + Kind: ColumnType_INT, + }, + DefaultExpr: &s, + Hidden: true, + Nullable: false, + } + desc.AddColumn(col) + idx := IndexDescriptor{ + Unique: true, + ColumnNames: []string{col.Name}, + ColumnDirections: []IndexDescriptor_Direction{IndexDescriptor_ASC}, + } + if err := desc.AddIndex(idx, true); err != nil { + return err + } + } + return nil +} + +func (desc *TableDescriptor) allocateIndexIDs(columnNames map[string]ColumnID) error { + if desc.NextIndexID == 0 { + desc.NextIndexID = 1 + } + // Keep track of unnamed indexes. anonymousIndexes := make([]*IndexDescriptor, 0, len(desc.Indexes)+len(desc.Mutations)) @@ -496,10 +541,17 @@ func (desc *TableDescriptor) AllocateIDs() error { } } } + return nil +} - primaryIndexColIDs := make(map[ColumnID]struct{}, len(desc.PrimaryIndex.ColumnIDs)) - for _, colID := range desc.PrimaryIndex.ColumnIDs { - primaryIndexColIDs[colID] = struct{}{} +func (desc *TableDescriptor) allocateColumnFamilyIDs(columnNames map[string]ColumnID) { + if desc.NextFamilyID == 0 { + if len(desc.Families) == 0 { + desc.Families = []ColumnFamilyDescriptor{ + {ID: 0, Name: "primary"}, + } + } + desc.NextFamilyID = 1 } columnsInFamilies := make(map[ColumnID]struct{}, len(desc.Columns)) @@ -522,6 +574,11 @@ func (desc *TableDescriptor) AllocateIDs() error { desc.Families[i] = family } + primaryIndexColIDs := make(map[ColumnID]struct{}, len(desc.PrimaryIndex.ColumnIDs)) + for _, colID := range desc.PrimaryIndex.ColumnIDs { + primaryIndexColIDs[colID] = struct{}{} + } + ensureColumnInFamily := func(col *ColumnDescriptor) { if _, ok := columnsInFamilies[col.ID]; ok { return @@ -593,17 +650,6 @@ func (desc *TableDescriptor) AllocateIDs() error { desc.Families[i] = family } - - // This is sort of ugly. If the descriptor does not have an ID, we hack one in - // to pass the table ID check. We use a non-reserved ID, reserved ones being set - // before AllocateIDs. - savedID := desc.ID - if desc.ID == 0 { - desc.ID = keys.MaxReservedDescID + 1 - } - err := desc.ValidateTable() - desc.ID = savedID - return err } // Validate validates that the table descriptor is well formed. Checks include @@ -756,7 +802,7 @@ func (desc *TableDescriptor) ValidateTable() error { } // TODO(dt, nathan): virtual descs don't validate (missing privs, PK, etc). - if desc.ID == keys.VirtualDescriptorID { + if desc.IsVirtualTable() { return nil } @@ -839,11 +885,30 @@ func (desc *TableDescriptor) ValidateTable() error { // TODO(dt): Validate each column only appears at-most-once in any FKs. + // Only validate column families and indexes if this is actually a table, not + // if it's just a view. + if desc.IsPhysicalTable() { + colIDToFamilyID, err := desc.validateColumnFamilies(columnIDs) + if err != nil { + return err + } + if err := desc.validateTableIndexes(columnNames, colIDToFamilyID); err != nil { + return err + } + } + + // Validate the privilege descriptor. + return desc.Privileges.Validate(desc.GetID()) +} + +func (desc *TableDescriptor) validateColumnFamilies( + columnIDs map[ColumnID]string, +) (map[ColumnID]FamilyID, error) { if len(desc.Families) < 1 { - return fmt.Errorf("at least 1 column family must be specified") + return nil, fmt.Errorf("at least 1 column family must be specified") } if desc.Families[0].ID != FamilyID(0) { - return fmt.Errorf("the 0th family must have ID 0") + return nil, fmt.Errorf("the 0th family must have ID 0") } familyNames := map[string]struct{}{} @@ -851,66 +916,55 @@ func (desc *TableDescriptor) ValidateTable() error { colIDToFamilyID := map[ColumnID]FamilyID{} for _, family := range desc.Families { if err := validateName(family.Name, "family"); err != nil { - return err + return nil, err } normName := ReNormalizeName(family.Name) if _, ok := familyNames[normName]; ok { - return fmt.Errorf("duplicate family name: \"%s\"", family.Name) + return nil, fmt.Errorf("duplicate family name: \"%s\"", family.Name) } familyNames[normName] = struct{}{} if other, ok := familyIDs[family.ID]; ok { - return fmt.Errorf("family \"%s\" duplicate ID of family \"%s\": %d", + return nil, fmt.Errorf("family \"%s\" duplicate ID of family \"%s\": %d", family.Name, other, family.ID) } familyIDs[family.ID] = family.Name if family.ID >= desc.NextFamilyID { - return fmt.Errorf("family \"%s\" invalid family ID (%d) > next family ID (%d)", + return nil, fmt.Errorf("family \"%s\" invalid family ID (%d) > next family ID (%d)", family.Name, family.ID, desc.NextFamilyID) } if len(family.ColumnIDs) != len(family.ColumnNames) { - return fmt.Errorf("mismatched column ID size (%d) and name size (%d)", + return nil, fmt.Errorf("mismatched column ID size (%d) and name size (%d)", len(family.ColumnIDs), len(family.ColumnNames)) } for i, colID := range family.ColumnIDs { name, ok := columnIDs[colID] if !ok { - return fmt.Errorf("family \"%s\" contains unknown column \"%d\"", family.Name, colID) + return nil, fmt.Errorf("family \"%s\" contains unknown column \"%d\"", family.Name, colID) } if ReNormalizeName(name) != ReNormalizeName(family.ColumnNames[i]) { - return fmt.Errorf("family \"%s\" column %d should have name %q, but found name %q", + return nil, fmt.Errorf("family \"%s\" column %d should have name %q, but found name %q", family.Name, colID, name, family.ColumnNames[i]) } } for _, colID := range family.ColumnIDs { if famID, ok := colIDToFamilyID[colID]; ok { - return fmt.Errorf("column %d is in both family %d and %d", colID, famID, family.ID) + return nil, fmt.Errorf("column %d is in both family %d and %d", colID, famID, family.ID) } colIDToFamilyID[colID] = family.ID } } for colID := range columnIDs { if _, ok := colIDToFamilyID[colID]; !ok { - return fmt.Errorf("column %d is not in any column family", colID) + return nil, fmt.Errorf("column %d is not in any column family", colID) } } - - // Only validate the indexes if this is actually a table, not if it's - // just a view. - if desc.IsTable() { - err := desc.validateTableIndexes(columnNames, colIDToFamilyID) - if err != nil { - return err - } - } - - // Validate the privilege descriptor. - return desc.Privileges.Validate(desc.GetID()) + return colIDToFamilyID, nil } func (desc *TableDescriptor) validateTableIndexes( @@ -1446,19 +1500,6 @@ func ColumnsSelectors(cols []ColumnDescriptor) parser.SelectExprs { return exprs } -// IsEmpty checks if the descriptor is uninitialized. -func (desc *TableDescriptor) IsEmpty() bool { - // Valid tables cannot have an ID of 0. - return desc.ID == 0 -} - -// HasPrimaryKey checks if the table descriptor has a primary key. It is -// safe to assume that all table descriptors have primary keys except for -// virtual schema tables, which are not persisted. -func (desc *TableDescriptor) HasPrimaryKey() bool { - return desc.ID != keys.VirtualDescriptorID -} - // SQLString returns the SQL string corresponding to the type. func (c *ColumnType) SQLString() string { switch c.Kind { From 7554c01b66dfba18cb43ddc50dfe876e1edb1d69 Mon Sep 17 00:00:00 2001 From: Alex Robinson Date: Tue, 13 Sep 2016 22:07:24 -0400 Subject: [PATCH 2/2] sql: Support for selecting from views In the process, open the floodgates to grabbing leases on views. I believe I've handled all the callers that get a table lease, but let me know if you think I might have missed something! --- sql/data_source.go | 102 +++++++++++++++++++++++++++++++++--------- sql/lease.go | 5 --- sql/scan.go | 17 +------ sql/testdata/delete | 22 +++++++++ sql/testdata/insert | 18 ++++++++ sql/testdata/truncate | 26 +++++++++++ sql/testdata/update | 22 +++++++++ sql/testdata/views | 60 ++++++++++++++++++++++++- sql/truncate.go | 4 ++ sql/update.go | 5 +++ 10 files changed, 239 insertions(+), 42 deletions(-) diff --git a/sql/data_source.go b/sql/data_source.go index 0ed6efc632ba..82c3954b7710 100644 --- a/sql/data_source.go +++ b/sql/data_source.go @@ -237,28 +237,10 @@ func (p *planner) getDataSource( if foundVirtual { return ds, nil } - - // This name designates a real table. - scan := p.Scan() - if err := scan.initTable(p, tn, hints, scanVisibility); err != nil { - return planDataSource{}, err - } - - return planDataSource{ - info: newSourceInfoForSingleTable(*tn, scan.Columns()), - plan: scan, - }, nil + return p.getTableScanOrViewPlan(tn, hints, scanVisibility) case *parser.Subquery: - // We have a subquery (this includes a simple "VALUES"). - plan, err := p.newPlan(t.Select, nil, false) - if err != nil { - return planDataSource{}, err - } - return planDataSource{ - info: newSourceInfoForSingleTable(parser.TableName{}, plan.Columns()), - plan: plan, - }, nil + return p.getSubqueryPlan(t.Select, nil) case *parser.JoinTableExpr: // Joins: two sources. @@ -324,6 +306,86 @@ func (p *planner) getDataSource( } } +// getTableScanOrViewPlan builds a planDataSource from a single data source +// clause (either a table or a view) in a SelectClause, expanding views out +// into subqueries. +func (p *planner) getTableScanOrViewPlan( + tn *parser.TableName, + hints *parser.IndexHints, + scanVisibility scanVisibility, +) (planDataSource, error) { + descFunc := p.getTableLease + if p.asOf { + // AS OF SYSTEM TIME queries need to fetch the table descriptor at the + // specified time, and never lease anything. The proto transaction already + // has its timestamps set correctly so mustGetTableDesc will fetch with the + // correct timestamp. + descFunc = p.mustGetTableDesc + } + desc, err := descFunc(tn) + if err != nil { + return planDataSource{}, err + } + + if desc.IsView() { + return p.getViewPlan(tn, desc) + } else if !desc.IsTable() { + return planDataSource{}, + errors.Errorf("unexpected table descriptor of type %s for %q", desc.TypeName(), tn) + } + + // This name designates a real table. + scan := p.Scan() + if err := scan.initTable(p, desc, hints, scanVisibility); err != nil { + return planDataSource{}, err + } + + return planDataSource{ + info: newSourceInfoForSingleTable(*tn, scan.Columns()), + plan: scan, + }, nil +} + +// getViewPlan builds a planDataSource for the view specified by the +// table name and descriptor, expanding out its subquery plan. +func (p *planner) getViewPlan( + tn *parser.TableName, desc *sqlbase.TableDescriptor, +) (planDataSource, error) { + // Parse the query as Traditional syntax because we know the query was + // saved in the descriptor by printing it with parser.Format. + stmt, err := parser.ParseOneTraditional(desc.ViewQuery) + if err != nil { + return planDataSource{}, errors.Wrapf(err, "failed to parse underlying query from view %q", tn) + } + sel, ok := stmt.(*parser.Select) + if !ok { + return planDataSource{}, + errors.Errorf("failed to parse underlying query from view %q as a select", tn) + } + // TODO(a-robinson): Support ORDER BY and LIMIT in views. Is it as simple as + // just passing the entire select here or will inserting an ORDER BY in the + // middle of a query plan break things? + return p.getSubqueryPlan(sel.Select, makeResultColumns(desc.Columns)) +} + +// getSubqueryPlan builds a planDataSource for a select statement, including +// for simple VALUES statements. +func (p *planner) getSubqueryPlan( + sel parser.SelectStatement, cols ResultColumns, +) (planDataSource, error) { + plan, err := p.newPlan(sel, nil, false) + if err != nil { + return planDataSource{}, err + } + if len(cols) == 0 { + cols = plan.Columns() + } + return planDataSource{ + info: newSourceInfoForSingleTable(parser.TableName{}, cols), + plan: plan, + }, nil +} + // expandStar returns the array of column metadata and name // expressions that correspond to the expansion of a star. func (src *dataSourceInfo) expandStar( diff --git a/sql/lease.go b/sql/lease.go index a6a7bcbf7e12..699d9ad28e00 100644 --- a/sql/lease.go +++ b/sql/lease.go @@ -162,11 +162,6 @@ func (s LeaseStore) Acquire( if tableDesc == nil { return nil, errors.Errorf("ID %d is not a table", tableID) } - // TODO(a-robinson): Allow taking leases on view descriptors once this - // method's callers know how to handle views. - if !tableDesc.IsTable() { - return nil, errors.Errorf("ID %d is a %s, not a table", tableID, tableDesc.TypeName()) - } if err := filterTableState(tableDesc); err != nil { return nil, err } diff --git a/sql/scan.go b/sql/scan.go index f4feea6b653b..11f751a268d6 100644 --- a/sql/scan.go +++ b/sql/scan.go @@ -251,26 +251,13 @@ func (n *scanNode) ExplainTypes(regTypes func(string, string)) { } } -// Initializes a scanNode with a tableName. Returns the table or index name that can be used for -// fully-qualified columns if an alias is not specified. +// Initializes a scanNode with a table descriptor. func (n *scanNode) initTable( p *planner, - tableName *parser.TableName, + desc *sqlbase.TableDescriptor, indexHints *parser.IndexHints, scanVisibility scanVisibility, ) error { - descFunc := p.getTableLease - if p.asOf { - // AS OF SYSTEM TIME queries need to fetch the table descriptor at the - // specified time, and never lease anything. The proto transaction already - // has its timestamps set correctly so mustGetTableDesc will fetch with the - // correct timestamp. - descFunc = p.mustGetTableDesc - } - desc, err := descFunc(tableName) - if err != nil { - return err - } n.desc = *desc if err := p.checkPrivilege(&n.desc, privilege.SELECT); err != nil { diff --git a/sql/testdata/delete b/sql/testdata/delete index 133fa0b31990..5760b6492e34 100644 --- a/sql/testdata/delete +++ b/sql/testdata/delete @@ -26,6 +26,28 @@ SELECT * FROM kv 5 6 7 8 +statement ok +CREATE VIEW kview AS SELECT * FROM kv + +query II +SELECT * FROM kview +---- +1 2 +3 4 +5 6 +7 8 + +statement error cannot run DELETE on view "kview" - views are not updateable +DELETE FROM kview + +query II +SELECT * FROM kview +---- +1 2 +3 4 +5 6 +7 8 + statement ok DELETE FROM kv WHERE k=3 OR v=6 diff --git a/sql/testdata/insert b/sql/testdata/insert index cceae5a6ee58..4e8c94527152 100644 --- a/sql/testdata/insert +++ b/sql/testdata/insert @@ -405,6 +405,24 @@ INSERT INTO return VALUES (1, 2) RETURNING x.*[1] statement error compound types not supported yet: "x\[1\]" INSERT INTO return VALUES (1, 2) RETURNING x[1] +statement ok +CREATE VIEW kview AS VALUES ('a', 'b'), ('c', 'd') + +query TT +SELECT * FROM kview +---- +a b +c d + +statement error cannot run INSERT on view "kview" - views are not updateable +INSERT INTO kview VALUES ('e', 'f') + +query TT +SELECT * FROM kview +---- +a b +c d + statement ok CREATE TABLE abc ( a INT, diff --git a/sql/testdata/truncate b/sql/testdata/truncate index a8f67375d7ba..ecde8976d496 100644 --- a/sql/testdata/truncate +++ b/sql/testdata/truncate @@ -15,9 +15,35 @@ SELECT * FROM kv 5 6 7 8 +statement ok +CREATE VIEW kview AS SELECT * FROM kv + +query II +SELECT * FROM kview +---- +1 2 +3 4 +5 6 +7 8 + +statement error cannot run TRUNCATE on view "kview" - views are not updateable +TRUNCATE TABLE kview + +query II +SELECT * FROM kview +---- +1 2 +3 4 +5 6 +7 8 + statement ok TRUNCATE TABLE kv query II SELECT * FROM kv ---- + +query II +SELECT * FROM kview +---- diff --git a/sql/testdata/update b/sql/testdata/update index 4295354026ff..c1cdd1e2486c 100644 --- a/sql/testdata/update +++ b/sql/testdata/update @@ -44,6 +44,28 @@ UPDATE kv SET k[0] = 9 statement error compound types not supported yet: "k\.v" UPDATE kv SET k.v = 9 +statement ok +CREATE VIEW kview as SELECT * from kv + +query II +SELECT * FROM kview +---- +1 10 +3 12 +5 11 +7 15 + +statement error cannot run UPDATE on view "kview" - views are not updateable +UPDATE kview SET v = 99 WHERE k IN (1, 3) + +query II +SELECT * FROM kview +---- +1 10 +3 12 +5 11 +7 15 + statement ok CREATE TABLE kv2 ( k CHAR PRIMARY KEY, diff --git a/sql/testdata/views b/sql/testdata/views index 3f77b9f773d6..b7ae6a6a18cf 100644 --- a/sql/testdata/views +++ b/sql/testdata/views @@ -1,6 +1,9 @@ statement ok CREATE TABLE t (a INT PRIMARY KEY, b INT) +statement ok +INSERT INTO t VALUES (1, 99), (2, 98), (3, 97) + statement ok CREATE VIEW v1 AS select * from t @@ -22,8 +25,58 @@ CREATE VIEW v4 (x, y, z) AS select * from t; statement error pgcode 42P01 table "dne" does not exist CREATE VIEW v5 AS select * from dne; -# TODO(a-robinson): Add test that creates view by selecting from another -# view once selecting from views is supported. +statement ok +CREATE VIEW v6 (x, y) AS select * from v1; + +query II colnames +SELECT * FROM v1; +---- +a b +1 99 +2 98 +3 97 + +query II colnames +SELECT * FROM v2; +---- +x y +1 99 +2 98 +3 97 + +query II colnames +SELECT * FROM v6; +---- +x y +1 99 +2 98 +3 97 + +query II +SELECT * FROM v2 ORDER BY x DESC LIMIT 1; +---- +3 97 + +query I +SELECT x FROM v2; +---- +1 +2 +3 + +query I +SELECT y FROM v2; +---- +99 +98 +97 + +query IIII +SELECT * FROM v1 AS v1 INNER JOIN v2 AS v2 ON v1.a = v2.x; +---- +1 99 1 99 +2 98 2 98 +3 97 3 97 statement error pgcode 42809 "v1" is not a table DROP TABLE v1; @@ -40,5 +93,8 @@ DROP VIEW v1; statement ok DROP VIEW v2; +statement ok +DROP VIEW v6; + statement ok DROP TABLE t; diff --git a/sql/truncate.go b/sql/truncate.go index b8c4fde9afb2..1bd8f65e1268 100644 --- a/sql/truncate.go +++ b/sql/truncate.go @@ -50,6 +50,10 @@ func (p *planner) Truncate(n *parser.Truncate) (planNode, error) { if err != nil { return nil, err } + // We don't support truncation on views, only real tables. + if !tableDesc.IsTable() { + return nil, errors.Errorf("cannot run TRUNCATE on view %q - views are not updateable", tn) + } if err := p.checkPrivilege(tableDesc, privilege.DROP); err != nil { return nil, err diff --git a/sql/update.go b/sql/update.go index 862f45dcd64c..08fe25a07742 100644 --- a/sql/update.go +++ b/sql/update.go @@ -44,6 +44,11 @@ func (p *planner) makeEditNode(tn *parser.TableName, autoCommit bool, priv privi if err != nil { return editNodeBase{}, err } + // We don't support update on views, only real tables. + if !tableDesc.IsTable() { + return editNodeBase{}, + errors.Errorf("cannot run %s on view %q - views are not updateable", priv, tn) + } if err := p.checkPrivilege(tableDesc, priv); err != nil { return editNodeBase{}, err