pqb 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -50,6 +50,14 @@ declare class TransactionAdapter implements Adapter {
50
50
  declare abstract class OrchidOrmError extends Error {
51
51
  abstract query: Query;
52
52
  }
53
+ /**
54
+ * When we search for a single record, and it is not found, it can either throw an error, or return `undefined`.
55
+ *
56
+ * Unlike other database libraries, `Orchid ORM` decided to throw errors by default when using methods `take`, `find`, `findBy`, `get` and the record is not found.
57
+ * It is a [good practice](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/errorhandling/centralizedhandling.md) to catch common errors in a centralized place (see [global error handling](https://orchid-orm.netlify.app/guide/error-handling.html#global-error-handling)), and this allows for a more concise code.
58
+ *
59
+ * If it's more suitable to get the `undefined` value instead of throwing, use `takeOptional`, `findOptional`, `findByOptional`, `getOptional` instead.
60
+ */
53
61
  declare class NotFoundError extends OrchidOrmError {
54
62
  query: Query;
55
63
  constructor(query: Query, message?: string);
@@ -173,6 +181,11 @@ declare const toSql: (table: Query, options?: ToSqlOptions) => Sql;
173
181
  declare const makeSql: (table: Query, options?: ToSqlOptionsInternal) => Sql;
174
182
 
175
183
  declare abstract class QueryBase implements QueryBaseCommon {
184
+ /**
185
+ * Clones the current query chain, useful for re-using partial query snippets in other queries without mutating the original.
186
+ *
187
+ * Used under the hood, and not really needed on the app side.
188
+ */
176
189
  clone<T extends QueryBase>(this: T): T;
177
190
  abstract result: ColumnsShapeBase;
178
191
  query: QueryData;
@@ -451,6 +464,7 @@ type CommonQueryData = {
451
464
  logger: QueryLogger;
452
465
  autoPreparedStatements?: boolean;
453
466
  [toSqlCacheKey]?: Sql;
467
+ transform?: ((input: unknown) => unknown)[];
454
468
  };
455
469
  type SelectQueryData = CommonQueryData & {
456
470
  type: undefined;
@@ -603,7 +617,7 @@ declare const parseRecord: (parsers: ColumnsParsers, row: any) => any;
603
617
 
604
618
  type SelectArg<T extends QueryBase> = '*' | StringKey<keyof T['selectable']> | SelectAsArg<T>;
605
619
  type SelectAsArg<T extends QueryBase> = Record<string, SelectAsValue<T>>;
606
- type SelectAsValue<T extends QueryBase> = StringKey<keyof T['selectable']> | RawExpression | ((q: T) => Query) | ((q: T) => RawExpression) | ((q: T) => Query | RawExpression);
620
+ type SelectAsValue<T extends QueryBase> = StringKey<keyof T['selectable']> | RawExpression | ((q: T) => QueryBase) | ((q: T) => RawExpression) | ((q: T) => QueryBase | RawExpression);
607
621
  type SelectObjectResultTuple = [ColumnsShapeBase, SelectableBase];
608
622
  type SelectResult<T extends Query, Args extends SelectArg<T>[], SelectStringsResult extends ColumnsShapeBase = SelectStringArgsResult<T, Args>, StringsKeys extends keyof SelectStringsResult = keyof SelectStringsResult, SelectAsResult extends SelectObjectResultTuple = SpreadSelectObjectArgs<T, Args, [
609
623
  EmptyObject,
@@ -622,7 +636,7 @@ type SelectStringArgsResult<T extends Query, Args extends SelectArg<T>[]> = {
622
636
  };
623
637
  type SpreadSelectObjectArgs<T extends Query, Args extends [...unknown[]], Result extends SelectObjectResultTuple> = Args extends [infer L, ...infer R] ? SpreadSelectObjectArgs<T, R, SelectAsResult<T, L, Result>> : Result;
624
638
  type SelectAsResult<T extends Query, Arg, Result extends SelectObjectResultTuple, Shape = Result[0], AddSelectable extends SelectableBase = {
625
- [K in keyof Arg]: Arg[K] extends ((q: T) => infer R extends Query) ? (x: {
639
+ [K in keyof Arg]: Arg[K] extends ((q: T) => infer R extends QueryBase) ? (x: {
626
640
  [C in keyof R['result'] as `${StringKey<K>}.${StringKey<C>}`]: {
627
641
  as: C;
628
642
  column: R['result'][C];
@@ -636,8 +650,8 @@ type SelectAsResult<T extends Query, Arg, Result extends SelectObjectResultTuple
636
650
  },
637
651
  Result[1] & AddSelectable
638
652
  ] : Result;
639
- type SelectAsValueResult<T extends Query, Arg extends SelectAsValue<T>> = Arg extends keyof T['selectable'] ? T['selectable'][Arg]['column'] : Arg extends RawExpression ? Arg['__column'] : Arg extends (q: T) => infer R ? R extends Query ? SelectSubQueryResult<R> : R extends RawExpression ? R['__column'] : R extends Query | RawExpression ? SelectSubQueryResult<Exclude<R, RawExpression>> | Exclude<R, Query>['__column'] : never : never;
640
- type SelectSubQueryResult<Arg extends Query & {
653
+ type SelectAsValueResult<T extends Query, Arg extends SelectAsValue<T>> = Arg extends keyof T['selectable'] ? T['selectable'][Arg]['column'] : Arg extends RawExpression ? Arg['__column'] : Arg extends (q: T) => infer R ? R extends QueryBase ? SelectSubQueryResult<R> : R extends RawExpression ? R['__column'] : R extends QueryBase | RawExpression ? SelectSubQueryResult<Exclude<R, RawExpression>> | Exclude<R, QueryBase>['__column'] : never : never;
654
+ type SelectSubQueryResult<Arg extends QueryBase & {
641
655
  [isRequiredRelationKey]?: boolean;
642
656
  }> = QueryReturnsAll<Arg['returnType']> extends true ? ArrayOfColumnsObjects<Arg['result']> : Arg['returnType'] extends 'valueOrThrow' ? Arg['result']['value'] : Arg['returnType'] extends 'pluck' ? PluckResultColumnType<Arg['result']['pluck']> : Arg[isRequiredRelationKey] extends true ? ColumnsObject<Arg['result']> : NullableColumn<ColumnsObject<Arg['result']>>;
643
657
  declare const addParserForRawExpression: (q: Query, key: string | getValueKey, raw: RawExpression) => void;
@@ -746,6 +760,34 @@ type FromQueryResult<T extends Query, Q extends Query, Selectable extends Select
746
760
  } : K extends 'selectable' ? Selectable : K extends 'result' | 'shape' ? Q['result'] : K extends 'then' ? QueryThen<Data> : K extends 'catch' ? QueryCatch<Data> : T[K];
747
761
  };
748
762
  declare class From {
763
+ /**
764
+ * Set the `FROM` value, by default the table name is used.
765
+ *
766
+ * ```ts
767
+ * // accepts sub-query:
768
+ * db.table.from(Otherdb.table.select('foo', 'bar'));
769
+ *
770
+ * // accepts raw sql by template literal:
771
+ * const value = 123;
772
+ * db.table.from`value = ${value}`;
773
+ *
774
+ * // accepts raw sql:
775
+ * db.table.from(db.table.sql`value = ${value}`);
776
+ *
777
+ * // accepts alias of `WITH` expression:
778
+ * q.with('foo', Otherdb.table.select('id', 'name')).from('foo');
779
+ * ```
780
+ *
781
+ * Optionally takes a second argument of type `{ only?: boolean }`, (see `FROM ONLY` in Postgres docs, this is related to table inheritance).
782
+ *
783
+ * ```ts
784
+ * db.table.from(Otherdb.table.select('foo', 'bar'), {
785
+ * only: true,
786
+ * });
787
+ * ```
788
+ *
789
+ * @param args - query, raw SQL, name of CTE table, or a template string
790
+ */
749
791
  from<T extends Query, Args extends FromArgs<T>>(this: T, ...args: Args): FromResult<T, Args>;
750
792
  _from<T extends Query, Args extends FromArgs<T>>(this: T, ...args: Args): FromResult<T, Args>;
751
793
  }
@@ -2471,6 +2513,106 @@ type SqlResult<T extends Query, Args extends SqlArgs<T>> = Args extends SqlColum
2471
2513
  }] | TemplateLiteralArgs ? RawExpression : SqlFn<ColumnType>;
2472
2514
  type RawArgs<CT extends ColumnTypesBase, C extends ColumnType> = [column: (types: CT) => C, sql: string, values?: Record<string, unknown>] | [sql: string, values?: Record<string, unknown>];
2473
2515
  declare class RawSqlMethods {
2516
+ /**
2517
+ * When there is a need to use a piece of raw SQL, use the `sql` method.
2518
+ *
2519
+ * To select with a raw SQL, need to specify a column type as a first argument, so the TS could use it to guess the result type of the query:
2520
+ *
2521
+ * ```ts
2522
+ * const result: { num: number }[] = await db.table.select({
2523
+ * num: db.table.sql((t) => t.integer())`
2524
+ * random() * 100
2525
+ * `,
2526
+ * });
2527
+ * ```
2528
+ *
2529
+ * Other than for select, the column type can be omitted:
2530
+ *
2531
+ * ```ts
2532
+ * await db.table.where(db.table.sql`
2533
+ * "someValue" = random() * 100
2534
+ * `);
2535
+ * ```
2536
+ *
2537
+ * Interpolating values in template literals is completely safe:
2538
+ *
2539
+ * ```ts
2540
+ * // get value from user-provided params
2541
+ * const { value } = req.params;
2542
+ *
2543
+ * // SQL injection is prevented by a library, this is safe:
2544
+ * await db.table.where(db.table.sql`
2545
+ * column = ${value}
2546
+ * `);
2547
+ * ```
2548
+ *
2549
+ * SQL can be passed with a simple string, it's important to note that this is not safe to interpolate values in it.
2550
+ *
2551
+ * ```ts
2552
+ * // no interpolation is okay
2553
+ * await db.table.where(db.table.sql({ raw: 'column = random() * 100' }));
2554
+ *
2555
+ * // get value from user-provided params
2556
+ * const { value } = req.params;
2557
+ *
2558
+ * // this is NOT safe, SQL injection is possible:
2559
+ * await db.table.where(db.table.sql({ raw: `column = random() * ${value}` }));
2560
+ * ```
2561
+ *
2562
+ * To inject values into `raw` SQL strings, define it with `$` in the string and provide `values` object.
2563
+ *
2564
+ * Use `$$` to provide column or/and table name. Column names will be quoted so don't quote them manually.
2565
+ *
2566
+ * ```ts
2567
+ * // get value from user-provided params
2568
+ * const { value } = req.params;
2569
+ *
2570
+ * // this is SAFE, SQL injection are prevented:
2571
+ * await db.table.where(
2572
+ * db.table.sql({
2573
+ * values: {
2574
+ * column: 'someTable.someColumn', // or simply 'column'
2575
+ * one: value,
2576
+ * two: 123,
2577
+ * },
2578
+ * raw: '$$column = random() * $value',
2579
+ * }),
2580
+ * );
2581
+ * ```
2582
+ *
2583
+ * Summarizing:
2584
+ *
2585
+ * ```ts
2586
+ * // simplest form:
2587
+ * db.table`key = ${value}`;
2588
+ *
2589
+ * // with column type for select:
2590
+ * db.table((t) => t.boolean())`key = ${value}`;
2591
+ *
2592
+ * // raw SQL string, not allowed to interpolate:
2593
+ * db.table({ raw: 'random()' });
2594
+ *
2595
+ * // with values:
2596
+ * db.table({
2597
+ * values: {
2598
+ * column: 'columnName',
2599
+ * one: 1,
2600
+ * two: 2,
2601
+ * },
2602
+ * raw: '$$columnName = $one + $two',
2603
+ * });
2604
+ *
2605
+ * // with column type for select:
2606
+ * db.table((t) => t.decimal(), { raw: 'random()' });
2607
+ *
2608
+ * // combine values and template literal:
2609
+ * db.table({ values: { one: 1, two: 2 } })`
2610
+ * ($one + $two) / $one
2611
+ * `;
2612
+ * ```
2613
+ *
2614
+ * @param args - template string or a specific options
2615
+ */
2474
2616
  sql<T extends Query, Args extends SqlArgs<T>>(this: T, ...args: Args): SqlResult<T, Args>;
2475
2617
  /**
2476
2618
  * @deprecated use `sql` method instead, `raw` will be removed
@@ -2530,10 +2672,87 @@ declare class CopyMethods {
2530
2672
  }
2531
2673
 
2532
2674
  declare abstract class AsMethods extends QueryBase {
2675
+ /**
2676
+ * Sets table alias:
2677
+ *
2678
+ * ```ts
2679
+ * db.table.as('u').select('u.name');
2680
+ *
2681
+ * // Can be used in the join:
2682
+ * db.table.join(Profile.as('p'), 'p.userId', 'user.id');
2683
+ * ```
2684
+ *
2685
+ * @param as - alias for the table of this query
2686
+ */
2533
2687
  as<T extends AsMethods, As extends string>(this: T, as: As): SetQueryTableAlias<T, As>;
2534
2688
  _as<T extends AsMethods, As extends string>(this: T, as: As): SetQueryTableAlias<T, As>;
2535
2689
  }
2536
2690
 
2691
+ type TransformFn<T extends Query> = (input: T['catch'] extends QueryCatch<infer Data> ? Data : never) => unknown;
2692
+ type QueryTransform<T extends QueryBase, Fn extends TransformFn<Query>, Data = ReturnType<Fn>> = {
2693
+ [K in keyof QueryBase]: K extends 'returnType' ? 'valueOrThrow' : K extends 'result' ? {
2694
+ value: ColumnTypeBase<Data>;
2695
+ } : T[K];
2696
+ } & {
2697
+ then: QueryThen<Data>;
2698
+ catch: QueryCatch<Data>;
2699
+ };
2700
+ declare class TransformMethods {
2701
+ /**
2702
+ * Transform the result of the query right after loading it.
2703
+ *
2704
+ * `transform` method should be called in the last order, other methods can't be chained after calling it.
2705
+ *
2706
+ * The [hooks](/guide/hooks.html) that are going to run after the query will receive the query result **before** transferring.
2707
+ *
2708
+ * Consider the following example of a cursor-based pagination by `id`:
2709
+ *
2710
+ * ```ts
2711
+ * const lastId: number | undefined = req.query.cursor;
2712
+ *
2713
+ * type Result = {
2714
+ * nodes: { id: number; text: string }[];
2715
+ * cursor?: number;
2716
+ * };
2717
+ *
2718
+ * // result is only for demo, it will be inferred
2719
+ * const posts: Result = await db.post
2720
+ * .select('id', 'text')
2721
+ * .where({ id: { lt: lastId } })
2722
+ * .order({ id: 'DESC' })
2723
+ * .limit(100)
2724
+ * .transform((nodes) => ({ nodes, cursor: nodes.at(-1)?.id }));
2725
+ * ```
2726
+ *
2727
+ * You can also use the `tranform` on a nested sub-queries:
2728
+ *
2729
+ * ```ts
2730
+ * type Result = {
2731
+ * nodes: {
2732
+ * id: number;
2733
+ * text: string;
2734
+ * comments: { nodes: { id: number; text: string }[]; cursor?: number };
2735
+ * }[];
2736
+ * cursor?: number;
2737
+ * };
2738
+ *
2739
+ * const postsWithComments: Result = await db.post
2740
+ * .select('id', 'text')
2741
+ * .select({
2742
+ * comments: (q) =>
2743
+ * q.comments
2744
+ * .select('id', 'text')
2745
+ * .transform((nodes) => ({ nodes, cursor: nodes.at(-1)?.id })),
2746
+ * })
2747
+ * .transform((nodes) => ({ nodes, cursor: nodes.at(-1)?.id }));
2748
+ * ```
2749
+ *
2750
+ * @param fn - function to transform query result with
2751
+ */
2752
+ transform<T extends Query, Fn extends TransformFn<T>>(this: T, fn: Fn): QueryTransform<T, Fn>;
2753
+ _transform<T extends Query, Fn extends TransformFn<T>>(this: T, fn: Fn): QueryTransform<T, Fn>;
2754
+ }
2755
+
2537
2756
  type WindowArg<T extends Query> = Record<string, WindowArgDeclaration<T> | RawExpression>;
2538
2757
  type WindowArgDeclaration<T extends Query = Query> = {
2539
2758
  partitionBy?: Expression<T> | Expression<T>[];
@@ -2552,7 +2771,7 @@ type FindArgs<T extends Query> = [T['shape'][T['singlePrimaryKey']]['type'] | Ra
2552
2771
  type QueryHelper<T extends Query, Args extends unknown[], Result> = <Q extends {
2553
2772
  [K in keyof T]: K extends 'then' ? QueryThen<unknown> : K extends 'result' ? ColumnsShapeBase : T[K];
2554
2773
  }>(q: Q, ...args: Args) => Result extends Query ? MergeQuery<Q, Result> : Result;
2555
- interface QueryMethods extends Omit<AsMethods, 'result'>, Aggregate, Select, From, Join, With, Union, Omit<JsonModifiers, 'result'>, JsonMethods, Create, Update, Delete, Transaction, For, ColumnInfoMethods, Omit<Where, 'result'>, Clear, Having, Window, Then, QueryLog, QueryHooks, QueryUpsertOrCreate, QueryGet, MergeQueryMethods, RawSqlMethods, CopyMethods {
2774
+ interface QueryMethods extends Omit<AsMethods, 'result'>, Aggregate, Select, From, Join, With, Union, Omit<JsonModifiers, 'result'>, JsonMethods, Create, Update, Delete, Transaction, For, ColumnInfoMethods, Omit<Where, 'result'>, Clear, Having, Window, Then, QueryLog, QueryHooks, QueryUpsertOrCreate, QueryGet, MergeQueryMethods, RawSqlMethods, CopyMethods, TransformMethods {
2556
2775
  }
2557
2776
  declare class QueryMethods {
2558
2777
  windows: EmptyObject;
@@ -2629,6 +2848,22 @@ declare class QueryMethods {
2629
2848
  exec<T extends Query>(this: T): SetQueryReturnsVoid<T>;
2630
2849
  _exec<T extends Query>(this: T): SetQueryReturnsVoid<T>;
2631
2850
  toSql(this: Query, options?: ToSqlOptions): Sql;
2851
+ /**
2852
+ * Adds a `DISTINCT` keyword to `SELECT`:
2853
+ *
2854
+ * ```ts
2855
+ * db.table.distinct().select('name');
2856
+ * ```
2857
+ *
2858
+ * Can accept column names or raw expressions to place it to `DISTINCT ON (...)`:
2859
+ *
2860
+ * ```ts
2861
+ * // Distinct on the name and raw SQL
2862
+ * db.table.distinct('name', db.table.sql`raw sql`).select('id', 'name');
2863
+ * ```
2864
+ *
2865
+ * @param columns - column names or a raw SQL
2866
+ */
2632
2867
  distinct<T extends Query>(this: T, ...columns: Expression<T>[]): T;
2633
2868
  _distinct<T extends Query>(this: T, ...columns: Expression<T>[]): T;
2634
2869
  /**
@@ -2703,20 +2938,108 @@ declare class QueryMethods {
2703
2938
  */
2704
2939
  withSchema<T extends Query>(this: T, schema: string): T;
2705
2940
  _withSchema<T extends Query>(this: T, schema: string): T;
2941
+ /**
2942
+ * For the `GROUP BY` SQL statement, it is accepting column names or raw expressions.
2943
+ *
2944
+ * `group` is useful when aggregating values.
2945
+ *
2946
+ * ```ts
2947
+ * // Select the category and sum of prices grouped by the category
2948
+ * const results = Product.select('category')
2949
+ * .selectSum('price', { as: 'sumPrice' })
2950
+ * .group('category');
2951
+ * ```
2952
+ *
2953
+ * @param columns - column names or a raw SQL
2954
+ */
2706
2955
  group<T extends Query>(this: T, ...columns: Expression<T>[]): T;
2707
2956
  _group<T extends Query>(this: T, ...columns: Expression<T>[]): T;
2708
2957
  window<T extends Query, W extends WindowArg<T>>(this: T, arg: W): WindowResult<T, W>;
2709
2958
  _window<T extends Query, W extends WindowArg<T>>(this: T, arg: W): WindowResult<T, W>;
2710
2959
  wrap<T extends Query, Q extends Query, As extends string = 't'>(this: T, query: Q, as?: As): SetQueryTableAlias<Q, As>;
2711
2960
  _wrap<T extends Query, Q extends Query, As extends string = 't'>(this: T, query: Q, as?: As): SetQueryTableAlias<Q, As>;
2961
+ /**
2962
+ * Adds an order by clause to the query.
2963
+ *
2964
+ * Takes one or more arguments, each argument can be a column name, an object, or a raw expression.
2965
+ *
2966
+ * ```ts
2967
+ * db.table.order('id', 'name'); // ASC by default
2968
+ *
2969
+ * db.table.order({
2970
+ * id: 'ASC', // or DESC
2971
+ *
2972
+ * // to set nulls order:
2973
+ * name: 'ASC NULLS FIRST',
2974
+ * age: 'DESC NULLS LAST',
2975
+ * });
2976
+ *
2977
+ * // order by raw SQL expression:
2978
+ * db.table.order`raw sql`;
2979
+ * // or
2980
+ * db.table.order(db.table.sql`raw sql`);
2981
+ * ```
2982
+ *
2983
+ * `order` can refer to the values returned from `select` sub-queries (unlike `where` which cannot).
2984
+ * So you can select a count of related records and order by it.
2985
+ *
2986
+ * For example, `comment` has many `likes`.
2987
+ * We are selecting few columns of `comment`, selecting `likesCount` by a sub-query in a select, and ordering comments by likes count:
2988
+ *
2989
+ * ```ts
2990
+ * db.comment
2991
+ * .select('title', 'content', {
2992
+ * likesCount: (q) => q.likes.count(),
2993
+ * })
2994
+ * .order({
2995
+ * likesCount: 'DESC',
2996
+ * });
2997
+ * ```
2998
+ *
2999
+ * @param args - column name(s), raw SQL, or an object with column names and sort directions.
3000
+ */
2712
3001
  order<T extends Query>(this: T, ...args: OrderArgs<T>): T;
2713
3002
  _order<T extends Query>(this: T, ...args: OrderArgs<T>): T;
3003
+ /**
3004
+ * Adds a limit clause to the query.
3005
+ *
3006
+ * ```ts
3007
+ * db.table.limit(10);
3008
+ * ```
3009
+ *
3010
+ * @param arg - limit number
3011
+ */
2714
3012
  limit<T extends Query>(this: T, arg: number | undefined): T;
2715
3013
  _limit<T extends Query>(this: T, arg: number | undefined): T;
3014
+ /**
3015
+ * Adds an offset clause to the query.
3016
+ *
3017
+ * ```ts
3018
+ * db.table.offset(10);
3019
+ * ```
3020
+ *
3021
+ * @param arg - offset number
3022
+ */
2716
3023
  offset<T extends Query>(this: T, arg: number | undefined): T;
2717
3024
  _offset<T extends Query>(this: T, arg: number | undefined): T;
2718
3025
  exists<T extends Query>(this: T): SetQueryReturnsColumn<T, BooleanColumn>;
2719
3026
  _exists<T extends Query>(this: T): SetQueryReturnsColumn<T, BooleanColumn>;
3027
+ /**
3028
+ * Truncates the specified table.
3029
+ *
3030
+ * ```ts
3031
+ * // simply truncate
3032
+ * await db.table.truncate();
3033
+ *
3034
+ * // restart autoincrementing columns:
3035
+ * await db.table.truncate({ restartIdentity: true });
3036
+ *
3037
+ * // truncate also dependant tables:
3038
+ * await db.table.truncate({ cascade: true });
3039
+ * ```
3040
+ *
3041
+ * @param options - truncate options, may have `cascade: true` and `restartIdentity: true`
3042
+ */
2720
3043
  truncate<T extends Query>(this: T, options?: {
2721
3044
  restartIdentity?: boolean;
2722
3045
  cascade?: boolean;