Skip to content

Commit 55ed31f

Browse files
committed
sql: error in cluster_logical_timestamp when not SERIALIZABLE
Previously, running `cluster_logical_timestamp` within an isolation level that tolerated write skew would result in an unhandled panic. This commit adds a gate at the SQL level that will instead return a FeatureNotSupported pgerror. This behavior may be permanent or may be removed once we determine how CommitTimestamp should function at these isolation levels. Epic: CRDB-26546 Part of: cockroachdb#103245 Release note (sql change): cluster_logical_timestamp now returns an error when called at isolation levels lower than SERIALIZABLE.
1 parent ea114b9 commit 55ed31f

File tree

10 files changed

+101
-19
lines changed

10 files changed

+101
-19
lines changed

docs/generated/sql/functions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,6 +3116,7 @@ Case mode values range between 0 - 1, representing lower casing and upper casing
31163116
a CockroachDB HLC in decimal form.</p>
31173117
<p>Note that uses of this function disable server-side optimizations and
31183118
may increase either contention or retry errors, or both.</p>
3119+
<p>Returns an error if run in a transaction with an isolation level weaker than SERIALIZABLE.</p>
31193120
</span></td><td>Volatile</td></tr>
31203121
<tr><td><a name="crdb_internal.active_version"></a><code>crdb_internal.active_version() &rarr; jsonb</code></td><td><span class="funcdesc"><p>Returns the current active cluster version.</p>
31213122
</span></td><td>Volatile</td></tr>

pkg/sql/conn_executor.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,21 +3433,6 @@ func (ex *connExecutor) txnIsolationLevelToKV(
34333433
return ret
34343434
}
34353435

3436-
func kvTxnIsolationLevelToTree(level isolation.Level) tree.IsolationLevel {
3437-
var ret tree.IsolationLevel
3438-
switch level {
3439-
case isolation.Serializable:
3440-
ret = tree.SerializableIsolation
3441-
case isolation.ReadCommitted:
3442-
ret = tree.ReadCommittedIsolation
3443-
case isolation.Snapshot:
3444-
ret = tree.SnapshotIsolation
3445-
default:
3446-
log.Fatalf(context.Background(), "unknown isolation level: %s", level)
3447-
}
3448-
return ret
3449-
}
3450-
34513436
func txnPriorityToProto(mode tree.UserPriority) roachpb.UserPriority {
34523437
var pri roachpb.UserPriority
34533438
switch mode {
@@ -4003,7 +3988,7 @@ func (ex *connExecutor) serialize() serverpb.Session {
40033988
Priority: ex.state.priority.String(),
40043989
QualityOfService: sessiondatapb.ToQoSLevelString(txn.AdmissionHeader().Priority),
40053990
LastAutoRetryReason: autoRetryReasonStr,
4006-
IsolationLevel: kvTxnIsolationLevelToTree(ex.state.isolationLevel).String(),
3991+
IsolationLevel: tree.IsolationLevelFromKVTxnIsolationLevel(ex.state.isolationLevel).String(),
40073992
}
40083993
}
40093994

pkg/sql/crdb_internal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7705,7 +7705,7 @@ func genClusterLocksGenerator(
77057705
tree.MakeDBool(tree.DBool(granted)), /* granted */
77067706
tree.MakeDBool(len(curLock.Waiters) > 0), /* contended */
77077707
durationDatum, /* duration */
7708-
tree.NewDString(kvTxnIsolationLevelToTree(curLock.LockHolder.IsoLevel).String()), /* isolation_level */
7708+
tree.NewDString(tree.IsolationLevelFromKVTxnIsolationLevel(curLock.LockHolder.IsoLevel).String()), /* isolation_level */
77097709
}, nil
77107710

77117711
}, nil, nil

pkg/sql/logictest/testdata/logic_test/txn

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,3 +1523,20 @@ regular
15231523
statement error pq: SET CLUSTER SETTING cannot be used inside a multi-statement transaction
15241524
SET CLUSTER SETTING sql.defaults.use_declarative_schema_changer = 'on';
15251525
SET CLUSTER SETTING sql.defaults.use_declarative_schema_changer = 'off';
1526+
1527+
statement ok
1528+
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
1529+
SELECT cluster_logical_timestamp();
1530+
1531+
statement ok
1532+
ROLLBACK
1533+
1534+
statement ok
1535+
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
1536+
1537+
skipif config local-mixed-22.2-23.1
1538+
statement error pq: cluster_logical_timestamp\(\): unsupported in READ COMMITTED isolation
1539+
SELECT cluster_logical_timestamp();
1540+
1541+
statement ok
1542+
ROLLBACK

pkg/sql/sem/builtins/builtins.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2779,7 +2779,9 @@ nearest replica.`, builtinconstants.DefaultFollowerReadDuration),
27792779
a CockroachDB HLC in decimal form.
27802780
27812781
Note that uses of this function disable server-side optimizations and
2782-
may increase either contention or retry errors, or both.`,
2782+
may increase either contention or retry errors, or both.
2783+
2784+
Returns an error if run in a transaction with an isolation level weaker than SERIALIZABLE.`,
27832785
Volatility: volatility.Volatile,
27842786
},
27852787
),

pkg/sql/sem/eval/context.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,15 @@ func (ec *Context) GetClusterTimestamp() (*tree.DDecimal, error) {
540540
if ec.Txn == nil {
541541
return nil, ErrNilTxnInClusterContext
542542
}
543+
544+
// CommitTimestamp panics for isolation levels that can operate across
545+
// multiple timestamps. Prevent this with a gate at the SQL level and return
546+
// a pgerror until we decide how this will officially behave. See #103245.
547+
if ec.TxnIsoLevel.ToleratesWriteSkew() {
548+
treeIso := tree.IsolationLevelFromKVTxnIsolationLevel(ec.TxnIsoLevel)
549+
return nil, pgerror.Newf(pgcode.FeatureNotSupported, "unsupported in %s isolation", treeIso.String())
550+
}
551+
543552
ts := ec.Txn.CommitTimestamp()
544553
if ts.IsEmpty() {
545554
return nil, errors.AssertionFailedf("zero cluster timestamp in txn")

pkg/sql/sem/tree/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ go_library(
125125
"//pkg/col/typeconv", # keep
126126
"//pkg/geo",
127127
"//pkg/geo/geopb",
128+
"//pkg/kv/kvserver/concurrency/isolation",
128129
"//pkg/sql/lex",
129130
"//pkg/sql/lexbase",
130131
"//pkg/sql/pgrepl/lsn",
@@ -203,6 +204,7 @@ go_test(
203204
"pretty_test.go",
204205
"table_name_test.go",
205206
"time_test.go",
207+
"txn_test.go",
206208
"type_check_internal_test.go",
207209
"type_check_test.go",
208210
"type_name_test.go",
@@ -218,6 +220,7 @@ go_test(
218220
"//pkg/col/coldata",
219221
"//pkg/col/coldataext",
220222
"//pkg/internal/rsg",
223+
"//pkg/kv/kvserver/concurrency/isolation",
221224
"//pkg/security/securityassets",
222225
"//pkg/security/securitytest",
223226
"//pkg/settings/cluster",

pkg/sql/sem/tree/txn.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"fmt"
1515
"strings"
1616

17+
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/isolation"
1718
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
1819
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1920
"github.com/cockroachdb/redact"
@@ -63,6 +64,24 @@ func (i IsolationLevel) String() string {
6364
return isolationLevelNames[i]
6465
}
6566

67+
// IsolationLevelFromKVTxnIsolationLevel converts a kv level isolation.Level to
68+
// its SQL semantic equivalent.
69+
func IsolationLevelFromKVTxnIsolationLevel(level isolation.Level) IsolationLevel {
70+
var ret IsolationLevel
71+
switch level {
72+
case isolation.Serializable:
73+
ret = SerializableIsolation
74+
case isolation.ReadCommitted:
75+
ret = ReadCommittedIsolation
76+
case isolation.Snapshot:
77+
ret = SnapshotIsolation
78+
default:
79+
panic("What to do here? Log is a banned import")
80+
// log.Fatalf(context.Background(), "unknown isolation level: %s", level)
81+
}
82+
return ret
83+
}
84+
6685
// UserPriority holds the user priority for a transaction.
6786
type UserPriority int
6887

pkg/sql/sem/tree/txn_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2015 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package tree_test
12+
13+
import (
14+
"testing"
15+
16+
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/isolation"
17+
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
18+
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
19+
"github.com/stretchr/testify/require"
20+
)
21+
22+
func TestIsolationLevelFromKVTxnIsolationLevel(t *testing.T) {
23+
defer leaktest.AfterTest(t)()
24+
25+
testCases := []struct {
26+
In isolation.Level
27+
Out tree.IsolationLevel
28+
}{
29+
{
30+
In: isolation.Serializable,
31+
Out: tree.SerializableIsolation,
32+
},
33+
{
34+
In: isolation.ReadCommitted,
35+
Out: tree.ReadCommittedIsolation,
36+
},
37+
{
38+
In: isolation.Snapshot,
39+
Out: tree.SnapshotIsolation,
40+
},
41+
}
42+
43+
for _, tc := range testCases {
44+
require.Equal(t, tc.Out, tree.IsolationLevelFromKVTxnIsolationLevel(tc.In))
45+
}
46+
}

pkg/sql/vars.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,7 @@ var varGen = map[string]sessionVar{
14911491
// See https://github.com/postgres/postgres/blob/REL_10_STABLE/src/backend/utils/misc/guc.c#L3401-L3409
14921492
`transaction_isolation`: {
14931493
Get: func(evalCtx *extendedEvalContext, _ *kv.Txn) (string, error) {
1494-
level := kvTxnIsolationLevelToTree(evalCtx.Txn.IsoLevel())
1494+
level := tree.IsolationLevelFromKVTxnIsolationLevel(evalCtx.Txn.IsoLevel())
14951495
return strings.ToLower(level.String()), nil
14961496
},
14971497
RuntimeSet: func(ctx context.Context, evalCtx *extendedEvalContext, local bool, s string) error {

0 commit comments

Comments
 (0)