## Code Analysis ### Replacement removes the existing function before construction completes `server/node/create_function.go:143-171` ```go funcID := id.NewFunction(schemaName, c.FunctionName, inputParamTypes...) replaced := c.Replace && funcCollection.HasFunction(ctx, funcID) if replaced { if err = funcCollection.DropFunction(ctx, funcID); err != nil { return nil, err } } if len(c.ExtensionName) > 0 { if _, err = extensions.GetFunction(c.ExtensionName, c.ExtensionSymbol); err != nil { return nil, err } } err = funcCollection.AddFunction(ctx, functions.Function{ ID: funcID, Definition: c.Definition, Operations: c.Statements, }) if err != nil { return nil, err } ``` The replacement path drops the existing function before extension validation and `AddFunction` complete. The later error returns do not restore the dropped function, so a failed replacement can leave no valid original routine to call. ### Routine replacement is expected to preserve callable behavior and ACL `testing/go/alter_default_privileges_test.go:536-551` ```go Name: "CREATE OR REPLACE FUNCTION preserves existing ACL instead of applying new defaults", ... {Query: `CREATE OR REPLACE FUNCTION replace_existing() RETURNS INT AS $$ BEGIN RETURN 2; END; $$ LANGUAGE plpgsql;`}, {Query: `SELECT replace_existing();`, Username: "replace_reader", Expected: []sql.Row{{2}}}, {Query: `SELECT replace_existing();`, Username: "replace_new_reader", ExpectedErr: "denied"}, ``` The focused repository coverage requires the replaced routine to remain callable by its existing grantee while the newly granted default does not alter that ACL. ### Invalid output value can panic during result encoding `server/functions/int4.go:61-69` ```go var int4out = framework.Function1{ Name: "int4out", Return: pgtypes.Cstring, Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32}, Strict: true, Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) { return strconv.FormatInt(int64(val.(int32)), 10), nil }, } ``` The unchecked `val.(int32)` assertion can panic when the malformed replacement leaves an incompatible result value. The handler records such recovered panics as server errors rather than turning the invalid value into a normal database response (`server/doltgres_handler.go:697-705`). ### Observed execution #### Catalog readback ```text privilege26_retry|keep_original|SELECT # A malformed CREATE OR REPLACE was accepted, then invocation caused a server panic. # The original routine result 42 was observed before replacement. # The malformed replacement did not preserve callable behavior. ``` The readback captured the routine's remaining catalog privilege after the malformed replacement; the accompanying test evidence records the successful pre-replacement result and the failed post-replacement invocation. #### Server log ```text time="2026-10-09T21:00:17Z" level=warning msg="error running query" connectionID=112 error="DoltgresHandler caught panic: interface conversion: interface {} is []types.RecordValue, not int32 github.com/dolthub/doltgresql/server.(*DoltgresHandler).resultForDefaultIter.func1 /workspaces/doltgresql/server/doltgres_handler.go:704 github.com/dolthub/doltgresql/server/functions.init.func214 /workspaces/doltgresql/server/functions/int4.go:68 ``` ### Result The source ordering and captured result-encoding stack support the reported failure: a malformed routine replacement was accepted, the original callable behavior was not preserved, and a subsequent invocation reached a server panic instead of returning a clean database error.