Skip to content

Commit e0097f8

Browse files
authored
Merge pull request #2139 from codeaucafe/codeaucafe/1863/fix-validate-create-schema-database-context
#1863: validate database exists before CREATE SCHEMA
2 parents f23b522 + ef94138 commit e0097f8

4 files changed

Lines changed: 192 additions & 1 deletion

File tree

server/analyzer/init.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const (
4646
ruleId_OptimizeFunctions // optimizeFunctions
4747
ruleId_ValidateColumnDefaults // validateColumnDefaults
4848
ruleId_ValidateCreateTable // validateCreateTable
49+
ruleId_ValidateCreateSchema // validateCreateSchema
4950
ruleId_ResolveAlterColumn // resolveAlterColumn
5051
ruleId_ValidateCreateFunction
5152
)
@@ -62,6 +63,7 @@ func Init() {
6263
analyzer.Rule{Id: ruleId_AssignUpdateCasts, Apply: AssignUpdateCasts},
6364
analyzer.Rule{Id: ruleId_AssignTriggers, Apply: AssignTriggers},
6465
analyzer.Rule{Id: ruleId_ValidateCreateFunction, Apply: ValidateCreateFunction},
66+
analyzer.Rule{Id: ruleId_ValidateCreateSchema, Apply: ValidateCreateSchema},
6567
)
6668

6769
analyzer.OnceBeforeDefault = append([]analyzer.Rule{
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2025 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package analyzer
16+
17+
import (
18+
"github.com/dolthub/go-mysql-server/sql"
19+
"github.com/dolthub/go-mysql-server/sql/analyzer"
20+
"github.com/dolthub/go-mysql-server/sql/plan"
21+
"github.com/dolthub/go-mysql-server/sql/transform"
22+
23+
"github.com/dolthub/doltgresql/core"
24+
)
25+
26+
// ValidateCreateSchema validates that CREATE SCHEMA is executed with a valid database context.
27+
// In PostgreSQL, schemas exist within databases, so CREATE SCHEMA requires an active database.
28+
// See: https://github.com/dolthub/doltgresql/issues/1863
29+
func ValidateCreateSchema(
30+
ctx *sql.Context,
31+
a *analyzer.Analyzer,
32+
n sql.Node,
33+
scope *plan.Scope,
34+
sel analyzer.RuleSelector,
35+
qFlags *sql.QueryFlags,
36+
) (sql.Node, transform.TreeIdentity, error) {
37+
cs, ok := n.(*plan.CreateSchema)
38+
if !ok {
39+
return n, transform.SameTree, nil
40+
}
41+
42+
// Check if the current database actually has a working root.
43+
// GetRootFromContext will return sql.ErrDatabaseNotFound if the database
44+
// is not properly initialized.
45+
_, _, err := core.GetRootFromContext(ctx)
46+
if err != nil {
47+
if sql.ErrDatabaseNotFound.Is(err) {
48+
return nil, transform.SameTree, sql.ErrNoDatabaseSelected.New()
49+
}
50+
return nil, transform.SameTree, err
51+
}
52+
53+
return cs, transform.SameTree, nil
54+
}

testing/bats/doltgres.bats

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,35 @@ query_server_for_user_and_pass() {
396396
shift
397397
shift
398398
shift
399-
399+
400400
nativevar PGPASSWORD "$pass" /w
401401
psql -U "$user" -h localhost -p $PORT "$@" $db
402402
}
403+
404+
# Test for https://github.com/dolthub/doltgresql/issues/1863
405+
@test 'doltgres: connection to non-existent database fails' {
406+
start_sql_server
407+
408+
# Connecting to the default postgres database should work
409+
run query_server -c "SELECT 1"
410+
[ "$status" -eq 0 ]
411+
412+
# Connecting to a non-existent database should fail
413+
nativevar PGPASSWORD "password" /w
414+
run psql -U postgres -h localhost -p $PORT -c "SELECT 1" nonexistent_db
415+
[ "$status" -ne 0 ]
416+
[[ "$output" =~ "does not exist" ]] || false
417+
}
418+
419+
@test 'doltgres: CREATE SCHEMA works on valid database' {
420+
start_sql_server
421+
422+
# CREATE SCHEMA should work on a valid database
423+
run query_server -c "CREATE SCHEMA test_schema_bats"
424+
[ "$status" -eq 0 ]
425+
426+
# Verify schema was created
427+
run query_server -c "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'test_schema_bats'"
428+
[ "$status" -eq 0 ]
429+
[[ "$output" =~ "test_schema_bats" ]] || false
430+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2025 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package _go
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"testing"
21+
22+
"github.com/dolthub/go-mysql-server/sql"
23+
"github.com/jackc/pgx/v5"
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
26+
27+
dserver "github.com/dolthub/doltgresql/server"
28+
"github.com/dolthub/doltgresql/servercfg"
29+
"github.com/dolthub/doltgresql/servercfg/cfgdetails"
30+
)
31+
32+
// TestCreateSchemaWithNonExistentDatabase tests that connecting to a non-existent
33+
// database fails with the appropriate error at the connection level.
34+
// This is the PostgreSQL-compliant behavior where database validation happens
35+
// during connection establishment.
36+
//
37+
// NOTE: This test cannot use RunScripts because RunScripts auto-creates the
38+
// database before running assertions. We need to test the connection-level
39+
// failure when connecting to a non-existent database.
40+
//
41+
// See: https://github.com/dolthub/doltgresql/issues/1863
42+
func TestCreateSchemaWithNonExistentDatabase(t *testing.T) {
43+
port, err := sql.GetEmptyPort()
44+
require.NoError(t, err)
45+
46+
// Start server using the same pattern as CreateServerWithPort
47+
controller, err := dserver.RunInMemory(
48+
&servercfg.DoltgresConfig{
49+
DoltgresConfig: cfgdetails.DoltgresConfig{
50+
ListenerConfig: &cfgdetails.DoltgresListenerConfig{
51+
PortNumber: &port,
52+
HostStr: &serverHost,
53+
},
54+
LogLevelStr: &testServerLogLevel,
55+
},
56+
}, dserver.NewListener,
57+
)
58+
require.NoError(t, err)
59+
defer func() {
60+
controller.Stop()
61+
require.NoError(t, controller.WaitForStop())
62+
}()
63+
64+
ctx := context.Background()
65+
66+
t.Run(
67+
"connection to non-existent database fails", func(t *testing.T) {
68+
// Attempt to connect to a database that doesn't exist
69+
connStr := fmt.Sprintf(
70+
"postgres://postgres:password@%s:%d/nonexistent_db?sslmode=disable",
71+
serverHost,
72+
port,
73+
)
74+
_, err := pgx.Connect(ctx, connStr)
75+
76+
require.Error(t, err, "connection should fail when database doesn't exist")
77+
assert.Contains(
78+
t, err.Error(), "does not exist",
79+
"expected 'does not exist' error, got: %v", err,
80+
)
81+
},
82+
)
83+
84+
t.Run(
85+
"connection to existing database succeeds", func(t *testing.T) {
86+
// Verify that connecting to the default "postgres" database works
87+
connStr := fmt.Sprintf("postgres://postgres:password@%s:%d/postgres?sslmode=disable", serverHost, port)
88+
conn, err := pgx.Connect(ctx, connStr)
89+
require.NoError(t, err)
90+
defer conn.Close(ctx)
91+
92+
// Verify we can create a schema on the valid database
93+
_, err = conn.Exec(ctx, "CREATE SCHEMA test_schema_1863")
94+
require.NoError(t, err)
95+
96+
// Verify the schema was created
97+
rows, err := conn.Query(
98+
ctx,
99+
"SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'test_schema_1863'",
100+
)
101+
require.NoError(t, err)
102+
defer rows.Close()
103+
104+
require.True(t, rows.Next(), "expected to find test_schema_1863")
105+
},
106+
)
107+
}

0 commit comments

Comments
 (0)