Skip to content

Commit

Permalink
Merge pull request #162 from patrickdronk/master
Browse files Browse the repository at this point in the history
Add truncate functionality
  • Loading branch information
martijndeh committed Dec 5, 2020
2 parents e5e7d6c + 21b15e5 commit cd118c7
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -266,6 +266,13 @@ Below is a list of clauses per query and a short description on what we Mammoth
- [ RETURNING \* | output_expression [ [ AS ] output_name ] [, ...] ] — supported, but limited to 10 expressions
</details>

<details>
<summary>Truncate</summary>

- RESTART IDENTITY
- CASCADE
</details>

## Versioning

Now that we've hit 1.0 Mammoth will stick to semantic versioning, meaning, breaking changes will only be included in major updates.
Expand Down
5 changes: 5 additions & 0 deletions src/__checks__/__snapshots__/truncate.check.ts.snap
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`truncate should truncate (type) should match snapshot 1`] = `"never"`;

exports[`truncate should truncate and await affected row count (type) should match snapshot 1`] = `"number"`;
29 changes: 29 additions & 0 deletions src/__checks__/truncate.check.ts
@@ -0,0 +1,29 @@
import { defineDb, defineTable, integer, text, timestampWithTimeZone, uuid } from '../../.build';
import { Query } from '../../.build/query';
import { ResultSet } from '../../.build/result-set';

const toSnap = <T extends Query<any>>(query: T): ResultSet<T, true> => {
return undefined as any;
};

/** @dts-jest enable:test-type */

const foo = defineTable({
id: uuid().primaryKey().default(`gen_random_uuid()`),
createDate: timestampWithTimeZone().notNull().default(`now()`),
name: text().notNull(),
value: integer(),
});

const db = defineDb({ foo }, () => Promise.resolve({ rows: [], affectedCount: 0 }));

// @dts-jest:group truncate
{
// @dts-jest:snap should truncate
toSnap(db.truncate(db.foo));

db.truncate(db.foo).then((result) => {
// @dts-jest:snap should truncate and await affected row count
result;
});
}
89 changes: 89 additions & 0 deletions src/__tests__/truncate.test.ts
@@ -0,0 +1,89 @@
import { defineDb, defineTable, integer, text, timestampWithTimeZone, uuid } from '..';

import { toSnap } from './helpers';

describe(`truncate`, () => {
const foo = defineTable({
id: uuid().primaryKey().default(`gen_random_uuid()`),
createDate: timestampWithTimeZone().notNull().default(`now()`),
name: text().notNull(),
value: integer(),
});

const bar = defineTable({
id: uuid().primaryKey().default(`gen_random_uuid()`),
});

const baz = defineTable({
id: uuid().primaryKey().default(`gen_random_uuid()`),
});

const db = defineDb(
{
foo,
bar,
baz,
},
() => Promise.resolve({ rows: [], affectedCount: 1 }),
);

it(`should truncate`, () => {
const query = db.truncate(db.foo);

expect(toSnap(query)).toMatchInlineSnapshot(`
Object {
"parameters": Array [],
"text": "TRUNCATE foo",
}
`);
});

it(`should restart identity`, () => {
const query = db.truncate(db.foo).restartIdentity();

expect(toSnap(query)).toMatchInlineSnapshot(`
Object {
"parameters": Array [],
"text": "TRUNCATE foo RESTART IDENTITY",
}
`);
});

it(`should continue identity`, () => {
const query = db.truncate(db.foo).continueIdentity();

expect(toSnap(query)).toMatchInlineSnapshot(`
Object {
"parameters": Array [],
"text": "TRUNCATE foo CONTINUE IDENTITY",
}
`);
});

it(`should cascade`, () => {
const query = db.truncate(db.foo).cascade();

expect(toSnap(query)).toMatchInlineSnapshot(`
Object {
"parameters": Array [],
"text": "TRUNCATE foo CASCADE",
}
`);
});

it(`should restrict`, () => {
const query = db.truncate(db.foo).restrict();

expect(toSnap(query)).toMatchInlineSnapshot(`
Object {
"parameters": Array [],
"text": "TRUNCATE foo RESTRICT",
}
`);
});

it(`should return affectedCount`, async () => {
const affectedRows = await db.truncate(db.foo);
expect(affectedRows).toBe(1);
});
});
2 changes: 2 additions & 0 deletions src/db.ts
Expand Up @@ -9,6 +9,7 @@ import { CaseStatement } from './case';
import { QueryExecutorFn } from './types';
import { Table } from './TableType';
import { makeDeleteFrom } from './delete';
import { makeTruncate } from './truncate';
import { makeUpdate } from './update';
import { makeWith } from './with';
import { toSnakeCase } from './naming';
Expand Down Expand Up @@ -83,6 +84,7 @@ export const defineDb = <TableDefinitions extends { [key: string]: TableDefiniti
deleteFrom: makeDeleteFrom(queryExecutor),
update: makeUpdate(queryExecutor),
with: makeWith(queryExecutor),
truncate: makeTruncate(queryExecutor),
case: () => new CaseStatement<never>([]),
...sqlFunctions,

Expand Down
69 changes: 69 additions & 0 deletions src/truncate.ts
@@ -0,0 +1,69 @@
import { QueryExecutorFn, ResultType } from './types';
import { StringToken, Token, createQueryState } from './tokens';

import { Query } from './query';
import { Table } from './TableType';
import { TableDefinition } from './table';

export const makeTruncate = (queryExecutor: QueryExecutorFn) => <T extends Table<any, any>>(
table: T,
): T extends TableDefinition<any> ? never : TruncateQuery<T> => {
return new TruncateQuery<T>(queryExecutor, table, 'AFFECTED_COUNT', [
new StringToken(`TRUNCATE`),
new StringToken((table as Table<any, any>).getName()),
]) as any;
};

export class TruncateQuery<
T extends Table<any, any>,
Returning = number,
TableColumns = T extends Table<any, infer Columns> ? Columns : never
> extends Query<Returning> {
constructor(
private readonly queryExecutor: QueryExecutorFn,
private readonly table: T,
private readonly resultType: ResultType,
private readonly tokens: Token[],
) {
super();
}

then(
onFulfilled?: ((value: number) => any | PromiseLike<any>) | undefined | null,
onRejected?: ((reason: any) => void | PromiseLike<void>) | undefined | null,
) {
const queryState = createQueryState(this.tokens);

return this.queryExecutor(queryState.text.join(` `), queryState.parameters)
.then((result) => onFulfilled?.(result.affectedCount))
.catch(onRejected);
}

restartIdentity<T extends Table<any, any>>() {
return this.newTruncateQuery([...this.tokens, new StringToken(`RESTART IDENTITY`)]) as any;
}

continueIdentity<T extends Table<any, any>>() {
return this.newTruncateQuery([...this.tokens, new StringToken(`CONTINUE IDENTITY`)]) as any;
}

cascade<T extends Table<any, any>>() {
return this.newTruncateQuery([...this.tokens, new StringToken('CASCADE')]);
}

restrict<T extends Table<any, any>>() {
return this.newTruncateQuery([...this.tokens, new StringToken('RESTRICT')]);
}

private newTruncateQuery(tokens: Token[]): TruncateQuery<any> {
return new TruncateQuery(this.queryExecutor, this.table, 'AFFECTED_COUNT', tokens);
}

getReturningKeys(): string[] {
return [];
}

toTokens(): Token[] {
return this.tokens;
}
}

0 comments on commit cd118c7

Please sign in to comment.