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

feat: add getAllViews getAllTypes dropAllTypes and dropAllViews functions #784

Merged
merged 12 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions adonis-typings/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,22 @@ declare module '@ioc:Adonis/Lucid/Database' {
}

/**
* Dialect specfic methods
* Dialect specific methods
*/
export interface DialectContract {
readonly name: 'mssql' | 'mysql' | 'oracledb' | 'postgres' | 'redshift' | 'sqlite3'
readonly version?: string
readonly supportsAdvisoryLocks: boolean
readonly dateTimeFormat: string

getAllTables(schemas?: string[]): Promise<string[]>
getAllViews(schemas?: string[]): Promise<string[]>
getAllTypes(schemas?: string[]): Promise<string[]>

dropAllTables(schemas?: string[]): Promise<void>
dropAllViews(schemas?: string[]): Promise<void>
dropAllTypes(schemas?: string[]): Promise<void>

truncate(table: string, cascade?: boolean): Promise<void>
getAdvisoryLock(key: string | number, timeout?: number): Promise<boolean>
releaseAdvisoryLock(key: string | number): Promise<boolean>
Expand Down Expand Up @@ -179,11 +186,31 @@ declare module '@ioc:Adonis/Lucid/Database' {
*/
getAllTables(schemas?: string[]): Promise<string[]>

/**
* Returns an array of all views names for one or many schemas
*/
getAllViews(schemas?: string[]): Promise<string[]>

/**
* Returns an array of all types names
*/
getAllTypes(schemas?: string[]): Promise<string[]>

/**
* Drop all tables inside database
*/
dropAllTables(schemas?: string[]): Promise<void>

/**
* Drop all views inside the database
*/
dropAllViews(schemas?: string[]): Promise<void>

/**
* Drop all types inside the database
*/
dropAllTypes(schemas?: string[]): Promise<void>

/**
* Same as `query()`, but also selects the table for the query. The `from` method
* doesn't allow defining the return type and one must use `query` to define
Expand Down Expand Up @@ -545,7 +572,7 @@ declare module '@ioc:Adonis/Lucid/Database' {

/**
* Add a new connection to the list of managed connection. You must call
* connect seperately to instantiate a connection instance
* connect separately to instantiate a connection instance
*/
add(connectionName: string, config: ConnectionConfig): void

Expand Down
2 changes: 1 addition & 1 deletion adonis-typings/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ declare module '@ioc:Adonis/Lucid/Orm' {
*/
export type ExtractScopes<Model> = {
[Scope in keyof PickProperties<Model, QueryScope<QueryScopeCallback>>]: (
...args: OmitFirst<Model[Scope]>
...args: any[]
) => ExtractScopes<Model>
}

Expand Down
26 changes: 24 additions & 2 deletions commands/DbWipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BaseCommand, flags } from '@adonisjs/core/build/standalone'

export default class DbWipe extends BaseCommand {
public static commandName = 'db:wipe'
public static description = 'Drop all tables in database'
public static description = 'Drop all tables, views and types in database'

/**
* Choose a custom pre-defined connection. Otherwise, we use the
Expand All @@ -11,6 +11,18 @@ export default class DbWipe extends BaseCommand {
@flags.string({ description: 'Define a custom database connection', alias: 'c' })
public connection: string

/**
* Drop all views in database
*/
@flags.boolean({ description: 'Also drop all views in database' })
public dropViews: boolean

/**
* Drop all types in database
*/
@flags.boolean({ description: 'Also drop all types in database ( Postgres only )' })
public dropTypes: boolean

/**
* Force command execution in production
*/
Expand Down Expand Up @@ -48,8 +60,18 @@ export default class DbWipe extends BaseCommand {
return
}

if (this.dropViews) {
await db.connection().dropAllViews()
this.logger.info('All views dropped successfully')
}

await db.connection().dropAllTables()
this.logger.success('All tables have been dropped successfully')
this.logger.info('All tables have been dropped successfully')

if (this.dropTypes) {
await db.connection().dropAllTypes()
this.logger.info('All types dropped successfully')
}
}

/**
Expand Down
14 changes: 14 additions & 0 deletions commands/Migration/Fresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ export default class Refresh extends MigrationsBase {
@flags.boolean({ description: 'Indicates if the seed task should run.' })
public seed: boolean

/**
* Drop all views in database
*/
@flags.boolean({ description: 'Also drop all views in database' })
public dropViews: boolean

/**
* Drop all types in database
*/
@flags.boolean({ description: 'Also drop all types in database ( Postgres only )' })
public dropTypes: boolean

/**
* This command loads the application, since we need the runtime
* to find the migration directories for a given connection
Expand Down Expand Up @@ -94,6 +106,8 @@ export default class Refresh extends MigrationsBase {
const resetCmd = new DbWipe(this.application, this.kernel)
resetCmd.connection = this.connection
resetCmd.force = true
resetCmd.dropTypes = this.dropTypes
resetCmd.dropViews = this.dropViews

await resetCmd.run()
}
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
"scripts": {
"mrm": "mrm --preset=@adonisjs/mrm-preset",
"pretest": "npm run lint",
"test:sqlite": "DB=sqlite FORCE_COLOR=true node japaFile.js",
"test:mysql": "DB=mysql FORCE_COLOR=true node japaFile.js",
"test:mysql_legacy": "DB=mysql_legacy FORCE_COLOR=true node japaFile.js",
"test:mssql": "DB=mssql FORCE_COLOR=true node japaFile.js",
"test:pg": "DB=pg FORCE_COLOR=true node japaFile.js",
"test:sqlite": "cross-env DB=sqlite FORCE_COLOR=true node japaFile.js",
"test:mysql": "cross-env DB=mysql FORCE_COLOR=true node japaFile.js",
"test:mysql_legacy": "cross-env DB=mysql_legacy FORCE_COLOR=true node japaFile.js",
"test:mssql": "cross-env DB=mssql FORCE_COLOR=true node japaFile.js",
"test:pg": "cross-env DB=pg FORCE_COLOR=true node japaFile.js",
"test:docker": "npm run test:sqlite && npm run test:mysql && npm run test:mysql_legacy && npm run test:pg && npm run test:mssql",
"test": "docker-compose -f docker-compose.yml -f docker-compose-test.yml build && docker-compose -f docker-compose.yml -f docker-compose-test.yml run --rm test",
"lint": "eslint . --ext=.ts",
Expand Down Expand Up @@ -75,6 +75,7 @@
"@types/qs": "^6.9.7",
"chance": "^1.1.8",
"copyfiles": "^2.4.1",
"cross-env": "^7.0.3",
"del-cli": "^4.0.1",
"dotenv": "^10.0.0",
"eslint": "^7.31.0",
Expand Down
24 changes: 24 additions & 0 deletions src/Dialects/Mssql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,30 @@ export class MssqlDialect implements DialectContract {
await this.client.rawQuery(`EXEC sp_MSforeachtable 'DROP TABLE \\?';`)
}

public async getAllViews(): Promise<string[]> {
throw new Error(
'"getAllViews" method not implemented is not implemented for mssql. Create a PR to add the feature'
)
}

public async getAllTypes(): Promise<string[]> {
throw new Error(
'"getAllTypes" method not implemented is not implemented for mssql. Create a PR to add the feature'
)
}

public async dropAllViews(): Promise<void> {
throw new Error(
'"dropAllViews" method not implemented is not implemented for mssql. Create a PR to add the feature'
)
}

public async dropAllTypes(): Promise<void> {
throw new Error(
'"dropAllTypes" method not implemented is not implemented for mssql. Create a PR to add the feature'
)
}

public getAdvisoryLock(): Promise<boolean> {
throw new Error(
'Support for advisory locks is not implemented for mssql. Create a PR to add the feature'
Expand Down
48 changes: 46 additions & 2 deletions src/Dialects/Mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,39 @@ export class MysqlDialect implements DialectContract {
return tables.map(({ table_name }) => table_name)
}

/**
* Returns an array of all views names
*/
public async getAllViews(): Promise<string[]> {
const tables = await this.client
.query()
.from('information_schema.tables')
.select('table_name as table_name')
.where('TABLE_TYPE', 'VIEW')
.where('table_schema', new RawBuilder('database()'))
.orderBy('table_name', 'asc')

return tables.map(({ table_name }) => table_name)
}

/**
* Returns an array of all types names
*/
public async getAllTypes(): Promise<string[]> {
throw new Error("MySQL doesn't support types")
}

/**
* Drop all tables inside the database
*/
public async dropAllTables() {
const tables = await this.getAllTables()
let tables = await this.getAllTables()

/**
* Add backquote around table names to avoid syntax errors
* in case of a table name with a reserved keyword
*/
tables = tables.map((table) => '`' + table + '`')

/**
* Cascade and truncate
Expand All @@ -81,7 +109,7 @@ export class MysqlDialect implements DialectContract {

try {
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=0;')
await trx.rawQuery(`DROP table ${tables.join(',')};`)
await trx.rawQuery(`DROP TABLE ${tables.join(',')};`)
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=1;')
await trx.commit()
} catch (error) {
Expand All @@ -90,6 +118,22 @@ export class MysqlDialect implements DialectContract {
}
}

/**
* Drop all views inside the database
*/
public async dropAllViews(): Promise<void> {
const views = await this.getAllViews()

return this.client.rawQuery(`DROP VIEW ${views.join(',')};`)
}

/**
* Drop all custom types inside the database
*/
public async dropAllTypes(): Promise<void> {
throw new Error("MySQL doesn't support types")
}

/**
* Attempts to add advisory lock to the database and
* returns it's status.
Expand Down
24 changes: 24 additions & 0 deletions src/Dialects/Oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@ export class OracleDialect implements DialectContract {
)
}

public async getAllViews(): Promise<string[]> {
throw new Error(
'"getAllViews" method is not implemented for oracledb. Create a PR to add the feature.'
)
}

public async getAllTypes(): Promise<string[]> {
throw new Error(
'"getAllTypes" method is not implemented for oracledb. Create a PR to add the feature.'
)
}

public async dropAllViews(): Promise<void> {
throw new Error(
'"dropAllViews" method is not implemented for oracledb. Create a PR to add the feature.'
)
}

public async dropAllTypes(): Promise<void> {
throw new Error(
'"dropAllTypes" method is not implemented for oracledb. Create a PR to add the feature.'
)
}

public getAdvisoryLock(): Promise<boolean> {
throw new Error(
'Support for advisory locks is not implemented for oracledb. Create a PR to add the feature'
Expand Down
48 changes: 48 additions & 0 deletions src/Dialects/Pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ export class PgDialect implements DialectContract {
return tables.map(({ table_name }) => table_name)
}

/**
* Returns an array of all views names for one or many schemas
*/
public async getAllViews(schemas: string[]) {
const views = await this.client
.query()
.from('pg_catalog.pg_views')
.select('viewname as view_name')
.whereIn('schemaname', schemas)
.orderBy('viewname', 'asc')

return views.map(({ view_name }) => view_name)
}

/**
* Returns an array of all types names
*/
public async getAllTypes(_schemas: string[]) {
const types = await this.client
.query()
.select('pg_type.typname')
.distinct()
.from('pg_type')
.innerJoin('pg_enum', 'pg_enum.enumtypid', 'pg_type.oid')

return types.map(({ typname }) => typname)
}

/**
* Truncate pg table with option to cascade and restart identity
*/
Expand All @@ -62,6 +90,26 @@ export class PgDialect implements DialectContract {
await this.client.rawQuery(`DROP table ${tables.join(',')} CASCADE;`)
}

/**
* Drop all views inside the database
*/
public async dropAllViews(schemas: string[]) {
const views = await this.getAllViews(schemas)
if (!views.length) return

await this.client.rawQuery(`DROP view ${views.join(',')} CASCADE;`)
}

/**
* Drop all types inside the database
*/
public async dropAllTypes(schemas: string[]) {
const types = await this.getAllTypes(schemas)
if (!types.length) return

await this.client.rawQuery(`DROP type ${types.join(',')};`)
}

/**
* Attempts to add advisory lock to the database and
* returns it's status.
Expand Down
Loading