matrixorigin / matrixorigin/matrixone

[Bug]: CREATE TEMPORARY TABLE ... CLONE leaves an unqueryable session ghost after failing

Open
#27,042 2 comments 0 reactions 1 assignee Claimed by @gouhongshen View on GitHub
ai-easy deferred kind/bug
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.