@casekit/orm 0.0.1-alpha.14 → 0.0.1-alpha.16

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 (215) hide show
  1. package/lib/orm.query.test.d.ts +2 -0
  2. package/lib/orm.query.test.d.ts.map +1 -0
  3. package/lib/orm.query.test.js +22 -0
  4. package/lib/orm.query.test.js.map +1 -0
  5. package/package.json +3 -6
  6. package/src/Connection.ts +65 -0
  7. package/src/errors.ts +18 -0
  8. package/src/index.ts +16 -0
  9. package/src/logger.ts +3 -0
  10. package/src/migrate/commands/implode.ts +46 -0
  11. package/src/migrate/index.ts +1 -0
  12. package/src/migrate/migrator.ts +24 -0
  13. package/src/migrate/sql/createExtensionsSql.test.ts +26 -0
  14. package/src/migrate/sql/createExtensionsSql.ts +16 -0
  15. package/src/migrate/sql/createForeignKeyConstraintSql.test.ts +50 -0
  16. package/src/migrate/sql/createForeignKeyConstraintSql.ts +44 -0
  17. package/src/migrate/sql/createSchemasSql.test.ts +81 -0
  18. package/src/migrate/sql/createSchemasSql.ts +15 -0
  19. package/src/migrate/sql/createTableSql.properties.ts +38 -0
  20. package/src/migrate/sql/createTableSql.test.ts +74 -0
  21. package/src/migrate/sql/createTableSql.ts +53 -0
  22. package/src/migrate/sql/createUniqueConstraintSql.ts +27 -0
  23. package/src/migrate/sql/dropSchemasSql.ts +15 -0
  24. package/src/migrate/sql/dropTableSql.ts +13 -0
  25. package/src/orm.query.test.ts +28 -0
  26. package/src/orm.ts +370 -0
  27. package/src/pull/index.ts +1 -0
  28. package/src/pull/introspect/getForeignKeys.ts +64 -0
  29. package/src/pull/introspect/getPrimaryKeys.ts +26 -0
  30. package/src/pull/introspect/getTables.ts +51 -0
  31. package/src/pull/introspect/getUniqueConstraints.ts +39 -0
  32. package/src/pull/parse/parseCreateUniqueIndexStatement.test.ts +14 -0
  33. package/src/pull/parse/parseCreateUniqueIndexStatement.ts +19 -0
  34. package/src/pull/pull.ts +78 -0
  35. package/src/pull/render/renderModel.test.ts +144 -0
  36. package/src/pull/render/renderModel.ts +141 -0
  37. package/src/pull/render/renderModelsIndex.ts +24 -0
  38. package/src/pull/render/renderRelations.ts +77 -0
  39. package/src/pull/render/renderRelationsIndex.ts +24 -0
  40. package/src/pull/types/ColumnMeta.ts +10 -0
  41. package/src/pull/types/ForeignKey.ts +6 -0
  42. package/src/pull/types/PrimaryKey.ts +6 -0
  43. package/src/pull/types/UniqueConstraint.ts +8 -0
  44. package/src/pull/util/format.ts +17 -0
  45. package/src/pull/util/quote.ts +7 -0
  46. package/src/pull/util/unquote.ts +9 -0
  47. package/src/queries/clauses/IncludeClause.ts +39 -0
  48. package/src/queries/clauses/LateralByClause.ts +4 -0
  49. package/src/queries/clauses/ReturningClause.ts +7 -0
  50. package/src/queries/clauses/SelectClause.ts +7 -0
  51. package/src/queries/clauses/WhereClause.ts +16 -0
  52. package/src/queries/clauses/helpers/OptionalColumn.ts +18 -0
  53. package/src/queries/clauses/helpers/OptionalParams.ts +14 -0
  54. package/src/queries/clauses/helpers/RequiredColumn.ts +8 -0
  55. package/src/queries/clauses/helpers/RequiredParams.ts +14 -0
  56. package/src/queries/clauses/include/IncludedRelationModel.ts +13 -0
  57. package/src/queries/clauses/include/IncludedRelationName.ts +11 -0
  58. package/src/queries/clauses/include/IncludedRelationQuery.ts +20 -0
  59. package/src/queries/clauses/where/buildWhereClause.ts +121 -0
  60. package/src/queries/clauses/where/buildWhereClauses.test.ts +145 -0
  61. package/src/queries/clauses/where/buildWhereClauses.ts +45 -0
  62. package/src/queries/clauses/where/operators.ts +13 -0
  63. package/src/queries/clauses/where/types/WhereClauseValue.ts +39 -0
  64. package/src/queries/count/buildCount.ts +77 -0
  65. package/src/queries/count/countToSql.ts +66 -0
  66. package/src/queries/count/tests/count.test.ts +35 -0
  67. package/src/queries/count/types/BaseCountParams.ts +11 -0
  68. package/src/queries/count/types/CountBuilder.ts +45 -0
  69. package/src/queries/count/types/CountParams.ts +27 -0
  70. package/src/queries/count.ts +33 -0
  71. package/src/queries/create/buildCreate.ts +67 -0
  72. package/src/queries/create/createResultSchema.ts +24 -0
  73. package/src/queries/create/createToSql.ts +44 -0
  74. package/src/queries/create/tests/createMany.varied-keys.test.ts +28 -0
  75. package/src/queries/create/tests/createOne.test-d.ts +116 -0
  76. package/src/queries/create/tests/createOne.test.ts +197 -0
  77. package/src/queries/create/types/BaseCreateManyParams.ts +7 -0
  78. package/src/queries/create/types/BaseCreateOneParams.ts +7 -0
  79. package/src/queries/create/types/CreateManyParams.ts +15 -0
  80. package/src/queries/create/types/CreateManyResult.ts +17 -0
  81. package/src/queries/create/types/CreateOneParams.ts +22 -0
  82. package/src/queries/create/types/CreateOneResult.ts +17 -0
  83. package/src/queries/createMany.ts +38 -0
  84. package/src/queries/createOne.ts +27 -0
  85. package/src/queries/delete/buildDelete.ts +56 -0
  86. package/src/queries/delete/deleteResultSchema.ts +23 -0
  87. package/src/queries/delete/deleteToSql.ts +48 -0
  88. package/src/queries/delete/tests/deleteOne.test.ts +108 -0
  89. package/src/queries/delete/types/BaseDeleteParams.ts +9 -0
  90. package/src/queries/delete/types/DeleteManyResult.ts +16 -0
  91. package/src/queries/delete/types/DeleteOneResult.ts +16 -0
  92. package/src/queries/delete/types/DeleteParams.ts +12 -0
  93. package/src/queries/deleteMany.ts +33 -0
  94. package/src/queries/deleteOne.ts +32 -0
  95. package/src/queries/find/buildFind.ts +138 -0
  96. package/src/queries/find/findResultSchema.ts +32 -0
  97. package/src/queries/find/findToSql.test.ts +123 -0
  98. package/src/queries/find/findToSql.ts +141 -0
  99. package/src/queries/find/getIncludedManyToManyRelations.ts +49 -0
  100. package/src/queries/find/getIncludedOneToManyRelations.ts +44 -0
  101. package/src/queries/find/tests/findMany.include.test.ts +107 -0
  102. package/src/queries/find/tests/findMany.limit.test-d.ts +75 -0
  103. package/src/queries/find/tests/findMany.limit.test.ts +176 -0
  104. package/src/queries/find/tests/findMany.nullable-relations.test.ts +127 -0
  105. package/src/queries/find/tests/findMany.orderBy.test-d.ts +84 -0
  106. package/src/queries/find/tests/findMany.orderBy.test.ts +184 -0
  107. package/src/queries/find/tests/findMany.select.test-d.ts +117 -0
  108. package/src/queries/find/tests/findMany.select.test.ts +188 -0
  109. package/src/queries/find/tests/findMany.too-deep.test-d.ts +154 -0
  110. package/src/queries/find/tests/findMany.where.test-d.ts +85 -0
  111. package/src/queries/find/tests/findMany.where.test.ts +76 -0
  112. package/src/queries/find/tests/middleware.find.where.test.ts +467 -0
  113. package/src/queries/find/types/BaseFindParams.ts +18 -0
  114. package/src/queries/find/types/FindBuilder.ts +73 -0
  115. package/src/queries/find/types/FindManyParams.ts +24 -0
  116. package/src/queries/find/types/FindManyResult.ts +13 -0
  117. package/src/queries/find/types/FindOneParams.ts +17 -0
  118. package/src/queries/find/types/FindOneResult.ts +50 -0
  119. package/src/queries/findMany.ts +134 -0
  120. package/src/queries/findOne.ts +30 -0
  121. package/src/queries/middleware/Middleware.ts +53 -0
  122. package/src/queries/middleware/ValuesMiddleware.ts +9 -0
  123. package/src/queries/middleware/WhereMiddleware.ts +16 -0
  124. package/src/queries/update/buildUpdate.ts +76 -0
  125. package/src/queries/update/tests/updateOne.test.ts +118 -0
  126. package/src/queries/update/types/BaseUpdateParams.ts +10 -0
  127. package/src/queries/update/types/UpdateManyResult.ts +16 -0
  128. package/src/queries/update/types/UpdateOneResult.ts +16 -0
  129. package/src/queries/update/types/UpdateParams.ts +14 -0
  130. package/src/queries/update/types/UpdateValues.ts +12 -0
  131. package/src/queries/update/updateResultSchema.ts +23 -0
  132. package/src/queries/update/updateToSql.ts +43 -0
  133. package/src/queries/updateMany.ts +33 -0
  134. package/src/queries/updateOne.ts +33 -0
  135. package/src/queries/util/hasConditions.test.ts +36 -0
  136. package/src/queries/util/hasConditions.ts +14 -0
  137. package/src/queries/util/rowToObject.test.ts +76 -0
  138. package/src/queries/util/rowToObject.ts +13 -0
  139. package/src/queries/util/tableAlias.test.ts +20 -0
  140. package/src/queries/util/tableAlias.ts +10 -0
  141. package/src/schema/populate/composeMiddleware.ts +51 -0
  142. package/src/schema/populate/populateConfiguration.ts +48 -0
  143. package/src/schema/populate/populateModel.ts +98 -0
  144. package/src/schema/populate/suggestedColumnSchema.ts +62 -0
  145. package/src/schema/types/base/BaseColumn.ts +10 -0
  146. package/src/schema/types/base/BaseConfiguration.ts +14 -0
  147. package/src/schema/types/base/BaseModel.ts +12 -0
  148. package/src/schema/types/base/BaseModels.ts +3 -0
  149. package/src/schema/types/base/BaseOrm.ts +8 -0
  150. package/src/schema/types/base/BaseRelation.ts +19 -0
  151. package/src/schema/types/base/BaseRelations.ts +3 -0
  152. package/src/schema/types/constraints/ForeignKey.ts +13 -0
  153. package/src/schema/types/constraints/UniqueConstraint.ts +9 -0
  154. package/src/schema/types/helpers/ColumnName.ts +6 -0
  155. package/src/schema/types/helpers/ColumnType.ts +23 -0
  156. package/src/schema/types/helpers/Columns.ts +3 -0
  157. package/src/schema/types/helpers/HasDefault.ts +11 -0
  158. package/src/schema/types/helpers/IsNullable.ts +7 -0
  159. package/src/schema/types/helpers/IsProvided.ts +8 -0
  160. package/src/schema/types/helpers/IsSerial.ts +10 -0
  161. package/src/schema/types/helpers/ModelName.ts +6 -0
  162. package/src/schema/types/loose/LooseColumnDefinition.ts +80 -0
  163. package/src/schema/types/loose/LooseModelDefinition.ts +47 -0
  164. package/src/schema/types/loose/LooseModelDefinitions.ts +3 -0
  165. package/src/schema/types/loose/LooseRelationDefinition.ts +10 -0
  166. package/src/schema/types/loose/LooseRelationsDefinition.ts +8 -0
  167. package/src/schema/types/loose/LooseRelationsDefinitions.ts +7 -0
  168. package/src/schema/types/postgres/DataType.ts +65 -0
  169. package/src/schema/types/relations/ManyToManyRelation.ts +15 -0
  170. package/src/schema/types/relations/ManyToOneRelation.ts +14 -0
  171. package/src/schema/types/relations/OneToManyRelation.ts +12 -0
  172. package/src/schema/types/strict/ColumnDefinition.ts +81 -0
  173. package/src/schema/types/strict/ModelDefinition.ts +47 -0
  174. package/src/schema/types/strict/ModelDefinitions.ts +3 -0
  175. package/src/schema/types/strict/RelationDefinition.ts +13 -0
  176. package/src/schema/types/strict/RelationsDefinition.ts +8 -0
  177. package/src/schema/types/strict/RelationsDefinitions.ts +7 -0
  178. package/src/schema/validate/validateConfiguration.ts +9 -0
  179. package/src/schema/validate/validateModel.ts +32 -0
  180. package/src/sql/SQLStatement.test.ts +112 -0
  181. package/src/sql/SQLStatement.ts +86 -0
  182. package/src/sql/index.ts +2 -0
  183. package/src/sql/sql.ts +47 -0
  184. package/src/test/db/index.ts +27 -0
  185. package/src/test/db/models/foo.model.ts +24 -0
  186. package/src/test/db/models/foo.relations.ts +4 -0
  187. package/src/test/db/models/post.model.ts +34 -0
  188. package/src/test/db/models/post.relations.ts +21 -0
  189. package/src/test/db/models/tenant.model.ts +14 -0
  190. package/src/test/db/models/tenant.relations.ts +22 -0
  191. package/src/test/db/models/tenantUser.model.ts +23 -0
  192. package/src/test/db/models/tenantUser.relations.ts +15 -0
  193. package/src/test/db/models/user.model.ts +29 -0
  194. package/src/test/db/models/user.relations.ts +38 -0
  195. package/src/test/db/models.ts +15 -0
  196. package/src/test/db/relations.ts +13 -0
  197. package/src/test/gen/column.ts +39 -0
  198. package/src/test/gen/index.ts +2 -0
  199. package/src/test/gen/model.ts +58 -0
  200. package/src/test/gen/sqldate.ts +8 -0
  201. package/src/test/globalSetup.ts +6 -0
  202. package/src/test/seed/index.ts +75 -0
  203. package/src/test/util/withRollback.ts +18 -0
  204. package/src/test/util/withTransaction.ts +19 -0
  205. package/src/types/ColumnName.ts +6 -0
  206. package/src/types/ColumnType.ts +20 -0
  207. package/src/types/Configuration.ts +21 -0
  208. package/src/types/ModelType.ts +20 -0
  209. package/src/types/util/DeepRequired.ts +7 -0
  210. package/src/types/util/DisallowExtraKeys.ts +5 -0
  211. package/src/types/util/NonEmptyArray.ts +1 -0
  212. package/src/types/util/Simplify.ts +8 -0
  213. package/src/util/ensureArray.ts +4 -0
  214. package/src/util/interleave.test.ts +35 -0
  215. package/src/util/interleave.ts +8 -0
@@ -0,0 +1,38 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+
3
+ import { Connection } from "../Connection";
4
+ import { logger } from "../logger";
5
+ import { buildCreate } from "./create/buildCreate";
6
+ import { createToSql } from "./create/createToSql";
7
+ import { BaseCreateManyParams } from "./create/types/BaseCreateManyParams";
8
+ import { rowToObject } from "./util/rowToObject";
9
+
10
+ export const createMany = async (
11
+ conn: Connection,
12
+ config: BaseConfiguration,
13
+ m: string,
14
+ params: BaseCreateManyParams,
15
+ ) => {
16
+ if (params.values.length === 0) {
17
+ return params.returning ? [] : 0;
18
+ }
19
+
20
+ const builder = buildCreate(config, m, params);
21
+ const statement = createToSql(builder);
22
+
23
+ logger.info({
24
+ message: "Executing create",
25
+ sql: statement.text,
26
+ values: statement.values,
27
+ });
28
+
29
+ if (process.env.ORM_VERBOSE_LOGGING) {
30
+ console.log(statement.text);
31
+ console.log(statement.values);
32
+ }
33
+
34
+ const result = await conn.query(statement);
35
+ return params.returning
36
+ ? result.rows.map(rowToObject(builder.returning))
37
+ : result.rowCount;
38
+ };
@@ -0,0 +1,27 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+
3
+ import { OrmError } from "..";
4
+ import { Connection } from "../Connection";
5
+ import { BaseCreateOneParams } from "./create/types/BaseCreateOneParams";
6
+ import { createMany } from "./createMany";
7
+
8
+ export const createOne = async (
9
+ conn: Connection,
10
+ config: BaseConfiguration,
11
+ m: string,
12
+ params: BaseCreateOneParams,
13
+ ) => {
14
+ if (params.onConflict?.do === "nothing" && params.returning) {
15
+ throw new OrmError(
16
+ "Cannot use 'returning' with 'onConflict' in createOne",
17
+ {
18
+ data: { m, params },
19
+ },
20
+ );
21
+ }
22
+ const result = await createMany(conn, config, m, {
23
+ ...params,
24
+ values: [params.values],
25
+ });
26
+ return Array.isArray(result) ? result[0] : result;
27
+ };
@@ -0,0 +1,56 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+
3
+ import { OrmError } from "../../errors";
4
+ import { ModelName } from "../../schema/types/helpers/ModelName";
5
+ import { LooseModelDefinitions } from "../../schema/types/loose/LooseModelDefinitions";
6
+ import { WhereClause } from "../clauses/WhereClause";
7
+ import { tableAlias } from "../util/tableAlias";
8
+ import { BaseDeleteParams } from "./types/BaseDeleteParams";
9
+
10
+ export type DeleteBuilder = {
11
+ tableIndex: number;
12
+ table: { table: string; model: string; alias: string; schema: string };
13
+ where: WhereClause<LooseModelDefinitions, ModelName<LooseModelDefinitions>>;
14
+ returning: { name: string; path: string; alias: string }[];
15
+ };
16
+
17
+ export const buildDelete = (
18
+ config: BaseConfiguration,
19
+ m: string,
20
+ params: BaseDeleteParams,
21
+ _tableIndex = 0,
22
+ ): DeleteBuilder => {
23
+ const builder: DeleteBuilder = {
24
+ tableIndex: _tableIndex,
25
+ table: {
26
+ table: config.models[m].table,
27
+ schema: config.models[m].schema,
28
+ model: m,
29
+ alias: tableAlias(_tableIndex++),
30
+ },
31
+ where: config.middleware.delete?.where
32
+ ? config.middleware.delete.where(params.where, {
33
+ config,
34
+ model: m,
35
+ })!
36
+ : params.where,
37
+ returning: [],
38
+ };
39
+ let colIndex = 0;
40
+ const model = config.models[m];
41
+
42
+ if (Object.keys(builder.where).length === 0) {
43
+ throw new OrmError("No where clause provided for delete operation", {
44
+ data: { m, model, params },
45
+ });
46
+ }
47
+
48
+ for (const f of params.returning ?? []) {
49
+ builder.returning.push({
50
+ name: model.columns[f]["name"],
51
+ path: f,
52
+ alias: `${builder.table.alias}_${colIndex++}`,
53
+ });
54
+ }
55
+ return builder;
56
+ };
@@ -0,0 +1,23 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+ import { ZodSchema, z } from "zod";
3
+
4
+ import { BaseDeleteParams } from "./types/BaseDeleteParams";
5
+
6
+ export const deleteResultSchema = (
7
+ config: BaseConfiguration,
8
+ m: string,
9
+ params: BaseDeleteParams,
10
+ ) => {
11
+ if (!params.returning) return z.number();
12
+
13
+ const obj: Record<string, ZodSchema<unknown>> = {};
14
+
15
+ params.returning?.forEach((s) => {
16
+ const col = config.models[m].columns[s];
17
+ obj[s] = col.nullable
18
+ ? col.zodSchema.nullish().transform((x) => x ?? null)
19
+ : col.zodSchema;
20
+ });
21
+
22
+ return z.object(obj);
23
+ };
@@ -0,0 +1,48 @@
1
+ import pgfmt from "pg-format";
2
+
3
+ import { OrmError } from "../../errors";
4
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
5
+ import { SQLStatement, sql } from "../../sql";
6
+ import { buildWhereClauses } from "../clauses/where/buildWhereClauses";
7
+ import { hasConditions } from "../util/hasConditions";
8
+ import { DeleteBuilder } from "./buildDelete";
9
+
10
+ export const deleteToSql = (
11
+ config: BaseConfiguration,
12
+ m: string,
13
+ builder: DeleteBuilder,
14
+ ): SQLStatement => {
15
+ const { table, where, returning } = builder;
16
+
17
+ const frag = sql`DELETE FROM %I.%I AS %I`.withIdentifiers(
18
+ table.schema,
19
+ table.table,
20
+ table.alias,
21
+ );
22
+
23
+ if (!hasConditions(where)) {
24
+ throw new OrmError(`No where clause provided for delete`, {
25
+ data: {
26
+ table,
27
+ where,
28
+ returning,
29
+ },
30
+ });
31
+ }
32
+ frag.push("\nWHERE\n ");
33
+ frag.push(buildWhereClauses(config, table, where));
34
+
35
+ if (returning.length > 0) {
36
+ frag.push("\nRETURNING\n");
37
+ frag.push(
38
+ returning
39
+ .map((r) =>
40
+ pgfmt(` %I.%I as %I`, table.alias, r.name, r.alias),
41
+ )
42
+ .join(",\n"),
43
+ );
44
+ }
45
+ frag.push(";\n");
46
+
47
+ return frag;
48
+ };
@@ -0,0 +1,108 @@
1
+ import { describe, expect, test } from "vitest";
2
+
3
+ import { OrmError } from "../../../errors";
4
+ import { db } from "../../../test/db";
5
+ import { seed } from "../../../test/seed";
6
+ import { $like } from "../../clauses/where/operators";
7
+
8
+ describe("deleteOne", () => {
9
+ test("it deletes a single record in the database", async () => {
10
+ await db.transact(
11
+ async (db) => {
12
+ const { posts } = await seed(db, {
13
+ users: [
14
+ {
15
+ username: "Stewart House",
16
+ tenants: [{ name: "Popova Park", posts: 3 }],
17
+ },
18
+ ],
19
+ });
20
+
21
+ const deleted = await db.deleteOne("post", {
22
+ where: { title: "Post a" },
23
+ returning: ["id"],
24
+ });
25
+
26
+ expect(deleted.id).toEqual(
27
+ posts.find((p) => p.title === "Post a")?.id,
28
+ );
29
+
30
+ const results = await db.findMany("post", {
31
+ select: ["id", "title"],
32
+ include: {
33
+ author: {
34
+ select: ["id", "username"],
35
+ where: { username: { [$like]: "Stewart %" } },
36
+ },
37
+ },
38
+ });
39
+
40
+ expect(results.map((p) => p.title)).toEqual([
41
+ "Post b",
42
+ "Post c",
43
+ ]);
44
+ },
45
+ { rollback: true },
46
+ );
47
+ });
48
+
49
+ test("if no records are deleted, it throws an error and rolls back to before the delete", async () => {
50
+ await db.transact(
51
+ async (db) => {
52
+ await seed(db, {
53
+ users: [
54
+ {
55
+ username: "Stewart House",
56
+ tenants: [{ name: "Popova Park", posts: 3 }],
57
+ },
58
+ ],
59
+ });
60
+
61
+ await expect(
62
+ db.deleteOne("post", {
63
+ where: { title: "Wrong" },
64
+ returning: ["id"],
65
+ }),
66
+ ).rejects.toEqual(new OrmError("No rows deleted"));
67
+ },
68
+ { rollback: true },
69
+ );
70
+ });
71
+
72
+ test("if multiple records are deleted, it throws an error and rolls back to before the delete", async () => {
73
+ await db.transact(
74
+ async (db) => {
75
+ await seed(db, {
76
+ users: [
77
+ {
78
+ username: "Stewart House",
79
+ tenants: [{ name: "Popova Park", posts: 3 }],
80
+ },
81
+ {
82
+ username: "Stewart Home",
83
+ tenants: [{ name: "Popova Park", posts: 3 }],
84
+ },
85
+ ],
86
+ });
87
+
88
+ await expect(
89
+ db.deleteOne("post", {
90
+ where: { title: { [$like]: "Post%" } },
91
+ returning: ["id"],
92
+ }),
93
+ ).rejects.toEqual(
94
+ new OrmError(
95
+ "More than one deleted row for deleteOne, rolling back",
96
+ ),
97
+ );
98
+
99
+ const result = await db.findMany("post", {
100
+ select: ["id"],
101
+ });
102
+
103
+ expect(result.length).toEqual(6);
104
+ },
105
+ { rollback: true },
106
+ );
107
+ });
108
+ });
@@ -0,0 +1,9 @@
1
+ import { ModelName } from "../../../schema/types/helpers/ModelName";
2
+ import { LooseModelDefinitions } from "../../../schema/types/loose/LooseModelDefinitions";
3
+ import { NonEmptyArray } from "../../../types/util/NonEmptyArray";
4
+ import { WhereClause } from "../../clauses/WhereClause";
5
+
6
+ export type BaseDeleteParams = {
7
+ where: WhereClause<LooseModelDefinitions, ModelName<LooseModelDefinitions>>;
8
+ returning?: NonEmptyArray<string>;
9
+ };
@@ -0,0 +1,16 @@
1
+ import { ColumnType } from "../../../schema/types/helpers/ColumnType";
2
+ import { ModelName } from "../../../schema/types/helpers/ModelName";
3
+ import { LooseModelDefinitions } from "../../../schema/types/loose/LooseModelDefinitions";
4
+ import { ReturningClause } from "../../clauses/ReturningClause";
5
+ import { DeleteParams } from "./DeleteParams";
6
+
7
+ export type DeleteManyResult<
8
+ Models extends LooseModelDefinitions,
9
+ M extends ModelName<Models>,
10
+ P extends DeleteParams<Models, M>,
11
+ > =
12
+ P["returning"] extends ReturningClause<Models[M]>
13
+ ? Readonly<{
14
+ [C in P["returning"][number]]: ColumnType<Models, M, C>;
15
+ }>[]
16
+ : number;
@@ -0,0 +1,16 @@
1
+ import { ColumnType } from "../../../schema/types/helpers/ColumnType";
2
+ import { ModelName } from "../../../schema/types/helpers/ModelName";
3
+ import { LooseModelDefinitions } from "../../../schema/types/loose/LooseModelDefinitions";
4
+ import { ReturningClause } from "../../clauses/ReturningClause";
5
+ import { DeleteParams } from "./DeleteParams";
6
+
7
+ export type DeleteOneResult<
8
+ Models extends LooseModelDefinitions,
9
+ M extends ModelName<Models>,
10
+ P extends DeleteParams<Models, M>,
11
+ > =
12
+ P["returning"] extends ReturningClause<Models[M]>
13
+ ? Readonly<{
14
+ [C in P["returning"][number]]: ColumnType<Models, M, C>;
15
+ }>
16
+ : number;
@@ -0,0 +1,12 @@
1
+ import { ModelName } from "../../../schema/types/helpers/ModelName";
2
+ import { LooseModelDefinitions } from "../../../schema/types/loose/LooseModelDefinitions";
3
+ import { ReturningClause } from "../../clauses/ReturningClause";
4
+ import { WhereClause } from "../../clauses/WhereClause";
5
+
6
+ export type DeleteParams<
7
+ Models extends LooseModelDefinitions,
8
+ M extends ModelName<Models>,
9
+ > = {
10
+ where: WhereClause<Models, M>;
11
+ returning?: ReturningClause<Models[M]>;
12
+ };
@@ -0,0 +1,33 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+
3
+ import { Connection } from "../Connection";
4
+ import { logger } from "../logger";
5
+ import { buildDelete } from "./delete/buildDelete";
6
+ import { deleteToSql } from "./delete/deleteToSql";
7
+ import { BaseDeleteParams } from "./delete/types/BaseDeleteParams";
8
+ import { rowToObject } from "./util/rowToObject";
9
+
10
+ export const deleteMany = async (
11
+ conn: Connection,
12
+ config: BaseConfiguration,
13
+ m: string,
14
+ params: BaseDeleteParams,
15
+ ) => {
16
+ const builder = buildDelete(config, m, params);
17
+ const statement = deleteToSql(config, m, builder);
18
+ logger.info({
19
+ message: "Executing delete",
20
+ sql: statement.text,
21
+ values: statement.values,
22
+ });
23
+
24
+ if (process.env.ORM_VERBOSE_LOGGING) {
25
+ console.log(statement.text);
26
+ console.log(statement.values);
27
+ }
28
+
29
+ const result = await conn.query(statement);
30
+ return params.returning
31
+ ? result.rows.map(rowToObject(builder.returning))
32
+ : result.rowCount ?? 0;
33
+ };
@@ -0,0 +1,32 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+
3
+ import { Connection } from "../Connection";
4
+ import { OrmError } from "../errors";
5
+ import { BaseDeleteParams } from "./delete/types/BaseDeleteParams";
6
+ import { deleteMany } from "./deleteMany";
7
+
8
+ export const deleteOne = async (
9
+ conn: Connection,
10
+ config: BaseConfiguration,
11
+ m: string,
12
+ params: BaseDeleteParams,
13
+ ) => {
14
+ return await conn.transact(async (conn) => {
15
+ const results = await deleteMany(conn, config, m, params);
16
+ const deletedCount =
17
+ typeof results === "number" ? results : results?.length ?? 0;
18
+
19
+ if (deletedCount === 0) {
20
+ throw new OrmError("No rows deleted", { data: { m, params } });
21
+ }
22
+
23
+ if (deletedCount > 1) {
24
+ throw new OrmError(
25
+ "More than one deleted row for deleteOne, rolling back",
26
+ { data: { m, params, deletedCount } },
27
+ );
28
+ }
29
+
30
+ return typeof results === "number" ? results : results[0];
31
+ });
32
+ };
@@ -0,0 +1,138 @@
1
+ import { max, min, uniq } from "lodash-es";
2
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
3
+
4
+ import { ensureArray } from "../../util/ensureArray";
5
+ import { tableAlias } from "../util/tableAlias";
6
+ import { BaseFindParams } from "./types/BaseFindParams";
7
+ import { FindBuilder } from "./types/FindBuilder";
8
+
9
+ export const buildFind = (
10
+ config: BaseConfiguration,
11
+ m: string,
12
+ query: BaseFindParams,
13
+ path: string[] = [],
14
+ _tableIndex = 0,
15
+ ): FindBuilder => {
16
+ const builder: FindBuilder = {
17
+ columns: [],
18
+ table: {
19
+ table: config.models[m]["table"],
20
+ model: m,
21
+ schema: config.models[m]["schema"],
22
+ alias: tableAlias(_tableIndex++),
23
+ where: config.middleware.find?.where
24
+ ? config.middleware.find.where(query.where, {
25
+ config,
26
+ model: m,
27
+ })
28
+ : query.where,
29
+ joins: [],
30
+ },
31
+ orderBy: [],
32
+ for: query.for,
33
+ tableIndex: _tableIndex,
34
+ };
35
+
36
+ const model = config.models[m];
37
+
38
+ const alias = builder.table.alias;
39
+ let colIndex = 0;
40
+
41
+ // make sure we always select the model's primary key,
42
+ // and if necessary the foreign key for a lateral join
43
+ // - we'll strip them out later
44
+ const select = uniq([
45
+ ...query.select,
46
+ ...model.primaryKey,
47
+ ...(query.lateralBy ?? []).map(({ column }) => column),
48
+ ]);
49
+
50
+ for (const f of select) {
51
+ builder.columns.push({
52
+ table: alias,
53
+ name: model.columns[f]["name"],
54
+ alias: `${alias}_${colIndex++}`,
55
+ path: [...path, f],
56
+ });
57
+ }
58
+
59
+ for (const [r, subquery] of Object.entries(query.include ?? {})) {
60
+ const relation = config.relations[m][r];
61
+ const joinedModel = config.models[relation.model];
62
+ if (relation.type === "N:1") {
63
+ const joinBuilder = buildFind(
64
+ config,
65
+ relation.model,
66
+ subquery!,
67
+ [...path, r],
68
+ builder.tableIndex++,
69
+ );
70
+ const joinedTable = joinBuilder.table;
71
+ builder.table.joins.push(
72
+ {
73
+ from: {
74
+ schema: config.models[m].schema,
75
+ table: config.models[m].table,
76
+ alias,
77
+ model: m,
78
+ columns: ensureArray(relation.foreignKey).map(
79
+ (c) => model.columns[c].name,
80
+ ),
81
+ },
82
+ to: {
83
+ schema: joinedModel.schema,
84
+ table: joinedTable.table,
85
+ alias: joinedTable.alias,
86
+ model: relation.model,
87
+ columns: joinedModel.primaryKey.map(
88
+ (c) => joinedModel.columns[c].name,
89
+ ),
90
+ },
91
+ type: relation.optional ? "left" : "inner",
92
+ where: joinedTable.where,
93
+ },
94
+ ...joinedTable.joins,
95
+ );
96
+ // this is admittedly a bit weird,
97
+ // we wouldn't expect N:1 relations to specify
98
+ // ordering, skipping, and limiting, and typescript
99
+ // prevents users from doing this, but
100
+ // we rely on them being present as part of the N:N
101
+ // implementation
102
+ builder.columns.push(...joinBuilder.columns);
103
+ builder.orderBy.push(...joinBuilder.orderBy);
104
+ builder.limit = min([builder.limit, joinBuilder.limit]);
105
+ builder.offset = max([builder.offset, joinBuilder.offset]);
106
+ }
107
+ }
108
+
109
+ if (query.lateralBy) {
110
+ builder.lateralBy = {
111
+ groupTable: tableAlias(builder.tableIndex++),
112
+ itemTable: tableAlias(builder.tableIndex++),
113
+ columns: query.lateralBy.map(({ column, values }) => ({
114
+ column: model.columns[column].name,
115
+ type: model.columns[column].type,
116
+ values,
117
+ })),
118
+ };
119
+ }
120
+
121
+ if (query.orderBy) {
122
+ builder.orderBy = query.orderBy.map((o) => ({
123
+ table: alias,
124
+ column: model.columns[Array.isArray(o) ? o[0] : o].name,
125
+ direction: Array.isArray(o) ? o[1] : "asc",
126
+ }));
127
+ }
128
+
129
+ if (query.limit) {
130
+ builder.limit = query.limit;
131
+ }
132
+
133
+ if (query.offset) {
134
+ builder.offset = query.offset;
135
+ }
136
+
137
+ return builder;
138
+ };
@@ -0,0 +1,32 @@
1
+ import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
2
+ import { ZodSchema, z } from "zod";
3
+
4
+ import { BaseFindParams } from "./types/BaseFindParams";
5
+
6
+ export const findResultSchema = (
7
+ config: BaseConfiguration,
8
+ m: string,
9
+ query: BaseFindParams,
10
+ ) => {
11
+ const obj: Record<string, ZodSchema<unknown>> = {};
12
+
13
+ query.select.forEach((field) => {
14
+ const col = config.models[m].columns[field];
15
+ obj[field] = col.nullable
16
+ ? col.zodSchema.nullish().transform((x) => x ?? null)
17
+ : col.zodSchema;
18
+ });
19
+
20
+ for (const [field, subquery] of Object.entries(query.include || {})) {
21
+ const relation = config.relations[m][field];
22
+ const schema = findResultSchema(config, relation.model, subquery!);
23
+ obj[field] =
24
+ relation.type === "N:1"
25
+ ? relation.optional
26
+ ? schema.nullish().transform((x) => x ?? null)
27
+ : schema
28
+ : z.array(schema);
29
+ }
30
+
31
+ return z.object(obj);
32
+ };