drizzle-team / drizzle-team/drizzle-orm

[BUG]: sql`` selected fields strip table qualifiers inside correlated subqueries

Open
#5,734 0 comments 3 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Report hasn't been filed before.

- [x] I have verified that the bug I'm about to report hasn't been filed before.

### What version of `drizzle-orm` are you using?

1.0.0-beta.22

Also reproduced on 0.44.7.

### What version of `drizzle-kit` are you using?

N/A

### Other packages

N/A

### Describe the Bug

When a raw `sql`` expression is used as a selected field, Drizzle renders interpolated columns without table qualifiers inside that SQL chunk.

This breaks correlated subqueries in the select list and can silently return wrong results, because a condition like:

```sql
`b`.`c_id` = `a`.`c_id`
```

is emitted as:

```sql
`c_id` = `c_id`
```

The same `sql`` expression renders correctly when used in `.where(...)`.

Reproduction:

```ts
import { mysqlTable, int, varchar } from "drizzle-orm/mysql-core";
import { drizzle } from "drizzle-orm/mysql-proxy";
import { eq, sql, and } from "drizzle-orm";

const tableA = mysqlTable("a", {
id: int("id").primaryKey(),
name: varchar("name", { length: 255 }).notNull(),
bId: int("b_id").notNull(),
cId: int("c_id").notNull(),
});

const tableB = mysqlTable("b", {
id: int("id").primaryKey(),
cId: int("c_id").notNull(),
label: varchar("label", { length: 255 }).notNull(),
});

const db = drizzle(async () => ({ rows: [] }));

const subquery = sql`${db
.select({ id: tableB.id })
.from(tableB)
.where(eq(tableB.cId, tableA.cId))
.limit(1)}`;

const raw = sql`select ${tableB.label} from ${tableB} where ${tableB.cId} = ${tableA.cId} limit 1`;

const query = db
.select({
id: tableA.id,
name: tableA.name,
bRaw: raw,
bSq: subquery,
})
.from(tableA)
.where(and(eq("RAW", raw), eq("SQ", subquery)));

console.log(query.toSQL().sql);
```

Actual SQL:

```sql
select `id`, `name`, select `label` from `b` where `c_id` = `c_id` limit 1, (select `id` from `b` where `b`.`c_id` = `a`.`c_id` limit ?) from `a` where ((? = select `b`.`label` from `b` where `b`.`c_id` = `a`.`c_id` limit 1) and (? = (select `id` from `b` where `b`.`c_id` = `a`.`c_id` limit ?)))
```

Notice that in the select list, `bRaw` renders as:

```sql
select `label` from `b` where `c_id` = `c_id` limit 1
```

But the same `raw` SQL chunk in `.where(...)` renders correctly:

```sql
select `b`.`label` from `b` where `b`.`c_id` = `a`.`c_id` limit 1
```

Expected behavior:

The raw selected field should preserve table qualifiers, the same way it does in `.where(...)`:

```sql
select `b`.`label` from `b` where `b`.`c_id` = `a`.`c_id` limit 1
```

Why this matters:

This can silently return wrong data. In SQL, `where c_id = c_id` inside the subquery resolves both sides to the inner table scope, rather than correlating the subquery with the outer table.

Possibly related:

- https://github.com/drizzle-team/drizzle-orm/issues/3096
- https://github.com/drizzle-team/drizzle-orm/issues/4696
- https://github.com/drizzle-team/drizzle-orm/issues/5049

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.