From a419139632b9a058ec752b4018fe8661ced299ff Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Sat, 18 Apr 2026 01:28:10 +0800 Subject: [PATCH] feat: driver respect own tables --- packages/core/src/database.ts | 2 + packages/core/src/driver.ts | 2 +- packages/mongo/src/index.ts | 5 +- packages/mongo/tests/migration.spec.ts | 94 +++++++++++++------------- packages/mysql/src/index.ts | 12 ++-- packages/postgres/src/index.ts | 24 ++----- packages/sqlite/src/index.ts | 10 +-- 7 files changed, 71 insertions(+), 78 deletions(-) diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index d931e071..43f53276 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -130,6 +130,8 @@ export class Database extends Service { const driver = this.getDriver(name) if (!driver) return + driver.tables.add(name) + const { fields } = driver.model(name) Object.values(fields).forEach(field => field?.transformers?.forEach(x => driver.define(x))) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 3a8a49b3..8c8cac0e 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -75,7 +75,7 @@ export abstract class Driver { abstract dropIndex(table: string, name: string): Promise public database!: Database - + public tables: Set = new Set() public types: Dict = Object.create(null) constructor(public ctx: Context, public config: T) {} diff --git a/packages/mongo/src/index.ts b/packages/mongo/src/index.ts index 510e511b..98cffbde 100644 --- a/packages/mongo/src/index.ts +++ b/packages/mongo/src/index.ts @@ -267,14 +267,15 @@ export class MongoDriver extends Driver { } async dropAll() { + const tables = [...this.tables] await Promise.all([ '_fields', - ...Object.keys(this.database.tables), + ...tables, ].map(name => this.db.dropCollection(name, { session: this.session }))) } private async _collStats() { - const tables = Object.keys(this.database.tables) + const tables = [...this.tables] const entries = await Promise.all(tables.map(async (name) => { const coll = this.db.collection(name) const [{ storageStats: { count, size } }] = await coll.aggregate([{ diff --git a/packages/mongo/tests/migration.spec.ts b/packages/mongo/tests/migration.spec.ts index 967a4ba0..fbd24f29 100644 --- a/packages/mongo/tests/migration.spec.ts +++ b/packages/mongo/tests/migration.spec.ts @@ -49,18 +49,20 @@ interface RawDoc { value?: number } -interface Tables { - temp1: Foo - temp2: Bar - temp3: Baz - temp4: Qux +declare module 'minato' { + interface Tables { + mongo1: Foo + mongo2: Bar + mongo3: Baz + mongo4: Qux + } } describe('@minatojs/driver-mongo/migrate-virtualKey', () => { const ctx = new Context() let database: Database - let fiber: Fiber | undefined + let fiber: Fiber | undefined const resetConfig = async (optimizeIndex: boolean) => { await fiber?.dispose() @@ -90,13 +92,13 @@ describe('@minatojs/driver-mongo/migrate-virtualKey', () => { await fiber?.dispose() }) - const getCollection = (table: keyof Tables) => { - const driver = database['getDriver'](table as string) as MongoDriver - return driver.db.collection(table as string) + const getCollection = (table: string) => { + const driver = database['getDriver'](table) as MongoDriver + return driver.db.collection(table) } it('reset optimizeIndex', async () => { - database.extend('temp1', { + database.extend('mongo1', { id: 'unsigned', text: 'string', value: 'integer', @@ -112,33 +114,33 @@ describe('@minatojs/driver-mongo/migrate-virtualKey', () => { }) const table: Foo[] = [] - table.push(await database.create('temp1', { + table.push(await database.create('mongo1', { text: 'awesome foo', timestamp: new Date('2000-01-01'), date: new Date('2020-01-01'), time: new Date('2020-01-01 12:00:00'), })) - table.push(await database.create('temp1', { text: 'awesome bar' })) - table.push(await database.create('temp1', { text: 'awesome baz' })) - await expect(database.get('temp1', {})).to.eventually.deep.eq(table) + table.push(await database.create('mongo1', { text: 'awesome bar' })) + table.push(await database.create('mongo1', { text: 'awesome baz' })) + await expect(database.get('mongo1', {})).to.eventually.deep.eq(table) await resetConfig(true) - await expect(database.get('temp1', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo1', {})).to.eventually.deep.eq(table) await resetConfig(false) - await expect(database.get('temp1', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo1', {})).to.eventually.deep.eq(table) await (Object.values(database.drivers)[0] as Driver).drop('_fields') await resetConfig(true) - await expect(database.get('temp1', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo1', {})).to.eventually.deep.eq(table) await (Object.values(database.drivers)[0] as Driver).drop('_fields') await resetConfig(false) - await expect(database.get('temp1', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo1', {})).to.eventually.deep.eq(table) }) it('using primary', async () => { - database.extend('temp2', { + database.extend('mongo2', { id: 'primary', text: 'string', value: 'integer', @@ -151,41 +153,41 @@ describe('@minatojs/driver-mongo/migrate-virtualKey', () => { foreign: 'primary', }) - await database.remove('temp2', {}) + await database.remove('mongo2', {}) const table: Bar[] = [] - table.push(await database.create('temp2', { + table.push(await database.create('mongo2', { text: 'awesome foo', timestamp: new Date('2000-01-01'), date: new Date('2020-01-01'), time: new Date('2020-01-01 12:00:00'), })) - table.push(await database.create('temp2', { text: 'awesome bar' })) - table.push(await database.create('temp2', { text: 'awesome baz' })) - await expect(database.get('temp2', {})).to.eventually.deep.eq(table) + table.push(await database.create('mongo2', { text: 'awesome bar' })) + table.push(await database.create('mongo2', { text: 'awesome baz' })) + await expect(database.get('mongo2', {})).to.eventually.deep.eq(table) - await expect(database.get('temp2', table[0].id?.toString() as any)).to.eventually.deep.eq([table[0]]) - await expect(database.get('temp2', { id: table[0].id?.toString() as any })).to.eventually.deep.eq([table[0]]) - await expect(database.get('temp2', row => $.eq(row.id, $.literal(table[0].id?.toString(), 'primary') as any))).to.eventually.deep.eq([table[0]]) + await expect(database.get('mongo2', table[0].id?.toString() as any)).to.eventually.deep.eq([table[0]]) + await expect(database.get('mongo2', { id: table[0].id?.toString() as any })).to.eventually.deep.eq([table[0]]) + await expect(database.get('mongo2', row => $.eq(row.id, $.literal(table[0].id?.toString(), 'primary') as any))).to.eventually.deep.eq([table[0]]) await (Object.values(database.drivers)[0] as Driver).drop('_fields') await resetConfig(true) - await expect(database.get('temp2', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo2', {})).to.eventually.deep.eq(table) await (Object.values(database.drivers)[0] as Driver).drop('_fields') await resetConfig(false) - await expect(database.get('temp2', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo2', {})).to.eventually.deep.eq(table) // query & eval - table.push(await database.create('temp2', { foreign: table[0].id })) - await expect(database.get('temp2', {})).to.eventually.deep.eq(table) - await expect(database.get('temp2', { foreign: table[0].id })).to.eventually.deep.eq([table[3]]) - await expect(database.get('temp2', row => $.eq(row.foreign, table[0].id!))).to.eventually.deep.eq([table[3]]) + table.push(await database.create('mongo2', { foreign: table[0].id })) + await expect(database.get('mongo2', {})).to.eventually.deep.eq(table) + await expect(database.get('mongo2', { foreign: table[0].id })).to.eventually.deep.eq([table[3]]) + await expect(database.get('mongo2', row => $.eq(row.foreign, table[0].id!))).to.eventually.deep.eq([table[3]]) }) it('upsert for ensurePrimary path', async () => { await resetConfig(true) - database.extend('temp3', { + database.extend('mongo3', { id: 'unsigned', text: 'string', value: 'integer', @@ -193,21 +195,21 @@ describe('@minatojs/driver-mongo/migrate-virtualKey', () => { autoInc: true, }) - await database.remove('temp3', {}) + await database.remove('mongo3', {}) - const existing = await database.create('temp3', { text: 'before', value: 1 }) + const existing = await database.create('mongo3', { text: 'before', value: 1 }) const insertedId = existing.id! + 1 - await expect(database.upsert('temp3', [ + await expect(database.upsert('mongo3', [ { id: existing.id, text: 'after', value: 2 }, { id: insertedId, text: 'inserted', value: 3 }, ])).to.eventually.have.shape({ inserted: 1, matched: 1 }) - await expect(database.get('temp3', {})).to.eventually.have.deep.members([ + await expect(database.get('mongo3', {})).to.eventually.have.deep.members([ { id: existing.id, text: 'after', value: 2 }, { id: insertedId, text: 'inserted', value: 3 }, ]) - const docs = await getCollection('temp3') + const docs = await getCollection('mongo3') .find({}, { projection: { _id: 1, id: 1, text: 1, value: 1 } }) .sort({ _id: 1 }) .toArray() @@ -221,30 +223,30 @@ describe('@minatojs/driver-mongo/migrate-virtualKey', () => { }) it('upsert for pipeline path', async () => { - database.extend('temp4', { + database.extend('mongo4', { id: 'primary', text: 'string', value: 'integer', }) - await database.remove('temp4', {}) + await database.remove('mongo4', {}) - const existing = await database.create('temp4', { text: 'before', value: 1 }) + const existing = await database.create('mongo4', { text: 'before', value: 1 }) const insertedId = new ObjectId() as unknown as Primary - await expect(database.upsert('temp4', [ + await expect(database.upsert('mongo4', [ { id: existing.id, text: 'after', value: 2 }, { id: insertedId, text: 'inserted', value: 3 }, ])).to.eventually.have.shape({ inserted: 1, matched: 1 }) - const [updated] = await database.get('temp4', { id: existing.id?.toString() as any }) + const [updated] = await database.get('mongo4', { id: existing.id?.toString() as any }) expect(updated).to.have.shape({ text: 'after', value: 2 }) expect(updated.id?.toString()).to.equal(existing.id?.toString()) - const [inserted] = await database.get('temp4', { id: insertedId.toString() as any }) + const [inserted] = await database.get('mongo4', { id: insertedId.toString() as any }) expect(inserted).to.have.shape({ text: 'inserted', value: 3 }) expect(inserted.id?.toString()).to.equal(insertedId.toString()) - const docs = await getCollection('temp4') + const docs = await getCollection('mongo4') .find({}, { projection: { _id: 1, id: 1, text: 1, value: 1 } }) .toArray() diff --git a/packages/mysql/src/index.ts b/packages/mysql/src/index.ts index 45ba7ac1..34ef5f78 100644 --- a/packages/mysql/src/index.ts +++ b/packages/mysql/src/index.ts @@ -354,18 +354,22 @@ INSERT INTO mtt VALUES(json_extract(j, concat('$[', i, ']'))); SET i=i+1; END WH } async dropAll() { - const data = await this._select('information_schema.tables', ['TABLE_NAME'], 'TABLE_SCHEMA = ? AND TABLE_TYPE = ?', [this.config.database, 'BASE TABLE']) - if (!data.length) return + const tables = [...this.tables] + if (!tables.length) return await this.query([ 'SET foreign_key_checks = 0', - ...data.map(({ TABLE_NAME }) => `DROP TABLE ${escapeId(TABLE_NAME)}`), + ...tables.map(table => `DROP TABLE ${escapeId(table)}`), 'SET foreign_key_checks = 1', ].join('; ')) } async stats() { + const tables = [...this.tables] + if (!tables.length) return { size: 0, tables: {} } const data = await this._select( - 'information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], 'TABLE_SCHEMA = ? AND TABLE_TYPE = ?', [this.config.database, 'BASE TABLE'], + 'information_schema.tables', ['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH'], + 'TABLE_SCHEMA = ? AND TABLE_TYPE = ? AND TABLE_NAME IN (?)', + [this.config.database, 'BASE TABLE', tables], ) const stats: Partial = { size: 0 } stats.tables = Object.fromEntries(data.map(({ TABLE_NAME: name, TABLE_ROWS: count, DATA_LENGTH: size }) => { diff --git a/packages/postgres/src/index.ts b/packages/postgres/src/index.ts index 7abb1040..f5a23450 100644 --- a/packages/postgres/src/index.ts +++ b/packages/postgres/src/index.ts @@ -47,21 +47,6 @@ interface IndexInfo { indexdef: string } -interface TableInfo { - table_catalog: string - table_schema: string - table_name: string - table_type: string - self_referencing_column_name: null - reference_generation: null - user_defined_type_catalog: null - user_defined_type_schema: null - user_defined_type_name: null - is_insertable_into: string - is_typed: string - commit_action: null -} - interface QueryTask { sql: string resolve: (value: any) => void @@ -276,15 +261,14 @@ export class PostgresDriver extends Driver { } async dropAll() { - const tables: TableInfo[] = await this.queue(`SELECT * FROM information_schema.tables WHERE table_schema = 'public'`) + const tables = [...this.tables] if (!tables.length) return - await this.query(`DROP TABLE IF EXISTS ${tables.map(t => escapeId(t.table_name)).join(',')} CASCADE`) + await this.query(`DROP TABLE IF EXISTS ${tables.map(t => escapeId(t)).join(',')} CASCADE`) } async stats(): Promise> { - const names = Object.keys(this.database.tables) - const tables = (await this.queue(`SELECT * FROM information_schema.tables WHERE table_schema = 'public'`)) - .map(t => t.table_name).filter(name => names.includes(name)) + const tables = [...this.tables] + if (!tables.length) return { size: 0, tables: {} } const tableStats = await this.queue( tables.map( (name) => `SELECT '${name}' AS name, pg_total_relation_size('${escapeId(name)}') AS size, COUNT(*) AS count FROM ${escapeId(name)}`, diff --git a/packages/sqlite/src/index.ts b/packages/sqlite/src/index.ts index c2c78eb3..d3bf3401 100644 --- a/packages/sqlite/src/index.ts +++ b/packages/sqlite/src/index.ts @@ -258,24 +258,24 @@ export class SQLiteDriver extends Driver { } async dropAll() { - const tables = Object.keys(this.database.tables) + const tables = [...this.tables] for (const table of tables) { this._run(`DROP TABLE ${escapeId(table)}`) } } async stats() { + const tables = [...this.tables] const pageCount = this._get(`PRAGMA page_count`) as { page_count?: number | bigint } const pageSize = this._get(`PRAGMA page_size`) as { page_size?: number | bigint } const stats: Driver.Stats = { size: Number(pageCount?.page_count ?? 0) * Number(pageSize?.page_size ?? 0), tables: {}, } - const tableNames: { name: string }[] = this._all("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;") const dbstats: { name: string; size: number }[] = this._all('SELECT name, pgsize as size FROM "dbstat" WHERE aggregate=TRUE;') - tableNames.forEach(tbl => { - stats.tables[tbl.name] = this._get(`SELECT COUNT(*) as count FROM ${escapeId(tbl.name)};`) - stats.tables[tbl.name].size = dbstats.find(o => o.name === tbl.name)!.size + tables.forEach(name => { + stats.tables[name] = this._get(`SELECT COUNT(*) as count FROM ${escapeId(name)};`) + stats.tables[name].size = dbstats.find(o => o.name === name)?.size ?? 0 }) return stats }