diff --git a/core/rootvalue.go b/core/rootvalue.go index ed133963ff..7189732fe1 100644 --- a/core/rootvalue.go +++ b/core/rootvalue.go @@ -17,6 +17,7 @@ package core import ( "bytes" "context" + "fmt" "slices" "sort" "strconv" @@ -87,6 +88,62 @@ func (root *RootValue) CreateDatabaseSchema(ctx context.Context, dbSchema schema return root.withStorage(r), nil } +// DropDatabaseSchema implements the interface doltdb.RootValue. +func (root *RootValue) DropDatabaseSchema(ctx context.Context, dbSchema schema.DatabaseSchema) (doltdb.RootValue, error) { + schemas, err := root.st.GetSchemas(ctx) + if err != nil { + return nil, err + } + + found := false + schemaName := dbSchema.Name + for i, s := range schemas { + if strings.EqualFold(s.Name, dbSchema.Name) { + found = true + schemaName = s.Name + // remove this element in the slice + schemas = append(schemas[:i], schemas[i+1:]...) + break + } + } + + if !found { + return nil, fmt.Errorf("No schema with the name %s exists", dbSchema.Name) + } + + // Check for dangling objects in the schema and reject the drop if there are any + danglingObjects := false + root.IterRootObjects(ctx, func(name doltdb.TableName, table doltdb.RootObject) (stop bool, err error) { + if strings.EqualFold(name.Schema, dbSchema.Name) { + danglingObjects = true + } + return false, nil + }) + + // check the tables specifically in addition to root objects. This is just an extra paranoid step to avoid + // removing a schema that still has tables. + tableMap, err := root.getTableMap(ctx, schemaName) + if err != nil { + return nil, err + } + + tableMap.Iter(ctx, func(name string, addr hash.Hash) (bool, error) { + danglingObjects = true + return true, nil + }) + + if danglingObjects { + return nil, fmt.Errorf("cannot drop schema %s because other objects depend on it", dbSchema.Name) + } + + r, err := root.st.SetSchemas(ctx, schemas) + if err != nil { + return nil, err + } + + return root.withStorage(r), nil +} + // validateSchemaForCreate returns an error if a schema with the name given cannot be created func validateSchemaForCreate(existingSchemas []schema.DatabaseSchema, dbSchema schema.DatabaseSchema) error { if dbSchema.Name == "" { diff --git a/go.mod b/go.mod index 14395743bc..4d6139ba20 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( github.com/PuerkitoBio/goquery v1.8.1 github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a github.com/cockroachdb/errors v1.7.5 - github.com/dolthub/dolt/go v0.40.5-0.20260106173753-e3c81824fc90 + github.com/dolthub/dolt/go v0.40.5-0.20260108000424-ed62ee89285b github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 - github.com/dolthub/go-mysql-server v0.20.1-0.20260106170317-759307e19edd + github.com/dolthub/go-mysql-server v0.20.1-0.20260108224859-4dd05696542e github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 - github.com/dolthub/vitess v0.0.0-20251210200925-1d33d416d162 + github.com/dolthub/vitess v0.0.0-20260108222406-f8a2587c4954 github.com/fatih/color v1.13.0 github.com/goccy/go-json v0.10.2 github.com/gogo/protobuf v1.3.2 diff --git a/go.sum b/go.sum index fd9575c625..1b855f2b2b 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,8 @@ github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:I github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo= github.com/dolthub/dolt-mcp v0.2.2 h1:bpROmam74n95uU4EA3BpOIVlTDT0pzeFMBwe/YRq2mI= github.com/dolthub/dolt-mcp v0.2.2/go.mod h1:S++DJ4QWTAXq+0TNzFa7Oq3IhoT456DJHwAINFAHgDQ= -github.com/dolthub/dolt/go v0.40.5-0.20260106173753-e3c81824fc90 h1:lWOjwImXbtzaL34XjFYVCcnniJUt/jjGx9omvu4y5NU= -github.com/dolthub/dolt/go v0.40.5-0.20260106173753-e3c81824fc90/go.mod h1:RrRSqn/03/yMFx3nxUyL1H8YkC60tJwhqq2UyuvIGNA= +github.com/dolthub/dolt/go v0.40.5-0.20260108000424-ed62ee89285b h1:2DTDLbZwltHrge1NSe8EWkv9f+oCs4QCAE1OVVEF1gw= +github.com/dolthub/dolt/go v0.40.5-0.20260108000424-ed62ee89285b/go.mod h1:z5e06zwixH1wM1gOwBsp4yMllZyZ8PnmXKdnjTf1Vcs= github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca h1:BGFz/0OlKIuC6qHIZQbvPapFvdAJkeEyGXWVgL5clmE= github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca/go.mod h1:CoDLfgPqHyBtth0Cp+fi/CmC4R81zJNX4wPjShdZ+Bw= github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= @@ -238,8 +238,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc= github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE= -github.com/dolthub/go-mysql-server v0.20.1-0.20260106170317-759307e19edd h1:AgpawshAhriZokzNFNTEeeojRDXX96t9Z/Vu6px8F+g= -github.com/dolthub/go-mysql-server v0.20.1-0.20260106170317-759307e19edd/go.mod h1:NjewWKoa5bVSLdKwL7fg7eAfrcIxDybWUKoWEHWRTw4= +github.com/dolthub/go-mysql-server v0.20.1-0.20260108224859-4dd05696542e h1:OKp5W4bgSJIAKESEtBtyOWF85HwvE7UIe+T204v5EN0= +github.com/dolthub/go-mysql-server v0.20.1-0.20260108224859-4dd05696542e/go.mod h1:2bgnal91FRGVdJyirmUSMCd1Tp3Ci8dTDsrJWkTp/hs= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= @@ -250,8 +250,8 @@ github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1 h1:GY17cGA4 github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1/go.mod h1:qnrZP3/1slFl2Bq5yw38HLOsArZareGwdpEceriblLc= github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 h1:JWkKRE4EHUcEVQCMRBej8DYxjYjRz/9MdF/NNQh0o70= github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216/go.mod h1:e/FIZVvT2IR53HBCAo41NjqgtEnjMJGKca3Y/dAmZaA= -github.com/dolthub/vitess v0.0.0-20251210200925-1d33d416d162 h1:6RW2VpUs/cUFdvk4mXSmJfQZLs9wJABVjke3CHGJBcs= -github.com/dolthub/vitess v0.0.0-20251210200925-1d33d416d162/go.mod h1:FLWqdXsAeeBQyFwDjmBVu0GnbjI2MKeRf3tRVdJEKlI= +github.com/dolthub/vitess v0.0.0-20260108222406-f8a2587c4954 h1:VN2ZjnYPyxcAN/XetvcdumFbI2Ad/Gb47Qwdo9REY3A= +github.com/dolthub/vitess v0.0.0-20260108222406-f8a2587c4954/go.mod h1:FLWqdXsAeeBQyFwDjmBVu0GnbjI2MKeRf3tRVdJEKlI= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= diff --git a/server/ast/drop_database.go b/server/ast/drop_database.go index ea47dd0044..7558b15375 100644 --- a/server/ast/drop_database.go +++ b/server/ast/drop_database.go @@ -31,8 +31,9 @@ func nodeDropDatabase(ctx *Context, node *tree.DropDatabase) (*vitess.DBDDL, err return nil, errors.Errorf("WITH ( FORCE ) is not yet supported") } return &vitess.DBDDL{ - Action: vitess.DropStr, - DBName: string(node.Name), - IfExists: node.IfExists, + Action: vitess.DropStr, + SchemaOrDatabase: "database", + DBName: string(node.Name), + IfExists: node.IfExists, }, nil } diff --git a/server/ast/drop_schema.go b/server/ast/drop_schema.go index 85e4de6081..6e6cfc0ea6 100644 --- a/server/ast/drop_schema.go +++ b/server/ast/drop_schema.go @@ -17,6 +17,8 @@ package ast import ( vitess "github.com/dolthub/vitess/go/vt/sqlparser" + "github.com/dolthub/doltgresql/server/auth" + "github.com/dolthub/doltgresql/postgres/parser/sem/tree" ) @@ -27,5 +29,26 @@ func nodeDropSchema(ctx *Context, node *tree.DropSchema) (vitess.Statement, erro return nil, nil } - return NotYetSupportedError("DROP SCHEMA is not yet supported") + if len(node.Names) > 1 { + return NotYetSupportedError("DROP SCHEMA with multiple schema names is not yet supported.") + } + + if node.DropBehavior == tree.DropCascade { + return NotYetSupportedError("DROP SCHEMA with CASCADE behavior is not yet supported.") + } + + schemaName := node.Names[0] + + return &vitess.DBDDL{ + Action: vitess.DropStr, + SchemaOrDatabase: "schema", + DBName: schemaName, + CharsetCollate: nil, + IfExists: node.IfExists, + Auth: vitess.AuthInformation{ + AuthType: auth.AuthType_DELETE, + TargetType: auth.AuthTargetType_SchemaIdentifiers, + TargetNames: []string{"", schemaName}, + }, + }, nil } diff --git a/server/ast/no_op.go b/server/ast/no_op.go index 3200cc5ebd..11de1e7fea 100755 --- a/server/ast/no_op.go +++ b/server/ast/no_op.go @@ -53,5 +53,5 @@ func NotYetSupportedError(errorMsg string) (vitess.Statement, error) { return NewNoOp(errorMsg), nil } - return nil, errors.New(errorMsg) + return nil, errors.Errorf(errorMsg + " Please file an issue at https://github.com/dolthub/doltgresql/issues") } diff --git a/testing/generation/command_docs/output/drop_schema_test.go b/testing/generation/command_docs/output/drop_schema_test.go index d6fbc35f01..acf67735c5 100644 --- a/testing/generation/command_docs/output/drop_schema_test.go +++ b/testing/generation/command_docs/output/drop_schema_test.go @@ -18,16 +18,16 @@ import "testing" func TestDropSchema(t *testing.T) { tests := []QueryParses{ - Parses("DROP SCHEMA name"), - Parses("DROP SCHEMA IF EXISTS name"), + Converts("DROP SCHEMA name"), + Converts("DROP SCHEMA IF EXISTS name"), Parses("DROP SCHEMA name , name"), Parses("DROP SCHEMA IF EXISTS name , name"), Parses("DROP SCHEMA name CASCADE"), Parses("DROP SCHEMA IF EXISTS name CASCADE"), Parses("DROP SCHEMA name , name CASCADE"), Parses("DROP SCHEMA IF EXISTS name , name CASCADE"), - Parses("DROP SCHEMA name RESTRICT"), - Parses("DROP SCHEMA IF EXISTS name RESTRICT"), + Converts("DROP SCHEMA name RESTRICT"), + Converts("DROP SCHEMA IF EXISTS name RESTRICT"), Parses("DROP SCHEMA name , name RESTRICT"), Parses("DROP SCHEMA IF EXISTS name , name RESTRICT"), } diff --git a/testing/go/auth_quick_test.go b/testing/go/auth_quick_test.go index 94b16475a2..958a19d8c9 100644 --- a/testing/go/auth_quick_test.go +++ b/testing/go/auth_quick_test.go @@ -104,13 +104,13 @@ func TestAuthQuick(t *testing.T) { "CREATE SCHEMA newsch;", }, }, - { // This isn't supported yet, but it is supposed to fail since tester is not an owner + { Queries: []string{ "GRANT CREATE ON DATABASE postgres TO tester;", "CREATE SCHEMA newsch;", "DROP SCHEMA newsch;", }, - ExpectedErr: "not yet supported", + ExpectedErr: "permission denied for schema", }, { Queries: []string{ diff --git a/testing/go/schemas_test.go b/testing/go/schemas_test.go index aa25ca68a2..4775b56ffe 100755 --- a/testing/go/schemas_test.go +++ b/testing/go/schemas_test.go @@ -869,11 +869,80 @@ var SchemaTests = []ScriptTest{ }, }, }, + { + Name: "drop schema", + SetUpScript: []string{ + "CREATE SCHEMA dropme", + `CREATE schema "hasTables"`, + "CREATE TABLE hasTables.t1 (pk BIGINT PRIMARY KEY, v1 BIGINT);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "Show schemas", + Expected: []sql.Row{ + {"dolt"}, + {"dropme"}, + {"hasTables"}, + {"pg_catalog"}, + {"public"}, + {"information_schema"}, + }, + }, + { + Query: "DROP SCHEMA dropme;", + Expected: []sql.Row{}, + }, + { + Query: "Show schemas", + Expected: []sql.Row{ + {"dolt"}, + {"hasTables"}, + {"pg_catalog"}, + {"public"}, + {"information_schema"}, + }, + }, + { + Query: "DROP SCHEMA dropme;", + ExpectedErr: "database schema not found", + }, + { + Query: "drop schema if exists dropme;", + }, + { + Query: "DROP SCHEMA hasTables;", + ExpectedErr: "cannot drop schema hastables because other objects depend on it", + }, + { + Skip: true, // not implemented yet + Query: "drop schema hasTables cascade;", + }, + { + Query: "create schema hastype;", + }, + { + Query: "create type hastype.mytype as enum('a', 'b', 'c');", + }, + { + Query: "DROP SCHEMA hastype;", + ExpectedErr: "cannot drop schema hastype because other objects depend on it", + }, + { + Query: "create schema hassequence;", + }, + { + Query: "create sequence hassequence.myseq start 1 increment 1;", + }, + { + Query: "DROP SCHEMA hassequence;", + ExpectedErr: "cannot drop schema hassequence because other objects depend on it", + }, + }, + }, // More tests: // * alter table statements, when they work better // * AS OF (when supported) // * revision qualifiers - // * drop schema // * more statement types // * INSERT INTO schema1 SELECT FROM schema2 // * Subqueries accessing different schemas in the same SELECT