diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index 43b46ce41e05..381a62f3ce4c 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -458,10 +458,6 @@ func (s *Server) newConnExecutor( mon: &sessionRootMon, sessionMon: &sessionMon, sessionData: sd, - prepStmtsNamespace: prepStmtNamespace{ - prepStmts: make(map[string]prepStmtEntry), - portals: make(map[string]portalEntry), - }, state: txnState{ mon: &txnMon, connCtx: ctx, @@ -684,10 +680,8 @@ func (ex *connExecutor) close(ctx context.Context, closeType closeType) { } if closeType != panicClose { - // Close all statements and prepared portals by first unifying the namespaces - // and the closing what remains. - ex.commitPrepStmtNamespace(ctx) - ex.prepStmtsNamespace.resetTo(ctx, &prepStmtNamespace{}) + // Close all statements and prepared portals. + ex.prepStmtsNamespace.clear(ctx) } if ex.sessionTracing.Enabled() { @@ -785,17 +779,6 @@ type connExecutor struct { // // Set via setTxnRewindPos(). txnRewindPos CmdPos - - // prepStmtsNamespaceAtTxnRewindPos is a snapshot of the prep stmts/portals - // (ex.prepStmtsNamespace) before processing the command at position - // txnRewindPos. - // Here's the deal: prepared statements are not transactional, but they do - // need to interact properly with automatic retries (i.e. rewinding the - // command buffer). When doing a rewind, we need to be able to restore the - // prep stmts as they were. We do this by taking a snapshot every time - // txnRewindPos is advanced. Prepared statements are shared between the two - // collections, but these collections are periodically reconciled. - prepStmtsNamespaceAtTxnRewindPos prepStmtNamespace } // sessionData contains the user-configurable connection variables. @@ -836,8 +819,13 @@ type connExecutor struct { parallelizeQueue ParallelizeQueue // prepStmtNamespace contains the prepared statements and portals that the - // session currently has access to. - prepStmtsNamespace prepStmtNamespace + // session currently has access to. The structure can be checkpointed and + // reverted, which is important because prepared statements are not + // transactional, but they do need to interact properly with automatic + // retries (i.e. rewinding the command buffer). When doing a rewind, we need + // to be able to restore the prep stmts as they were. We do this by taking a + // snapshot every time txnRewindPos is advanced. + prepStmtsNamespace checkpointedPrepStmtNamespace // mu contains of all elements of the struct that can be changed // after initialization, and may be accessed from another thread. @@ -1053,7 +1041,7 @@ func (ex *connExecutor) run( // ExecPortal is handled like ExecStmt, except that the placeholder info // is taken from the portal. - portal, ok := ex.prepStmtsNamespace.portals[tcmd.Name] + portal, ok := ex.prepStmtsNamespace.getPortal(tcmd.Name) if !ok { err := pgerror.NewErrorf( pgerror.CodeInvalidCursorNameError, "unknown portal %q", tcmd.Name) @@ -1226,7 +1214,7 @@ func (ex *connExecutor) run( return err } case rewind: - ex.rewindPrepStmtNamespace(ex.Ctx()) + ex.prepStmtsNamespace.rewind(ex.Ctx()) advInfo.rewCap.rewindAndUnlock(ex.Ctx()) case stayInPlace: // Nothing to do. The same statement will be executed again. @@ -1298,7 +1286,7 @@ func (ex *connExecutor) updateTxnRewindPosMaybe( case ExecStmt: canAdvance = ex.stmtDoesntNeedRetry(tcmd.Stmt) case ExecPortal: - portal := ex.prepStmtsNamespace.portals[tcmd.Name] + portal, _ := ex.prepStmtsNamespace.getPortal(tcmd.Name) canAdvance = ex.stmtDoesntNeedRetry(portal.Stmt.Statement) case PrepareStmt: canAdvance = true @@ -1340,7 +1328,7 @@ func (ex *connExecutor) setTxnRewindPos(ctx context.Context, pos CmdPos) { } ex.extraTxnState.txnRewindPos = pos ex.stmtBuf.ltrim(ctx, pos) - ex.commitPrepStmtNamespace(ctx) + ex.prepStmtsNamespace.snapshot(ctx) } // stmtDoesntNeedRetry returns true if the given statement does not need to be @@ -2075,6 +2063,7 @@ func (sc *StatementCounters) incrementCount(stmt tree.Statement) { // connExPrepStmtsAccessor is an implementation of preparedStatementsAccessor // that gives access to a connExecutor's prepared statements. +// TODO(nvanbenschoten): replace this. type connExPrepStmtsAccessor struct { ex *connExecutor } @@ -2083,7 +2072,7 @@ var _ preparedStatementsAccessor = connExPrepStmtsAccessor{} // Get is part of the preparedStatementsAccessor interface. func (ps connExPrepStmtsAccessor) Get(name string) (*PreparedStatement, bool) { - s, ok := ps.ex.prepStmtsNamespace.prepStmts[name] + s, ok := ps.ex.prepStmtsNamespace.getPrepStmt(name) return s.PreparedStatement, ok } @@ -2093,16 +2082,13 @@ func (ps connExPrepStmtsAccessor) Delete(ctx context.Context, name string) bool if !ok { return false } - ps.ex.deletePreparedStmt(ctx, name) + ps.ex.prepStmtsNamespace.delPrepStmt(ctx, name) return true } // DeleteAll is part of the preparedStatementsAccessor interface. func (ps connExPrepStmtsAccessor) DeleteAll(ctx context.Context) { - ps.ex.prepStmtsNamespace = prepStmtNamespace{ - prepStmts: make(map[string]prepStmtEntry), - portals: make(map[string]portalEntry), - } + ps.ex.prepStmtsNamespace.clear(ctx) } // contextStatementKey is an empty type for the handle associated with the diff --git a/pkg/sql/conn_executor_exec.go b/pkg/sql/conn_executor_exec.go index e0f0a3506e56..53746fb0714f 100644 --- a/pkg/sql/conn_executor_exec.go +++ b/pkg/sql/conn_executor_exec.go @@ -284,7 +284,7 @@ func (ex *connExecutor) execStmtInOpenState( // This is handling the SQL statement "PREPARE". See execPrepare for // handling of the protocol-level command for preparing statements. name := s.Name.String() - if _, ok := ex.prepStmtsNamespace.prepStmts[name]; ok { + if _, ok := ex.prepStmtsNamespace.getPrepStmt(name); ok { err := pgerror.NewErrorf( pgerror.CodeDuplicatePreparedStatementError, "prepared statement %q already exists", name, @@ -304,7 +304,7 @@ func (ex *connExecutor) execStmtInOpenState( // Replace the `EXECUTE foo` statement with the prepared statement, and // continue execution below. name := s.Name.String() - ps, ok := ex.prepStmtsNamespace.prepStmts[name] + ps, ok := ex.prepStmtsNamespace.getPrepStmt(name) if !ok { err := pgerror.NewErrorf( pgerror.CodeInvalidSQLStatementNameError, diff --git a/pkg/sql/conn_executor_prepare.go b/pkg/sql/conn_executor_prepare.go index a3082f571786..62bc73ac114d 100644 --- a/pkg/sql/conn_executor_prepare.go +++ b/pkg/sql/conn_executor_prepare.go @@ -20,93 +20,306 @@ import ( "strconv" "unsafe" + "github.com/lib/pq/oid" + "github.com/pkg/errors" + "github.com/cockroachdb/cockroach/pkg/internal/client" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/util/fsm" "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/lib/pq/oid" - "github.com/pkg/errors" ) -type prepStmtNamespace struct { - // prepStmts contains the prepared statements currently available on the - // session. - prepStmts map[string]prepStmtEntry - // portals contains the portals currently available on the session. - portals map[string]portalEntry -} - type prepStmtEntry struct { *PreparedStatement portals map[string]struct{} } -func (pe *prepStmtEntry) copy() prepStmtEntry { +func (ps *prepStmtEntry) copy() prepStmtEntry { cpy := prepStmtEntry{} - cpy.PreparedStatement = pe.PreparedStatement - cpy.portals = make(map[string]struct{}) - for pname := range pe.portals { + cpy.PreparedStatement = ps.PreparedStatement + cpy.portals = make(map[string]struct{}, len(ps.portals)) + for pname := range ps.portals { cpy.portals[pname] = struct{}{} } return cpy } +type prepStmtEntries struct { + // unnamed special-cases access to the unnamed prepared statement. + unnamed struct { + prepStmtEntry + set bool + } + // m contains all other prepared statements. + m map[string]prepStmtEntry +} + +func (pse *prepStmtEntries) get(name string) (prepStmtEntry, bool) { + if name == "" { + return pse.unnamed.prepStmtEntry, pse.unnamed.set + } + ps, ok := pse.m[name] + return ps, ok +} + +func (pse *prepStmtEntries) add(name string, ps prepStmtEntry) { + if name == "" { + pse.unnamed.prepStmtEntry = ps + pse.unnamed.set = true + return + } + if pse.m == nil { + pse.m = make(map[string]prepStmtEntry) + } + pse.m[name] = ps +} + +func (pse *prepStmtEntries) del(name string) (prepStmtEntry, bool) { + if name == "" { + if !pse.unnamed.set { + return prepStmtEntry{}, false + } + ps := pse.unnamed.prepStmtEntry + pse.unnamed.prepStmtEntry = prepStmtEntry{} + pse.unnamed.set = false + return ps, true + } + ps, ok := pse.m[name] + if !ok { + return prepStmtEntry{}, false + } + delete(pse.m, name) + return ps, true +} + +func (pse *prepStmtEntries) copy() prepStmtEntries { + cpy := prepStmtEntries{} + cpy.unnamed = pse.unnamed + if len(pse.m) > 0 { + cpy.m = make(map[string]prepStmtEntry, len(pse.m)) + for name, ps := range pse.m { + cpy.m[name] = ps.copy() + } + } + return cpy +} + type portalEntry struct { *PreparedPortal psName string } -// resetTo resets a namespace to equate another one (`to`). Prep stmts and portals -// that are present in ns but not in to are deallocated. -// -// A (pointer to) empty `to` can be passed in to deallocate everything. -func (ns *prepStmtNamespace) resetTo(ctx context.Context, to *prepStmtNamespace) { - for name, ps := range ns.prepStmts { - bps, ok := to.prepStmts[name] - // If the prepared statement didn't exist before (including if a statement - // with the same name existed, but it was different), close it. - if !ok || bps.PreparedStatement != ps.PreparedStatement { - ps.close(ctx) +type portalEntries struct { + // unnamed special-cases access to the unnamed portal. + unnamed struct { + portalEntry + set bool + } + // m contains all other portals. + m map[string]portalEntry +} + +func (pe *portalEntries) get(name string) (portalEntry, bool) { + if name == "" { + return pe.unnamed.portalEntry, pe.unnamed.set + } + p, ok := pe.m[name] + return p, ok +} + +func (pe *portalEntries) add(name string, p portalEntry) { + if name == "" { + pe.unnamed.portalEntry = p + pe.unnamed.set = true + return + } + if pe.m == nil { + pe.m = make(map[string]portalEntry) + } + pe.m[name] = p +} + +func (pe *portalEntries) del(name string) (portalEntry, bool) { + if name == "" { + if !pe.unnamed.set { + return portalEntry{}, false } + p := pe.unnamed.portalEntry + pe.unnamed.portalEntry = portalEntry{} + pe.unnamed.set = false + return p, true } - for name, p := range ns.portals { - bp, ok := to.portals[name] - // If the prepared statement didn't exist before (including if a statement - // with the same name existed, but it was different), close it. - if !ok || bp.PreparedPortal != p.PreparedPortal { - p.close(ctx) + p, ok := pe.m[name] + if !ok { + return portalEntry{}, false + } + delete(pe.m, name) + return p, true +} + +func (pe *portalEntries) copy() portalEntries { + cpy := portalEntries{} + cpy.unnamed = pe.unnamed + if len(pe.m) > 0 { + cpy.m = make(map[string]portalEntry, len(pe.m)) + for name, p := range pe.m { + cpy.m[name] = p } } - *ns = to.copy() + return cpy +} + +type prepStmtNamespace struct { + prepStmts prepStmtEntries + portals portalEntries } func (ns *prepStmtNamespace) copy() prepStmtNamespace { var cpy prepStmtNamespace - cpy.prepStmts = make(map[string]prepStmtEntry) - for name, psEntry := range ns.prepStmts { - cpy.prepStmts[name] = psEntry.copy() + cpy.prepStmts = ns.prepStmts.copy() + cpy.portals = ns.portals.copy() + return cpy +} + +// clear clears all prepared statements and portals from ns. It closes any +// prepared statement or portal that isn't also in dontClose. +func (ns *prepStmtNamespace) clear(ctx context.Context, dontClose prepStmtNamespace) { + // If the unnamed prepared statement isn't the same, close it. + if ns.prepStmts.unnamed.set { + if ns.prepStmts.unnamed.PreparedStatement != dontClose.prepStmts.unnamed.PreparedStatement { + ns.prepStmts.unnamed.close(ctx) + } + } + // Close each other prepared statement from ns that differs. + for name, ps := range ns.prepStmts.m { + notPS, ok := dontClose.prepStmts.m[name] + if !ok || ps.PreparedStatement != notPS.PreparedStatement { + ps.close(ctx) + } } - cpy.portals = make(map[string]portalEntry) - for name, p := range ns.portals { - cpy.portals[name] = p + // If the unnamed portal isn't the same, close it. + if ns.portals.unnamed.set { + if ns.portals.unnamed.PreparedPortal != dontClose.portals.unnamed.PreparedPortal { + ns.portals.unnamed.close(ctx) + } } - return cpy + // Close each other prepared statement from ns that differs. + for name, p := range ns.portals.m { + notP, ok := dontClose.portals.m[name] + if !ok || p.PreparedPortal != notP.PreparedPortal { + p.close(ctx) + } + } + *ns = prepStmtNamespace{} +} + +// checkpointedPrepStmtNamespace is a prepared statement namespace that is +// capable of taking a snapshot and restoring to that snapshot. +// checkpointedPrepStmtNamespace's zero value can be used directly. +type checkpointedPrepStmtNamespace struct { + // checkpoint is the checkpointed prepStmtNamespace. It is treated as + // immutable and is only modified when replaced by workspace. + checkpoint prepStmtNamespace + // workspace is a prepStmtNamespace that is used for modifications. When + // in use, it replaces checkpoint when the namespace is checkpointed and + // it is discarded when the namespace is rewind. + workspace struct { + prepStmtNamespace + inUse bool + } +} + +// snapshot captures a snapshot of the prepared statement namespace. Future +// calls to rewind will return the namespace to this state. +func (cns *checkpointedPrepStmtNamespace) snapshot(ctx context.Context) { + if !cns.workspace.inUse { + return + } + // Clear the checkpoint and replace with the workspace. + cns.checkpoint.clear(ctx, cns.workspace.prepStmtNamespace) + cns.checkpoint = cns.workspace.prepStmtNamespace + cns.workspace.prepStmtNamespace = prepStmtNamespace{} + cns.workspace.inUse = false +} + +// Rewind reverts the namespace to the state of the last snapshot. +func (cns *checkpointedPrepStmtNamespace) rewind(ctx context.Context) { + if !cns.workspace.inUse { + return + } + // Clear the workspace. + cns.workspace.clear(ctx, cns.checkpoint) + cns.workspace.prepStmtNamespace = prepStmtNamespace{} + cns.workspace.inUse = false +} + +// clear clears all prepared statements in the namespace. +func (cns *checkpointedPrepStmtNamespace) clear(ctx context.Context) { + // Reconcile the namespaces and clear. + cns.snapshot(ctx) + cns.checkpoint.clear(ctx, prepStmtNamespace{}) +} + +func (cns *checkpointedPrepStmtNamespace) ns(mut bool) *prepStmtNamespace { + if cns.workspace.inUse { + return &cns.workspace.prepStmtNamespace + } + if mut { + cns.workspace.prepStmtNamespace = cns.checkpoint.copy() + cns.workspace.inUse = true + return &cns.workspace.prepStmtNamespace + } + return &cns.checkpoint +} + +func (cns *checkpointedPrepStmtNamespace) getPrepStmt(name string) (prepStmtEntry, bool) { + return cns.ns(false).prepStmts.get(name) +} + +func (cns *checkpointedPrepStmtNamespace) getMutablePrepStmt(name string) (prepStmtEntry, bool) { + return cns.ns(true).prepStmts.get(name) +} + +func (cns *checkpointedPrepStmtNamespace) addPrepStmt(name string, p prepStmtEntry) { + cns.ns(true).prepStmts.add(name, p) } -// commitPrepStmtNamespace deallocates everything in -// prepStmtsNamespaceAtTxnRewindPos that's not part of prepStmtsNamespace. -func (ex *connExecutor) commitPrepStmtNamespace(ctx context.Context) { - ex.extraTxnState.prepStmtsNamespaceAtTxnRewindPos.resetTo( - ctx, &ex.prepStmtsNamespace) +func (cns *checkpointedPrepStmtNamespace) delPrepStmt(ctx context.Context, name string) { + ps, ok := cns.ns(true).prepStmts.del(name) + if !ok { + return + } + ps2, ok := cns.checkpoint.prepStmts.get(name) + if !ok || ps.PreparedStatement != ps2.PreparedStatement { + ps.close(ctx) + } + for portalName := range ps.portals { + cns.delPortal(ctx, portalName) + } +} + +func (cns *checkpointedPrepStmtNamespace) getPortal(name string) (portalEntry, bool) { + return cns.ns(false).portals.get(name) } -// commitPrepStmtNamespace deallocates everything in prepStmtsNamespace that's -// not part of prepStmtsNamespaceAtTxnRewindPos. -func (ex *connExecutor) rewindPrepStmtNamespace(ctx context.Context) { - ex.prepStmtsNamespace.resetTo( - ctx, &ex.extraTxnState.prepStmtsNamespaceAtTxnRewindPos) +func (cns *checkpointedPrepStmtNamespace) addPortal(name string, p portalEntry) { + cns.ns(true).portals.add(name, p) +} + +func (cns *checkpointedPrepStmtNamespace) delPortal(ctx context.Context, name string) { + p, ok := cns.ns(true).portals.del(name) + if !ok { + return + } + p2, ok := cns.checkpoint.portals.get(name) + if !ok || p.PreparedPortal != p2.PreparedPortal { + p.close(ctx) + } + if ps, ok := cns.getMutablePrepStmt(p.psName); ok { + delete(ps.portals, name) + } } // addPreparedStmt creates a new PreparedStatement with the provided name using @@ -118,7 +331,7 @@ func (ex *connExecutor) rewindPrepStmtNamespace(ctx context.Context) { func (ex *connExecutor) addPreparedStmt( ctx context.Context, name string, stmt Statement, placeholderHints tree.PlaceholderTypes, ) (*PreparedStatement, error) { - if _, ok := ex.prepStmtsNamespace.prepStmts[name]; ok { + if _, ok := ex.prepStmtsNamespace.getPrepStmt(name); ok { panic(fmt.Sprintf("prepared statement already exists: %q", name)) } @@ -131,10 +344,10 @@ func (ex *connExecutor) addPreparedStmt( if err := prepared.memAcc.Grow(ctx, int64(len(name))); err != nil { return nil, err } - ex.prepStmtsNamespace.prepStmts[name] = prepStmtEntry{ + ex.prepStmtsNamespace.addPrepStmt(name, prepStmtEntry{ PreparedStatement: prepared, portals: make(map[string]struct{}), - } + }) return prepared, nil } @@ -150,7 +363,7 @@ func (ex *connExecutor) addPortal( qargs tree.QueryArguments, outFormats []pgwirebase.FormatCode, ) error { - if _, ok := ex.prepStmtsNamespace.portals[portalName]; ok { + if _, ok := ex.prepStmtsNamespace.getPortal(portalName); ok { panic(fmt.Sprintf("portal already exists: %q", portalName)) } @@ -159,43 +372,16 @@ func (ex *connExecutor) addPortal( return err } - ex.prepStmtsNamespace.portals[portalName] = portalEntry{ + ex.prepStmtsNamespace.addPortal(portalName, portalEntry{ PreparedPortal: portal, psName: psName, - } - ex.prepStmtsNamespace.prepStmts[psName].portals[portalName] = struct{}{} - return nil -} - -func (ex *connExecutor) deletePreparedStmt(ctx context.Context, name string) { - psEntry, ok := ex.prepStmtsNamespace.prepStmts[name] - if !ok { - return - } - // If the prepared statement only exists in prepStmtsNamespace, it's up to us - // to close it. - baseP, inBase := ex.extraTxnState.prepStmtsNamespaceAtTxnRewindPos.prepStmts[name] - if !inBase || (baseP.PreparedStatement != psEntry.PreparedStatement) { - psEntry.close(ctx) - } - for portalName := range psEntry.portals { - ex.deletePortal(ctx, portalName) - } - delete(ex.prepStmtsNamespace.prepStmts, name) -} - -func (ex *connExecutor) deletePortal(ctx context.Context, name string) { - portalEntry, ok := ex.prepStmtsNamespace.portals[name] + }) + ps, ok := ex.prepStmtsNamespace.getMutablePrepStmt(psName) if !ok { - return - } - // If the portal only exists in prepStmtsNamespace, it's up to us to close it. - baseP, inBase := ex.extraTxnState.prepStmtsNamespaceAtTxnRewindPos.portals[name] - if !inBase || (baseP.PreparedPortal != portalEntry.PreparedPortal) { - portalEntry.close(ctx) + panic(fmt.Sprintf("prepared statement does not exist: %q", psName)) } - delete(ex.prepStmtsNamespace.portals, name) - delete(ex.prepStmtsNamespace.prepStmts[portalEntry.psName].portals, name) + ps.portals[portalName] = struct{}{} + return nil } func (ex *connExecutor) execPrepare( @@ -206,9 +392,9 @@ func (ex *connExecutor) execPrepare( return eventNonRetriableErr{IsCommit: fsm.False}, eventNonRetriableErrPayload{err: err} } - // The anonymous statement can be overwritter. + // The anonymous statement can be overwritten. if parseCmd.Name != "" { - if _, ok := ex.prepStmtsNamespace.prepStmts[parseCmd.Name]; ok { + if _, ok := ex.prepStmtsNamespace.getPrepStmt(parseCmd.Name); ok { err := pgerror.NewErrorf( pgerror.CodeDuplicatePreparedStatementError, "prepared statement %q already exists", parseCmd.Name, @@ -217,7 +403,7 @@ func (ex *connExecutor) execPrepare( } } else { // Deallocate the unnamed statement, if it exists. - ex.deletePreparedStmt(ctx, "") + ex.prepStmtsNamespace.delPrepStmt(ctx, "") } ps, err := ex.addPreparedStmt( @@ -406,16 +592,16 @@ func (ex *connExecutor) execBind( portalName := bindCmd.PortalName // The unnamed portal can be freely overwritten. if portalName != "" { - if _, ok := ex.prepStmtsNamespace.portals[portalName]; ok { + if _, ok := ex.prepStmtsNamespace.getPortal(portalName); ok { return retErr(pgerror.NewErrorf( pgerror.CodeDuplicateCursorError, "portal %q already exists", portalName)) } } else { // Deallocate the unnamed portal, if it exists. - ex.deletePortal(ctx, "") + ex.prepStmtsNamespace.delPortal(ctx, "") } - ps, ok := ex.prepStmtsNamespace.prepStmts[bindCmd.PreparedStatementName] + ps, ok := ex.prepStmtsNamespace.getPrepStmt(bindCmd.PreparedStatementName) if !ok { return retErr(pgerror.NewErrorf( pgerror.CodeInvalidSQLStatementNameError, @@ -527,7 +713,7 @@ func (ex *connExecutor) execDelPrepStmt( ) (fsm.Event, fsm.EventPayload) { switch delCmd.Type { case pgwirebase.PrepareStatement: - _, ok := ex.prepStmtsNamespace.prepStmts[delCmd.Name] + _, ok := ex.prepStmtsNamespace.getPrepStmt(delCmd.Name) if !ok { // The spec says "It is not an error to issue Close against a nonexistent // statement or portal name". See @@ -535,13 +721,13 @@ func (ex *connExecutor) execDelPrepStmt( break } - ex.deletePreparedStmt(ctx, delCmd.Name) + ex.prepStmtsNamespace.delPrepStmt(ctx, delCmd.Name) case pgwirebase.PreparePortal: - _, ok := ex.prepStmtsNamespace.portals[delCmd.Name] + _, ok := ex.prepStmtsNamespace.getPortal(delCmd.Name) if !ok { break } - ex.deletePortal(ctx, delCmd.Name) + ex.prepStmtsNamespace.delPortal(ctx, delCmd.Name) default: panic(fmt.Sprintf("unknown del type: %s", delCmd.Type)) } @@ -558,7 +744,7 @@ func (ex *connExecutor) execDescribe( switch descCmd.Type { case pgwirebase.PrepareStatement: - ps, ok := ex.prepStmtsNamespace.prepStmts[descCmd.Name] + ps, ok := ex.prepStmtsNamespace.getPrepStmt(descCmd.Name) if !ok { return retErr(pgerror.NewErrorf( pgerror.CodeInvalidSQLStatementNameError, @@ -573,7 +759,7 @@ func (ex *connExecutor) execDescribe( res.SetPrepStmtOutput(ctx, ps.Columns) } case pgwirebase.PreparePortal: - portal, ok := ex.prepStmtsNamespace.portals[descCmd.Name] + portal, ok := ex.prepStmtsNamespace.getPortal(descCmd.Name) if !ok { return retErr(pgerror.NewErrorf( pgerror.CodeInvalidCursorNameError, "unknown portal %q", descCmd.Name))