matrixorigin / matrixorigin/matrixone
[Bug]: CREATE TEMPORARY TABLE ... CLONE leaves an unqueryable session ghost after failing
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 311
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 768
Description
## Description
`CREATE TEMPORARY TABLE dst CLONE src` is accepted by the SQL grammar, but it cannot create a usable temporary destination. The first execution returns `ERROR 1146` for the destination itself and leaves a stale session alias. Within that session, the table is simultaneously:
- absent to `SHOW`, `SELECT`, `INSERT`, and `DROP`;
- present to all later `CREATE TEMPORARY TABLE` statements.
Even `DROP TEMPORARY TABLE IF EXISTS` returns success without clearing the ghost alias. The session cannot reuse the name until it disconnects.
## Environment
- Branch: `main`
- Commit: `2d9ee9c75398b5492f14db7d9bd4d9f777251bd3`
- Test date: `2026-08-12`
- Deployment: local single process, 1 Log Service + 1 TN + 2 CN; reproduced through CN1
## Steps to reproduce
Run all statements in one session:
```sql
DROP DATABASE IF EXISTS clone_temp_target;
CREATE DATABASE clone_temp_target;
USE clone_temp_target;
CREATE TABLE src(id INT PRIMARY KEY, v VARCHAR(20));
INSERT INTO src VALUES (1,'one'),(2,'two');
CREATE TEMPORARY TABLE temp_dst CLONE src;
SHOW CREATE TABLE temp_dst;
SELECT * FROM temp_dst;
CREATE TEMPORARY TABLE temp_dst CLONE src;
CREATE TEMPORARY TABLE temp_dst(id INT PRIMARY KEY, v VARCHAR(20));
DROP TEMPORARY TABLE IF EXISTS temp_dst;
CREATE TEMPORARY TABLE temp_dst(id INT PRIMARY KEY, v VARCHAR(20));
```
## Actual behavior
```text
CREATE TEMPORARY TABLE temp_dst CLONE src;
ERROR 1146 (HY000): no such table clone_temp_target.temp_dst
SHOW CREATE TABLE temp_dst;
ERROR 1146 (HY000): no such table clone_temp_target.temp_dst
SELECT * FROM temp_dst;
ERROR 1146 (HY000): table "temp_dst" does not exist
CREATE TEMPORARY TABLE temp_dst CLONE src;
ERROR 1050 (HY000): table temp_dst already exists
CREATE TEMPORARY TABLE temp_dst(id INT PRIMARY KEY, v VARCHAR(20));
ERROR 1050 (HY000): table temp_dst already exists
DROP TEMPORARY TABLE IF EXISTS temp_dst;
Query OK
CREATE TEMPORARY TABLE temp_dst(id INT PRIMARY KEY, v VARCHAR(20));
ERROR 1050 (HY000): table temp_dst already exists
```
There is no permanent catalog row for the target.
## Expected behavior
Because the grammar explicitly supports the temporary option for CLONE, the statement should create a queryable session-local table containing the source rows.
If temporary CLONE is intentionally unsupported, it must be rejected before creating physical or session state, with a clear unsupported-operation error. A failed statement must never reserve an unusable name in the session.
## Stability and controls
- Reproducer: `3/3` with independent names `temp_dst_r1`, `temp_dst_r2`, and `temp_dst_r3` in one session.
- Every first attempt returned `1146`; every second attempt returned `1050`.
- `DROP TEMPORARY TABLE IF EXISTS` returned success but did not clear the stale name.
- Permanent catalog count for all three target names: `0`.
- Fresh-session control: ordinary temporary creation, insert, select, and drop using the same name all succeeded.
- `CREATE TEMPORARY TABLE ... LIKE src` plus `INSERT ... SELECT` succeeded and returned both source rows.
- Temporary-source control: cloning a temporary source into a permanent destination succeeded and returned the exact row.
- Source rows remained unchanged.
## Evidence
The complete reproducer is preserved at:
```text
/private/tmp/mo-clone-2d9-validation/temp_target.sql
```
Three-round signature:
```text
r1: first=1146, second=1050
r2: first=1146, second=1050
r3: first=1146, second=1050
permanent-catalog-count=0
```
## Code analysis
The failure crosses the temporary-table create path and the generic block-clone path:
1. `pkg/sql/parsers/dialect/mysql/mysql_sql.y:9843-9854` explicitly accepts `CREATE temporary_opt TABLE ... CLONE ...` and stores the temporary flag.
2. `pkg/sql/compile/ddl.go:1397-1406` rewrites the destination to a generated session-local physical name. After creation, `pkg/sql/compile/ddl.go:1505-1513` registers the user alias; `pkg/sql/compile/ddl.go:2091-2095` commits that alias before `CreateTable` returns success.
3. `pkg/sql/compile/ddl.go:4033-4047` then unconditionally enters `RestoreTable`.
4. The clone operator still resolves `Ctx.DstTblName`, which is the user-facing alias. `pkg/sql/colexec/table_clone/table_clone.go:321-325` calls `dstDB.Relation` with that alias instead of the generated physical temporary-table name, producing `1146`.
5. The statement rollback removes the physical relation, but the already-committed session alias is outside transaction rollback. Subsequent create checks `session.GetTempTable` at `pkg/sql/compile/ddl.go:1355-1359` and returns `1050`.
This also explains why disconnecting clears the problem while `DROP ... IF EXISTS` does not.
## Regression coverage
Add a same-session distributed CLONE BVT under `test/distributed/cases/git4data/clone/` that asserts:
1. temporary destination CLONE returns the exact source schema and rows;
2. the target is invisible from a second session;
3. disconnect removes it;
4. any injected or unsupported failure leaves no physical relation and no session alias;
5. `DROP TEMPORARY TABLE IF EXISTS` truly releases the name.
Add a focused planner/operator test that verifies the clone destination is the generated temporary physical name, not the user alias.
## Related
No matching open or closed issue or fix PR was found using exact searches for `CREATE TEMPORARY TABLE ... CLONE`, temporary CLONE `no such table`, and stale temporary-table aliases. #26087 concerns concurrent Data Branch quota accounting and is not a duplicate.
Contributor guide
Assessment
This issue has not been assessed yet.