@expo/entity-database-adapter-knex 0.40.0 → 0.42.0

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 (63) hide show
  1. package/build/EntityFields.d.ts +3 -3
  2. package/build/EntityFields.js.map +1 -1
  3. package/build/PostgresEntityDatabaseAdapter.d.ts +2 -2
  4. package/build/PostgresEntityDatabaseAdapter.js +31 -5
  5. package/build/PostgresEntityDatabaseAdapter.js.map +1 -1
  6. package/build/PostgresEntityDatabaseAdapterProvider.d.ts +1 -1
  7. package/build/PostgresEntityDatabaseAdapterProvider.js.map +1 -1
  8. package/build/tsconfig.build.tsbuildinfo +1 -0
  9. package/package.json +9 -7
  10. package/src/EntityFields.ts +11 -3
  11. package/src/PostgresEntityDatabaseAdapter.ts +38 -7
  12. package/src/PostgresEntityDatabaseAdapterProvider.ts +3 -3
  13. package/src/__integration-tests__/PostgresEntityIntegration-test.ts +209 -250
  14. package/src/__integration-tests__/PostgresEntityQueryContextProvider-test.ts +17 -26
  15. package/src/__integration-tests__/PostgresInvalidSetup-test.ts +13 -18
  16. package/src/__integration-tests__/errors-test.ts +3 -15
  17. package/src/{testfixtures → __testfixtures__}/ErrorsTestEntity.ts +9 -12
  18. package/src/{testfixtures → __testfixtures__}/InvalidTestEntity.ts +12 -8
  19. package/src/{testfixtures → __testfixtures__}/PostgresTestEntity.ts +13 -9
  20. package/src/{testfixtures → __testfixtures__}/PostgresTriggerTestEntity.ts +29 -27
  21. package/src/{testfixtures → __testfixtures__}/PostgresUniqueTestEntity.ts +26 -24
  22. package/src/{testfixtures → __testfixtures__}/PostgresValidatorTestEntity.ts +28 -26
  23. package/src/{testfixtures → __testfixtures__}/createKnexIntegrationTestEntityCompanionProvider.ts +1 -1
  24. package/src/__tests__/EntityFields-test.ts +1 -1
  25. package/build/__integration-tests__/PostgresEntityIntegration-test.d.ts +0 -1
  26. package/build/__integration-tests__/PostgresEntityIntegration-test.js +0 -589
  27. package/build/__integration-tests__/PostgresEntityIntegration-test.js.map +0 -1
  28. package/build/__integration-tests__/PostgresEntityQueryContextProvider-test.d.ts +0 -1
  29. package/build/__integration-tests__/PostgresEntityQueryContextProvider-test.js +0 -95
  30. package/build/__integration-tests__/PostgresEntityQueryContextProvider-test.js.map +0 -1
  31. package/build/__integration-tests__/PostgresInvalidSetup-test.d.ts +0 -1
  32. package/build/__integration-tests__/PostgresInvalidSetup-test.js +0 -74
  33. package/build/__integration-tests__/PostgresInvalidSetup-test.js.map +0 -1
  34. package/build/__integration-tests__/errors-test.d.ts +0 -1
  35. package/build/__integration-tests__/errors-test.js +0 -139
  36. package/build/__integration-tests__/errors-test.js.map +0 -1
  37. package/build/__tests__/EntityFields-test.d.ts +0 -1
  38. package/build/__tests__/EntityFields-test.js +0 -8
  39. package/build/__tests__/EntityFields-test.js.map +0 -1
  40. package/build/errors/__tests__/wrapNativePostgresCallAsync-test.d.ts +0 -1
  41. package/build/errors/__tests__/wrapNativePostgresCallAsync-test.js +0 -24
  42. package/build/errors/__tests__/wrapNativePostgresCallAsync-test.js.map +0 -1
  43. package/build/testfixtures/ErrorsTestEntity.d.ts +0 -24
  44. package/build/testfixtures/ErrorsTestEntity.js +0 -106
  45. package/build/testfixtures/ErrorsTestEntity.js.map +0 -1
  46. package/build/testfixtures/InvalidTestEntity.d.ts +0 -19
  47. package/build/testfixtures/InvalidTestEntity.js +0 -61
  48. package/build/testfixtures/InvalidTestEntity.js.map +0 -1
  49. package/build/testfixtures/PostgresTestEntity.d.ts +0 -31
  50. package/build/testfixtures/PostgresTestEntity.js +0 -95
  51. package/build/testfixtures/PostgresTestEntity.js.map +0 -1
  52. package/build/testfixtures/PostgresTriggerTestEntity.d.ts +0 -19
  53. package/build/testfixtures/PostgresTriggerTestEntity.js +0 -101
  54. package/build/testfixtures/PostgresTriggerTestEntity.js.map +0 -1
  55. package/build/testfixtures/PostgresUniqueTestEntity.d.ts +0 -19
  56. package/build/testfixtures/PostgresUniqueTestEntity.js +0 -62
  57. package/build/testfixtures/PostgresUniqueTestEntity.js.map +0 -1
  58. package/build/testfixtures/PostgresValidatorTestEntity.d.ts +0 -19
  59. package/build/testfixtures/PostgresValidatorTestEntity.js +0 -77
  60. package/build/testfixtures/PostgresValidatorTestEntity.js.map +0 -1
  61. package/build/testfixtures/createKnexIntegrationTestEntityCompanionProvider.d.ts +0 -3
  62. package/build/testfixtures/createKnexIntegrationTestEntityCompanionProvider.js +0 -29
  63. package/build/testfixtures/createKnexIntegrationTestEntityCompanionProvider.js.map +0 -1
@@ -2,19 +2,19 @@ import { EntityFieldDefinition } from '@expo/entity';
2
2
  /**
3
3
  * EntityFieldDefinition for a Postres column with a JS JSON array type.
4
4
  */
5
- export declare class JSONArrayField extends EntityFieldDefinition<any[]> {
5
+ export declare class JSONArrayField<TRequireExplicitCache extends boolean> extends EntityFieldDefinition<any[], TRequireExplicitCache> {
6
6
  protected validateInputValueInternal(value: any[]): boolean;
7
7
  }
8
8
  /**
9
9
  * EntityFieldDefinition for a Postgres column that may be a JS JSON array type.
10
10
  * Does not do any validation.
11
11
  */
12
- export declare class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
12
+ export declare class MaybeJSONArrayField<TRequireExplicitCache extends boolean> extends EntityFieldDefinition<any | any[], TRequireExplicitCache> {
13
13
  protected validateInputValueInternal(_value: any): boolean;
14
14
  }
15
15
  /**
16
16
  * EntityFieldDefinition for a Postgres BIGINT column.
17
17
  */
18
- export declare class BigIntField extends EntityFieldDefinition<string> {
18
+ export declare class BigIntField<TRequireExplicitCache extends boolean> extends EntityFieldDefinition<string, TRequireExplicitCache> {
19
19
  protected validateInputValueInternal(value: string): boolean;
20
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EntityFields.js","sourceRoot":"","sources":["../src/EntityFields.ts"],"names":[],"mappings":";;;AAAA,yCAAqD;AAErD;;GAEG;AACH,MAAa,cAAe,SAAQ,8BAA4B;IACpD,0BAA0B,CAAC,KAAY;QAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;CACF;AAJD,wCAIC;AAED;;;GAGG;AACH,MAAa,mBAAoB,SAAQ,8BAAkC;IAC/D,0BAA0B,CAAC,MAAW;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAJD,kDAIC;AAED;;GAEG;AACH,MAAa,WAAY,SAAQ,8BAA6B;IAClD,0BAA0B,CAAC,KAAa;QAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC;IACnC,CAAC;CACF;AAJD,kCAIC"}
1
+ {"version":3,"file":"EntityFields.js","sourceRoot":"","sources":["../src/EntityFields.ts"],"names":[],"mappings":";;;AAAA,yCAAqD;AAErD;;GAEG;AACH,MAAa,cAAsD,SAAQ,8BAG1E;IACW,0BAA0B,CAAC,KAAY;QAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;CACF;AAPD,wCAOC;AAED;;;GAGG;AACH,MAAa,mBAEX,SAAQ,8BAAyD;IACvD,0BAA0B,CAAC,MAAW;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAND,kDAMC;AAED;;GAEG;AACH,MAAa,WAAmD,SAAQ,8BAGvE;IACW,0BAA0B,CAAC,KAAa;QAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC;IACnC,CAAC;CACF;AAPD,kCAOC"}
@@ -1,8 +1,8 @@
1
1
  import { EntityDatabaseAdapter, FieldTransformerMap, TableQuerySelectionModifiers, TableFieldSingleValueEqualityCondition, TableFieldMultiValueEqualityCondition, TableQuerySelectionModifiersWithOrderByRaw } from '@expo/entity';
2
2
  import { Knex } from 'knex';
3
- export default class PostgresEntityDatabaseAdapter<TFields extends Record<string, any>> extends EntityDatabaseAdapter<TFields> {
3
+ export default class PostgresEntityDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields> extends EntityDatabaseAdapter<TFields, TIDField> {
4
4
  protected getFieldTransformerMap(): FieldTransformerMap;
5
- protected fetchManyWhereInternalAsync(queryInterface: Knex, tableName: string, tableField: string, tableValues: readonly any[]): Promise<object[]>;
5
+ protected fetchManyWhereInternalAsync(queryInterface: Knex, tableName: string, tableColumns: readonly string[], tableTuples: any[][]): Promise<object[]>;
6
6
  private applyQueryModifiersToQueryOrderByRaw;
7
7
  private applyQueryModifiersToQuery;
8
8
  protected fetchManyByFieldEqualityConjunctionInternalAsync(queryInterface: Knex, tableName: string, tableFieldSingleValueEqualityOperands: TableFieldSingleValueEqualityCondition[], tableFieldMultiValueEqualityOperands: TableFieldMultiValueEqualityCondition[], querySelectionModifiers: TableQuerySelectionModifiers): Promise<object[]>;
@@ -32,11 +32,37 @@ class PostgresEntityDatabaseAdapter extends entity_1.EntityDatabaseAdapter {
32
32
  ],
33
33
  ]);
34
34
  }
35
- async fetchManyWhereInternalAsync(queryInterface, tableName, tableField, tableValues) {
36
- return await (0, wrapNativePostgresCallAsync_1.default)(() => queryInterface
37
- .select()
38
- .from(tableName)
39
- .whereRaw('?? = ANY(?)', [tableField, tableValues]));
35
+ async fetchManyWhereInternalAsync(queryInterface, tableName, tableColumns, tableTuples) {
36
+ // For single column queries, use the ANY operator to derive a consistent
37
+ // query shape in the postgres query stats table.
38
+ // This produces a query of the form `SELECT * FROM table WHERE ("id") = ANY(?)`
39
+ // with value bindings of the form `[[1]]`, thus not making different value cardinalities
40
+ // produce different query shapes.
41
+ //
42
+ // But for multi-column queries, we must use the IN operator as the ANY operator
43
+ // does not support anonymous composite types. The solution to keep using the ANY operator would be explicit
44
+ // postgres type casting on each value in each tableTuple, thus creating a unique query shape and defeating the purpose.
45
+ // The same applies to using UNNEST on anonymous composite types.
46
+ // Note that this solution is not possible in entity though since we don't have the postgres column types and they
47
+ // can't be derived dynamically.
48
+ //
49
+ // Therefore, for multi-column quries, we use the IN operator which produces a query of the form
50
+ // `SELECT * FROM table WHERE ("id", "name") IN ((?, ?), (?, ?))` with value bindings of the form
51
+ // `[[1, 'a'], [2, 'b']]`, which will produce a unique query shape in the postgres query stats table for
52
+ // each value cardinality.
53
+ //
54
+ // We could use the IN operator for single column queries as well, but we prefer to use ANY to at least keep some
55
+ // consistency in the query shape for the stats table.
56
+ if (tableColumns.length === 1) {
57
+ return await (0, wrapNativePostgresCallAsync_1.default)(() => queryInterface
58
+ .select()
59
+ .from(tableName)
60
+ .whereRaw(`(??) = ANY(?)`, [
61
+ tableColumns[0],
62
+ tableTuples.map((tableTuple) => tableTuple[0]),
63
+ ]));
64
+ }
65
+ return await (0, wrapNativePostgresCallAsync_1.default)(() => queryInterface.select().from(tableName).whereIn(tableColumns, tableTuples));
40
66
  }
41
67
  applyQueryModifiersToQueryOrderByRaw(query, querySelectionModifiers) {
42
68
  let ret = this.applyQueryModifiersToQuery(query, querySelectionModifiers);
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresEntityDatabaseAdapter.js","sourceRoot":"","sources":["../src/PostgresEntityDatabaseAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PostgresEntityDatabaseAdapter.js","sourceRoot":"","sources":["../src/PostgresEntityDatabaseAdapter.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import { IEntityDatabaseAdapterProvider, EntityConfiguration, EntityDatabaseAdapter } from '@expo/entity';
2
2
  export default class PostgresEntityDatabaseAdapterProvider implements IEntityDatabaseAdapterProvider {
3
- getDatabaseAdapter<TFields extends Record<string, any>>(entityConfiguration: EntityConfiguration<TFields>): EntityDatabaseAdapter<TFields>;
3
+ getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(entityConfiguration: EntityConfiguration<TFields, TIDField>): EntityDatabaseAdapter<TFields, TIDField>;
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresEntityDatabaseAdapterProvider.js","sourceRoot":"","sources":["../src/PostgresEntityDatabaseAdapterProvider.ts"],"names":[],"mappings":";;;;;AAMA,oGAA4E;AAE5E,MAAqB,qCAAqC;IAGxD,kBAAkB,CAChB,mBAAiD;QAEjD,OAAO,IAAI,uCAA6B,CAAC,mBAAmB,CAAC,CAAC;IAChE,CAAC;CACF;AARD,wDAQC"}
1
+ {"version":3,"file":"PostgresEntityDatabaseAdapterProvider.js","sourceRoot":"","sources":["../src/PostgresEntityDatabaseAdapterProvider.ts"],"names":[],"mappings":";;;;;AAMA,oGAA4E;AAE5E,MAAqB,qCAAqC;IAGxD,kBAAkB,CAChB,mBAA2D;QAE3D,OAAO,IAAI,uCAA6B,CAAC,mBAAmB,CAAC,CAAC;IAChE,CAAC;CACF;AARD,wDAQC"}
@@ -0,0 +1 @@
1
+ {"root":["../src/entityfields.ts","../src/postgresentitydatabaseadapter.ts","../src/postgresentitydatabaseadapterprovider.ts","../src/postgresentityquerycontextprovider.ts","../src/index.ts","../src/errors/wrapnativepostgrescallasync.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/entity-database-adapter-knex",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Knex database adapter for @expo/entity",
5
5
  "files": [
6
6
  "build",
@@ -10,6 +10,7 @@
10
10
  "types": "build/index.d.ts",
11
11
  "scripts": {
12
12
  "tsc": "tsc",
13
+ "build": "tsc -b tsconfig.build.json",
13
14
  "clean": "rm -rf build coverage coverage-integration",
14
15
  "lint": "eslint src",
15
16
  "lint-fix": "eslint src --fix",
@@ -27,23 +28,24 @@
27
28
  "author": "Expo",
28
29
  "license": "MIT",
29
30
  "dependencies": {
30
- "@expo/entity": "^0.40.0",
31
+ "@expo/entity": "^0.42.0",
31
32
  "knex": "^3.1.0"
32
33
  },
33
34
  "devDependencies": {
34
- "@types/jest": "^29.5.12",
35
+ "@expo/entity-testing-utils": "^0.42.0",
36
+ "@types/jest": "^29.5.14",
35
37
  "@types/node": "^20.14.1",
36
38
  "ctix": "^2.7.0",
37
39
  "eslint": "^8.57.1",
38
40
  "eslint-config-universe": "^14.0.0",
39
41
  "eslint-plugin-tsdoc": "^0.3.0",
40
42
  "jest": "^29.7.0",
41
- "pg": "8.13.1",
43
+ "pg": "8.14.1",
42
44
  "prettier": "^3.3.3",
43
45
  "prettier-plugin-organize-imports": "^4.1.0",
44
- "ts-jest": "^29.2.5",
46
+ "ts-jest": "^29.3.1",
45
47
  "ts-mockito": "^2.6.1",
46
- "typescript": "^5.7.3"
48
+ "typescript": "^5.8.3"
47
49
  },
48
- "gitHead": "74c396255c8177a87cf80da2608072f5989dd519"
50
+ "gitHead": "8414d96d948882735687da146e84913397cd8368"
49
51
  }
@@ -3,7 +3,10 @@ import { EntityFieldDefinition } from '@expo/entity';
3
3
  /**
4
4
  * EntityFieldDefinition for a Postres column with a JS JSON array type.
5
5
  */
6
- export class JSONArrayField extends EntityFieldDefinition<any[]> {
6
+ export class JSONArrayField<TRequireExplicitCache extends boolean> extends EntityFieldDefinition<
7
+ any[],
8
+ TRequireExplicitCache
9
+ > {
7
10
  protected validateInputValueInternal(value: any[]): boolean {
8
11
  return Array.isArray(value);
9
12
  }
@@ -13,7 +16,9 @@ export class JSONArrayField extends EntityFieldDefinition<any[]> {
13
16
  * EntityFieldDefinition for a Postgres column that may be a JS JSON array type.
14
17
  * Does not do any validation.
15
18
  */
16
- export class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
19
+ export class MaybeJSONArrayField<
20
+ TRequireExplicitCache extends boolean,
21
+ > extends EntityFieldDefinition<any | any[], TRequireExplicitCache> {
17
22
  protected validateInputValueInternal(_value: any): boolean {
18
23
  return true;
19
24
  }
@@ -22,7 +27,10 @@ export class MaybeJSONArrayField extends EntityFieldDefinition<any | any[]> {
22
27
  /**
23
28
  * EntityFieldDefinition for a Postgres BIGINT column.
24
29
  */
25
- export class BigIntField extends EntityFieldDefinition<string> {
30
+ export class BigIntField<TRequireExplicitCache extends boolean> extends EntityFieldDefinition<
31
+ string,
32
+ TRequireExplicitCache
33
+ > {
26
34
  protected validateInputValueInternal(value: string): boolean {
27
35
  return typeof value === 'string';
28
36
  }
@@ -14,7 +14,8 @@ import wrapNativePostgresCallAsync from './errors/wrapNativePostgresCallAsync';
14
14
 
15
15
  export default class PostgresEntityDatabaseAdapter<
16
16
  TFields extends Record<string, any>,
17
- > extends EntityDatabaseAdapter<TFields> {
17
+ TIDField extends keyof TFields,
18
+ > extends EntityDatabaseAdapter<TFields, TIDField> {
18
19
  protected getFieldTransformerMap(): FieldTransformerMap {
19
20
  return new Map<string, FieldTransformer<any>>([
20
21
  [
@@ -44,14 +45,44 @@ export default class PostgresEntityDatabaseAdapter<
44
45
  protected async fetchManyWhereInternalAsync(
45
46
  queryInterface: Knex,
46
47
  tableName: string,
47
- tableField: string,
48
- tableValues: readonly any[],
48
+ tableColumns: readonly string[],
49
+ tableTuples: any[][],
49
50
  ): Promise<object[]> {
51
+ // For single column queries, use the ANY operator to derive a consistent
52
+ // query shape in the postgres query stats table.
53
+ // This produces a query of the form `SELECT * FROM table WHERE ("id") = ANY(?)`
54
+ // with value bindings of the form `[[1]]`, thus not making different value cardinalities
55
+ // produce different query shapes.
56
+ //
57
+ // But for multi-column queries, we must use the IN operator as the ANY operator
58
+ // does not support anonymous composite types. The solution to keep using the ANY operator would be explicit
59
+ // postgres type casting on each value in each tableTuple, thus creating a unique query shape and defeating the purpose.
60
+ // The same applies to using UNNEST on anonymous composite types.
61
+ // Note that this solution is not possible in entity though since we don't have the postgres column types and they
62
+ // can't be derived dynamically.
63
+ //
64
+ // Therefore, for multi-column quries, we use the IN operator which produces a query of the form
65
+ // `SELECT * FROM table WHERE ("id", "name") IN ((?, ?), (?, ?))` with value bindings of the form
66
+ // `[[1, 'a'], [2, 'b']]`, which will produce a unique query shape in the postgres query stats table for
67
+ // each value cardinality.
68
+ //
69
+ // We could use the IN operator for single column queries as well, but we prefer to use ANY to at least keep some
70
+ // consistency in the query shape for the stats table.
71
+
72
+ if (tableColumns.length === 1) {
73
+ return await wrapNativePostgresCallAsync(() =>
74
+ queryInterface
75
+ .select()
76
+ .from(tableName)
77
+ .whereRaw(`(??) = ANY(?)`, [
78
+ tableColumns[0],
79
+ tableTuples.map((tableTuple) => tableTuple[0]),
80
+ ]),
81
+ );
82
+ }
83
+
50
84
  return await wrapNativePostgresCallAsync(() =>
51
- queryInterface
52
- .select()
53
- .from(tableName)
54
- .whereRaw('?? = ANY(?)', [tableField, tableValues as any[]]),
85
+ queryInterface.select().from(tableName).whereIn(tableColumns, tableTuples),
55
86
  );
56
87
  }
57
88
 
@@ -9,9 +9,9 @@ import PostgresEntityDatabaseAdapter from './PostgresEntityDatabaseAdapter';
9
9
  export default class PostgresEntityDatabaseAdapterProvider
10
10
  implements IEntityDatabaseAdapterProvider
11
11
  {
12
- getDatabaseAdapter<TFields extends Record<string, any>>(
13
- entityConfiguration: EntityConfiguration<TFields>,
14
- ): EntityDatabaseAdapter<TFields> {
12
+ getDatabaseAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
13
+ entityConfiguration: EntityConfiguration<TFields, TIDField>,
14
+ ): EntityDatabaseAdapter<TFields, TIDField> {
15
15
  return new PostgresEntityDatabaseAdapter(entityConfiguration);
16
16
  }
17
17
  }