pqb 0.3.2 → 0.3.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqb",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Postgres query builder",
5
5
  "homepage": "https://porm.netlify.app/guide/query-builder.html",
6
6
  "repository": {
@@ -10,6 +10,7 @@ import {
10
10
  useTestDatabase,
11
11
  } from '../test-utils';
12
12
  import { createDb } from '../db';
13
+ import { columnTypes } from './columnTypes';
13
14
 
14
15
  describe('column base', () => {
15
16
  useTestDatabase();
@@ -107,7 +108,7 @@ describe('column base', () => {
107
108
  });
108
109
 
109
110
  it('should return column data as returned from db if not set', async () => {
110
- const db = createDb({ adapter });
111
+ const db = createDb({ adapter, columnTypes });
111
112
 
112
113
  const UserWithPlainTimestamp = db('user', (t) => ({
113
114
  createdAt: t.timestamp(),
@@ -1,6 +1,7 @@
1
1
  import { ColumnInput, ColumnOutput, ColumnType } from './columnType';
2
2
  import { Operators } from '../columnsOperators';
3
- import { SetOptional, SomeIsTrue, UnionToIntersection } from '../utils';
3
+ import { SetOptional, SomeIsTrue } from '../utils';
4
+ import { StringKey } from '../common';
4
5
 
5
6
  export type ColumnsShape = Record<string, ColumnType>;
6
7
 
@@ -53,63 +54,22 @@ export abstract class PluckResultColumnType<
53
54
  C extends ColumnType,
54
55
  > extends ColumnType<C['type'][], typeof Operators.any> {}
55
56
 
56
- type UnionKeyofToOvlds<S, U> = UnionToIntersection<
57
- U extends keyof S ? (f: U) => void : never
58
- >;
59
-
60
- type PopKeyofColumnShapeUnion<
61
- S extends ColumnsShape,
62
- U extends keyof S,
63
- > = UnionKeyofToOvlds<S, U> extends (a: infer A extends keyof S) => void
64
- ? A
65
- : never;
66
-
67
- type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
68
-
69
- export type UnionToArray<
70
- S extends ColumnsShape,
71
- T extends keyof S,
72
- A extends [...(keyof S)[]] = [],
73
- > = IsUnion<T> extends true
74
- ? UnionToArray<
75
- S,
76
- Exclude<T, PopKeyofColumnShapeUnion<S, T>>,
77
- [PopKeyofColumnShapeUnion<S, T>, ...A]
78
- >
79
- : [T, ...A];
80
-
81
- type GetPrimaryKeys<S extends ColumnsShape> = UnionToArray<
82
- S,
83
- { [K in keyof S]: S[K] extends { isPrimaryKey: true } ? K : never }[keyof S]
57
+ // resolves in string literal of single primary key
58
+ // if table has two or more primary keys it will resolve in never
59
+ export type SinglePrimaryKey<Shape extends ColumnsShape> = StringKey<
60
+ {
61
+ [K in keyof Shape]: Shape[K]['isPrimaryKey'] extends true
62
+ ? [
63
+ {
64
+ [S in keyof Shape]: Shape[S]['isPrimaryKey'] extends true
65
+ ? S extends K
66
+ ? never
67
+ : S
68
+ : never;
69
+ }[keyof Shape],
70
+ ] extends [never]
71
+ ? K
72
+ : never
73
+ : never;
74
+ }[keyof Shape]
84
75
  >;
85
-
86
- type GetPrimaryTypes<
87
- S extends ColumnsShape,
88
- Keys extends [...(keyof S | string)[]] = GetPrimaryKeys<S>,
89
- > = GetTypesFromKeys<S, Keys>;
90
-
91
- type GetTypesFromKeys<
92
- S extends ColumnsShape,
93
- T extends [...(keyof S)[]],
94
- > = T extends [
95
- infer Head extends keyof S,
96
- ...infer Tail extends [...(keyof S)[]],
97
- ]
98
- ? [GetTypeFromKey<S, Head>, ...GetTypesFromKeys<S, Tail>]
99
- : [];
100
-
101
- type GetTypeFromKey<S extends ColumnsShape, T extends keyof S> = S[T]['type'];
102
-
103
- export class TableSchema<Shape extends ColumnsShape> {
104
- primaryKeys: string extends keyof Shape ? string[] : GetPrimaryKeys<Shape>;
105
- primaryTypes!: GetPrimaryTypes<Shape>;
106
-
107
- constructor(public shape: Shape) {
108
- this.primaryKeys = Object.entries(this.shape)
109
- .filter(([, column]) => {
110
- return column.isPrimaryKey;
111
- })
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
- .map(([key]) => key) as any;
114
- }
115
- }
package/src/db.test.ts CHANGED
@@ -30,7 +30,7 @@ describe('db', () => {
30
30
  it('should return date as string by default', async () => {
31
31
  await User.insert(userData);
32
32
 
33
- const db = createDb({ adapter });
33
+ const db = createDb({ adapter, columnTypes });
34
34
  const table = db('user', (t) => ({
35
35
  createdAt: t.timestamp(),
36
36
  }));
package/src/db.ts CHANGED
@@ -17,13 +17,11 @@ import { QueryData, SelectQueryData, Sql, ToSqlOptions } from './sql';
17
17
  import { AdapterOptions, Adapter } from './adapter';
18
18
  import {
19
19
  ColumnsShape,
20
- columnTypes,
21
20
  ColumnShapeOutput,
22
- TableSchema,
23
21
  ColumnShapeInput,
24
- ColumnTypes,
25
22
  ColumnTypesBase,
26
23
  getColumnTypes,
24
+ SinglePrimaryKey,
27
25
  } from './columnSchema';
28
26
  import { applyMixins, pushOrNewArray } from './utils';
29
27
  import { StringKey } from './common';
@@ -46,23 +44,20 @@ export interface Db<
46
44
  ): this;
47
45
 
48
46
  adapter: Adapter;
47
+ columns: (keyof ColumnShapeOutput<Shape>)[];
48
+
49
49
  queryBuilder: Db;
50
50
  whereQueryBuilder: Query['whereQueryBuilder'];
51
+ onQueryBuilder: Query['onQueryBuilder'];
51
52
  table: Table;
52
53
  shape: Shape;
53
- schema: TableSchema<Shape>;
54
+ singlePrimaryKey: SinglePrimaryKey<Shape>;
55
+ primaryKeys: Query['primaryKeys'];
54
56
  type: ColumnShapeOutput<Shape>;
55
57
  inputType: ColumnShapeInput<Shape>;
56
- returnType: 'all';
57
- then: ThenResult<
58
- Pick<ColumnShapeOutput<Shape>, DefaultSelectColumns<Shape>[number]>[]
59
- >;
60
58
  query: QueryData;
61
- columns: (keyof ColumnShapeOutput<Shape>)[];
62
- defaultSelectColumns: DefaultSelectColumns<Shape>;
63
- columnsParsers?: ColumnsParsers;
64
59
  result: Pick<Shape, DefaultSelectColumns<Shape>[number]>;
65
- hasSelect: false;
60
+ hasSelect: Query['hasSelect'];
66
61
  hasWhere: boolean;
67
62
  selectable: { [K in keyof Shape]: { as: K; column: Shape[K] } } & {
68
63
  [K in keyof Shape as `${Table}.${StringKey<K>}`]: {
@@ -70,11 +65,17 @@ export interface Db<
70
65
  column: Shape[K];
71
66
  };
72
67
  };
68
+ returnType: Query['returnType'];
69
+ then: ThenResult<
70
+ Pick<ColumnShapeOutput<Shape>, DefaultSelectColumns<Shape>[number]>[]
71
+ >;
73
72
  tableAlias: undefined;
74
- windows: PropertyKey[];
75
- withData: Query['withData'];
76
73
  joinedTables: Query['joinedTables'];
74
+ windows: Query['windows'];
75
+ defaultSelectColumns: DefaultSelectColumns<Shape>;
76
+ columnsParsers?: ColumnsParsers;
77
77
  relations: Relations;
78
+ withData: Query['withData'];
78
79
  [defaultsKey]: Record<
79
80
  {
80
81
  [K in keyof Shape]: Shape[K]['hasDefault'] extends true ? K : never;
@@ -105,7 +106,6 @@ export class Db<
105
106
  this.query = {
106
107
  adapter,
107
108
  handleResult: handleResult,
108
- returnType: 'all',
109
109
  logger,
110
110
  log: logParamToLogObject(logger, options.log),
111
111
  } as QueryData;
@@ -114,7 +114,14 @@ export class Db<
114
114
  this.query.schema = options.schema;
115
115
  }
116
116
 
117
- this.schema = new TableSchema(shape);
117
+ this.primaryKeys = Object.keys(shape).filter(
118
+ (key) => shape[key].isPrimaryKey,
119
+ );
120
+ if (this.primaryKeys.length === 1) {
121
+ this.singlePrimaryKey = this
122
+ .primaryKeys[0] as unknown as SinglePrimaryKey<Shape>;
123
+ }
124
+
118
125
  const columns = Object.keys(
119
126
  shape,
120
127
  ) as unknown as (keyof ColumnShapeOutput<Shape>)[];
@@ -176,18 +183,18 @@ type DbResult<CT extends ColumnTypesBase> = Db & {
176
183
  close: Adapter['close'];
177
184
  };
178
185
 
179
- export type DbOptions<CT extends ColumnTypesBase = ColumnTypes> = (
186
+ export type DbOptions<CT extends ColumnTypesBase> = (
180
187
  | { adapter: Adapter }
181
188
  | Omit<AdapterOptions, 'log'>
182
189
  ) &
183
190
  QueryLogOptions & {
184
- columnTypes?: CT;
191
+ columnTypes: CT;
185
192
  };
186
193
 
187
- export const createDb = <CT extends ColumnTypesBase = ColumnTypes>({
194
+ export const createDb = <CT extends ColumnTypesBase>({
188
195
  log,
189
196
  logger,
190
- columnTypes: ct = columnTypes as unknown as CT,
197
+ columnTypes: ct,
191
198
  ...options
192
199
  }: DbOptions<CT>): DbResult<CT> => {
193
200
  const adapter = 'adapter' in options ? options.adapter : new Adapter(options);
package/src/query.ts CHANGED
@@ -1,20 +1,18 @@
1
- import { QueryMethods } from './queryMethods/queryMethods';
2
- import { QueryData } from './sql';
3
1
  import {
4
- ColumnShapeOutput,
5
- ColumnsShape,
6
- ColumnType,
7
- TableSchema,
8
- } from './columnSchema';
9
- import { Spread } from './utils';
2
+ QueryMethods,
3
+ ThenResult,
4
+ ColumnInfo,
5
+ WhereQueryBuilder,
6
+ OnQueryBuilder,
7
+ GetArg,
8
+ getValueKey,
9
+ } from './queryMethods';
10
+ import { QueryData } from './sql';
11
+ import { ColumnShapeOutput, ColumnsShape, ColumnType } from './columnSchema';
12
+ import { EmptyObject, Spread } from './utils';
10
13
  import { AliasOrTable, RawExpression, StringKey } from './common';
11
- import { ThenResult } from './queryMethods/then';
12
14
  import { Db } from './db';
13
- import { ColumnInfo } from './queryMethods/columnInfo';
14
15
  import { RelationQueryBase, RelationsBase } from './relations';
15
- import { WhereQueryBuilder } from './queryMethods/where';
16
- import { OnQueryBuilder } from './queryMethods/join';
17
- import { GetArg, getValueKey } from './queryMethods/get';
18
16
 
19
17
  export type ColumnParser = (input: unknown) => unknown;
20
18
  export type ColumnsParsers = Record<string | getValueKey, ColumnParser>;
@@ -48,12 +46,8 @@ export type Query = QueryMethods & {
48
46
  onQueryBuilder: typeof OnQueryBuilder;
49
47
  table?: string;
50
48
  shape: ColumnsShape;
51
- schema: Omit<TableSchema<ColumnsShape>, 'primaryKeys' | 'primaryTypes'> & {
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- primaryKeys: any[];
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- primaryTypes: any[];
56
- };
49
+ singlePrimaryKey: string;
50
+ primaryKeys: string[];
57
51
  type: Record<string, unknown>;
58
52
  inputType: Record<string, unknown>;
59
53
  query: QueryData;
@@ -65,7 +59,7 @@ export type Query = QueryMethods & {
65
59
  then: ThenResult<unknown>;
66
60
  tableAlias: string | undefined;
67
61
  joinedTables: Record<string, Pick<Query, 'result' | 'tableAlias' | 'table'>>;
68
- windows: PropertyKey[];
62
+ windows: EmptyObject;
69
63
  defaultSelectColumns: string[];
70
64
  columnsParsers?: ColumnsParsers;
71
65
  relations: RelationsBase;
@@ -93,15 +87,26 @@ export type QueryReturnType =
93
87
  | 'rowCount'
94
88
  | 'void';
95
89
 
90
+ export const queryTypeWithLimitOne = {
91
+ one: true,
92
+ oneOrThrow: true,
93
+ } as Record<QueryReturnType, true | undefined>;
94
+
96
95
  export type JoinedTablesBase = Record<
97
96
  string,
98
97
  Pick<Query, 'result' | 'tableAlias' | 'table'>
99
98
  >;
100
99
 
101
- type QueryThen<
100
+ export type QueryReturnsAll<T extends QueryReturnType> = (
101
+ QueryReturnType extends T ? 'all' : T
102
+ ) extends 'all'
103
+ ? true
104
+ : false;
105
+
106
+ export type QueryThen<
102
107
  ReturnType extends QueryReturnType,
103
108
  Result extends ColumnsShape,
104
- > = ReturnType extends 'all'
109
+ > = QueryReturnsAll<ReturnType> extends true
105
110
  ? ThenResult<ColumnShapeOutput<Result>[]>
106
111
  : ReturnType extends 'one'
107
112
  ? ThenResult<ColumnShapeOutput<Result> | undefined>
@@ -130,15 +135,15 @@ type QueryThen<
130
135
  export type AddQuerySelect<
131
136
  T extends Pick<Query, 'hasSelect' | 'result' | 'then' | 'returnType'>,
132
137
  Result extends ColumnsShape,
133
- > = T['hasSelect'] extends false
134
- ? Omit<T, 'hasSelect' | 'result' | 'then'> & {
138
+ > = T['hasSelect'] extends true
139
+ ? Omit<T, 'result' | 'then'> & {
140
+ result: Spread<[T['result'], Result]>;
141
+ then: QueryThen<T['returnType'], Spread<[T['result'], Result]>>;
142
+ }
143
+ : Omit<T, 'result' | 'then'> & {
135
144
  hasSelect: true;
136
145
  result: Result;
137
146
  then: QueryThen<T['returnType'], Result>;
138
- }
139
- : Omit<T, 'result' | 'then'> & {
140
- result: Spread<[T['result'], Result]>;
141
- then: QueryThen<T['returnType'], Spread<[T['result'], Result]>>;
142
147
  };
143
148
 
144
149
  export type QuerySelectAll<T extends Query> = Omit<
@@ -288,7 +293,6 @@ export type AddQueryWith<
288
293
  With extends WithDataItem,
289
294
  > = SetQueryWith<T, Spread<[T['withData'], { [K in With['table']]: With }]>>;
290
295
 
291
- export type SetQueryWindows<T extends Query, W extends PropertyKey[]> = Omit<
292
- T,
293
- 'windows'
294
- > & { windows: W };
296
+ export type SetQueryWindows<T extends Query, W extends EmptyObject> = T & {
297
+ windows: W;
298
+ };
@@ -1,13 +1,6 @@
1
- import { Query } from './query';
2
1
  import { QueryData } from './sql';
3
2
  import { pushOrNewArrayToObject } from './utils';
4
3
 
5
- // TODO: remove
6
- export const removeFromQuery = <T extends Query>(q: T, key: string): T => {
7
- if (q.query) delete q.query[key as keyof typeof q.query];
8
- return q;
9
- };
10
-
11
4
  export const pushQueryArray = <T extends { query: QueryData }>(
12
5
  q: T,
13
6
  key: string,
@@ -7,7 +7,7 @@ import {
7
7
  StringExpression,
8
8
  } from '../common';
9
9
  import { AddQuerySelect, Query, SetQueryReturnsValue } from '../query';
10
- import { pushQueryValue, removeFromQuery } from '../queryDataUtils';
10
+ import { pushQueryValue } from '../queryDataUtils';
11
11
  import {
12
12
  ArrayColumn,
13
13
  BooleanColumn,
@@ -42,7 +42,7 @@ export type AggregateOptions<
42
42
  filter?: WhereArg<T>;
43
43
  filterOr?: WhereArg<T>[];
44
44
  withinGroup?: boolean;
45
- over?: T['windows'][number] | WindowArgDeclaration<T>;
45
+ over?: keyof T['windows'] | WindowArgDeclaration<T>;
46
46
  };
47
47
 
48
48
  // 1 in the name means only methods which takes 1 argument are listed here
@@ -130,7 +130,6 @@ const get = <T extends Query, Column extends ColumnType>(
130
130
  q: Query,
131
131
  ): SetQueryReturnsValue<T, Column> => {
132
132
  q.query.returnType = 'valueOrThrow';
133
- removeFromQuery(q, 'take');
134
133
 
135
134
  const select = q.query.select as SelectItem[];
136
135
  if (select.length > 1) {
@@ -1,5 +1,4 @@
1
1
  import { Query } from '../query';
2
- import { removeFromQuery } from '../queryDataUtils';
3
2
  import { isRaw } from '../common';
4
3
 
5
4
  export type ClearStatement =
@@ -24,8 +23,8 @@ export class Clear {
24
23
  _clear<T extends Query>(this: T, ...clears: ClearStatement[]): T {
25
24
  clears.forEach((clear) => {
26
25
  if (clear === 'where') {
27
- removeFromQuery(this, 'and');
28
- removeFromQuery(this, 'or');
26
+ delete this.query.and;
27
+ delete this.query.or;
29
28
  } else if (clear === 'counters') {
30
29
  if ('type' in this.query && this.query.type === 'update') {
31
30
  this.query.updateData = this.query.updateData.filter((item) => {
@@ -50,7 +49,7 @@ export class Clear {
50
49
  });
51
50
  }
52
51
  } else {
53
- removeFromQuery(this, clear);
52
+ delete (this.query as Record<string, unknown>)[clear];
54
53
  }
55
54
  });
56
55
  return this;
@@ -4,9 +4,9 @@ type DeleteArgs<T extends Query> = T['hasWhere'] extends true
4
4
  ? [forceAll?: boolean]
5
5
  : [true];
6
6
 
7
- type DeleteResult<T extends Query> = T['hasSelect'] extends false
8
- ? SetQueryReturnsRowCount<T>
9
- : T;
7
+ type DeleteResult<T extends Query> = T['hasSelect'] extends true
8
+ ? T
9
+ : SetQueryReturnsRowCount<T>;
10
10
 
11
11
  const del = <T extends Query>(
12
12
  self: T,
@@ -50,16 +50,15 @@ describe('from', () => {
50
50
  it('should not insert sub query and alias if provided query is simple', () => {
51
51
  const q = User.all();
52
52
  expectSql(
53
- q.select('name').from(User).toSql(),
53
+ User.select('name').from(User).toSql(),
54
54
  'SELECT "user"."name" FROM "user"',
55
55
  );
56
56
  expectQueryNotMutated(q);
57
57
  });
58
58
 
59
59
  it('should add ONLY keyword when `only` parameter is provided', () => {
60
- const q = User.all();
61
60
  expectSql(
62
- q.select('id').from(User, { only: true }).toSql(),
61
+ User.select('id').from(User, { only: true }).toSql(),
63
62
  'SELECT "user"."id" FROM ONLY "user"',
64
63
  );
65
64
  });
@@ -39,7 +39,6 @@ const _get = <
39
39
  arg: Arg,
40
40
  ): R extends 'value' ? GetOptionalResult<T, Arg> : GetResult<T, Arg> => {
41
41
  q.query.returnType = returnType;
42
- q.query.take = true;
43
42
 
44
43
  if (typeof arg === 'object' && isRaw(arg)) {
45
44
  addParserForRawExpression(q, getValueKey, arg);
@@ -11,6 +11,7 @@ export * from './insert';
11
11
  export * from './join';
12
12
  export * from './json';
13
13
  export * from './log';
14
+ export * from './merge';
14
15
  export * from './queryMethods';
15
16
  export * from './select';
16
17
  export * from './then';
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  defaultsKey,
3
3
  Query,
4
+ QueryReturnsAll,
4
5
  QueryReturnType,
5
6
  SetQueryReturnsAll,
6
7
  SetQueryReturnsOne,
@@ -136,19 +137,19 @@ type InsertHasManyData<
136
137
 
137
138
  type InsertRawData = { columns: string[]; values: RawExpression };
138
139
 
139
- type InsertOneResult<T extends Query> = T['hasSelect'] extends false
140
- ? SetQueryReturnsRowCount<T>
141
- : T['returnType'] extends 'all'
142
- ? SetQueryReturnsOne<T>
143
- : T['returnType'] extends 'one'
144
- ? SetQueryReturnsOne<T>
145
- : T;
140
+ type InsertOneResult<T extends Query> = T['hasSelect'] extends true
141
+ ? QueryReturnsAll<T['returnType']> extends true
142
+ ? SetQueryReturnsOne<T>
143
+ : T['returnType'] extends 'one'
144
+ ? SetQueryReturnsOne<T>
145
+ : T
146
+ : SetQueryReturnsRowCount<T>;
146
147
 
147
- type InsertManyResult<T extends Query> = T['hasSelect'] extends false
148
- ? SetQueryReturnsRowCount<T>
149
- : T['returnType'] extends 'one' | 'oneOrThrow'
150
- ? SetQueryReturnsAll<T>
151
- : T;
148
+ type InsertManyResult<T extends Query> = T['hasSelect'] extends true
149
+ ? T['returnType'] extends 'one' | 'oneOrThrow'
150
+ ? SetQueryReturnsAll<T>
151
+ : T
152
+ : SetQueryReturnsRowCount<T>;
152
153
 
153
154
  type OnConflictArg<T extends Query> =
154
155
  | keyof T['shape']
@@ -223,7 +224,7 @@ const createInsertCtx = (q: Query): InsertCtx => ({
223
224
  });
224
225
 
225
226
  const getInsertSingleReturnType = (q: Query) => {
226
- const { select, returnType } = q.query;
227
+ const { select, returnType = 'all' } = q.query;
227
228
  if (select) {
228
229
  return returnType === 'all' ? 'one' : returnType;
229
230
  } else {
@@ -1,4 +1,9 @@
1
- import { AddQuerySelect, Query, SetQueryReturnsValueOptional } from '../query';
1
+ import {
2
+ AddQuerySelect,
3
+ Query,
4
+ queryTypeWithLimitOne,
5
+ SetQueryReturnsValueOptional,
6
+ } from '../query';
2
7
  import { pushQueryValue } from '../queryDataUtils';
3
8
  import { ColumnType, StringColumn } from '../columnSchema';
4
9
  import { JsonItem } from '../sql';
@@ -51,12 +56,11 @@ export class Json {
51
56
  const q = this._wrap(this.__model.clone()) as T;
52
57
  q._getOptional(
53
58
  raw<StringColumn>(
54
- this.query.take
59
+ queryTypeWithLimitOne[this.query.returnType]
55
60
  ? `row_to_json("t".*)`
56
61
  : `COALESCE(json_agg(row_to_json("t".*)), '[]')`,
57
62
  ),
58
63
  );
59
- delete q.query.take;
60
64
  return q as unknown as SetQueryReturnsValueOptional<T, StringColumn>;
61
65
  }
62
66