Skip to content

Commit 5c678af

Browse files
committed
feat: add cascade support to truncate methods
1 parent 6a22a17 commit 5c678af

File tree

10 files changed

+123
-14
lines changed

10 files changed

+123
-14
lines changed

adonis-typings/database.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ declare module '@ioc:Adonis/Lucid/Database' {
4444
readonly name: 'mssql' | 'mysql' | 'oracledb' | 'postgres' | 'redshift' | 'sqlite3'
4545
readonly supportsAdvisoryLocks: boolean
4646
readonly dateTimeFormat: string
47+
truncate (table: string, cascade?: boolean): Promise<void>
4748
getAdvisoryLock (key: string | number, timeout?: number): Promise<boolean>
4849
releaseAdvisoryLock (key: string | number): Promise<boolean>
4950
}
@@ -125,7 +126,7 @@ declare module '@ioc:Adonis/Lucid/Database' {
125126
/**
126127
* Truncate a given table
127128
*/
128-
truncate (table: string): Promise<void>,
129+
truncate (table: string, cascade?: boolean): Promise<void>,
129130

130131
/**
131132
* Returns columns info for a given table

src/Dialects/Mssql.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
/// <reference path="../../adonis-typings/index.ts" />
1111

12-
import { DialectContract } from '@ioc:Adonis/Lucid/Database'
12+
import { DialectContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database'
1313

1414
export class MssqlDialect implements DialectContract {
1515
public readonly name = 'mssql'
@@ -21,6 +21,16 @@ export class MssqlDialect implements DialectContract {
2121
*/
2222
public readonly dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ'
2323

24+
constructor (private client: QueryClientContract) {
25+
}
26+
27+
/**
28+
* Truncate mssql table
29+
*/
30+
public async truncate (table: string, _: boolean) {
31+
return this.client.knexQuery().table(table).truncate()
32+
}
33+
2434
public getAdvisoryLock (): Promise<boolean> {
2535
throw new Error('Support for advisory locks is not implemented for mssql. Create a PR to add the feature')
2636
}

src/Dialects/Mysql.ts

+23
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ export class MysqlDialect implements DialectContract {
2424
constructor (private client: QueryClientContract) {
2525
}
2626

27+
/**
28+
* Truncate mysql table with option to cascade
29+
*/
30+
public async truncate (table: string, cascade: boolean = false) {
31+
if (!cascade) {
32+
return this.client.knexQuery().table(table).truncate()
33+
}
34+
35+
/**
36+
* Cascade and truncate
37+
*/
38+
const trx = await this.client.transaction()
39+
try {
40+
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=0;')
41+
await trx.knexQuery().table(table).truncate()
42+
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=1;')
43+
await trx.commit()
44+
} catch (error) {
45+
await trx.rollback()
46+
throw error
47+
}
48+
}
49+
2750
/**
2851
* Attempts to add advisory lock to the database and
2952
* returns it's status.

src/Dialects/Oracle.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
/// <reference path="../../adonis-typings/index.ts" />
1111

12-
import { DialectContract } from '@ioc:Adonis/Lucid/Database'
12+
import { DialectContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database'
1313

1414
export class OracleDialect implements DialectContract {
1515
public readonly name = 'oracledb'
@@ -21,6 +21,18 @@ export class OracleDialect implements DialectContract {
2121
*/
2222
public readonly dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
2323

24+
constructor (private client: QueryClientContract) {
25+
}
26+
27+
/**
28+
* Truncate pg table with option to cascade and restart identity
29+
*/
30+
public async truncate (table: string, cascade: boolean = false) {
31+
return cascade
32+
? this.client.rawQuery(`TRUNCATE ${table} CASCADE;`)
33+
: this.client.rawQuery(`TRUNCATE ${table};`)
34+
}
35+
2436
public getAdvisoryLock (): Promise<boolean> {
2537
throw new Error('Support for advisory locks is not implemented for oracledb. Create a PR to add the feature')
2638
}

src/Dialects/Pg.ts

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ export class PgDialect implements DialectContract {
2424
constructor (private client: QueryClientContract) {
2525
}
2626

27+
/**
28+
* Truncate pg table with option to cascade and restart identity
29+
*/
30+
public async truncate (table: string, cascade: boolean = false) {
31+
return cascade
32+
? this.client.rawQuery(`TRUNCATE ${table} RESTART IDENTITY CASCADE;`)
33+
: this.client.rawQuery(`TRUNCATE ${table};`)
34+
}
35+
2736
/**
2837
* Attempts to add advisory lock to the database and
2938
* returns it's status.

src/Dialects/Redshift.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
/// <reference path="../../adonis-typings/index.ts" />
1111

12-
import { DialectContract } from '@ioc:Adonis/Lucid/Database'
12+
import { DialectContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database'
1313

1414
export class RedshiftDialect implements DialectContract {
1515
public readonly name = 'redshift'
@@ -21,6 +21,20 @@ export class RedshiftDialect implements DialectContract {
2121
*/
2222
public readonly dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZZ'
2323

24+
constructor (private client: QueryClientContract) {
25+
}
26+
27+
/**
28+
* Truncate redshift table with option to cascade and restart identity.
29+
*
30+
* NOTE: ASSUMING FEATURE PARITY WITH POSTGRESQL HERE (NOT TESTED)
31+
*/
32+
public async truncate (table: string, cascade: boolean = false) {
33+
return cascade
34+
? this.client.rawQuery(`TRUNCATE ${table} RESTART IDENTITY CASCADE;`)
35+
: this.client.rawQuery(`TRUNCATE ${table};`)
36+
}
37+
2438
/**
2539
* Redshift doesn't support advisory locks. Learn more:
2640
* https://tableplus.com/blog/2018/10/redshift-vs-postgres-database-comparison.html

src/Dialects/Sqlite.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
/// <reference path="../../adonis-typings/index.ts" />
1111

12-
import { DialectContract } from '@ioc:Adonis/Lucid/Database'
12+
import { DialectContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database'
1313

1414
export class SqliteDialect implements DialectContract {
1515
public readonly name = 'sqlite3'
@@ -21,6 +21,16 @@ export class SqliteDialect implements DialectContract {
2121
*/
2222
public readonly dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
2323

24+
constructor (private client: QueryClientContract) {
25+
}
26+
27+
/**
28+
* Truncate SQLITE tables
29+
*/
30+
public async truncate (table: string, _: boolean) {
31+
return this.client.knexQuery().table(table).truncate()
32+
}
33+
2434
/**
2535
* Attempts to add advisory lock to the database and
2636
* returns it's status.

src/QueryClient/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ export class QueryClient implements QueryClientContract {
9898
/**
9999
* Truncate table
100100
*/
101-
public async truncate (table: string): Promise<void> {
102-
await this.getWriteClient().table(table).truncate()
101+
public async truncate (table: string, cascade: boolean = false): Promise<void> {
102+
await this.dialect.truncate(table, cascade)
103103
}
104104

105105
/**

src/TransactionClient/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export class TransactionClient extends EventEmitter implements TransactionClient
8181
/**
8282
* Truncate tables inside a transaction
8383
*/
84-
public async truncate (table: string): Promise<void> {
85-
await this.knexClient.table(table).truncate()
84+
public async truncate (table: string, cascade: boolean = false): Promise<void> {
85+
await this.dialect.truncate(table, cascade)
8686
}
8787

8888
/**

test/database/query-client.spec.ts

+35-5
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,42 @@ test.group('Query client', (group) => {
8080

8181
const client = new QueryClient('write', connection)
8282
const column = await client.columnsInfo('users', 'id')
83-
assert.deepEqual(column, {
84-
type: 'integer',
85-
maxLength: null,
86-
nullable: false,
87-
defaultValue: null,
83+
assert.equal(column.type, 'integer')
84+
})
85+
86+
test('truncate table with cascade', async (_assert) => {
87+
const connection = new Connection('primary', getConfig(), getLogger())
88+
connection.connect()
89+
90+
/**
91+
* Create tables
92+
*/
93+
await connection.client?.schema.createTableIfNotExists('test_users', (table) => {
94+
table.increments('id').primary()
95+
table.string('username')
8896
})
97+
await connection.client?.schema.createTableIfNotExists('test_profiles', (table) => {
98+
table.increments('id').primary()
99+
table.integer('user_id').references('test_users.id').onDelete('CASCADE')
100+
})
101+
102+
/**
103+
* Insert table
104+
*/
105+
const returnValues = await connection.client?.table('test_users').insert({ username: 'virk' })
106+
await connection.client?.table('test_profiles').insert({ user_id: returnValues![0] })
107+
108+
/**
109+
* Truncate
110+
*/
111+
const client = new QueryClient('write', connection)
112+
await client.truncate('test_users', true)
113+
114+
/**
115+
* Drop tables
116+
*/
117+
await connection.client?.schema.dropTable('test_profiles')
118+
await connection.client?.schema.dropTable('test_users')
89119
})
90120
})
91121

0 commit comments

Comments
 (0)