Skip to content

Commit 1d3c11e

Browse files
craig[bot]adityamarumgartner
committed
107570: sql,jobs: short-term fix for `UndefinedColumn` job_type error r=HonoreDB a=adityamaru In cockroachdb#106762 we noticed that if a query is executed with an AS OF SYSTEM TIME clause that picks a transaction timestamp before the job_type migration, then parts of the jobs infrastructure will attempt to query the job_type column even though it doesn't exist at the transaction's timestamp. As a short term fix, when we encounter an `UndefinedColumn` error for the `job_type` column in `crdb_internal.jobs` we generate a synthetic retryable error so that the txn is pushed to a higher timestamp at which the upgrade will have completed and the `job_type` column will be visible. The longer term fix is being tracked in cockroachdb#106764. We are intentionally approaching this issue with a whack-a-mole approach to stabilize the tests the are running into this issue. We think time is better spent designing and investing in the longer term solution that will be tracked in cockroachdb#106764. Fixes: cockroachdb#107169 Informs: cockroachdb#106762 Release note: None 107641: sql: disallow aggregate functions in ORDER BY in DELETE and UPDATE r=mgartner a=mgartner This commit disallows aggregate functions in the context of an `ORDER BY` clause in a `DELETE` or `UPDATE` statement. An aggregate function in an `ORDER BY` would require a `GROUP BY` clause to group non-aggregate columns. A `GROUP BY` is not allowed in `DELETE` or `UPDATE` statements as it's not obvious how grouping in these statements would behave. So we simply disallow aggregates in `ORDER BY` instead. Fixes cockroachdb#107634 Release note (bug fix): A bug has been fixed that caused internal errors when using an aggregate function in an `ORDER BY` clause of a `DELETE` or `UPDATE` statement. Aggregate functions are no longer allowed in these contexts. The bug has been present since at least version 20.2. Co-authored-by: adityamaru <[email protected]> Co-authored-by: Marcus Gartner <[email protected]>
3 parents d93799f + ac55715 + b3a90bd commit 1d3c11e

File tree

12 files changed

+87
-10
lines changed

12 files changed

+87
-10
lines changed

pkg/jobs/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ go_library(
4949
"//pkg/sql/catalog/descpb",
5050
"//pkg/sql/catalog/descs",
5151
"//pkg/sql/isql",
52+
"//pkg/sql/pgwire/pgcode",
5253
"//pkg/sql/pgwire/pgerror",
5354
"//pkg/sql/protoreflect",
5455
"//pkg/sql/sem/builtins",

pkg/jobs/utils.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"github.com/cockroachdb/cockroach/pkg/jobs/jobspb"
2020
"github.com/cockroachdb/cockroach/pkg/kv"
2121
"github.com/cockroachdb/cockroach/pkg/sql/isql"
22+
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
23+
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
2224
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
2325
"github.com/cockroachdb/errors"
2426
)
@@ -153,3 +155,33 @@ func JobExists(
153155
}
154156
return row != nil, nil
155157
}
158+
159+
// IsJobTypeColumnDoesNotExistError returns true if the error is of the form
160+
// `column "job_type" does not exist`.
161+
func isJobTypeColumnDoesNotExistError(err error) bool {
162+
return pgerror.GetPGCode(err) == pgcode.UndefinedColumn &&
163+
strings.Contains(err.Error(), "column \"job_type\" does not exist")
164+
}
165+
166+
// MaybeGenerateForcedRetryableError returns a
167+
// TransactionRetryWithProtoRefreshError that will cause the txn to be retried
168+
// if the error is because of an undefined job_type column.
169+
//
170+
// In https://github.com/cockroachdb/cockroach/issues/106762 we noticed that if
171+
// a query is executed with an AS OF SYSTEM TIME clause that picks a transaction
172+
// timestamp before the job_type migration, then parts of the jobs
173+
// infrastructure will attempt to query the job_type column even though it
174+
// doesn't exist at the transaction's timestamp.
175+
//
176+
// As a short term fix, when we encounter an `UndefinedColumn` error we
177+
// generate a synthetic retryable error so that the txn is pushed to a
178+
// higher timestamp at which the upgrade will have completed and the
179+
// `job_type` column will be visible. The longer term fix is being tracked
180+
// in https://github.com/cockroachdb/cockroach/issues/106764.
181+
func MaybeGenerateForcedRetryableError(ctx context.Context, txn *kv.Txn, err error) error {
182+
if err != nil && isJobTypeColumnDoesNotExistError(err) {
183+
return txn.GenerateForcedRetryableError(ctx, "synthetic error "+
184+
"to push timestamp to after the `job_type` upgrade has run")
185+
}
186+
return err
187+
}

pkg/sql/crdb_internal.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ func populateSystemJobsTableRows(
10691069
params...,
10701070
)
10711071
if err != nil {
1072-
return matched, err
1072+
return matched, jobs.MaybeGenerateForcedRetryableError(ctx, p.Txn(), err)
10731073
}
10741074

10751075
cleanup := func(ctx context.Context) {
@@ -1082,7 +1082,7 @@ func populateSystemJobsTableRows(
10821082
for {
10831083
hasNext, err := it.Next(ctx)
10841084
if !hasNext || err != nil {
1085-
return matched, err
1085+
return matched, jobs.MaybeGenerateForcedRetryableError(ctx, p.Txn(), err)
10861086
}
10871087

10881088
currentRow := it.Cur()

pkg/sql/logictest/testdata/logic_test/delete

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,3 +565,14 @@ false
565565
query I
566566
SELECT a FROM t99630a@idx WHERE a > 0
567567
----
568+
569+
# Regression test for #107634. Do not allow aggregate functions in ORDER BY.
570+
subtest regression_107634
571+
572+
statement ok
573+
CREATE TABLE t107634 (a INT)
574+
575+
statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORDER BY in DELETE
576+
DELETE FROM t107634 ORDER BY sum(a) LIMIT 1;
577+
578+
subtest end

pkg/sql/logictest/testdata/logic_test/update

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,3 +661,14 @@ SELECT * FROM generated_as_id_t ORDER BY a;
661661
7 4 4
662662
8 5 5
663663
9 6 6
664+
665+
# Regression test for #107634. Do not allow aggregate functions in ORDER BY.
666+
subtest regression_107634
667+
668+
statement ok
669+
CREATE TABLE t107634 (a INT)
670+
671+
statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORDER BY in UPDATE
672+
UPDATE t107634 SET a = 1 ORDER BY sum(a) LIMIT 1;
673+
674+
subtest end

pkg/sql/opt/optbuilder/mutation_builder.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,8 @@ func (mb *mutationBuilder) buildInputForUpdate(
336336
// SELECT + ORDER BY (which may add projected expressions)
337337
projectionsScope := mb.outScope.replace()
338338
projectionsScope.appendColumnsFromScope(mb.outScope)
339-
orderByScope := mb.b.analyzeOrderBy(orderBy, mb.outScope, projectionsScope, tree.RejectGenerators)
339+
orderByScope := mb.b.analyzeOrderBy(orderBy, mb.outScope, projectionsScope,
340+
exprKindOrderByUpdate, tree.RejectGenerators|tree.RejectAggregates)
340341
mb.b.buildOrderBy(mb.outScope, projectionsScope, orderByScope)
341342
mb.b.constructProjectForScope(mb.outScope, projectionsScope)
342343

@@ -448,7 +449,8 @@ func (mb *mutationBuilder) buildInputForDelete(
448449
// SELECT + ORDER BY (which may add projected expressions)
449450
projectionsScope := mb.outScope.replace()
450451
projectionsScope.appendColumnsFromScope(mb.outScope)
451-
orderByScope := mb.b.analyzeOrderBy(orderBy, mb.outScope, projectionsScope, tree.RejectGenerators)
452+
orderByScope := mb.b.analyzeOrderBy(orderBy, mb.outScope, projectionsScope,
453+
exprKindOrderByDelete, tree.RejectGenerators|tree.RejectAggregates)
452454
mb.b.buildOrderBy(mb.outScope, projectionsScope, orderByScope)
453455
mb.b.constructProjectForScope(mb.outScope, projectionsScope)
454456

pkg/sql/opt/optbuilder/orderby.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ import (
2828
// analyzeOrderBy analyzes an Ordering physical property from the ORDER BY
2929
// clause and adds the resulting typed expressions to orderByScope.
3030
func (b *Builder) analyzeOrderBy(
31-
orderBy tree.OrderBy, inScope, projectionsScope *scope, rejectFlags tree.SemaRejectFlags,
31+
orderBy tree.OrderBy,
32+
inScope, projectionsScope *scope,
33+
kind exprKind,
34+
rejectFlags tree.SemaRejectFlags,
3235
) (orderByScope *scope) {
3336
if orderBy == nil {
3437
return nil
@@ -41,8 +44,8 @@ func (b *Builder) analyzeOrderBy(
4144
// semaCtx in case we are recursively called within a subquery
4245
// context.
4346
defer b.semaCtx.Properties.Restore(b.semaCtx.Properties)
44-
b.semaCtx.Properties.Require(exprKindOrderBy.String(), rejectFlags)
45-
inScope.context = exprKindOrderBy
47+
b.semaCtx.Properties.Require(kind.String(), rejectFlags)
48+
inScope.context = kind
4649

4750
for i := range orderBy {
4851
b.analyzeOrderByArg(orderBy[i], inScope, projectionsScope, orderByScope)

pkg/sql/opt/optbuilder/scope.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ const (
132132
exprKindOffset
133133
exprKindOn
134134
exprKindOrderBy
135+
exprKindOrderByDelete
136+
exprKindOrderByUpdate
135137
exprKindReturning
136138
exprKindSelect
137139
exprKindStoreID
@@ -153,6 +155,8 @@ var exprKindName = [...]string{
153155
exprKindOffset: "OFFSET",
154156
exprKindOn: "ON",
155157
exprKindOrderBy: "ORDER BY",
158+
exprKindOrderByDelete: "ORDER BY in DELETE",
159+
exprKindOrderByUpdate: "ORDER BY in UPDATE",
156160
exprKindReturning: "RETURNING",
157161
exprKindSelect: "SELECT",
158162
exprKindStoreID: "RELOCATE STORE ID",

pkg/sql/opt/optbuilder/select.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,8 @@ func (b *Builder) buildSelectStmtWithoutParens(
11061106
col := projectionsScope.addColumn(scopeColName(""), expr)
11071107
b.buildScalar(expr, outScope, projectionsScope, col, nil)
11081108
}
1109-
orderByScope := b.analyzeOrderBy(orderBy, outScope, projectionsScope, tree.RejectGenerators|tree.RejectAggregates|tree.RejectWindowApplications)
1109+
orderByScope := b.analyzeOrderBy(orderBy, outScope, projectionsScope, exprKindOrderBy,
1110+
tree.RejectGenerators|tree.RejectAggregates|tree.RejectWindowApplications)
11101111
b.buildOrderBy(outScope, projectionsScope, orderByScope)
11111112
b.constructProjectForScope(outScope, projectionsScope)
11121113
outScope = projectionsScope
@@ -1151,7 +1152,8 @@ func (b *Builder) buildSelectClause(
11511152
// Any aggregates in the HAVING, ORDER BY and DISTINCT ON clauses (if they
11521153
// exist) will be added here.
11531154
havingExpr := b.analyzeHaving(sel.Having, fromScope)
1154-
orderByScope := b.analyzeOrderBy(orderBy, fromScope, projectionsScope, tree.RejectGenerators)
1155+
orderByScope := b.analyzeOrderBy(orderBy, fromScope, projectionsScope,
1156+
exprKindOrderBy, tree.RejectGenerators)
11551157
distinctOnScope := b.analyzeDistinctOnArgs(sel.DistinctOn, fromScope, projectionsScope)
11561158

11571159
var having opt.ScalarExpr

pkg/sql/opt/optbuilder/testdata/delete

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ DELETE FROM abcde WHERE b=1 ORDER BY c
299299
----
300300
error (42601): DELETE statement requires LIMIT when ORDER BY is used
301301

302+
# Aggregate functions are not allowed in ORDER BY.
303+
build
304+
DELETE FROM abcde WHERE b=1 ORDER BY sum(c) LIMIT 1
305+
----
306+
error (42803): sum(): aggregate functions are not allowed in ORDER BY in DELETE
307+
302308
# ------------------------------------------------------------------------------
303309
# Test RETURNING.
304310
# ------------------------------------------------------------------------------

0 commit comments

Comments
 (0)