From bf2647685e31fdf24458d0b2ab8cb7c0a9337fa7 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Thu, 31 Oct 2024 15:35:57 +0800 Subject: [PATCH] stage --- packages/core/src/database.ts | 4 ++-- packages/core/src/driver.ts | 17 ++++++++++----- packages/core/src/locales/en-US.yml | 3 +++ packages/core/src/locales/zh-CN.yml | 3 +++ packages/core/src/model.ts | 5 +---- packages/core/src/selection.ts | 2 +- packages/mongo/src/index.ts | 16 +++++++------- packages/mongo/src/locales/en-US.yml | 1 - packages/mongo/src/locales/zh-CN.yml | 1 - packages/mysql/src/index.ts | 28 ++++++++++++------------- packages/mysql/src/locales/en-US.yml | 1 - packages/mysql/src/locales/zh-CN.yml | 1 - packages/postgres/src/index.ts | 26 ++++++++++++----------- packages/postgres/src/locales/en-US.yml | 1 - packages/postgres/src/locales/zh-CN.yml | 1 - packages/sqlite/src/index.ts | 15 ++++++------- packages/sqlite/src/locales/en-US.yml | 1 - packages/sqlite/src/locales/zh-CN.yml | 1 - packages/tests/src/migration.ts | 7 +++---- 19 files changed, 68 insertions(+), 66 deletions(-) create mode 100644 packages/core/src/locales/en-US.yml create mode 100644 packages/core/src/locales/zh-CN.yml diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index 2af1e8fc..b9f8b162 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -603,13 +603,13 @@ export class Database extends Servi async drop>(table: K) { if (this[Database.transact]) throw new Error('cannot drop table in transaction') const driver = this.getDriver(table) - if (driver.config.readOnly) throw new Error('cannot drop table in read-only mode') + if (driver.config.readonly) throw new Error('cannot drop table in read-only mode') await driver.drop(table) } async dropAll() { if (this[Database.transact]) throw new Error('cannot drop table in transaction') - await Promise.all(Object.values(this.drivers).filter(driver => !driver.config.readOnly).map(driver => driver.dropAll())) + await Promise.all(Object.values(this.drivers).filter(driver => !driver.config.readonly).map(driver => driver.dropAll())) } async stats() { diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index c8559ee1..70928b7d 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -6,6 +6,8 @@ import { Field, Model, Relation } from './model.ts' import { Database } from './database.ts' import { Type } from './type.ts' import { FlatKeys, Keys, Values } from './utils.ts' +import enUS from './locales/en-US.yml' +import zhCN from './locales/zh-CN.yml' export namespace Driver { export interface Stats { @@ -165,14 +167,14 @@ export abstract class Driver info.name === index.name) if (!oldIndex) { await this.createIndex(table, index) - } else if (!deepEqual(oldIndex, index)) { + } else if (this.config.migrateStrategy === 'auto' && !deepEqual(oldIndex, index)) { await this.dropIndex(table, index.name!) await this.createIndex(table, index) } @@ -182,11 +184,16 @@ export abstract class Driver = z.object({ - readOnly: z.boolean().default(false), + readonly: z.boolean().default(false), + migrateStrategy: z.union([z.const('auto'), z.const('create'), z.const('never')]).default('auto'), + }).i18n({ + 'en-US': enUS, + 'zh-CN': zhCN, }) } diff --git a/packages/core/src/locales/en-US.yml b/packages/core/src/locales/en-US.yml new file mode 100644 index 00000000..a9bc22ba --- /dev/null +++ b/packages/core/src/locales/en-US.yml @@ -0,0 +1,3 @@ +$description: Access settings +readonly: Connect in read-only mode. +migrateStrategy: Table migration strategy. diff --git a/packages/core/src/locales/zh-CN.yml b/packages/core/src/locales/zh-CN.yml new file mode 100644 index 00000000..805ed2b3 --- /dev/null +++ b/packages/core/src/locales/zh-CN.yml @@ -0,0 +1,3 @@ +$description: 访问设置 +readonly: 以只读模式连接。 +migrateStrategy: 表迁移策略。 diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts index c64cfd04..ebc2bf2e 100644 --- a/packages/core/src/model.ts +++ b/packages/core/src/model.ts @@ -245,7 +245,6 @@ export namespace Model { export interface Config { callback?: Migration autoInc: boolean - immutable: boolean primary: MaybeArray unique: MaybeArray[] indexes: (MaybeArray | Driver.IndexDef)[] @@ -267,7 +266,6 @@ export class Model { constructor(public name: string) { this.autoInc = false - this.immutable = false this.primary = 'id' as never this.unique = [] this.indexes = [] @@ -276,11 +274,10 @@ export class Model { extend(fields: Field.Extension, config?: Partial): void extend(fields = {}, config: Partial = {}) { - const { primary, autoInc, immutable, unique = [], indexes = [], foreign, callback } = config + const { primary, autoInc, unique = [], indexes = [], foreign, callback } = config this.primary = primary || this.primary this.autoInc = autoInc || this.autoInc - this.immutable = immutable || this.immutable unique.forEach(key => this.unique.includes(key) || this.unique.push(key)) indexes.map(x => this.parseIndex(x)).forEach(index => (this.indexes.some(ind => deepEqual(ind, index))) || this.indexes.push(index)) Object.assign(this.foreign, foreign) diff --git a/packages/core/src/selection.ts b/packages/core/src/selection.ts index c8d41500..91eae13b 100644 --- a/packages/core/src/selection.ts +++ b/packages/core/src/selection.ts @@ -146,7 +146,7 @@ class Executable { } async execute(): Promise { - if (this.driver.config.readOnly && !['get', 'eval'].includes(this.type)) { + if (this.driver.config.readonly && !['get', 'eval'].includes(this.type)) { throw new Error(`database is in read-only mode`) } await this.driver.database.prepared() diff --git a/packages/mongo/src/index.ts b/packages/mongo/src/index.ts index cd69533d..0c9dc7b7 100644 --- a/packages/mongo/src/index.ts +++ b/packages/mongo/src/index.ts @@ -229,10 +229,8 @@ export class MongoDriver extends Driver { /** synchronize table schema */ async prepare(table: string) { - const { immutable } = this.model(table) - - if (immutable || this.config.readOnly) { - if (immutable && this.shouldEnsurePrimary(table)) { + if (this.config.migrateStrategy === 'never' || this.config.readonly) { + if (this.config.migrateStrategy === 'never' && this.shouldEnsurePrimary(table)) { throw new Error(`immutable table ${table} cannot be autoInc`) } return @@ -566,7 +564,6 @@ export namespace MongoDriver { } export const Config: z = z.intersect([ - Driver.Config, z.object({ protocol: z.string().default('mongodb'), host: z.string().default('localhost'), @@ -584,11 +581,12 @@ export namespace MongoDriver { wtimeoutMS: z.number(), journal: z.boolean(), }) as any, + }).i18n({ + 'en-US': enUS, + 'zh-CN': zhCN, }), - ]).i18n({ - 'en-US': enUS, - 'zh-CN': zhCN, - }) + Driver.Config, + ]) } export default MongoDriver diff --git a/packages/mongo/src/locales/en-US.yml b/packages/mongo/src/locales/en-US.yml index 68f118b7..8471c859 100644 --- a/packages/mongo/src/locales/en-US.yml +++ b/packages/mongo/src/locales/en-US.yml @@ -15,4 +15,3 @@ writeConcern: - Majority wtimeoutMS: The write concern timeout. journal: The journal write concern. -readOnly: Connect in read-only mode. diff --git a/packages/mongo/src/locales/zh-CN.yml b/packages/mongo/src/locales/zh-CN.yml index 30963c28..7b66cd9d 100644 --- a/packages/mongo/src/locales/zh-CN.yml +++ b/packages/mongo/src/locales/zh-CN.yml @@ -15,4 +15,3 @@ writeConcern: - Majority wtimeoutMS: The write concern timeout. journal: The journal write concern. -readOnly: 以只读模式连接。 diff --git a/packages/mysql/src/index.ts b/packages/mysql/src/index.ts index d8fba3fd..d31f5fc6 100644 --- a/packages/mysql/src/index.ts +++ b/packages/mysql/src/index.ts @@ -157,7 +157,6 @@ export class MySQLDriver extends Driver { const table = this.model(name) const { primary, foreign, autoInc } = table - const immutable = table.immutable || this.config.readOnly const fields = table.avaiableFields() const unique = [...table.unique] const create: string[] = [] @@ -233,18 +232,21 @@ export class MySQLDriver extends Driver { } } - if (immutable) { + if (!columns.length) { + if (this.config.readonly || this.config.migrateStrategy === 'never') { + throw new Error(`immutable table ${name} cannot be created`) + } + this.logger.info('auto creating table %c', name) + return this.query(`CREATE TABLE ${escapeId(name)} (${create.join(', ')}) COLLATE = ${this.sql.escape(this.config.charset ?? 'utf8mb4_general_ci')}`) + } + + if (this.config.readonly || this.config.migrateStrategy !== 'auto') { if (create.length || update.length) { throw new Error(`immutable table ${name} cannot be migrated`) } return } - if (!columns.length) { - this.logger.info('auto creating table %c', name) - return this.query(`CREATE TABLE ${escapeId(name)} (${create.join(', ')}) COLLATE = ${this.sql.escape(this.config.charset ?? 'utf8mb4_general_ci')}`) - } - const operations = [ ...create.map(def => 'ADD ' + def), ...update, @@ -604,15 +606,12 @@ export namespace MySQLDriver { export interface Config extends Driver.Config, PoolConfig {} export const Config: z = z.intersect([ - Driver.Config, z.object({ host: z.string().default('localhost'), port: z.natural().max(65535).default(3306), user: z.string().default('root'), password: z.string().role('secret'), database: z.string().required(), - }), - z.object({ ssl: z.union([ z.const(undefined), z.object({ @@ -639,11 +638,12 @@ export namespace MySQLDriver { sessionTimeout: z.number(), }), ]) as any, + }).i18n({ + 'en-US': enUS, + 'zh-CN': zhCN, }), - ]).i18n({ - 'en-US': enUS, - 'zh-CN': zhCN, - }) + Driver.Config, + ]) } export default MySQLDriver diff --git a/packages/mysql/src/locales/en-US.yml b/packages/mysql/src/locales/en-US.yml index 965bf36f..6b951df3 100644 --- a/packages/mysql/src/locales/en-US.yml +++ b/packages/mysql/src/locales/en-US.yml @@ -10,4 +10,3 @@ ssl: - Default - $description: Custom rejectUnauthorized: Reject clients with invalid certificates. -readOnly: Connect in read-only mode. diff --git a/packages/mysql/src/locales/zh-CN.yml b/packages/mysql/src/locales/zh-CN.yml index c3fbddf3..8a79dc6f 100644 --- a/packages/mysql/src/locales/zh-CN.yml +++ b/packages/mysql/src/locales/zh-CN.yml @@ -10,4 +10,3 @@ ssl: - 默认值 - $description: 自定义 rejectUnauthorized: 拒绝使用无效证书的客户端。 -readOnly: 以只读模式连接。 diff --git a/packages/postgres/src/index.ts b/packages/postgres/src/index.ts index a8c95b52..990dfdc3 100644 --- a/packages/postgres/src/index.ts +++ b/packages/postgres/src/index.ts @@ -176,7 +176,6 @@ export class PostgresDriver extends Driver { const table = this.model(name) const { primary, foreign } = table - const immutable = table.immutable || this.config.readOnly const fields = { ...table.avaiableFields() } const unique = [...table.unique] const create: string[] = [] @@ -237,18 +236,21 @@ export class PostgresDriver extends Driver { } } - if (immutable) { + if (!columns.length) { + if (this.config.readonly || this.config.migrateStrategy === 'never') { + throw new Error(`immutable table ${name} cannot be created`) + } + this.logger.info('auto creating table %c', name) + return this.query(`CREATE TABLE ${escapeId(name)} (${create.join(', ')}, _pg_mtime BIGINT)`) + } + + if (this.config.readonly || this.config.migrateStrategy !== 'auto') { if (create.length || update.length) { throw new Error(`immutable table ${name} cannot be migrated`) } return } - if (!columns.length) { - this.logger.info('auto creating table %c', name) - return this.query(`CREATE TABLE ${escapeId(name)} (${create.join(', ')}, _pg_mtime BIGINT)`) - } - const operations = [ ...create.map(def => 'ADD ' + def), ...update, @@ -539,18 +541,18 @@ export namespace PostgresDriver { } export const Config: z = z.intersect([ - Driver.Config, z.object({ host: z.string().default('localhost'), port: z.natural().max(65535).default(5432), user: z.string().default('root'), password: z.string().role('secret'), database: z.string().required(), + }).i18n({ + 'en-US': enUS, + 'zh-CN': zhCN, }), - ]).i18n({ - 'en-US': enUS, - 'zh-CN': zhCN, - }) + Driver.Config, + ]) } export default PostgresDriver diff --git a/packages/postgres/src/locales/en-US.yml b/packages/postgres/src/locales/en-US.yml index 1523c27d..6b373cbe 100644 --- a/packages/postgres/src/locales/en-US.yml +++ b/packages/postgres/src/locales/en-US.yml @@ -3,4 +3,3 @@ port: The port number to connect to. user: The MySQL user to authenticate as. password: The password of that MySQL user. database: Name of the database to use for this connection. -readOnly: Connect in read-only mode. diff --git a/packages/postgres/src/locales/zh-CN.yml b/packages/postgres/src/locales/zh-CN.yml index dfe3f74a..353924bd 100644 --- a/packages/postgres/src/locales/zh-CN.yml +++ b/packages/postgres/src/locales/zh-CN.yml @@ -3,4 +3,3 @@ port: 要连接到的端口号。 username: 要使用的用户名。 password: 要使用的密码。 database: 要访问的数据库名。 -readOnly: 以只读模式连接。 diff --git a/packages/sqlite/src/index.ts b/packages/sqlite/src/index.ts index 845b26b8..ee8cce4f 100644 --- a/packages/sqlite/src/index.ts +++ b/packages/sqlite/src/index.ts @@ -63,7 +63,6 @@ export class SQLiteDriver extends Driver { async prepare(table: string, dropKeys?: string[]) { const columns = this._all(`PRAGMA table_info(${escapeId(table)})`) as SQLiteFieldInfo[] const model = this.model(table) - const immutable = model.immutable || this.config.readOnly const columnDefs: string[] = [] const indexDefs: string[] = [] const alter: string[] = [] @@ -113,7 +112,7 @@ export class SQLiteDriver extends Driver { })) } - if (immutable) { + if (this.config.readonly || this.config.migrateStrategy === 'never') { if (!columns.length || shouldMigrate || alter.length) { throw new Error(`immutable table ${table} cannot be migrated`) } @@ -123,6 +122,8 @@ export class SQLiteDriver extends Driver { if (!columns.length) { this.logger.info('auto creating table %c', table) this._run(`CREATE TABLE ${escapeId(table)} (${[...columnDefs, ...indexDefs].join(', ')})`) + } else if (this.config.migrateStrategy === 'create') { + throw new Error(`immutable table ${table} cannot be migrated`) } else if (shouldMigrate) { // preserve old columns for (const { name, type, notnull, pk, dflt_value: value } of columns) { @@ -495,14 +496,14 @@ export namespace SQLiteDriver { } export const Config: z = z.intersect([ - Driver.Config, z.object({ path: z.string().role('path').required(), + }).i18n({ + 'en-US': enUS, + 'zh-CN': zhCN, }), - ]).i18n({ - 'en-US': enUS, - 'zh-CN': zhCN, - }) + Driver.Config, + ]) } export default SQLiteDriver diff --git a/packages/sqlite/src/locales/en-US.yml b/packages/sqlite/src/locales/en-US.yml index b56a1f79..4728d564 100644 --- a/packages/sqlite/src/locales/en-US.yml +++ b/packages/sqlite/src/locales/en-US.yml @@ -1,2 +1 @@ path: Database path. -readOnly: Connect in read-only mode. diff --git a/packages/sqlite/src/locales/zh-CN.yml b/packages/sqlite/src/locales/zh-CN.yml index 0edeab89..b09c9d6b 100644 --- a/packages/sqlite/src/locales/zh-CN.yml +++ b/packages/sqlite/src/locales/zh-CN.yml @@ -1,2 +1 @@ path: 数据库路径。 -readOnly: 以只读模式连接。 diff --git a/packages/tests/src/migration.ts b/packages/tests/src/migration.ts index d97221fd..7e492825 100644 --- a/packages/tests/src/migration.ts +++ b/packages/tests/src/migration.ts @@ -280,7 +280,7 @@ function MigrationTests(database: Database, options: MigrationOptions = }))).to.not.be.undefined }) - definition && it('model.immutable', async () => { + definition && it('immutable model', async () => { Reflect.deleteProperty(database.tables, 'qux') database.extend('qux', { @@ -299,12 +299,10 @@ function MigrationTests(database: Database, options: MigrationOptions = ]) Reflect.deleteProperty(database.tables, 'qux') - + Object.values(database.drivers)[0].config.migrateStrategy = 'never' database.extend('qux', { id: 'unsigned', text: 'integer' as any, - }, { - immutable: true, }) await expect(database.upsert('qux', [ @@ -314,6 +312,7 @@ function MigrationTests(database: Database, options: MigrationOptions = Reflect.deleteProperty(database.tables, 'qux') Reflect.deleteProperty(database['prepareTasks'], 'qux') + Object.values(database.drivers)[0].config.migrateStrategy = 'auto' }) }