From eb842749ad4aba6891cb3e287d275020f40e1c75 Mon Sep 17 00:00:00 2001 From: Mario564 Date: Wed, 13 Nov 2024 10:31:53 -0800 Subject: [PATCH 1/6] Add `getViewSelectedFields` util function --- drizzle-orm/src/utils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drizzle-orm/src/utils.ts b/drizzle-orm/src/utils.ts index 8e1382c7a..e2df51385 100644 --- a/drizzle-orm/src/utils.ts +++ b/drizzle-orm/src/utils.ts @@ -188,6 +188,10 @@ export function getTableColumns(table: T): T['_']['columns'] { return table[Table.Symbol.Columns]; } +export function getViewSelectedFields(view: T): T['_']['selectedFields'] { + return view[ViewBaseConfig].selectedFields; +} + /** @internal */ export function getTableLikeName(table: TableLike): string | undefined { return is(table, Subquery) From 147b134cb6501ed99202ebead46efc642a03fff8 Mon Sep 17 00:00:00 2001 From: Mario564 Date: Wed, 13 Nov 2024 12:25:07 -0800 Subject: [PATCH 2/6] Fix view types --- drizzle-orm/src/mysql-core/view.ts | 5 +- drizzle-orm/src/pg-core/view-base.ts | 2 +- drizzle-orm/src/pg-core/view.ts | 13 ++-- .../src/query-builders/select.types.ts | 2 + drizzle-orm/src/sql/sql.ts | 6 +- drizzle-orm/src/sqlite-core/view.ts | 5 +- drizzle-orm/type-tests/mysql/select.ts | 33 +++++++++- drizzle-orm/type-tests/pg/select.ts | 64 +++++++++++++++++++ drizzle-orm/type-tests/sqlite/select.ts | 34 ++++++++++ 9 files changed, 146 insertions(+), 18 deletions(-) diff --git a/drizzle-orm/src/mysql-core/view.ts b/drizzle-orm/src/mysql-core/view.ts index 6054e022c..42d9b3af6 100644 --- a/drizzle-orm/src/mysql-core/view.ts +++ b/drizzle-orm/src/mysql-core/view.ts @@ -7,7 +7,6 @@ import type { ColumnsSelection, SQL } from '~/sql/sql.ts'; import { getTableColumns } from '~/utils.ts'; import type { MySqlColumn, MySqlColumnBuilderBase } from './columns/index.ts'; import { QueryBuilder } from './query-builders/query-builder.ts'; -import type { SelectedFields } from './query-builders/select.types.ts'; import { mysqlTable } from './table.ts'; import { MySqlViewBase } from './view-base.ts'; import { MySqlViewConfig } from './view-common.ts'; @@ -58,7 +57,7 @@ export class ViewBuilderCore extends ViewBuilderCore<{ name: TName }> { static override readonly [entityKind]: string = 'MySqlViewBuilder'; - as( + as( qb: TypedQueryBuilder | ((qb: QueryBuilder) => TypedQueryBuilder), ): MySqlViewWithSelection> { if (typeof qb === 'function') { @@ -160,7 +159,7 @@ export class MySqlView< config: { name: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; query: SQL | undefined; }; }) { diff --git a/drizzle-orm/src/pg-core/view-base.ts b/drizzle-orm/src/pg-core/view-base.ts index d3f52a501..0f7bee81a 100644 --- a/drizzle-orm/src/pg-core/view-base.ts +++ b/drizzle-orm/src/pg-core/view-base.ts @@ -4,7 +4,7 @@ import { type ColumnsSelection, View } from '~/sql/sql.ts'; export abstract class PgViewBase< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection, + TSelectedFields extends ColumnsSelection = ColumnsSelection > extends View { static override readonly [entityKind]: string = 'PgViewBase'; diff --git a/drizzle-orm/src/pg-core/view.ts b/drizzle-orm/src/pg-core/view.ts index 2f88d7e17..4abb9b13e 100644 --- a/drizzle-orm/src/pg-core/view.ts +++ b/drizzle-orm/src/pg-core/view.ts @@ -8,7 +8,6 @@ import { getTableColumns } from '~/utils.ts'; import type { RequireAtLeastOne } from '~/utils.ts'; import type { PgColumn, PgColumnBuilderBase } from './columns/common.ts'; import { QueryBuilder } from './query-builders/query-builder.ts'; -import type { SelectedFields } from './query-builders/select.types.ts'; import { pgTable } from './table.ts'; import { PgViewBase } from './view-base.ts'; import { PgViewConfig } from './view-common.ts'; @@ -45,7 +44,7 @@ export class DefaultViewBuilderCore extends DefaultViewBuilderCore<{ name: TName }> { static override readonly [entityKind]: string = 'PgViewBuilder'; - as( + as( qb: TypedQueryBuilder | ((qb: QueryBuilder) => TypedQueryBuilder), ): PgViewWithSelection> { if (typeof qb === 'function') { @@ -198,7 +197,7 @@ export class MaterializedViewBuilder { static override readonly [entityKind]: string = 'PgMaterializedViewBuilder'; - as( + as( qb: TypedQueryBuilder | ((qb: QueryBuilder) => TypedQueryBuilder), ): PgMaterializedViewWithSelection> { if (typeof qb === 'function') { @@ -302,7 +301,7 @@ export class ManualMaterializedViewBuilder< export class PgView< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection, + TSelectedFields extends ColumnsSelection = ColumnsSelection > extends PgViewBase { static override readonly [entityKind]: string = 'PgView'; @@ -317,7 +316,7 @@ export class PgView< config: { name: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; query: SQL | undefined; }; }) { @@ -333,7 +332,7 @@ export class PgView< export type PgViewWithSelection< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection, + TSelectedFields extends ColumnsSelection = ColumnsSelection > = PgView & TSelectedFields; export const PgMaterializedViewConfig = Symbol.for('drizzle:PgMaterializedViewConfig'); @@ -362,7 +361,7 @@ export class PgMaterializedView< config: { name: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; query: SQL | undefined; }; }) { diff --git a/drizzle-orm/src/query-builders/select.types.ts b/drizzle-orm/src/query-builders/select.types.ts index 07579662f..e7975af65 100644 --- a/drizzle-orm/src/query-builders/select.types.ts +++ b/drizzle-orm/src/query-builders/select.types.ts @@ -93,6 +93,7 @@ export type AddAliasToSelection< : { [Key in keyof TSelection]: TSelection[Key] extends Column ? ChangeColumnTableName + : TSelection[Key] extends Table ? AddAliasToSelection : TSelection[Key] extends SQL | SQL.Aliased ? TSelection[Key] : TSelection[Key] extends ColumnsSelection ? MapColumnsToTableAlias : never; @@ -120,6 +121,7 @@ export type BuildSubquerySelection< [Key in keyof TSelection]: TSelection[Key] extends SQL ? DrizzleTypeError<'You cannot reference this field without assigning it an alias first - use `.as()`'> : TSelection[Key] extends SQL.Aliased ? TSelection[Key] + : TSelection[Key] extends Table ? BuildSubquerySelection : TSelection[Key] extends Column ? ApplyNullabilityToColumn : TSelection[Key] extends ColumnsSelection ? BuildSubquerySelection diff --git a/drizzle-orm/src/sql/sql.ts b/drizzle-orm/src/sql/sql.ts index 19185f1bf..f1657b47c 100644 --- a/drizzle-orm/src/sql/sql.ts +++ b/drizzle-orm/src/sql/sql.ts @@ -612,7 +612,7 @@ export type ColumnsSelection = Record; export abstract class View< TName extends string = string, TExisting extends boolean = boolean, - TSelection extends ColumnsSelection = ColumnsSelection, + TSelection extends ColumnsSelection = ColumnsSelection > implements SQLWrapper { static readonly [entityKind]: string = 'View'; @@ -629,7 +629,7 @@ export abstract class View< name: TName; originalName: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; isExisting: TExisting; query: TExisting extends true ? undefined : SQL; isAlias: boolean; @@ -639,7 +639,7 @@ export abstract class View< { name, schema, selectedFields, query }: { name: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; query: SQL | undefined; }, ) { diff --git a/drizzle-orm/src/sqlite-core/view.ts b/drizzle-orm/src/sqlite-core/view.ts index 03ef08025..1caf211ce 100644 --- a/drizzle-orm/src/sqlite-core/view.ts +++ b/drizzle-orm/src/sqlite-core/view.ts @@ -7,7 +7,6 @@ import type { ColumnsSelection, SQL } from '~/sql/sql.ts'; import { getTableColumns } from '~/utils.ts'; import type { SQLiteColumn, SQLiteColumnBuilderBase } from './columns/common.ts'; import { QueryBuilder } from './query-builders/query-builder.ts'; -import type { SelectedFields } from './query-builders/select.types.ts'; import { sqliteTable } from './table.ts'; import { SQLiteViewBase } from './view-base.ts'; @@ -38,7 +37,7 @@ export class ViewBuilderCore< export class ViewBuilder extends ViewBuilderCore<{ name: TName }> { static override readonly [entityKind]: string = 'SQLiteViewBuilder'; - as( + as( qb: TypedQueryBuilder | ((qb: QueryBuilder) => TypedQueryBuilder), ): SQLiteViewWithSelection> { if (typeof qb === 'function') { @@ -135,7 +134,7 @@ export class SQLiteView< config: { name: TName; schema: string | undefined; - selectedFields: SelectedFields; + selectedFields: ColumnsSelection; query: SQL | undefined; }; }) { diff --git a/drizzle-orm/type-tests/mysql/select.ts b/drizzle-orm/type-tests/mysql/select.ts index 0a6af743b..0031bb2b2 100644 --- a/drizzle-orm/type-tests/mysql/select.ts +++ b/drizzle-orm/type-tests/mysql/select.ts @@ -26,7 +26,7 @@ import { param, sql } from '~/sql/sql.ts'; import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; -import { type MySqlSelect, type MySqlSelectQueryBuilder, QueryBuilder } from '~/mysql-core/index.ts'; +import { int, type MySqlSelect, type MySqlSelectQueryBuilder, mysqlTable, mysqlView, QueryBuilder, text } from '~/mysql-core/index.ts'; import { db } from './db.ts'; import { cities, classes, newYorkers, users } from './tables.ts'; @@ -604,3 +604,34 @@ await db // @ts-expect-error method was already called .for('update'); } + +{ + const table1 = mysqlTable('table1', { + id: int().primaryKey(), + name: text().notNull(), + }); + const table2 = mysqlTable('table2', { + id: int().primaryKey(), + age: int().notNull(), + }); + const table3 = mysqlTable('table3', { + id: int().primaryKey(), + phone: text().notNull(), + }); + const view = mysqlView('view').as((qb) => qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + } + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const result = await db.select().from(view); + + Expect>; +} diff --git a/drizzle-orm/type-tests/pg/select.ts b/drizzle-orm/type-tests/pg/select.ts index 0fde90a71..e8424839e 100644 --- a/drizzle-orm/type-tests/pg/select.ts +++ b/drizzle-orm/type-tests/pg/select.ts @@ -31,9 +31,11 @@ import { alias } from '~/pg-core/alias.ts'; import { boolean, integer, + pgMaterializedView, type PgSelect, type PgSelectQueryBuilder, pgTable, + pgView, QueryBuilder, text, } from '~/pg-core/index.ts'; @@ -1156,3 +1158,65 @@ await db ), ); } + +{ + const table1 = pgTable('table1', { + id: integer().primaryKey(), + name: text().notNull(), + }); + const table2 = pgTable('table2', { + id: integer().primaryKey(), + age: integer().notNull(), + }); + const table3 = pgTable('table3', { + id: integer().primaryKey(), + phone: text().notNull(), + }); + const view = pgView('view').as((qb) => qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + } + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const result = await db.select().from(view); + + Expect>; +} + +{ + const table1 = pgTable('table1', { + id: integer().primaryKey(), + name: text().notNull(), + }); + const table2 = pgTable('table2', { + id: integer().primaryKey(), + age: integer().notNull(), + }); + const table3 = pgTable('table3', { + id: integer().primaryKey(), + phone: text().notNull(), + }); + const view = pgMaterializedView('view').as((qb) => qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + } + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const result = await db.select().from(view); + + Expect>; +} diff --git a/drizzle-orm/type-tests/sqlite/select.ts b/drizzle-orm/type-tests/sqlite/select.ts index 196dbc4de..3cbd031ce 100644 --- a/drizzle-orm/type-tests/sqlite/select.ts +++ b/drizzle-orm/type-tests/sqlite/select.ts @@ -29,6 +29,9 @@ import { Expect } from 'type-tests/utils.ts'; import type { SQLiteSelect, SQLiteSelectQueryBuilder } from '~/sqlite-core/query-builders/select.types.ts'; import { db } from './db.ts'; import { cities, classes, newYorkers, users } from './tables.ts'; +import { sqliteTable } from '~/sqlite-core/table.ts'; +import { sqliteView } from '~/sqlite-core/view.ts'; +import { integer, text } from '~/sqlite-core/index.ts'; const city = alias(cities, 'city'); const city1 = alias(cities, 'city1'); @@ -579,3 +582,34 @@ Expect< // @ts-expect-error method was already called .offset(10); } + +{ + const table1 = sqliteTable('table1', { + id: integer().primaryKey(), + name: text().notNull(), + }); + const table2 = sqliteTable('table2', { + id: integer().primaryKey(), + age: integer().notNull(), + }); + const table3 = sqliteTable('table3', { + id: integer().primaryKey(), + phone: text().notNull(), + }); + const view = sqliteView('view').as((qb) => qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + } + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const result = await db.select().from(view); + + Expect>; +} From a2ed22b555c2482856922c11d85bb2bc4d5874b1 Mon Sep 17 00:00:00 2001 From: Mario564 Date: Wed, 13 Nov 2024 12:48:13 -0800 Subject: [PATCH 3/6] Add `$inferSelect` to views --- drizzle-orm/src/sql/sql.ts | 12 ++++++++++++ drizzle-orm/type-tests/mysql/select.ts | 4 +++- drizzle-orm/type-tests/pg/select.ts | 6 +++++- drizzle-orm/type-tests/sqlite/select.ts | 4 +++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drizzle-orm/src/sql/sql.ts b/drizzle-orm/src/sql/sql.ts index f1657b47c..a0cd3cb65 100644 --- a/drizzle-orm/src/sql/sql.ts +++ b/drizzle-orm/src/sql/sql.ts @@ -8,6 +8,8 @@ import { ViewBaseConfig } from '~/view-common.ts'; import type { AnyColumn } from '../column.ts'; import { Column } from '../column.ts'; import { Table } from '../table.ts'; +import { Assume, Equal } from '~/utils.ts'; +import { SelectResult } from '~/query-builders/select.types.ts'; /** * This class is used to indicate a primitive param value that is used in `sql` tag. @@ -635,6 +637,8 @@ export abstract class View< isAlias: boolean; }; + declare readonly $inferSelect: InferSelectViewModel, TExisting, TSelection>>; + constructor( { name, schema, selectedFields, query }: { name: TName; @@ -659,6 +663,14 @@ export abstract class View< } } +export type InferSelectViewModel = Equal extends true + ? { [x: string]: unknown } + : SelectResult< + TView['_']['selectedFields'], + 'single', + Record + >; + // Defined separately from the Column class to resolve circular dependency Column.prototype.getSQL = function() { return new SQL([this]); diff --git a/drizzle-orm/type-tests/mysql/select.ts b/drizzle-orm/type-tests/mysql/select.ts index 0031bb2b2..39f1029e7 100644 --- a/drizzle-orm/type-tests/mysql/select.ts +++ b/drizzle-orm/type-tests/mysql/select.ts @@ -22,7 +22,7 @@ import { or, } from '~/expressions.ts'; import { alias } from '~/mysql-core/alias.ts'; -import { param, sql } from '~/sql/sql.ts'; +import { InferSelectViewModel, param, sql } from '~/sql/sql.ts'; import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; @@ -634,4 +634,6 @@ await db column: string | null; } }[]>>; + Expect>; + Expect[]>>; } diff --git a/drizzle-orm/type-tests/pg/select.ts b/drizzle-orm/type-tests/pg/select.ts index e8424839e..55f56d03d 100644 --- a/drizzle-orm/type-tests/pg/select.ts +++ b/drizzle-orm/type-tests/pg/select.ts @@ -39,7 +39,7 @@ import { QueryBuilder, text, } from '~/pg-core/index.ts'; -import { type SQL, sql } from '~/sql/sql.ts'; +import { InferSelectViewModel, type SQL, sql } from '~/sql/sql.ts'; import { db } from './db.ts'; import { cities, classes, newYorkers, newYorkers2, users } from './tables.ts'; @@ -1188,6 +1188,8 @@ await db column: string | null; } }[]>>; + Expect>; + Expect[]>>; } { @@ -1219,4 +1221,6 @@ await db column: string | null; } }[]>>; + Expect>; + Expect[]>>; } diff --git a/drizzle-orm/type-tests/sqlite/select.ts b/drizzle-orm/type-tests/sqlite/select.ts index 3cbd031ce..63b75a2f2 100644 --- a/drizzle-orm/type-tests/sqlite/select.ts +++ b/drizzle-orm/type-tests/sqlite/select.ts @@ -21,7 +21,7 @@ import { notLike, or, } from '~/expressions.ts'; -import { param, sql } from '~/sql/sql.ts'; +import { InferSelectViewModel, param, sql } from '~/sql/sql.ts'; import { alias } from '~/sqlite-core/alias.ts'; import type { Equal } from 'type-tests/utils.ts'; @@ -612,4 +612,6 @@ Expect< column: string | null; } }[]>>; + Expect>; + Expect[]>>; } From 9b256e3cfeaac50a8f175ae2119225f308e38c7f Mon Sep 17 00:00:00 2001 From: Mario564 Date: Thu, 14 Nov 2024 09:24:42 -0800 Subject: [PATCH 4/6] Format --- drizzle-kit/package.json | 2 +- drizzle-orm/package.json | 2 +- drizzle-orm/src/pg-core/view-base.ts | 2 +- drizzle-orm/src/pg-core/view.ts | 4 +- drizzle-orm/src/sql/sql.ts | 20 ++++---- drizzle-orm/type-tests/mysql/select.ts | 42 ++++++++++------ drizzle-orm/type-tests/pg/select.ts | 64 ++++++++++++++----------- drizzle-orm/type-tests/sqlite/select.ts | 38 ++++++++------- 8 files changed, 99 insertions(+), 75 deletions(-) diff --git a/drizzle-kit/package.json b/drizzle-kit/package.json index e45f7dbc5..1c911f23f 100644 --- a/drizzle-kit/package.json +++ b/drizzle-kit/package.json @@ -141,4 +141,4 @@ "default": "./api.mjs" } } -} \ No newline at end of file +} diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index 598ee9a1e..b2b204ead 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -203,4 +203,4 @@ "zod": "^3.20.2", "zx": "^7.2.2" } -} \ No newline at end of file +} diff --git a/drizzle-orm/src/pg-core/view-base.ts b/drizzle-orm/src/pg-core/view-base.ts index 0f7bee81a..d3f52a501 100644 --- a/drizzle-orm/src/pg-core/view-base.ts +++ b/drizzle-orm/src/pg-core/view-base.ts @@ -4,7 +4,7 @@ import { type ColumnsSelection, View } from '~/sql/sql.ts'; export abstract class PgViewBase< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection + TSelectedFields extends ColumnsSelection = ColumnsSelection, > extends View { static override readonly [entityKind]: string = 'PgViewBase'; diff --git a/drizzle-orm/src/pg-core/view.ts b/drizzle-orm/src/pg-core/view.ts index 4abb9b13e..f8e916a31 100644 --- a/drizzle-orm/src/pg-core/view.ts +++ b/drizzle-orm/src/pg-core/view.ts @@ -301,7 +301,7 @@ export class ManualMaterializedViewBuilder< export class PgView< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection + TSelectedFields extends ColumnsSelection = ColumnsSelection, > extends PgViewBase { static override readonly [entityKind]: string = 'PgView'; @@ -332,7 +332,7 @@ export class PgView< export type PgViewWithSelection< TName extends string = string, TExisting extends boolean = boolean, - TSelectedFields extends ColumnsSelection = ColumnsSelection + TSelectedFields extends ColumnsSelection = ColumnsSelection, > = PgView & TSelectedFields; export const PgMaterializedViewConfig = Symbol.for('drizzle:PgMaterializedViewConfig'); diff --git a/drizzle-orm/src/sql/sql.ts b/drizzle-orm/src/sql/sql.ts index a0cd3cb65..d05efeab3 100644 --- a/drizzle-orm/src/sql/sql.ts +++ b/drizzle-orm/src/sql/sql.ts @@ -2,14 +2,14 @@ import type { CasingCache } from '~/casing.ts'; import { entityKind, is } from '~/entity.ts'; import type { SelectedFields } from '~/operations.ts'; import { isPgEnum } from '~/pg-core/columns/enum.ts'; +import { SelectResult } from '~/query-builders/select.types.ts'; import { Subquery } from '~/subquery.ts'; import { tracer } from '~/tracing.ts'; +import { Assume, Equal } from '~/utils.ts'; import { ViewBaseConfig } from '~/view-common.ts'; import type { AnyColumn } from '../column.ts'; import { Column } from '../column.ts'; import { Table } from '../table.ts'; -import { Assume, Equal } from '~/utils.ts'; -import { SelectResult } from '~/query-builders/select.types.ts'; /** * This class is used to indicate a primitive param value that is used in `sql` tag. @@ -614,7 +614,7 @@ export type ColumnsSelection = Record; export abstract class View< TName extends string = string, TExisting extends boolean = boolean, - TSelection extends ColumnsSelection = ColumnsSelection + TSelection extends ColumnsSelection = ColumnsSelection, > implements SQLWrapper { static readonly [entityKind]: string = 'View'; @@ -663,13 +663,13 @@ export abstract class View< } } -export type InferSelectViewModel = Equal extends true - ? { [x: string]: unknown } - : SelectResult< - TView['_']['selectedFields'], - 'single', - Record - >; +export type InferSelectViewModel = + Equal extends true ? { [x: string]: unknown } + : SelectResult< + TView['_']['selectedFields'], + 'single', + Record + >; // Defined separately from the Column class to resolve circular dependency Column.prototype.getSQL = function() { diff --git a/drizzle-orm/type-tests/mysql/select.ts b/drizzle-orm/type-tests/mysql/select.ts index 39f1029e7..63acd990e 100644 --- a/drizzle-orm/type-tests/mysql/select.ts +++ b/drizzle-orm/type-tests/mysql/select.ts @@ -26,7 +26,15 @@ import { InferSelectViewModel, param, sql } from '~/sql/sql.ts'; import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; -import { int, type MySqlSelect, type MySqlSelectQueryBuilder, mysqlTable, mysqlView, QueryBuilder, text } from '~/mysql-core/index.ts'; +import { + int, + type MySqlSelect, + type MySqlSelectQueryBuilder, + mysqlTable, + mysqlView, + QueryBuilder, + text, +} from '~/mysql-core/index.ts'; import { db } from './db.ts'; import { cities, classes, newYorkers, users } from './tables.ts'; @@ -618,22 +626,26 @@ await db id: int().primaryKey(), phone: text().notNull(), }); - const view = mysqlView('view').as((qb) => qb.select({ - table: table1, - column: table2.age, - nested: { - column: table3.phone, - } - }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const view = mysqlView('view').as((qb) => + qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + }, + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``) + ); const result = await db.select().from(view); - Expect>; + Expect< + Equal + >; Expect>; Expect[]>>; } diff --git a/drizzle-orm/type-tests/pg/select.ts b/drizzle-orm/type-tests/pg/select.ts index 55f56d03d..0bad3f8b4 100644 --- a/drizzle-orm/type-tests/pg/select.ts +++ b/drizzle-orm/type-tests/pg/select.ts @@ -1172,22 +1172,26 @@ await db id: integer().primaryKey(), phone: text().notNull(), }); - const view = pgView('view').as((qb) => qb.select({ - table: table1, - column: table2.age, - nested: { - column: table3.phone, - } - }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const view = pgView('view').as((qb) => + qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + }, + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``) + ); const result = await db.select().from(view); - Expect>; + Expect< + Equal + >; Expect>; Expect[]>>; } @@ -1205,22 +1209,26 @@ await db id: integer().primaryKey(), phone: text().notNull(), }); - const view = pgMaterializedView('view').as((qb) => qb.select({ - table: table1, - column: table2.age, - nested: { - column: table3.phone, - } - }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const view = pgMaterializedView('view').as((qb) => + qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + }, + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``) + ); const result = await db.select().from(view); - Expect>; + Expect< + Equal + >; Expect>; Expect[]>>; } diff --git a/drizzle-orm/type-tests/sqlite/select.ts b/drizzle-orm/type-tests/sqlite/select.ts index 63b75a2f2..f42ccd47e 100644 --- a/drizzle-orm/type-tests/sqlite/select.ts +++ b/drizzle-orm/type-tests/sqlite/select.ts @@ -26,12 +26,12 @@ import { alias } from '~/sqlite-core/alias.ts'; import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; +import { integer, text } from '~/sqlite-core/index.ts'; import type { SQLiteSelect, SQLiteSelectQueryBuilder } from '~/sqlite-core/query-builders/select.types.ts'; -import { db } from './db.ts'; -import { cities, classes, newYorkers, users } from './tables.ts'; import { sqliteTable } from '~/sqlite-core/table.ts'; import { sqliteView } from '~/sqlite-core/view.ts'; -import { integer, text } from '~/sqlite-core/index.ts'; +import { db } from './db.ts'; +import { cities, classes, newYorkers, users } from './tables.ts'; const city = alias(cities, 'city'); const city1 = alias(cities, 'city1'); @@ -596,22 +596,26 @@ Expect< id: integer().primaryKey(), phone: text().notNull(), }); - const view = sqliteView('view').as((qb) => qb.select({ - table: table1, - column: table2.age, - nested: { - column: table3.phone, - } - }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``)); + const view = sqliteView('view').as((qb) => + qb.select({ + table: table1, + column: table2.age, + nested: { + column: table3.phone, + }, + }).from(table1).innerJoin(table2, sql``).leftJoin(table3, sql``) + ); const result = await db.select().from(view); - Expect>; + Expect< + Equal + >; Expect>; Expect[]>>; } From d91df79934e0fb88f13691f8923050870adced94 Mon Sep 17 00:00:00 2001 From: Mario564 Date: Thu, 14 Nov 2024 09:50:38 -0800 Subject: [PATCH 5/6] Fix lint errors --- drizzle-orm/src/sql/sql.ts | 5 ++--- drizzle-orm/type-tests/mysql/select.ts | 2 +- drizzle-orm/type-tests/pg/select.ts | 2 +- drizzle-orm/type-tests/sqlite/select.ts | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drizzle-orm/src/sql/sql.ts b/drizzle-orm/src/sql/sql.ts index d05efeab3..03aea7287 100644 --- a/drizzle-orm/src/sql/sql.ts +++ b/drizzle-orm/src/sql/sql.ts @@ -1,11 +1,10 @@ import type { CasingCache } from '~/casing.ts'; import { entityKind, is } from '~/entity.ts'; -import type { SelectedFields } from '~/operations.ts'; import { isPgEnum } from '~/pg-core/columns/enum.ts'; -import { SelectResult } from '~/query-builders/select.types.ts'; +import type { SelectResult } from '~/query-builders/select.types.ts'; import { Subquery } from '~/subquery.ts'; import { tracer } from '~/tracing.ts'; -import { Assume, Equal } from '~/utils.ts'; +import type { Assume, Equal } from '~/utils.ts'; import { ViewBaseConfig } from '~/view-common.ts'; import type { AnyColumn } from '../column.ts'; import { Column } from '../column.ts'; diff --git a/drizzle-orm/type-tests/mysql/select.ts b/drizzle-orm/type-tests/mysql/select.ts index 63acd990e..bad55aa59 100644 --- a/drizzle-orm/type-tests/mysql/select.ts +++ b/drizzle-orm/type-tests/mysql/select.ts @@ -22,7 +22,7 @@ import { or, } from '~/expressions.ts'; import { alias } from '~/mysql-core/alias.ts'; -import { InferSelectViewModel, param, sql } from '~/sql/sql.ts'; +import { type InferSelectViewModel, param, sql } from '~/sql/sql.ts'; import type { Equal } from 'type-tests/utils.ts'; import { Expect } from 'type-tests/utils.ts'; diff --git a/drizzle-orm/type-tests/pg/select.ts b/drizzle-orm/type-tests/pg/select.ts index 0bad3f8b4..4ab3a86b3 100644 --- a/drizzle-orm/type-tests/pg/select.ts +++ b/drizzle-orm/type-tests/pg/select.ts @@ -39,7 +39,7 @@ import { QueryBuilder, text, } from '~/pg-core/index.ts'; -import { InferSelectViewModel, type SQL, sql } from '~/sql/sql.ts'; +import { type InferSelectViewModel, type SQL, sql } from '~/sql/sql.ts'; import { db } from './db.ts'; import { cities, classes, newYorkers, newYorkers2, users } from './tables.ts'; diff --git a/drizzle-orm/type-tests/sqlite/select.ts b/drizzle-orm/type-tests/sqlite/select.ts index f42ccd47e..92bb6055a 100644 --- a/drizzle-orm/type-tests/sqlite/select.ts +++ b/drizzle-orm/type-tests/sqlite/select.ts @@ -21,7 +21,7 @@ import { notLike, or, } from '~/expressions.ts'; -import { InferSelectViewModel, param, sql } from '~/sql/sql.ts'; +import { type InferSelectViewModel, param, sql } from '~/sql/sql.ts'; import { alias } from '~/sqlite-core/alias.ts'; import type { Equal } from 'type-tests/utils.ts'; From bbaf6f07474db7aa17660c3f38635b7481e146dd Mon Sep 17 00:00:00 2001 From: Mario564 Date: Fri, 15 Nov 2024 10:07:21 -0800 Subject: [PATCH 6/6] Add `isView` util function --- drizzle-orm/src/sql/sql.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drizzle-orm/src/sql/sql.ts b/drizzle-orm/src/sql/sql.ts index 03aea7287..44f30ae4f 100644 --- a/drizzle-orm/src/sql/sql.ts +++ b/drizzle-orm/src/sql/sql.ts @@ -610,6 +610,8 @@ export function fillPlaceholders(params: unknown[], values: Record; +const IsDrizzleView = Symbol.for('drizzle:IsDrizzleView'); + export abstract class View< TName extends string = string, TExisting extends boolean = boolean, @@ -636,6 +638,9 @@ export abstract class View< isAlias: boolean; }; + /** @internal */ + [IsDrizzleView] = true; + declare readonly $inferSelect: InferSelectViewModel, TExisting, TSelection>>; constructor( @@ -662,6 +667,10 @@ export abstract class View< } } +export function isView(view: unknown): view is View { + return typeof view === 'object' && view !== null && IsDrizzleView in view; +} + export type InferSelectViewModel = Equal extends true ? { [x: string]: unknown } : SelectResult<