Moved search path functionality to its package

This commit is contained in:
Zach Musgrave
2024-05-23 12:55:28 -07:00
parent dd6065f767
commit b02fcd2fe9
5 changed files with 152 additions and 131 deletions

View File

@@ -23,6 +23,7 @@ import (
"strings"
flatbuffers "github.com/dolthub/flatbuffers/v23/go"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/gen/fb/serial"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
@@ -1211,3 +1212,21 @@ func NewDataCacheKey(rv RootValue) (DataCacheKey, error) {
return DataCacheKey{hash}, nil
}
// ResolveDatabaseSchema returns the case-sensitive name for the schema requested, if it exists, and an error if
// schemas could not be loaded.
func ResolveDatabaseSchema(ctx *sql.Context, root RootValue, schemaName string) (string, bool, error) {
schemas, err := root.GetDatabaseSchemas(ctx)
if err != nil {
return "", false, err
}
for _, databaseSchema := range schemas {
if strings.EqualFold(databaseSchema.Name, schemaName) {
return databaseSchema.Name, true, nil
}
}
return "", false, nil
}

View File

@@ -22,6 +22,7 @@ import (
"strings"
"time"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/search_path"
sqle "github.com/dolthub/go-mysql-server"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/analyzer"
@@ -677,9 +678,9 @@ func (db Database) getTable(ctx *sql.Context, root doltdb.RootValue, tableName s
}
var tbl *doltdb.Table
if UseSearchPath && db.schemaName == "" {
if search_path.UseSearchPath && db.schemaName == "" {
var schemaName string
tableName, schemaName, tbl, ok, err = db.resolveTableWithSearchPath(ctx, root, tableName)
tableName, schemaName, tbl, ok, err = search_path.ResolveTableWithSearchPath(ctx, root, tableName)
if err != nil {
return nil, false, err
} else if !ok {
@@ -750,65 +751,6 @@ func (db Database) resolveTable(ctx *sql.Context, root doltdb.RootValue, tableNa
return tableName, tbl, true, nil
}
var defaultSearchPath = "doltgres,public"
func (db Database) resolveTableWithSearchPath(ctx *sql.Context, root doltdb.RootValue, tableName string) (string, string, *doltdb.Table, bool, error) {
schemasToSearch, err := searchPath(ctx)
if err != nil {
return "", "", nil, false, err
}
for _, schemaName := range schemasToSearch {
tablesInSchema, err := root.GetTableNames(ctx, schemaName)
if err != nil {
return "", "", nil, false, err
}
correctedTableName, ok := sql.GetTableNameInsensitive(tableName, tablesInSchema)
if !ok {
continue
}
// TODO: what schema name do we use for system tables?
tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: correctedTableName, Schema: schemaName})
if err != nil {
return "", "", nil, false, err
} else if !ok {
// Should be impossible
return "", "", nil, false, doltdb.ErrTableNotFound
}
return correctedTableName, schemaName, tbl, true, nil
}
return "", "", nil, false, nil
}
// searchPath returns all the schemas in the search_path setting, with elements like "$user" expanded
func searchPath(ctx *sql.Context) ([]string, error) {
searchPathVar, err := ctx.GetSessionVariable(ctx, "search_path")
if err != nil {
return nil, err
}
pathElems := strings.Split(searchPathVar.(string), ",")
path := make([]string, len(pathElems))
for i, pathElem := range pathElems {
path[i] = normalizeSearchPathSchema(ctx, pathElem)
}
return path, nil
}
func normalizeSearchPathSchema(ctx *sql.Context, schemaName string) string {
schemaName = strings.Trim(schemaName, " ")
if schemaName == "\"$user\"" {
client := ctx.Session.Client()
return client.User
}
return schemaName
}
// newDoltTable returns a sql.Table wrapping the given underlying dolt table
func (db Database) newDoltTable(tableName string, sch schema.Schema, tbl *doltdb.Table) (sql.Table, error) {
readonlyTable, err := NewDoltTable(tableName, sch, tbl, db, db.editOpts)
@@ -1115,8 +1057,8 @@ func (db Database) createSqlTable(ctx *sql.Context, tableName string, schemaName
}
root := ws.WorkingRoot()
if UseSearchPath && db.schemaName == "" {
schemaName, err = firstExistingSchemaOnSearchPath(ctx, root)
if search_path.UseSearchPath && db.schemaName == "" {
schemaName, err = search_path.FirstExistingSchemaOnSearchPath(ctx, root)
if err != nil {
return err
}
@@ -1158,49 +1100,6 @@ func (db Database) createSqlTable(ctx *sql.Context, tableName string, schemaName
return db.createDoltTable(ctx, tableName, schemaName, root, doltSch)
}
// firstExistingSchemaOnSearchPath returns the first schema in the search path that exists in the database.
func firstExistingSchemaOnSearchPath(ctx *sql.Context, root doltdb.RootValue) (string, error) {
schemas, err := searchPath(ctx)
if err != nil {
return "", err
}
schemaName := ""
for _, s := range schemas {
var exists bool
schemaName, exists, err = resolveDatabaseSchema(ctx, root, s)
if err != nil {
return "", err
}
if exists {
break
}
}
// No existing schema found in the search_path and none specified in the statement means we can't create the table
if schemaName == "" {
return "", sql.ErrDatabaseNoDatabaseSchemaSelectedCreate.New()
}
return schemaName, nil
}
func hasDatabaseSchema(ctx context.Context, root doltdb.RootValue, schemaName string) (bool, error) {
schemas, err := root.GetDatabaseSchemas(ctx)
if err != nil {
return false, err
}
for _, schema := range schemas {
if strings.EqualFold(schema.Name, schemaName) {
return true, nil
}
}
return false, nil
}
// createIndexedSqlTable is the private version of createSqlTable. It doesn't enforce any table name checks.
func (db Database) createIndexedSqlTable(ctx *sql.Context, tableName string, schemaName string, sch sql.PrimaryKeySchema, idxDef sql.IndexDef, collation sql.CollationID) error {
ws, err := db.GetWorkingSet(ctx)
@@ -1209,8 +1108,8 @@ func (db Database) createIndexedSqlTable(ctx *sql.Context, tableName string, sch
}
root := ws.WorkingRoot()
if UseSearchPath && db.schemaName == "" {
schemaName, err = firstExistingSchemaOnSearchPath(ctx, root)
if search_path.UseSearchPath && db.schemaName == "" {
schemaName, err = search_path.FirstExistingSchemaOnSearchPath(ctx, root)
if err != nil {
return err
}
@@ -1324,7 +1223,7 @@ func (db Database) CreateSchema(ctx *sql.Context, schemaName string) error {
return err
}
_, exists, err := resolveDatabaseSchema(ctx, root, schemaName)
_, exists, err := doltdb.ResolveDatabaseSchema(ctx, root, schemaName)
if err != nil {
return err
}
@@ -1343,23 +1242,6 @@ func (db Database) CreateSchema(ctx *sql.Context, schemaName string) error {
return db.SetRoot(ctx, root)
}
// resolveDatabaseSchema returns the case-sensitive name for the schema requested, if it exists, and an error if
// schemas could not be loaded.
func resolveDatabaseSchema(ctx *sql.Context, root doltdb.RootValue, schemaName string) (string, bool, error) {
schemas, err := root.GetDatabaseSchemas(ctx)
if err != nil {
return "", false, err
}
for _, databaseSchema := range schemas {
if strings.EqualFold(databaseSchema.Name, schemaName) {
return databaseSchema.Name, true, nil
}
}
return "", false, nil
}
// GetSchema implements sql.SchemaDatabase
func (db Database) GetSchema(ctx *sql.Context, schemaName string) (sql.DatabaseSchema, bool, error) {
ws, err := db.GetWorkingSet(ctx)
@@ -1381,7 +1263,7 @@ func (db Database) GetSchema(ctx *sql.Context, schemaName string) (sql.DatabaseS
}
// For a temporary backwards compatibility solution, always pretend the public schema exists.
// Should create it explicitly when we create a new db in future.
// We create it explicitly for new databases.
if strings.EqualFold(schemaName, "public") {
db.schemaName = "public"
return db, true, nil

View File

@@ -23,6 +23,7 @@ import (
"strings"
"sync"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/search_path"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
@@ -66,8 +67,6 @@ type DoltDatabaseProvider struct {
isStandby *bool
}
var UseSearchPath = false
var _ sql.DatabaseProvider = (*DoltDatabaseProvider)(nil)
var _ sql.SchemaDatabaseProvider = (*DoltDatabaseProvider)(nil)
var _ sql.FunctionProvider = (*DoltDatabaseProvider)(nil)
@@ -252,7 +251,7 @@ func (p *DoltDatabaseProvider) Database(ctx *sql.Context, name string) (sql.Data
func (p *DoltDatabaseProvider) SchemaDatabase(ctx *sql.Context, dbName, schemaName string) (sql.DatabaseSchema, bool, error) {
// If search path isn't enabled, this becomes a simple DB lookup on the schema name, which is the qualifier specified
// in the query
if !UseSearchPath {
if !search_path.UseSearchPath {
database, err := p.Database(ctx, schemaName)
return database, err == nil, err
}
@@ -485,7 +484,7 @@ func (p *DoltDatabaseProvider) CreateCollatedDatabase(ctx *sql.Context, name str
}
// If the search path is enabled, we need to create our initial schema object (public is available by default)
if UseSearchPath {
if search_path.UseSearchPath {
workingRoot, err := newEnv.WorkingRoot(ctx)
if err != nil {
return err

View File

@@ -24,6 +24,7 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/dolt/go/libraries/doltcore/env/actions"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/search_path"
"github.com/dolthub/go-mysql-server/sql"
)
@@ -92,6 +93,9 @@ func doDoltAdd(ctx *sql.Context, args []string) (int, error) {
}
// TODO: schema name
if search_path.UseSearchPath {
}
roots, err = actions.StageTables(ctx, roots, doltdb.ToTableNames(apr.Args, doltdb.DefaultSchemaName), !apr.Contains(cli.ForceFlag))
if err != nil {
return 1, err

View File

@@ -0,0 +1,117 @@
// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package search_path
import (
"strings"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/go-mysql-server/sql"
)
// UseSearchPath is a global variable that determines whether or not to use the search path when resolving table names.
// Currently used by Doltgres
var UseSearchPath = false
// ResolveTableWithSearchPath resolves a table name to a table in the root value, searching through the schemas in the
func ResolveTableWithSearchPath(
ctx *sql.Context,
root doltdb.RootValue,
tableName string,
) (string, string, *doltdb.Table, bool, error) {
schemasToSearch, err := SearchPath(ctx)
if err != nil {
return "", "", nil, false, err
}
for _, schemaName := range schemasToSearch {
tablesInSchema, err := root.GetTableNames(ctx, schemaName)
if err != nil {
return "", "", nil, false, err
}
correctedTableName, ok := sql.GetTableNameInsensitive(tableName, tablesInSchema)
if !ok {
continue
}
// TODO: what schema name do we use for system tables?
tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: correctedTableName, Schema: schemaName})
if err != nil {
return "", "", nil, false, err
} else if !ok {
// Should be impossible
return "", "", nil, false, doltdb.ErrTableNotFound
}
return correctedTableName, schemaName, tbl, true, nil
}
return "", "", nil, false, nil
}
// SearchPath returns all the schemas in the search_path setting, with elements like "$user" expanded
func SearchPath(ctx *sql.Context) ([]string, error) {
searchPathVar, err := ctx.GetSessionVariable(ctx, "search_path")
if err != nil {
return nil, err
}
pathElems := strings.Split(searchPathVar.(string), ",")
path := make([]string, len(pathElems))
for i, pathElem := range pathElems {
path[i] = normalizeSearchPathSchema(ctx, pathElem)
}
return path, nil
}
func normalizeSearchPathSchema(ctx *sql.Context, schemaName string) string {
schemaName = strings.Trim(schemaName, " ")
if schemaName == "\"$user\"" {
client := ctx.Session.Client()
return client.User
}
return schemaName
}
// FirstExistingSchemaOnSearchPath returns the first schema in the search path that exists in the database.
func FirstExistingSchemaOnSearchPath(ctx *sql.Context, root doltdb.RootValue) (string, error) {
schemas, err := SearchPath(ctx)
if err != nil {
return "", err
}
schemaName := ""
for _, s := range schemas {
var exists bool
schemaName, exists, err = doltdb.ResolveDatabaseSchema(ctx, root, s)
if err != nil {
return "", err
}
if exists {
break
}
}
// No existing schema found in the search_path and none specified in the statement means we can't create the table
if schemaName == "" {
return "", sql.ErrDatabaseNoDatabaseSchemaSelectedCreate.New()
}
return schemaName, nil
}