drizzle-team / drizzle-team/drizzle-orm
[BUG]: sql`` selected fields strip table qualifiers inside correlated subqueries
- 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
Assessment
This issue has not been assessed yet.