pqb 0.4.6 → 0.4.8

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.
Files changed (53) hide show
  1. package/dist/index.d.ts +25 -11
  2. package/dist/index.esm.js +78 -40
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/index.js +79 -39
  5. package/dist/index.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/columnSchema/columnType.test.ts +220 -0
  8. package/src/columnSchema/columnType.ts +7 -0
  9. package/src/columnSchema/columnTypes.test.ts +55 -53
  10. package/src/columnSchema/dateTime.ts +11 -0
  11. package/src/columnSchema/timestamps.test.ts +3 -4
  12. package/src/columnSchema/timestamps.ts +1 -1
  13. package/src/columnsOperators.test.ts +18 -19
  14. package/src/columnsOperators.ts +1 -1
  15. package/src/common.ts +17 -30
  16. package/src/db.ts +23 -9
  17. package/src/errors.test.ts +2 -4
  18. package/src/index.ts +1 -1
  19. package/src/query.ts +7 -1
  20. package/src/queryMethods/aggregate.test.ts +9 -7
  21. package/src/queryMethods/create.test.ts +48 -5
  22. package/src/queryMethods/create.ts +28 -6
  23. package/src/queryMethods/for.test.ts +1 -2
  24. package/src/queryMethods/for.ts +1 -1
  25. package/src/queryMethods/from.test.ts +2 -3
  26. package/src/queryMethods/get.test.ts +5 -5
  27. package/src/queryMethods/having.test.ts +6 -5
  28. package/src/queryMethods/index.ts +1 -0
  29. package/src/queryMethods/json.ts +2 -2
  30. package/src/queryMethods/merge.test.ts +10 -4
  31. package/src/queryMethods/queryMethods.test.ts +10 -12
  32. package/src/queryMethods/queryMethods.ts +7 -4
  33. package/src/queryMethods/raw.test.ts +19 -0
  34. package/src/queryMethods/raw.ts +27 -0
  35. package/src/queryMethods/select.test.ts +4 -4
  36. package/src/queryMethods/then.test.ts +2 -4
  37. package/src/queryMethods/union.test.ts +11 -6
  38. package/src/queryMethods/update.test.ts +21 -3
  39. package/src/queryMethods/update.ts +11 -1
  40. package/src/queryMethods/where.test.ts +65 -56
  41. package/src/queryMethods/where.ts +1 -1
  42. package/src/queryMethods/with.test.ts +5 -6
  43. package/src/sql/common.ts +0 -1
  44. package/src/sql/fromAndAs.ts +1 -1
  45. package/src/sql/insert.ts +1 -1
  46. package/src/sql/join.ts +1 -1
  47. package/src/sql/orderBy.ts +1 -1
  48. package/src/sql/select.ts +1 -2
  49. package/src/sql/toSql.ts +1 -1
  50. package/src/sql/update.ts +1 -1
  51. package/src/sql/where.ts +1 -1
  52. package/src/sql/window.ts +3 -4
  53. package/src/sql/with.ts +1 -1
@@ -8,7 +8,6 @@ import {
8
8
  SetQueryReturnsOne,
9
9
  } from '../query';
10
10
  import { pushQueryArray } from '../queryDataUtils';
11
- import { RawExpression } from '../common';
12
11
  import {
13
12
  BelongsToNestedInsert,
14
13
  BelongsToRelation,
@@ -26,6 +25,9 @@ import { InsertQueryData, OnConflictItem, OnConflictMergeUpdate } from '../sql';
26
25
  import { WhereArg } from './where';
27
26
  import { parseResult, queryMethodByReturnType } from './then';
28
27
  import { NotFoundError } from '../errors';
28
+ import { RawExpression } from '../common';
29
+ import { ColumnsShape } from '../columnSchema';
30
+ import { anyShape } from '../db';
29
31
 
30
32
  export type CreateData<
31
33
  T extends Query,
@@ -172,6 +174,8 @@ type CreateCtx = {
172
174
  relations: Record<string, Relation>;
173
175
  };
174
176
 
177
+ type Encoder = (input: unknown) => unknown;
178
+
175
179
  const handleSelect = (q: Query) => {
176
180
  const select = q.query.select?.[0];
177
181
  const isCount =
@@ -191,7 +195,9 @@ const processCreateItem = (
191
195
  rowIndex: number,
192
196
  ctx: CreateCtx,
193
197
  columns: string[],
198
+ encoders: Record<string, Encoder>,
194
199
  columnsMap: Record<string, number>,
200
+ shape: ColumnsShape,
195
201
  ) => {
196
202
  Object.keys(item).forEach((key) => {
197
203
  if (ctx.relations[key]) {
@@ -222,8 +228,12 @@ const processCreateItem = (
222
228
  item[key] as NestedInsertItem,
223
229
  ]);
224
230
  }
225
- } else if (columnsMap[key] === undefined) {
231
+ } else if (
232
+ columnsMap[key] === undefined &&
233
+ (shape[key] || shape === anyShape)
234
+ ) {
226
235
  columnsMap[key] = columns.length;
236
+ encoders[key] = shape[key]?.encodeFn as Encoder;
227
237
  columns.push(key);
228
238
  }
229
239
  });
@@ -256,8 +266,19 @@ const getManyReturnType = (q: Query) => {
256
266
  }
257
267
  };
258
268
 
269
+ const mapColumnValues = (
270
+ columns: string[],
271
+ encoders: Record<string, Encoder>,
272
+ data: Record<string, unknown>,
273
+ ) => {
274
+ return columns.map((key) =>
275
+ encoders[key] ? encoders[key](data[key]) : data[key],
276
+ );
277
+ };
278
+
259
279
  const handleOneData = (q: Query, data: CreateData<Query>, ctx: CreateCtx) => {
260
280
  const columns: string[] = [];
281
+ const encoders: Record<string, Encoder> = {};
261
282
  const columnsMap: Record<string, number> = {};
262
283
  const defaults = q.query.defaults;
263
284
 
@@ -265,9 +286,9 @@ const handleOneData = (q: Query, data: CreateData<Query>, ctx: CreateCtx) => {
265
286
  data = { ...defaults, ...data };
266
287
  }
267
288
 
268
- processCreateItem(data, 0, ctx, columns, columnsMap);
289
+ processCreateItem(data, 0, ctx, columns, encoders, columnsMap, q.shape);
269
290
 
270
- const values = [columns.map((key) => (data as Record<string, unknown>)[key])];
291
+ const values = [mapColumnValues(columns, encoders, data)];
271
292
 
272
293
  return { columns, values };
273
294
  };
@@ -278,6 +299,7 @@ const handleManyData = (
278
299
  ctx: CreateCtx,
279
300
  ) => {
280
301
  const columns: string[] = [];
302
+ const encoders: Record<string, Encoder> = {};
281
303
  const columnsMap: Record<string, number> = {};
282
304
  const defaults = q.query.defaults;
283
305
 
@@ -286,13 +308,13 @@ const handleManyData = (
286
308
  }
287
309
 
288
310
  data.forEach((item, i) => {
289
- processCreateItem(item, i, ctx, columns, columnsMap);
311
+ processCreateItem(item, i, ctx, columns, encoders, columnsMap, q.shape);
290
312
  });
291
313
 
292
314
  const values = Array(data.length);
293
315
 
294
316
  data.forEach((item, i) => {
295
- (values as unknown[][])[i] = columns.map((key) => item[key]);
317
+ (values as unknown[][])[i] = mapColumnValues(columns, encoders, item);
296
318
  });
297
319
 
298
320
  return { columns, values };
@@ -1,5 +1,4 @@
1
1
  import { expectQueryNotMutated, expectSql, User } from '../test-utils';
2
- import { raw } from '../common';
3
2
 
4
3
  describe('for', () => {
5
4
  describe.each`
@@ -30,7 +29,7 @@ describe('for', () => {
30
29
  it('should accept raw sql', () => {
31
30
  const q = User.all();
32
31
  expectSql(
33
- q[method as 'forUpdate'](raw('raw sql')).toSql(),
32
+ q[method as 'forUpdate'](User.raw('raw sql')).toSql(),
34
33
  `SELECT * FROM "user" FOR ${sql} OF raw sql`,
35
34
  );
36
35
  expectQueryNotMutated(q);
@@ -1,6 +1,6 @@
1
1
  import { Query } from '../query';
2
- import { RawExpression } from '../common';
3
2
  import { SelectQueryData } from '../sql';
3
+ import { RawExpression } from '../common';
4
4
 
5
5
  type ForQueryBuilder<Q extends Query> = Q & {
6
6
  noWait<T extends ForQueryBuilder<Q>>(this: T): T;
@@ -1,5 +1,4 @@
1
- import { expectQueryNotMutated, expectSql, User } from '../test-utils';
2
- import { raw } from '../common';
1
+ import { db, expectQueryNotMutated, expectSql, User } from '../test-utils';
3
2
 
4
3
  describe('from', () => {
5
4
  it('should accept string parameter', () => {
@@ -20,7 +19,7 @@ describe('from', () => {
20
19
  it('should accept raw parameter', () => {
21
20
  const q = User.all();
22
21
  expectSql(
23
- q.as('t').from(raw('profile')).toSql(),
22
+ q.as('t').from(db.raw('profile')).toSql(),
24
23
  `SELECT * FROM profile AS "t"`,
25
24
  );
26
25
  expectQueryNotMutated(q);
@@ -1,7 +1,5 @@
1
- import { assertType, User, userData, useTestDatabase } from '../test-utils';
2
- import { NumberColumn } from '../columnSchema';
1
+ import { assertType, db, User, userData, useTestDatabase } from '../test-utils';
3
2
  import { NotFoundError } from '../errors';
4
- import { raw } from '../common';
5
3
 
6
4
  describe('get', () => {
7
5
  useTestDatabase();
@@ -18,7 +16,9 @@ describe('get', () => {
18
16
  });
19
17
 
20
18
  it('should select raw and return a single value', async () => {
21
- const received = await User.get(raw<NumberColumn>('count(*)::int'));
19
+ const received = await User.get(
20
+ db.raw((t) => t.integer(), 'count(*)::int'),
21
+ );
22
22
 
23
23
  assertType<typeof received, number>();
24
24
 
@@ -43,7 +43,7 @@ describe('get', () => {
43
43
 
44
44
  it('should select raw and return a single value when exists', async () => {
45
45
  const received = await User.getOptional(
46
- raw<NumberColumn>('count(*)::int'),
46
+ db.raw((t) => t.integer(), 'count(*)::int'),
47
47
  );
48
48
 
49
49
  assertType<typeof received, number | undefined>();
@@ -1,5 +1,4 @@
1
- import { expectQueryNotMutated, expectSql, User } from '../test-utils';
2
- import { raw } from '../common';
1
+ import { db, expectQueryNotMutated, expectSql, User } from '../test-utils';
3
2
 
4
3
  describe('having', () => {
5
4
  it('should support { count: value } object', () => {
@@ -196,12 +195,12 @@ describe('having', () => {
196
195
  `;
197
196
 
198
197
  expectSql(
199
- q.having(raw('count(*) = 1'), raw('sum(id) = 2')).toSql(),
198
+ q.having(db.raw('count(*) = 1'), db.raw('sum(id) = 2')).toSql(),
200
199
  expectedSql,
201
200
  );
202
201
  expectQueryNotMutated(q);
203
202
 
204
- q._having(raw('count(*) = 1'), raw('sum(id) = 2'));
203
+ q._having(db.raw('count(*) = 1'), db.raw('sum(id) = 2'));
205
204
  expectSql(q.toSql({ clearCache: true }), expectedSql);
206
205
  });
207
206
 
@@ -235,7 +234,9 @@ describe('having', () => {
235
234
  it('should accept raw sql', () => {
236
235
  const q = User.all();
237
236
  expectSql(
238
- q.havingOr(raw('count(*) = 1 + 2'), raw('count(*) = 2 + 3')).toSql(),
237
+ q
238
+ .havingOr(db.raw('count(*) = 1 + 2'), db.raw('count(*) = 2 + 3'))
239
+ .toSql(),
239
240
  `
240
241
  SELECT * FROM "user"
241
242
  HAVING count(*) = 1 + 2 OR count(*) = 2 + 3
@@ -21,3 +21,4 @@ export * from './update';
21
21
  export * from './upsert';
22
22
  export * from './where';
23
23
  export * from './with';
24
+ export * from './raw';
@@ -7,7 +7,7 @@ import {
7
7
  import { pushQueryValue } from '../queryDataUtils';
8
8
  import { ColumnType, StringColumn } from '../columnSchema';
9
9
  import { JsonItem } from '../sql';
10
- import { raw, StringKey } from '../common';
10
+ import { StringKey } from '../common';
11
11
 
12
12
  type JsonColumnName<T extends Pick<Query, 'selectable'>> = StringKey<
13
13
  {
@@ -55,7 +55,7 @@ export class Json {
55
55
  ): SetQueryReturnsValueOptional<T, StringColumn> {
56
56
  const q = this._wrap(this.__model.clone()) as T;
57
57
  q._getOptional(
58
- raw<StringColumn>(
58
+ this.raw<Query, StringColumn>(
59
59
  queryTypeWithLimitOne[this.query.returnType]
60
60
  ? `row_to_json("t".*)`
61
61
  : `COALESCE(json_agg(row_to_json("t".*)), '[]')`,
@@ -1,4 +1,11 @@
1
- import { assertType, expectSql, Message, Profile, User } from '../test-utils';
1
+ import {
2
+ assertType,
3
+ db,
4
+ expectSql,
5
+ Message,
6
+ Profile,
7
+ User,
8
+ } from '../test-utils';
2
9
  import { QueryReturnType } from '../query';
3
10
  import { IntegerColumn, TextColumn } from '../columnSchema';
4
11
  import { getValueKey } from './get';
@@ -11,7 +18,6 @@ import {
11
18
  TruncateQueryData,
12
19
  UpdateQueryData,
13
20
  } from '../sql';
14
- import { raw } from '../common';
15
21
 
16
22
  describe('merge queries', () => {
17
23
  describe('select', () => {
@@ -350,8 +356,8 @@ describe('merge queries', () => {
350
356
  s2.having = [{ b: { b: 2 } }];
351
357
  s1.havingOr = [[{ a: { a: 1 } }]];
352
358
  s2.havingOr = [[{ b: { b: 2 } }]];
353
- s1.union = [{ arg: raw('a'), kind: 'UNION' }];
354
- s2.union = [{ arg: raw('b'), kind: 'EXCEPT' }];
359
+ s1.union = [{ arg: db.raw('a'), kind: 'UNION' }];
360
+ s2.union = [{ arg: db.raw('b'), kind: 'EXCEPT' }];
355
361
  s1.order = [{ id: 'ASC' }];
356
362
  s2.order = [{ name: 'DESC' }];
357
363
  s1.limit = 1;
@@ -1,4 +1,3 @@
1
- import { raw } from '../common';
2
1
  import {
3
2
  expectQueryNotMutated,
4
3
  adapter,
@@ -12,7 +11,6 @@ import {
12
11
  assertType,
13
12
  UserRecord,
14
13
  } from '../test-utils';
15
- import { NumberColumn } from '../columnSchema';
16
14
  import { NotFoundError } from '../errors';
17
15
 
18
16
  describe('queryMethods', () => {
@@ -126,7 +124,7 @@ describe('queryMethods', () => {
126
124
  });
127
125
 
128
126
  it('should support raw expression', async () => {
129
- const result = await User.pluck(raw<NumberColumn>('123'));
127
+ const result = await User.pluck(db.raw((t) => t.integer(), '123'));
130
128
  expect(result).toEqual([123, 123, 123]);
131
129
 
132
130
  assertType<typeof result, number[]>();
@@ -206,7 +204,7 @@ describe('queryMethods', () => {
206
204
  it('should add distinct on raw sql', () => {
207
205
  const q = User.all();
208
206
  expectSql(
209
- q.distinct(raw('"user".id')).toSql(),
207
+ q.distinct(db.raw('"user".id')).toSql(),
210
208
  `
211
209
  SELECT DISTINCT ON ("user".id) * FROM "user"
212
210
  `,
@@ -236,7 +234,7 @@ describe('queryMethods', () => {
236
234
 
237
235
  it('should accept raw sql', () => {
238
236
  const q = User.all();
239
- const query = q.find(raw('$1 + $2', 1, 2));
237
+ const query = q.find(db.raw('$1 + $2', 1, 2));
240
238
 
241
239
  assertType<Awaited<typeof query>, UserRecord>();
242
240
 
@@ -274,7 +272,7 @@ describe('queryMethods', () => {
274
272
 
275
273
  it('should accept raw sql', () => {
276
274
  const q = User.all();
277
- const query = q.findOptional(raw('$1 + $2', 1, 2));
275
+ const query = q.findOptional(db.raw('$1 + $2', 1, 2));
278
276
 
279
277
  assertType<Awaited<typeof query>, UserRecord | undefined>();
280
278
 
@@ -305,7 +303,7 @@ describe('queryMethods', () => {
305
303
  it('should accept raw', () => {
306
304
  const q = User.all();
307
305
  expectSql(
308
- q.findBy({ name: raw(`'string'`) }).toSql(),
306
+ q.findBy({ name: db.raw(`'string'`) }).toSql(),
309
307
  `SELECT * FROM "user" WHERE "user"."name" = 'string' LIMIT $1`,
310
308
  [1],
311
309
  );
@@ -330,7 +328,7 @@ describe('queryMethods', () => {
330
328
 
331
329
  it('should accept raw', () => {
332
330
  const q = User.all();
333
- const query = q.findByOptional({ name: raw(`'string'`) });
331
+ const query = q.findByOptional({ name: db.raw(`'string'`) });
334
332
 
335
333
  assertType<Awaited<typeof query>, UserRecord | undefined>();
336
334
 
@@ -431,10 +429,10 @@ describe('queryMethods', () => {
431
429
  SELECT * FROM "user"
432
430
  GROUP BY id, name
433
431
  `;
434
- expectSql(q.group(raw('id'), raw('name')).toSql(), expectedSql);
432
+ expectSql(q.group(db.raw('id'), db.raw('name')).toSql(), expectedSql);
435
433
  expectQueryNotMutated(q);
436
434
 
437
- q._group(raw('id'), raw('name'));
435
+ q._group(db.raw('id'), db.raw('name'));
438
436
  expectSql(q.toSql({ clearCache: true }), expectedSql);
439
437
  });
440
438
  });
@@ -471,7 +469,7 @@ describe('queryMethods', () => {
471
469
  const windowSql = 'PARTITION BY id ORDER BY name DESC';
472
470
  expectSql(
473
471
  q
474
- .window({ w: raw(windowSql) })
472
+ .window({ w: db.raw(windowSql) })
475
473
  .selectAvg('id', {
476
474
  over: 'w',
477
475
  })
@@ -527,7 +525,7 @@ describe('queryMethods', () => {
527
525
  it('adds order with raw sql', () => {
528
526
  const q = User.all();
529
527
  expectSql(
530
- q.order(raw('id ASC NULLS FIRST')).toSql(),
528
+ q.order(db.raw('id ASC NULLS FIRST')).toSql(),
531
529
  `
532
530
  SELECT * FROM "user"
533
531
  ORDER BY id ASC NULLS FIRST
@@ -1,4 +1,4 @@
1
- import { Expression, raw, RawExpression } from '../common';
1
+ import { Expression, RawExpression } from '../common';
2
2
  import {
3
3
  Query,
4
4
  QueryBase,
@@ -48,6 +48,7 @@ import { QueryCallbacks } from './callbacks';
48
48
  import { QueryUpsert } from './upsert';
49
49
  import { QueryGet } from './get';
50
50
  import { MergeQueryMethods } from './merge';
51
+ import { RawMethods } from './raw';
51
52
 
52
53
  export type WindowArg<T extends Query> = Record<
53
54
  string,
@@ -96,7 +97,8 @@ export interface QueryMethods
96
97
  QueryCallbacks,
97
98
  QueryUpsert,
98
99
  QueryGet,
99
- MergeQueryMethods {}
100
+ MergeQueryMethods,
101
+ RawMethods {}
100
102
 
101
103
  export class QueryMethods {
102
104
  windows!: EmptyObject;
@@ -309,7 +311,7 @@ export class QueryMethods {
309
311
  return query
310
312
  .as(as ?? 't')
311
313
  ._from(
312
- raw(`(${sql.text})`, ...sql.values),
314
+ this.raw(`(${sql.text})`, ...sql.values),
313
315
  ) as unknown as SetQueryTableAlias<Q, As>;
314
316
  }
315
317
 
@@ -344,7 +346,7 @@ export class QueryMethods {
344
346
  }
345
347
 
346
348
  _exists<T extends Query>(this: T): SetQueryReturnsValue<T, BooleanColumn> {
347
- const q = this._getOptional(raw<BooleanColumn>('true'));
349
+ const q = this._getOptional(this.raw<Query, BooleanColumn>('true'));
348
350
  q.query.notFoundDefault = false;
349
351
  q.query.coalesceValue = false;
350
352
  return q as unknown as SetQueryReturnsValue<T, BooleanColumn>;
@@ -397,4 +399,5 @@ applyMixins(QueryMethods, [
397
399
  QueryUpsert,
398
400
  QueryGet,
399
401
  MergeQueryMethods,
402
+ RawMethods,
400
403
  ]);
@@ -0,0 +1,19 @@
1
+ import { ColumnType } from '../columnSchema';
2
+ import { createDb } from '../db';
3
+ import { adapter } from '../test-utils';
4
+
5
+ describe('raw', () => {
6
+ it('should use column types in callback from a db instance', () => {
7
+ const type = {} as unknown as ColumnType;
8
+ const db = createDb({
9
+ adapter,
10
+ columnTypes: {
11
+ type: () => type,
12
+ },
13
+ });
14
+
15
+ const value = db.raw((t) => t.type(), 'sql');
16
+
17
+ expect(value.__column).toBe(type);
18
+ });
19
+ });
@@ -0,0 +1,27 @@
1
+ import { ColumnType, ColumnTypesBase } from '../columnSchema';
2
+ import { Query } from '../query';
3
+ import { RawExpression } from '../common';
4
+
5
+ type RawArgs<CT extends ColumnTypesBase, C extends ColumnType> =
6
+ | [column: (types: CT) => C, sql: string, ...values: unknown[]]
7
+ | [sql: string, ...values: unknown[]];
8
+
9
+ export class RawMethods {
10
+ raw<T extends Query, C extends ColumnType>(
11
+ this: T,
12
+ ...args: RawArgs<T['columnTypes'], C>
13
+ ): RawExpression<C> {
14
+ if (typeof args[0] === 'string') {
15
+ return {
16
+ __raw: args[0],
17
+ __values: args.slice(1),
18
+ } as RawExpression<C>;
19
+ } else {
20
+ return {
21
+ __column: args[0](this.columnTypes),
22
+ __raw: args[1],
23
+ __values: args.slice(2),
24
+ } as RawExpression<C>;
25
+ }
26
+ }
27
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  assertType,
3
+ db,
3
4
  expectQueryNotMutated,
4
5
  expectSql,
5
6
  Profile,
@@ -9,7 +10,6 @@ import {
9
10
  UserRecord,
10
11
  useTestDatabase,
11
12
  } from '../test-utils';
12
- import { raw } from '../common';
13
13
  import { DateColumn } from '../columnSchema';
14
14
 
15
15
  const insertUserAndProfile = async () => {
@@ -236,7 +236,7 @@ describe('selectMethods', () => {
236
236
 
237
237
  it('can select raw', () => {
238
238
  const q = User.all();
239
- const query = q.select({ one: raw('1') });
239
+ const query = q.select({ one: db.raw('1') });
240
240
 
241
241
  assertType<Awaited<typeof query>, { one: unknown }[]>();
242
242
 
@@ -345,8 +345,8 @@ describe('selectMethods', () => {
345
345
 
346
346
  it('should parse raw column', async () => {
347
347
  const q = User.select({
348
- date: raw(
349
- new DateColumn().parse((input) => new Date(input)),
348
+ date: db.raw(
349
+ () => new DateColumn().parse((input) => new Date(input)),
350
350
  '"createdAt"',
351
351
  ),
352
352
  });
@@ -1,6 +1,4 @@
1
- import { assertType, User, useTestDatabase } from '../test-utils';
2
- import { raw } from '../common';
3
- import { columnTypes } from '../columnSchema';
1
+ import { assertType, db, User, useTestDatabase } from '../test-utils';
4
2
 
5
3
  describe('then', () => {
6
4
  useTestDatabase();
@@ -8,7 +6,7 @@ describe('then', () => {
8
6
  describe('catch', () => {
9
7
  it('should catch error', (done) => {
10
8
  const query = User.select({
11
- column: raw(columnTypes.boolean(), 'koko'),
9
+ column: db.raw((t) => t.boolean(), 'koko'),
12
10
  }).catch((err) => {
13
11
  expect(err.message).toBe(`column "koko" does not exist`);
14
12
  done();
@@ -1,5 +1,10 @@
1
- import { Chat, expectQueryNotMutated, expectSql, User } from '../test-utils';
2
- import { raw } from '../common';
1
+ import {
2
+ Chat,
3
+ db,
4
+ expectQueryNotMutated,
5
+ expectSql,
6
+ User,
7
+ } from '../test-utils';
3
8
 
4
9
  ['union', 'intersect', 'except'].forEach((what) => {
5
10
  const upper = what.toUpperCase();
@@ -7,10 +12,10 @@ import { raw } from '../common';
7
12
  it(`adds ${what}`, () => {
8
13
  const q = User.all();
9
14
  let query = q.select('id');
10
- query = query[what as 'union']([Chat.select('id'), raw('SELECT 1')]);
15
+ query = query[what as 'union']([Chat.select('id'), db.raw('SELECT 1')]);
11
16
  query = query[
12
17
  (what + 'All') as 'unionAll' | 'intersectAll' | 'exceptAll'
13
- ]([raw('SELECT 2')], true);
18
+ ]([db.raw('SELECT 2')], true);
14
19
 
15
20
  const wrapped = query.wrap(User.select('id'));
16
21
 
@@ -34,7 +39,7 @@ import { raw } from '../common';
34
39
 
35
40
  it('has modifier', () => {
36
41
  const q = User.select('id');
37
- q[`_${what}` as '_union']([raw('SELECT 1')]);
42
+ q[`_${what}` as '_union']([db.raw('SELECT 1')]);
38
43
  expectSql(
39
44
  q.toSql(),
40
45
  `
@@ -43,7 +48,7 @@ import { raw } from '../common';
43
48
  SELECT 1
44
49
  `,
45
50
  );
46
- q[`_${what}All` as '_unionAll']([raw('SELECT 2')], true);
51
+ q[`_${what}All` as '_unionAll']([db.raw('SELECT 2')], true);
47
52
  expectSql(
48
53
  q.toSql({ clearCache: true }),
49
54
  `
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  assertType,
3
+ db,
3
4
  expectQueryNotMutated,
4
5
  expectSql,
5
6
  User,
6
7
  userData,
8
+ UserRecord,
7
9
  useTestDatabase,
8
10
  } from '../test-utils';
9
- import { raw } from '../common';
10
11
 
11
12
  describe('update', () => {
12
13
  useTestDatabase();
@@ -37,7 +38,7 @@ describe('update', () => {
37
38
  const count = 2;
38
39
  const users = await User.select('id').createMany([userData, userData]);
39
40
 
40
- const query = User.or(...users).updateRaw(raw(`name = 'name'`));
41
+ const query = User.or(...users).updateRaw(db.raw(`name = 'name'`));
41
42
  expectSql(
42
43
  query.toSql(),
43
44
  `
@@ -254,7 +255,7 @@ describe('update', () => {
254
255
 
255
256
  it('should support raw sql as a value', () => {
256
257
  const query = User.where({ id: 1 }).update({
257
- name: raw(`'raw sql'`),
258
+ name: db.raw(`'raw sql'`),
258
259
  });
259
260
  expectSql(
260
261
  query.toSql(),
@@ -321,6 +322,23 @@ describe('update', () => {
321
322
  });
322
323
  });
323
324
 
325
+ it('should strip unknown keys', () => {
326
+ const query = User.find(1).update({
327
+ name: 'name',
328
+ unknown: 'should be stripped',
329
+ } as unknown as UserRecord);
330
+
331
+ expectSql(
332
+ query.toSql(),
333
+ `
334
+ UPDATE "user"
335
+ SET "name" = $1, "updatedAt" = now()
336
+ WHERE "user"."id" = $2
337
+ `,
338
+ ['name', 1],
339
+ );
340
+ });
341
+
324
342
  describe('increment', () => {
325
343
  it('should not mutate query', () => {
326
344
  const q = User.all();
@@ -16,6 +16,8 @@ import { EmptyObject, MaybeArray } from '../utils';
16
16
  import { CreateData } from './create';
17
17
  import { parseResult, queryMethodByReturnType } from './then';
18
18
  import { UpdateQueryData } from '../sql';
19
+ import { ColumnsShape } from '../columnSchema';
20
+ import { anyShape } from '../db';
19
21
 
20
22
  export type UpdateData<T extends Query> = {
21
23
  [K in keyof T['type']]?: T['type'][K] | RawExpression;
@@ -174,7 +176,10 @@ export class Update {
174
176
  const set: Record<string, unknown> = { ...data };
175
177
  pushQueryValue(this, 'updateData', set);
176
178
 
177
- const relations = this.relations as Record<string, Relation>;
179
+ const { relations, shape } = this as {
180
+ relations: Record<string, Relation>;
181
+ shape: ColumnsShape;
182
+ };
178
183
 
179
184
  const prependRelations: Record<string, Record<string, unknown>> = {};
180
185
  const appendRelations: Record<string, Record<string, unknown>> = {};
@@ -195,6 +200,11 @@ export class Update {
195
200
  }
196
201
  appendRelations[key] = data[key] as Record<string, unknown>;
197
202
  }
203
+ } else if (!shape[key] && shape !== anyShape) {
204
+ delete set[key];
205
+ } else {
206
+ const encode = shape[key].encodeFn;
207
+ if (encode) set[key] = encode(set[key]);
198
208
  }
199
209
  }
200
210