Skip to content

Commit 510b681

Browse files
committed
column.as(alias) prototype
1 parent e5cd576 commit 510b681

File tree

12 files changed

+169
-33
lines changed

12 files changed

+169
-33
lines changed

drizzle-orm/src/alias.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type * as V1 from './_relations.ts';
2+
import { OriginalColumn } from './column-common.ts';
23
import type { AnyColumn } from './column.ts';
34
import { Column } from './column.ts';
45
import { entityKind, is } from './entity.ts';
@@ -7,8 +8,8 @@ import { SQL, sql } from './sql/sql.ts';
78
import { Table } from './table.ts';
89
import { ViewBaseConfig } from './view-common.ts';
910

10-
export class ColumnAliasProxyHandler<TColumn extends Column> implements ProxyHandler<TColumn> {
11-
static readonly [entityKind]: string = 'ColumnAliasProxyHandler';
11+
export class ColumnTableAliasProxyHandler<TColumn extends Column> implements ProxyHandler<TColumn> {
12+
static readonly [entityKind]: string = 'ColumnTableAliasProxyHandler';
1213

1314
constructor(private table: Table | View) {}
1415

@@ -58,7 +59,7 @@ export class TableAliasProxyHandler<T extends Table | View> implements ProxyHand
5859
Object.keys(columns).map((key) => {
5960
proxiedColumns[key] = new Proxy(
6061
columns[key]!,
61-
new ColumnAliasProxyHandler(new Proxy(target, this)),
62+
new ColumnTableAliasProxyHandler(new Proxy(target, this)),
6263
);
6364
});
6465

@@ -67,13 +68,39 @@ export class TableAliasProxyHandler<T extends Table | View> implements ProxyHand
6768

6869
const value = target[prop as keyof typeof target];
6970
if (is(value, Column)) {
70-
return new Proxy(value as AnyColumn, new ColumnAliasProxyHandler(new Proxy(target, this)));
71+
return new Proxy(value as AnyColumn, new ColumnTableAliasProxyHandler(new Proxy(target, this)));
7172
}
7273

7374
return value;
7475
}
7576
}
7677

78+
export class ColumnAliasProxyHandler<T extends Column> implements ProxyHandler<T> {
79+
static readonly [entityKind]: string = 'ColumnAliasProxyHandler';
80+
81+
constructor(private alias: string) {}
82+
83+
get(target: T, prop: keyof Column): any {
84+
if (prop === 'isAlias') {
85+
return true;
86+
}
87+
88+
if (prop === 'name') {
89+
return this.alias;
90+
}
91+
92+
if (prop === 'keyAsName') {
93+
return false;
94+
}
95+
96+
if (prop === OriginalColumn) {
97+
return () => target;
98+
}
99+
100+
return target[prop];
101+
}
102+
}
103+
77104
export class RelationTableAliasProxyHandler<T extends V1.Relation> implements ProxyHandler<T> {
78105
static readonly [entityKind]: string = 'RelationTableAliasProxyHandler';
79106

@@ -92,14 +119,18 @@ export function aliasedTable<T extends Table | View>(table: T, tableAlias: strin
92119
return new Proxy(table, new TableAliasProxyHandler(tableAlias, false));
93120
}
94121

122+
export function aliasedColumn<T extends Column>(column: T, alias: string): T {
123+
return new Proxy(column, new ColumnAliasProxyHandler(alias));
124+
}
125+
95126
export function aliasedRelation<T extends V1.Relation>(relation: T, tableAlias: string): T {
96127
return new Proxy(relation, new RelationTableAliasProxyHandler(tableAlias));
97128
}
98129

99130
export function aliasedTableColumn<T extends AnyColumn>(column: T, tableAlias: string): T {
100131
return new Proxy(
101132
column,
102-
new ColumnAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false))),
133+
new ColumnTableAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false))),
103134
);
104135
}
105136

@@ -121,3 +152,12 @@ export function mapColumnsInSQLToAlias(query: SQL, alias: string): SQL {
121152
return c;
122153
}));
123154
}
155+
156+
// Defined separately from the Column class to resolve circular dependency
157+
Column.prototype.as = function(alias: string): Column {
158+
return aliasedColumn(this, alias);
159+
};
160+
161+
export function getOriginalColumnFromAlias<T extends Column>(column: T): T {
162+
return column[OriginalColumn]();
163+
}

drizzle-orm/src/cockroach-core/dialect.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import {
511
CockroachColumn,
@@ -234,9 +240,13 @@ export class CockroachDialect {
234240
}
235241
} else if (is(field, Column)) {
236242
if (isSingleTable) {
237-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
243+
chunk.push(
244+
field.isAlias
245+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
246+
: sql.identifier(this.casing.getColumnCasing(field)),
247+
);
238248
} else {
239-
chunk.push(field);
249+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
240250
}
241251
}
242252

drizzle-orm/src/column-common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const OriginalColumn = Symbol.for('drizzle:OriginalColumn');

drizzle-orm/src/column.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
GeneratedColumnConfig,
55
GeneratedIdentityConfig,
66
} from './column-builder.ts';
7+
import { OriginalColumn } from './column-common.ts';
78
import { entityKind } from './entity.ts';
89
import type { DriverValueMapper, SQL, SQLWrapper } from './sql/sql.ts';
910
import type { Table } from './table.ts';
@@ -31,7 +32,10 @@ export interface Column<
3132
TRuntimeConfig extends object = object,
3233
> extends DriverValueMapper<T['data'], T['driverParam']>, SQLWrapper {
3334
// SQLWrapper runtime implementation is defined in 'sql/sql.ts'
35+
// `as` runtime implementation is defined in 'alias.ts'
36+
as(alias: string): this;
3437
}
38+
3539
/*
3640
`Column` only accepts a full `ColumnConfig` as its generic.
3741
To infer parts of the config, use `AnyColumn` that accepts a partial config.
@@ -65,6 +69,7 @@ export abstract class Column<
6569
readonly generatedIdentity: GeneratedIdentityConfig | undefined = undefined;
6670
readonly length: number | undefined;
6771
readonly isLengthExact: boolean | undefined;
72+
readonly isAlias: boolean;
6873

6974
/** @internal */
7075
protected config: ColumnBuilderRuntimeConfig<T['data']> & TRuntimeConfig;
@@ -84,6 +89,7 @@ export abstract class Column<
8489
this.table = table;
8590

8691
this.name = config.name;
92+
this.isAlias = false;
8793
this.keyAsName = config.keyAsName;
8894
this.notNull = config.notNull;
8995
this.default = config.default;
@@ -116,6 +122,11 @@ export abstract class Column<
116122
shouldDisableInsert(): boolean {
117123
return this.config.generated !== undefined && this.config.generated.type !== 'byDefault';
118124
}
125+
126+
/** @internal */
127+
[OriginalColumn](): this {
128+
return this;
129+
}
119130
}
120131

121132
export type UpdateColConfig<

drizzle-orm/src/gel-core/dialect.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import { Column } from '~/column.ts';
511
import { entityKind, is } from '~/entity.ts';
@@ -241,9 +247,13 @@ export class GelDialect {
241247
// Gel throws an error when more than one similarly named columns exist within context instead of preferring the closest one
242248
// thus forcing us to be explicit about column's source
243249
// if (isSingleTable) {
244-
// chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
250+
// chunk.push(
251+
// field.isAlias
252+
// ? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
253+
// : sql.identifier(this.casing.getColumnCasing(field)),
254+
// );
245255
// } else {
246-
chunk.push(field);
256+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
247257
// }
248258
}
249259

drizzle-orm/src/mssql-core/dialect.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import { Column } from '~/column.ts';
511
import { entityKind, is } from '~/entity.ts';
@@ -215,9 +221,13 @@ export class MsSqlDialect {
215221
}
216222
} else if (is(field, Column)) {
217223
if (isSingleTable) {
218-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
224+
chunk.push(
225+
field.isAlias
226+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
227+
: sql.identifier(this.casing.getColumnCasing(field)),
228+
);
219229
} else {
220-
chunk.push(field);
230+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
221231
}
222232
}
223233

@@ -261,7 +271,14 @@ export class MsSqlDialect {
261271
chunk.push(sql` as ${sql.identifier(field.fieldAlias)}`);
262272
}
263273
} else if (is(field, Column)) {
264-
chunk.push(sql.join([sql.raw(`${type}.`), sql.identifier(this.casing.getColumnCasing(field))]));
274+
chunk.push(
275+
sql.join([
276+
sql.raw(`${type}.`),
277+
field.isAlias
278+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
279+
: sql.identifier(this.casing.getColumnCasing(field)),
280+
]),
281+
);
265282
}
266283

267284
if (i < columnsLen - 1) {

drizzle-orm/src/mysql-core/dialect.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import { Column } from '~/column.ts';
511
import { entityKind, is } from '~/entity.ts';
@@ -233,9 +239,13 @@ export class MySqlDialect {
233239
}
234240
} else if (is(field, Column)) {
235241
if (isSingleTable) {
236-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
242+
chunk.push(
243+
field.isAlias
244+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
245+
: sql.identifier(this.casing.getColumnCasing(field)),
246+
);
237247
} else {
238-
chunk.push(field);
248+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
239249
}
240250
}
241251

drizzle-orm/src/pg-core/dialect.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import { Column } from '~/column.ts';
511
import { entityKind, is } from '~/entity.ts';
@@ -249,9 +255,13 @@ export class PgDialect {
249255
}
250256
} else if (is(field, Column)) {
251257
if (isSingleTable) {
252-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
258+
chunk.push(
259+
field.isAlias
260+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
261+
: sql.identifier(this.casing.getColumnCasing(field)),
262+
);
253263
} else {
254-
chunk.push(field);
264+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
255265
}
256266
}
257267

drizzle-orm/src/selection-proxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ColumnAliasProxyHandler, TableAliasProxyHandler } from './alias.ts';
1+
import { ColumnTableAliasProxyHandler, TableAliasProxyHandler } from './alias.ts';
22
import { Column } from './column.ts';
33
import { entityKind, is } from './entity.ts';
44
import { SQL, View } from './sql/sql.ts';
@@ -101,7 +101,7 @@ export class SelectionProxyHandler<T extends Subquery | Record<string, unknown>
101101
if (this.config.alias) {
102102
return new Proxy(
103103
value,
104-
new ColumnAliasProxyHandler(
104+
new ColumnTableAliasProxyHandler(
105105
new Proxy(
106106
value.table,
107107
new TableAliasProxyHandler(this.config.alias, this.config.replaceOriginalName ?? false),

drizzle-orm/src/singlestore-core/dialect.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as V1 from '~/_relations.ts';
2-
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from '~/alias.ts';
2+
import {
3+
aliasedTable,
4+
aliasedTableColumn,
5+
getOriginalColumnFromAlias,
6+
mapColumnsInAliasedSQLToAlias,
7+
mapColumnsInSQLToAlias,
8+
} from '~/alias.ts';
39
import { CasingCache } from '~/casing.ts';
410
import { Column } from '~/column.ts';
511
import { entityKind, is } from '~/entity.ts';
@@ -229,9 +235,13 @@ export class SingleStoreDialect {
229235
}
230236
} else if (is(field, Column)) {
231237
if (isSingleTable) {
232-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
238+
chunk.push(
239+
field.isAlias
240+
? sql`${sql.identifier(this.casing.getColumnCasing(getOriginalColumnFromAlias(field)))} as ${field}`
241+
: sql.identifier(this.casing.getColumnCasing(field)),
242+
);
233243
} else {
234-
chunk.push(field);
244+
chunk.push(field.isAlias ? sql`${getOriginalColumnFromAlias(field)} as ${field}` : field);
235245
}
236246
}
237247

0 commit comments

Comments
 (0)