drizzle-team / drizzle-team/drizzle-orm

[BUG]: Drizzle Doesn't Disambiguate column names (specifically subqueries, and CTEs)

Open
#5,049 0 comments 0 reactions 0 assignees View on GitHub
bug bug/fixed-in-beta
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

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.