drizzle-team / drizzle-team/drizzle-orm
[BUG]: Drizzle Doesn't Disambiguate column names (specifically subqueries, and CTEs)
- 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?
0.44.7
### What version of `drizzle-kit` are you using?
0.30.6
### Other packages
drizzle-zod"@0.8.3
### Describe the Bug
Repro Steps + Undesired Behavior
This simple code (which has an obvious correct SQL interpretation):
```typescript
db.select({
id: view1.id
name: view1.name,
})
.from(view1)
.innerJoin(table1, eq(view1.id, table1.id))
```
produces this incorrect SQL:
```sql
select
"id",
"name"
from "view1"
inner join "table1" on "id" = "table1"."id"
```
Desired Result
```sql
select
"view1"."id",
"view1"."name"
from "view1"
inner join "table1" on "view1"."id" = "table1"."id"
```
Workaround
```typescript
type SubqueryOrCTE =
| SubqueryWithSelection
| WithSubqueryWithSelection;
// The disambiguate function takes a view of the above defined type and returns a new 'view'
// where each query column is associated with its alias.
// It essentially allows the user to reference a database column in code without using the
// actual column name, thus reducing the likelihood of errors due to manual typing of column names.
export function disambiguate(
view: V,
): V & { noFieldAlias: V } {
// A helper function to get alias of a column. If 'noAlias' is true,
// it simply returns the column reference without aliasing.
function _getProperty(target: any, prop: any, noFieldAlias: boolean) {
const column = target[prop];
if ('fieldAlias' in column) {
const _sql = sql`${target}.${column}`;
if (noFieldAlias) {
return _sql;
}
return _sql.as(column.fieldAlias);
}
return target[prop];
}
let noAliasProxy: V | null = null;
// Use JavaScript Proxy to intercept the get() operations on 'view'
// When the 'noAlias' property is accessed, return a new Proxy where *no* column aliasing will be
// done when accessing properties. This allows query without field alias.
// For all other property accesses, aliasing will be performed as usual.
return new Proxy(view, {
get(target, prop: any) {
if (prop === 'noFieldAlias') {
if (noAliasProxy === null) {
noAliasProxy = new Proxy(view, {
get(target, prop: any) {
return _getProperty(target, prop, true);
},
});
}
return noAliasProxy;
}
return _getProperty(target, prop, false);
},
}) as unknown as V & { noFieldAlias: V };
}
```
```typescript
const disambiguatedView1 = disambiguate(view1)
db.select({
id: disambiguatedView1.id
name: disambiguatedView1.name,
})
.from(view1)
.innerJoin(table1, eq(disambiguatedView1.noFieldAlias.id, table1.id))
```
Contributor guide
Assessment
This issue has not been assessed yet.