This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new dc848b3fc32 [fix](temp-table) Fix CTAS/DROP for temporary tables and
unmute 5 P0 cases (#67529)
dc848b3fc32 is described below
commit dc848b3fc32899ffe0a213da489ba6ef032cd1ae
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Fri Sep 4 23:28:52 2026 +0800
[fix](temp-table) Fix CTAS/DROP for temporary tables and unmute 5 P0 cases
(#67529)
### What problem does this PR solve?
Five P0 regression cases are muted on TeamCity. Three of them fail on
**every** master build — they only look green on `branch-4.1` because
the suite is skipped there (`if (true) { return }` at the top of
`test_temp_table.groovy`), so the mute is hiding persistent master
failures rather than flakiness. The other two are genuinely flaky.
| muted case | failures / last 300 P0 runs |
|---|---|
| `nereids_rules_p0/pkfk/eliminate_inner` | 251 |
| `compaction/test_vertical_compaction_agg_state` | 254 |
| `temp_table_p0/test_temp_table` | 250 |
| `load_p0/routine_load/test_routine_load` | 32 |
| `query_p0/cache/sql_cache_object_type` | 10 / 103 |
Two of them turned out to be real FE bugs.
#### 1. `CREATE TEMPORARY TABLE ... AS SELECT` wrongly rejected
A temporary table is created under `<sessionId>#TEMP#<name>`, but the
CTAS existence probe added in #66112
(`CreateTableCommand.targetTableExists`) looked up the **bare** name:
```java
return database != null && database.isTableExist(qualifiedName.get(2));
```
So any normal table sharing that name makes the statement fail with
`Table 'x' already exists`, even though the table it would create does
not exist. This is user-visible, not just a test problem:
```sql
CREATE TABLE t (id INT) DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES('replication_num'='1');
CREATE TEMPORARY TABLE t PROPERTIES('replication_num'='1') AS SELECT * FROM
src;
-- ERROR 1105: errCode = 2, detailMessage = Table 't' already exists
```
The probe now mangles the name exactly the way
`InternalCatalog.createTable` does. Plain `CREATE TEMPORARY TABLE t
(...)` was never affected, because that path goes straight to
`Env.createTable`, which mangles internally. This is the failure
`temp_table_p0/test_temp_table` hits at line 456.
#### 2. `DROP TEMPORARY TABLE IF EXISTS` ignored `IF EXISTS`
`Database.getTableNullable` resolves the temporary table first and falls
back to the normal table, so when a session owns no temporary table of
that name, `table != null` and the earlier `ifExists` branch is skipped.
The `mustTemporary` guard then raised `Unknown table` unconditionally:
```sql
DROP TEMPORARY TABLE IF EXISTS never_existed; -- OK, no-op
DROP TEMPORARY TABLE IF EXISTS t; -- ERROR 1105: Unknown table
't' (t is a normal table)
```
`IF EXISTS` is now honored there. Without `IF EXISTS` the statement
still reports `Unknown table`, and a `DROP TEMPORARY TABLE` still never
drops the normal table.
### Case and baseline fixes
- **`eliminate_inner`** — baseline went stale on 2026-07-16 when #65264
added `shapeInfo()` overrides to `Cast`, `IsNull` and `Not`, which now
preserve the table qualifier (`cast(f as ...)` → `cast(fkt_not_null.f as
...)`). `branch-4.1` has no such override, which is why it stayed green
there. Regenerated the 8 affected shape lines.
- **`test_vertical_compaction_agg_state`** — the first assertion
compared a literal `collect_set_merge` ordering, but `collect_set` is
backed by `flat_hash_set`, whose iteration order is unspecified. Wrapped
it in `array_sort`, matching the two sibling assertions already in the
same suite. The set contents were never wrong, only their order.
- **`test_routine_load`** — the `load_to_single_tablet` section waited
only for the job to leave `NEED_SCHEDULE` (i.e. to be *scheduled*), not
for a batch to be committed, and its baseline recorded the empty table
that race produced. It now uses the same data-visibility wait as the
other nine sections, and the baseline holds the rows that actually load.
9 of the 14 most recent failures of this suite were exactly this tag.
- **`sql_cache_object_type`** — asserted that a cache entry survived.
The FE map holds soft values under a bounded size
(`Config.sql_cache_manage_num`) and the rows live in the BE result
cache, so neither is guaranteed to persist. It re-primes the cache
instead; the assertion that each `return_object_data_as_binary` setting
is served its own result is unchanged.
---
.../apache/doris/datasource/InternalCatalog.java | 8 ++
.../insert/streaming/StreamingInsertJob.java | 24 +++++
.../streaming/StreamingJobSchedulerTask.java | 7 +-
.../trees/plans/commands/CreateTableCommand.java | 13 ++-
.../StreamingInsertJobStatusTransitionTest.java | 66 +++++++++++++
.../test_vertical_compaction_agg_state.out | 2 +-
.../load_p0/routine_load/test_routine_load.out | 20 ++++
.../data/nereids_rules_p0/pkfk/eliminate_inner.out | 16 ++--
.../test_mow_compact_multi_segments.groovy | 16 +++-
.../test_vertical_compaction_agg_state.groovy | 2 +-
.../load_p0/routine_load/test_routine_load.groovy | 22 +++++
.../query_p0/cache/sql_cache_object_type.groovy | 7 +-
.../runtime_filter/rf_bucket_pruning.groovy | 5 +
.../temp_table_p0/test_drop_temporary_table.groovy | 92 ++++++++++++++++++
.../test_temp_table_ctas_name_conflict.groovy | 106 +++++++++++++++++++++
15 files changed, 391 insertions(+), 15 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
index 69463ca6dd0..1f3a52c24d7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
@@ -956,6 +956,14 @@ public class InternalCatalog implements
CatalogIf<Database> {
dropTableInternal(db, table, false, true, watch, costTimes);
} else {
if (mustTemporary) {
+ // Name resolution falls back to the normal table when
this session owns no
+ // temporary one, but DROP TEMPORARY TABLE must never drop
that table. So the
+ // temporary table the statement asked for does not exist,
and IF EXISTS asks
+ // for a no-op in exactly that case.
+ if (ifExists) {
+ LOG.info("drop temporary table[{}] which does not
exist", tableName);
+ return;
+ }
ErrorReport.reportDdlException(ErrorCode.ERR_UNKNOWN_TABLE, tableName, dbName);
}
dropTableInternal(db, table, isView, force, watch, costTimes);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJob.java
b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJob.java
index 185aa720c4a..a8cbcab6ac6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJob.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJob.java
@@ -568,6 +568,30 @@ public class StreamingInsertJob extends
AbstractJob<StreamingJobSchedulerTask, M
}
}
+ /**
+ * Move the job to {@code newStatus} only if it is still in {@code
expectedStatus}.
+ *
+ * <p>{@link #createStreamingTask()} hands the task to {@code
StreamingTaskScheduler} before the
+ * caller has marked the job RUNNING, so that scheduler thread may already
have failed the task
+ * and paused the job. Writing RUNNING unconditionally would drop that
PAUSED, and the job would
+ * then sit in RUNNING with a canceled task forever: handleRunningState()
never creates a task
+ * for a TVF source, and the auto resume handler only runs while PAUSED.
+ *
+ * @return true when the status was updated, false when another thread
already moved the job
+ */
+ public boolean updateJobStatusIfCurrent(JobStatus expectedStatus,
JobStatus newStatus) throws JobException {
+ lock.writeLock().lock();
+ try {
+ if (!expectedStatus.equals(getJobStatus())) {
+ return false;
+ }
+ updateJobStatus(newStatus);
+ return true;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
public void resetFailureInfo(FailureReason reason) {
this.setFailureReason(reason);
// Currently, only delayMsg is present here, which needs to be cleared
when the status changes.
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingJobSchedulerTask.java
b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingJobSchedulerTask.java
index 030c3bb2b8b..c5dfacaadfa 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingJobSchedulerTask.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingJobSchedulerTask.java
@@ -81,7 +81,12 @@ public class StreamingJobSchedulerTask extends AbstractTask {
}
streamingInsertJob.createStreamingTask();
streamingInsertJob.setSampleStartTime(System.currentTimeMillis());
- streamingInsertJob.updateJobStatus(JobStatus.RUNNING);
+ // The task is already visible to StreamingTaskScheduler, which pauses
the job when it fails
+ // to schedule it. Only claim RUNNING while the job is still PENDING,
so that PAUSED survives.
+ if (!streamingInsertJob.updateJobStatusIfCurrent(JobStatus.PENDING,
JobStatus.RUNNING)) {
+ log.info("streaming job {} left PENDING while its task was
dispatched, keep status {}",
+ streamingInsertJob.getJobId(),
streamingInsertJob.getJobStatus());
+ }
}
private void handleRunningState() throws JobException {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java
index 3031e67be7d..b819ab532fb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java
@@ -23,6 +23,7 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.util.Util;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.nereids.NereidsPlanner;
import org.apache.doris.nereids.analyzer.UnboundResultSink;
@@ -253,7 +254,17 @@ public class CreateTableCommand extends Command implements
NeedAuditEncryption,
return false;
}
DatabaseIf<?> database = catalog.getDbNullable(qualifiedName.get(1));
- return database != null && database.isTableExist(qualifiedName.get(2));
+ if (database == null) {
+ return false;
+ }
+ // A temporary table lives in a per-session namespace and is stored
under
+ // <sessionId>#TEMP#<name>, which is also the name InternalCatalog
will create it with.
+ // Probing the bare name would instead match a normal table (or
another session's temp
+ // table) of the same name, none of which is the table this statement
would create.
+ String tableName = createTableInfo.isTemp()
+ ? Util.generateTempTableInnerName(qualifiedName.get(2))
+ : qualifiedName.get(2);
+ return database.isTableExist(tableName);
}
private String getAutoRangePartitionNameOrNull() {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJobStatusTransitionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJobStatusTransitionTest.java
new file mode 100644
index 00000000000..08d5bcd3fd9
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJobStatusTransitionTest.java
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.job.extensions.insert.streaming;
+
+import org.apache.doris.common.jmockit.Deencapsulation;
+import org.apache.doris.job.common.JobStatus;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class StreamingInsertJobStatusTransitionTest {
+
+ private static StreamingInsertJob newJob(JobStatus status) {
+ StreamingInsertJob job =
Deencapsulation.newInstance(StreamingInsertJob.class);
+ Deencapsulation.setField(job, "lock", new
ReentrantReadWriteLock(true));
+ Deencapsulation.setField(job, "jobId", 8001L);
+ Deencapsulation.setField(job, "jobName", "test_job");
+ Deencapsulation.setField(job, "jobStatus", status);
+ return job;
+ }
+
+ @Test
+ public void testPendingIsPromotedToRunning() throws Exception {
+ StreamingInsertJob job = newJob(JobStatus.PENDING);
+
+ Assert.assertTrue(job.updateJobStatusIfCurrent(JobStatus.PENDING,
JobStatus.RUNNING));
+ Assert.assertEquals(JobStatus.RUNNING, job.getJobStatus());
+ }
+
+ @Test
+ public void testPausedSurvivesTheRunningWrite() throws Exception {
+ // StreamingTaskScheduler failed the freshly registered task and
paused the job before the
+ // dispatching thread reached its RUNNING write. Overwriting PAUSED
here would strand the
+ // job: it holds a canceled task, and only the PAUSED branch can auto
resume it.
+ StreamingInsertJob job = newJob(JobStatus.PAUSED);
+
+ Assert.assertFalse(job.updateJobStatusIfCurrent(JobStatus.PENDING,
JobStatus.RUNNING));
+ Assert.assertEquals(JobStatus.PAUSED, job.getJobStatus());
+ }
+
+ @Test
+ public void testStoppedSurvivesTheRunningWrite() throws Exception {
+ // A concurrent DROP/STOP JOB leaves a terminal status that must not
be revived either.
+ StreamingInsertJob job = newJob(JobStatus.STOPPED);
+
+ Assert.assertFalse(job.updateJobStatusIfCurrent(JobStatus.PENDING,
JobStatus.RUNNING));
+ Assert.assertEquals(JobStatus.STOPPED, job.getJobStatus());
+ }
+}
diff --git
a/regression-test/data/compaction/test_vertical_compaction_agg_state.out
b/regression-test/data/compaction/test_vertical_compaction_agg_state.out
index 668e00d9179..a3a848a37ca 100644
--- a/regression-test/data/compaction/test_vertical_compaction_agg_state.out
+++ b/regression-test/data/compaction/test_vertical_compaction_agg_state.out
@@ -1,6 +1,6 @@
-- This file is automatically generated. You should know what you did if you
want to edit this
-- !select_default --
-a ["aa", "a"]
+a ["a", "aa"]
-- !select_default --
a ["a", "aa", "aaa"]
diff --git a/regression-test/data/load_p0/routine_load/test_routine_load.out
b/regression-test/data/load_p0/routine_load/test_routine_load.out
index 2506366be11..743bd320edc 100644
--- a/regression-test/data/load_p0/routine_load/test_routine_load.out
+++ b/regression-test/data/load_p0/routine_load/test_routine_load.out
@@ -162,6 +162,26 @@
91 2023-08-27 true \N 2465 702240964
6373830997821598984 305860046137409400 15991.356
1.599972327386147E9 -165530947.0 \N 2023-04-26T19:31:10
2023-07-21 \N 2
B7YKYBYT8w0YC926bZ8Yz1VzyiWw2NWDAiTlEoPVyz9AXGti2Npg1FxWqWk4hEaALw0ZBSuiAIPj41lq36g5QRpPmAjNPK
{"fruit":"apple","color":"red","qty":5,"price":2.5} true 1 2
3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
-- !sql_load_to_single_tablet --
+8 2023-08-14 true 109 -31573 -1362465190
3990845741226497177 2732763251146840270 -25698.553
1.312831962567818E9 771983879.0 173937916.0 2023-03-07T14:13:19
2022-10-18 2023-07-16T05:03:13 D
PBn1wa6X8WneZYLMac11zzyhGl7tPXB5XgjmOV8L6uav9ja5oY433ktb2yhyQQIqBveZPkme
{"animal":"lion","weight":200,"habitat":["savannah","grassland"]} true
1 2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+20 2023-08-17 false -5 18158 784479801
1485484354598941738 -6632681928222776815 9708.431
-3.30432620706069E8 -816424174.0 571112646.0 2022-09-15T21:40:55
2023-02-23 2023-08-13T21:31:54 O X
2pYmX2vAhfEEHZZYPsgAmda1G7otnwx5TmUC879FPhDeIjvWI79ksBZpfFG2gp7jhCSbpZiecKGklB5SvG8tm31i5SUqe1xrWgLt4HSq7lMJWp75tx2kxD7pRIOpn
{"name":"Sarah","age":30,"city":"London","isMarried":false} true 1
2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:0 [...]
+21 2023-08-18 false 63 -27847 -35409596
8638201997392767650 4919963231735304178 -23382.541
-1.803403621426313E9 -22009767.0 661750756.0 2023-03-31T10:56:14
2023-01-20 2023-02-18T13:37:52 N T
PSiFwUEx3eVFNtjlnQ70YkgZNvKrGmQ2DN5K9yYHiSdFWeEDB1UpL3Frt8z1kEAIWRDWqXZuyi
{"city":"Sydney","population":5312000,"area":2058.7} true 1 2
3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+31 2023-08-27 false 17 -18849 1728109133
3266501886640700374 527195452623418935 -24062.328
-1.514348021262435E9 -322205854.0 -278237157.0 2022-10-07T03:24:23
2022-09-25 \N 0 8
yKMiAntORoRa8svnMfcxlOPwwND1m5s2fdS26Xu6cfs6HK5SAibqIp9h8sZcpjHy4
{"team":"Manchester United","players":["Ronaldo","Rooney","Giggs"],"coach":"Ole
Gunnar Solskjaer"} true 1 2 3 4 5 6.0
7.0 888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤身体
我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+41 2023-08-27 true -104 22750 \N 8527773271030840740
5554497317268279215 -5296.828 -1.71564688801304E9 -306075962.0
897769189.0 2022-12-02T17:56:44 2022-10-12 2023-02-19T07:02:54
V \N
E9GzQdTwX1ITUQz27IVznAs6Ca4WwprKk6Odjs6SH75D2F1089QiY3HQ52LXRD1V6xAWjhLE2hWgW3EdHuAOnUDVrb5V
{"food":"Sushi","price":10,"restaurant":"Sushi King"} true 1 2
3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+49 2023-08-08 false \N 16275 -2144851675
-2303421957908954634 -46526938720058765 -13141.143
-6.866322332302E8 229942298.0 -152553823.0 2022-09-01T00:16:01
2023-03-25 2022-09-07T14:59:03 s yvuILR2iNxfe8RRml
{"student":true,"name":"Alice","grade":9,"subjects":["math","science","history"]}
true 1 2 3 4 5 6.0 7.0
888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤身体
我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+50 2023-08-06 true 109 -6330 1479023892
-8630800697573159428 -1645095773540208759 17880.96
-1.453844792013949E9 -158871820.0 -862940384.0 2022-09-22T02:03:21
2023-05-14 2023-03-25T02:18:34 m
JKnIgXvGVidGiWl9YRSi3mFI7wHKt1sBpWSadKF8VX3LAuElm4sdc9gtxREaUr57oikSYlU8We8h1MWqQlYNiJObl
{"city":"Tokyo","temperature":20.5,"humidity":75} true 1
2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+50 2023-08-24 true 15 14403 \N -6418906115745394180
9205303779366462513 -4331.549 -6.15112179557648E8 367305015.0
-551652958.0 2022-12-29T02:27:20 2023-06-01 2023-08-12T04:50:04
a
eCl38sztIvBQvGvGKyYZmyMXy9vIJx197iu3JwP9doJGcrYUl9Uova0rz4iCCgrjlAiZU18Fs9YtCq830nhM
{"band":"The Beatles","members":["John Lennon","Paul McCartney","George
Harrison","Ringo Starr"]} true 1 2 3 4 5
6.0 7.0 888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤 [...]
+57 2023-08-19 true 2 -25462 -74112029
6458082754318544493 -7910671781690629051 -15205.859
-3.06870797484914E8 759730669.0 -628556336.0 2023-07-10T18:39:10
2023-02-12 2023-01-27T07:26:06 y
Xi9nDVrLv8m6AwEpUxmtzFAuK48sQ {"name":"John","age":25,"city":"New York"}
true 1 2 3 4 5 6.0 7.0
888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤身体
我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+58 2023-08-22 \N 0 -18231 1832867360
6997858407575297145 2480714305422728023 -5450.489
1.475901032138386E9 -893480655.0 -607891858.0 2023-02-02T05:13:24
2022-09-18 2023-04-23T10:51:15 k
LdFXF7Kmfzgmnn2R6zLsXdmi3A2cLBLq4G4WDVNDhxvH7dYH8Kga2WA47uSIxp6NSrwPSdw0ssB1TS8RFJTDJAB0Uba3e05NL2Aiw0ja
{"restaurant":"Pizza Hut","menu":["pizza","pasta","salad"]} true
1 2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤 [...]
+60 2023-08-27 false -52 -2338 -757056972
1047567408607120856 6541476642780646552 6614.0894
-1.204448798517855E9 236657733.0 731515433.0 2022-12-29T14:47:30
2022-09-24 2023-08-01T12:41:59 O F
RM4F1Ke7lkcnuxF2nK0j9VBW3MDcgyHR4pseBjtFnqS6GUkVFuzF6u3Cp9Nv7ab0O6UYrpP4DhU
{"game":"Chess","players":2,"time":"1 hour"} true 1 2 3
4 5 6.0 7.0 888888888.000000000 999999999.000000000
2023-08-24 2023-08-24T12:00 2023-08-24 2023-08-24T12:00
我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+62 2023-08-21 false 81 20302 -200761532
6365479976421007608 \N -29916.533 1.709141750828478E9
549873536.0 -119205359.0 2023-05-04T01:14:51 2022-09-17
2022-12-04T19:30:09 d v
BKWy9dTNg1aZW7ancEJAmEDOPK5TwFsNSHbI78emu9gymeIlx5NoLmyii0QAqdzRvSQPZKiqKkwInGCTIBnK1yYkK7zD
{"username":"user123","password":"pass123","email":"[email protected]"}
true 1 2 3 4 5 6.0 7.0
888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤身体
我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+65 2023-08-09 false 94 31514 814994517
-297697460695940343 734910652450318597 -13061.892
6.2750847041706E7 -9808654.0 \N 2023-08-14T22:01:27
2023-05-19 2022-11-13T13:44:28 V
aGeMsI24O12chGlP5ak0AHghAz7bu5MargJBStHnt0yMnChH0JnfYhsfH1u59XIHkJKMsHYktBqORkGlovu8V47E74KeFpaqxn5yLyXfDbhhzUKf
{"language":"Python","version":3.9,"frameworks":["Django","Flask"]}
true 1 2 3 4 5 6.0 7.0
888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 我能吞下玻璃而不伤身体
我能吞下玻 [...]
+66 2023-08-15 true -91 28378 609923317
4872185586197131212 1207709464099378591 \N -1.863683325985123E9
-783792012.0 -708986976.0 2022-09-24T10:39:23 2022-09-24
2022-10-16T18:36:43 Y z
AI1BSPQdKiHJiQH1kguyLSWsDXkC7zwy7PwgWnyGSaa9tBKRex8vHBdxg2QSKZKL2mV2lHz7iI1PnsTd4MXDcIKhqiHyPuQPt2tEtgt0UgF6
{"book":{"title":"The Great Gatsby","author":"F. Scott
Fitzgerald"},"year":1925} true 1 2 3 4 5
6.0 7.0 888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08-24T12:00 [...]
+68 2023-08-23 true -73 20117 1737338128
795638676048937749 -5551546237562433901 -30627.04
6.8589475684545E7 585022347.0 513722420.0 2022-12-28T20:26:51
2022-10-04 2023-07-30T00:20:06 y
keZ3JlWWpdnPBejf0cuiCQCVBBTd5gjvO08NVdcAFewqL7nRT4N9lnvSU6pWmletA5VbPQCeQapJdcnQCHfZUDCf4ulCnczyqr7SGrbGRT0XYcd7iktKM
{"country":"Brazil","continent":"South America","population":211049527} true
1 2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12: [...]
+80 2023-08-18 false -18 -8971 679027874
6535956962935330265 3960889045799757165 -13219.76
1.187161924505394E9 -526615878.0 -947410627.0 2023-03-11T07:40
2022-11-29 2023-01-14T07:24:07 \N D
3Nhx6xX1qdwaq7lxwLRSKMtJFbC03swWv12mpySSVysH3igGZTiGPuKMsYW7HAkf6CWc7c0nzqDsjuH3FYVMNCWRmfxMrmY8rykQCC4Ve
{"car":"BMW","model":"X5","year":2020,"color":"black"} true 1
2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 [...]
+81 2023-08-23 false 106 11492 -667795397
4480250461471356146 -5346660566234294101 9082.75 3.85167225902608E8
-717553011.0 649146853.0 2023-03-20T03:33:16 2022-11-24
2023-02-16T18:29:41 G 9
Lk3eNVQNjucbekD1rZmUlGPiXS5JvcWr2LQzRU8GSGIbSag
{"flower":"rose","color":"red","fragrance":true} true 1 2
3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+85 2023-08-11 true -7 24304 -2043877415
-2024144417867729183 \N 5363.0244 -5.78615669042831E8
-378574346.0 -810302932.0 2023-07-15T01:07:41 2023-08-13
2023-01-20T11:57:48 i WQ9dh9ajPu0y
{"country":"France","capital":"Paris","population":67081000} true 1
2 3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
+90 2023-08-27 true 22 16456 -1476824962
-3279894870153540825 8990195191470116763 26651.906
2.06860148942546E8 -580959198.0 -210329147.0 2022-10-07T03:11:03
2023-03-18 2023-04-15T00:38:33 T L
QW0GQ3GoMtHgxPQOWGfVaveynahNpsNs09siMFA1OtO6QEDBQTdivmGyq7bFzejAqwbbVQQpREAmeLjcFSXLnQuou2KbwYD
{"company":"Apple","products":[{"name":"iPhone","price":1000},{"name":"MacBook","price":1500}]}
true 1 2 3 4 5 6.0 7.0
888888888.000000000 999999999.000000000 2023-08-24
2023-08-24T12:00 2023-08-24 2023-08 [...]
+91 2023-08-27 true 90 2465 702240964
6373830997821598984 305860046137409400 15991.356
1.599972327386147E9 -165530947.0 \N 2023-04-26T19:31:10
2023-07-21 \N 2
B7YKYBYT8w0YC926bZ8Yz1VzyiWw2NWDAiTlEoPVyz9AXGti2Npg1FxWqWk4hEaALw0ZBSuiAIPj41lq36g5QRpPmAjNPK
{"fruit":"apple","color":"red","qty":5,"price":2.5} true 1 2
3 4 5 6.0 7.0 888888888.000000000
999999999.000000000 2023-08-24 2023-08-24T12:00 2023-08-24
2023-08-24T12:00 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 我能吞下玻璃而不伤身体 \N
-- !sql_column_separator --
diff --git a/regression-test/data/nereids_rules_p0/pkfk/eliminate_inner.out
b/regression-test/data/nereids_rules_p0/pkfk/eliminate_inner.out
index 1a2c37fe9df..5441e21cdf8 100644
--- a/regression-test/data/nereids_rules_p0/pkfk/eliminate_inner.out
+++ b/regression-test/data/nereids_rules_p0/pkfk/eliminate_inner.out
@@ -144,7 +144,7 @@ PhysicalResultSink
--NestedLoopJoin[INNER_JOIN]
----filter((pkt.pk = 1))
------PhysicalOlapScan[pkt]
-----filter((cast(f as DECIMALV3(38, 6)) = 1.000000) and (fkt_not_null.fk = 1))
+----filter((cast(fkt_not_null.f as DECIMALV3(38, 6)) = 1.000000) and
(fkt_not_null.fk = 1))
------PhysicalOlapScan[fkt_not_null]
-- !res --
@@ -155,9 +155,9 @@ pk with filter that not same as fk
-- !shape --
PhysicalResultSink
--NestedLoopJoin[INNER_JOIN]
-----filter((cast(p as DECIMALV3(38, 6)) = 1.000000) and (pkt.pk = 1))
+----filter((cast(pkt.p as DECIMALV3(38, 6)) = 1.000000) and (pkt.pk = 1))
------PhysicalOlapScan[pkt]
-----filter((cast(f as DECIMALV3(38, 6)) = 1.000000) and (fkt_not_null.fk = 1))
+----filter((cast(fkt_not_null.f as DECIMALV3(38, 6)) = 1.000000) and
(fkt_not_null.fk = 1))
------PhysicalOlapScan[fkt_not_null]
-- !res --
@@ -167,7 +167,7 @@ simple_case
-- !shape --
PhysicalResultSink
---filter(( not fk IS NULL))
+--filter(( not fkt.fk IS NULL))
----PhysicalOlapScan[fkt]
-- !res --
@@ -300,7 +300,7 @@ PhysicalResultSink
--NestedLoopJoin[INNER_JOIN]
----filter((pkt.pk = 1))
------PhysicalOlapScan[pkt]
-----filter((cast(f as DECIMALV3(38, 6)) = 1.000000) and (fkt.fk = 1))
+----filter((cast(fkt.f as DECIMALV3(38, 6)) = 1.000000) and (fkt.fk = 1))
------PhysicalOlapScan[fkt]
-- !res --
@@ -311,9 +311,9 @@ pk with filter that not same as fk
-- !shape --
PhysicalResultSink
--NestedLoopJoin[INNER_JOIN]
-----filter((cast(p as DECIMALV3(38, 6)) = 1.000000) and (pkt.pk = 1))
+----filter((cast(pkt.p as DECIMALV3(38, 6)) = 1.000000) and (pkt.pk = 1))
------PhysicalOlapScan[pkt]
-----filter((cast(f as DECIMALV3(38, 6)) = 1.000000) and (fkt.fk = 1))
+----filter((cast(fkt.f as DECIMALV3(38, 6)) = 1.000000) and (fkt.fk = 1))
------PhysicalOlapScan[fkt]
-- !res --
@@ -325,7 +325,7 @@ multi_table_join_with_pk_predicate
PhysicalResultSink
--hashJoin[INNER_JOIN] hashCondition=((fkt_not_null.fk = fkt_not_null2.fk))
otherCondition=()
----hashJoin[INNER_JOIN] hashCondition=((pkt.pk = fkt_not_null.fk))
otherCondition=()
-------filter((cast(p as DECIMALV3(38, 6)) = 1.000000))
+------filter((cast(pkt.p as DECIMALV3(38, 6)) = 1.000000))
--------PhysicalOlapScan[pkt]
------PhysicalOlapScan[fkt_not_null]
----PhysicalOlapScan[fkt_not_null(fkt_not_null2)]
diff --git
a/regression-test/suites/compaction/test_mow_compact_multi_segments.groovy
b/regression-test/suites/compaction/test_mow_compact_multi_segments.groovy
index 09bb3c18a93..2fa2f993357 100644
--- a/regression-test/suites/compaction/test_mow_compact_multi_segments.groovy
+++ b/regression-test/suites/compaction/test_mow_compact_multi_segments.groovy
@@ -190,8 +190,14 @@ suite("test_mow_compact_multi_segments", "nonConcurrent") {
getTabletStatus(tablet, 2, 3)
// trigger compaction
+ // The cloud and the local cumulative policies are different classes with
their own debug point,
+ // and this suite runs in both deployments, so pin the input rowsets on
both. Without the local
+ // one, the storage-compute-coupled BE picks the input rowsets by
size/score: [2-2] alone is
+ // below both thresholds and is skipped, and the next round merges [2-2]
with [3-3].
GetDebugPoint().enableDebugPointForAllBEs("CloudSizeBasedCumulativeCompactionPolicy::pick_input_rowsets.set_input_rowsets",
[tablet_id: "${tablet.TabletId}", start_version: 2, end_version:
2])
+
GetDebugPoint().enableDebugPointForAllBEs("SizeBasedCumulativeCompactionPolicy::pick_input_rowsets.set_input_rowsets",
+ [tablet_id: "${tablet.TabletId}", start_version: 2, end_version:
2])
def (code, out, err) =
be_run_cumulative_compaction(backendId_to_backendIP.get(backend_id),
backendId_to_backendHttpPort.get(backend_id), tablet_id)
logger.info("Run compaction: code=" + code + ", out=" + out + ", err=" +
err)
assertEquals(code, 0)
@@ -204,7 +210,9 @@ suite("test_mow_compact_multi_segments", "nonConcurrent") {
}
sleep(100)
}
- getTabletStatus(tablet, 2, 1)
+ // enableAssert: the loop above exits on timeout as well, so assert the
segment count here
+ // instead of letting a compaction that never ran slip through to the next
step.
+ getTabletStatus(tablet, 2, 1, true)
sql """ select * from ${tableName} limit 1; """
// load 2
@@ -236,6 +244,8 @@ suite("test_mow_compact_multi_segments", "nonConcurrent") {
// trigger compaction for load 2
GetDebugPoint().enableDebugPointForAllBEs("CloudSizeBasedCumulativeCompactionPolicy::pick_input_rowsets.set_input_rowsets",
[tablet_id: "${tablet.TabletId}", start_version: 3, end_version:
3])
+
GetDebugPoint().enableDebugPointForAllBEs("SizeBasedCumulativeCompactionPolicy::pick_input_rowsets.set_input_rowsets",
+ [tablet_id: "${tablet.TabletId}", start_version: 3, end_version:
3])
(code, out, err) =
be_run_cumulative_compaction(backendId_to_backendIP.get(backend_id),
backendId_to_backendHttpPort.get(backend_id), tablet_id)
logger.info("Run compaction: code=" + code + ", out=" + out + ", err=" +
err)
assertEquals(code, 0)
@@ -249,7 +259,9 @@ suite("test_mow_compact_multi_segments", "nonConcurrent") {
}
sleep(100)
}
- getTabletStatus(tablet, 3, 1)
+ // enableAssert: the loop above exits on timeout as well, so assert the
segment count here
+ // instead of letting a compaction that never ran slip through to the next
step.
+ getTabletStatus(tablet, 3, 1, true)
GetDebugPoint().enableDebugPointForAllBEs("DeleteBitmapAction._handle_show_local_delete_bitmap_count.vacuum_stale_rowsets")
// cloud
GetDebugPoint().enableDebugPointForAllBEs("DeleteBitmapAction._handle_show_local_delete_bitmap_count.start_delete_unused_rowset")
// local
diff --git
a/regression-test/suites/compaction/test_vertical_compaction_agg_state.groovy
b/regression-test/suites/compaction/test_vertical_compaction_agg_state.groovy
index b9c56d2d5dd..f66c2bf8956 100644
---
a/regression-test/suites/compaction/test_vertical_compaction_agg_state.groovy
+++
b/regression-test/suites/compaction/test_vertical_compaction_agg_state.groovy
@@ -62,7 +62,7 @@ suite("test_vertical_compaction_agg_state") {
('a',collect_set_state('aa'))
"""
- qt_select_default """ SELECT user_id,collect_set_merge(agg_user_id)
FROM ${tableName} t group by user_id ORDER BY user_id;"""
+ qt_select_default """ SELECT
user_id,array_sort(collect_set_merge(agg_user_id)) FROM ${tableName} t group by
user_id ORDER BY user_id;"""
sql """ INSERT INTO ${tableName} VALUES
('b',collect_set_state('b'))
diff --git
a/regression-test/suites/load_p0/routine_load/test_routine_load.groovy
b/regression-test/suites/load_p0/routine_load/test_routine_load.groovy
index bbc058b861e..c4e4b01343b 100644
--- a/regression-test/suites/load_p0/routine_load/test_routine_load.groovy
+++ b/regression-test/suites/load_p0/routine_load/test_routine_load.groovy
@@ -1143,7 +1143,29 @@ suite("test_routine_load","p0") {
}
}
+ def count = 0
def tableName1 = "routine_load_" + tableName
+ // Leaving NEED_SCHEDULE only means the job has been
scheduled, not that any batch
+ // has been committed. Without waiting for the rows to become
visible the query
+ // below races the load and returns whatever happens to have
landed so far.
+ while (true) {
+ def res = sql "select count(*) from ${tableName1}"
+ def state = sql "show routine load for ${jobs[i]}"
+ log.info("routine load state:
${state[0][8].toString()}".toString())
+ log.info("routine load statistic:
${state[0][14].toString()}".toString())
+ log.info("reason of state changed:
${state[0][17].toString()}".toString())
+ if (res[0][0] > 0) {
+ break
+ }
+ if (count >= 120) {
+ log.error("routine load can not visible for long time")
+ assertEquals(20, res[0][0])
+ break
+ }
+ sleep(5000)
+ count++
+ }
+
if (i <= 3) {
qt_sql_load_to_single_tablet "select * from ${tableName1}
order by k00,k01"
} else {
diff --git a/regression-test/suites/query_p0/cache/sql_cache_object_type.groovy
b/regression-test/suites/query_p0/cache/sql_cache_object_type.groovy
index 6300840d20f..4e2520e70c5 100644
--- a/regression-test/suites/query_p0/cache/sql_cache_object_type.groovy
+++ b/regression-test/suites/query_p0/cache/sql_cache_object_type.groovy
@@ -98,8 +98,13 @@ suite("sql_cache_object_type") {
assertTrue(isNonEmpty(asBinaryCached[0][0]))
assertTrue(isNonEmpty(asBinaryCached[0][1]))
+ // The sql cache is best-effort: the FE map holds soft values under a
bounded size
+ // (Config.sql_cache_manage_num) and the rows themselves live in the
BE result cache, so the
+ // entry created above may legitimately be gone by now. Re-prime it
instead of asserting it
+ // survived; what must hold is that this setting is served its own
NULLs and never the
+ // binary rows cached under the other one.
run "set return_object_data_as_binary=false"
- assertTrue(hasSqlCache(objectSql))
+ primeSqlCache(objectSql)
def asNullAgain = run(objectSql)
assertNull(asNullAgain[0][0])
assertNull(asNullAgain[0][1])
diff --git
a/regression-test/suites/query_p0/runtime_filter/rf_bucket_pruning.groovy
b/regression-test/suites/query_p0/runtime_filter/rf_bucket_pruning.groovy
index 9419c00dffb..084b389ff7b 100644
--- a/regression-test/suites/query_p0/runtime_filter/rf_bucket_pruning.groovy
+++ b/regression-test/suites/query_p0/runtime_filter/rf_bucket_pruning.groovy
@@ -23,6 +23,11 @@ suite("rf_bucket_pruning", "nonConcurrent") {
sql "set enable_runtime_filter_bucket_prune=true"
sql "set runtime_filter_wait_infinitely=true"
sql "set runtime_filter_type='IN'"
+ // Shared regression environments fuzz runtime_filter_max_in_num, and one
of the four fuzz
+ // branches sets it to 0. Bucket pruning inverts the IN set, so it bails
out as soon as the set
+ // is larger than this bound -- with 0 even a single-value filter prunes
nothing. Pin it, the
+ // same way rf_partition_pruning does.
+ sql "set runtime_filter_max_in_num=1024"
sql "set disable_join_reorder=true"
sql "set enable_profile=true"
sql "set profile_level=2"
diff --git
a/regression-test/suites/temp_table_p0/test_drop_temporary_table.groovy
b/regression-test/suites/temp_table_p0/test_drop_temporary_table.groovy
new file mode 100644
index 00000000000..25a37803e33
--- /dev/null
+++ b/regression-test/suites/temp_table_p0/test_drop_temporary_table.groovy
@@ -0,0 +1,92 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// DROP TEMPORARY TABLE only ever targets the temporary table of the current
session. Name
+// resolution falls back to the normal table when this session owns no
temporary one, so the drop
+// path has to tell "found the temporary table" from "found something else
with that name":
+// it must never drop the normal table, and IF EXISTS must turn the miss into
a no-op instead of
+// raising "Unknown table".
+suite('test_drop_temporary_table', 'p0') {
+ def srcTable = "t_drop_temp_src"
+ def sharedName = "t_drop_temp_shared"
+
+ // The two tables carry different row counts so a query can tell which one
it resolved to:
+ // the normal table holds 1 row, the temporary table built from srcTable
holds 3.
+ sql """DROP TABLE IF EXISTS ${srcTable}"""
+ sql """
+ CREATE TABLE ${srcTable} (id INT, name VARCHAR(32))
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ('replication_num' = '1')
+ """
+ sql """INSERT INTO ${srcTable} VALUES (1, 'Alice'), (2, 'Bob'), (3,
'Carl')"""
+
+ sql """DROP TABLE IF EXISTS ${sharedName}"""
+ sql """
+ CREATE TABLE ${sharedName} (id INT, name VARCHAR(32))
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ('replication_num' = '1')
+ """
+ sql """INSERT INTO ${sharedName} VALUES (99, 'normal')"""
+
+ try {
+ // Nothing at all goes by this name: plainly a no-op.
+ sql """DROP TEMPORARY TABLE IF EXISTS t_drop_temp_never_existed"""
+
+ // Only a normal table goes by this name. The temporary table asked
for does not exist,
+ // so IF EXISTS makes this a no-op -- and the normal table must
survive untouched.
+ sql """DROP TEMPORARY TABLE IF EXISTS ${sharedName}"""
+ assertEquals(1, sql("select * from ${sharedName}").size())
+
+ // Without IF EXISTS the same statement reports the missing temporary
table, and still
+ // must not drop the normal one.
+ try {
+ sql """DROP TEMPORARY TABLE ${sharedName}"""
+ throw new IllegalStateException("Should throw error")
+ } catch (Exception ex) {
+ assertTrue(ex.getMessage().contains("Unknown table"),
ex.getMessage())
+ }
+ assertEquals(1, sql("select * from ${sharedName}").size())
+
+ // With a temporary table shadowing the normal one, the drop takes the
temporary table
+ // and leaves the normal table in place.
+ sql """
+ CREATE TEMPORARY TABLE ${sharedName} PROPERTIES ('replication_num'
= '1') AS
+ SELECT * FROM ${srcTable}
+ """
+ assertEquals(3, sql("select * from ${sharedName}").size())
+ sql """DROP TEMPORARY TABLE ${sharedName}"""
+ assertEquals(1, sql("select * from ${sharedName}").size())
+
+ // A temporary table belongs to the session that created it: another
session neither sees
+ // it nor can drop it, and its IF EXISTS no-op must leave both tables
alone.
+ sql """
+ CREATE TEMPORARY TABLE ${sharedName} PROPERTIES ('replication_num'
= '1') AS
+ SELECT * FROM ${srcTable}
+ """
+ connect('root') {
+ sql "use ${context.dbName}"
+ assertEquals(1, sql("select * from ${sharedName}").size())
+ sql """DROP TEMPORARY TABLE IF EXISTS ${sharedName}"""
+ assertEquals(1, sql("select * from ${sharedName}").size())
+ }
+ assertEquals(3, sql("select * from ${sharedName}").size())
+ } finally {
+ sql """DROP TEMPORARY TABLE IF EXISTS ${sharedName}"""
+ sql """DROP TABLE IF EXISTS ${sharedName}"""
+ sql """DROP TABLE IF EXISTS ${srcTable}"""
+ }
+}
diff --git
a/regression-test/suites/temp_table_p0/test_temp_table_ctas_name_conflict.groovy
b/regression-test/suites/temp_table_p0/test_temp_table_ctas_name_conflict.groovy
new file mode 100644
index 00000000000..7e8e1ba9bc0
--- /dev/null
+++
b/regression-test/suites/temp_table_p0/test_temp_table_ctas_name_conflict.groovy
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// A temporary table is stored under <sessionId>#TEMP#<name>, so its name only
has to be unique
+// inside the creating session. CTAS probes the catalog for an existing table
before it creates
+// anything (CreateTableCommand.targetTableExists); that probe has to use the
same namespace the
+// table will be created in, otherwise an unrelated normal table of the same
name makes
+// CREATE TEMPORARY TABLE ... AS SELECT fail with "Table 'x' already exists".
+suite('test_temp_table_ctas_name_conflict', 'p0') {
+ def srcTable = "t_ctas_src"
+ def sharedName = "t_ctas_shadowed"
+
+ sql """DROP TABLE IF EXISTS ${srcTable}"""
+ sql """
+ CREATE TABLE ${srcTable} (
+ id INT,
+ name VARCHAR(32)
+ )
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ('replication_num' = '1')
+ """
+ sql """INSERT INTO ${srcTable} VALUES (1, 'Alice'), (2, 'Bob'), (3,
'Carl')"""
+
+ // A normal table occupies the name in this database, and stays empty on
purpose so that a
+ // query can tell the two tables apart.
+ sql """DROP TABLE IF EXISTS ${sharedName}"""
+ sql """
+ CREATE TABLE ${sharedName} (
+ id INT,
+ name VARCHAR(32)
+ )
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ('replication_num' = '1')
+ """
+
+ try {
+ // The regression: this must not be rejected because of the normal
table above.
+ sql """
+ CREATE TEMPORARY TABLE ${sharedName} PROPERTIES ('replication_num'
= '1') AS
+ SELECT * FROM ${srcTable}
+ """
+
+ // Inside this session the temporary table shadows the normal one.
+ def showCreate = sql "show create table ${sharedName}"
+ assertEquals(1, showCreate.size())
+ assertEquals(sharedName, showCreate[0][0])
+ assertTrue(showCreate[0][1].contains("CREATE TEMPORARY TABLE"),
showCreate[0][1])
+ assertEquals(3, sql("select * from ${sharedName}").size())
+
+ // Creating it a second time in the same session does collide, because
now the probe and
+ // the create target the very same <sessionId>#TEMP#<name>.
+ try {
+ sql """
+ CREATE TEMPORARY TABLE ${sharedName} PROPERTIES
('replication_num' = '1') AS
+ SELECT * FROM ${srcTable}
+ """
+ throw new IllegalStateException("Should throw error")
+ } catch (Exception ex) {
+ assertTrue(ex.getMessage().contains("already exists"),
ex.getMessage())
+ }
+
+ // A normal table of an already taken name must still be rejected: the
probe is only
+ // relaxed for the temporary namespace, not disabled.
+ try {
+ sql """CREATE TABLE ${sharedName} PROPERTIES ('replication_num' =
'1') AS SELECT * FROM ${srcTable}"""
+ throw new IllegalStateException("Should throw error")
+ } catch (Exception ex) {
+ assertTrue(ex.getMessage().contains("already exists"),
ex.getMessage())
+ }
+
+ // Another session has its own temporary namespace, so it may create
the same name again,
+ // and it keeps seeing the normal (empty) table until it does.
+ connect('root') {
+ sql "use ${context.dbName}"
+
+ def otherShowCreate = sql "show create table ${sharedName}"
+ assertEquals(1, otherShowCreate.size())
+ assertFalse(otherShowCreate[0][1].contains("CREATE TEMPORARY
TABLE"), otherShowCreate[0][1])
+ assertEquals(0, sql("select * from ${sharedName}").size())
+
+ sql """
+ CREATE TEMPORARY TABLE ${sharedName} PROPERTIES
('replication_num' = '1') AS
+ SELECT * FROM ${srcTable}
+ """
+ assertEquals(3, sql("select * from ${sharedName}").size())
+ }
+ } finally {
+ sql """DROP TEMPORARY TABLE IF EXISTS ${sharedName}"""
+ sql """DROP TABLE IF EXISTS ${sharedName}"""
+ sql """DROP TABLE IF EXISTS ${srcTable}"""
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]