Skip to content

Commit d63e5f8

Browse files
craig[bot]sousa16msbutlerrafiss
committed
143579: sql,plpgsql: fix case sensitivity in composite element assignment r=DrewKimball a=sousa16 #### sql,plpgsql: fix case sensitivity in composite element assignment Previously, the logic that resolved an element of a composite-typed PL/pgSQL variable for assignment normalized the element name after it was already normalized. This caused every field to be treated as lowercase, making it impossible to perform an assignment like `NEW."FOO_Bar" = 100`. This commit removes the extra normalization and replaces it with a conversion to string. Fixes #142083 Release note (sql change): Assigning to an element of a composite-typed variable in a PL/pgSQL routine now respects case-sensitivity rules. For example, a field named `"FOO_Bar"` can be assigned like `NEW."FOO_Bar" = 100`. 147545: crosscluster/physical: deflake TestTenantStreamingFailback r=kev-cao a=msbutler Previously, this test would fail with a scary fingerprint mismatch violation error, but it was because one test path did not wait for the fingerprint time to get replicated. Fixes #146444 Release note: none 147548: crosscluster/physical: skip TestAlterTenantAddReader under deadlock r=kev-cao a=msbutler Informs #146788 Epic: none 147627: sql: skip TestIndexBackfillerResumePreservesProgress under deadlock/race r=rafiss a=rafiss Epic: None Release note: None Co-authored-by: João Sousa <[email protected]> Co-authored-by: Michael Butler <[email protected]> Co-authored-by: Rafi Shamim <[email protected]>
5 parents 3868fb3 + 3a0abb9 + 5c5a35e + 0d303da + 92b8ce6 commit d63e5f8

File tree

6 files changed

+223
-9
lines changed

6 files changed

+223
-9
lines changed

pkg/ccl/logictestccl/testdata/logic_test/plpgsql_assign

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,97 @@ CREATE FUNCTION f(val xy) RETURNS xy AS $$
121121
END
122122
$$ LANGUAGE PLpgSQL;
123123

124+
subtest assign_elem_case_sensitivity
125+
126+
# Composite type with unquoted (lowercase) fields.
127+
statement ok
128+
CREATE TYPE comp1 AS (foo INT, bar INT);
129+
130+
statement ok
131+
CREATE FUNCTION f1(val comp1) RETURNS comp1 AS $$
132+
BEGIN
133+
val.foo := 10;
134+
val.bar := 20;
135+
RETURN val;
136+
END
137+
$$ LANGUAGE PLpgSQL;
138+
139+
query T
140+
SELECT f1(ROW(1, 2));
141+
----
142+
(10,20)
143+
144+
statement ok
145+
DROP FUNCTION f1;
146+
147+
# Composite type with quoted (uppercase) fields.
148+
statement ok
149+
CREATE TYPE comp2 AS ("FOO" INT, "BAR" INT);
150+
151+
statement ok
152+
CREATE FUNCTION f2(val comp2) RETURNS comp2 AS $$
153+
BEGIN
154+
val."FOO" := 100;
155+
val."BAR" := 200;
156+
RETURN val;
157+
END
158+
$$ LANGUAGE PLpgSQL;
159+
160+
query T
161+
SELECT f2(ROW(1, 2)::comp2);
162+
----
163+
(100,200)
164+
165+
statement ok
166+
DROP FUNCTION f2;
167+
168+
# Attempt to assign using wrong case (should fail).
169+
statement error pgcode 42703 pq: record "val" has no field "FOO"
170+
CREATE FUNCTION f3(val comp1) RETURNS comp1 AS $$
171+
BEGIN
172+
val."FOO" := 1;
173+
RETURN val;
174+
END
175+
$$ LANGUAGE PLpgSQL;
176+
177+
statement error pgcode 42703 pq: record "val" has no field "foo"
178+
CREATE FUNCTION f4(val comp2) RETURNS comp2 AS $$
179+
BEGIN
180+
val.foo := 1;
181+
RETURN val;
182+
END
183+
$$ LANGUAGE PLpgSQL;
184+
185+
# Mixed quoted/unquoted field names in composite type.
186+
statement ok
187+
CREATE TYPE comp3 AS (foo INT, "BAR" INT);
188+
189+
statement ok
190+
CREATE FUNCTION f5(val comp3) RETURNS comp3 AS $$
191+
BEGIN
192+
val.foo := 5;
193+
val."BAR" := 6;
194+
RETURN val;
195+
END
196+
$$ LANGUAGE PLpgSQL;
197+
198+
query T
199+
SELECT f5(ROW(0, 0)::comp3);
200+
----
201+
(5,6)
202+
203+
statement ok
204+
DROP FUNCTION f5;
205+
206+
statement ok
207+
DROP TYPE comp1;
208+
209+
statement ok
210+
DROP TYPE comp2;
211+
212+
statement ok
213+
DROP TYPE comp3;
214+
124215
subtest end
125216

126217
# Ordinal parameter references should reflect updates made by variable

pkg/ccl/logictestccl/testdata/logic_test/triggers

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4247,6 +4247,125 @@ SELECT id, a, b FROM tab_141810 ORDER BY id
42474247
3 1 2
42484248
4 NULL NULL
42494249

4250+
# ==============================================================================
4251+
# Test case sensitivity of column names in triggers
4252+
# ==============================================================================
4253+
4254+
subtest case_sensitivity
4255+
4256+
# Setup: clean up any existing tables and functions
4257+
statement ok
4258+
DROP TABLE IF EXISTS t1, t2, t3;
4259+
4260+
statement ok
4261+
DROP TRIGGER IF EXISTS tr1 ON t1;
4262+
DROP TRIGGER IF EXISTS tr2 ON t1;
4263+
DROP TRIGGER IF EXISTS tr3 ON t1;
4264+
-- tr4 failed to create, so no need to drop
4265+
4266+
statement ok
4267+
DROP TRIGGER IF EXISTS tr5 ON t2;
4268+
DROP TRIGGER IF EXISTS tr6 ON t2;
4269+
DROP TRIGGER IF EXISTS tr7 ON t2;
4270+
-- tr8 failed to create, so no need to drop
4271+
4272+
statement ok
4273+
DROP TRIGGER IF EXISTS tr12 ON t3;
4274+
-- tr9, tr10, tr11 failed to create, so no need to drop
4275+
4276+
# Setup: Create tables with different casing
4277+
statement ok
4278+
CREATE TABLE t1 (foo_bar INT);
4279+
CREATE TABLE t2 ("foo_bar" INT);
4280+
CREATE TABLE t3 ("FOO_BAR" INT);
4281+
4282+
# Trigger functions with different quoting/casing styles
4283+
statement ok
4284+
CREATE FUNCTION f1() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
4285+
BEGIN
4286+
NEW.foo_bar := 1;
4287+
RETURN NEW;
4288+
END;
4289+
$$;
4290+
4291+
statement ok
4292+
CREATE FUNCTION f2() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
4293+
BEGIN
4294+
NEW.FOO_BAR := 1;
4295+
RETURN NEW;
4296+
END;
4297+
$$;
4298+
4299+
statement ok
4300+
CREATE FUNCTION f3() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
4301+
BEGIN
4302+
NEW."foo_bar" := 1;
4303+
RETURN NEW;
4304+
END;
4305+
$$;
4306+
4307+
statement ok
4308+
CREATE FUNCTION f4() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
4309+
BEGIN
4310+
NEW."FOO_BAR" := 1;
4311+
RETURN NEW;
4312+
END;
4313+
$$;
4314+
4315+
# Attach each function to each table and check success/failure
4316+
4317+
# Unquoted table
4318+
statement ok
4319+
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION f1();
4320+
4321+
statement ok
4322+
CREATE TRIGGER tr2 BEFORE INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION f2();
4323+
4324+
statement ok
4325+
CREATE TRIGGER tr3 BEFORE INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION f3();
4326+
4327+
statement error pgcode 42703 pq: record "new" has no field "FOO_BAR"
4328+
CREATE TRIGGER tr4 BEFORE INSERT ON t1 FOR EACH ROW EXECUTE FUNCTION f4();
4329+
4330+
# Quoted lowercase column
4331+
statement ok
4332+
CREATE TRIGGER tr5 BEFORE INSERT ON t2 FOR EACH ROW EXECUTE FUNCTION f1();
4333+
4334+
statement ok
4335+
CREATE TRIGGER tr6 BEFORE INSERT ON t2 FOR EACH ROW EXECUTE FUNCTION f2();
4336+
4337+
statement ok
4338+
CREATE TRIGGER tr7 BEFORE INSERT ON t2 FOR EACH ROW EXECUTE FUNCTION f3();
4339+
4340+
statement error pgcode 42703 pq: record "new" has no field "FOO_BAR"
4341+
CREATE TRIGGER tr8 BEFORE INSERT ON t2 FOR EACH ROW EXECUTE FUNCTION f4();
4342+
4343+
# Quoted uppercase column
4344+
statement error pgcode 42703 pq: record "new" has no field "foo_bar"
4345+
CREATE TRIGGER tr9 BEFORE INSERT ON t3 FOR EACH ROW EXECUTE FUNCTION f1();
4346+
4347+
statement error pgcode 42703 pq: record "new" has no field "foo_bar"
4348+
CREATE TRIGGER tr10 BEFORE INSERT ON t3 FOR EACH ROW EXECUTE FUNCTION f2();
4349+
4350+
statement error pgcode 42703 pq: record "new" has no field "foo_bar"
4351+
CREATE TRIGGER tr11 BEFORE INSERT ON t3 FOR EACH ROW EXECUTE FUNCTION f3();
4352+
4353+
statement ok
4354+
CREATE TRIGGER tr12 BEFORE INSERT ON t3 FOR EACH ROW EXECUTE FUNCTION f4();
4355+
4356+
# Drop all tables
4357+
statement ok
4358+
DROP TABLE t1;
4359+
DROP TABLE t2;
4360+
DROP TABLE t3;
4361+
4362+
# Drop all functions
4363+
statement ok
4364+
DROP FUNCTION f1();
4365+
DROP FUNCTION f2();
4366+
DROP FUNCTION f3();
4367+
DROP FUNCTION f4();
4368+
42504369
subtest end
42514370

42524371
# Until #135311 can be fixed, disallow TG_ARGV references by default. There is

pkg/crosscluster/physical/alter_replication_job_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/cockroachdb/cockroach/pkg/testutils"
2121
"github.com/cockroachdb/cockroach/pkg/testutils/jobutils"
2222
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
23+
"github.com/cockroachdb/cockroach/pkg/testutils/skip"
2324
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
2425
"github.com/cockroachdb/cockroach/pkg/util/hlc"
2526
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
@@ -116,6 +117,8 @@ func TestAlterTenantAddReader(t *testing.T) {
116117
defer leaktest.AfterTest(t)()
117118
defer log.Scope(t).Close(t)
118119

120+
skip.UnderDeadlock(t, "flakes with deadlock detector")
121+
119122
ctx := context.Background()
120123
args := replicationtestutils.DefaultTenantStreamingClustersArgs
121124

pkg/crosscluster/physical/stream_ingestion_job_test.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,21 +199,19 @@ func TestTenantStreamingFailback(t *testing.T) {
199199
var ts1 string
200200
sqlA.QueryRow(t, "SELECT cluster_logical_timestamp()").Scan(&ts1)
201201

202+
t.Logf("waiting for g@%s", ts1)
203+
replicationtestutils.WaitUntilReplicatedTime(t,
204+
replicationtestutils.DecimalTimeToHLC(t, ts1),
205+
sqlB,
206+
jobspb.JobID(consumerGJobID))
207+
202208
// Randomize query execution to verify fast failback works for both
203209
// `COMPLETE REPLICATION TO LATEST` and `COMPLETE REPLICATION TO SYSTEM TIME`
204210
rng, _ := randutil.NewPseudoRand()
205211
if rng.Intn(2) == 0 {
206-
t.Logf("waiting for g@%s", ts1)
207-
replicationtestutils.WaitUntilReplicatedTime(t,
208-
replicationtestutils.DecimalTimeToHLC(t, ts1),
209-
sqlB,
210-
jobspb.JobID(consumerGJobID))
211-
212212
t.Logf("completing replication on g@%s", ts1)
213213
sqlB.Exec(t, fmt.Sprintf("ALTER VIRTUAL CLUSTER g COMPLETE REPLICATION TO SYSTEM TIME '%s'", ts1))
214214
} else {
215-
t.Log("waiting for initial scan on g")
216-
replicationtestutils.WaitUntilStartTimeReached(t, sqlB, jobspb.JobID(consumerGJobID))
217215
t.Log("completing replication on g to latest")
218216
sqlB.Exec(t, "ALTER VIRTUAL CLUSTER g COMPLETE REPLICATION TO LATEST")
219217
}

pkg/sql/indexbackfiller_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
"github.com/cockroachdb/cockroach/pkg/sql/types"
4444
"github.com/cockroachdb/cockroach/pkg/testutils"
4545
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
46+
"github.com/cockroachdb/cockroach/pkg/testutils/skip"
4647
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
4748
"github.com/cockroachdb/cockroach/pkg/util/ctxgroup"
4849
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
@@ -575,6 +576,8 @@ INSERT INTO foo VALUES (1), (10), (100);
575576
func TestIndexBackfillerResumePreservesProgress(t *testing.T) {
576577
defer leaktest.AfterTest(t)()
577578
defer log.Scope(t).Close(t)
579+
skip.UnderDeadlock(t, "slow timing sensitive test")
580+
skip.UnderRace(t, "slow timing sensitive test")
578581

579582
ctx := context.Background()
580583
backfillProgressCompletedCh := make(chan []roachpb.Span)

pkg/sql/opt/optbuilder/plpgsql.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1608,7 +1608,7 @@ const noIndirection = ""
16081608
func (b *plpgsqlBuilder) handleIndirectionForAssign(
16091609
inScope *scope, typ *types.T, ident ast.Variable, indirection tree.Name, val tree.Expr,
16101610
) opt.ScalarExpr {
1611-
elemName := indirection.Normalize()
1611+
elemName := string(indirection)
16121612

16131613
// We do not yet support qualifying a variable with a block label.
16141614
b.checkBlockLabelReference(elemName)

0 commit comments

Comments
 (0)