import { Column, TableData, ColumnsShape, Query, RelationConfigBase, QueryManyTake, QueryManyTakeOptional, EmptyObject, CreateData, UpdateData, WhereArg, SelectableFromShape, CreateManyMethodsNames, QueryHasWhere, CreateMethodsNames, MaybeArray, JoinQueryMethod, DeleteMethodsNames, Db, IsolationLevel, TransactionOptions, AfterCommitStandaloneHook, AdapterBase, FromArg, FromResult, StorageOptions, DbSharedOptions, QuerySchema, ColumnSchemaConfig, DbSqlMethod, TableDataItem, QueryData, IsQuery, TableDataFn, ComputedOptionsFactory, DbTableOptionScopes, QueryScopes, RecordUnknown, QueryBeforeHook, QueryAfterHook, QueryBeforeActionHook, AfterHook, ShallowSimplify, ShapeColumnPrimaryKeys, ShapeUniqueColumns, TableDataItemsUniqueColumns, TableDataItemsUniqueColumnTuples, UniqueConstraints, TableDataItemsUniqueConstraints, ComputedColumnsFromOptions, MapTableScopesOption, ComputedOptionsConfig, QueryOrExpression, DefaultSchemaConfig, DefaultColumnTypes, QueryReturnType, MergeQuery } from 'pqb/internal'; export * from 'pqb'; interface RelationRefsOptions { required?: boolean; columns: Column[]; references: (keyof Shape)[]; foreignKey?: boolean | TableData.References.Options; on?: ColumnsShape.InputPartial; } interface RelationThroughOptions { through: Through; source: Source; } interface HasOne extends RelationThunkBase { type: 'hasOne'; options: HasOneOptions; } interface RelationHasOneThroughOptions extends RelationThroughOptions { required?: boolean; } type HasOneOptions = RelationRefsOptions | RelationHasOneThroughOptions; type HasOneParams = Options extends RelationRefsOptions ? { [Name in Options['columns'][number]]: T['columns']['shape'][Name]['type']; } : Options extends RelationThroughOptions ? RelationConfigParams : never; type HasOneQueryThrough = { [K in keyof TableQuery]: K extends '__selectable' ? SelectableFromShape : K extends '__as' ? Name : K extends CreateMethodsNames ? never : TableQuery[K]; } & QueryHasWhere & HasRelJoin; type HasOneQuery = T['relations'][Name]['options'] extends RelationRefsOptions ? { [K in keyof TableQuery]: K extends '__defaults' ? { [K in keyof TableQuery['__defaults'] | T['relations'][Name]['options']['references'][number]]: true; } : K extends '__selectable' ? SelectableFromShape : K extends '__as' ? Name : K extends CreateManyMethodsNames ? never : TableQuery[K]; } & QueryHasWhere & HasRelJoin : HasOneQueryThrough; interface HasOneInfo extends RelationConfigBase { returnsOne: true; query: Q; params: HasOneParams; maybeSingle: T['relations'][Name]['options']['required'] extends true ? QueryManyTake : QueryManyTakeOptional; omitForeignKeyInCreate: never; optionalDataForCreate: T['relations'][Name]['options'] extends RelationThroughOptions ? EmptyObject : { [P in Name]?: RelationToOneDataForCreate<{ nestedCreateQuery: CreateData; table: Q; }>; }; dataForCreate: never; dataForUpdate: { disconnect: boolean; } | { delete: boolean; } | { update: UpdateData; }; dataForUpdateOne: { disconnect: boolean; } | { set: WhereArg; } | { delete: boolean; } | { update: UpdateData; } | { upsert: { update: UpdateData; create: CreateData | (() => CreateData); }; } | { create: CreateData; }; } interface HasMany extends RelationThunkBase { type: 'hasMany'; options: HasOneOptions; } type HasManyQuery = T['relations'][Name]['options'] extends RelationRefsOptions ? { [K in keyof TableQuery]: K extends '__defaults' ? { [K in keyof TableQuery['__defaults'] | T['relations'][Name]['options']['references'][number]]: true; } : K extends '__selectable' ? SelectableFromShape : K extends '__as' ? Name : TableQuery[K]; } & QueryHasWhere & HasRelJoin : HasOneQueryThrough; interface HasManyInfo extends RelationConfigBase { returnsOne: false; query: Q; params: HasOneParams; maybeSingle: Q; omitForeignKeyInCreate: never; optionalDataForCreate: { [P in Name]?: T['relations'][Name]['options'] extends RelationThroughOptions ? EmptyObject : { create?: CreateData[]; connect?: WhereArg[]; connectOrCreate?: { where: WhereArg; create: CreateData; }[]; }; }; dataForCreate: never; dataForUpdate: { disconnect?: MaybeArray>; delete?: MaybeArray>; update?: { where: MaybeArray>; data: UpdateData; }; }; dataForUpdateOne: { disconnect?: MaybeArray>; delete?: MaybeArray>; update?: { where: MaybeArray>; data: UpdateData; }; set?: MaybeArray>; add?: MaybeArray>; create?: CreateData[]; }; } interface RelJoin extends JoinQueryMethod { (this: T): T; } interface HasRelJoin { join: RelJoin; } interface BelongsTo extends RelationThunkBase { type: 'belongsTo'; options: BelongsToOptions; } interface BelongsToOptions { required?: boolean; columns: (keyof Columns)[]; references: (keyof Related['columns']['shape'])[]; foreignKey?: boolean | TableData.References.Options; on?: ColumnsShape.InputPartial; } type BelongsToParams = { [Name in FK]: T['columns']['shape'][Name]['type']; }; type BelongsToQuery = { [P in keyof T]: P extends '__selectable' ? SelectableFromShape : P extends '__as' ? Name : P extends CreateMethodsNames | DeleteMethodsNames ? never : T[P]; } & QueryHasWhere & HasRelJoin; interface BelongsToInfo extends RelationConfigBase { returnsOne: true; query: Q; params: BelongsToParams; maybeSingle: Required extends true ? QueryManyTake : QueryManyTakeOptional; omitForeignKeyInCreate: FK; dataForCreate: { columns: FK; nested: Required extends true ? { [Key in Name]: RelationToOneDataForCreateSameQuery; } : { [Key in Name]?: RelationToOneDataForCreateSameQuery; }; }; optionalDataForCreate: EmptyObject; dataForUpdate: { disconnect: boolean; } | { set: WhereArg; } | { delete: boolean; } | { update: UpdateData; } | { create: CreateData; }; dataForUpdateOne: { disconnect: boolean; } | { set: WhereArg; } | { delete: boolean; } | { update: UpdateData; } | { create: CreateData; } | { upsert: { update: UpdateData; create: CreateData | (() => CreateData); }; }; } declare function transaction(this: { $qb: Db; }, fn: () => Promise): Promise; declare function transaction(this: { $qb: Db; }, options: IsolationLevel | TransactionOptions, fn: () => Promise): Promise; declare function ensureTransaction(this: { $qb: Db; }, cb: () => Promise): Promise; declare function isInTransaction(this: { $qb: Db; }): boolean; declare function afterCommit(this: { $qb: Db; }, hook: AfterCommitStandaloneHook): void; interface FromQuery extends Query { returnType: 'all'; } type OrchidORM = { [K in keyof T]: T[K] extends { new (): infer R extends ORMTableInput; } ? TableToDb : never; } & OrchidORMMethods; interface OrchidORMMethods { /** * @see import('pqb').QueryTransaction.prototype.transaction */ $transaction: typeof transaction; /** * @see import('pqb').QueryTransaction.prototype.ensureTransaction */ $ensureTransaction: typeof ensureTransaction; /** * @see import('pqb').QueryTransaction.prototype.isInTransaction */ $isInTransaction: typeof isInTransaction; /** * @see import('pqb').QueryTransaction.prototype.afterCommit */ $afterCommit: typeof afterCommit; $qb: Db; $adapterNotInTransaction: AdapterBase; /** * Adapter is a wrapper on top of `postgres-js`, `node-postgres`, or other db driver. * * When in transaction, returns a db adapter object for the transaction, * returns a default adapter object otherwise. * * Treat the adapter as implementation detail and avoid accessing it directly. */ $getAdapter(): AdapterBase; /** * Use `$query` to perform raw SQL queries. * * ```ts * const value = 1; * * // it is safe to interpolate inside the backticks (``): * const result = await db.$query<{ one: number }>`SELECT ${value} one`; * // data is inside `rows` array: * result.rows[0].one; * ``` * * If the query is executing inside a transaction, it will use the transaction connection automatically. * * ```ts * await db.transaction(async () => { * // both queries will execute in the same transaction * await db.$query`SELECT 1`; * await db.$query`SELECT 2`; * }); * ``` * * Alternatively, support a simple SQL string, with optional `values`: * * Note that the values is a simple array, and the SQL is referring to the values with `$1`, `$2` and so on. * * ```ts * const value = 1; * * // it is NOT safe to interpolate inside a simple string, use `values` to pass the values. * const result = await db.$query<{ one: number }>({ * raw: 'SELECT $1 AS one', * values: [value], * }); * // data is inside `rows` array: * result.rows[0].one; * ``` * * @param args - SQL template literal, or an object { raw: string, values?: unknown[] } */ $query: Db['query']; /** * The same as the {@link $query}, but returns an array of arrays instead of objects: * * ```ts * const value = 1; * * // it is safe to interpolate inside the backticks (``): * const result = await db.$queryArrays<[number]>`SELECT ${value} AS one`; * // `rows` is an array of arrays: * const row = result.rows[0]; * row[0]; // our value * ``` * * @param args - SQL template literal, or an object { raw: string, values?: unknown[] } */ $queryArrays: Db['queryArrays']; /** * See {@link FromMethods.from} */ $from>>(arg: Arg): FromResult; /** * `$withOptions` supports overriding `log` and `schema`. * * - `log`: boolean, enables or disables logging in the scope of the callback. * - `schema`: set a **default** schema, note that it does not override * if you already have a schema set in the ORM config or for a specific table. * * ```ts * await db.$withOptions({ log: true, schema: 'custom' }, async () => { * // will log this query, and will use the custom schema for this table, * // unless this table already has a configured schema. * await db.table.find(123); * }); * ``` */ $withOptions(options: StorageOptions, cb: () => Promise): Promise; $close(): Promise; } type OrchidOrmParam = true | null extends true ? 'Set strict: true to tsconfig' : Options; declare const orchidORMWithAdapter: ({ log, logger, autoPreparedStatements, noPrimaryKey, schema, ...options }: OrchidOrmParam<({ db: Query; } | { adapter: AdapterBase; }) & DbSharedOptions>, tables: T) => OrchidORM; interface HasAndBelongsToMany extends RelationThunkBase { type: 'hasAndBelongsToMany'; options: HasAndBelongsToManyOptions; } interface HasAndBelongsToManyOptions { required?: boolean; columns: (keyof Columns)[]; references: string[]; foreignKey?: boolean | TableData.References.Options; through: { schema?: QuerySchema; table: string; columns: string[]; references: (keyof Related['columns']['shape'])[]; foreignKey?: boolean | TableData.References.Options; }; on?: ColumnsShape.InputPartial; } type HasAndBelongsToManyParams = { [Name in FK]: T['columns']['shape'][Name]['type']; }; type HasAndBelongsToManyQuery = { [K in keyof TableQuery]: K extends '__selectable' ? SelectableFromShape : K extends '__as' ? Name : TableQuery[K]; } & QueryHasWhere & HasRelJoin; interface HasAndBelongsToManyInfo extends RelationConfigBase { returnsOne: false; query: Q; params: HasAndBelongsToManyParams; maybeSingle: Q; omitForeignKeyInCreate: never; optionalDataForCreate: { [P in Name]?: { create?: CreateData[]; connect?: WhereArg[]; connectOrCreate?: { where: WhereArg; create: CreateData; }[]; }; }; dataForCreate: never; dataForUpdate: { disconnect?: MaybeArray>; set?: MaybeArray>; add?: MaybeArray>; delete?: MaybeArray>; update?: { where: MaybeArray>; data: UpdateData; }; create?: CreateData[]; }; dataForUpdateOne: { disconnect?: MaybeArray>; set?: MaybeArray>; add?: MaybeArray>; delete?: MaybeArray>; update?: { where: MaybeArray>; data: UpdateData; }; create?: CreateData[]; }; } type RelationToOneDataForCreate = { create: Rel['nestedCreateQuery']; connect?: never; connectOrCreate?: never; } | { create?: never; connect: WhereArg; connectOrCreate?: never; } | { create?: never; connect?: never; connectOrCreate: { where: WhereArg; create: Rel['nestedCreateQuery']; }; }; type RelationToOneDataForCreateSameQuery = { create: CreateData; connect?: never; connectOrCreate?: never; } | { create?: never; connect: WhereArg; connectOrCreate?: never; } | { create?: never; connect?: never; connectOrCreate: { where: WhereArg; create: CreateData; }; }; interface RelationThunkBase { type: string; related: ORMTableInput; options: unknown; } type RelationThunk = BelongsTo | HasOne | HasMany | HasAndBelongsToMany; interface RelationThunks { [K: string]: RelationThunk; } type RelationTableToQuery = Relation extends { related: infer R extends ORMTableInput; } ? TableToDb : never; interface RelationConfigSelf { columns: { shape: Column.Shape.QueryInit; }; relations: RelationThunks; } type RelationConfigParams = Relation extends BelongsTo ? BelongsToParams : Relation extends HasOne | HasMany ? HasOneParams : Relation extends HasAndBelongsToMany ? HasAndBelongsToManyParams : never; interface TableClass { new (): T; } interface TableClasses { [K: string]: TableClass; } interface TableInfo { definedAs: string; db: OrchidORM; getFilePath(): string; name: string; } interface Table extends Query, TableInfo { } interface TableToDb extends TableInfo, Db extends never ? never : ShapeColumnPrimaryKeys, ShapeUniqueColumns | TableDataItemsUniqueColumns, TableDataItemsUniqueColumnTuples, UniqueConstraints | TableDataItemsUniqueConstraints, T['types'], T['columns']['shape'] & ComputedColumnsFromOptions, MapTableScopesOption, ColumnsShape.DefaultSelectKeys> { relations: T extends RelationConfigSelf ? { [K in keyof T['relations'] & string]: T['relations'][K] extends BelongsTo ? BelongsToInfo, K>> : T['relations'][K] extends HasOne ? HasOneInfo>> : T['relations'][K] extends HasMany ? HasManyInfo>> : T['relations'][K] extends HasAndBelongsToMany ? HasAndBelongsToManyInfo>> : never; } : EmptyObject; } interface ORMTableInput { table: string; columns: { shape: Column.Shape.QueryInit; data: MaybeArray; }; schema?: QuerySchema; types: unknown; noPrimaryKey?: boolean; filePath: string; language?: string; /** * collect computed columns returned by {@link BaseTable.setColumns} */ computed?: ComputedOptionsFactory; scopes?: RecordUnknown; readonly softDelete?: true | string; comment?: string; autoForeignKeys?: TableData.References.BaseOptions | boolean; } type Queryable = ShallowSimplify<{ [K in keyof T['columns']['shape']]?: T['columns']['shape'][K]['queryType']; }>; type DefaultSelect = ShallowSimplify>; type Selectable = T['computed'] extends ((t: never) => infer R extends ComputedOptionsConfig) ? ShallowSimplify & { [K in keyof R]: R[K] extends QueryOrExpression ? R[K]['result']['value']['outputType'] : R[K] extends () => { result: { value: infer Value extends Column.Pick.QueryColumn; }; } ? Value['outputType'] : never; }> : ShallowSimplify>; type Insertable = ShallowSimplify>; type Updatable = ShallowSimplify>; type BeforeHookMethod = (cb: QueryBeforeHook) => void; type BeforeActionHookMethod = (cb: QueryBeforeActionHook) => void; type AfterHookMethod = (cb: QueryAfterHook) => void; type AfterSelectableHookMethod = (this: { columns: { shape: Shape; }; }, select: S, cb: AfterHook) => void; interface SetColumnsResult>> { shape: Shape; data: Data extends unknown[] ? Data : [Data]; } interface BaseTableInstance { table: string; columns: { shape: Column.Shape.QueryInit; data: MaybeArray; }; schema?: QuerySchema; noPrimaryKey?: boolean; snakeCase?: boolean; types: ColumnTypes; q: QueryData; language?: string; filePath: string; result: Column.Shape.QueryInit; clone(this: T): T; getFilePath(): string; setColumns>(fn: (t: ColumnTypes) => Shape, tableData?: TableDataFn): SetColumnsResult; /** * You can add a generated column in the migration (see [generated](/guide/migration-column-methods.html#generated-column)), * such column will persist in the database, it can be indexed. * * Or you can add a computed column on the ORM level, without adding it to the database, in such a way: * * ```ts * import { BaseTable, sql } from './baseTable'; * * export class UserTable extends BaseTable { * readonly table = 'user'; * columns = this.setColumns((t) => ({ * id: t.identity().primaryKey(), * firstName: t.string(), * lastName: t.string(), * })); * * computed = this.setComputed({ * fullName: (q) => * sql`${q.column('firstName')} || ' ' || ${q.column('lastName')}`.type( * (t) => t.string(), * ), * }); * } * ``` * * `setComputed` takes an object where keys are computed column names, and values are functions returning raw SQL. * * Use `q.column` as shown above to reference a table column, it will be prefixed with a correct table name even if the table is joined under a different name. * * Computed columns are not selected by default, only on demand: * * ```ts * const a = await db.user.take(); * a.fullName; // not selected * * const b = await db.user.select('*', 'fullName'); * b.fullName; // selected * * // Table post belongs to user as an author. * // it's possible to select joined computed column: * const posts = await db.post * .join('author') * .select('post.title', 'author.fullName'); * ``` * * SQL query can be generated dynamically based on the current request context. * * Imagine we are using [AsyncLocalStorage](https://nodejs.org/api/async_context.html#asynchronous-context-tracking) * to keep track of current user's language. * * And we have articles translated to different languages, each article has `title_en`, `title_uk`, `title_be` and so on. * * We can define a computed `title` by passing a function into `sql` method: * * ```ts * import { sql } from './baseTable'; * * type Locale = 'en' | 'uk' | 'be'; * const asyncLanguageStorage = new AsyncLocalStorage(); * const defaultLocale: Locale = 'en'; * * export class ArticleTable extends BaseTable { * readonly table = 'article'; * columns = this.setColumns((t) => ({ * id: t.identity().primaryKey(), * title_en: t.text(), * title_uk: t.text().nullable(), * title_be: t.text().nullable(), * })); * * computed = this.setComputed({ * title: () => * // `sql` accepts a callback to generate a new query on every run * sql(() => { * // get locale dynamically based on current storage value * const locale = asyncLanguageStorage.getStore() || defaultLocale; * * // use COALESCE in case when localized title is NULL, use title_en * return sql`COALESCE( * ${q.column(`title_${locale}`)}, * ${q.column(`title_${defaultLocale}`)} * )`; * }).type((t) => t.text()), * }); * } * ``` * * @param computed - object where keys are column names and values are functions returning raw SQL */ setComputed>(this: { columns: { shape: Shape; }; }, computed: Computed): Computed; /** * See {@link ScopeMethods} */ setScopes(this: { table: Table; columns: { shape: Shape; }; }, scopes: DbTableOptionScopes): QueryScopes; belongsTo>(this: { columns: { shape: Columns; }; }, fn: () => { new (): Related; }, options: Options): { type: 'belongsTo'; related: Related; options: Options; }; hasOne>(this: { columns: { shape: Columns; }; }, fn: () => { new (): Related; }, options: Options): { type: 'hasOne'; related: Related; options: Options; }; hasMany>(this: { columns: { shape: Columns; }; }, fn: () => { new (): Related; }, options: Options): { type: 'hasMany'; related: Related; options: Options; }; hasAndBelongsToMany>(this: { columns: { shape: Columns; }; }, fn: () => { new (): Related; }, options: Options): { type: 'hasAndBelongsToMany'; related: Related; options: Options; }; beforeQuery: BeforeHookMethod; afterQuery: AfterHookMethod; beforeCreate: BeforeActionHookMethod; afterCreate: AfterSelectableHookMethod; afterCreateCommit: AfterSelectableHookMethod; beforeUpdate: BeforeActionHookMethod; afterUpdate: AfterSelectableHookMethod; afterUpdateCommit: AfterSelectableHookMethod; beforeSave: BeforeActionHookMethod; afterSave: AfterSelectableHookMethod; afterSaveCommit: AfterSelectableHookMethod; beforeDelete: BeforeHookMethod; afterDelete: AfterSelectableHookMethod; afterDeleteCommit: AfterSelectableHookMethod; } interface BaseTableClass { nowSQL: string | undefined; exportAs: string; columnTypes: ColumnTypes; getFilePath(): string; sql: DbSqlMethod; new (): BaseTableInstance; instance(): BaseTableInstance; /** * All column types for inserting. */ inputSchema: SchemaConfig['inputSchema']; /** * All column types as returned from a database. */ outputSchema: SchemaConfig['outputSchema']; /** * All column types for query methods such as `where`. */ querySchema: SchemaConfig['querySchema']; /** * Primary key column(s) type for query methods such as `where`. */ pkeySchema: SchemaConfig['pkeySchema']; /** * Column types for inserting, excluding primary keys. * Equals to {@link inputSchema} without primary keys. */ createSchema: SchemaConfig['createSchema']; /** * Column types for updating, excluding primary keys. * Equals to partial {@link createSchema}. */ updateSchema: SchemaConfig['updateSchema']; } declare function createBaseTable>({ schemaConfig, columnTypes: columnTypesArg, snakeCase, filePath: filePathArg, nowSQL, exportAs, language, autoForeignKeys, }?: { schemaConfig?: SchemaConfig; columnTypes?: ColumnTypes | ((t: DefaultColumnTypes) => ColumnTypes); snakeCase?: boolean; filePath?: string; nowSQL?: string; exportAs?: string; language?: string; autoForeignKeys?: boolean | TableData.References.BaseOptions; }): BaseTableClass; type QueryMethods = Record any>; type QueryOne = { [K in keyof T]: K extends 'returnType' ? Exclude : T[K]; }; interface MethodsBase { queryMethods?: QueryMethods; queryOneMethods?: QueryMethods>; queryWithWhereMethods?: QueryMethods; queryOneWithWhereMethods?: QueryMethods>; methods?: RecordUnknown; } type MapQueryMethods = Method extends (q: any, ...args: infer Args) => infer Result ? (this: T, ...args: Args) => Result extends Query ? MergeQuery : Result : never; type MapMethods> = { [K in keyof Methods['queryMethods'] | keyof Methods['queryOneMethods'] | keyof Methods['queryWithWhereMethods'] | keyof Methods['queryOneWithWhereMethods'] | keyof Methods['methods']]: K extends keyof Methods['methods'] ? Methods['methods'][K] : K extends keyof Methods['queryOneWithWhereMethods'] ? MapQueryMethods, Methods['queryOneWithWhereMethods'][K]> : K extends keyof Methods['queryWithWhereMethods'] ? MapQueryMethods : K extends keyof Methods['queryOneMethods'] ? MapQueryMethods, Methods['queryOneMethods'][K]> : K extends keyof Methods['queryMethods'] ? MapQueryMethods : never; }; type Repo> = T & MapMethods; declare const createRepo: >(table: T, methods: Methods) => Repo<((q: Q) => Query & Q & MapMethods) & T, Methods>; export { createBaseTable, createRepo, orchidORMWithAdapter }; export type { BaseTableClass, BaseTableInstance, DefaultSelect, FromQuery, Insertable, MapMethods, MapQueryMethods, MethodsBase, ORMTableInput, OrchidORM, OrchidOrmParam, Queryable, Repo, Selectable, SetColumnsResult, Table, TableClass, TableClasses, TableInfo, TableToDb, Updatable };