sonamu 0.2.54 → 0.2.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/base-model-BzMJ2E_I.d.mts +43 -0
  2. package/dist/base-model-CWRKUX49.d.ts +43 -0
  3. package/dist/bin/cli.js +118 -89
  4. package/dist/bin/cli.js.map +1 -1
  5. package/dist/bin/cli.mjs +74 -45
  6. package/dist/bin/cli.mjs.map +1 -1
  7. package/dist/chunk-4K2F3SOM.mjs +231 -0
  8. package/dist/chunk-4K2F3SOM.mjs.map +1 -0
  9. package/dist/chunk-6SP5N5ND.mjs +1579 -0
  10. package/dist/chunk-6SP5N5ND.mjs.map +1 -0
  11. package/dist/chunk-EUP6N7EK.js +1579 -0
  12. package/dist/chunk-EUP6N7EK.js.map +1 -0
  13. package/dist/chunk-HVVCQLAU.mjs +280 -0
  14. package/dist/chunk-HVVCQLAU.mjs.map +1 -0
  15. package/dist/chunk-N6N3LENC.js +231 -0
  16. package/dist/chunk-N6N3LENC.js.map +1 -0
  17. package/dist/chunk-UAG3SKFM.js +280 -0
  18. package/dist/chunk-UAG3SKFM.js.map +1 -0
  19. package/dist/{chunk-JOHF7PK4.js → chunk-WJGRXAXE.js} +5301 -5623
  20. package/dist/chunk-WJGRXAXE.js.map +1 -0
  21. package/dist/{chunk-L4KELCY7.mjs → chunk-ZFLQLW37.mjs} +5252 -5574
  22. package/dist/chunk-ZFLQLW37.mjs.map +1 -0
  23. package/dist/database/drivers/knex/base-model.d.mts +16 -0
  24. package/dist/database/drivers/knex/base-model.d.ts +16 -0
  25. package/dist/database/drivers/knex/base-model.js +55 -0
  26. package/dist/database/drivers/knex/base-model.js.map +1 -0
  27. package/dist/database/drivers/knex/base-model.mjs +56 -0
  28. package/dist/database/drivers/knex/base-model.mjs.map +1 -0
  29. package/dist/database/drivers/kysely/base-model.d.mts +22 -0
  30. package/dist/database/drivers/kysely/base-model.d.ts +22 -0
  31. package/dist/database/drivers/kysely/base-model.js +64 -0
  32. package/dist/database/drivers/kysely/base-model.js.map +1 -0
  33. package/dist/database/drivers/kysely/base-model.mjs +65 -0
  34. package/dist/database/drivers/kysely/base-model.mjs.map +1 -0
  35. package/dist/index.d.mts +226 -931
  36. package/dist/index.d.ts +226 -931
  37. package/dist/index.js +13 -26
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.mjs +18 -31
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/model-CAH_4oQh.d.mts +1042 -0
  42. package/dist/model-CAH_4oQh.d.ts +1042 -0
  43. package/package.json +1 -1
  44. package/src/api/code-converters.ts +1 -1
  45. package/src/entity/migrator.ts +3 -0
  46. package/src/types/types.ts +1 -0
  47. package/dist/chunk-JOHF7PK4.js.map +0 -1
  48. package/dist/chunk-L4KELCY7.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/entity/migrator.ts","../src/testing/fixture-manager.ts","../src/testing/_relation-graph.ts"],"names":["answer","files","matched","chalk","_","inflection","field","id","args","entity","row","path"],"mappings":"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","sourcesContent":["import _ from \"lodash\";\nimport chalk from \"chalk\";\nimport { DateTime } from \"luxon\";\nimport fs from \"fs-extra\";\nimport equal from \"fast-deep-equal\";\nimport inflection from \"inflection\";\nimport prompts from \"prompts\";\nimport { execSync } from \"child_process\";\nimport path from \"path\";\n\nimport {\n GenMigrationCode,\n isBelongsToOneRelationProp,\n isHasManyRelationProp,\n isManyToManyRelationProp,\n isOneToOneRelationProp,\n isRelationProp,\n isVirtualProp,\n isStringProp,\n KnexColumnType,\n MigrationColumn,\n MigrationForeign,\n MigrationIndex,\n MigrationJoinTable,\n MigrationSet,\n MigrationSetAndJoinTable,\n isDecimalProp,\n isFloatProp,\n isTextProp,\n isEnumProp,\n isIntegerProp,\n isKnexError,\n RelationOn,\n} from \"../types/types\";\nimport { EntityManager } from \"./entity-manager\";\nimport { Entity } from \"./entity\";\nimport { Sonamu } from \"../api\";\nimport { ServiceUnavailableException } from \"../exceptions/so-exceptions\";\nimport { DB } from \"../database/db\";\nimport { KnexClient } from \"../database/drivers/knex/client\";\nimport { KyselyClient } from \"../database/drivers/kysely/client\";\n\ntype MigratorMode = \"dev\" | \"deploy\";\nexport type MigratorOptions = {\n readonly mode: MigratorMode;\n};\ntype MigrationCode = {\n name: string;\n path: string;\n};\ntype ConnString = `${\"mysql2\"}://${string}@${string}:${number}/${string}`; // mysql2://account@host:port/database\nexport type MigrationStatus = {\n codes: MigrationCode[];\n conns: {\n name: string;\n connKey: string;\n connString: ConnString;\n currentVersion: string;\n status: string | number;\n pending: string[];\n }[];\n preparedCodes: GenMigrationCode[];\n};\n\nexport class Migrator {\n readonly mode: MigratorMode;\n\n targets: {\n compare?: KnexClient | KyselyClient;\n pending: KnexClient | KyselyClient;\n shadow: KnexClient | KyselyClient;\n apply: (KnexClient | KyselyClient)[];\n };\n\n constructor(options: MigratorOptions) {\n this.mode = options.mode;\n\n if (this.mode === \"dev\") {\n const devDB = DB.getClient(\"development_master\");\n const testDB = DB.getClient(\"test\");\n const fixtureLocalDB = DB.getClient(\"fixture_local\");\n\n const uniqConfigs = DB.getUniqueConfigs([\n \"development_master\",\n \"test\",\n \"fixture_local\",\n \"fixture_remote\",\n ]);\n const applyDBs = [devDB, testDB, fixtureLocalDB];\n if (uniqConfigs.length === 4) {\n const fixtureRemoteDB = DB.getClient(\"fixture_remote\");\n applyDBs.push(fixtureRemoteDB);\n }\n\n this.targets = {\n compare: devDB,\n pending: devDB,\n shadow: testDB,\n apply: applyDBs,\n };\n } else if (this.mode === \"deploy\") {\n const productionDB = DB.getClient(\"production_master\");\n const testDB = DB.getClient(\"test\");\n\n this.targets = {\n pending: productionDB,\n shadow: testDB,\n apply: [productionDB],\n };\n } else {\n throw new Error(`잘못된 모드 ${this.mode} 입력`);\n }\n }\n\n async getMigrationCodes(): Promise<{\n normal: MigrationCode[];\n onlyTs: MigrationCode[];\n onlyJs: MigrationCode[];\n }> {\n const srcMigrationsDir = `${Sonamu.apiRootPath}/src/migrations`;\n const distMigrationsDir = `${Sonamu.apiRootPath}/dist/migrations`;\n\n if (fs.existsSync(srcMigrationsDir) === false) {\n fs.mkdirSync(srcMigrationsDir, {\n recursive: true,\n });\n }\n if (fs.existsSync(distMigrationsDir) === false) {\n fs.mkdirSync(distMigrationsDir, {\n recursive: true,\n });\n }\n const srcMigrations = fs\n .readdirSync(srcMigrationsDir)\n .filter((f) => f.endsWith(\".ts\"))\n .map((f) => f.split(\".\")[0]);\n const distMigrations = fs\n .readdirSync(distMigrationsDir)\n .filter((f) => f.endsWith(\".js\"))\n .map((f) => f.split(\".\")[0]);\n\n const normal = _.intersection(srcMigrations, distMigrations)\n .map((filename) => {\n return {\n name: filename,\n path: path.join(srcMigrationsDir, filename) + \".ts\",\n };\n })\n .sort((a, b) => (a > b ? 1 : -1));\n\n const onlyTs = _.difference(srcMigrations, distMigrations).map(\n (filename) => {\n return {\n name: filename,\n path: path.join(srcMigrationsDir, filename) + \".ts\",\n };\n }\n );\n\n const onlyJs = _.difference(distMigrations, srcMigrations).map(\n (filename) => {\n return {\n name: filename,\n path: path.join(distMigrationsDir, filename) + \".js\",\n };\n }\n );\n\n return {\n normal,\n onlyTs,\n onlyJs,\n };\n }\n\n async getStatus(): Promise<MigrationStatus> {\n const { normal, onlyTs, onlyJs } = await this.getMigrationCodes();\n if (onlyTs.length > 0) {\n console.debug({ onlyTs });\n throw new ServiceUnavailableException(\n `There are un-compiled TS migration files.\\nPlease compile them first.\\n\\n${onlyTs\n .map((f) => f.name)\n .join(\"\\n\")}`\n );\n }\n if (onlyJs.length > 0) {\n console.debug({ onlyJs });\n await Promise.all(\n onlyJs.map(async (f) => {\n execSync(\n `rm -f ${f.path.replace(\"/src/\", \"/dist/\").replace(\".ts\", \".js\")}`\n );\n })\n );\n }\n\n const connKeys = Object.keys(DB.fullConfig).filter(\n (key) => key.endsWith(\"_slave\") === false\n ) as (keyof typeof DB.fullConfig)[];\n\n const statuses = await Promise.all(\n connKeys.map(async (connKey) => {\n const tConn = DB.getClient(connKey);\n\n const status = await (async () => {\n try {\n return await tConn.status();\n } catch (err) {\n console.error(err);\n return \"error\";\n }\n })();\n const pending = await (async () => {\n try {\n return await tConn.getMigrations();\n } catch (err) {\n console.error(err);\n return [];\n }\n })();\n const currentVersion = await (async () => {\n // try {\n // return tConn.migrate.currentVersion();\n // } catch (err) {\n return \"error\";\n // }\n })();\n\n const info = tConn.connectionInfo;\n\n await tConn.destroy();\n\n return {\n name: connKey.replace(\"_master\", \"\"),\n connKey,\n connString: `mysql2://${info.user ?? \"\"}@${\n info.host\n }:${info.port}/${info.database}` as ConnString,\n currentVersion,\n status,\n pending,\n };\n })\n );\n\n const preparedCodes: GenMigrationCode[] = await (async () => {\n const status0conn = statuses.find((status) => status.status === 0);\n if (status0conn === undefined) {\n return [];\n }\n\n const compareDBconn = DB.getClient(status0conn.connKey);\n const genCodes = await this.compareMigrations(compareDBconn);\n\n await compareDBconn.destroy();\n\n return genCodes;\n })();\n\n return {\n conns: statuses,\n codes: normal,\n preparedCodes,\n };\n /*\n TS/JS 코드 컴파일 상태 확인\n 1. 원본 파일 없는 JS파일이 존재하는 경우: 삭제\n 2. 컴파일 되지 않은 TS파일이 존재하는 경우: throw 쳐서 데브 서버 오픈 요청\n \n DB 마이그레이션 상태 확인\n 1. 전체 DB설정에 대해서 현재 마이그레이션 상태 확인\n - connKey: string\n - status: number\n - currentVersion: string\n - list: { file: string; directory: string }[]\n \n */\n }\n\n async runAction(\n action: \"latest\" | \"rollback\",\n targets: string[]\n ): Promise<\n {\n connKey: string;\n batchNo: number;\n applied: string[];\n }[]\n > {\n // get uniq knex configs\n const configs = DB.getUniqueConfigs(targets as any);\n\n // get connections\n const conns = await Promise.all(\n configs.map(async (config) => ({\n connKey: config.connKey,\n db: DB.getClient(config.connKey),\n }))\n );\n\n // action\n // TODO: 마이그레이션 결과 리턴값 정리해야됨(kysely/knex)\n const result = await (async () => {\n switch (action) {\n case \"latest\":\n return Promise.all(\n conns.map(async ({ connKey, db }) => {\n const [batchNo, applied] = await db.migrate();\n return {\n connKey,\n batchNo,\n applied,\n };\n })\n );\n case \"rollback\":\n return Promise.all(\n conns.map(async ({ connKey, db }) => {\n const [batchNo, applied] = await db.rollback();\n return {\n connKey,\n batchNo,\n applied,\n };\n })\n );\n }\n })();\n\n // destroy\n await Promise.all(\n conns.map(({ db }) => {\n return db.destroy();\n })\n );\n\n return result;\n }\n\n async delCodes(codeNames: string[]): Promise<number> {\n const { conns } = await this.getStatus();\n if (\n conns.some((conn) => {\n return codeNames.some(\n (codeName) => conn.pending.includes(codeName) === false\n );\n })\n ) {\n throw new Error(\n \"You cannot delete a migration file if there is already applied.\"\n );\n }\n\n const delFiles = codeNames\n .map((codeName) => [\n `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`,\n `${Sonamu.apiRootPath}/dist/migrations/${codeName}.js`,\n ])\n .flat();\n\n const res = await Promise.all(\n delFiles.map((delFile) => {\n if (fs.existsSync(delFile)) {\n console.log(chalk.red(`DELETE: ${delFile}`));\n fs.unlinkSync(delFile);\n return delFiles.includes(\".ts\") ? 1 : 0;\n }\n return 0;\n })\n );\n return _.sum(res);\n }\n\n async generatePreparedCodes(): Promise<number> {\n const { preparedCodes } = await this.getStatus();\n if (preparedCodes.length === 0) {\n console.log(chalk.green(\"\\n현재 모두 싱크된 상태입니다.\"));\n return 0;\n }\n\n // 실제 코드 생성\n const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;\n preparedCodes\n .filter((pcode) => pcode.formatted)\n .map((pcode, index) => {\n const dateTag = DateTime.local()\n .plus({ seconds: index })\n .toFormat(\"yyyyMMddHHmmss\");\n const filePath = `${migrationsDir}/${dateTag}_${pcode.title}.ts`;\n fs.writeFileSync(filePath, pcode.formatted!);\n console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));\n });\n\n return preparedCodes.length;\n }\n\n async clearPendingList(): Promise<void> {\n const pendingList = await this.targets.pending.getMigrations();\n const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;\n const delList = pendingList.map((df) => {\n return path.join(migrationsDir, `${df}.ts`);\n });\n for (let p of delList) {\n if (fs.existsSync(p)) {\n fs.unlinkSync(p);\n }\n }\n await this.cleanUpDist(true);\n }\n\n async check(): Promise<void> {\n const codes = await this.compareMigrations(this.targets.compare!);\n if (codes.length === 0) {\n console.log(chalk.green(\"\\n현재 모두 싱크된 상태입니다.\"));\n return;\n }\n\n // 현재 생성된 코드 표기\n console.table(codes, [\"type\", \"title\"]);\n console.log(codes[0]);\n }\n\n async run(): Promise<void> {\n // pending 마이그레이션 확인\n const pendingList = await this.targets.pending.getMigrations();\n if (pendingList.length > 0) {\n console.log(\n chalk.red(\"pending 된 마이그레이션이 존재합니다.\"),\n pendingList.map((pending: any) => pending.file)\n );\n\n // pending이 있는 경우 Shadow DB 테스트 진행 여부 컨펌\n const answer = await prompts({\n type: \"confirm\",\n name: \"value\",\n message: \"Shadow DB 테스트를 진행하시겠습니까?\",\n initial: true,\n });\n if (answer.value === false) {\n return;\n }\n\n console.time(chalk.blue(\"Migrator - runShadowTest\"));\n await this.runShadowTest();\n console.timeEnd(chalk.blue(\"Migrator - runShadowTest\"));\n await Promise.all(\n this.targets.apply.map(async (applyDb) => {\n const info = applyDb.connectionInfo;\n const label = chalk.green(`APPLIED ${info.host} ${info.database}`);\n console.time(label);\n await applyDb.migrate();\n console.timeEnd(label);\n })\n );\n }\n\n // Entity-DB간 비교하여 코드 생성 리턴\n const codes = await this.compareMigrations(this.targets.compare!);\n if (codes.length === 0) {\n console.log(chalk.green(\"\\n현재 모두 싱크된 상태입니다.\"));\n return;\n }\n\n // 현재 생성된 코드 표기\n console.table(codes, [\"type\", \"title\"]);\n\n /* DEBUG: 디버깅용 코드\n codes.map((code) => console.log(code.formatted));\n process.exit();\n */\n\n // 실제 파일 생성 프롬프트\n const answer = await prompts({\n type: \"confirm\",\n name: \"value\",\n message: \"마이그레이션 코드를 생성하시겠습니까?\",\n initial: false,\n });\n if (answer.value === false) {\n return;\n }\n\n // 실제 코드 생성\n const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;\n codes\n .filter((code) => code.formatted)\n .map((code, index) => {\n const dateTag = DateTime.local()\n .plus({ seconds: index })\n .toFormat(\"yyyyMMddHHmmss\");\n const filePath = `${migrationsDir}/${dateTag}_${code.title}.ts`;\n fs.writeFileSync(filePath, code.formatted!);\n console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));\n });\n }\n\n async rollback() {\n console.time(chalk.red(\"rollback:\"));\n const rollbackAllResult = await Promise.all(\n this.targets.apply.map(async (db) => {\n // await db.migrate.forceFreeMigrationsLock();\n return db.rollback();\n })\n );\n console.dir({ rollbackAllResult }, { depth: null });\n console.timeEnd(chalk.red(\"rollback:\"));\n }\n\n async cleanUpDist(force: boolean = false): Promise<void> {\n const files = ([\"src\", \"dist\"] as const).reduce(\n (r, which) => {\n const migrationPath = path.join(\n Sonamu.apiRootPath,\n which,\n \"migrations\"\n );\n if (fs.existsSync(migrationPath) === false) {\n fs.mkdirSync(migrationPath, {\n recursive: true,\n });\n }\n const files = fs\n .readdirSync(migrationPath)\n .filter((filename) => filename.startsWith(\".\") === false);\n r[which] = files;\n return r;\n },\n {\n src: [] as string[],\n dist: [] as string[],\n }\n );\n\n const diffOnSrc = _.differenceBy(\n files.src,\n files.dist,\n (filename) => filename.split(\".\")[0]\n );\n if (diffOnSrc.length > 0) {\n throw new Error(\n \"컴파일 되지 않은 파일이 있습니다.\\n\" + diffOnSrc.join(\"\\n\")\n );\n }\n\n const diffOnDist = _.differenceBy(\n files.dist,\n files.src,\n (filename) => filename.split(\".\")[0]\n );\n if (diffOnDist.length > 0) {\n console.log(chalk.red(\"원본 ts파일을 찾을 수 없는 js파일이 있습니다.\"));\n console.log(diffOnDist);\n\n if (!force) {\n const answer = await prompts({\n type: \"confirm\",\n name: \"value\",\n message: \"삭제를 진행하시겠습니까?\",\n initial: true,\n });\n if (answer.value === false) {\n return;\n }\n }\n\n const filesToRm = diffOnDist.map((filename) => {\n return path.join(Sonamu.apiRootPath, \"dist\", \"migrations\", filename);\n });\n filesToRm.map((filePath) => {\n fs.unlinkSync(filePath);\n });\n console.log(chalk.green(`${filesToRm.length}건 삭제되었습니다!`));\n }\n }\n\n async runShadowTest(): Promise<\n {\n connKey: string;\n batchNo: number;\n applied: string[];\n }[]\n > {\n // ShadowDB 생성 후 테스트 진행\n const tdb = DB.getClient(\"test\");\n const tdbConn = tdb.connectionInfo;\n const shadowDatabase = tdbConn.database + \"__migration_shadow\";\n const tmpSqlPath = `/tmp/${shadowDatabase}.sql`;\n\n // 테스트DB 덤프 후 Database명 치환\n console.log(\n chalk.magenta(`${tdbConn.database}의 데이터 ${tmpSqlPath}로 덤프`)\n );\n execSync(\n `mysqldump -h${tdbConn.host} -P${tdbConn.port} -u${tdbConn.user} -p'${tdbConn.password}' ${tdbConn.database} --single-transaction --no-create-db --triggers > ${tmpSqlPath};`\n );\n execSync(\n `sed -i'' -e 's/\\`${tdbConn.database}\\`/\\`${shadowDatabase}\\`/g' ${tmpSqlPath};`\n );\n\n // 기존 ShadowDB 리셋\n console.log(chalk.magenta(`${shadowDatabase} 리셋`));\n await tdb.raw(`DROP DATABASE IF EXISTS \\`${shadowDatabase}\\`;`);\n await tdb.raw(`CREATE DATABASE \\`${shadowDatabase}\\`;`);\n\n // ShadowDB 테이블 + 데이터 생성\n console.log(chalk.magenta(`${shadowDatabase} 데이터베이스 생성`));\n execSync(\n `mysql -h${tdbConn.host} -P${tdbConn.port} -u${tdbConn.user} -p'${tdbConn.password}' ${shadowDatabase} < ${tmpSqlPath};`\n );\n\n // shadow db 테스트 진행\n try {\n await tdb.raw(`USE \\`${shadowDatabase}\\`;`);\n const [batchNo, applied] = await tdb.migrate();\n console.log(chalk.green(\"Shadow DB 테스트에 성공했습니다!\"), {\n batchNo,\n applied,\n });\n\n // 생성한 Shadow DB 삭제\n console.log(chalk.magenta(`${shadowDatabase} 삭제`));\n await tdb.raw(`DROP DATABASE IF EXISTS \\`${shadowDatabase}\\`;`);\n\n return [\n {\n connKey: \"shadow\",\n batchNo,\n applied,\n },\n ];\n } catch (e) {\n console.error(e);\n throw new ServiceUnavailableException(\"Shadow DB 테스트 진행 중 에러\");\n } finally {\n await tdb.destroy();\n }\n }\n\n async resetAll() {\n const answer = await prompts({\n type: \"confirm\",\n name: \"value\",\n message: \"모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제하시겠습니까?\",\n initial: false,\n });\n if (answer.value === false) {\n return;\n }\n\n console.time(chalk.red(\"rollback-all:\"));\n const rollbackAllResult = await Promise.all(\n this.targets.apply.map(async (db) => {\n // await db.migrate.forceFreeMigrationsLock();\n return db.rollbackAll();\n })\n );\n console.log({ rollbackAllResult });\n console.timeEnd(chalk.red(\"rollback-all:\"));\n\n const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;\n console.time(chalk.red(\"delete migration files\"));\n execSync(`rm -f ${migrationsDir}/*`);\n execSync(`rm -f ${migrationsDir.replace(\"/src/\", \"/dist/\")}/*`);\n console.timeEnd(chalk.red(\"delete migration files\"));\n }\n\n async compareMigrations(\n compareDB: KnexClient | KyselyClient\n ): Promise<GenMigrationCode[]> {\n // Entity 순회하여 싱크\n const entityIds = EntityManager.getAllIds();\n\n // 조인테이블 포함하여 Entity에서 MigrationSet 추출\n const entitySetsWithJoinTable = entityIds\n .filter((entityId) => {\n const entity = EntityManager.get(entityId);\n return entity.props.length > 0;\n })\n .map((entityId) => {\n const entity = EntityManager.get(entityId);\n return this.getMigrationSetFromEntity(entity);\n });\n\n // 조인테이블만 추출\n const joinTablesWithDup = entitySetsWithJoinTable\n .map((entitySet) => entitySet.joinTables)\n .flat();\n // 중복 제거 (중복인 경우 indexes를 병합)\n const joinTables = Object.values(\n _.groupBy(joinTablesWithDup, (jt) => jt.table)\n ).map((tables) => {\n if (tables.length === 1) {\n return tables[0];\n }\n return {\n ...tables[0],\n indexes: _.uniqBy(\n tables.flatMap((t) => t.indexes),\n (index) => [index.type, ...index.columns.sort()].join(\"-\")\n ),\n };\n });\n\n // 조인테이블 포함하여 MigrationSet 배열\n const entitySets: MigrationSet[] = [\n ...entitySetsWithJoinTable,\n ...joinTables,\n ];\n\n const codes: GenMigrationCode[] = (\n await Promise.all(\n entitySets.map(async (entitySet) => {\n const dbSet = await this.getMigrationSetFromDB(\n compareDB,\n entitySet.table\n );\n if (dbSet === null) {\n // 기존 테이블 없음, 새로 테이블 생성\n return [\n await DB.generator.generateCreateCode_ColumnAndIndexes(\n entitySet.table,\n entitySet.columns,\n entitySet.indexes\n ),\n ...(await DB.generator.generateCreateCode_Foreign(\n entitySet.table,\n entitySet.foreigns\n )),\n ];\n }\n\n // 기존 테이블 존재하는 케이스\n const alterCodes: (GenMigrationCode | GenMigrationCode[] | null)[] =\n await Promise.all(\n ([\"columnsAndIndexes\", \"foreigns\"] as const).map((key) => {\n // 배열 원소의 순서가 달라서 불일치가 발생하는걸 방지하기 위해 각 항목별로 정렬 처리 후 비교\n if (key === \"columnsAndIndexes\") {\n const replaceColumnDefaultTo = (col: MigrationColumn) => {\n // float인 경우 기본값을 0으로 지정하는 경우 \"0.00\"으로 변환되는 케이스 대응\n if (\n col.type === \"float\" &&\n col.defaultTo &&\n String(col.defaultTo).includes('\"') === false\n ) {\n col.defaultTo = `\"${Number(col.defaultTo).toFixed(\n col.scale ?? 2\n )}\"`;\n }\n // string인 경우 기본값이 빈 스트링인 경우 대응\n if (col.type === \"string\" && col.defaultTo === \"\") {\n col.defaultTo = '\"\"';\n }\n return col;\n };\n const entityColumns = _.sortBy(\n entitySet.columns,\n (a) => a.name\n ).map(replaceColumnDefaultTo);\n const dbColumns = _.sortBy(dbSet.columns, (a) => a.name).map(\n replaceColumnDefaultTo\n );\n\n /* 디버깅용 코드, 특정 컬럼에서 불일치 발생할 때 확인\n const entityColumn = entitySet.columns.find(\n (col) => col.name === \"price_krw\"\n );\n const dbColumn = dbSet.columns.find(\n (col) => col.name === \"price_krw\"\n );\n console.debug({ entityColumn, dbColumn });\n */\n\n const entityIndexes = _.sortBy(entitySet.indexes, (a) =>\n [\n a.type,\n ...a.columns.sort((c1, c2) => (c1 > c2 ? 1 : -1)),\n ].join(\"-\")\n );\n const dbIndexes = _.sortBy(dbSet.indexes, (a) =>\n [\n a.type,\n ...a.columns.sort((c1, c2) => (c1 > c2 ? 1 : -1)),\n ].join(\"-\")\n );\n\n const isEqualColumns = equal(entityColumns, dbColumns);\n const isEqualIndexes = equal(entityIndexes, dbIndexes);\n if (isEqualColumns && isEqualIndexes) {\n return null;\n } else {\n // this.showMigrationSet(\"Entity\", entitySet);\n // this.showMigrationSet(\"DB\", dbSet);\n return DB.generator.generateAlterCode_ColumnAndIndexes(\n entitySet.table,\n entityColumns,\n entityIndexes,\n dbColumns,\n dbIndexes\n );\n }\n } else {\n const replaceNoActionOnMySQL = (f: MigrationForeign) => {\n // MySQL에서 RESTRICT와 NO ACTION은 동일함\n const { onDelete, onUpdate } = f;\n return {\n ...f,\n onUpdate:\n onUpdate === \"RESTRICT\" ? \"NO ACTION\" : onUpdate,\n onDelete:\n onDelete === \"RESTRICT\" ? \"NO ACTION\" : onDelete,\n };\n };\n\n const entityForeigns = _.sortBy(entitySet.foreigns, (a) =>\n [a.to, ...a.columns].join(\"-\")\n ).map((f) => replaceNoActionOnMySQL(f));\n const dbForeigns = _.sortBy(dbSet.foreigns, (a) =>\n [a.to, ...a.columns].join(\"-\")\n ).map((f) => replaceNoActionOnMySQL(f));\n\n if (equal(entityForeigns, dbForeigns) === false) {\n // console.dir(\n // {\n // debugOn: \"foreign\",\n // table: entitySet.table,\n // entityForeigns,\n // dbForeigns,\n // },\n // { depth: null }\n // );\n return DB.generator.generateAlterCode_Foreigns(\n entitySet.table,\n entityForeigns,\n dbForeigns\n );\n }\n }\n return null;\n })\n );\n if (alterCodes.every((alterCode) => alterCode === null)) {\n return null;\n } else {\n return alterCodes.filter((alterCode) => alterCode !== null).flat();\n }\n })\n )\n )\n .flat()\n .filter((code) => code !== null) as GenMigrationCode[];\n\n /*\n normal 타입이 앞으로, foreign 이 뒤로\n */\n codes.sort((codeA, codeB) => {\n if (codeA.type === \"foreign\" && codeB.type == \"normal\") {\n return 1;\n } else if (codeA.type === \"normal\" && codeB.type === \"foreign\") {\n return -1;\n } else {\n return 0;\n }\n });\n\n return codes;\n }\n\n /*\n 기존 테이블 정보 읽어서 MigrationSet 형식으로 리턴\n */\n async getMigrationSetFromDB(\n compareDB: KnexClient | KyselyClient,\n table: string\n ): Promise<MigrationSet | null> {\n let dbColumns: DBColumn[], dbIndexes: DBIndex[], dbForeigns: DBForeign[];\n try {\n [dbColumns, dbIndexes, dbForeigns] = await this.readTable(\n compareDB,\n table\n );\n } catch (e: unknown) {\n if (isKnexError(e) && e.code === \"ER_NO_SUCH_TABLE\") {\n return null;\n }\n console.error(e);\n return null;\n }\n\n const columns: MigrationColumn[] = dbColumns.map((dbColumn) => {\n const dbColType = this.resolveDBColType(dbColumn.Type, dbColumn.Field);\n return {\n name: dbColumn.Field,\n nullable: dbColumn.Null !== \"NO\",\n ...dbColType,\n ...(() => {\n if (dbColumn.Default !== null) {\n return {\n defaultTo: dbColumn.Default,\n };\n }\n return {};\n })(),\n };\n });\n\n const dbIndexesGroup = _.groupBy(\n dbIndexes.filter(\n (dbIndex) =>\n dbIndex.Key_name !== \"PRIMARY\" &&\n !dbForeigns.find(\n (dbForeign) => dbForeign.keyName === dbIndex.Key_name\n )\n ),\n (dbIndex) => dbIndex.Key_name\n );\n\n // indexes 처리\n const indexes: MigrationIndex[] = Object.keys(dbIndexesGroup).map(\n (keyName) => {\n const currentIndexes = dbIndexesGroup[keyName];\n return {\n type: currentIndexes[0].Non_unique === 1 ? \"index\" : \"unique\",\n columns: currentIndexes.map(\n (currentIndex) => currentIndex.Column_name\n ),\n };\n }\n );\n // console.log(table);\n // console.table(dbIndexes);\n // console.table(dbForeigns);\n\n // foreigns 처리\n const foreigns: MigrationForeign[] = dbForeigns.map((dbForeign) => {\n return {\n columns: [dbForeign.from],\n to: `${dbForeign.referencesTable}.${dbForeign.referencesField}`,\n onUpdate: dbForeign.onUpdate as RelationOn,\n onDelete: dbForeign.onDelete as RelationOn,\n };\n });\n\n return {\n table,\n columns,\n indexes,\n foreigns,\n };\n }\n\n resolveDBColType(\n colType: string,\n colField: string\n ): Pick<\n MigrationColumn,\n \"type\" | \"unsigned\" | \"length\" | \"precision\" | \"scale\"\n > {\n let [rawType, unsigned] = colType.split(\" \");\n const matched = rawType.match(/\\(([0-9]+)\\)/);\n let length;\n if (matched !== null && matched[1]) {\n rawType = rawType.replace(/\\(([0-9]+)\\)/, \"\");\n length = parseInt(matched[1]);\n }\n\n if (rawType === \"char\" && colField === \"uuid\") {\n return {\n type: \"uuid\",\n };\n }\n\n switch (rawType) {\n case \"int\":\n return {\n type: \"integer\",\n unsigned: unsigned === \"unsigned\",\n };\n case \"varchar\":\n // case \"char\":\n return {\n type: \"string\",\n ...(length !== undefined && {\n length,\n }),\n };\n case \"text\":\n case \"mediumtext\":\n case \"longtext\":\n case \"timestamp\":\n case \"json\":\n case \"date\":\n case \"time\":\n return {\n type: rawType,\n };\n case \"datetime\":\n return {\n type: \"datetime\",\n };\n case \"tinyint\":\n return {\n type: \"boolean\",\n };\n default:\n // decimal 처리\n if (rawType.startsWith(\"decimal\")) {\n const [, precision, scale] =\n rawType.match(/decimal\\(([0-9]+),([0-9]+)\\)/) ?? [];\n return {\n type: \"decimal\",\n precision: parseInt(precision),\n scale: parseInt(scale),\n ...(unsigned === \"unsigned\" && {\n unsigned: true,\n }),\n };\n } else if (rawType.startsWith(\"float\")) {\n const [, precision, scale] =\n rawType.match(/float\\(([0-9]+),([0-9]+)\\)/) ?? [];\n return {\n type: \"float\",\n precision: parseInt(precision),\n scale: parseInt(scale),\n ...(unsigned === \"unsigned\" && {\n unsigned: true,\n }),\n };\n }\n throw new Error(`resolve 불가능한 DB컬럼 타입 ${colType} ${rawType}`);\n }\n }\n\n /*\n 기존 테이블 읽어서 cols, indexes 반환\n */\n async readTable(\n compareDB: KnexClient | KyselyClient,\n tableName: string\n ): Promise<[DBColumn[], DBIndex[], DBForeign[]]> {\n // 테이블 정보\n try {\n const _cols = await compareDB.raw<DBColumn>(\n `SHOW FIELDS FROM ${tableName}`\n );\n\n const cols = _cols.map((col) => ({\n ...col,\n // Default 값은 숫자나 MySQL Expression이 아닌 경우 \"\"로 감싸줌\n ...(col.Default !== null && {\n Default:\n col.Default.replace(/[0-9]+/g, \"\").length > 0 &&\n col.Extra !== \"DEFAULT_GENERATED\"\n ? `\"${col.Default}\"`\n : col.Default,\n }),\n }));\n\n const indexes = await compareDB.raw<DBIndex>(\n `SHOW INDEX FROM ${tableName}`\n );\n const [row] = await compareDB.raw<{\n \"Create Table\": string;\n }>(`SHOW CREATE TABLE ${tableName}`);\n const ddl = row[\"Create Table\"];\n const matched = ddl.match(/CONSTRAINT .+/g);\n const foreignKeys = (matched ?? []).map((line: string) => {\n // 해당 라인을 정규식으로 파싱\n const matched = line.match(\n /CONSTRAINT `(.+)` FOREIGN KEY \\(`(.+)`\\) REFERENCES `(.+)` \\(`(.+)`\\)( ON [A-Z ]+)*/\n );\n if (!matched) {\n throw new Error(`인식할 수 없는 FOREIGN KEY CONSTRAINT ${line}`);\n }\n const [, keyName, from, referencesTable, referencesField, onClause] =\n matched;\n // console.debug({ tableName, line, onClause });\n\n const [onUpdateFull, _onUpdate] =\n (onClause ?? \"\").match(/ON UPDATE ([A-Z ]+)$/) ?? [];\n const onUpdate = _onUpdate ?? \"NO ACTION\";\n\n const onDelete =\n (onClause ?? \"\")\n .replace(onUpdateFull ?? \"\", \"\")\n .match(/ON DELETE ([A-Z ]+)/)?.[1]\n ?.trim() ?? \"NO ACTION\";\n\n return {\n keyName,\n from,\n referencesTable,\n referencesField,\n onDelete,\n onUpdate,\n };\n });\n return [cols, indexes, foreignKeys];\n } catch (e) {\n throw e;\n }\n }\n\n /*\n Entity 내용 읽어서 MigrationSetAndJoinTable 추출\n */\n getMigrationSetFromEntity(entity: Entity): MigrationSetAndJoinTable {\n const migrationSet: MigrationSetAndJoinTable = entity.props.reduce(\n (r, prop) => {\n // virtual 필드 제외\n if (isVirtualProp(prop)) {\n return r;\n }\n // HasMany 케이스는 아무 처리도 하지 않음\n if (isHasManyRelationProp(prop)) {\n return r;\n }\n\n // 일반 컬럼\n if (!isRelationProp(prop)) {\n // type resolve\n let type: KnexColumnType;\n if (isTextProp(prop)) {\n type = prop.textType;\n } else if (isEnumProp(prop)) {\n type = \"string\";\n } else {\n type = prop.type as KnexColumnType;\n }\n\n const column = {\n name: prop.name,\n type,\n ...(isIntegerProp(prop) && { unsigned: prop.unsigned === true }),\n ...((isStringProp(prop) || isEnumProp(prop)) && {\n length: prop.length,\n }),\n nullable: prop.nullable === true,\n ...(() => {\n if (prop.dbDefault !== undefined) {\n return {\n defaultTo: prop.dbDefault,\n };\n }\n return {};\n })(),\n // FIXME: float(N, M) deprecated\n // Decimal, Float 타입의 경우 precision, scale 추가\n ...((isDecimalProp(prop) || isFloatProp(prop)) && {\n precision: prop.precision ?? 8,\n scale: prop.scale ?? 2,\n }),\n };\n\n r.columns.push(column);\n }\n\n if (isManyToManyRelationProp(prop)) {\n // ManyToMany 케이스\n const relMd = EntityManager.get(prop.with);\n const [table1, table2] = prop.joinTable.split(\"__\");\n const join = {\n from: `${entity.table}.id`,\n through: {\n from: `${prop.joinTable}.${inflection.singularize(table1)}_id`,\n to: `${prop.joinTable}.${inflection.singularize(table2)}_id`,\n onUpdate: prop.onUpdate,\n onDelete: prop.onDelete,\n },\n to: `${relMd.table}.id`,\n };\n const through = join.through;\n const fields = [through.from, through.to];\n r.joinTables.push({\n table: through.from.split(\".\")[0],\n indexes: [\n {\n type: \"unique\",\n columns: [\"uuid\"],\n },\n // 조인 테이블에 걸린 인덱스 찾아와서 연결\n ...entity.indexes\n .filter((index) =>\n index.columns.find((col) =>\n col.includes(prop.joinTable + \".\")\n )\n )\n .map((index) => ({\n ...index,\n columns: index.columns.map((col) =>\n col.replace(prop.joinTable + \".\", \"\")\n ),\n })),\n ],\n columns: [\n {\n name: \"id\",\n type: \"integer\",\n nullable: false,\n unsigned: true,\n },\n ...fields.map((field) => {\n return {\n name: field.split(\".\")[1],\n type: \"integer\",\n nullable: false,\n unsigned: true,\n } as MigrationColumn;\n }),\n {\n name: \"uuid\",\n nullable: true,\n type: \"uuid\",\n },\n ],\n foreigns: fields.map((field) => {\n // 현재 필드가 어떤 테이블에 속하는지 판단\n const col = field.split(\".\")[1];\n const to = (() => {\n if (\n inflection.singularize(join.to.split(\".\")[0]) + \"_id\" ===\n col\n ) {\n return join.to;\n } else {\n return join.from;\n }\n })();\n return {\n columns: [col],\n to,\n onUpdate: through.onUpdate,\n onDelete: through.onDelete,\n };\n }),\n });\n return r;\n } else if (\n isBelongsToOneRelationProp(prop) ||\n (isOneToOneRelationProp(prop) && prop.hasJoinColumn)\n ) {\n // -OneRelation 케이스\n const idColumnName = prop.name + \"_id\";\n r.columns.push({\n name: idColumnName,\n type: \"integer\",\n unsigned: true,\n nullable: prop.nullable ?? false,\n });\n r.foreigns.push({\n columns: [idColumnName],\n to: `${inflection.underscore(inflection.pluralize(prop.with)).toLowerCase()}.id`,\n onUpdate: prop.onUpdate,\n onDelete: prop.onDelete,\n });\n }\n\n return r;\n },\n {\n table: entity.table,\n columns: [] as MigrationColumn[],\n indexes: [] as MigrationIndex[],\n foreigns: [] as MigrationForeign[],\n joinTables: [] as MigrationJoinTable[],\n }\n );\n\n // indexes\n migrationSet.indexes = entity.indexes.filter((index) =>\n index.columns.find((col) => col.includes(\".\") === false)\n );\n\n // uuid\n migrationSet.columns = migrationSet.columns.concat({\n name: \"uuid\",\n nullable: true,\n type: \"uuid\",\n } as MigrationColumn);\n migrationSet.indexes = migrationSet.indexes.concat({\n type: \"unique\",\n columns: [\"uuid\"],\n } as MigrationIndex);\n\n return migrationSet;\n }\n\n /*\n 마이그레이션 컬럼 배열 비교용 코드\n */\n showMigrationSet(which: \"Entity\" | \"DB\", migrationSet: MigrationSet): void {\n const { columns, indexes, foreigns } = migrationSet;\n const styledChalk =\n which === \"Entity\" ? chalk.bgGreen.black : chalk.bgBlue.black;\n console.log(\n styledChalk(\n `${which} ${migrationSet.table} Columns\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`\n )\n );\n console.table(\n columns.map((column) => {\n return {\n ..._.pick(column, [\n \"name\",\n \"type\",\n \"nullable\",\n \"unsigned\",\n \"length\",\n \"defaultTo\",\n \"precision\",\n \"scale\",\n ]),\n };\n }),\n [\n \"name\",\n \"type\",\n \"nullable\",\n \"unsigned\",\n \"length\",\n \"defaultTo\",\n \"precision\",\n \"scale\",\n ]\n );\n\n if (indexes.length > 0) {\n console.log(\n styledChalk(\n `${which} ${migrationSet.table} Indexes\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`\n )\n );\n console.table(\n indexes.map((index) => {\n return {\n ..._.pick(index, [\"type\", \"columns\", \"name\"]),\n };\n })\n );\n }\n\n if (foreigns.length > 0) {\n console.log(\n chalk.bgMagenta.black(\n `${which} ${migrationSet.table} Foreigns\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`\n )\n );\n console.table(\n foreigns.map((foreign) => {\n return {\n ..._.pick(foreign, [\"columns\", \"to\", \"onUpdate\", \"onDelete\"]),\n };\n })\n );\n }\n }\n\n async destroy(): Promise<void> {\n await Promise.all(\n this.targets.apply.map((db) => {\n return db.destroy();\n })\n );\n }\n}\n\ntype DBColumn = {\n Field: string;\n Type: string;\n Null: string;\n Key: string;\n Default: string | null;\n Extra: string;\n};\ntype DBIndex = {\n Table: string;\n Non_unique: number;\n Key_name: string;\n Seq_in_index: number;\n Column_name: string;\n Collation: string | null;\n Cardinality: number | null;\n Sub_part: number | null;\n Packed: string | null;\n Null: string;\n Index_type: string;\n Comment: string;\n Index_comment: string;\n Visible: string;\n Expression: string | null;\n};\ntype DBForeign = {\n keyName: string;\n from: string;\n referencesTable: string;\n referencesField: string;\n onDelete: string;\n onUpdate: string;\n};\n","import chalk from \"chalk\";\nimport _ from \"lodash\";\nimport { Sonamu } from \"../api\";\nimport { EntityManager } from \"../entity/entity-manager\";\nimport {\n EntityProp,\n FixtureImportResult,\n FixtureRecord,\n FixtureSearchOptions,\n ManyToManyRelationProp,\n isBelongsToOneRelationProp,\n isHasManyRelationProp,\n isManyToManyRelationProp,\n isOneToOneRelationProp,\n isRelationProp,\n isVirtualProp,\n} from \"../types/types\";\nimport { Entity } from \"../entity/entity\";\nimport inflection from \"inflection\";\nimport { readFileSync, writeFileSync } from \"fs\";\nimport { RelationGraph } from \"./_relation-graph\";\nimport { SonamuDBConfig, WhereClause } from \"../database/types\";\nimport { DB } from \"../database/db\";\nimport { KyselyClient } from \"../database/drivers/kysely/client\";\nimport { KnexClient } from \"../database/drivers/knex/client\";\n\nexport class FixtureManagerClass {\n private relationGraph = new RelationGraph();\n\n init() {\n DB.testInit();\n }\n\n async cleanAndSeed(usingTables?: string[]) {\n const tableNames = await (async () => {\n if (usingTables) {\n return usingTables;\n }\n\n const tables = await DB.tdb.raw<{ Name: string }>(\n `SHOW TABLE STATUS WHERE Engine IS NOT NULL`\n );\n return tables.map((tableInfo: any) => tableInfo[\"Name\"] as string);\n })();\n\n await DB.tdb.raw(`SET FOREIGN_KEY_CHECKS = 0`);\n for await (let tableName of tableNames) {\n if (tableName == \"migrations\") {\n continue;\n }\n\n const [fdbChecksumRow] = await DB.fdb.raw<{ Checksum: string }>(\n `CHECKSUM TABLE ${tableName}`\n );\n const fdbChecksum = fdbChecksumRow[\"Checksum\"];\n\n const [tdbChecksumRow] = await DB.tdb.raw<{ Checksum: string }>(\n `CHECKSUM TABLE ${tableName}`\n );\n const tdbChecksum = tdbChecksumRow[\"Checksum\"];\n\n if (fdbChecksum !== tdbChecksum) {\n await DB.tdb.truncate(tableName);\n const rawQuery = `INSERT INTO ${DB.connectionInfo.test.database}.${tableName}\n SELECT * FROM ${DB.connectionInfo.fixture_local.database}.${tableName}`;\n await DB.tdb.raw(rawQuery);\n }\n }\n await DB.tdb.raw(`SET FOREIGN_KEY_CHECKS = 1`);\n\n // console.timeEnd(\"FIXTURE-CleanAndSeed\");\n }\n\n async getChecksum(db: KnexClient | KyselyClient, tableName: string) {\n const [checksumRow] = await db.raw<{ Checksum: string }>(\n `CHECKSUM TABLE ${tableName}`\n );\n return checksumRow.Checksum;\n }\n\n async sync() {\n const frdb = DB.getClient(\"fixture_remote\");\n\n const tables = await DB.fdb.raw<{ Name: string }>(\n \"SHOW TABLE STATUS WHERE Engine IS NOT NULL\"\n );\n const tableNames: string[] = tables.map(\n (table: any) => table.Name as string\n );\n\n console.log(chalk.magenta(\"SYNC...\"));\n await Promise.all(\n tableNames.map(async (tableName) => {\n if (tableName.startsWith(DB.migrationTable)) {\n return;\n }\n\n const remoteChecksum = await this.getChecksum(frdb, tableName);\n const localChecksum = await this.getChecksum(DB.fdb, tableName);\n\n if (remoteChecksum !== localChecksum) {\n await DB.fdb.trx(async (transaction) => {\n await transaction.raw(`SET FOREIGN_KEY_CHECKS = 0`);\n await transaction.truncate(tableName);\n\n const rows = await frdb.raw(`SELECT * FROM ${tableName}`);\n if (rows.length === 0) {\n return;\n }\n\n console.log(chalk.blue(tableName), rows.length);\n await transaction.raw(\n `INSERT INTO ${tableName} (${Object.keys(rows[0] as any)\n .map((k) => `\\`${k}\\``)\n .join(\",\")}) VALUES ?`,\n [\n rows.map((row: any) =>\n Object.values(row).map((v) => {\n if (v === null) {\n return null;\n } else if (typeof v === \"boolean\") {\n return v ? 1 : 0;\n } else if (typeof v === \"object\") {\n return JSON.stringify(v);\n } else {\n return v;\n }\n })\n ),\n ]\n );\n console.log(\"OK\");\n await transaction.raw(`SET FOREIGN_KEY_CHECKS = 1`);\n });\n }\n })\n );\n console.log(chalk.magenta(\"DONE!\"));\n\n await frdb.destroy();\n }\n\n async importFixture(entityId: string, ids: number[]) {\n const queries = _.uniq(\n (\n await Promise.all(\n ids.map(async (id) => {\n return await this.getImportQueries(entityId, \"id\", id);\n })\n )\n ).flat()\n );\n\n const wdb = DB.toClient(DB.getDB(\"w\"));\n for (let query of queries) {\n const [rsh] = await wdb.raw<{ info: any }>(query);\n console.log({\n query,\n info: rsh.info,\n });\n }\n }\n\n async getImportQueries(\n entityId: string,\n field: string,\n id: number\n ): Promise<string[]> {\n console.log({ entityId, field, id });\n const entity = EntityManager.get(entityId);\n const wdb = DB.toClient(DB.getDB(\"w\"));\n\n // 여기서 실DB의 row 가져옴\n const [row] = await wdb.raw<any>(\n `SELECT * FROM ${entity.table} WHERE ${field} = ${id} LIMIT 1`\n );\n if (row === undefined) {\n throw new Error(`${entityId}#${id} row를 찾을 수 없습니다.`);\n }\n\n // 픽스쳐DB, 실DB\n const fixtureDatabase = DB.connectionInfo.fixture_remote.database;\n const realDatabase = DB.connectionInfo.production_master.database;\n\n const selfQuery = `INSERT IGNORE INTO \\`${fixtureDatabase}\\`.\\`${entity.table}\\` (SELECT * FROM \\`${realDatabase}\\`.\\`${entity.table}\\` WHERE \\`id\\` = ${id})`;\n\n const args = Object.entries(entity.relations)\n .filter(\n ([, relation]) =>\n isBelongsToOneRelationProp(relation) ||\n (isOneToOneRelationProp(relation) &&\n relation.customJoinClause === undefined)\n )\n .map(([, relation]) => {\n /*\n BelongsToOne인 경우\n Category / 'id' / row[category_id] 호출\n OneToOne에 joinColumn === true 인 경우\n Profile / 'id' / row[profile_id] 호출\n OneToOne에 joinColumn === false 인 경우\n Profile / 'profile_id' / row['id'] 호출\n */\n let field: string;\n let id: number;\n if (isOneToOneRelationProp(relation) && !relation.hasJoinColumn) {\n field = `${relation.name}_id`;\n id = row[\"id\"];\n } else {\n field = \"id\";\n id = row[`${relation.name}_id`];\n }\n return {\n entityId: relation.with,\n field,\n id,\n };\n })\n .filter((arg) => arg.id !== null);\n\n const relQueries = await Promise.all(\n args.map(async (args) => {\n return this.getImportQueries(args.entityId, args.field, args.id);\n })\n );\n\n return [..._.uniq(relQueries.reverse().flat()), selfQuery];\n }\n\n async destory() {\n await DB.testDestroy();\n await DB.destroy();\n }\n\n async getFixtures(\n sourceDBName: keyof SonamuDBConfig,\n targetDBName: keyof SonamuDBConfig,\n searchOptions: FixtureSearchOptions\n ) {\n const sourceDB = DB.getClient(sourceDBName);\n const targetDB = DB.getClient(targetDBName);\n const { entityId, field, value, searchType } = searchOptions;\n\n const entity = EntityManager.get(entityId);\n const column =\n entity.props.find((prop) => prop.name === field)?.type === \"relation\"\n ? `${field}_id`\n : field;\n\n let query = sourceDB.from(entity.table).selectAll();\n if (searchType === \"equals\") {\n query = query.where([column, \"=\", value]);\n } else if (searchType === \"like\") {\n query = query.where([column, \"like\", `%${value}%`]);\n }\n\n const rows = await query.execute();\n if (rows.length === 0) {\n throw new Error(\"No records found\");\n }\n\n const fixtures: FixtureRecord[] = [];\n for (const row of rows) {\n const initialRecordsLength = fixtures.length;\n const newRecords = await this.createFixtureRecord(entity, row);\n fixtures.push(...newRecords);\n const currentFixtureRecord = fixtures.find(\n (r) => r.fixtureId === `${entityId}#${row.id}`\n );\n\n if (currentFixtureRecord) {\n // 현재 fixture로부터 생성된 fetchedRecords 설정\n currentFixtureRecord.fetchedRecords = fixtures\n .filter((r) => r.fixtureId !== currentFixtureRecord.fixtureId)\n .slice(initialRecordsLength)\n .map((r) => r.fixtureId);\n }\n }\n\n for await (const fixture of fixtures) {\n const entity = EntityManager.get(fixture.entityId);\n\n // ID를 이용하여 targetDB에 레코드가 존재하는지 확인\n const [row] = await targetDB\n .from(entity.table)\n .selectAll()\n .where([\"id\", \"=\", fixture.id])\n .first()\n .execute();\n if (row) {\n const [record] = await this.createFixtureRecord(entity, row, {\n singleRecord: true,\n _db: targetDB,\n });\n fixture.target = record;\n continue;\n }\n\n // ID를 이용하여 targetDB에서 조회되지 않는 경우, unique 제약을 위반하는지 확인\n const uniqueRow = await this.checkUniqueViolation(\n targetDB,\n entity,\n fixture\n );\n if (uniqueRow) {\n const [record] = await this.createFixtureRecord(entity, uniqueRow, {\n singleRecord: true,\n _db: targetDB,\n });\n fixture.unique = record;\n }\n }\n\n return _.uniqBy(fixtures, (f) => f.fixtureId);\n }\n\n async createFixtureRecord(\n entity: Entity,\n row: any,\n options?: {\n singleRecord?: boolean;\n _db?: KnexClient | KyselyClient;\n }\n ): Promise<FixtureRecord[]> {\n const records: FixtureRecord[] = [];\n const visitedEntities = new Set<string>();\n\n const create = async (entity: Entity, row: any) => {\n const fixtureId = `${entity.id}#${row.id}`;\n if (visitedEntities.has(fixtureId)) {\n return;\n }\n visitedEntities.add(fixtureId);\n\n const record: FixtureRecord = {\n fixtureId,\n entityId: entity.id,\n id: row.id,\n columns: {},\n fetchedRecords: [],\n belongsRecords: [],\n };\n\n for (const prop of entity.props) {\n if (isVirtualProp(prop)) {\n continue;\n }\n\n record.columns[prop.name] = {\n prop: prop,\n value: row[prop.name],\n };\n\n const db = options?._db ?? DB.toClient(DB.getDB(\"w\"));\n if (isManyToManyRelationProp(prop)) {\n const relatedEntity = EntityManager.get(prop.with);\n const throughTable = prop.joinTable;\n const fromColumn = `${inflection.singularize(entity.table)}_id`;\n const toColumn = `${inflection.singularize(relatedEntity.table)}_id`;\n\n const _relatedIds = await db\n .from(throughTable)\n .select(toColumn)\n .where([fromColumn, \"=\", row.id])\n .execute();\n const relatedIds = _relatedIds.map((r) => parseInt(r[toColumn]));\n\n record.columns[prop.name].value = relatedIds;\n } else if (isHasManyRelationProp(prop)) {\n const relatedEntity = EntityManager.get(prop.with);\n const relatedIds = await db\n .from(relatedEntity.table)\n .select(\"id\")\n .where([prop.joinColumn, \"=\", row.id])\n .pluck(\"id\");\n record.columns[prop.name].value = relatedIds;\n } else if (isOneToOneRelationProp(prop) && !prop.hasJoinColumn) {\n const relatedEntity = EntityManager.get(prop.with);\n const relatedProp = relatedEntity.props.find(\n (p) => isRelationProp(p) && p.with === entity.id\n );\n if (relatedProp) {\n const [relatedRow] = await db\n .from(relatedEntity.table)\n .select(\"id\")\n .where([relatedProp.name, \"=\", row.id])\n .first()\n .execute();\n\n record.columns[prop.name].value = relatedRow?.id;\n }\n } else if (isRelationProp(prop)) {\n const relatedId = row[`${prop.name}_id`];\n record.columns[prop.name].value = relatedId;\n if (relatedId) {\n record.belongsRecords.push(`${prop.with}#${relatedId}`);\n }\n if (!options?.singleRecord && relatedId) {\n const relatedEntity = EntityManager.get(prop.with);\n const [relatedRow] = await db\n .from(relatedEntity.table)\n .selectAll()\n .where([\"id\", \"=\", relatedId])\n .first()\n .execute();\n if (relatedRow) {\n await create(relatedEntity, relatedRow);\n }\n }\n }\n }\n\n records.push(record);\n };\n\n await create(entity, row);\n\n return records;\n }\n\n async insertFixtures(\n dbName: keyof SonamuDBConfig,\n _fixtures: FixtureRecord[]\n ) {\n const fixtures = _.uniqBy(_fixtures, (f) => f.fixtureId);\n\n this.relationGraph.buildGraph(fixtures);\n const insertionOrder = this.relationGraph.getInsertionOrder();\n const db = DB.getClient(dbName);\n\n await db.trx(async (trx) => {\n await trx.raw(`SET FOREIGN_KEY_CHECKS = 0`);\n\n for (const fixtureId of insertionOrder) {\n const fixture = fixtures.find((f) => f.fixtureId === fixtureId)!;\n const result = await this.insertFixture(trx as any, fixture);\n if (result.id !== fixture.id) {\n // ID가 변경된 경우, 다른 fixture에서 참조하는 경우가 찾아서 수정\n console.log(\n chalk.yellow(\n `Unique constraint violation: ${fixture.entityId}#${fixture.id} -> ${fixture.entityId}#${result.id}`\n )\n );\n fixtures.forEach((f) => {\n Object.values(f.columns).forEach((column) => {\n if (\n column.prop.type === \"relation\" &&\n column.prop.with === result.entityId &&\n column.value === fixture.id\n ) {\n column.value = result.id;\n }\n });\n });\n fixture.id = result.id;\n }\n }\n\n for (const fixtureId of insertionOrder) {\n const fixture = fixtures.find((f) => f.fixtureId === fixtureId)!;\n await this.handleManyToManyRelations(trx as any, fixture, fixtures);\n }\n await trx.raw(`SET FOREIGN_KEY_CHECKS = 1`);\n });\n\n const records: FixtureImportResult[] = [];\n\n for await (const r of fixtures) {\n const entity = EntityManager.get(r.entityId);\n const [record] = await db\n .from(entity.table)\n .selectAll()\n .where([\"id\", \"=\", r.id])\n .first()\n .execute();\n records.push({\n entityId: r.entityId,\n data: record,\n });\n }\n\n return _.uniqBy(records, (r) => `${r.entityId}#${r.data.id}`);\n }\n\n private prepareInsertData(fixture: FixtureRecord) {\n const insertData: any = {};\n for (const [propName, column] of Object.entries(fixture.columns)) {\n if (isVirtualProp(column.prop)) {\n continue;\n }\n\n const prop = column.prop as EntityProp;\n if (!isRelationProp(prop)) {\n if (prop.type === \"json\") {\n insertData[propName] = JSON.stringify(column.value);\n } else {\n insertData[propName] = column.value;\n }\n } else if (\n isBelongsToOneRelationProp(prop) ||\n (isOneToOneRelationProp(prop) && prop.hasJoinColumn)\n ) {\n insertData[`${propName}_id`] = column.value;\n }\n }\n return insertData;\n }\n\n private async insertFixture(\n db: KnexClient | KyselyClient,\n fixture: FixtureRecord\n ) {\n const insertData = this.prepareInsertData(fixture);\n const entity = EntityManager.get(fixture.entityId);\n\n try {\n const uniqueFound = await this.checkUniqueViolation(db, entity, fixture);\n if (uniqueFound) {\n return {\n entityId: fixture.entityId,\n id: uniqueFound.id,\n };\n }\n\n const [found] = await db\n .from(entity.table)\n .select(\"id\")\n .where([\"id\", \"=\", fixture.id])\n .first()\n .execute();\n if (found && !fixture.override) {\n return {\n entityId: fixture.entityId,\n id: found.id,\n };\n }\n\n await db.upsert(entity.table, [insertData]);\n\n return {\n entityId: fixture.entityId,\n id: fixture.id,\n };\n } catch (err) {\n console.log(err);\n throw err;\n }\n }\n\n private async handleManyToManyRelations(\n db: KnexClient | KyselyClient,\n fixture: FixtureRecord,\n fixtures: FixtureRecord[]\n ) {\n for (const [, column] of Object.entries(fixture.columns)) {\n const prop = column.prop as EntityProp;\n if (isManyToManyRelationProp(prop)) {\n const joinTable = (prop as ManyToManyRelationProp).joinTable;\n const relatedIds = column.value as number[];\n\n for (const relatedId of relatedIds) {\n if (\n !fixtures.find((f) => f.fixtureId === `${prop.with}#${relatedId}`)\n ) {\n continue;\n }\n\n const entity = EntityManager.get(fixture.entityId);\n const relatedEntity = EntityManager.get(prop.with);\n if (!entity || !relatedEntity) {\n throw new Error(\n `Entity not found: ${fixture.entityId}, ${prop.with}`\n );\n }\n\n const [found] = await db\n .from(joinTable)\n .select(\"id\")\n .where([\n [`${inflection.singularize(entity.table)}_id`, \"=\", fixture.id],\n [\n `${inflection.singularize(relatedEntity.table)}_id`,\n \"=\",\n relatedId,\n ],\n ])\n .first()\n .execute();\n if (found) {\n continue;\n }\n\n const newIds = await db.insert(joinTable, [\n {\n [`${inflection.singularize(entity.table)}_id`]: fixture.id,\n [`${inflection.singularize(relatedEntity.table)}_id`]: relatedId,\n },\n ]);\n console.log(\n chalk.green(\n `Inserted into ${joinTable}: ${entity.table}(${fixture.id}) - ${relatedEntity.table}(${relatedId}) ID: ${newIds}`\n )\n );\n }\n }\n }\n }\n\n async addFixtureLoader(code: string) {\n const path = Sonamu.apiRootPath + \"/src/testing/fixture.ts\";\n let content = readFileSync(path).toString();\n\n const fixtureLoaderStart = content.indexOf(\"const fixtureLoader = {\");\n const fixtureLoaderEnd = content.indexOf(\"};\", fixtureLoaderStart);\n\n if (fixtureLoaderStart !== -1 && fixtureLoaderEnd !== -1) {\n const newContent =\n content.slice(0, fixtureLoaderEnd) +\n \" \" +\n code +\n \"\\n\" +\n content.slice(fixtureLoaderEnd);\n\n writeFileSync(path, newContent);\n } else {\n throw new Error(\"Failed to find fixtureLoader in fixture.ts\");\n }\n }\n\n // 해당 픽스쳐의 값으로 유니크 제약에 위배되는 레코드가 있는지 확인\n private async checkUniqueViolation(\n db: KnexClient | KyselyClient,\n entity: Entity,\n fixture: FixtureRecord\n ) {\n const _uniqueIndexes = entity.indexes.filter((i) => i.type === \"unique\");\n\n // ManyToMany 관계 테이블의 유니크 제약은 제외\n const uniqueIndexes = _uniqueIndexes.filter((index) =>\n index.columns.every((column) => !column.startsWith(`${entity.table}__`))\n );\n if (uniqueIndexes.length === 0) {\n return null;\n }\n\n let uniqueQuery = db.from(entity.table).selectAll();\n const whereClauses = uniqueIndexes\n .map((index) => {\n // 컬럼 중 하나라도 null이면 유니크 제약을 위반하지 않기 때문에 해당 인덱스는 무시\n const containsNull = index.columns.some((column) => {\n const field = column.split(\"_id\")[0];\n return fixture.columns[field].value === null;\n });\n if (containsNull) {\n return;\n }\n\n return index.columns.map((c) => {\n const field = c.split(\"_id\")[0];\n if (Array.isArray(fixture.columns[field].value)) {\n return [c, \"in\", fixture.columns[field].value];\n } else {\n return [c, \"=\", fixture.columns[field].value];\n }\n });\n })\n .filter(Boolean) as WhereClause[];\n\n for (const clauses of whereClauses) {\n uniqueQuery = uniqueQuery.orWhere(clauses);\n }\n\n const [uniqueFound] = await uniqueQuery.execute();\n return uniqueFound;\n }\n}\nexport const FixtureManager = new FixtureManagerClass();\n","import { RelationNode, EntityProp, FixtureRecord } from \"../types/types\";\nimport { EntityManager } from \"../entity/entity-manager\";\nimport {\n isRelationProp,\n isBelongsToOneRelationProp,\n isOneToOneRelationProp,\n isManyToManyRelationProp,\n} from \"../types/types\";\n\n// 관계 그래프 처리를 별도 클래스로 분리\nexport class RelationGraph {\n private graph: Map<string, RelationNode> = new Map();\n\n buildGraph(fixtures: FixtureRecord[]): void {\n this.graph.clear();\n\n // 1. 노드 추가\n for (const fixture of fixtures) {\n this.graph.set(fixture.fixtureId, {\n fixtureId: fixture.fixtureId,\n entityId: fixture.entityId,\n related: new Set(),\n });\n }\n\n // 2. 의존성 추가\n for (const fixture of fixtures) {\n const node = this.graph.get(fixture.fixtureId)!;\n\n for (const [, column] of Object.entries(fixture.columns)) {\n const prop = column.prop as EntityProp;\n\n if (isRelationProp(prop)) {\n if (\n isBelongsToOneRelationProp(prop) ||\n (isOneToOneRelationProp(prop) && prop.hasJoinColumn)\n ) {\n const relatedFixtureId = `${prop.with}#${column.value}`;\n if (this.graph.has(relatedFixtureId)) {\n node.related.add(relatedFixtureId);\n }\n } else if (isManyToManyRelationProp(prop)) {\n // ManyToMany 관계의 경우 양방향 의존성 추가\n const relatedIds = column.value as number[];\n for (const relatedId of relatedIds) {\n const relatedFixtureId = `${prop.with}#${relatedId}`;\n if (this.graph.has(relatedFixtureId)) {\n node.related.add(relatedFixtureId);\n this.graph\n .get(relatedFixtureId)!\n .related.add(fixture.fixtureId);\n }\n }\n }\n }\n }\n }\n }\n\n getInsertionOrder(): string[] {\n const visited = new Set<string>();\n const order: string[] = [];\n const tempVisited = new Set<string>();\n\n const visit = (fixtureId: string) => {\n if (visited.has(fixtureId)) return;\n if (tempVisited.has(fixtureId)) {\n console.warn(`Circular dependency detected involving: ${fixtureId}`);\n return;\n }\n\n tempVisited.add(fixtureId);\n\n const node = this.graph.get(fixtureId)!;\n const entity = EntityManager.get(node.entityId);\n\n for (const depId of node.related) {\n const depNode = this.graph.get(depId)!;\n\n // BelongsToOne 관계이면서 nullable이 아닌 경우 먼저 방문\n const relationProp = entity.props.find(\n (prop) =>\n isRelationProp(prop) &&\n (isBelongsToOneRelationProp(prop) ||\n (isOneToOneRelationProp(prop) && prop.hasJoinColumn)) &&\n prop.with === depNode.entityId\n );\n if (relationProp && !relationProp.nullable) {\n visit(depId);\n }\n }\n\n tempVisited.delete(fixtureId);\n visited.add(fixtureId);\n order.push(fixtureId);\n };\n\n for (const fixtureId of this.graph.keys()) {\n visit(fixtureId);\n }\n\n // circular dependency로 인해 방문되지 않은 fixtureId 추가\n for (const fixtureId of this.graph.keys()) {\n if (!visited.has(fixtureId)) {\n order.push(fixtureId);\n }\n }\n\n return order;\n }\n}\n"]}
@@ -0,0 +1,280 @@
1
+ import {
2
+ DB,
3
+ EntityManager,
4
+ nonNullable
5
+ } from "./chunk-ZFLQLW37.mjs";
6
+
7
+ // src/database/upsert-builder.ts
8
+ import { v4 as uuidv4 } from "uuid";
9
+ import _ from "lodash";
10
+
11
+ // src/database/_batch_update.ts
12
+ async function batchUpdate(db, tableName, ids, rows, chunkSize = 50, trx = null) {
13
+ const chunks = [];
14
+ for (let i = 0; i < rows.length; i += chunkSize) {
15
+ chunks.push(rows.slice(i, i + chunkSize));
16
+ }
17
+ const executeUpdate = async (chunk, transaction) => {
18
+ const sql = generateBatchUpdateSQL(db, tableName, chunk, ids);
19
+ return transaction.raw(sql);
20
+ };
21
+ if (trx) {
22
+ for (const chunk of chunks) {
23
+ await executeUpdate(chunk, DB.toClient(trx));
24
+ }
25
+ } else {
26
+ await db.trx(async (newTrx) => {
27
+ for (const chunk of chunks) {
28
+ await executeUpdate(chunk, newTrx);
29
+ }
30
+ });
31
+ }
32
+ }
33
+ function generateKeySetFromData(data) {
34
+ const keySet = /* @__PURE__ */ new Set();
35
+ for (const row of data) {
36
+ for (const key of Object.keys(row)) {
37
+ keySet.add(key);
38
+ }
39
+ }
40
+ return keySet;
41
+ }
42
+ function generateBatchUpdateSQL(db, tableName, data, identifiers) {
43
+ const keySet = generateKeySetFromData(data);
44
+ const bindings = [];
45
+ const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));
46
+ if (invalidIdentifiers.length > 0) {
47
+ throw new Error(
48
+ `Invalid identifiers: ${invalidIdentifiers.join(", ")}. Identifiers must exist in the data`
49
+ );
50
+ }
51
+ const cases = [];
52
+ for (const key of keySet) {
53
+ if (identifiers.includes(key)) continue;
54
+ const rows = [];
55
+ for (const row of data) {
56
+ if (Object.hasOwnProperty.call(row, key)) {
57
+ const whereClause = identifiers.map((id) => `\`${id}\` = ?`).join(" AND ");
58
+ rows.push(`WHEN (${whereClause}) THEN ?`);
59
+ bindings.push(...identifiers.map((i) => row[i]), row[key]);
60
+ }
61
+ }
62
+ const whenThen = rows.join(" ");
63
+ cases.push(`\`${key}\` = CASE ${whenThen} ELSE \`${key}\` END`);
64
+ }
65
+ const whereInClauses = identifiers.map((col) => `${col} IN (${data.map(() => "?").join(", ")})`).join(" AND ");
66
+ const whereInBindings = identifiers.flatMap(
67
+ (col) => data.map((row) => row[col])
68
+ );
69
+ const sql = db.createRawQuery(
70
+ `UPDATE \`${tableName}\` SET ${cases.join(", ")} WHERE ${whereInClauses}`,
71
+ [...bindings, ...whereInBindings]
72
+ );
73
+ return sql;
74
+ }
75
+
76
+ // src/database/upsert-builder.ts
77
+ function isRefField(field) {
78
+ return field !== void 0 && field !== null && field.of !== void 0 && field.uuid !== void 0;
79
+ }
80
+ var UpsertBuilder = class {
81
+ tables;
82
+ constructor() {
83
+ this.tables = /* @__PURE__ */ new Map();
84
+ }
85
+ getTable(tableName) {
86
+ const table = this.tables.get(tableName);
87
+ if (table === void 0) {
88
+ const tableSpec = (() => {
89
+ try {
90
+ return EntityManager.getTableSpec(tableName);
91
+ } catch {
92
+ return null;
93
+ }
94
+ })();
95
+ this.tables.set(tableName, {
96
+ references: /* @__PURE__ */ new Set(),
97
+ rows: [],
98
+ uniqueIndexes: tableSpec?.uniqueIndexes ?? [],
99
+ uniquesMap: /* @__PURE__ */ new Map()
100
+ });
101
+ }
102
+ return this.tables.get(tableName);
103
+ }
104
+ hasTable(tableName) {
105
+ return this.tables.has(tableName);
106
+ }
107
+ register(tableName, row) {
108
+ const table = this.getTable(tableName);
109
+ const uniqueKeys = table.uniqueIndexes.map((unqIndex) => {
110
+ const uniqueKeyArray = unqIndex.columns.map((unqCol) => {
111
+ const val = row[unqCol];
112
+ if (isRefField(val)) {
113
+ return val.uuid;
114
+ } else {
115
+ return row[unqCol] ?? uuidv4();
116
+ }
117
+ });
118
+ if (uniqueKeyArray.length === 0) {
119
+ return null;
120
+ }
121
+ return uniqueKeyArray.join("---delimiter--");
122
+ }).filter(nonNullable);
123
+ const uuid = (() => {
124
+ if (uniqueKeys.length > 0) {
125
+ for (const uniqueKey of uniqueKeys) {
126
+ if (table.uniquesMap.has(uniqueKey)) {
127
+ return table.uniquesMap.get(uniqueKey);
128
+ }
129
+ }
130
+ }
131
+ return uuidv4();
132
+ })();
133
+ if (uniqueKeys.length > 0) {
134
+ for (const uniqueKey of uniqueKeys) {
135
+ table.uniquesMap.set(uniqueKey, uuid);
136
+ }
137
+ }
138
+ row = Object.keys(row).reduce((r, rowKey) => {
139
+ const rowValue = row[rowKey];
140
+ if (isRefField(rowValue)) {
141
+ rowValue.use ??= "id";
142
+ table.references.add(rowValue.of + "." + rowValue.use);
143
+ r[rowKey] = rowValue;
144
+ } else if (typeof rowValue === "object") {
145
+ r[rowKey] = rowValue === null ? null : JSON.stringify(rowValue);
146
+ } else {
147
+ r[rowKey] = rowValue;
148
+ }
149
+ return r;
150
+ }, {});
151
+ table.rows.push({
152
+ uuid,
153
+ ...row
154
+ });
155
+ return {
156
+ of: tableName,
157
+ uuid: row.uuid ?? uuid
158
+ };
159
+ }
160
+ async upsert(wdb, tableName, chunkSize) {
161
+ return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
162
+ }
163
+ async insertOnly(wdb, tableName, chunkSize) {
164
+ return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
165
+ }
166
+ async upsertOrInsert(_wdb, tableName, mode, chunkSize) {
167
+ if (this.hasTable(tableName) === false) {
168
+ return [];
169
+ }
170
+ const table = this.tables.get(tableName);
171
+ if (table === void 0) {
172
+ throw new Error(`\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uD14C\uC774\uBE14 ${tableName}\uC5D0 upsert \uC694\uCCAD`);
173
+ } else if (table.rows.length === 0) {
174
+ throw new Error(`${tableName}\uC5D0 upsert \uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`);
175
+ }
176
+ if (table.rows.some(
177
+ (row) => Object.entries(row).some(
178
+ ([, value]) => isRefField(value) && value.of !== tableName
179
+ )
180
+ )) {
181
+ throw new Error(`${tableName} \uD574\uACB0\uB418\uC9C0 \uC54A\uC740 \uCC38\uC870\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`);
182
+ }
183
+ const wdb = DB.toClient(_wdb);
184
+ const { references, refTables } = Array.from(this.tables).reduce(
185
+ (r, [, table2]) => {
186
+ const reference = Array.from(table2.references.values()).find(
187
+ (ref) => ref.includes(tableName + ".")
188
+ );
189
+ if (reference) {
190
+ r.references.push(reference);
191
+ r.refTables.push(table2);
192
+ }
193
+ return r;
194
+ },
195
+ {
196
+ references: [],
197
+ refTables: []
198
+ }
199
+ );
200
+ const extractFields = _.uniq(references).map(
201
+ (reference) => reference.split(".")[1]
202
+ );
203
+ const groups = _.groupBy(
204
+ table.rows,
205
+ (row) => Object.entries(row).some(([, value]) => isRefField(value)) ? "selfRef" : "normal"
206
+ );
207
+ const normalRows = groups.normal ?? [];
208
+ const selfRefRows = groups.selfRef ?? [];
209
+ const chunks = chunkSize ? _.chunk(normalRows, chunkSize) : [normalRows];
210
+ const uuidMap = /* @__PURE__ */ new Map();
211
+ for (const chunk of chunks) {
212
+ if (mode === "insert") {
213
+ await wdb.insert(tableName, chunk);
214
+ } else if (mode === "upsert") {
215
+ await wdb.upsert(tableName, chunk);
216
+ }
217
+ const uuids = chunk.map((row) => row.uuid);
218
+ const upsertedRows = await wdb.from(tableName).select(_.uniq(["uuid", "id", ...extractFields])).where(["uuid", "in", uuids]).execute();
219
+ upsertedRows.forEach((row) => {
220
+ uuidMap.set(row.uuid, row);
221
+ });
222
+ }
223
+ refTables.map((table2) => {
224
+ table2.rows = table2.rows.map((row) => {
225
+ Object.keys(row).map((key) => {
226
+ const prop = row[key];
227
+ if (isRefField(prop) && prop.of === tableName) {
228
+ const parent = uuidMap.get(prop.uuid);
229
+ if (parent === void 0) {
230
+ console.error(prop);
231
+ throw new Error(
232
+ `\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 uuid ${prop.uuid} -- in ${tableName}`
233
+ );
234
+ }
235
+ row[key] = parent[prop.use ?? "id"];
236
+ }
237
+ });
238
+ return row;
239
+ });
240
+ });
241
+ const allIds = Array.from(uuidMap.values()).map((row) => row.id);
242
+ if (selfRefRows.length > 0) {
243
+ table.rows = selfRefRows;
244
+ const selfRefIds = await this.upsert(_wdb, tableName, chunkSize);
245
+ allIds.push(...selfRefIds);
246
+ }
247
+ return allIds;
248
+ }
249
+ async updateBatch(wdb, tableName, options) {
250
+ options = _.defaults(options, {
251
+ chunkSize: 500,
252
+ where: "id"
253
+ });
254
+ if (this.hasTable(tableName) === false) {
255
+ return;
256
+ }
257
+ const table = this.tables.get(tableName);
258
+ if (table.rows.length === 0) {
259
+ return;
260
+ }
261
+ const whereColumns = Array.isArray(options.where) ? options.where : [options.where ?? "id"];
262
+ const rows = table.rows.map((_row) => {
263
+ const { uuid, ...row } = _row;
264
+ return row;
265
+ });
266
+ await batchUpdate(
267
+ DB.toClient(wdb),
268
+ tableName,
269
+ whereColumns,
270
+ rows,
271
+ options.chunkSize
272
+ );
273
+ }
274
+ };
275
+
276
+ export {
277
+ isRefField,
278
+ UpsertBuilder
279
+ };
280
+ //# sourceMappingURL=chunk-HVVCQLAU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/database/upsert-builder.ts","../src/database/_batch_update.ts"],"sourcesContent":["import { v4 as uuidv4 } from \"uuid\";\nimport _ from \"lodash\";\nimport { Knex } from \"knex\";\nimport { Kysely } from \"kysely\";\nimport { EntityManager } from \"../entity/entity-manager\";\nimport { nonNullable } from \"../utils/utils\";\nimport { RowWithId, batchUpdate } from \"./_batch_update\";\nimport { Database, DatabaseDriver, DriverSpec } from \"./types\";\nimport { DB } from \"./db\";\n\ntype TableData = {\n references: Set<string>;\n rows: any[];\n uniqueIndexes: { name?: string; columns: string[] }[];\n uniquesMap: Map<string, string>;\n};\nexport type UBRef = {\n uuid: string;\n of: string;\n use?: string;\n};\nexport function isRefField(field: any): field is UBRef {\n return (\n field !== undefined &&\n field !== null &&\n field.of !== undefined &&\n field.uuid !== undefined\n );\n}\n\nexport class UpsertBuilder<D extends DatabaseDriver> {\n tables: Map<string, TableData>;\n constructor() {\n this.tables = new Map();\n }\n\n getTable(tableName: string): TableData {\n const table = this.tables.get(tableName);\n if (table === undefined) {\n const tableSpec = (() => {\n try {\n return EntityManager.getTableSpec(tableName);\n } catch {\n return null;\n }\n })();\n\n this.tables.set(tableName, {\n references: new Set(),\n rows: [],\n uniqueIndexes: tableSpec?.uniqueIndexes ?? [],\n uniquesMap: new Map<string, string>(),\n });\n }\n\n return this.tables.get(tableName)!;\n }\n\n hasTable(tableName: string): boolean {\n return this.tables.has(tableName);\n }\n\n register<T extends string>(\n tableName: DriverSpec[D][\"table\"],\n row: {\n [key in T]?:\n | UBRef\n | string\n | number\n | boolean\n | bigint\n | null\n | object\n | unknown;\n }\n ): UBRef {\n const table = this.getTable(tableName);\n\n // 해당 테이블의 unique 인덱스를 순회하며 키 생성\n const uniqueKeys = table.uniqueIndexes\n .map((unqIndex) => {\n const uniqueKeyArray = unqIndex.columns.map((unqCol) => {\n const val = row[unqCol as keyof typeof row];\n if (isRefField(val)) {\n return val.uuid;\n } else {\n return row[unqCol as keyof typeof row] ?? uuidv4(); // nullable인 경우 uuid로 랜덤값 삽입\n }\n });\n\n // 값이 모두 null인 경우 키 생성 패스\n if (uniqueKeyArray.length === 0) {\n return null;\n }\n return uniqueKeyArray.join(\"---delimiter--\");\n })\n .filter(nonNullable);\n\n // uuid 생성 로직\n const uuid: string = (() => {\n // 키를 순회하여 이미 존재하는 키가 있는지 확인\n if (uniqueKeys.length > 0) {\n for (const uniqueKey of uniqueKeys) {\n if (table.uniquesMap.has(uniqueKey)) {\n return table.uniquesMap.get(uniqueKey)!; // 이미 has 체크를 했으므로 undefined 불가능\n }\n }\n }\n\n // 찾을 수 없는 경우 생성\n return uuidv4();\n })();\n\n // 모든 유니크키에 대해 유니크맵에 uuid 저장\n if (uniqueKeys.length > 0) {\n for (const uniqueKey of uniqueKeys) {\n table.uniquesMap.set(uniqueKey, uuid);\n }\n }\n\n // 이 테이블에 사용된 RefField를 순회하여, 현재 테이블 정보에 어떤 필드를 참조하는지 추가\n // 이 정보를 나중에 치환할 때 사용\n row = Object.keys(row).reduce((r, rowKey) => {\n const rowValue = row[rowKey as keyof typeof row];\n\n if (isRefField(rowValue)) {\n rowValue.use ??= \"id\";\n table.references.add(rowValue.of + \".\" + rowValue.use);\n r[rowKey] = rowValue;\n } else if (typeof rowValue === \"object\") {\n // object인 경우 JSON으로 변환\n r[rowKey] = rowValue === null ? null : JSON.stringify(rowValue);\n } else {\n r[rowKey] = rowValue;\n }\n return r;\n }, {} as any);\n\n table.rows.push({\n uuid,\n ...row,\n });\n\n return {\n of: tableName,\n uuid: (row as { uuid?: string }).uuid ?? uuid,\n };\n }\n\n async upsert(\n wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n chunkSize?: number\n ): Promise<number[]> {\n return this.upsertOrInsert(wdb, tableName, \"upsert\", chunkSize);\n }\n async insertOnly(\n wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n chunkSize?: number\n ): Promise<number[]> {\n return this.upsertOrInsert(wdb, tableName, \"insert\", chunkSize);\n }\n\n async upsertOrInsert(\n _wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n mode: \"upsert\" | \"insert\",\n chunkSize?: number\n ): Promise<number[]> {\n if (this.hasTable(tableName) === false) {\n return [];\n }\n\n const table = this.tables.get(tableName);\n if (table === undefined) {\n throw new Error(`존재하지 않는 테이블 ${tableName}에 upsert 요청`);\n } else if (table.rows.length === 0) {\n throw new Error(`${tableName}에 upsert 할 데이터가 없습니다.`);\n }\n\n if (\n table.rows.some((row) =>\n Object.entries(row).some(\n ([, value]) => isRefField(value) && value.of !== tableName\n )\n )\n ) {\n throw new Error(`${tableName} 해결되지 않은 참조가 있습니다.`);\n }\n\n const wdb = DB.toClient(_wdb);\n\n // 전체 테이블 순회하여 현재 테이블 참조하는 모든 테이블 추출\n const { references, refTables } = Array.from(this.tables).reduce(\n (r, [, table]) => {\n const reference = Array.from(table.references.values()).find((ref) =>\n ref.includes(tableName + \".\")\n );\n if (reference) {\n r.references.push(reference);\n r.refTables.push(table);\n }\n\n return r;\n },\n {\n references: [] as string[],\n refTables: [] as TableData[],\n }\n );\n const extractFields = _.uniq(references).map(\n (reference) => reference.split(\".\")[1]\n );\n\n // 내부 참조 있는 경우 필터하여 분리\n const groups = _.groupBy(table.rows, (row) =>\n Object.entries(row).some(([, value]) => isRefField(value))\n ? \"selfRef\"\n : \"normal\"\n );\n const normalRows = groups.normal ?? [];\n const selfRefRows = groups.selfRef ?? [];\n\n const chunks = chunkSize ? _.chunk(normalRows, chunkSize) : [normalRows];\n const uuidMap = new Map<string, any>();\n\n for (const chunk of chunks) {\n if (mode === \"insert\") {\n await wdb.insert(tableName, chunk);\n } else if (mode === \"upsert\") {\n await wdb.upsert(tableName, chunk);\n // await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));\n }\n\n // upsert된 row들을 다시 조회하여 uuidMap에 저장\n const uuids = chunk.map((row) => row.uuid);\n const upsertedRows = await wdb\n .from(tableName)\n .select(_.uniq([\"uuid\", \"id\", ...extractFields]))\n .where([\"uuid\", \"in\", uuids])\n .execute();\n upsertedRows.forEach((row: any) => {\n uuidMap.set(row.uuid, row);\n });\n }\n\n // 해당 테이블 참조를 실제 밸류로 변경\n refTables.map((table) => {\n table.rows = table.rows.map((row) => {\n Object.keys(row).map((key) => {\n const prop = row[key];\n if (isRefField(prop) && prop.of === tableName) {\n const parent = uuidMap.get(prop.uuid);\n if (parent === undefined) {\n console.error(prop);\n throw new Error(\n `존재하지 않는 uuid ${prop.uuid} -- in ${tableName}`\n );\n }\n row[key] = parent[prop.use ?? \"id\"];\n }\n });\n return row;\n });\n });\n\n const allIds = Array.from(uuidMap.values()).map((row) => row.id);\n\n // 자기 참조가 있는 경우 재귀적으로 upsert\n if (selfRefRows.length > 0) {\n // 처리된 데이터를 제외하고 다시 upsert\n table.rows = selfRefRows;\n const selfRefIds = await this.upsert(_wdb, tableName, chunkSize);\n allIds.push(...selfRefIds);\n }\n\n return allIds;\n }\n\n async updateBatch(\n wdb: Knex | Kysely<Database>,\n tableName: DriverSpec[D][\"table\"],\n options?: {\n chunkSize?: number;\n where?: DriverSpec[D][\"column\"] | DriverSpec[D][\"column\"][];\n }\n ): Promise<void> {\n options = _.defaults(options, {\n chunkSize: 500,\n where: \"id\",\n });\n\n if (this.hasTable(tableName) === false) {\n return;\n }\n const table = this.tables.get(tableName)!;\n if (table.rows.length === 0) {\n return;\n }\n\n const whereColumns = Array.isArray(options.where)\n ? options.where\n : [options.where ?? \"id\"];\n const rows = table.rows.map((_row) => {\n const { uuid, ...row } = _row;\n return row as RowWithId<string>;\n });\n\n await batchUpdate(\n DB.toClient(wdb),\n tableName,\n whereColumns as string[],\n rows,\n options.chunkSize\n );\n }\n}\n","/*\n 아래의 링크에서 참고해서 가져온 소스코드\n https://github.com/knex/knex/issues/5716\n*/\n\nimport { Knex } from \"knex\";\nimport { DB } from \"./db\";\nimport { KnexClient } from \"./drivers/knex/client\";\nimport { KyselyClient } from \"./drivers/kysely/client\";\nimport { Transaction } from \"kysely\";\nimport { Database } from \"./types\";\n\nexport type RowWithId<Id extends string> = {\n [key in Id]: any;\n} & Record<string, any>;\n\n/**\n * Batch update rows in a table. Technically its a patch since it only updates the specified columns. Any omitted columns will not be affected\n * @param db\n * @param tableName\n * @param ids\n * @param rows\n * @param chunkSize\n * @param trx\n */\nexport async function batchUpdate<Id extends string>(\n db: KnexClient | KyselyClient,\n tableName: string,\n ids: Id[],\n rows: RowWithId<Id>[],\n chunkSize = 50,\n trx: Knex.Transaction | Transaction<Database> | null = null\n) {\n const chunks: RowWithId<Id>[][] = [];\n for (let i = 0; i < rows.length; i += chunkSize) {\n chunks.push(rows.slice(i, i + chunkSize));\n }\n\n const executeUpdate = async (\n chunk: RowWithId<Id>[],\n transaction: KyselyClient | KnexClient\n ) => {\n const sql = generateBatchUpdateSQL(db, tableName, chunk, ids);\n return transaction.raw(sql);\n };\n\n if (trx) {\n for (const chunk of chunks) {\n await executeUpdate(chunk, DB.toClient(trx));\n }\n } else {\n await db.trx(async (newTrx) => {\n for (const chunk of chunks) {\n await executeUpdate(chunk, newTrx);\n }\n });\n }\n}\n\n/**\n * Generate a set of unique keys in a data array\n *\n * Example:\n * [ { a: 1, b: 2 }, { a: 3, c: 4 } ] => Set([ \"a\", \"b\", \"c\" ])\n * @param data\n */\nfunction generateKeySetFromData(data: Record<string, any>[]) {\n const keySet: Set<string> = new Set();\n for (const row of data) {\n for (const key of Object.keys(row)) {\n keySet.add(key);\n }\n }\n return keySet;\n}\n\nfunction generateBatchUpdateSQL<Id extends string>(\n db: KnexClient | KyselyClient,\n tableName: string,\n data: Record<string, any>[],\n identifiers: Id[]\n) {\n const keySet = generateKeySetFromData(data);\n const bindings = [];\n\n const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));\n if (invalidIdentifiers.length > 0) {\n throw new Error(\n `Invalid identifiers: ${invalidIdentifiers.join(\", \")}. Identifiers must exist in the data`\n );\n }\n\n const cases = [];\n for (const key of keySet) {\n if (identifiers.includes(key as Id)) continue;\n\n const rows = [];\n for (const row of data) {\n if (Object.hasOwnProperty.call(row, key)) {\n const whereClause = identifiers\n .map((id) => `\\`${id}\\` = ?`)\n .join(\" AND \");\n rows.push(`WHEN (${whereClause}) THEN ?`);\n bindings.push(...identifiers.map((i) => row[i]), row[key]);\n }\n }\n\n const whenThen = rows.join(\" \");\n cases.push(`\\`${key}\\` = CASE ${whenThen} ELSE \\`${key}\\` END`);\n }\n\n const whereInClauses = identifiers\n .map((col) => `${col} IN (${data.map(() => \"?\").join(\", \")})`)\n .join(\" AND \");\n\n const whereInBindings = identifiers.flatMap((col) =>\n data.map((row) => row[col])\n );\n\n const sql = db.createRawQuery(\n `UPDATE \\`${tableName}\\` SET ${cases.join(\", \")} WHERE ${whereInClauses}`,\n [...bindings, ...whereInBindings]\n );\n\n return sql;\n}\n"],"mappings":"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","names":["table"]}