Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WITHOUT ROWID support #69

Merged
merged 5 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quick-cups-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"d1-orm": minor
---

add WITHOUT ROWID support(opt-out)
helloimalastair marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 15 additions & 2 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import type { GenerateQueryOptions } from "./queryBuilder.js";
*/
export class Model<T extends Record<string, ModelColumn>> {
/**
* @param options - The options for the model. All parameters except autoIncrement and uniqueKeys are required.
* @param options - The options for the model. All parameters except autoIncrement, withRowId, and uniqueKeys are required.
* @param options.tableName - The name of the table to use.
* @param options.D1Orm - The D1Orm instance to use - optional. If not set initially, you must use SetOrm() to set before querying.
* @param options.primaryKeys - The primary key or keys of the table.
* @param options.autoIncrement - The column to use for auto incrementing. If specified, only one primary key is allowed, and must be of type INTEGER.
* @param options.uniqueKeys - The unique keys of the table. For example `[ ['id'], ['username', 'discriminator'] ]` would cause ID to be unique, as well as the combination of username and discriminator.
* @param options.withRowId - Whether or not D1 should generate a `rowid` column automatically. Defaults to false.
* @param columns - The columns for the model. The keys are the column names, and the values are the column options. See {@link ModelColumn}
*/
constructor(
Expand All @@ -22,6 +23,7 @@ export class Model<T extends Record<string, ModelColumn>> {
primaryKeys: Extract<keyof T, string> | Extract<keyof T, string>[];
autoIncrement?: Extract<keyof T, string>;
uniqueKeys?: Extract<keyof T, string>[][];
withRowId?: boolean;
},
columns: T
) {
Expand All @@ -33,6 +35,7 @@ export class Model<T extends Record<string, ModelColumn>> {
: [options.primaryKeys].filter(Boolean);
this.#autoIncrementColumn = options.autoIncrement;
this.uniqueKeys = options.uniqueKeys || [];
this.#withRowId = options.withRowId ?? false;

if (
this.#D1Orm &&
Expand All @@ -51,6 +54,13 @@ export class Model<T extends Record<string, ModelColumn>> {
"Options.primaryKeys must be a string or an array of strings"
);
}

if (this.#withRowId && this.#autoIncrementColumn) {
throw new Error(
"Options.autoIncrement and Options.withRowId cannot both be set"
);
}

if (!columns) {
throw new Error("Model columns must be defined");
}
Expand Down Expand Up @@ -97,6 +107,7 @@ export class Model<T extends Record<string, ModelColumn>> {
public readonly uniqueKeys: Extract<keyof T, string>[][];
#D1Orm: D1Orm | null;
readonly #autoIncrementColumn?: Extract<keyof T, string>;
readonly #withRowId: boolean;

/**
* @returns The ORM instance that this model is using.
Expand Down Expand Up @@ -149,7 +160,9 @@ export class Model<T extends Record<string, ModelColumn>> {
}
return `CREATE TABLE \`${this.tableName}\` (${columnDefinition.join(
", "
)});`;
)})${
this.#autoIncrementColumn || this.#withRowId ? "" : " WITHOUT ROWID"
};`;
}

/**
Expand Down
46 changes: 38 additions & 8 deletions test/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ describe("Model Validation", () => {
"Options.tableName must be a string"
);
});
it("should throw if autoincrement and withRowId are both set", () => {
expect(
() =>
new Model(
{
D1Orm: orm,
tableName: "users",
primaryKeys: "id",
autoIncrement: "id",
withRowId: true,
},
{ id: { type: DataTypes.INTEGER } }
)
).to.throw(
Error,
"Options.autoIncrement and Options.withRowId cannot both be set"
);
});
describe("Primary keys", () => {
it("should throw an error if no primary keys are provided", () => {
expect(() => new Model({ D1Orm: orm, tableName: "users" })).to.throw(
Expand Down Expand Up @@ -191,7 +209,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id, name));"
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id, name)) WITHOUT ROWID;"
);
});
it("should support a not null constraint", () => {
Expand All @@ -203,7 +221,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, name text NOT NULL, PRIMARY KEY (id));"
"CREATE TABLE `test` (id integer, name text NOT NULL, PRIMARY KEY (id)) WITHOUT ROWID;"
);
});
it("should support a valid autoIncrement constraint", () => {
Expand All @@ -218,6 +236,18 @@ describe("Model > Create Tables", () => {
"CREATE TABLE `test` (id integer PRIMARY KEY AUTOINCREMENT, name text);"
);
});
it("should not include WITHOUT ROWID", () => {
const model = new Model(
{ D1Orm: orm, tableName: "test", primaryKeys: "id", withRowId: true },
{
id: { type: DataTypes.INTEGER },
is_admin: { type: DataTypes.STRING },
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, is_admin text, PRIMARY KEY (id));"
);
});
describe("Unique Constraints", () => {
it("should support a unique constraint", () => {
const model = new Model(
Expand All @@ -233,7 +263,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id));"
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id)) WITHOUT ROWID;"
);
});
it("should support multiple unique constraints", () => {
Expand All @@ -250,7 +280,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id), UNIQUE (name));"
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id), UNIQUE (name)) WITHOUT ROWID;"
);
});
it("should support a unique constraint with multiple columns", () => {
Expand All @@ -267,7 +297,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id, name));"
"CREATE TABLE `test` (id integer, name text, PRIMARY KEY (id), UNIQUE (id, name)) WITHOUT ROWID;"
);
});
});
Expand All @@ -281,7 +311,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, is_admin text DEFAULT 'test', PRIMARY KEY (id));"
"CREATE TABLE `test` (id integer, is_admin text DEFAULT 'test', PRIMARY KEY (id)) WITHOUT ROWID;"
);
});
it("should support a number", () => {
Expand All @@ -293,7 +323,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, is_admin integer DEFAULT 1, PRIMARY KEY (id));"
"CREATE TABLE `test` (id integer, is_admin integer DEFAULT 1, PRIMARY KEY (id)) WITHOUT ROWID;"
);
});
it("should support a boolean", () => {
Expand All @@ -305,7 +335,7 @@ describe("Model > Create Tables", () => {
}
);
expect(model.createTableDefinition).to.equal(
"CREATE TABLE `test` (id integer, is_admin boolean DEFAULT true, PRIMARY KEY (id));"
"CREATE TABLE `test` (id integer, is_admin boolean DEFAULT true, PRIMARY KEY (id)) WITHOUT ROWID;"
);
});
});
Expand Down