Skip to content

Commit

Permalink
plpgsql: add builtin function for closing cursors
Browse files Browse the repository at this point in the history
This patch adds the `crdb_internal.plpgsql_close` builtin, which closes
the cursor with the given name. It returns a `34000` error if there is
no cursor with the given name. A following commit will use this to
implement the PLpgSQL CLOSE statement.

Informs cockroachdb#109709

Release note: None
  • Loading branch information
DrewKimball committed Sep 27, 2023
1 parent 24d184a commit 9a029f1
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 1 deletion.
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ func (*DummyEvalPlanner) Optimizer() interface{} {
return nil
}

// PLpgSQLCloseCursor is part of the eval.Planner interface.
func (*DummyEvalPlanner) PLpgSQLCloseCursor(_ tree.Name) error {
return nil
}

var _ eval.Planner = &DummyEvalPlanner{}

var errEvalPlanner = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext,
Expand Down
22 changes: 22 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/plpgsql_builtins
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Testing crdb_internal.plpgsql_close.
statement ok
BEGIN;
DECLARE foo CURSOR FOR SELECT generate_series(1, 5);

query T
SELECT name FROM pg_cursors;
----
foo

statement ok
SELECT crdb_internal.plpgsql_close('foo');

query T
SELECT name FROM pg_cursors;
----

statement ok
ABORT;

statement error pgcode 34000 pq: cursor \"foo\" does not exist
SELECT crdb_internal.plpgsql_close('foo');
7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions pkg/sql/sem/builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -8431,6 +8431,24 @@ specified store on the node it's run from. One of 'mvccGC', 'merge', 'split',
Volatility: volatility.Immutable,
},
),
"crdb_internal.plpgsql_close": makeBuiltin(tree.FunctionProperties{
Category: builtinconstants.CategoryString,
Undocumented: true,
},
tree.Overload{
Types: tree.ParamTypes{{Name: "name", Typ: types.String}},
ReturnType: tree.FixedReturnType(types.Int),
Fn: func(ctx context.Context, evalCtx *eval.Context, args tree.Datums) (tree.Datum, error) {
if args[0] == tree.DNull {
return nil, errors.AssertionFailedf("expected non-null argument for plpgsql_close")
}
return tree.DNull, evalCtx.Planner.PLpgSQLCloseCursor(tree.Name(tree.MustBeDString(args[0])))
},
Info: "This function is used internally to implement the PLpgSQL CLOSE statement.",
Volatility: volatility.Volatile,
CalledOnNullInput: true,
},
),
}

var lengthImpls = func(incBitOverload bool) builtinDefinition {
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/sem/builtins/fixed_oids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2463,6 +2463,7 @@ var builtinOidsArray = []string{
2492: `make_timestamptz(year: int, month: int, day: int, hour: int, min: int, sec: float, timezone: string) -> timestamptz`,
2493: `date_trunc(element: string, input: timestamptz, timezone: string) -> timestamptz`,
2494: `make_date(year: int, month: int, day: int) -> date`,
2495: `crdb_internal.plpgsql_close(name: string) -> int`,
}

var builtinOidsBySignature map[string]oid.Oid
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/sem/eval/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ type Planner interface {

// Optimizer returns the optimizer associated with this Planner, if any.
Optimizer() interface{}

// PLpgSQLCloseCursor closes the cursor with the given name, returning an
// error if the cursor doesn't exist. It is used to implement the PLpgSQL
// CLOSE statement.
PLpgSQLCloseCursor(cursorName tree.Name) error
}

// InternalRows is an iterator interface that's exposed by the internal
Expand Down
3 changes: 2 additions & 1 deletion pkg/sql/sem/tree/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,8 @@ func (e *MultipleResultsError) Error() string {
func (expr *FuncExpr) MaybeWrapError(err error) error {
// If we are facing an explicit error, propagate it unchanged.
fName := expr.Func.String()
if fName == `crdb_internal.force_error` || fName == `crdb_internal.plpgsql_raise` {
if fName == `crdb_internal.force_error` || fName == `crdb_internal.plpgsql_raise` ||
fName == `crdb_internal.plpgsql_close` {
return err
}
// Otherwise, wrap it with context.
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/sql_cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ func (p *planner) CloseCursor(ctx context.Context, n *tree.CloseCursor) (planNod
}, nil
}

// PLpgSQLCloseCursor implements the eval.Planner interface.
func (p *planner) PLpgSQLCloseCursor(cursorName tree.Name) error {
return p.sqlCursors.closeCursor(cursorName)
}

type sqlCursor struct {
isql.Rows
// txn is the transaction object that the internal executor for this cursor
Expand Down

0 comments on commit 9a029f1

Please sign in to comment.