From e13844c7f32a4fc046b3a9f4b56c2f3988a2919e Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Fri, 13 Dec 2024 18:02:03 +0200 Subject: [PATCH 01/18] support varchar for postgres using sql column type --- drizzle-seed/src/datasets/adjectives.ts | 2 + drizzle-seed/src/datasets/cityNames.ts | 2 + .../src/datasets/companyNameSuffixes.ts | 2 + drizzle-seed/src/datasets/countries.ts | 2 + drizzle-seed/src/datasets/emailDomains.ts | 2 + drizzle-seed/src/datasets/firstNames.ts | 2 + drizzle-seed/src/datasets/jobsTitles.ts | 2 + drizzle-seed/src/datasets/lastNames.ts | 2 + .../src/datasets/loremIpsumSentences.ts | 2 + drizzle-seed/src/datasets/states.ts | 2 + drizzle-seed/src/datasets/streetSuffix.ts | 2 + drizzle-seed/src/index.ts | 55 ++- .../src/services/GeneratorsWrappers.ts | 110 +++-- drizzle-seed/src/services/SeedService.ts | 392 ++++++++---------- drizzle-seed/src/types/tables.ts | 6 + 15 files changed, 332 insertions(+), 253 deletions(-) diff --git a/drizzle-seed/src/datasets/adjectives.ts b/drizzle-seed/src/datasets/adjectives.ts index c2b152af0..880e52636 100644 --- a/drizzle-seed/src/datasets/adjectives.ts +++ b/drizzle-seed/src/datasets/adjectives.ts @@ -4844,3 +4844,5 @@ export default [ 'zonked', 'zoological', ]; + +export const maxStringLength = 22; diff --git a/drizzle-seed/src/datasets/cityNames.ts b/drizzle-seed/src/datasets/cityNames.ts index 780b55213..3ea80747e 100644 --- a/drizzle-seed/src/datasets/cityNames.ts +++ b/drizzle-seed/src/datasets/cityNames.ts @@ -42857,3 +42857,5 @@ export default [ 'Garches', 'Chemini', ]; + +export const maxStringLength = 49; diff --git a/drizzle-seed/src/datasets/companyNameSuffixes.ts b/drizzle-seed/src/datasets/companyNameSuffixes.ts index ae8ce6163..1ce31a9c3 100644 --- a/drizzle-seed/src/datasets/companyNameSuffixes.ts +++ b/drizzle-seed/src/datasets/companyNameSuffixes.ts @@ -25,3 +25,5 @@ export default [ 'Co.', 'SCC', ]; + +export const maxStringLength = 7; diff --git a/drizzle-seed/src/datasets/countries.ts b/drizzle-seed/src/datasets/countries.ts index 4808fc5e5..eb1c001d0 100644 --- a/drizzle-seed/src/datasets/countries.ts +++ b/drizzle-seed/src/datasets/countries.ts @@ -169,3 +169,5 @@ export default [ 'Yemen', 'Zambia', ]; + +export const maxStringLength = 30; diff --git a/drizzle-seed/src/datasets/emailDomains.ts b/drizzle-seed/src/datasets/emailDomains.ts index 9904aad3e..ea323ed41 100644 --- a/drizzle-seed/src/datasets/emailDomains.ts +++ b/drizzle-seed/src/datasets/emailDomains.ts @@ -22,3 +22,5 @@ export default [ 'ymail.com', 'libero.it', ]; + +export const maxStringLength = 14; diff --git a/drizzle-seed/src/datasets/firstNames.ts b/drizzle-seed/src/datasets/firstNames.ts index 7ca0ff928..d719aa4e2 100644 --- a/drizzle-seed/src/datasets/firstNames.ts +++ b/drizzle-seed/src/datasets/firstNames.ts @@ -30277,3 +30277,5 @@ export default [ 'Lavasia', 'Laniqua', ]; + +export const maxStringLength = 15; diff --git a/drizzle-seed/src/datasets/jobsTitles.ts b/drizzle-seed/src/datasets/jobsTitles.ts index 3a38e3244..e7993da2a 100644 --- a/drizzle-seed/src/datasets/jobsTitles.ts +++ b/drizzle-seed/src/datasets/jobsTitles.ts @@ -150,3 +150,5 @@ export default [ 'Legal secretary', 'Market analyst', ]; + +export const maxStringLength = 35; diff --git a/drizzle-seed/src/datasets/lastNames.ts b/drizzle-seed/src/datasets/lastNames.ts index 117c5fe28..9d35f7cf7 100644 --- a/drizzle-seed/src/datasets/lastNames.ts +++ b/drizzle-seed/src/datasets/lastNames.ts @@ -50001,3 +50001,5 @@ export default [ 'Thagard', 'Leavelle', ]; + +export const maxStringLength = 15; diff --git a/drizzle-seed/src/datasets/loremIpsumSentences.ts b/drizzle-seed/src/datasets/loremIpsumSentences.ts index f03277d86..64fe59f71 100644 --- a/drizzle-seed/src/datasets/loremIpsumSentences.ts +++ b/drizzle-seed/src/datasets/loremIpsumSentences.ts @@ -1637,3 +1637,5 @@ export default [ 'Sed gravida enim quis nunc interdum imperdiet.', 'Proin cursus odio ac dolor blandit, quis sollicitudin ante rutrum.', ]; + +export const maxStringLength = 190; diff --git a/drizzle-seed/src/datasets/states.ts b/drizzle-seed/src/datasets/states.ts index 1de77160d..cd66cf330 100644 --- a/drizzle-seed/src/datasets/states.ts +++ b/drizzle-seed/src/datasets/states.ts @@ -50,3 +50,5 @@ export default [ 'Wisconsin', 'Wyoming', ]; + +export const maxStringLength = 14; diff --git a/drizzle-seed/src/datasets/streetSuffix.ts b/drizzle-seed/src/datasets/streetSuffix.ts index e9b20c392..90a70c2c6 100644 --- a/drizzle-seed/src/datasets/streetSuffix.ts +++ b/drizzle-seed/src/datasets/streetSuffix.ts @@ -198,3 +198,5 @@ export default [ 'Well', 'Wells', ]; + +export const maxStringLength = 10; diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index 8596863fb..4f72080ab 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -222,6 +222,8 @@ export async function seedForDrizzleStudio( name: col.name, dataType: 'string', columnType: col.type, + // TODO: revise later + typeParams: {}, default: col.default, hasDefault: col.default === undefined ? false : true, isUnique: col.isUnique === undefined ? false : col.isUnique, @@ -322,7 +324,7 @@ export async function seedForDrizzleStudio( export function seed< DB extends | PgDatabase - | MySqlDatabase + | MySqlDatabase | BaseSQLiteDatabase, SCHEMA extends { [key: string]: @@ -415,7 +417,7 @@ const seedFunc = async ( export async function reset< DB extends | PgDatabase - | MySqlDatabase + | MySqlDatabase | BaseSQLiteDatabase, SCHEMA extends { [key: string]: @@ -604,7 +606,8 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { ): Column['baseColumn'] => { const baseColumnResult: Column['baseColumn'] = { name: baseColumn.name, - columnType: baseColumn.columnType.replace('Pg', '').toLowerCase(), + columnType: baseColumn.getSQLType(), + typeParams: getTypeParams(baseColumn.getSQLType()), dataType: baseColumn.dataType, size: (baseColumn as PgArray).size, hasDefault: baseColumn.hasDefault, @@ -619,12 +622,54 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { return baseColumnResult; }; + const getTypeParams = (sqlType: string) => { + // get type params and set only type + const typeParams: Column['typeParams'] = {}; + + // handle dimensions + if (sqlType.includes('[')) { + const match = sqlType.match(/\[\w*]/g); + if (match) { + typeParams['dimensions'] = match.length; + } + } + + if ( + sqlType.startsWith('numeric') + || sqlType.startsWith('decimal') + || sqlType.startsWith('double precision') + || sqlType.startsWith('real') + ) { + const match = sqlType.match(/\((\d+),(\d+)\)/); + if (match) { + typeParams['precision'] = Number(match[1]); + typeParams['scale'] = Number(match[2]); + } + } else if ( + sqlType.startsWith('varchar') + || sqlType.startsWith('bpchar') + || sqlType.startsWith('char') + || sqlType.startsWith('bit') + || sqlType.startsWith('time') + || sqlType.startsWith('timestamp') + || sqlType.startsWith('interval') + ) { + const match = sqlType.match(/\((\d+)\)/); + if (match) { + typeParams['length'] = Number(match[1]); + } + } + + return typeParams; + }; + // console.log(tableConfig.columns); tables.push({ name: dbToTsTableNamesMap[tableConfig.name] as string, columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, - columnType: column.columnType.replace('Pg', '').toLowerCase(), + columnType: column.getSQLType(), + typeParams: getTypeParams(column.getSQLType()), dataType: column.dataType, size: (column as PgArray).size, hasDefault: column.hasDefault, @@ -852,6 +897,7 @@ const getMySqlInfo = (schema: { [key: string]: MySqlTable }) => { columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, columnType: column.columnType.replace('MySql', '').toLowerCase(), + typeParams: {}, dataType: column.dataType, hasDefault: column.hasDefault, default: column.default, @@ -1039,6 +1085,7 @@ const getSqliteInfo = (schema: { [key: string]: SQLiteTable }) => { columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, columnType: column.columnType.replace('SQLite', '').toLowerCase(), + typeParams: {}, dataType: column.dataType, hasDefault: column.hasDefault, default: column.default, diff --git a/drizzle-seed/src/services/GeneratorsWrappers.ts b/drizzle-seed/src/services/GeneratorsWrappers.ts index 06d6adeb5..a1a751f9a 100644 --- a/drizzle-seed/src/services/GeneratorsWrappers.ts +++ b/drizzle-seed/src/services/GeneratorsWrappers.ts @@ -24,6 +24,7 @@ export abstract class AbstractGenerator { public timeSpent?: number; public arraySize?: number; public baseColumnDataType?: string; + public length?: number; constructor(public params: T) {} @@ -1337,14 +1338,26 @@ export class GenerateString extends AbstractGenerator<{ }> { static override readonly [entityKind]: string = 'GenerateString'; - private state: { rng: prand.RandomGenerator } | undefined; + private state: { + rng: prand.RandomGenerator; + minStringLength: number; + maxStringLength: number; + } | undefined; override uniqueVersionOfGen = GenerateUniqueString; override init({ count, seed }: { count: number; seed: number }) { super.init({ count, seed }); + let minStringLength = 8; + let maxStringLength = 20; + if (this.length !== undefined) { + maxStringLength = this.length; + if (maxStringLength === 1) minStringLength = maxStringLength; + if (maxStringLength < minStringLength) minStringLength = 1; + } + const rng = prand.xoroshiro128plus(seed); - this.state = { rng }; + this.state = { rng, minStringLength, maxStringLength }; } generate() { @@ -1352,8 +1365,8 @@ export class GenerateString extends AbstractGenerator<{ throw new Error('state is not defined.'); } - const minStringLength = 7; - const maxStringLength = 20; + const minStringLength = this.state.minStringLength, + maxStringLength = this.state.maxStringLength; const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let idx: number, strLength: number, @@ -1380,12 +1393,31 @@ export class GenerateString extends AbstractGenerator<{ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean }> { static override readonly [entityKind]: string = 'GenerateUniqueString'; - private state: { rng: prand.RandomGenerator } | undefined; + private state: { + rng: prand.RandomGenerator; + minStringLength: number; + maxStringLength: number; + } | undefined; public override isUnique = true; - override init({ seed }: { seed: number }) { + override init({ seed, count }: { seed: number; count: number }) { const rng = prand.xoroshiro128plus(seed); - this.state = { rng }; + + let minStringLength = 8; + let maxStringLength = 20; + // TODO: revise later + if (this.length !== undefined) { + maxStringLength = this.length; + if (maxStringLength === 1 || maxStringLength < minStringLength) minStringLength = maxStringLength; + } + + if (maxStringLength < count.toString(16).length) { + throw new Error( + `You can't generate ${count} unique strings, with a maximum string length of ${maxStringLength}.`, + ); + } + + this.state = { rng, minStringLength, maxStringLength }; } generate({ i }: { i: number }) { @@ -1393,8 +1425,8 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean throw new Error('state is not defined.'); } - const minStringLength = 7; - const maxStringLength = 20; + const minStringLength = this.state.minStringLength, + maxStringLength = this.state.maxStringLength; const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let idx: number, strLength: number; @@ -1416,7 +1448,7 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean currStr += stringChars[idx]; } - return currStr.slice(0, 4) + uniqueStr + currStr.slice(4); + return uniqueStr + currStr; } } @@ -2871,7 +2903,7 @@ export const generatorsFuncs = { /** * generates same given value each time the generator is called. * @param defaultValue - value you want to generate - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -2890,7 +2922,7 @@ export const generatorsFuncs = { * generates values from given array * @param values - array of values you want to generate. can be array of weighted values. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -2952,7 +2984,7 @@ export const generatorsFuncs = { * precision equals 10 means that values will be accurate to one tenth (1.2, 34.6); * precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67). * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -2974,7 +3006,7 @@ export const generatorsFuncs = { * @param minValue - lower border of range. * @param maxValue - upper border of range. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -2993,7 +3025,7 @@ export const generatorsFuncs = { /** * generates boolean values(true or false) - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3013,7 +3045,7 @@ export const generatorsFuncs = { * generates date within given range. * @param minDate - lower border of range. * @param maxDate - upper border of range. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3031,7 +3063,7 @@ export const generatorsFuncs = { /** * generates time in 24 hours style. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3049,7 +3081,7 @@ export const generatorsFuncs = { /** * generates timestamps. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3067,7 +3099,7 @@ export const generatorsFuncs = { /** * generates datetime objects. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3085,7 +3117,7 @@ export const generatorsFuncs = { /** * generates years. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3103,7 +3135,7 @@ export const generatorsFuncs = { /** * generates json objects with fixed structure. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * json structure can equal this: * ``` @@ -3148,7 +3180,7 @@ export const generatorsFuncs = { * interval example: "1 years 12 days 5 minutes" * * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * @example * ```ts * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ @@ -3166,7 +3198,7 @@ export const generatorsFuncs = { /** * generates random strings. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3185,7 +3217,7 @@ export const generatorsFuncs = { /** * generates v4 UUID strings if arraySize is not specified, or v4 UUID 1D arrays if it is. * - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3205,7 +3237,7 @@ export const generatorsFuncs = { /** * generates person's first names. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3224,7 +3256,7 @@ export const generatorsFuncs = { /** * generates person's last names. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3243,7 +3275,7 @@ export const generatorsFuncs = { /** * generates person's full names. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3261,7 +3293,7 @@ export const generatorsFuncs = { /** * generates unique emails. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3278,7 +3310,7 @@ export const generatorsFuncs = { /** * generates unique phone numbers. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @param template - phone number template, where all '#' symbols will be substituted with generated digits. * @param prefixes - array of any string you want to be your phone number prefixes.(not compatible with template property) @@ -3319,7 +3351,7 @@ export const generatorsFuncs = { /** * generates country's names. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3338,7 +3370,7 @@ export const generatorsFuncs = { /** * generates city's names. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3357,7 +3389,7 @@ export const generatorsFuncs = { /** * generates street address. * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3375,7 +3407,7 @@ export const generatorsFuncs = { /** * generates job titles. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3394,7 +3426,7 @@ export const generatorsFuncs = { * generates postal codes. * * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3412,7 +3444,7 @@ export const generatorsFuncs = { /** * generates states of America. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3431,7 +3463,7 @@ export const generatorsFuncs = { * generates company's names. * * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3451,7 +3483,7 @@ export const generatorsFuncs = { * generates 'lorem ipsum' text sentences. * * @param sentencesCount - number of sentences you want to generate as one generated value(string). - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3474,7 +3506,7 @@ export const generatorsFuncs = { * @param maxXValue - upper bound of range for x coordinate. * @param minYValue - lower bound of range for y coordinate. * @param maxYValue - upper bound of range for y coordinate. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts @@ -3508,7 +3540,7 @@ export const generatorsFuncs = { * @param maxBValue - upper bound of range for y parameter. * @param minCValue - lower bound of range for y parameter. * @param maxCValue - upper bound of range for y parameter. - * @param arraySize - number of elements in each one-dimensional array. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) * * @example * ```ts diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 4ee43a921..579d070a6 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -252,7 +252,6 @@ class SeedService { } if (columnPossibleGenerator.generator === undefined) { - console.log(col); throw new Error( `column with type ${col.columnType} is not supported for now.`, ); @@ -260,6 +259,7 @@ class SeedService { columnPossibleGenerator.generator.isUnique = col.isUnique; columnPossibleGenerator.generator.dataType = col.dataType; + columnPossibleGenerator.generator.length = col.typeParams.length; tablePossibleGenerators.columnsPossibleGenerators.push( columnPossibleGenerator, @@ -432,263 +432,235 @@ class SeedService { table: Table, col: Column, ) => { - let generator: AbstractGenerator | undefined; + const pickGenerator = (table: Table, col: Column) => { + // ARRAY + if (col.columnType.match(/\[\w*]/g) !== null && col.baseColumn !== undefined) { + const baseColumnGen = this.pickGeneratorForPostgresColumn(table, col.baseColumn!) as AbstractGenerator; + if (baseColumnGen === undefined) { + throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); + } + const generator = new GenerateArray({ baseColumnGen, size: col.size }); - // INT ------------------------------------------------------------------------------------------------------------ - if ( - (col.columnType.includes('serial') - || col.columnType === 'integer' - || col.columnType === 'smallint' - || col.columnType.includes('bigint')) - && table.primaryKeys.includes(col.name) - ) { - generator = new GenerateIntPrimaryKey({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // INT ------------------------------------------------------------------------------------------------------------ + if ( + (col.columnType.includes('serial') + || col.columnType === 'integer' + || col.columnType === 'smallint' + || col.columnType.includes('bigint')) + && table.primaryKeys.includes(col.name) + ) { + const generator = new GenerateIntPrimaryKey({}); - let minValue: number | bigint | undefined; - let maxValue: number | bigint | undefined; - if (col.columnType.includes('serial')) { - minValue = 1; - if (col.columnType === 'smallserial') { - // 2^16 / 2 - 1, 2 bytes - maxValue = 32767; - } else if (col.columnType === 'serial') { - // 2^32 / 2 - 1, 4 bytes - maxValue = 2147483647; - } else if (col.columnType === 'bigserial') { - // 2^64 / 2 - 1, 8 bytes - minValue = BigInt(1); - maxValue = BigInt('9223372036854775807'); + return generator; } - } else if (col.columnType.includes('int')) { - if (col.columnType === 'smallint') { - // 2^16 / 2 - 1, 2 bytes - minValue = -32768; - maxValue = 32767; - } else if (col.columnType === 'integer') { - // 2^32 / 2 - 1, 4 bytes - minValue = -2147483648; - maxValue = 2147483647; - } else if (col.columnType.includes('bigint')) { - if (col.dataType === 'bigint') { + + let minValue: number | bigint | undefined; + let maxValue: number | bigint | undefined; + if (col.columnType.includes('serial')) { + minValue = 1; + if (col.columnType === 'smallserial') { + // 2^16 / 2 - 1, 2 bytes + maxValue = 32767; + } else if (col.columnType === 'serial') { + // 2^32 / 2 - 1, 4 bytes + maxValue = 2147483647; + } else if (col.columnType === 'bigserial') { // 2^64 / 2 - 1, 8 bytes - minValue = BigInt('-9223372036854775808'); + minValue = BigInt(1); maxValue = BigInt('9223372036854775807'); - } else if (col.dataType === 'number') { - // if you’re expecting values above 2^31 but below 2^53 - minValue = -9007199254740991; - maxValue = 9007199254740991; + } + } else if (col.columnType.includes('int')) { + if (col.columnType === 'smallint') { + // 2^16 / 2 - 1, 2 bytes + minValue = -32768; + maxValue = 32767; + } else if (col.columnType === 'integer') { + // 2^32 / 2 - 1, 4 bytes + minValue = -2147483648; + maxValue = 2147483647; + } else if (col.columnType.includes('bigint')) { + if (col.dataType === 'bigint') { + // 2^64 / 2 - 1, 8 bytes + minValue = BigInt('-9223372036854775808'); + maxValue = BigInt('9223372036854775807'); + } else if (col.dataType === 'number') { + // if you’re expecting values above 2^31 but below 2^53 + minValue = -9007199254740991; + maxValue = 9007199254740991; + } } } - } - if ( - col.columnType.includes('int') - && !col.columnType.includes('interval') - && !col.columnType.includes('point') - ) { - generator = new GenerateInt({ - minValue, - maxValue, - }); + if ( + col.columnType.includes('int') + && !col.columnType.includes('interval') + && !col.columnType.includes('point') + ) { + const generator = new GenerateInt({ + minValue, + maxValue, + }); - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + return generator; + } - if (col.columnType.includes('serial')) { - generator = new GenerateIntPrimaryKey({}); + if (col.columnType.includes('serial')) { + const generator = new GenerateIntPrimaryKey({}); - (generator as GenerateIntPrimaryKey).maxValue = maxValue; - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + (generator as GenerateIntPrimaryKey).maxValue = maxValue; - // NUMBER(real, double, decimal, numeric) - if ( - col.columnType === 'real' - || col.columnType === 'doubleprecision' - || col.columnType === 'decimal' - || col.columnType === 'numeric' - ) { - generator = new GenerateNumber({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // NUMBER(real, double, decimal, numeric) + if ( + col.columnType === 'real' + || col.columnType === 'double precision' + || col.columnType === 'decimal' + || col.columnType === 'numeric' + ) { + const generator = new GenerateNumber({}); - // STRING - if ( - (col.columnType === 'text' - || col.columnType === 'varchar' - || col.columnType === 'char') - && table.primaryKeys.includes(col.name) - ) { - generator = new GenerateUniqueString({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // STRING + if ( + (col.columnType === 'text' + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('char')) + && table.primaryKeys.includes(col.name) + ) { + const generator = new GenerateUniqueString({}); - if ( - (col.columnType === 'text' - || col.columnType === 'varchar' - || col.columnType === 'char') - && col.name.toLowerCase().includes('name') - ) { - generator = new GenerateFirstName({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if ( + (col.columnType === 'text' + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('char')) + && col.name.toLowerCase().includes('name') + ) { + const generator = new GenerateFirstName({}); - if ( - (col.columnType === 'text' - || col.columnType === 'varchar' - || col.columnType === 'char') - && col.name.toLowerCase().includes('email') - ) { - generator = new GenerateEmail({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if ( + (col.columnType === 'text' + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('char')) + && col.name.toLowerCase().includes('email') + ) { + const generator = new GenerateEmail({}); - if ( - col.columnType === 'text' - || col.columnType === 'varchar' - || col.columnType === 'char' - ) { - // console.log(col, table) - generator = new GenerateString({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if ( + col.columnType === 'text' + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('char') + ) { + // console.log(col, table) + const generator = new GenerateString({}); - // UUID - if (col.columnType === 'uuid') { - generator = new GenerateUUID({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // UUID + if (col.columnType === 'uuid') { + const generator = new GenerateUUID({}); - // BOOLEAN - if (col.columnType === 'boolean') { - generator = new GenerateBoolean({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // BOOLEAN + if (col.columnType === 'boolean') { + const generator = new GenerateBoolean({}); - // DATE, TIME, TIMESTAMP - if (col.columnType.includes('date')) { - generator = new GenerateDate({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // DATE, TIME, TIMESTAMP + if (col.columnType.includes('date')) { + const generator = new GenerateDate({}); - if (col.columnType === 'time') { - generator = new GenerateTime({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if (col.columnType === 'time') { + const generator = new GenerateTime({}); - if (col.columnType.includes('timestamp')) { - generator = new GenerateTimestamp({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if (col.columnType.includes('timestamp')) { + const generator = new GenerateTimestamp({}); - // JSON, JSONB - if (col.columnType === 'json' || col.columnType === 'jsonb') { - generator = new GenerateJson({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // JSON, JSONB + if (col.columnType === 'json' || col.columnType === 'jsonb') { + const generator = new GenerateJson({}); - // if (col.columnType === "jsonb") { - // const generator = new GenerateJsonb({}); - // return generator; - // } + return generator; + } - // ENUM - if (col.enumValues !== undefined) { - generator = new GenerateEnum({ - enumValues: col.enumValues, - }); + // if (col.columnType === "jsonb") { + // const generator = new GenerateJsonb({}); + // return generator; + // } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // ENUM + if (col.enumValues !== undefined) { + const generator = new GenerateEnum({ + enumValues: col.enumValues, + }); - // INTERVAL - if (col.columnType === 'interval') { - generator = new GenerateInterval({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // INTERVAL + if (col.columnType === 'interval') { + const generator = new GenerateInterval({}); - // POINT, LINE - if (col.columnType.includes('point')) { - generator = new GeneratePoint({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + // POINT, LINE + if (col.columnType.includes('point')) { + const generator = new GeneratePoint({}); - if (col.columnType.includes('line')) { - generator = new GenerateLine({}); + return generator; + } - generator.isUnique = col.isUnique; - generator.dataType = col.dataType; - return generator; - } + if (col.columnType.includes('line')) { + const generator = new GenerateLine({}); + + return generator; + } - // ARRAY - if (col.columnType.includes('array') && col.baseColumn !== undefined) { - const baseColumnGen = this.pickGeneratorForPostgresColumn(table, col.baseColumn!) as AbstractGenerator; - if (baseColumnGen === undefined) { - throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); + if (col.hasDefault && col.default !== undefined) { + const generator = new GenerateDefault({ + defaultValue: col.default, + }); + return generator; } - generator = new GenerateArray({ baseColumnGen, size: col.size }); + return; + }; + + const generator = pickGenerator(table, col); + if (generator !== undefined) { generator.isUnique = col.isUnique; generator.dataType = col.dataType; - return generator; - } - - if (col.hasDefault && col.default !== undefined) { - generator = new GenerateDefault({ - defaultValue: col.default, - }); - return generator; + generator.length = col.typeParams.length; } return generator; diff --git a/drizzle-seed/src/types/tables.ts b/drizzle-seed/src/types/tables.ts index 8473179ed..dc28c748d 100644 --- a/drizzle-seed/src/types/tables.ts +++ b/drizzle-seed/src/types/tables.ts @@ -4,6 +4,12 @@ export type Column = { name: string; dataType: string; columnType: string; + typeParams: { + precision?: number; + scale?: number; + length?: number; + dimensions?: number; + }; size?: number; default?: any; hasDefault: boolean; From 05d907bf3f409f4425abe528b6ec889b5f03300a Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Mon, 16 Dec 2024 13:09:43 +0200 Subject: [PATCH 02/18] added checks to string-like generators, updated sqlite and mysql seeding using sql column type --- drizzle-seed/src/index.ts | 35 +++- .../src/services/GeneratorsWrappers.ts | 196 ++++++++++++++++-- drizzle-seed/src/services/SeedService.ts | 19 +- 3 files changed, 222 insertions(+), 28 deletions(-) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index a60987867..fde4442a1 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -898,12 +898,41 @@ const getMySqlInfo = (schema: { [key: string]: MySqlTable }) => { } tableRelations[dbToTsTableNamesMap[tableConfig.name] as string]!.push(...newRelations); + const getTypeParams = (sqlType: string) => { + // get type params and set only type + const typeParams: Column['typeParams'] = {}; + + if ( + sqlType.startsWith('decimal') + || sqlType.startsWith('real') + || sqlType.startsWith('double') + || sqlType.startsWith('float') + ) { + const match = sqlType.match(/\((\d+),(\d+)\)/); + if (match) { + typeParams['precision'] = Number(match[1]); + typeParams['scale'] = Number(match[2]); + } + } else if ( + sqlType.startsWith('varchar') + || sqlType.startsWith('binary') + || sqlType.startsWith('varbinary') + ) { + const match = sqlType.match(/\((\d+)\)/); + if (match) { + typeParams['length'] = Number(match[1]); + } + } + + return typeParams; + }; + tables.push({ name: dbToTsTableNamesMap[tableConfig.name] as string, columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, - columnType: column.columnType.replace('MySql', '').toLowerCase(), - typeParams: {}, + columnType: column.getSQLType(), + typeParams: getTypeParams(column.getSQLType()), dataType: column.dataType, hasDefault: column.hasDefault, default: column.default, @@ -1092,7 +1121,7 @@ const getSqliteInfo = (schema: { [key: string]: SQLiteTable }) => { name: dbToTsTableNamesMap[tableConfig.name] as string, columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, - columnType: column.columnType.replace('SQLite', '').toLowerCase(), + columnType: column.getSQLType(), typeParams: {}, dataType: column.dataType, hasDefault: column.hasDefault, diff --git a/drizzle-seed/src/services/GeneratorsWrappers.ts b/drizzle-seed/src/services/GeneratorsWrappers.ts index a1a751f9a..374fa6447 100644 --- a/drizzle-seed/src/services/GeneratorsWrappers.ts +++ b/drizzle-seed/src/services/GeneratorsWrappers.ts @@ -1,17 +1,17 @@ import { entityKind } from 'drizzle-orm'; import prand from 'pure-rand'; -import adjectives from '../datasets/adjectives.ts'; -import cityNames from '../datasets/cityNames.ts'; -import companyNameSuffixes from '../datasets/companyNameSuffixes.ts'; -import countries from '../datasets/countries.ts'; -import emailDomains from '../datasets/emailDomains.ts'; -import firstNames from '../datasets/firstNames.ts'; -import jobsTitles from '../datasets/jobsTitles.ts'; -import lastNames from '../datasets/lastNames.ts'; -import loremIpsumSentences from '../datasets/loremIpsumSentences.ts'; +import adjectives, { maxStringLength as maxAdjectiveLength } from '../datasets/adjectives.ts'; +import cityNames, { maxStringLength as maxCityNameLength } from '../datasets/cityNames.ts'; +import companyNameSuffixes, { maxStringLength as maxCompanyNameSuffixLength } from '../datasets/companyNameSuffixes.ts'; +import countries, { maxStringLength as maxCountryLength } from '../datasets/countries.ts'; +import emailDomains, { maxStringLength as maxEmailDomainLength } from '../datasets/emailDomains.ts'; +import firstNames, { maxStringLength as maxFirstNameLength } from '../datasets/firstNames.ts'; +import jobsTitles, { maxStringLength as maxJobTitleLength } from '../datasets/jobsTitles.ts'; +import lastNames, { maxStringLength as maxLastNameLength } from '../datasets/lastNames.ts'; +import loremIpsumSentences, { maxStringLength as maxLoremIpsumLength } from '../datasets/loremIpsumSentences.ts'; import phonesInfo from '../datasets/phonesInfo.ts'; -import states from '../datasets/states.ts'; -import streetSuffix from '../datasets/streetSuffix.ts'; +import states, { maxStringLength as maxStateLength } from '../datasets/states.ts'; +import streetSuffix, { maxStringLength as maxStreetSuffixLength } from '../datasets/streetSuffix.ts'; import { fastCartesianProduct, fillTemplate, getWeightedIndices, isObject } from './utils.ts'; export abstract class AbstractGenerator { @@ -24,7 +24,7 @@ export abstract class AbstractGenerator { public timeSpent?: number; public arraySize?: number; public baseColumnDataType?: string; - public length?: number; + public stringLength?: number; constructor(public params: T) {} @@ -1350,8 +1350,8 @@ export class GenerateString extends AbstractGenerator<{ let minStringLength = 8; let maxStringLength = 20; - if (this.length !== undefined) { - maxStringLength = this.length; + if (this.stringLength !== undefined) { + maxStringLength = this.stringLength; if (maxStringLength === 1) minStringLength = maxStringLength; if (maxStringLength < minStringLength) minStringLength = 1; } @@ -1406,8 +1406,8 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean let minStringLength = 8; let maxStringLength = 20; // TODO: revise later - if (this.length !== undefined) { - maxStringLength = this.length; + if (this.stringLength !== undefined) { + maxStringLength = this.stringLength; if (maxStringLength === 1 || maxStringLength < minStringLength) minStringLength = maxStringLength; } @@ -1515,6 +1515,12 @@ export class GenerateFirstName extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxFirstNameLength) { + throw new Error( + `You can't use first name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxFirstNameLength}.`, + ); + } + this.state = { rng }; } @@ -1547,6 +1553,13 @@ export class GenerateUniqueFirstName extends AbstractGenerator<{ if (count > firstNames.length) { throw new Error('count exceeds max number of unique first names.'); } + + if (this.stringLength !== undefined && this.stringLength < maxFirstNameLength) { + throw new Error( + `You can't use first name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxFirstNameLength}.`, + ); + } + const genIndicesObj = new GenerateUniqueInt({ minValue: 0, maxValue: firstNames.length - 1 }); genIndicesObj.init({ count, seed }); @@ -1582,6 +1595,12 @@ export class GenerateLastName extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxLastNameLength) { + throw new Error( + `You can't use last name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxLastNameLength}.`, + ); + } + this.state = { rng }; } generate() { @@ -1609,6 +1628,12 @@ export class GenerateUniqueLastName extends AbstractGenerator<{ isUnique?: boole throw new Error('count exceeds max number of unique last names.'); } + if (this.stringLength !== undefined && this.stringLength < maxLastNameLength) { + throw new Error( + `You can't use last name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxLastNameLength}.`, + ); + } + const genIndicesObj = new GenerateUniqueInt({ minValue: 0, maxValue: lastNames.length - 1 }); genIndicesObj.init({ count, seed }); @@ -1643,6 +1668,14 @@ export class GenerateFullName extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < (maxFirstNameLength + maxLastNameLength + 1)) { + throw new Error( + `You can't use full name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${ + maxFirstNameLength + maxLastNameLength + 1 + }.`, + ); + } + this.state = { rng }; } @@ -1686,6 +1719,15 @@ export class GenerateUniqueFullName extends AbstractGenerator<{ `count exceeds max number of unique full names(${maxUniqueFullNamesNumber}).`, ); } + + if (this.stringLength !== undefined && this.stringLength < (maxFirstNameLength + maxLastNameLength + 1)) { + throw new Error( + `You can't use full name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${ + maxFirstNameLength + maxLastNameLength + 1 + }.`, + ); + } + const rng = prand.xoroshiro128plus(seed); const fullnameSet = new Set(); @@ -1747,6 +1789,13 @@ export class GenerateEmail extends AbstractGenerator<{ ); } + const maxEmailLength = maxAdjectiveLength + maxFirstNameLength + maxEmailDomainLength + 2; + if (this.stringLength !== undefined && this.stringLength < maxEmailLength) { + throw new Error( + `You can't use email generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxEmailLength}.`, + ); + } + const arraysToGenerateFrom = [adjectivesArray, namesArray, domainsArray]; const genIndicesObj = new GenerateUniqueInt({ minValue: 0, @@ -1805,6 +1854,13 @@ export class GeneratePhoneNumber extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); if (template !== undefined) { + if (this.stringLength !== undefined && this.stringLength < template.length) { + throw new Error( + `Length of phone number template is shorter than db column length restriction: ${this.stringLength}. + Set the maximum string length to at least ${template.length}.`, + ); + } + const iterArray = [...template.matchAll(/#/g)]; const placeholdersCount = iterArray.length; @@ -1860,6 +1916,17 @@ export class GeneratePhoneNumber extends AbstractGenerator<{ } } + const maxPrefixLength = Math.max(...prefixesArray.map((prefix) => prefix.length)); + const maxGeneratedDigits = Math.max(...generatedDigitsNumbers); + + if (this.stringLength !== undefined && this.stringLength < (maxPrefixLength + maxGeneratedDigits)) { + throw new Error( + `You can't use phone number generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${ + maxPrefixLength + maxGeneratedDigits + }.`, + ); + } + if (new Set(prefixesArray).size !== prefixesArray.length) { throw new Error('prefixes are not unique.'); } @@ -1939,7 +2006,7 @@ export class GeneratePhoneNumber extends AbstractGenerator<{ numberBody = '0'.repeat(digitsNumberDiff) + numberBody; } - phoneNumber = (prefix.includes('+') ? '' : '+') + prefix + '' + numberBody; + phoneNumber = prefix + '' + numberBody; return phoneNumber; } else { @@ -1972,6 +2039,12 @@ export class GenerateCountry extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxCountryLength) { + throw new Error( + `You can't use country generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCountryLength}.`, + ); + } + this.state = { rng }; } @@ -2002,6 +2075,12 @@ export class GenerateUniqueCountry extends AbstractGenerator<{ isUnique?: boolea throw new Error('count exceeds max number of unique countries.'); } + if (this.stringLength !== undefined && this.stringLength < maxCountryLength) { + throw new Error( + `You can't use country generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCountryLength}.`, + ); + } + const genIndicesObj = new GenerateUniqueInt({ minValue: 0, maxValue: countries.length - 1 }); genIndicesObj.init({ count, seed }); @@ -2034,6 +2113,12 @@ export class GenerateJobTitle extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxJobTitleLength) { + throw new Error( + `You can't use job title generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxJobTitleLength}.`, + ); + } + this.state = { rng }; } @@ -2066,6 +2151,14 @@ export class GenerateStreetAdddress extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); const possStreetNames = [firstNames, lastNames]; + + const maxStreetAddressLength = 4 + Math.max(maxFirstNameLength, maxLastNameLength) + 1 + maxStreetSuffixLength; + if (this.stringLength !== undefined && this.stringLength < maxStreetAddressLength) { + throw new Error( + `You can't use street address generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxStreetAddressLength}.`, + ); + } + this.state = { rng, possStreetNames }; } @@ -2116,6 +2209,13 @@ export class GenerateUniqueStreetAdddress extends AbstractGenerator<{ isUnique?: ); } + const maxStreetAddressLength = 4 + Math.max(maxFirstNameLength, maxLastNameLength) + 1 + maxStreetSuffixLength; + if (this.stringLength !== undefined && this.stringLength < maxStreetAddressLength) { + throw new Error( + `You can't use street address generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxStreetAddressLength}.`, + ); + } + const rng = prand.xoroshiro128plus(seed); // ["1", "2", ..., "999"] @@ -2193,6 +2293,12 @@ export class GenerateCity extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxCityNameLength) { + throw new Error( + `You can't use city generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCityNameLength}.`, + ); + } + this.state = { rng }; } @@ -2221,6 +2327,12 @@ export class GenerateUniqueCity extends AbstractGenerator<{ isUnique?: boolean } throw new Error('count exceeds max number of unique cities.'); } + if (this.stringLength !== undefined && this.stringLength < maxCityNameLength) { + throw new Error( + `You can't use city generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCityNameLength}.`, + ); + } + const genIndicesObj = new GenerateUniqueInt({ minValue: 0, maxValue: cityNames.length - 1 }); genIndicesObj.init({ count, seed }); @@ -2257,6 +2369,13 @@ export class GeneratePostcode extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); const templates = ['#####', '#####-####']; + const maxPostcodeLength = Math.max(...templates.map((template) => template.length)); + if (this.stringLength !== undefined && this.stringLength < maxPostcodeLength) { + throw new Error( + `You can't use postcode generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxPostcodeLength}.`, + ); + } + this.state = { rng, templates }; } @@ -2330,6 +2449,13 @@ export class GenerateUniquePostcode extends AbstractGenerator<{ isUnique?: boole }, ]; + const maxPostcodeLength = Math.max(...templates.map((template) => template.template.length)); + if (this.stringLength !== undefined && this.stringLength < maxPostcodeLength) { + throw new Error( + `You can't use postcode generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxPostcodeLength}.`, + ); + } + for (const templateObj of templates) { templateObj.indicesGen.skipCheck = true; templateObj.indicesGen.init({ count, seed }); @@ -2381,6 +2507,12 @@ export class GenerateState extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); + if (this.stringLength !== undefined && this.stringLength < maxStateLength) { + throw new Error( + `You can't use state generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxStateLength}.`, + ); + } + this.state = { rng }; } @@ -2419,6 +2551,17 @@ export class GenerateCompanyName extends AbstractGenerator<{ { template: '#, # and #', placeholdersCount: 3 }, ]; + // max( { template: '#', placeholdersCount: 1 }, { template: '#, # and #', placeholdersCount: 3 } ) + const maxCompanyNameLength = Math.max( + maxLastNameLength + maxCompanyNameSuffixLength + 1, + 3 * maxLastNameLength + 7, + ); + if (this.stringLength !== undefined && this.stringLength < maxCompanyNameLength) { + throw new Error( + `You can't use company name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCompanyNameLength}.`, + ); + } + this.state = { rng, templates }; } @@ -2482,6 +2625,17 @@ export class GenerateUniqueCompanyName extends AbstractGenerator<{ isUnique?: bo ); } + // max( { template: '#', placeholdersCount: 1 }, { template: '#, # and #', placeholdersCount: 3 } ) + const maxCompanyNameLength = Math.max( + maxLastNameLength + maxCompanyNameSuffixLength + 1, + 3 * maxLastNameLength + 7, + ); + if (this.stringLength !== undefined && this.stringLength < maxCompanyNameLength) { + throw new Error( + `You can't use company name generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxCompanyNameLength}.`, + ); + } + const rng = prand.xoroshiro128plus(seed); // when count reach maxUniqueCompanyNameNumber template will be deleted from array const templates = [ @@ -2570,6 +2724,14 @@ export class GenerateLoremIpsum extends AbstractGenerator<{ const rng = prand.xoroshiro128plus(seed); if (this.params.sentencesCount === undefined) this.params.sentencesCount = 1; + const maxLoremIpsumSentencesLength = maxLoremIpsumLength * this.params.sentencesCount + this.params.sentencesCount + - 1; + if (this.stringLength !== undefined && this.stringLength < maxLoremIpsumSentencesLength) { + throw new Error( + `You can't use lorem ipsum generator with a db column length restriction of ${this.stringLength}. Set the maximum string length to at least ${maxLoremIpsumSentencesLength}.`, + ); + } + this.state = { rng }; } diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 854fbe437..66617cb66 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -259,7 +259,7 @@ export class SeedService { columnPossibleGenerator.generator.isUnique = col.isUnique; columnPossibleGenerator.generator.dataType = col.dataType; - columnPossibleGenerator.generator.length = col.typeParams.length; + columnPossibleGenerator.generator.stringLength = col.typeParams.length; tablePossibleGenerators.columnsPossibleGenerators.push( columnPossibleGenerator, @@ -660,7 +660,7 @@ export class SeedService { if (generator !== undefined) { generator.isUnique = col.isUnique; generator.dataType = col.dataType; - generator.length = col.typeParams.length; + generator.stringLength = col.typeParams.length; } return generator; @@ -850,7 +850,7 @@ export class SeedService { ) => { // int section --------------------------------------------------------------------------------------- if ( - (col.columnType === 'integer' || col.columnType === 'numeric') + ((col.columnType === 'integer' && col.dataType === 'number') || col.columnType === 'numeric') && table.primaryKeys.includes(col.name) ) { const generator = new GenerateIntPrimaryKey({}); @@ -858,15 +858,15 @@ export class SeedService { } if ( - col.columnType === 'integer' + (col.columnType === 'integer' && col.dataType === 'number') || col.columnType === 'numeric' - || col.columnType === 'bigint' + || (col.dataType === 'bigint' && col.columnType === 'blob') ) { const generator = new GenerateInt({}); return generator; } - if (col.columnType === 'boolean') { + if (col.dataType === 'boolean' && col.columnType === 'integer') { const generator = new GenerateBoolean({}); return generator; } @@ -918,12 +918,15 @@ export class SeedService { return generator; } - if (col.columnType === 'textjson' || col.columnType === 'blobjson') { + if ( + (col.columnType === 'text' && col.dataType === 'json') + || (col.columnType === 'blob' && col.dataType === 'json') + ) { const generator = new GenerateJson({}); return generator; } - if (col.columnType === 'timestamp' || col.columnType === 'timestamp_ms') { + if ((col.columnType === 'integer' && col.dataType === 'date')) { const generator = new GenerateTimestamp({}); return generator; } From f6eaa62b7c29e88ab19e45dc581776c7247852eb Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Mon, 16 Dec 2024 14:51:09 +0200 Subject: [PATCH 03/18] added array support for studio --- drizzle-seed/src/services/SeedService.ts | 58 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 66617cb66..02cf5af2c 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -205,8 +205,12 @@ export class SeedService { const genObj = refinements[table.name]!.columns[col.name]!; // TODO: for now only GenerateValuesFromArray support notNull property genObj.notNull = col.notNull; - if (col.dataType === 'array') { - if (col.baseColumn?.dataType === 'array' && col.baseColumn?.columnType === 'array') { + if (col.columnType.match(/\[\w*]/g) !== null) { + if ( + (col.baseColumn?.dataType === 'array' && col.baseColumn.columnType.match(/\[\w*]/g) !== null) + // studio case + || (col.typeParams.dimensions !== undefined && col.typeParams.dimensions > 1) + ) { throw new Error("for now you can't specify generators for columns of dimensition greater than 1."); } @@ -444,6 +448,29 @@ export class SeedService { return generator; } + // ARRAY for studio + if (col.columnType.match(/\[\w*]/g) !== null) { + // remove dimensions from type + const baseColumnType = col.columnType.replace(/\[\w*]/g, ''); + const baseColumn: Column = { + ...col, + }; + baseColumn.columnType = baseColumnType; + + const baseColumnGen = this.pickGeneratorForPostgresColumn(table, baseColumn) as AbstractGenerator; + if (baseColumnGen === undefined) { + throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); + } + + let generator = new GenerateArray({ baseColumnGen }); + + for (let i = 0; i < col.typeParams.dimensions! - 1; i++) { + generator = new GenerateArray({ baseColumnGen: generator }); + } + + return generator; + } + // INT ------------------------------------------------------------------------------------------------------------ if ( (col.columnType.includes('serial') @@ -486,7 +513,8 @@ export class SeedService { // 2^64 / 2 - 1, 8 bytes minValue = BigInt('-9223372036854775808'); maxValue = BigInt('9223372036854775807'); - } else if (col.dataType === 'number') { + } else { + // if (col.dataType === 'number') // if you’re expecting values above 2^31 but below 2^53 minValue = -9007199254740991; maxValue = 9007199254740991; @@ -850,15 +878,25 @@ export class SeedService { ) => { // int section --------------------------------------------------------------------------------------- if ( - ((col.columnType === 'integer' && col.dataType === 'number') || col.columnType === 'numeric') + (col.columnType === 'integer' || col.columnType === 'numeric') && table.primaryKeys.includes(col.name) ) { const generator = new GenerateIntPrimaryKey({}); return generator; } + if (col.columnType === 'integer' && col.dataType === 'boolean') { + const generator = new GenerateBoolean({}); + return generator; + } + + if ((col.columnType === 'integer' && col.dataType === 'date')) { + const generator = new GenerateTimestamp({}); + return generator; + } + if ( - (col.columnType === 'integer' && col.dataType === 'number') + col.columnType === 'integer' || col.columnType === 'numeric' || (col.dataType === 'bigint' && col.columnType === 'blob') ) { @@ -866,11 +904,6 @@ export class SeedService { return generator; } - if (col.dataType === 'boolean' && col.columnType === 'integer') { - const generator = new GenerateBoolean({}); - return generator; - } - // number section ------------------------------------------------------------------------------------ if (col.columnType === 'real' || col.columnType === 'numeric') { const generator = new GenerateNumber({}); @@ -926,11 +959,6 @@ export class SeedService { return generator; } - if ((col.columnType === 'integer' && col.dataType === 'date')) { - const generator = new GenerateTimestamp({}); - return generator; - } - if (col.hasDefault && col.default !== undefined) { const generator = new GenerateDefault({ defaultValue: col.default, From 1c8cbad2203a5131154435e8cd545fcd34387f25 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Tue, 17 Dec 2024 12:42:10 +0200 Subject: [PATCH 04/18] updated postgres interval generators to work with fields, added tests --- .../src/services/GeneratorsWrappers.ts | 194 ++++++++++++------ drizzle-seed/src/services/SeedService.ts | 11 +- .../mysql_all_data_types.test.ts | 2 +- .../tests/pg/allDataTypesTest/pgSchema.ts | 16 ++ .../pg_all_data_types.test.ts | 30 +++ 5 files changed, 192 insertions(+), 61 deletions(-) diff --git a/drizzle-seed/src/services/GeneratorsWrappers.ts b/drizzle-seed/src/services/GeneratorsWrappers.ts index 374fa6447..4ab06ec3f 100644 --- a/drizzle-seed/src/services/GeneratorsWrappers.ts +++ b/drizzle-seed/src/services/GeneratorsWrappers.ts @@ -1222,19 +1222,76 @@ export class GenerateEnum extends AbstractGenerator<{ enumValues: (string | numb } export class GenerateInterval extends AbstractGenerator<{ + fields?: + | 'year' + | 'month' + | 'day' + | 'hour' + | 'minute' + | 'second' + | 'year to month' + | 'day to hour' + | 'day to minute' + | 'day to second' + | 'hour to minute' + | 'hour to second' + | 'minute to second'; isUnique?: boolean; arraySize?: number; }> { static override readonly [entityKind]: string = 'GenerateInterval'; - private state: { rng: prand.RandomGenerator } | undefined; + private state: { + rng: prand.RandomGenerator; + fieldsToGenerate: string[]; + } | undefined; override uniqueVersionOfGen = GenerateUniqueInterval; + private config: { [key: string]: { from: number; to: number } } = { + year: { + from: 0, + to: 5, + }, + month: { + from: 0, + to: 11, + }, + day: { + from: 1, + to: 29, + }, + hour: { + from: 0, + to: 23, + }, + minute: { + from: 0, + to: 59, + }, + second: { + from: 0, + to: 59, + }, + }; override init({ count, seed }: { count: number; seed: number }) { super.init({ count, seed }); + const allFields = ['year', 'month', 'day', 'hour', 'minute', 'second']; + let fieldsToGenerate: string[] = allFields; + + if (this.params.fields !== undefined && this.params.fields?.includes(' to ')) { + const tokens = this.params.fields.split(' to '); + const endIdx = allFields.indexOf(tokens[1]!); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } else if (this.params.fields !== undefined) { + const endIdx = allFields.indexOf(this.params.fields); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } + + this.config[fieldsToGenerate[Math.floor(fieldsToGenerate.length / 2)]!]!.from = 1; + const rng = prand.xoroshiro128plus(seed); - this.state = { rng }; + this.state = { rng, fieldsToGenerate }; } generate() { @@ -1242,55 +1299,98 @@ export class GenerateInterval extends AbstractGenerator<{ throw new Error('state is not defined.'); } - let yearsNumb: number, - monthsNumb: number, - daysNumb: number, - hoursNumb: number, - minutesNumb: number, - secondsNumb: number; - - let interval = ''; - - [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); - [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); - - [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); - - [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); - - [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); + let interval = '', numb: number; - [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - - interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` - + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` - + `${daysNumb === 0 ? '' : `${daysNumb} days `}` - + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` - + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` - + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; + for (const field of this.state.fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); + interval += `${numb === 0 ? '' : `${numb} ${field} `}`; + } return interval; } } -export class GenerateUniqueInterval extends AbstractGenerator<{ isUnique?: boolean }> { +export class GenerateUniqueInterval extends AbstractGenerator<{ + fields?: + | 'year' + | 'month' + | 'day' + | 'hour' + | 'minute' + | 'second' + | 'year to month' + | 'day to hour' + | 'day to minute' + | 'day to second' + | 'hour to minute' + | 'hour to second' + | 'minute to second'; + isUnique?: boolean; +}> { static override readonly [entityKind]: string = 'GenerateUniqueInterval'; private state: { rng: prand.RandomGenerator; + fieldsToGenerate: string[]; intervalSet: Set; } | undefined; public override isUnique = true; + private config: { [key: string]: { from: number; to: number } } = { + year: { + from: 0, + to: 5, + }, + month: { + from: 0, + to: 11, + }, + day: { + from: 1, + to: 29, + }, + hour: { + from: 0, + to: 23, + }, + minute: { + from: 0, + to: 59, + }, + second: { + from: 0, + to: 59, + }, + }; override init({ count, seed }: { count: number; seed: number }) { - const maxUniqueIntervalsNumber = 6 * 13 * 29 * 25 * 61 * 61; + const allFields = ['year', 'month', 'day', 'hour', 'minute', 'second']; + let fieldsToGenerate: string[] = allFields; + + if (this.params.fields !== undefined && this.params.fields?.includes(' to ')) { + const tokens = this.params.fields.split(' to '); + const endIdx = allFields.indexOf(tokens[1]!); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } else if (this.params.fields !== undefined) { + const endIdx = allFields.indexOf(this.params.fields); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } + + this.config[fieldsToGenerate[Math.floor(fieldsToGenerate.length / 2)]!]!.from = 1; + + let maxUniqueIntervalsNumber = 1; + for (const field of fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + maxUniqueIntervalsNumber *= from - to + 1; + } + if (count > maxUniqueIntervalsNumber) { throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); } const rng = prand.xoroshiro128plus(seed); const intervalSet = new Set(); - this.state = { rng, intervalSet }; + this.state = { rng, fieldsToGenerate, intervalSet }; } generate() { @@ -1298,34 +1398,12 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ isUnique?: boole throw new Error('state is not defined.'); } - let yearsNumb: number, - monthsNumb: number, - daysNumb: number, - hoursNumb: number, - minutesNumb: number, - secondsNumb: number; - - let interval = ''; + let interval = '', numb: number; - for (;;) { - [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); - [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); - [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); - [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); - [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - - interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` - + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` - + `${daysNumb === 0 ? '' : `${daysNumb} days `}` - + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` - + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` - + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; - - if (!this.state.intervalSet.has(interval)) { - this.state.intervalSet.add(interval); - break; - } + for (const field of this.state.fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); + interval += `${numb === 0 ? '' : `${numb} ${field} `}`; } return interval; @@ -1531,7 +1609,6 @@ export class GenerateFirstName extends AbstractGenerator<{ // logic for this generator // names dataset contains about 30000 unique names. - // TODO: generate names accordingly to max column length let idx: number; [idx, this.state.rng] = prand.uniformIntDistribution(0, firstNames.length - 1, this.state.rng); @@ -3343,6 +3420,7 @@ export const generatorsFuncs = { * * @param isUnique - property that controls if generated values gonna be unique or not. * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * @param fields - range of values you want to see in your intervals. * @example * ```ts * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 02cf5af2c..62283c817 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -655,8 +655,15 @@ export class SeedService { } // INTERVAL - if (col.columnType === 'interval') { - const generator = new GenerateInterval({}); + if (col.columnType.startsWith('interval')) { + if (col.columnType === 'interval') { + const generator = new GenerateInterval({}); + + return generator; + } + + const fields = col.columnType.replace('interval ', '') as GenerateInterval['params']['fields']; + const generator = new GenerateInterval({ fields }); return generator; } diff --git a/drizzle-seed/tests/mysql/allDataTypesTest/mysql_all_data_types.test.ts b/drizzle-seed/tests/mysql/allDataTypesTest/mysql_all_data_types.test.ts index 5a9ba9908..f39a55fef 100644 --- a/drizzle-seed/tests/mysql/allDataTypesTest/mysql_all_data_types.test.ts +++ b/drizzle-seed/tests/mysql/allDataTypesTest/mysql_all_data_types.test.ts @@ -11,7 +11,7 @@ import { seed } from '../../../src/index.ts'; import * as schema from './mysqlSchema.ts'; let mysqlContainer: Docker.Container; -let client: Connection; +let client: Connection | undefined; let db: MySql2Database; async function createDockerDB(): Promise { diff --git a/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts b/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts index 75ba20a43..16a55baf4 100644 --- a/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts +++ b/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts @@ -97,3 +97,19 @@ export const ndArrays = schema.table('nd_arrays', { integer3DArray: integer('integer_3d_array').array(3).array(4).array(5), integer4DArray: integer('integer_4d_array').array(3).array(4).array(5).array(6), }); + +export const intervals = schema.table('intervals', { + intervalYear: interval({ fields: 'year' }), + intervalYearToMonth: interval({ fields: 'year to month' }), + intervalMonth: interval({ fields: 'month' }), + intervalDay: interval({ fields: 'day' }), + intervalDayToHour: interval({ fields: 'day to hour' }), + intervalDayToMinute: interval({ fields: 'day to minute' }), + intervalDayToSecond: interval({ fields: 'day to second' }), + intervalHour: interval({ fields: 'hour' }), + intervalHourToMinute: interval({ fields: 'hour to minute' }), + intervalHourToSecond: interval({ fields: 'hour to second' }), + intervalMinute: interval({ fields: 'minute' }), + intervalMinuteToSecond: interval({ fields: 'minute to second' }), + intervalSecond: interval({ fields: 'second' }), +}); diff --git a/drizzle-seed/tests/pg/allDataTypesTest/pg_all_data_types.test.ts b/drizzle-seed/tests/pg/allDataTypesTest/pg_all_data_types.test.ts index 7dfbc089b..62d0895c0 100644 --- a/drizzle-seed/tests/pg/allDataTypesTest/pg_all_data_types.test.ts +++ b/drizzle-seed/tests/pg/allDataTypesTest/pg_all_data_types.test.ts @@ -105,6 +105,26 @@ beforeAll(async () => { ); `, ); + + await db.execute( + sql` + CREATE TABLE IF NOT EXISTS "seeder_lib_pg"."intervals" ( + "intervalYear" interval year, + "intervalYearToMonth" interval year to month, + "intervalMonth" interval month, + "intervalDay" interval day, + "intervalDayToHour" interval day to hour, + "intervalDayToMinute" interval day to minute, + "intervalDayToSecond" interval day to second, + "intervalHour" interval hour, + "intervalHourToMinute" interval hour to minute, + "intervalHourToSecond" interval hour to second, + "intervalMinute" interval minute, + "intervalMinuteToSecond" interval minute to second, + "intervalSecond" interval second + ); + `, + ); }); afterAll(async () => { @@ -157,3 +177,13 @@ test('nd arrays', async () => { expect(predicate0 && predicate1 && predicate2 && predicate3 && predicate4).toBe(true); }); + +test('intervals test', async () => { + await seed(db, { intervals: schema.intervals }, { count: 1000 }); + + const intervals = await db.select().from(schema.intervals); + // every value in each rows does not equal undefined. + const predicate = intervals.every((row) => Object.values(row).every((val) => val !== undefined && val !== null)); + + expect(predicate).toBe(true); +}); From 1b05f63f6ce744d779acec6d008cedd1914bd1ad Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 18 Dec 2024 14:04:38 +0200 Subject: [PATCH 05/18] fixes, partial implementation of generator versioning --- drizzle-seed/src/index.ts | 26 +- drizzle-seed/src/services/GeneratorFuncs.ts | 737 +++++++++++++++++ .../GeneratorVersions/GeneratorsV1.ts | 15 + .../{GeneratorsWrappers.ts => Generators.ts} | 739 +----------------- drizzle-seed/src/services/SeedService.ts | 228 +++--- drizzle-seed/src/types/seedService.ts | 2 +- .../tests/benchmarks/generatorsBenchmark.ts | 2 +- 7 files changed, 912 insertions(+), 837 deletions(-) create mode 100644 drizzle-seed/src/services/GeneratorFuncs.ts create mode 100644 drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts rename drizzle-seed/src/services/{GeneratorsWrappers.ts => Generators.ts} (81%) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index fde4442a1..248e7ead5 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -9,8 +9,8 @@ import { getTableConfig as getPgTableConfig, PgDatabase, PgTable } from 'drizzle import type { SQLiteColumn } from 'drizzle-orm/sqlite-core'; import { BaseSQLiteDatabase, getTableConfig as getSqliteTableConfig, SQLiteTable } from 'drizzle-orm/sqlite-core'; -import type { AbstractGenerator } from './services/GeneratorsWrappers.ts'; -import { generatorsFuncs } from './services/GeneratorsWrappers.ts'; +import { generatorsFuncs } from './services/GeneratorFuncs.ts'; +import type { AbstractGenerator } from './services/Generators.ts'; import { SeedService } from './services/SeedService.ts'; import type { DrizzleStudioObjectType, DrizzleStudioRelationType } from './types/drizzleStudio.ts'; import type { RefinementsType } from './types/seedService.ts'; @@ -138,7 +138,7 @@ class SeedPromise< constructor( private db: DB, private schema: SCHEMA, - private options?: { count?: number; seed?: number }, + private options?: { count?: number; seed?: number; version?: number }, ) {} then( @@ -259,7 +259,6 @@ export async function seedForDrizzleStudio( sqlDialect, tables, isCyclicRelations, - {}, // TODO: fix later refinements, options, ); @@ -337,7 +336,7 @@ export function seed< | SQLiteTable | any; }, ->(db: DB, schema: SCHEMA, options?: { count?: number; seed?: number }) { +>(db: DB, schema: SCHEMA, options?: { count?: number; seed?: number; version?: number }) { return new SeedPromise(db, schema, options); } @@ -352,7 +351,7 @@ const seedFunc = async ( | SQLiteTable | any; }, - options: { count?: number; seed?: number } = {}, + options: { count?: number; seed?: number; version?: number } = {}, refinements?: RefinementsType, ) => { if (is(db, PgDatabase)) { @@ -490,17 +489,16 @@ const filterPgTables = (schema: { const seedPostgres = async ( db: PgDatabase, schema: { [key: string]: PgTable }, - options: { count?: number; seed?: number } = {}, + options: { count?: number; seed?: number; version?: number } = {}, refinements?: RefinementsType, ) => { const seedService = new SeedService(); - const { tables, relations, tableRelations } = getPostgresInfo(schema); + const { tables, relations } = getPostgresInfo(schema); const generatedTablesGenerators = seedService.generatePossibleGenerators( 'postgresql', tables, relations, - tableRelations, refinements, options, ); @@ -784,10 +782,10 @@ const filterMySqlTables = (schema: { const seedMySql = async ( db: MySqlDatabase, schema: { [key: string]: MySqlTable }, - options: { count?: number; seed?: number } = {}, + options: { count?: number; seed?: number; version?: number } = {}, refinements?: RefinementsType, ) => { - const { tables, relations, tableRelations } = getMySqlInfo(schema); + const { tables, relations } = getMySqlInfo(schema); const seedService = new SeedService(); @@ -795,7 +793,6 @@ const seedMySql = async ( 'mysql', tables, relations, - tableRelations, refinements, options, ); @@ -1003,10 +1000,10 @@ const filterSqliteTables = (schema: { const seedSqlite = async ( db: BaseSQLiteDatabase, schema: { [key: string]: SQLiteTable }, - options: { count?: number; seed?: number } = {}, + options: { count?: number; seed?: number; version?: number } = {}, refinements?: RefinementsType, ) => { - const { tables, relations, tableRelations } = getSqliteInfo(schema); + const { tables, relations } = getSqliteInfo(schema); const seedService = new SeedService(); @@ -1014,7 +1011,6 @@ const seedSqlite = async ( 'sqlite', tables, relations, - tableRelations, refinements, options, ); diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts new file mode 100644 index 000000000..08f36b1dc --- /dev/null +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -0,0 +1,737 @@ +import type { AbstractGenerator } from './Generators.ts'; +import { + GenerateBoolean, + GenerateCity, + GenerateCompanyName, + GenerateCountry, + GenerateDate, + GenerateDatetime, + GenerateDefault, + GenerateEmail, + GenerateFirstName, + GenerateFullName, + GenerateInt, + GenerateInterval, + GenerateIntPrimaryKey, + GenerateJobTitle, + GenerateJson, + GenerateLastName, + GenerateLine, + GenerateLoremIpsum, + GenerateNumber, + GeneratePhoneNumber, + GeneratePoint, + GeneratePostcode, + GenerateState, + GenerateStreetAdddress, + GenerateString, + GenerateTime, + GenerateTimestamp, + GenerateUUID, + GenerateValuesFromArray, + GenerateYear, + WeightedRandomGenerator, +} from './Generators.ts'; + +function createGenerator, T>( + generatorConstructor: new(params: T) => GeneratorType, +) { + return ( + ...args: GeneratorType extends GenerateValuesFromArray | GenerateDefault | WeightedRandomGenerator ? [T] + : ([] | [T]) + ): GeneratorType => { + let params = args[0]; + if (params === undefined) params = {} as T; + return new generatorConstructor(params); + }; +} + +export const generatorsFuncs = { + /** + * generates same given value each time the generator is called. + * @param defaultValue - value you want to generate + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * content: funcs.default({ defaultValue: "post content" }), + * }, + * }, + * })); + * ``` + */ + default: createGenerator(GenerateDefault), + + /** + * generates values from given array + * @param values - array of values you want to generate. can be array of weighted values. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * title: funcs.valuesFromArray({ + * values: ["Title1", "Title2", "Title3", "Title4", "Title5"], + * isUnique: true + * }), + * }, + * }, + * })); + * + * ``` + * weighted values example + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * title: funcs.valuesFromArray({ + * values: [ + * { weight: 0.35, values: ["Title1", "Title2"] }, + * { weight: 0.5, values: ["Title3", "Title4"] }, + * { weight: 0.15, values: ["Title5"] }, + * ], + * isUnique: false + * }), + * }, + * }, + * })); + * + * ``` + */ + valuesFromArray: createGenerator(GenerateValuesFromArray), + + /** + * generates sequential integers starting with 1. + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * id: funcs.intPrimaryKey(), + * }, + * }, + * })); + * + * ``` + */ + intPrimaryKey: createGenerator(GenerateIntPrimaryKey), + + /** + * generates numbers with floating point in given range. + * @param minValue - lower border of range. + * @param maxValue - upper border of range. + * @param precision - precision of generated number: + * precision equals 10 means that values will be accurate to one tenth (1.2, 34.6); + * precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67). + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * products: { + * columns: { + * unitPrice: funcs.number({ minValue: 10, maxValue: 120, precision: 100, isUnique: false }), + * }, + * }, + * })); + * + * ``` + */ + number: createGenerator(GenerateNumber), + // uniqueNumber: createGenerator(GenerateUniqueNumber), + + /** + * generates integers within given range. + * @param minValue - lower border of range. + * @param maxValue - upper border of range. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * products: { + * columns: { + * unitsInStock: funcs.number({ minValue: 0, maxValue: 100, isUnique: false }), + * }, + * }, + * })); + * + * ``` + */ + int: createGenerator(GenerateInt), + // uniqueInt: createGenerator(GenerateUniqueInt), + + /** + * generates boolean values(true or false) + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * isAvailable: funcs.boolean() + * }, + * }, + * })); + * + * ``` + */ + boolean: createGenerator(GenerateBoolean), + + /** + * generates date within given range. + * @param minDate - lower border of range. + * @param maxDate - upper border of range. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * birthDate: funcs.date({ minDate: "1990-01-01", maxDate: "2010-12-31" }) + * }, + * }, + * })); + * + * ``` + */ + date: createGenerator(GenerateDate), + + /** + * generates time in 24 hours style. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * birthTime: funcs.time() + * }, + * }, + * })); + * + * ``` + */ + time: createGenerator(GenerateTime), + + /** + * generates timestamps. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * orders: { + * columns: { + * shippedDate: funcs.timestamp() + * }, + * }, + * })); + * + * ``` + */ + timestamp: createGenerator(GenerateTimestamp), + + /** + * generates datetime objects. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * orders: { + * columns: { + * shippedDate: funcs.datetime() + * }, + * }, + * })); + * + * ``` + */ + datetime: createGenerator(GenerateDatetime), + + /** + * generates years. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * birthYear: funcs.year() + * }, + * }, + * })); + * + * ``` + */ + year: createGenerator(GenerateYear), + + /** + * generates json objects with fixed structure. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * json structure can equal this: + * ``` + * { + * email, + * name, + * isGraduated, + * hasJob, + * salary, + * startedWorking, + * visitedCountries, + * } + * ``` + * or this + * ``` + * { + * email, + * name, + * isGraduated, + * hasJob, + * visitedCountries, + * } + * ``` + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * metadata: funcs.json() + * }, + * }, + * })); + * ``` + */ + json: createGenerator(GenerateJson), + // jsonb: createGenerator(GenerateJsonb), + + /** + * generates time intervals. + * + * interval example: "1 years 12 days 5 minutes" + * + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * @param fields - range of values you want to see in your intervals. + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * timeSpentOnWebsite: funcs.interval() + * }, + * }, + * })); + * ``` + */ + interval: createGenerator(GenerateInterval), + // uniqueInterval: createGenerator(GenerateUniqueInterval), + + /** + * generates random strings. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * hashedPassword: funcs.string({isUnique: false}) + * }, + * }, + * })); + * ``` + */ + string: createGenerator(GenerateString), + // uniqueString: createGenerator(GenerateUniqueString), + + /** + * generates v4 UUID strings if arraySize is not specified, or v4 UUID 1D arrays if it is. + * + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * uuid: funcs.uuid({ + * arraySize: 4 + * }) + * }, + * }, + * })); + * ``` + */ + uuid: createGenerator(GenerateUUID), + + /** + * generates person's first names. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * firstName: funcs.firstName({isUnique: true}) + * }, + * }, + * })); + * ``` + */ + firstName: createGenerator(GenerateFirstName), + // uniqueFirstName: createGenerator(GenerateUniqueName), + + /** + * generates person's last names. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * lastName: funcs.lastName({isUnique: false}) + * }, + * }, + * })); + * ``` + */ + lastName: createGenerator(GenerateLastName), + // uniqueLastName: createGenerator(GenerateUniqueSurname), + + /** + * generates person's full names. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * fullName: funcs.fullName({isUnique: true}) + * }, + * }, + * })); + * ``` + */ + fullName: createGenerator(GenerateFullName), + // uniqueFullName: createGenerator(GenerateUniqueFullName), + + /** + * generates unique emails. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * email: funcs.email() + * }, + * }, + * })); + * ``` + */ + email: createGenerator(GenerateEmail), + + /** + * generates unique phone numbers. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @param template - phone number template, where all '#' symbols will be substituted with generated digits. + * @param prefixes - array of any string you want to be your phone number prefixes.(not compatible with template property) + * @param generatedDigitsNumbers - number of digits that will be added at the end of prefixes.(not compatible with template property) + * @example + * ```ts + * //generate phone number using template property + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * phoneNumber: funcs.phoneNumber({template: "+(380) ###-####"}) + * }, + * }, + * })); + * + * //generate phone number using prefixes and generatedDigitsNumbers properties + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * phoneNumber: funcs.phoneNumber({prefixes: [ "+380 99", "+380 67" ], generatedDigitsNumbers: 7}) + * }, + * }, + * })); + * + * //generate phone number using prefixes and generatedDigitsNumbers properties but with different generatedDigitsNumbers for prefixes + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * phoneNumber: funcs.phoneNumber({prefixes: [ "+380 99", "+380 67", "+1" ], generatedDigitsNumbers: [7, 7, 10]}) + * }, + * }, + * })); + * + * ``` + */ + phoneNumber: createGenerator(GeneratePhoneNumber), + + /** + * generates country's names. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * country: funcs.country({isUnique: false}) + * }, + * }, + * })); + * ``` + */ + country: createGenerator(GenerateCountry), + // uniqueCountry: createGenerator(GenerateUniqueCountry), + + /** + * generates city's names. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * city: funcs.city({isUnique: false}) + * }, + * }, + * })); + * ``` + */ + city: createGenerator(GenerateCity), + // uniqueCity: createGenerator(GenerateUniqueCityName), + + /** + * generates street address. + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * streetAddress: funcs.streetAddress({isUnique: true}) + * }, + * }, + * })); + * ``` + */ + streetAddress: createGenerator(GenerateStreetAdddress), + // uniqueStreetAddress: createGenerator(GenerateUniqueStreetAdddress), + + /** + * generates job titles. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * jobTitle: funcs.jobTitle() + * }, + * }, + * })); + * ``` + */ + jobTitle: createGenerator(GenerateJobTitle), + + /** + * generates postal codes. + * + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * postcode: funcs.postcode({isUnique: true}) + * }, + * }, + * })); + * ``` + */ + postcode: createGenerator(GeneratePostcode), + // uniquePostcoe: createGenerator(GenerateUniquePostcode), + + /** + * generates states of America. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * state: funcs.state() + * }, + * }, + * })); + * ``` + */ + state: createGenerator(GenerateState), + + /** + * generates company's names. + * + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * users: { + * columns: { + * company: funcs.companyName({isUnique: true}) + * }, + * }, + * })); + * ``` + */ + companyName: createGenerator(GenerateCompanyName), + // uniqueCompanyName: createGenerator(GenerateUniqueCompanyName), + + /** + * generates 'lorem ipsum' text sentences. + * + * @param sentencesCount - number of sentences you want to generate as one generated value(string). + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * content: funcs.loremIpsum({sentencesCount: 2}) + * }, + * }, + * })); + * ``` + */ + loremIpsum: createGenerator(GenerateLoremIpsum), + + /** + * generates 2D points within specified ranges for x and y coordinates. + * + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param minXValue - lower bound of range for x coordinate. + * @param maxXValue - upper bound of range for x coordinate. + * @param minYValue - lower bound of range for y coordinate. + * @param maxYValue - upper bound of range for y coordinate. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * triangles: { + * columns: { + * pointCoords: funcs.point({ + * isUnique: true, + * minXValue: -5, maxXValue:20, + * minYValue: 0, maxYValue: 30 + * }) + * }, + * }, + * })); + * ``` + */ + point: createGenerator(GeneratePoint), + // uniquePoint: createGenerator(GenerateUniquePoint), + + /** + * generates 2D lines within specified ranges for a, b and c parameters of line. + * + * ``` + * line equation: a*x + b*y + c = 0 + * ``` + * + * @param isUnique - property that controls if generated values gonna be unique or not. + * @param minAValue - lower bound of range for a parameter. + * @param maxAValue - upper bound of range for x parameter. + * @param minBValue - lower bound of range for y parameter. + * @param maxBValue - upper bound of range for y parameter. + * @param minCValue - lower bound of range for y parameter. + * @param maxCValue - upper bound of range for y parameter. + * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * lines: { + * columns: { + * lineParams: funcs.point({ + * isUnique: true, + * minAValue: -5, maxAValue:20, + * minBValue: 0, maxBValue: 30, + * minCValue: 0, maxCValue: 10 + * }) + * }, + * }, + * })); + * ``` + */ + line: createGenerator(GenerateLine), + // uniqueLine: createGenerator(GenerateUniqueLine), + + /** + * gives you the opportunity to call different generators with different probabilities to generate values for one column. + * @param params - array of generators with probabilities you would like to call them to generate values. + * + * @example + * ```ts + * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ + * posts: { + * columns: { + * content: funcs.weightedRandom([ + * { + * weight: 0.6, + * value: funcs.loremIpsum({ sentencesCount: 3 }), + * }, + * { + * weight: 0.4, + * value: funcs.default({ defaultValue: "TODO" }), + * }, + * ]), + * }, + * }, + * })); + * ``` + */ + weightedRandom: createGenerator(WeightedRandomGenerator), +}; diff --git a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts new file mode 100644 index 000000000..e4fed2926 --- /dev/null +++ b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts @@ -0,0 +1,15 @@ +import { entityKind } from 'drizzle-orm'; +import { GenerateInterval, GenerateUniqueInterval } from '../Generators.ts'; + +export const version = 1; + +export class GenerateIntervalV1 extends GenerateInterval { + static override readonly [entityKind]: string = 'GenerateInterval'; + static override readonly ['version']: number = 1; + override uniqueVersionOfGen = GenerateUniqueIntervalV1; +} + +export class GenerateUniqueIntervalV1 extends GenerateUniqueInterval { + static override readonly [entityKind]: string = 'GenerateUniqueInterval'; + static override readonly ['version']: number = 1; +} diff --git a/drizzle-seed/src/services/GeneratorsWrappers.ts b/drizzle-seed/src/services/Generators.ts similarity index 81% rename from drizzle-seed/src/services/GeneratorsWrappers.ts rename to drizzle-seed/src/services/Generators.ts index 4ab06ec3f..f5fc44aab 100644 --- a/drizzle-seed/src/services/GeneratorsWrappers.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -14,8 +14,11 @@ import states, { maxStringLength as maxStateLength } from '../datasets/states.ts import streetSuffix, { maxStringLength as maxStreetSuffixLength } from '../datasets/streetSuffix.ts'; import { fastCartesianProduct, fillTemplate, getWeightedIndices, isObject } from './utils.ts'; +export const version = 2; + export abstract class AbstractGenerator { static readonly [entityKind]: string = 'AbstractGenerator'; + static readonly ['version']: number = 2; public isUnique = false; public notNull = false; @@ -90,19 +93,6 @@ export abstract class AbstractGenerator { } } -function createGenerator, T>( - generatorConstructor: new(params: T) => GeneratorType, -) { - return ( - ...args: GeneratorType extends GenerateValuesFromArray | GenerateDefault | WeightedRandomGenerator ? [T] - : ([] | [T]) - ): GeneratorType => { - let params = args[0]; - if (params === undefined) params = {} as T; - return new generatorConstructor(params); - }; -} - // Generators Classes ----------------------------------------------------------------------------------------------------------------------- export class GenerateArray extends AbstractGenerator<{ baseColumnGen: AbstractGenerator; size?: number }> { static override readonly [entityKind]: string = 'GenerateArray'; @@ -1253,7 +1243,7 @@ export class GenerateInterval extends AbstractGenerator<{ }, month: { from: 0, - to: 11, + to: 12, }, day: { from: 1, @@ -1261,15 +1251,15 @@ export class GenerateInterval extends AbstractGenerator<{ }, hour: { from: 0, - to: 23, + to: 24, }, minute: { from: 0, - to: 59, + to: 60, }, second: { from: 0, - to: 59, + to: 60, }, }; @@ -1288,8 +1278,6 @@ export class GenerateInterval extends AbstractGenerator<{ fieldsToGenerate = allFields.slice(0, endIdx + 1); } - this.config[fieldsToGenerate[Math.floor(fieldsToGenerate.length / 2)]!]!.from = 1; - const rng = prand.xoroshiro128plus(seed); this.state = { rng, fieldsToGenerate }; } @@ -1304,7 +1292,7 @@ export class GenerateInterval extends AbstractGenerator<{ for (const field of this.state.fieldsToGenerate) { const from = this.config[field]!.from, to = this.config[field]!.to; [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); - interval += `${numb === 0 ? '' : `${numb} ${field} `}`; + interval += `${numb} ${field} `; } return interval; @@ -1376,8 +1364,6 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ fieldsToGenerate = allFields.slice(0, endIdx + 1); } - this.config[fieldsToGenerate[Math.floor(fieldsToGenerate.length / 2)]!]!.from = 1; - let maxUniqueIntervalsNumber = 1; for (const field of fieldsToGenerate) { const from = this.config[field]!.from, to = this.config[field]!.to; @@ -1398,12 +1384,21 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ throw new Error('state is not defined.'); } - let interval = '', numb: number; + let interval, numb: number; - for (const field of this.state.fieldsToGenerate) { - const from = this.config[field]!.from, to = this.config[field]!.to; - [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); - interval += `${numb === 0 ? '' : `${numb} ${field} `}`; + for (;;) { + interval = ''; + + for (const field of this.state.fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); + interval += `${numb} ${field} `; + } + + if (!this.state.intervalSet.has(interval)) { + this.state.intervalSet.add(interval); + break; + } } return interval; @@ -3137,693 +3132,3 @@ export class GenerateUniqueLine extends AbstractGenerator<{ } } } - -export const generatorsFuncs = { - /** - * generates same given value each time the generator is called. - * @param defaultValue - value you want to generate - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * content: funcs.default({ defaultValue: "post content" }), - * }, - * }, - * })); - * ``` - */ - default: createGenerator(GenerateDefault), - - /** - * generates values from given array - * @param values - array of values you want to generate. can be array of weighted values. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * title: funcs.valuesFromArray({ - * values: ["Title1", "Title2", "Title3", "Title4", "Title5"], - * isUnique: true - * }), - * }, - * }, - * })); - * - * ``` - * weighted values example - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * title: funcs.valuesFromArray({ - * values: [ - * { weight: 0.35, values: ["Title1", "Title2"] }, - * { weight: 0.5, values: ["Title3", "Title4"] }, - * { weight: 0.15, values: ["Title5"] }, - * ], - * isUnique: false - * }), - * }, - * }, - * })); - * - * ``` - */ - valuesFromArray: createGenerator(GenerateValuesFromArray), - - /** - * generates sequential integers starting with 1. - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * id: funcs.intPrimaryKey(), - * }, - * }, - * })); - * - * ``` - */ - intPrimaryKey: createGenerator(GenerateIntPrimaryKey), - - /** - * generates numbers with floating point in given range. - * @param minValue - lower border of range. - * @param maxValue - upper border of range. - * @param precision - precision of generated number: - * precision equals 10 means that values will be accurate to one tenth (1.2, 34.6); - * precision equals 100 means that values will be accurate to one hundredth (1.23, 34.67). - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * products: { - * columns: { - * unitPrice: funcs.number({ minValue: 10, maxValue: 120, precision: 100, isUnique: false }), - * }, - * }, - * })); - * - * ``` - */ - number: createGenerator(GenerateNumber), - // uniqueNumber: createGenerator(GenerateUniqueNumber), - - /** - * generates integers within given range. - * @param minValue - lower border of range. - * @param maxValue - upper border of range. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * products: { - * columns: { - * unitsInStock: funcs.number({ minValue: 0, maxValue: 100, isUnique: false }), - * }, - * }, - * })); - * - * ``` - */ - int: createGenerator(GenerateInt), - // uniqueInt: createGenerator(GenerateUniqueInt), - - /** - * generates boolean values(true or false) - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * isAvailable: funcs.boolean() - * }, - * }, - * })); - * - * ``` - */ - boolean: createGenerator(GenerateBoolean), - - /** - * generates date within given range. - * @param minDate - lower border of range. - * @param maxDate - upper border of range. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * birthDate: funcs.date({ minDate: "1990-01-01", maxDate: "2010-12-31" }) - * }, - * }, - * })); - * - * ``` - */ - date: createGenerator(GenerateDate), - - /** - * generates time in 24 hours style. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * birthTime: funcs.time() - * }, - * }, - * })); - * - * ``` - */ - time: createGenerator(GenerateTime), - - /** - * generates timestamps. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * orders: { - * columns: { - * shippedDate: funcs.timestamp() - * }, - * }, - * })); - * - * ``` - */ - timestamp: createGenerator(GenerateTimestamp), - - /** - * generates datetime objects. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * orders: { - * columns: { - * shippedDate: funcs.datetime() - * }, - * }, - * })); - * - * ``` - */ - datetime: createGenerator(GenerateDatetime), - - /** - * generates years. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * birthYear: funcs.year() - * }, - * }, - * })); - * - * ``` - */ - year: createGenerator(GenerateYear), - - /** - * generates json objects with fixed structure. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * json structure can equal this: - * ``` - * { - * email, - * name, - * isGraduated, - * hasJob, - * salary, - * startedWorking, - * visitedCountries, - * } - * ``` - * or this - * ``` - * { - * email, - * name, - * isGraduated, - * hasJob, - * visitedCountries, - * } - * ``` - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * metadata: funcs.json() - * }, - * }, - * })); - * ``` - */ - json: createGenerator(GenerateJson), - // jsonb: createGenerator(GenerateJsonb), - - /** - * generates time intervals. - * - * interval example: "1 years 12 days 5 minutes" - * - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * @param fields - range of values you want to see in your intervals. - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * timeSpentOnWebsite: funcs.interval() - * }, - * }, - * })); - * ``` - */ - interval: createGenerator(GenerateInterval), - // uniqueInterval: createGenerator(GenerateUniqueInterval), - - /** - * generates random strings. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * hashedPassword: funcs.string({isUnique: false}) - * }, - * }, - * })); - * ``` - */ - string: createGenerator(GenerateString), - // uniqueString: createGenerator(GenerateUniqueString), - - /** - * generates v4 UUID strings if arraySize is not specified, or v4 UUID 1D arrays if it is. - * - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * uuid: funcs.uuid({ - * arraySize: 4 - * }) - * }, - * }, - * })); - * ``` - */ - uuid: createGenerator(GenerateUUID), - - /** - * generates person's first names. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * firstName: funcs.firstName({isUnique: true}) - * }, - * }, - * })); - * ``` - */ - firstName: createGenerator(GenerateFirstName), - // uniqueFirstName: createGenerator(GenerateUniqueName), - - /** - * generates person's last names. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * lastName: funcs.lastName({isUnique: false}) - * }, - * }, - * })); - * ``` - */ - lastName: createGenerator(GenerateLastName), - // uniqueLastName: createGenerator(GenerateUniqueSurname), - - /** - * generates person's full names. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * fullName: funcs.fullName({isUnique: true}) - * }, - * }, - * })); - * ``` - */ - fullName: createGenerator(GenerateFullName), - // uniqueFullName: createGenerator(GenerateUniqueFullName), - - /** - * generates unique emails. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * email: funcs.email() - * }, - * }, - * })); - * ``` - */ - email: createGenerator(GenerateEmail), - - /** - * generates unique phone numbers. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @param template - phone number template, where all '#' symbols will be substituted with generated digits. - * @param prefixes - array of any string you want to be your phone number prefixes.(not compatible with template property) - * @param generatedDigitsNumbers - number of digits that will be added at the end of prefixes.(not compatible with template property) - * @example - * ```ts - * //generate phone number using template property - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * phoneNumber: funcs.phoneNumber({template: "+(380) ###-####"}) - * }, - * }, - * })); - * - * //generate phone number using prefixes and generatedDigitsNumbers properties - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * phoneNumber: funcs.phoneNumber({prefixes: [ "+380 99", "+380 67" ], generatedDigitsNumbers: 7}) - * }, - * }, - * })); - * - * //generate phone number using prefixes and generatedDigitsNumbers properties but with different generatedDigitsNumbers for prefixes - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * phoneNumber: funcs.phoneNumber({prefixes: [ "+380 99", "+380 67", "+1" ], generatedDigitsNumbers: [7, 7, 10]}) - * }, - * }, - * })); - * - * ``` - */ - phoneNumber: createGenerator(GeneratePhoneNumber), - - /** - * generates country's names. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * country: funcs.country({isUnique: false}) - * }, - * }, - * })); - * ``` - */ - country: createGenerator(GenerateCountry), - // uniqueCountry: createGenerator(GenerateUniqueCountry), - - /** - * generates city's names. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * city: funcs.city({isUnique: false}) - * }, - * }, - * })); - * ``` - */ - city: createGenerator(GenerateCity), - // uniqueCity: createGenerator(GenerateUniqueCityName), - - /** - * generates street address. - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * streetAddress: funcs.streetAddress({isUnique: true}) - * }, - * }, - * })); - * ``` - */ - streetAddress: createGenerator(GenerateStreetAdddress), - // uniqueStreetAddress: createGenerator(GenerateUniqueStreetAdddress), - - /** - * generates job titles. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * jobTitle: funcs.jobTitle() - * }, - * }, - * })); - * ``` - */ - jobTitle: createGenerator(GenerateJobTitle), - - /** - * generates postal codes. - * - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * postcode: funcs.postcode({isUnique: true}) - * }, - * }, - * })); - * ``` - */ - postcode: createGenerator(GeneratePostcode), - // uniquePostcoe: createGenerator(GenerateUniquePostcode), - - /** - * generates states of America. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * state: funcs.state() - * }, - * }, - * })); - * ``` - */ - state: createGenerator(GenerateState), - - /** - * generates company's names. - * - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * users: { - * columns: { - * company: funcs.companyName({isUnique: true}) - * }, - * }, - * })); - * ``` - */ - companyName: createGenerator(GenerateCompanyName), - // uniqueCompanyName: createGenerator(GenerateUniqueCompanyName), - - /** - * generates 'lorem ipsum' text sentences. - * - * @param sentencesCount - number of sentences you want to generate as one generated value(string). - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * content: funcs.loremIpsum({sentencesCount: 2}) - * }, - * }, - * })); - * ``` - */ - loremIpsum: createGenerator(GenerateLoremIpsum), - - /** - * generates 2D points within specified ranges for x and y coordinates. - * - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param minXValue - lower bound of range for x coordinate. - * @param maxXValue - upper bound of range for x coordinate. - * @param minYValue - lower bound of range for y coordinate. - * @param maxYValue - upper bound of range for y coordinate. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * triangles: { - * columns: { - * pointCoords: funcs.point({ - * isUnique: true, - * minXValue: -5, maxXValue:20, - * minYValue: 0, maxYValue: 30 - * }) - * }, - * }, - * })); - * ``` - */ - point: createGenerator(GeneratePoint), - // uniquePoint: createGenerator(GenerateUniquePoint), - - /** - * generates 2D lines within specified ranges for a, b and c parameters of line. - * - * ``` - * line equation: a*x + b*y + c = 0 - * ``` - * - * @param isUnique - property that controls if generated values gonna be unique or not. - * @param minAValue - lower bound of range for a parameter. - * @param maxAValue - upper bound of range for x parameter. - * @param minBValue - lower bound of range for y parameter. - * @param maxBValue - upper bound of range for y parameter. - * @param minCValue - lower bound of range for y parameter. - * @param maxCValue - upper bound of range for y parameter. - * @param arraySize - number of elements in each one-dimensional array. (If specified, arrays will be generated.) - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * lines: { - * columns: { - * lineParams: funcs.point({ - * isUnique: true, - * minAValue: -5, maxAValue:20, - * minBValue: 0, maxBValue: 30, - * minCValue: 0, maxCValue: 10 - * }) - * }, - * }, - * })); - * ``` - */ - line: createGenerator(GenerateLine), - // uniqueLine: createGenerator(GenerateUniqueLine), - - /** - * gives you the opportunity to call different generators with different probabilities to generate values for one column. - * @param params - array of generators with probabilities you would like to call them to generate values. - * - * @example - * ```ts - * await seed(db, schema, { count: 1000 }).refine((funcs) => ({ - * posts: { - * columns: { - * content: funcs.weightedRandom([ - * { - * weight: 0.6, - * value: funcs.loremIpsum({ sentencesCount: 3 }), - * }, - * { - * weight: 0.4, - * value: funcs.default({ defaultValue: "TODO" }), - * }, - * ]), - * }, - * }, - * })); - * ``` - */ - weightedRandom: createGenerator(WeightedRandomGenerator), -}; diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 62283c817..6982ac980 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -11,35 +11,11 @@ import type { RefinementsType, TableGeneratorsType, } from '../types/seedService.ts'; -import type { Column, Prettify, Relation, RelationWithReferences, Table } from '../types/tables.ts'; -import type { AbstractGenerator } from './GeneratorsWrappers.ts'; -import { - GenerateArray, - GenerateBoolean, - GenerateDate, - GenerateDatetime, - GenerateDefault, - GenerateEmail, - GenerateEnum, - GenerateFirstName, - GenerateInt, - GenerateInterval, - GenerateIntPrimaryKey, - GenerateJson, - GenerateLine, - GenerateNumber, - GeneratePoint, - GenerateSelfRelationsValuesFromArray, - GenerateString, - GenerateTime, - GenerateTimestamp, - GenerateUniqueString, - GenerateUUID, - GenerateValuesFromArray, - GenerateWeightedCount, - GenerateYear, - HollowGenerator, -} from './GeneratorsWrappers.ts'; +import type { Column, Prettify, Relation, Table } from '../types/tables.ts'; +import type { AbstractGenerator } from './Generators.ts'; + +import * as Generators from './Generators.ts'; +import * as GeneratorsV1 from './GeneratorVersions/GeneratorsV1.ts'; import { equalSets, generateHashFromString } from './utils.ts'; export class SeedService { @@ -57,13 +33,13 @@ export class SeedService { connectionType: 'postgresql' | 'mysql' | 'sqlite', tables: Table[], relations: (Relation & { isCyclic: boolean })[], - tableRelations: { [tableName: string]: RelationWithReferences[] }, refinements?: RefinementsType, - options?: { count?: number; seed?: number }, + options?: { count?: number; seed?: number; version?: number }, ) => { let columnPossibleGenerator: Prettify; let tablePossibleGenerators: Prettify; const customSeed = options?.seed === undefined ? 0 : options.seed; + const version = options?.version === undefined ? Generators.version : options.version; // sorting table in order which they will be filled up (tables with foreign keys case) // relations = relations.filter(rel => rel.type === "one"); @@ -221,35 +197,42 @@ export class SeedService { columnPossibleGenerator.wasRefined = true; } else if (Object.hasOwn(foreignKeyColumns, col.name)) { // TODO: I might need to assign repeatedValuesCount to column there instead of doing so in generateTablesValues - const cyclicRelation = tableRelations[table.name]!.find((rel) => - rel.isCyclic === true + const cyclicRelation = relations.find((rel) => + rel.table === table.name + && rel.isCyclic === true && rel.columns.includes(col.name) ); + // const cyclicRelation = tableRelations[table.name]!.find((rel) => + // rel.isCyclic === true + // && rel.columns.includes(col.name) + // ); + if (cyclicRelation !== undefined) { columnPossibleGenerator.isCyclic = true; } const predicate = cyclicRelation !== undefined && col.notNull === false; if (predicate === true) { - columnPossibleGenerator.generator = new GenerateDefault({ defaultValue: null }); + columnPossibleGenerator.generator = new Generators.GenerateDefault({ defaultValue: null }); columnPossibleGenerator.wasDefinedBefore = true; } - columnPossibleGenerator.generator = new HollowGenerator({}); + columnPossibleGenerator.generator = new Generators.HollowGenerator({}); } // TODO: rewrite pickGeneratorFor... using new col properties: isUnique and notNull else if (connectionType === 'postgresql') { - columnPossibleGenerator.generator = this.pickGeneratorForPostgresColumn( + columnPossibleGenerator.generator = this.selectGeneratorForPostgresColumn( table, col, + version, ); } else if (connectionType === 'mysql') { - columnPossibleGenerator.generator = this.pickGeneratorForMysqlColumn( + columnPossibleGenerator.generator = this.selectGeneratorForMysqlColumn( table, col, ); } else if (connectionType === 'sqlite') { - columnPossibleGenerator.generator = this.pickGeneratorForSqlite( + columnPossibleGenerator.generator = this.selectGeneratorForSqlite( table, col, ); @@ -420,7 +403,7 @@ export class SeedService { count: number, seed: number, ) => { - const gen = new GenerateWeightedCount({}); + const gen = new Generators.GenerateWeightedCount({}); gen.init({ count: weightedCount, seed }); let weightedWithCount = 0; for (let i = 0; i < count; i++) { @@ -430,20 +413,59 @@ export class SeedService { return weightedWithCount; }; - // TODO: revise serial part generators + selectGeneratorOfVersion = (version: number, generatorEntityKind: string) => { + const GeneratorVersions = [Generators, GeneratorsV1]; + + type GeneratorConstructorT = new(params: any) => AbstractGenerator; + let generatorConstructor: GeneratorConstructorT | undefined; + for (const gens of GeneratorVersions) { + const { version: gensVersion, ...filteredGens } = gens; + if (version > gensVersion) break; + + for (const gen of Object.values(filteredGens)) { + const abstractGen = gen as typeof AbstractGenerator; + if (abstractGen[entityKind] === generatorEntityKind) { + generatorConstructor = abstractGen as unknown as GeneratorConstructorT; + + if (abstractGen.version === version) { + return { generatorConstructor }; + } + } + } + } + + if (generatorConstructor === undefined) { + throw new Error(`Can't select version for ${generatorEntityKind} generator`); + } + + return { generatorConstructor }; + }; - pickGeneratorForPostgresColumn = ( + // TODO: revise serial part generators + selectGeneratorForPostgresColumn = ( table: Table, col: Column, + version: number, ) => { const pickGenerator = (table: Table, col: Column) => { // ARRAY if (col.columnType.match(/\[\w*]/g) !== null && col.baseColumn !== undefined) { - const baseColumnGen = this.pickGeneratorForPostgresColumn(table, col.baseColumn!) as AbstractGenerator; + const baseColumnGen = this.selectGeneratorForPostgresColumn( + table, + col.baseColumn!, + version, + ) as AbstractGenerator; if (baseColumnGen === undefined) { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - const generator = new GenerateArray({ baseColumnGen, size: col.size }); + + const { generatorConstructor } = this.selectGeneratorOfVersion( + version, + Generators.GenerateArray[entityKind], + ); + + const generator = new generatorConstructor!({ baseColumnGen, size: col.size }); + // const generator = new Generators.GenerateArray({ baseColumnGen, size: col.size }); return generator; } @@ -457,15 +479,15 @@ export class SeedService { }; baseColumn.columnType = baseColumnType; - const baseColumnGen = this.pickGeneratorForPostgresColumn(table, baseColumn) as AbstractGenerator; + const baseColumnGen = this.selectGeneratorForPostgresColumn(table, baseColumn, version) as AbstractGenerator; if (baseColumnGen === undefined) { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - let generator = new GenerateArray({ baseColumnGen }); + let generator = new Generators.GenerateArray({ baseColumnGen }); for (let i = 0; i < col.typeParams.dimensions! - 1; i++) { - generator = new GenerateArray({ baseColumnGen: generator }); + generator = new Generators.GenerateArray({ baseColumnGen: generator }); } return generator; @@ -479,7 +501,7 @@ export class SeedService { || col.columnType.includes('bigint')) && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateIntPrimaryKey({}); + const generator = new Generators.GenerateIntPrimaryKey({}); return generator; } @@ -527,7 +549,7 @@ export class SeedService { && !col.columnType.includes('interval') && !col.columnType.includes('point') ) { - const generator = new GenerateInt({ + const generator = new Generators.GenerateInt({ minValue, maxValue, }); @@ -536,9 +558,9 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new GenerateIntPrimaryKey({}); + const generator = new Generators.GenerateIntPrimaryKey({}); - (generator as GenerateIntPrimaryKey).maxValue = maxValue; + (generator as Generators.GenerateIntPrimaryKey).maxValue = maxValue; return generator; } @@ -550,7 +572,7 @@ export class SeedService { || col.columnType === 'decimal' || col.columnType === 'numeric' ) { - const generator = new GenerateNumber({}); + const generator = new Generators.GenerateNumber({}); return generator; } @@ -562,7 +584,7 @@ export class SeedService { || col.columnType.startsWith('char')) && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateUniqueString({}); + const generator = new Generators.GenerateUniqueString({}); return generator; } @@ -573,7 +595,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('name') ) { - const generator = new GenerateFirstName({}); + const generator = new Generators.GenerateFirstName({}); return generator; } @@ -584,7 +606,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('email') ) { - const generator = new GenerateEmail({}); + const generator = new Generators.GenerateEmail({}); return generator; } @@ -595,47 +617,47 @@ export class SeedService { || col.columnType.startsWith('char') ) { // console.log(col, table) - const generator = new GenerateString({}); + const generator = new Generators.GenerateString({}); return generator; } // UUID if (col.columnType === 'uuid') { - const generator = new GenerateUUID({}); + const generator = new Generators.GenerateUUID({}); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new GenerateBoolean({}); + const generator = new Generators.GenerateBoolean({}); return generator; } // DATE, TIME, TIMESTAMP if (col.columnType.includes('date')) { - const generator = new GenerateDate({}); + const generator = new Generators.GenerateDate({}); return generator; } if (col.columnType === 'time') { - const generator = new GenerateTime({}); + const generator = new Generators.GenerateTime({}); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new GenerateTimestamp({}); + const generator = new Generators.GenerateTimestamp({}); return generator; } // JSON, JSONB if (col.columnType === 'json' || col.columnType === 'jsonb') { - const generator = new GenerateJson({}); + const generator = new Generators.GenerateJson({}); return generator; } @@ -647,7 +669,7 @@ export class SeedService { // ENUM if (col.enumValues !== undefined) { - const generator = new GenerateEnum({ + const generator = new Generators.GenerateEnum({ enumValues: col.enumValues, }); @@ -657,32 +679,32 @@ export class SeedService { // INTERVAL if (col.columnType.startsWith('interval')) { if (col.columnType === 'interval') { - const generator = new GenerateInterval({}); + const generator = new Generators.GenerateInterval({}); return generator; } - const fields = col.columnType.replace('interval ', '') as GenerateInterval['params']['fields']; - const generator = new GenerateInterval({ fields }); + const fields = col.columnType.replace('interval ', '') as Generators.GenerateInterval['params']['fields']; + const generator = new Generators.GenerateInterval({ fields }); return generator; } // POINT, LINE if (col.columnType.includes('point')) { - const generator = new GeneratePoint({}); + const generator = new Generators.GeneratePoint({}); return generator; } if (col.columnType.includes('line')) { - const generator = new GenerateLine({}); + const generator = new Generators.GenerateLine({}); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new GenerateDefault({ + const generator = new Generators.GenerateDefault({ defaultValue: col.default, }); return generator; @@ -701,7 +723,7 @@ export class SeedService { return generator; }; - pickGeneratorForMysqlColumn = ( + selectGeneratorForMysqlColumn = ( table: Table, col: Column, ) => { @@ -711,7 +733,7 @@ export class SeedService { (col.columnType.includes('serial') || col.columnType.includes('int')) && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateIntPrimaryKey({}); + const generator = new Generators.GenerateIntPrimaryKey({}); return generator; } @@ -746,7 +768,7 @@ export class SeedService { } if (col.columnType.includes('int')) { - const generator = new GenerateInt({ + const generator = new Generators.GenerateInt({ minValue, maxValue, }); @@ -754,7 +776,7 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new GenerateIntPrimaryKey({}); + const generator = new Generators.GenerateIntPrimaryKey({}); generator.maxValue = maxValue; return generator; } @@ -766,7 +788,7 @@ export class SeedService { || col.columnType === 'decimal' || col.columnType === 'float' ) { - const generator = new GenerateNumber({}); + const generator = new Generators.GenerateNumber({}); return generator; } @@ -780,7 +802,7 @@ export class SeedService { || col.columnType.includes('varbinary')) && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateUniqueString({}); + const generator = new Generators.GenerateUniqueString({}); return generator; } @@ -793,7 +815,7 @@ export class SeedService { || col.columnType.includes('varbinary')) && col.name.toLowerCase().includes('name') ) { - const generator = new GenerateFirstName({}); + const generator = new Generators.GenerateFirstName({}); return generator; } @@ -806,7 +828,7 @@ export class SeedService { || col.columnType.includes('varbinary')) && col.name.toLowerCase().includes('email') ) { - const generator = new GenerateEmail({}); + const generator = new Generators.GenerateEmail({}); return generator; } @@ -819,58 +841,58 @@ export class SeedService { || col.columnType.includes('varbinary') ) { // console.log(col, table); - const generator = new GenerateString({}); + const generator = new Generators.GenerateString({}); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new GenerateBoolean({}); + const generator = new Generators.GenerateBoolean({}); return generator; } // DATE, TIME, TIMESTAMP, DATETIME, YEAR if (col.columnType.includes('datetime')) { - const generator = new GenerateDatetime({}); + const generator = new Generators.GenerateDatetime({}); return generator; } if (col.columnType.includes('date')) { - const generator = new GenerateDate({}); + const generator = new Generators.GenerateDate({}); return generator; } if (col.columnType === 'time') { - const generator = new GenerateTime({}); + const generator = new Generators.GenerateTime({}); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new GenerateTimestamp({}); + const generator = new Generators.GenerateTimestamp({}); return generator; } if (col.columnType === 'year') { - const generator = new GenerateYear({}); + const generator = new Generators.GenerateYear({}); return generator; } // JSON if (col.columnType === 'json') { - const generator = new GenerateJson({}); + const generator = new Generators.GenerateJson({}); return generator; } // ENUM if (col.enumValues !== undefined) { - const generator = new GenerateEnum({ + const generator = new Generators.GenerateEnum({ enumValues: col.enumValues, }); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new GenerateDefault({ + const generator = new Generators.GenerateDefault({ defaultValue: col.default, }); return generator; @@ -879,7 +901,7 @@ export class SeedService { return; }; - pickGeneratorForSqlite = ( + selectGeneratorForSqlite = ( table: Table, col: Column, ) => { @@ -888,17 +910,17 @@ export class SeedService { (col.columnType === 'integer' || col.columnType === 'numeric') && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateIntPrimaryKey({}); + const generator = new Generators.GenerateIntPrimaryKey({}); return generator; } if (col.columnType === 'integer' && col.dataType === 'boolean') { - const generator = new GenerateBoolean({}); + const generator = new Generators.GenerateBoolean({}); return generator; } if ((col.columnType === 'integer' && col.dataType === 'date')) { - const generator = new GenerateTimestamp({}); + const generator = new Generators.GenerateTimestamp({}); return generator; } @@ -907,13 +929,13 @@ export class SeedService { || col.columnType === 'numeric' || (col.dataType === 'bigint' && col.columnType === 'blob') ) { - const generator = new GenerateInt({}); + const generator = new Generators.GenerateInt({}); return generator; } // number section ------------------------------------------------------------------------------------ if (col.columnType === 'real' || col.columnType === 'numeric') { - const generator = new GenerateNumber({}); + const generator = new Generators.GenerateNumber({}); return generator; } @@ -924,7 +946,7 @@ export class SeedService { || col.columnType === 'blob') && table.primaryKeys.includes(col.name) ) { - const generator = new GenerateUniqueString({}); + const generator = new Generators.GenerateUniqueString({}); return generator; } @@ -934,7 +956,7 @@ export class SeedService { || col.columnType === 'blob') && col.name.toLowerCase().includes('name') ) { - const generator = new GenerateFirstName({}); + const generator = new Generators.GenerateFirstName({}); return generator; } @@ -944,7 +966,7 @@ export class SeedService { || col.columnType === 'blob') && col.name.toLowerCase().includes('email') ) { - const generator = new GenerateEmail({}); + const generator = new Generators.GenerateEmail({}); return generator; } @@ -954,7 +976,7 @@ export class SeedService { || col.columnType === 'blob' || col.columnType === 'blobbuffer' ) { - const generator = new GenerateString({}); + const generator = new Generators.GenerateString({}); return generator; } @@ -962,12 +984,12 @@ export class SeedService { (col.columnType === 'text' && col.dataType === 'json') || (col.columnType === 'blob' && col.dataType === 'json') ) { - const generator = new GenerateJson({}); + const generator = new Generators.GenerateJson({}); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new GenerateDefault({ + const generator = new Generators.GenerateDefault({ defaultValue: col.default, }); return generator; @@ -1134,7 +1156,7 @@ export class SeedService { }))!.map((rows) => rows[refColName]) as (string | number | boolean)[]; hasSelfRelation = true; - genObj = new GenerateSelfRelationsValuesFromArray({ + genObj = new Generators.GenerateSelfRelationsValuesFromArray({ values: refColumnValues, }); } else if ( @@ -1154,10 +1176,10 @@ export class SeedService { weightedCountSeed = table.withFromTable[rel.refTable]!.weightedCountSeed; } - genObj = new GenerateValuesFromArray({ values: refColumnValues }); - (genObj as GenerateValuesFromArray).notNull = tableGenerators[rel.columns[colIdx]!]!.notNull; - (genObj as GenerateValuesFromArray).weightedCountSeed = weightedCountSeed; - (genObj as GenerateValuesFromArray).maxRepeatedValuesCount = repeatedValuesCount; + genObj = new Generators.GenerateValuesFromArray({ values: refColumnValues }); + (genObj as Generators.GenerateValuesFromArray).notNull = tableGenerators[rel.columns[colIdx]!]!.notNull; + (genObj as Generators.GenerateValuesFromArray).weightedCountSeed = weightedCountSeed; + (genObj as Generators.GenerateValuesFromArray).maxRepeatedValuesCount = repeatedValuesCount; } // console.log(rel.columns[colIdx], tableGenerators) diff --git a/drizzle-seed/src/types/seedService.ts b/drizzle-seed/src/types/seedService.ts index 0b5237468..1ae06f44c 100644 --- a/drizzle-seed/src/types/seedService.ts +++ b/drizzle-seed/src/types/seedService.ts @@ -1,4 +1,4 @@ -import type { AbstractGenerator } from '../services/GeneratorsWrappers.ts'; +import type { AbstractGenerator } from '../services/Generators.ts'; import type { Prettify } from './tables.ts'; export type TableGeneratorsType = { diff --git a/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts b/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts index 9d79ebaa8..3473e555a 100644 --- a/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts +++ b/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts @@ -40,7 +40,7 @@ import { GenerateValuesFromArray, GenerateYear, WeightedRandomGenerator, -} from '../../src/services/GeneratorsWrappers.ts'; +} from '../../src/services/Generators.ts'; const benchmark = ({ generatorName, generator, count = 100000, seed = 1 }: { generatorName: string; From 06a936837e1f3d61cf0f89002579209faf5570e0 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 18 Dec 2024 16:20:32 +0200 Subject: [PATCH 06/18] added generator versioning with version v1 for string and interval generators --- .../GeneratorVersions/GeneratorsV1.ts | 152 ++++- drizzle-seed/src/services/Generators.ts | 2 +- drizzle-seed/src/services/SeedService.ts | 525 ++++++++++-------- 3 files changed, 428 insertions(+), 251 deletions(-) diff --git a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts index e4fed2926..81655a941 100644 --- a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts +++ b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts @@ -1,5 +1,6 @@ import { entityKind } from 'drizzle-orm'; -import { GenerateInterval, GenerateUniqueInterval } from '../Generators.ts'; +import prand from 'pure-rand'; +import { AbstractGenerator, GenerateInterval } from '../Generators.ts'; export const version = 1; @@ -9,7 +10,152 @@ export class GenerateIntervalV1 extends GenerateInterval { override uniqueVersionOfGen = GenerateUniqueIntervalV1; } -export class GenerateUniqueIntervalV1 extends GenerateUniqueInterval { +export class GenerateUniqueIntervalV1 extends AbstractGenerator<{ + isUnique?: boolean; +}> { static override readonly [entityKind]: string = 'GenerateUniqueInterval'; - static override readonly ['version']: number = 1; + + private state: { + rng: prand.RandomGenerator; + intervalSet: Set; + } | undefined; + public override isUnique = true; + + override init({ count, seed }: { count: number; seed: number }) { + const maxUniqueIntervalsNumber = 6 * 13 * 29 * 25 * 61 * 61; + if (count > maxUniqueIntervalsNumber) { + throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); + } + + const rng = prand.xoroshiro128plus(seed); + const intervalSet = new Set(); + this.state = { rng, intervalSet }; + } + + generate() { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + let yearsNumb: number, + monthsNumb: number, + daysNumb: number, + hoursNumb: number, + minutesNumb: number, + secondsNumb: number; + + let interval = ''; + + for (;;) { + [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); + [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); + [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); + [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); + [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); + [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); + + interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` + + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` + + `${daysNumb === 0 ? '' : `${daysNumb} days `}` + + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` + + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` + + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; + + if (!this.state.intervalSet.has(interval)) { + this.state.intervalSet.add(interval); + break; + } + } + + return interval; + } +} + +export class GenerateStringV1 extends AbstractGenerator<{ + isUnique?: boolean; + arraySize?: number; +}> { + static override readonly [entityKind]: string = 'GenerateString'; + + private state: { rng: prand.RandomGenerator } | undefined; + override uniqueVersionOfGen = GenerateUniqueStringV1; + + override init({ count, seed }: { count: number; seed: number }) { + super.init({ count, seed }); + + const rng = prand.xoroshiro128plus(seed); + this.state = { rng }; + } + + generate() { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + const minStringLength = 7; + const maxStringLength = 20; + const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let idx: number, + strLength: number, + currStr: string; + + currStr = ''; + [strLength, this.state.rng] = prand.uniformIntDistribution( + minStringLength, + maxStringLength, + this.state.rng, + ); + for (let j = 0; j < strLength; j++) { + [idx, this.state.rng] = prand.uniformIntDistribution( + 0, + stringChars.length - 1, + this.state.rng, + ); + currStr += stringChars[idx]; + } + return currStr; + } +} + +export class GenerateUniqueStringV1 extends AbstractGenerator<{ isUnique?: boolean }> { + static override readonly [entityKind]: string = 'GenerateUniqueString'; + + private state: { rng: prand.RandomGenerator } | undefined; + public override isUnique = true; + + override init({ seed }: { seed: number }) { + const rng = prand.xoroshiro128plus(seed); + this.state = { rng }; + } + + generate({ i }: { i: number }) { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + const minStringLength = 7; + const maxStringLength = 20; + const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let idx: number, + strLength: number; + let currStr: string; + + currStr = ''; + const uniqueStr = i.toString(16); + [strLength, this.state.rng] = prand.uniformIntDistribution( + minStringLength, + maxStringLength - uniqueStr.length, + this.state.rng, + ); + for (let j = 0; j < strLength - uniqueStr.length; j++) { + [idx, this.state.rng] = prand.uniformIntDistribution( + 0, + stringChars.length - 1, + this.state.rng, + ); + currStr += stringChars[idx]; + } + + return currStr.slice(0, 4) + uniqueStr + currStr.slice(4); + } } diff --git a/drizzle-seed/src/services/Generators.ts b/drizzle-seed/src/services/Generators.ts index f5fc44aab..fa01a1e4f 100644 --- a/drizzle-seed/src/services/Generators.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -1235,7 +1235,7 @@ export class GenerateInterval extends AbstractGenerator<{ rng: prand.RandomGenerator; fieldsToGenerate: string[]; } | undefined; - override uniqueVersionOfGen = GenerateUniqueInterval; + override uniqueVersionOfGen: new(params: any) => AbstractGenerator = GenerateUniqueInterval; private config: { [key: string]: { from: number; to: number } } = { year: { from: 0, diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 6982ac980..6ceae5167 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -178,7 +178,12 @@ export class SeedService { && refinements[table.name]!.columns !== undefined && refinements[table.name]!.columns[col.name] !== undefined ) { - const genObj = refinements[table.name]!.columns[col.name]!; + let genObj = refinements[table.name]!.columns[col.name]!; + + const genObjEntityKind = genObj.getEntityKind(); + const generatorConstructor = this.selectGeneratorOfVersion(version, genObjEntityKind); + genObj = new generatorConstructor(genObj.params); + // TODO: for now only GenerateValuesFromArray support notNull property genObj.notNull = col.notNull; if (col.columnType.match(/\[\w*]/g) !== null) { @@ -203,11 +208,6 @@ export class SeedService { && rel.columns.includes(col.name) ); - // const cyclicRelation = tableRelations[table.name]!.find((rel) => - // rel.isCyclic === true - // && rel.columns.includes(col.name) - // ); - if (cyclicRelation !== undefined) { columnPossibleGenerator.isCyclic = true; } @@ -230,11 +230,13 @@ export class SeedService { columnPossibleGenerator.generator = this.selectGeneratorForMysqlColumn( table, col, + version, ); } else if (connectionType === 'sqlite') { columnPossibleGenerator.generator = this.selectGeneratorForSqlite( table, col, + version, ); } @@ -428,17 +430,17 @@ export class SeedService { generatorConstructor = abstractGen as unknown as GeneratorConstructorT; if (abstractGen.version === version) { - return { generatorConstructor }; + return generatorConstructor; } } } } if (generatorConstructor === undefined) { - throw new Error(`Can't select version for ${generatorEntityKind} generator`); + throw new Error(`Can't select ${generatorEntityKind} generator for ${version} version.`); } - return { generatorConstructor }; + return generatorConstructor; }; // TODO: revise serial part generators @@ -459,13 +461,13 @@ export class SeedService { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - const { generatorConstructor } = this.selectGeneratorOfVersion( - version, - Generators.GenerateArray[entityKind], - ); + // const { generatorConstructor } = this.selectGeneratorOfVersion( + // version, + // Generators.GenerateArray[entityKind], + // ); - const generator = new generatorConstructor!({ baseColumnGen, size: col.size }); - // const generator = new Generators.GenerateArray({ baseColumnGen, size: col.size }); + // const generator = new generatorConstructor!({ baseColumnGen, size: col.size }); + const generator = new Generators.GenerateArray({ baseColumnGen, size: col.size }); return generator; } @@ -616,7 +618,6 @@ export class SeedService { || col.columnType.startsWith('varchar') || col.columnType.startsWith('char') ) { - // console.log(col, table) const generator = new Generators.GenerateString({}); return generator; @@ -713,8 +714,15 @@ export class SeedService { return; }; - const generator = pickGenerator(table, col); + let generator = pickGenerator(table, col) as AbstractGenerator || undefined; if (generator !== undefined) { + const generatorConstructor = this.selectGeneratorOfVersion( + version, + generator.getEntityKind(), + ); + + generator = new generatorConstructor(generator.params); + generator.isUnique = col.isUnique; generator.dataType = col.dataType; generator.stringLength = col.typeParams.length; @@ -726,276 +734,304 @@ export class SeedService { selectGeneratorForMysqlColumn = ( table: Table, col: Column, + version: number, ) => { - // console.log(col); - // INT ------------------------------------------------------------------------------------------------------------ - if ( - (col.columnType.includes('serial') || col.columnType.includes('int')) - && table.primaryKeys.includes(col.name) - ) { - const generator = new Generators.GenerateIntPrimaryKey({}); - return generator; - } + const pickGenerator = (table: Table, col: Column) => { + // INT ------------------------------------------------------------------------------------------------------------ + if ( + (col.columnType.includes('serial') || col.columnType.includes('int')) + && table.primaryKeys.includes(col.name) + ) { + const generator = new Generators.GenerateIntPrimaryKey({}); + return generator; + } - let minValue: number | bigint | undefined; - let maxValue: number | bigint | undefined; - if (col.columnType === 'serial') { - // 2^64 % 2 - 1, 8 bytes - minValue = BigInt(0); - maxValue = BigInt('9223372036854775807'); - } else if (col.columnType.includes('int')) { - if (col.columnType === 'tinyint') { - // 2^8 / 2 - 1, 1 bytes - minValue = -128; - maxValue = 127; - } else if (col.columnType === 'smallint') { - // 2^16 / 2 - 1, 2 bytes - minValue = -32768; - maxValue = 32767; - } else if (col.columnType === 'mediumint') { - // 2^16 / 2 - 1, 2 bytes - minValue = -8388608; - maxValue = 8388607; - } else if (col.columnType === 'int') { - // 2^32 / 2 - 1, 4 bytes - minValue = -2147483648; - maxValue = 2147483647; - } else if (col.columnType === 'bigint') { - // 2^64 / 2 - 1, 8 bytes - minValue = BigInt('-9223372036854775808'); + let minValue: number | bigint | undefined; + let maxValue: number | bigint | undefined; + if (col.columnType === 'serial') { + // 2^64 % 2 - 1, 8 bytes + minValue = BigInt(0); maxValue = BigInt('9223372036854775807'); + } else if (col.columnType.includes('int')) { + if (col.columnType === 'tinyint') { + // 2^8 / 2 - 1, 1 bytes + minValue = -128; + maxValue = 127; + } else if (col.columnType === 'smallint') { + // 2^16 / 2 - 1, 2 bytes + minValue = -32768; + maxValue = 32767; + } else if (col.columnType === 'mediumint') { + // 2^16 / 2 - 1, 2 bytes + minValue = -8388608; + maxValue = 8388607; + } else if (col.columnType === 'int') { + // 2^32 / 2 - 1, 4 bytes + minValue = -2147483648; + maxValue = 2147483647; + } else if (col.columnType === 'bigint') { + // 2^64 / 2 - 1, 8 bytes + minValue = BigInt('-9223372036854775808'); + maxValue = BigInt('9223372036854775807'); + } } - } - if (col.columnType.includes('int')) { - const generator = new Generators.GenerateInt({ - minValue, - maxValue, - }); - return generator; - } + if (col.columnType.includes('int')) { + const generator = new Generators.GenerateInt({ + minValue, + maxValue, + }); + return generator; + } - if (col.columnType.includes('serial')) { - const generator = new Generators.GenerateIntPrimaryKey({}); - generator.maxValue = maxValue; - return generator; - } + if (col.columnType.includes('serial')) { + const generator = new Generators.GenerateIntPrimaryKey({}); + generator.maxValue = maxValue; + return generator; + } - // NUMBER(real, double, decimal, float) - if ( - col.columnType === 'real' - || col.columnType === 'double' - || col.columnType === 'decimal' - || col.columnType === 'float' - ) { - const generator = new Generators.GenerateNumber({}); - return generator; - } + // NUMBER(real, double, decimal, float) + if ( + col.columnType === 'real' + || col.columnType === 'double' + || col.columnType === 'decimal' + || col.columnType === 'float' + ) { + const generator = new Generators.GenerateNumber({}); + return generator; + } - // STRING - if ( - (col.columnType === 'text' - || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) - && table.primaryKeys.includes(col.name) - ) { - const generator = new Generators.GenerateUniqueString({}); - return generator; - } + // STRING + if ( + (col.columnType === 'text' + || col.columnType === 'blob' + || col.columnType.includes('char') + || col.columnType.includes('varchar') + || col.columnType.includes('binary') + || col.columnType.includes('varbinary')) + && table.primaryKeys.includes(col.name) + ) { + const generator = new Generators.GenerateUniqueString({}); + return generator; + } - if ( - (col.columnType === 'text' - || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) - && col.name.toLowerCase().includes('name') - ) { - const generator = new Generators.GenerateFirstName({}); - return generator; - } + if ( + (col.columnType === 'text' + || col.columnType === 'blob' + || col.columnType.includes('char') + || col.columnType.includes('varchar') + || col.columnType.includes('binary') + || col.columnType.includes('varbinary')) + && col.name.toLowerCase().includes('name') + ) { + const generator = new Generators.GenerateFirstName({}); + return generator; + } - if ( - (col.columnType === 'text' + if ( + (col.columnType === 'text' + || col.columnType === 'blob' + || col.columnType.includes('char') + || col.columnType.includes('varchar') + || col.columnType.includes('binary') + || col.columnType.includes('varbinary')) + && col.name.toLowerCase().includes('email') + ) { + const generator = new Generators.GenerateEmail({}); + return generator; + } + + if ( + col.columnType === 'text' || col.columnType === 'blob' || col.columnType.includes('char') || col.columnType.includes('varchar') || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) - && col.name.toLowerCase().includes('email') - ) { - const generator = new Generators.GenerateEmail({}); - return generator; - } + || col.columnType.includes('varbinary') + ) { + const generator = new Generators.GenerateString({}); + return generator; + } - if ( - col.columnType === 'text' - || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary') - ) { - // console.log(col, table); - const generator = new Generators.GenerateString({}); - return generator; - } + // BOOLEAN + if (col.columnType === 'boolean') { + const generator = new Generators.GenerateBoolean({}); + return generator; + } - // BOOLEAN - if (col.columnType === 'boolean') { - const generator = new Generators.GenerateBoolean({}); - return generator; - } + // DATE, TIME, TIMESTAMP, DATETIME, YEAR + if (col.columnType.includes('datetime')) { + const generator = new Generators.GenerateDatetime({}); + return generator; + } - // DATE, TIME, TIMESTAMP, DATETIME, YEAR - if (col.columnType.includes('datetime')) { - const generator = new Generators.GenerateDatetime({}); - return generator; - } + if (col.columnType.includes('date')) { + const generator = new Generators.GenerateDate({}); + return generator; + } - if (col.columnType.includes('date')) { - const generator = new Generators.GenerateDate({}); - return generator; - } + if (col.columnType === 'time') { + const generator = new Generators.GenerateTime({}); + return generator; + } - if (col.columnType === 'time') { - const generator = new Generators.GenerateTime({}); - return generator; - } + if (col.columnType.includes('timestamp')) { + const generator = new Generators.GenerateTimestamp({}); + return generator; + } - if (col.columnType.includes('timestamp')) { - const generator = new Generators.GenerateTimestamp({}); - return generator; - } + if (col.columnType === 'year') { + const generator = new Generators.GenerateYear({}); + return generator; + } - if (col.columnType === 'year') { - const generator = new Generators.GenerateYear({}); - return generator; - } + // JSON + if (col.columnType === 'json') { + const generator = new Generators.GenerateJson({}); + return generator; + } - // JSON - if (col.columnType === 'json') { - const generator = new Generators.GenerateJson({}); - return generator; - } + // ENUM + if (col.enumValues !== undefined) { + const generator = new Generators.GenerateEnum({ + enumValues: col.enumValues, + }); + return generator; + } - // ENUM - if (col.enumValues !== undefined) { - const generator = new Generators.GenerateEnum({ - enumValues: col.enumValues, - }); - return generator; - } + if (col.hasDefault && col.default !== undefined) { + const generator = new Generators.GenerateDefault({ + defaultValue: col.default, + }); + return generator; + } - if (col.hasDefault && col.default !== undefined) { - const generator = new Generators.GenerateDefault({ - defaultValue: col.default, - }); - return generator; + return; + }; + + let generator = pickGenerator(table, col) as AbstractGenerator || undefined; + if (generator !== undefined) { + const generatorConstructor = this.selectGeneratorOfVersion( + version, + generator.getEntityKind(), + ); + + generator = new generatorConstructor(generator.params); } - return; + return generator; }; selectGeneratorForSqlite = ( table: Table, col: Column, + version: number, ) => { - // int section --------------------------------------------------------------------------------------- - if ( - (col.columnType === 'integer' || col.columnType === 'numeric') - && table.primaryKeys.includes(col.name) - ) { - const generator = new Generators.GenerateIntPrimaryKey({}); - return generator; - } + const pickGenerator = (table: Table, col: Column) => { + // int section --------------------------------------------------------------------------------------- + if ( + (col.columnType === 'integer' || col.columnType === 'numeric') + && table.primaryKeys.includes(col.name) + ) { + const generator = new Generators.GenerateIntPrimaryKey({}); + return generator; + } - if (col.columnType === 'integer' && col.dataType === 'boolean') { - const generator = new Generators.GenerateBoolean({}); - return generator; - } + if (col.columnType === 'integer' && col.dataType === 'boolean') { + const generator = new Generators.GenerateBoolean({}); + return generator; + } - if ((col.columnType === 'integer' && col.dataType === 'date')) { - const generator = new Generators.GenerateTimestamp({}); - return generator; - } + if ((col.columnType === 'integer' && col.dataType === 'date')) { + const generator = new Generators.GenerateTimestamp({}); + return generator; + } - if ( - col.columnType === 'integer' - || col.columnType === 'numeric' - || (col.dataType === 'bigint' && col.columnType === 'blob') - ) { - const generator = new Generators.GenerateInt({}); - return generator; - } + if ( + col.columnType === 'integer' + || col.columnType === 'numeric' + || (col.dataType === 'bigint' && col.columnType === 'blob') + ) { + const generator = new Generators.GenerateInt({}); + return generator; + } - // number section ------------------------------------------------------------------------------------ - if (col.columnType === 'real' || col.columnType === 'numeric') { - const generator = new Generators.GenerateNumber({}); - return generator; - } + // number section ------------------------------------------------------------------------------------ + if (col.columnType === 'real' || col.columnType === 'numeric') { + const generator = new Generators.GenerateNumber({}); + return generator; + } - // string section ------------------------------------------------------------------------------------ - if ( - (col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob') - && table.primaryKeys.includes(col.name) - ) { - const generator = new Generators.GenerateUniqueString({}); - return generator; - } + // string section ------------------------------------------------------------------------------------ + if ( + (col.columnType === 'text' + || col.columnType === 'numeric' + || col.columnType === 'blob') + && table.primaryKeys.includes(col.name) + ) { + const generator = new Generators.GenerateUniqueString({}); + return generator; + } - if ( - (col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob') - && col.name.toLowerCase().includes('name') - ) { - const generator = new Generators.GenerateFirstName({}); - return generator; - } + if ( + (col.columnType === 'text' + || col.columnType === 'numeric' + || col.columnType === 'blob') + && col.name.toLowerCase().includes('name') + ) { + const generator = new Generators.GenerateFirstName({}); + return generator; + } - if ( - (col.columnType === 'text' + if ( + (col.columnType === 'text' + || col.columnType === 'numeric' + || col.columnType === 'blob') + && col.name.toLowerCase().includes('email') + ) { + const generator = new Generators.GenerateEmail({}); + return generator; + } + + if ( + col.columnType === 'text' || col.columnType === 'numeric' - || col.columnType === 'blob') - && col.name.toLowerCase().includes('email') - ) { - const generator = new Generators.GenerateEmail({}); - return generator; - } + || col.columnType === 'blob' + || col.columnType === 'blobbuffer' + ) { + const generator = new Generators.GenerateString({}); + return generator; + } - if ( - col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob' - || col.columnType === 'blobbuffer' - ) { - const generator = new Generators.GenerateString({}); - return generator; - } + if ( + (col.columnType === 'text' && col.dataType === 'json') + || (col.columnType === 'blob' && col.dataType === 'json') + ) { + const generator = new Generators.GenerateJson({}); + return generator; + } - if ( - (col.columnType === 'text' && col.dataType === 'json') - || (col.columnType === 'blob' && col.dataType === 'json') - ) { - const generator = new Generators.GenerateJson({}); - return generator; - } + if (col.hasDefault && col.default !== undefined) { + const generator = new Generators.GenerateDefault({ + defaultValue: col.default, + }); + return generator; + } - if (col.hasDefault && col.default !== undefined) { - const generator = new Generators.GenerateDefault({ - defaultValue: col.default, - }); - return generator; + return; + }; + + let generator = pickGenerator(table, col) as AbstractGenerator || undefined; + if (generator !== undefined) { + const generatorConstructor = this.selectGeneratorOfVersion( + version, + generator.getEntityKind(), + ); + + generator = new generatorConstructor(generator.params); } - return; + return generator; }; filterCyclicTables = (tablesGenerators: ReturnType) => { @@ -1056,9 +1092,6 @@ export class SeedService { tablesUniqueNotNullColumn?: { [tableName: string]: { uniqueNotNullColName: string } }; }, ) => { - // console.time( - // "generateTablesValues-----------------------------------------------------" - // ); const customSeed = options?.seed === undefined ? 0 : options.seed; let tableCount: number | undefined; let columnsGenerators: Prettify[]; @@ -1074,7 +1107,6 @@ export class SeedService { }[] = options?.tablesValues === undefined ? [] : options.tablesValues; let pRNGSeed: number; - // relations = relations.filter(rel => rel.type === "one"); let filteredRelations: typeof relations; let preserveData: boolean, insertDataInDb: boolean = true, updateDataInDb: boolean = false; @@ -1182,7 +1214,6 @@ export class SeedService { (genObj as Generators.GenerateValuesFromArray).maxRepeatedValuesCount = repeatedValuesCount; } - // console.log(rel.columns[colIdx], tableGenerators) if (genObj !== undefined) { tableGenerators[rel.columns[colIdx]!]!.generator = genObj; } From 455725c635960ba9757870c2522dd0e8e9ce0388 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 18 Dec 2024 19:46:41 +0200 Subject: [PATCH 07/18] updated changelogs --- changelogs/drizzle-seed/0.1.4.md | 106 +++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 changelogs/drizzle-seed/0.1.4.md diff --git a/changelogs/drizzle-seed/0.1.4.md b/changelogs/drizzle-seed/0.1.4.md new file mode 100644 index 000000000..84c3821af --- /dev/null +++ b/changelogs/drizzle-seed/0.1.4.md @@ -0,0 +1,106 @@ +## Improvements + +- A new parameter, `version`, was added to the `seed` function options, which will control generator versioning. + +```ts +await seed(db, schema, { version: 1 }); +``` + +Generator versions will change within a single major version of drizzle-seed. + +The old version of the generator will become available if the generator has been changed and the output of the new generator does not match the output of the old generator. + +#### For example: + +`FirstNameGen` was changed, and old version, `V1`, of this generator became available. + +Later, `LastNameGen` was changed in the next minor version of drizzle-seed, making `V2` version of this generator available. + +| | `V1` | `V2` | `V3(latest)` | +| :--------------: | :--------------: | :-------------: | :--------------: | +| **FirstNameGen** | `FirstNameGenV1` | `same as V3` | `FirstNameGenV3` | +| **LastNameGen** | `same as V2` | `LastNameGenV2` | `LastNameGenV3` | + +If you omit version, `V3` version of generators will be used. + +If you specify version of 2, `FirstNameGen` will use its `V3` version and `LastNameGen` will use its `V2` version. + +If you specify version of 1, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V2` version. + +## + +- added `fields` as new parameter in `interval` generator + +`interval` generator generates intervals based on the following principle: + +fields to the right of the last specified field are zeroed out, while fields to the left remain valid. + +Thus, for example, there is no difference between the `year to month` fields and the `month` fields, because fields to the right of `month` (`day`, `hour`, `minute`, `second`) are zeroed out, while fields to the left (`year`) remain valid. + +Example + +```ts +import { pgTable, interval } from "drizzle-orm/pg-core"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { seed } from "drizzle-seed"; + +const intervals = pgTable("intervals", { + interval: interval(), +}); + +async function main() { + const db = drizzle(process.env.DATABASE_URL!); + + await seed(db, { intervals }, { count: 1000 }).refine((funcs) => ({ + intervals: { + columns: { + interval: funcs.interval({ + fields: "day to hour", + }), + }, + }, + })); +} + +main(); +``` + +You can also specify fields in a table and seed them automatically. + +```ts +import { pgTable, interval } from "drizzle-orm/pg-core"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { seed } from "drizzle-seed"; + +const intervals = pgTable("intervals", { + interval: interval({ fields: "day to hour" }), +}); + +async function main() { + const db = drizzle(process.env.DATABASE_URL!); + + await seed(db, { intervals }, { count: 1000 }); +} + +main(); +``` + +## Breaking changes + +- Unique `interval` generator was changed, so `1` version of this generator become available. **The latest version is `2`.** + +**Cause:** + +**Bug in generator:** +Old version of generator could generate intervals like: `1 minute 60 second`, `2 minute 0 second` and treat them as different intervals. + +However, after inserting the `1 minute 60 second` interval, PostgreSQL database will convert it to `2 minute 0 second`. As a result, subsequent insertion of the `2 minute 0 second` interval into a unique column will cause an error. + +## + +- Both non-unique and unique `string` generators were changed, making version `1` of these generators available. **The latest version is `2`.** + +**Cause:** + +**Generating strings based on text-like column length:** +Now (in version 2), the maximum length of a string depends on the length of the text column (e.g., `varchar(20)`). From cb89775679a335e8700019e0e4878b98479e0e1e Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Thu, 19 Dec 2024 14:56:51 +0200 Subject: [PATCH 08/18] updated generator versioning --- drizzle-seed/src/services/GeneratorFuncs.ts | 4 +- .../GeneratorVersions/GeneratorsV1.ts | 161 ------------ .../GeneratorVersions/GeneratorsV2.ts | 240 ++++++++++++++++++ drizzle-seed/src/services/Generators.ts | 148 +++-------- drizzle-seed/src/services/SeedService.ts | 16 +- 5 files changed, 288 insertions(+), 281 deletions(-) delete mode 100644 drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts create mode 100644 drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts index 08f36b1dc..65e31fc09 100644 --- a/drizzle-seed/src/services/GeneratorFuncs.ts +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -24,7 +24,6 @@ import { GeneratePostcode, GenerateState, GenerateStreetAdddress, - GenerateString, GenerateTime, GenerateTimestamp, GenerateUUID, @@ -32,6 +31,7 @@ import { GenerateYear, WeightedRandomGenerator, } from './Generators.ts'; +import { GenerateStringV2 } from './GeneratorVersions/GeneratorsV2.ts'; function createGenerator, T>( generatorConstructor: new(params: T) => GeneratorType, @@ -359,7 +359,7 @@ export const generatorsFuncs = { * })); * ``` */ - string: createGenerator(GenerateString), + string: createGenerator(GenerateStringV2), // uniqueString: createGenerator(GenerateUniqueString), /** diff --git a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts deleted file mode 100644 index 81655a941..000000000 --- a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV1.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { entityKind } from 'drizzle-orm'; -import prand from 'pure-rand'; -import { AbstractGenerator, GenerateInterval } from '../Generators.ts'; - -export const version = 1; - -export class GenerateIntervalV1 extends GenerateInterval { - static override readonly [entityKind]: string = 'GenerateInterval'; - static override readonly ['version']: number = 1; - override uniqueVersionOfGen = GenerateUniqueIntervalV1; -} - -export class GenerateUniqueIntervalV1 extends AbstractGenerator<{ - isUnique?: boolean; -}> { - static override readonly [entityKind]: string = 'GenerateUniqueInterval'; - - private state: { - rng: prand.RandomGenerator; - intervalSet: Set; - } | undefined; - public override isUnique = true; - - override init({ count, seed }: { count: number; seed: number }) { - const maxUniqueIntervalsNumber = 6 * 13 * 29 * 25 * 61 * 61; - if (count > maxUniqueIntervalsNumber) { - throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); - } - - const rng = prand.xoroshiro128plus(seed); - const intervalSet = new Set(); - this.state = { rng, intervalSet }; - } - - generate() { - if (this.state === undefined) { - throw new Error('state is not defined.'); - } - - let yearsNumb: number, - monthsNumb: number, - daysNumb: number, - hoursNumb: number, - minutesNumb: number, - secondsNumb: number; - - let interval = ''; - - for (;;) { - [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); - [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); - [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); - [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); - [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - - interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` - + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` - + `${daysNumb === 0 ? '' : `${daysNumb} days `}` - + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` - + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` - + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; - - if (!this.state.intervalSet.has(interval)) { - this.state.intervalSet.add(interval); - break; - } - } - - return interval; - } -} - -export class GenerateStringV1 extends AbstractGenerator<{ - isUnique?: boolean; - arraySize?: number; -}> { - static override readonly [entityKind]: string = 'GenerateString'; - - private state: { rng: prand.RandomGenerator } | undefined; - override uniqueVersionOfGen = GenerateUniqueStringV1; - - override init({ count, seed }: { count: number; seed: number }) { - super.init({ count, seed }); - - const rng = prand.xoroshiro128plus(seed); - this.state = { rng }; - } - - generate() { - if (this.state === undefined) { - throw new Error('state is not defined.'); - } - - const minStringLength = 7; - const maxStringLength = 20; - const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - let idx: number, - strLength: number, - currStr: string; - - currStr = ''; - [strLength, this.state.rng] = prand.uniformIntDistribution( - minStringLength, - maxStringLength, - this.state.rng, - ); - for (let j = 0; j < strLength; j++) { - [idx, this.state.rng] = prand.uniformIntDistribution( - 0, - stringChars.length - 1, - this.state.rng, - ); - currStr += stringChars[idx]; - } - return currStr; - } -} - -export class GenerateUniqueStringV1 extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueString'; - - private state: { rng: prand.RandomGenerator } | undefined; - public override isUnique = true; - - override init({ seed }: { seed: number }) { - const rng = prand.xoroshiro128plus(seed); - this.state = { rng }; - } - - generate({ i }: { i: number }) { - if (this.state === undefined) { - throw new Error('state is not defined.'); - } - - const minStringLength = 7; - const maxStringLength = 20; - const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - let idx: number, - strLength: number; - let currStr: string; - - currStr = ''; - const uniqueStr = i.toString(16); - [strLength, this.state.rng] = prand.uniformIntDistribution( - minStringLength, - maxStringLength - uniqueStr.length, - this.state.rng, - ); - for (let j = 0; j < strLength - uniqueStr.length; j++) { - [idx, this.state.rng] = prand.uniformIntDistribution( - 0, - stringChars.length - 1, - this.state.rng, - ); - currStr += stringChars[idx]; - } - - return currStr.slice(0, 4) + uniqueStr + currStr.slice(4); - } -} diff --git a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts new file mode 100644 index 000000000..daedcdc98 --- /dev/null +++ b/drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts @@ -0,0 +1,240 @@ +import { entityKind } from 'drizzle-orm'; +import prand from 'pure-rand'; +import { AbstractGenerator, GenerateInterval } from '../Generators.ts'; + +export const version = 2; + +export class GenerateIntervalV2 extends GenerateInterval { + static override readonly [entityKind]: string = 'GenerateInterval'; + static override readonly ['version']: number = 2; + override uniqueVersionOfGen = GenerateUniqueIntervalV2; +} + +export class GenerateUniqueIntervalV2 extends AbstractGenerator<{ + fields?: + | 'year' + | 'month' + | 'day' + | 'hour' + | 'minute' + | 'second' + | 'year to month' + | 'day to hour' + | 'day to minute' + | 'day to second' + | 'hour to minute' + | 'hour to second' + | 'minute to second'; + isUnique?: boolean; +}> { + static override readonly [entityKind]: string = 'GenerateUniqueInterval'; + static override readonly ['version']: number = 2; + + private state: { + rng: prand.RandomGenerator; + fieldsToGenerate: string[]; + intervalSet: Set; + } | undefined; + public override isUnique = true; + private config: { [key: string]: { from: number; to: number } } = { + year: { + from: 0, + to: 5, + }, + month: { + from: 0, + to: 11, + }, + day: { + from: 0, + to: 29, + }, + hour: { + from: 0, + to: 23, + }, + minute: { + from: 0, + to: 59, + }, + second: { + from: 0, + to: 59, + }, + }; + + override init({ count, seed }: { count: number; seed: number }) { + const allFields = ['year', 'month', 'day', 'hour', 'minute', 'second']; + let fieldsToGenerate: string[] = allFields; + + if (this.params.fields !== undefined && this.params.fields?.includes(' to ')) { + const tokens = this.params.fields.split(' to '); + const endIdx = allFields.indexOf(tokens[1]!); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } else if (this.params.fields !== undefined) { + const endIdx = allFields.indexOf(this.params.fields); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } + + let maxUniqueIntervalsNumber = 1; + for (const field of fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + maxUniqueIntervalsNumber *= from - to + 1; + } + + if (count > maxUniqueIntervalsNumber) { + throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); + } + + const rng = prand.xoroshiro128plus(seed); + const intervalSet = new Set(); + this.state = { rng, fieldsToGenerate, intervalSet }; + } + + generate() { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + let interval, numb: number; + + for (;;) { + interval = ''; + + for (const field of this.state.fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); + interval += `${numb} ${field} `; + } + + if (!this.state.intervalSet.has(interval)) { + this.state.intervalSet.add(interval); + break; + } + } + + return interval; + } +} + +export class GenerateStringV2 extends AbstractGenerator<{ + isUnique?: boolean; + arraySize?: number; +}> { + static override readonly [entityKind]: string = 'GenerateString'; + static override readonly ['version']: number = 2; + + private state: { + rng: prand.RandomGenerator; + minStringLength: number; + maxStringLength: number; + } | undefined; + override uniqueVersionOfGen = GenerateUniqueStringV2; + + override init({ count, seed }: { count: number; seed: number }) { + super.init({ count, seed }); + + let minStringLength = 8; + let maxStringLength = 20; + if (this.stringLength !== undefined) { + maxStringLength = this.stringLength; + if (maxStringLength === 1) minStringLength = maxStringLength; + if (maxStringLength < minStringLength) minStringLength = 1; + } + + const rng = prand.xoroshiro128plus(seed); + this.state = { rng, minStringLength, maxStringLength }; + } + + generate() { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + const minStringLength = this.state.minStringLength, + maxStringLength = this.state.maxStringLength; + const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let idx: number, + strLength: number, + currStr: string; + + currStr = ''; + [strLength, this.state.rng] = prand.uniformIntDistribution( + minStringLength, + maxStringLength, + this.state.rng, + ); + for (let j = 0; j < strLength; j++) { + [idx, this.state.rng] = prand.uniformIntDistribution( + 0, + stringChars.length - 1, + this.state.rng, + ); + currStr += stringChars[idx]; + } + return currStr; + } +} + +export class GenerateUniqueStringV2 extends AbstractGenerator<{ isUnique?: boolean }> { + static override readonly [entityKind]: string = 'GenerateUniqueString'; + static override readonly ['version']: number = 2; + + private state: { + rng: prand.RandomGenerator; + minStringLength: number; + maxStringLength: number; + } | undefined; + public override isUnique = true; + + override init({ seed, count }: { seed: number; count: number }) { + const rng = prand.xoroshiro128plus(seed); + + let minStringLength = 8; + let maxStringLength = 20; + // TODO: revise later + if (this.stringLength !== undefined) { + maxStringLength = this.stringLength; + if (maxStringLength === 1 || maxStringLength < minStringLength) minStringLength = maxStringLength; + } + + if (maxStringLength < count.toString(16).length) { + throw new Error( + `You can't generate ${count} unique strings, with a maximum string length of ${maxStringLength}.`, + ); + } + + this.state = { rng, minStringLength, maxStringLength }; + } + + generate({ i }: { i: number }) { + if (this.state === undefined) { + throw new Error('state is not defined.'); + } + + const minStringLength = this.state.minStringLength, + maxStringLength = this.state.maxStringLength; + const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let idx: number, + strLength: number; + let currStr: string; + + currStr = ''; + const uniqueStr = i.toString(16); + [strLength, this.state.rng] = prand.uniformIntDistribution( + minStringLength, + maxStringLength - uniqueStr.length, + this.state.rng, + ); + for (let j = 0; j < strLength - uniqueStr.length; j++) { + [idx, this.state.rng] = prand.uniformIntDistribution( + 0, + stringChars.length - 1, + this.state.rng, + ); + currStr += stringChars[idx]; + } + + return uniqueStr + currStr; + } +} diff --git a/drizzle-seed/src/services/Generators.ts b/drizzle-seed/src/services/Generators.ts index fa01a1e4f..eb5e256e4 100644 --- a/drizzle-seed/src/services/Generators.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -14,7 +14,8 @@ import states, { maxStringLength as maxStateLength } from '../datasets/states.ts import streetSuffix, { maxStringLength as maxStreetSuffixLength } from '../datasets/streetSuffix.ts'; import { fastCartesianProduct, fillTemplate, getWeightedIndices, isObject } from './utils.ts'; -export const version = 2; +export const latestVersion = 2; +export const version = 1; export abstract class AbstractGenerator { static readonly [entityKind]: string = 'AbstractGenerator'; @@ -1299,84 +1300,27 @@ export class GenerateInterval extends AbstractGenerator<{ } } +// has a newer version export class GenerateUniqueInterval extends AbstractGenerator<{ - fields?: - | 'year' - | 'month' - | 'day' - | 'hour' - | 'minute' - | 'second' - | 'year to month' - | 'day to hour' - | 'day to minute' - | 'day to second' - | 'hour to minute' - | 'hour to second' - | 'minute to second'; isUnique?: boolean; }> { static override readonly [entityKind]: string = 'GenerateUniqueInterval'; private state: { rng: prand.RandomGenerator; - fieldsToGenerate: string[]; intervalSet: Set; } | undefined; public override isUnique = true; - private config: { [key: string]: { from: number; to: number } } = { - year: { - from: 0, - to: 5, - }, - month: { - from: 0, - to: 11, - }, - day: { - from: 1, - to: 29, - }, - hour: { - from: 0, - to: 23, - }, - minute: { - from: 0, - to: 59, - }, - second: { - from: 0, - to: 59, - }, - }; override init({ count, seed }: { count: number; seed: number }) { - const allFields = ['year', 'month', 'day', 'hour', 'minute', 'second']; - let fieldsToGenerate: string[] = allFields; - - if (this.params.fields !== undefined && this.params.fields?.includes(' to ')) { - const tokens = this.params.fields.split(' to '); - const endIdx = allFields.indexOf(tokens[1]!); - fieldsToGenerate = allFields.slice(0, endIdx + 1); - } else if (this.params.fields !== undefined) { - const endIdx = allFields.indexOf(this.params.fields); - fieldsToGenerate = allFields.slice(0, endIdx + 1); - } - - let maxUniqueIntervalsNumber = 1; - for (const field of fieldsToGenerate) { - const from = this.config[field]!.from, to = this.config[field]!.to; - maxUniqueIntervalsNumber *= from - to + 1; - } - + const maxUniqueIntervalsNumber = 6 * 13 * 29 * 25 * 61 * 61; if (count > maxUniqueIntervalsNumber) { throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); } const rng = prand.xoroshiro128plus(seed); const intervalSet = new Set(); - this.state = { rng, fieldsToGenerate, intervalSet }; + this.state = { rng, intervalSet }; } generate() { @@ -1384,16 +1328,29 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ throw new Error('state is not defined.'); } - let interval, numb: number; + let yearsNumb: number, + monthsNumb: number, + daysNumb: number, + hoursNumb: number, + minutesNumb: number, + secondsNumb: number; - for (;;) { - interval = ''; + let interval = ''; - for (const field of this.state.fieldsToGenerate) { - const from = this.config[field]!.from, to = this.config[field]!.to; - [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); - interval += `${numb} ${field} `; - } + for (;;) { + [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); + [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); + [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); + [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); + [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); + [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); + + interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` + + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` + + `${daysNumb === 0 ? '' : `${daysNumb} days `}` + + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` + + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` + + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; if (!this.state.intervalSet.has(interval)) { this.state.intervalSet.add(interval); @@ -1405,32 +1362,21 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ } } +// has a newer version export class GenerateString extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { static override readonly [entityKind]: string = 'GenerateString'; - private state: { - rng: prand.RandomGenerator; - minStringLength: number; - maxStringLength: number; - } | undefined; + private state: { rng: prand.RandomGenerator } | undefined; override uniqueVersionOfGen = GenerateUniqueString; override init({ count, seed }: { count: number; seed: number }) { super.init({ count, seed }); - let minStringLength = 8; - let maxStringLength = 20; - if (this.stringLength !== undefined) { - maxStringLength = this.stringLength; - if (maxStringLength === 1) minStringLength = maxStringLength; - if (maxStringLength < minStringLength) minStringLength = 1; - } - const rng = prand.xoroshiro128plus(seed); - this.state = { rng, minStringLength, maxStringLength }; + this.state = { rng }; } generate() { @@ -1438,8 +1384,8 @@ export class GenerateString extends AbstractGenerator<{ throw new Error('state is not defined.'); } - const minStringLength = this.state.minStringLength, - maxStringLength = this.state.maxStringLength; + const minStringLength = 7; + const maxStringLength = 20; const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let idx: number, strLength: number, @@ -1463,34 +1409,16 @@ export class GenerateString extends AbstractGenerator<{ } } +// has a newer version export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean }> { static override readonly [entityKind]: string = 'GenerateUniqueString'; - private state: { - rng: prand.RandomGenerator; - minStringLength: number; - maxStringLength: number; - } | undefined; + private state: { rng: prand.RandomGenerator } | undefined; public override isUnique = true; - override init({ seed, count }: { seed: number; count: number }) { + override init({ seed }: { seed: number }) { const rng = prand.xoroshiro128plus(seed); - - let minStringLength = 8; - let maxStringLength = 20; - // TODO: revise later - if (this.stringLength !== undefined) { - maxStringLength = this.stringLength; - if (maxStringLength === 1 || maxStringLength < minStringLength) minStringLength = maxStringLength; - } - - if (maxStringLength < count.toString(16).length) { - throw new Error( - `You can't generate ${count} unique strings, with a maximum string length of ${maxStringLength}.`, - ); - } - - this.state = { rng, minStringLength, maxStringLength }; + this.state = { rng }; } generate({ i }: { i: number }) { @@ -1498,8 +1426,8 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean throw new Error('state is not defined.'); } - const minStringLength = this.state.minStringLength, - maxStringLength = this.state.maxStringLength; + const minStringLength = 7; + const maxStringLength = 20; const stringChars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let idx: number, strLength: number; @@ -1521,7 +1449,7 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean currStr += stringChars[idx]; } - return uniqueStr + currStr; + return currStr.slice(0, 4) + uniqueStr + currStr.slice(4); } } diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 6ceae5167..b55736e4d 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -15,7 +15,7 @@ import type { Column, Prettify, Relation, Table } from '../types/tables.ts'; import type { AbstractGenerator } from './Generators.ts'; import * as Generators from './Generators.ts'; -import * as GeneratorsV1 from './GeneratorVersions/GeneratorsV1.ts'; +import * as GeneratorsV2 from './GeneratorVersions/GeneratorsV2.ts'; import { equalSets, generateHashFromString } from './utils.ts'; export class SeedService { @@ -39,10 +39,12 @@ export class SeedService { let columnPossibleGenerator: Prettify; let tablePossibleGenerators: Prettify; const customSeed = options?.seed === undefined ? 0 : options.seed; - const version = options?.version === undefined ? Generators.version : options.version; + const version = options?.version === undefined ? Generators.latestVersion : options.version; + if (version < Generators.version || version > Generators.latestVersion) { + throw new Error(`Version should be in range [${Generators.version}, ${Generators.latestVersion}].`); + } // sorting table in order which they will be filled up (tables with foreign keys case) - // relations = relations.filter(rel => rel.type === "one"); const { tablesInOutRelations } = this.getInfoFromRelations(relations); const orderedTablesNames = this.getOrderedTablesList(tablesInOutRelations); tables = tables.sort((table1, table2) => { @@ -416,22 +418,20 @@ export class SeedService { }; selectGeneratorOfVersion = (version: number, generatorEntityKind: string) => { - const GeneratorVersions = [Generators, GeneratorsV1]; + const GeneratorVersions = [GeneratorsV2, Generators]; type GeneratorConstructorT = new(params: any) => AbstractGenerator; let generatorConstructor: GeneratorConstructorT | undefined; for (const gens of GeneratorVersions) { const { version: gensVersion, ...filteredGens } = gens; - if (version > gensVersion) break; + if (gensVersion > version) continue; for (const gen of Object.values(filteredGens)) { const abstractGen = gen as typeof AbstractGenerator; if (abstractGen[entityKind] === generatorEntityKind) { generatorConstructor = abstractGen as unknown as GeneratorConstructorT; - if (abstractGen.version === version) { - return generatorConstructor; - } + return generatorConstructor; } } } From db33c875c09f955403e11a2568654053ca6fe74d Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Thu, 19 Dec 2024 15:09:56 +0200 Subject: [PATCH 09/18] updated changelogs --- changelogs/drizzle-seed/0.1.4.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/changelogs/drizzle-seed/0.1.4.md b/changelogs/drizzle-seed/0.1.4.md index 84c3821af..9d3c0a5e2 100644 --- a/changelogs/drizzle-seed/0.1.4.md +++ b/changelogs/drizzle-seed/0.1.4.md @@ -8,24 +8,24 @@ await seed(db, schema, { version: 1 }); Generator versions will change within a single major version of drizzle-seed. -The old version of the generator will become available if the generator has been changed and the output of the new generator does not match the output of the old generator. +The new version of the generator will become available if the generator has been changed and the output of the new generator does not match the output of the old generator. #### For example: -`FirstNameGen` was changed, and old version, `V1`, of this generator became available. +`LastNameGen` was changed, and new version, `V2`, of this generator became available. -Later, `LastNameGen` was changed in the next minor version of drizzle-seed, making `V2` version of this generator available. +Later, `FirstNameGen` was changed in the next minor version of drizzle-seed, making `V3` version of this generator available. | | `V1` | `V2` | `V3(latest)` | | :--------------: | :--------------: | :-------------: | :--------------: | -| **FirstNameGen** | `FirstNameGenV1` | `same as V3` | `FirstNameGenV3` | -| **LastNameGen** | `same as V2` | `LastNameGenV2` | `LastNameGenV3` | +| **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | | +| **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` | -If you omit version, `V3` version of generators will be used. +If you omit version, latest version of generators(`V3`) will be used: `FirstNameGen` will use its `V3` version, and `LastNameGen` will use its `V2` version. -If you specify version of 2, `FirstNameGen` will use its `V3` version and `LastNameGen` will use its `V2` version. +If you specify version of 2, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V2` version. -If you specify version of 1, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V2` version. +If you specify version of 1, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V1` version. ## @@ -87,7 +87,7 @@ main(); ## Breaking changes -- Unique `interval` generator was changed, so `1` version of this generator become available. **The latest version is `2`.** +- Unique `interval` generator was changed, so `2` version of this generator become available. **The latest version is `2`.** **Cause:** @@ -98,7 +98,7 @@ However, after inserting the `1 minute 60 second` interval, PostgreSQL database ## -- Both non-unique and unique `string` generators were changed, making version `1` of these generators available. **The latest version is `2`.** +- Both non-unique and unique `string` generators were changed, making version `2` of these generators available. **The latest version is `2`.** **Cause:** From 88bfe72fbf3db73bdc57cd0e4531a8063ceaff7d Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Fri, 20 Dec 2024 11:17:38 +0200 Subject: [PATCH 10/18] Review --- .../drizzle-seed/{0.1.4.md => 0.2.0.md} | 43 ++++++++++----- drizzle-seed/src/services/GeneratorFuncs.ts | 53 ++++++++++++++++++- drizzle-seed/src/services/Generators.ts | 5 +- drizzle-seed/src/services/SeedService.ts | 22 ++++---- drizzle-seed/src/services/apiVersion.ts | 1 + .../GeneratorsV2.ts => versioning/v2.ts} | 10 ++-- 6 files changed, 99 insertions(+), 35 deletions(-) rename changelogs/drizzle-seed/{0.1.4.md => 0.2.0.md} (63%) create mode 100644 drizzle-seed/src/services/apiVersion.ts rename drizzle-seed/src/services/{GeneratorVersions/GeneratorsV2.ts => versioning/v2.ts} (96%) diff --git a/changelogs/drizzle-seed/0.1.4.md b/changelogs/drizzle-seed/0.2.0.md similarity index 63% rename from changelogs/drizzle-seed/0.1.4.md rename to changelogs/drizzle-seed/0.2.0.md index 9d3c0a5e2..be19a8949 100644 --- a/changelogs/drizzle-seed/0.1.4.md +++ b/changelogs/drizzle-seed/0.2.0.md @@ -1,33 +1,50 @@ -## Improvements +## API updates -- A new parameter, `version`, was added to the `seed` function options, which will control generator versioning. +We are introducing a new parameter, `version`, to the `seed` function options. This parameter, which controls generator versioning, has been added to make it easier to update deterministic generators in the future. Since values should remain consistent after each regeneration, it is crucial to provide a well-designed API for gradual updates ```ts -await seed(db, schema, { version: 1 }); +await seed(db, schema, { version: 2 }); ``` -Generator versions will change within a single major version of drizzle-seed. +#### Example: -The new version of the generator will become available if the generator has been changed and the output of the new generator does not match the output of the old generator. +> This is not an actual API change; it is just an example of how we will proceed with `drizzle-seed` versioning -#### For example: +For example, `lastName` generator was changed, and new version, `V2`, of this generator became available. -`LastNameGen` was changed, and new version, `V2`, of this generator became available. - -Later, `FirstNameGen` was changed in the next minor version of drizzle-seed, making `V3` version of this generator available. +Later, `firstName` generator was changed, making `V3` version of this generator available. | | `V1` | `V2` | `V3(latest)` | | :--------------: | :--------------: | :-------------: | :--------------: | | **LastNameGen** | `LastNameGenV1` | `LastNameGenV2` | | | **FirstNameGen** | `FirstNameGenV1` | | `FirstNameGenV3` | -If you omit version, latest version of generators(`V3`) will be used: `FirstNameGen` will use its `V3` version, and `LastNameGen` will use its `V2` version. -If you specify version of 2, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V2` version. +##### Use the `firstName` generator of version 3 and the `lastName` generator of version 2 +```ts +await seed(db, schema); +``` -If you specify version of 1, `FirstNameGen` will use its `V1` version and `LastNameGen` will use its `V1` version. +If you are not ready to use latest generator version right away, you can specify max version to use -## +##### Use the `firstName` generator of version 1 and the `lastName` generator of version 2 +```ts +await seed(db, schema, { version: '2' }); +``` + +##### Use the `firstName` generator of version 1 and the `lastName` generator of version 1. +```ts +await seed(db, schema, { version: '1' }); +``` + +Each update with breaking changes for generators will be documented on our docs and in release notes, explaining which version you should use, if you are not ready to upgrade the way generators works + +## Breaking changes + +### `interval` unique generator was changed and upgraded to v2 +### `string` generators were changed and upgraded to v2 + +## New Features - added `fields` as new parameter in `interval` generator diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts index 65e31fc09..2810134f0 100644 --- a/drizzle-seed/src/services/GeneratorFuncs.ts +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -31,7 +31,7 @@ import { GenerateYear, WeightedRandomGenerator, } from './Generators.ts'; -import { GenerateStringV2 } from './GeneratorVersions/GeneratorsV2.ts'; +import { GenerateStringV2 } from './versioning/v2.ts'; function createGenerator, T>( generatorConstructor: new(params: T) => GeneratorType, @@ -735,3 +735,54 @@ export const generatorsFuncs = { */ weightedRandom: createGenerator(WeightedRandomGenerator), }; + +export const generatorsList: (new(params: any) => AbstractGenerator)[] = [ + GenerateBoolean, + GenerateCity, + GenerateCompanyName, + GenerateCountry, + GenerateDate, + GenerateDatetime, + GenerateDefault, + GenerateEmail, + GenerateFirstName, + GenerateFullName, + GenerateInt, + GenerateInterval, + GenerateIntPrimaryKey, + GenerateJobTitle, + GenerateJson, + GenerateLastName, + GenerateLine, + GenerateLoremIpsum, + GenerateNumber, + GeneratePhoneNumber, + GeneratePoint, + GeneratePostcode, + GenerateState, + GenerateStreetAdddress, + GenerateTime, + GenerateTimestamp, + GenerateUUID, + GenerateValuesFromArray, + GenerateYear, + WeightedRandomGenerator, +]; + +// +const generatorsObj = { + [entityKind]: [ + GenerateStreetAdddress, + GenerateUUID, + ], + [entityKind]: [ + GenerateVarchar, + GenerateVarcharV2, + GenerateVarcharV3, + ], + [entityKind]: [ + GenerateVarchar, + GenerateVarcharV2, + GenerateVarcharV3, + ], +}; diff --git a/drizzle-seed/src/services/Generators.ts b/drizzle-seed/src/services/Generators.ts index eb5e256e4..629aff5a3 100644 --- a/drizzle-seed/src/services/Generators.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -14,12 +14,9 @@ import states, { maxStringLength as maxStateLength } from '../datasets/states.ts import streetSuffix, { maxStringLength as maxStreetSuffixLength } from '../datasets/streetSuffix.ts'; import { fastCartesianProduct, fillTemplate, getWeightedIndices, isObject } from './utils.ts'; -export const latestVersion = 2; -export const version = 1; - export abstract class AbstractGenerator { static readonly [entityKind]: string = 'AbstractGenerator'; - static readonly ['version']: number = 2; + static readonly version: number = 1; public isUnique = false; public notNull = false; diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index b55736e4d..4ba30ed11 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -14,9 +14,10 @@ import type { import type { Column, Prettify, Relation, Table } from '../types/tables.ts'; import type { AbstractGenerator } from './Generators.ts'; -import * as Generators from './Generators.ts'; -import * as GeneratorsV2 from './GeneratorVersions/GeneratorsV2.ts'; +import { latestVersion } from './apiVersion.ts'; +// import * as Generators from './Generators.ts'; import { equalSets, generateHashFromString } from './utils.ts'; +import * as GeneratorsV2 from './versioning/v2.ts'; export class SeedService { static readonly [entityKind]: string = 'SeedService'; @@ -39,9 +40,9 @@ export class SeedService { let columnPossibleGenerator: Prettify; let tablePossibleGenerators: Prettify; const customSeed = options?.seed === undefined ? 0 : options.seed; - const version = options?.version === undefined ? Generators.latestVersion : options.version; - if (version < Generators.version || version > Generators.latestVersion) { - throw new Error(`Version should be in range [${Generators.version}, ${Generators.latestVersion}].`); + const version = options?.version === undefined ? latestVersion : options.version; + if (version < 1 || version > latestVersion) { + throw new Error(`Version should be in range [1, ${latestVersion}].`); } // sorting table in order which they will be filled up (tables with foreign keys case) @@ -248,6 +249,8 @@ export class SeedService { ); } + // check if new version exists + columnPossibleGenerator.generator.isUnique = col.isUnique; columnPossibleGenerator.generator.dataType = col.dataType; columnPossibleGenerator.generator.stringLength = col.typeParams.length; @@ -418,15 +421,12 @@ export class SeedService { }; selectGeneratorOfVersion = (version: number, generatorEntityKind: string) => { - const GeneratorVersions = [GeneratorsV2, Generators]; - type GeneratorConstructorT = new(params: any) => AbstractGenerator; + const GeneratorVersions = [...Object.values(GeneratorsV2), ...Object.values(Generators)]; + let generatorConstructor: GeneratorConstructorT | undefined; for (const gens of GeneratorVersions) { - const { version: gensVersion, ...filteredGens } = gens; - if (gensVersion > version) continue; - - for (const gen of Object.values(filteredGens)) { + for (const gen of Object.values(gens)) { const abstractGen = gen as typeof AbstractGenerator; if (abstractGen[entityKind] === generatorEntityKind) { generatorConstructor = abstractGen as unknown as GeneratorConstructorT; diff --git a/drizzle-seed/src/services/apiVersion.ts b/drizzle-seed/src/services/apiVersion.ts new file mode 100644 index 000000000..6cda0267e --- /dev/null +++ b/drizzle-seed/src/services/apiVersion.ts @@ -0,0 +1 @@ +export const latestVersion = 2; diff --git a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts b/drizzle-seed/src/services/versioning/v2.ts similarity index 96% rename from drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts rename to drizzle-seed/src/services/versioning/v2.ts index daedcdc98..96451b2f8 100644 --- a/drizzle-seed/src/services/GeneratorVersions/GeneratorsV2.ts +++ b/drizzle-seed/src/services/versioning/v2.ts @@ -2,11 +2,9 @@ import { entityKind } from 'drizzle-orm'; import prand from 'pure-rand'; import { AbstractGenerator, GenerateInterval } from '../Generators.ts'; -export const version = 2; - export class GenerateIntervalV2 extends GenerateInterval { static override readonly [entityKind]: string = 'GenerateInterval'; - static override readonly ['version']: number = 2; + override readonly version: number = 2; override uniqueVersionOfGen = GenerateUniqueIntervalV2; } @@ -28,7 +26,7 @@ export class GenerateUniqueIntervalV2 extends AbstractGenerator<{ isUnique?: boolean; }> { static override readonly [entityKind]: string = 'GenerateUniqueInterval'; - static override readonly ['version']: number = 2; + override readonly version: number = 2; private state: { rng: prand.RandomGenerator; @@ -122,7 +120,7 @@ export class GenerateStringV2 extends AbstractGenerator<{ arraySize?: number; }> { static override readonly [entityKind]: string = 'GenerateString'; - static override readonly ['version']: number = 2; + override readonly version: number = 2; private state: { rng: prand.RandomGenerator; @@ -178,7 +176,7 @@ export class GenerateStringV2 extends AbstractGenerator<{ export class GenerateUniqueStringV2 extends AbstractGenerator<{ isUnique?: boolean }> { static override readonly [entityKind]: string = 'GenerateUniqueString'; - static override readonly ['version']: number = 2; + override readonly version: number = 2; private state: { rng: prand.RandomGenerator; From e11941e086a79b9e1441e6eb5d5bbedc02300c52 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Fri, 20 Dec 2024 11:44:53 +0200 Subject: [PATCH 11/18] changes in generator selection --- drizzle-seed/src/index.ts | 11 +- drizzle-seed/src/services/SeedService.ts | 126 ++++++++++++++--------- 2 files changed, 86 insertions(+), 51 deletions(-) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index 248e7ead5..50e40def2 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -570,7 +570,10 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { // might be empty list const newRelations = tableConfig.foreignKeys.map((fk) => { const table = dbToTsTableNamesMap[tableConfig.name] as string; - const refTable = dbToTsTableNamesMap[getTableName(fk.reference().foreignTable)] as string; + const refTableName0 = fk.reference(); + const refTableName1 = refTableName0.foreignTable; + const refTableName2 = getTableName(refTableName1); + const refTable = dbToTsTableNamesMap[refTableName2] as string; const dbToTsColumnNamesMapForRefTable = getDbToTsColumnNamesMap( fk.reference().foreignTable, @@ -625,7 +628,7 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { }; const getTypeParams = (sqlType: string) => { - // get type params and set only type + // get type params const typeParams: Column['typeParams'] = {}; // handle dimensions @@ -642,7 +645,7 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { || sqlType.startsWith('double precision') || sqlType.startsWith('real') ) { - const match = sqlType.match(/\((\d+),(\d+)\)/); + const match = sqlType.match(/\((\d+), *(\d+)\)/); if (match) { typeParams['precision'] = Number(match[1]); typeParams['scale'] = Number(match[2]); @@ -905,7 +908,7 @@ const getMySqlInfo = (schema: { [key: string]: MySqlTable }) => { || sqlType.startsWith('double') || sqlType.startsWith('float') ) { - const match = sqlType.match(/\((\d+),(\d+)\)/); + const match = sqlType.match(/\((\d+), *(\d+)\)/); if (match) { typeParams['precision'] = Number(match[1]); typeParams['scale'] = Number(match[2]); diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index b55736e4d..329cc6289 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -461,12 +461,6 @@ export class SeedService { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - // const { generatorConstructor } = this.selectGeneratorOfVersion( - // version, - // Generators.GenerateArray[entityKind], - // ); - - // const generator = new generatorConstructor!({ baseColumnGen, size: col.size }); const generator = new Generators.GenerateArray({ baseColumnGen, size: col.size }); return generator; @@ -569,11 +563,23 @@ export class SeedService { // NUMBER(real, double, decimal, numeric) if ( - col.columnType === 'real' - || col.columnType === 'double precision' - || col.columnType === 'decimal' - || col.columnType === 'numeric' + col.columnType.startsWith('real') + || col.columnType.startsWith('double precision') + || col.columnType.startsWith('decimal') + || col.columnType.startsWith('numeric') ) { + if (col.typeParams.precision !== undefined) { + const precision = col.typeParams.precision; + const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; + + const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); + const generator = new Generators.GenerateNumber({ + minValue: -maxAbsoluteValue, + maxValue: maxAbsoluteValue, + precision: Math.pow(10, scale), + }); + return generator; + } const generator = new Generators.GenerateNumber({}); return generator; @@ -792,11 +798,25 @@ export class SeedService { // NUMBER(real, double, decimal, float) if ( - col.columnType === 'real' - || col.columnType === 'double' - || col.columnType === 'decimal' - || col.columnType === 'float' + col.columnType.startsWith('real') + || col.columnType.startsWith('double') + || col.columnType.startsWith('decimal') + || col.columnType.startsWith('float') + || col.columnType.startsWith('numeric') ) { + if (col.typeParams.precision !== undefined) { + const precision = col.typeParams.precision; + const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; + + const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); + const generator = new Generators.GenerateNumber({ + minValue: -maxAbsoluteValue, + maxValue: maxAbsoluteValue, + precision: Math.pow(10, scale), + }); + return generator; + } + const generator = new Generators.GenerateNumber({}); return generator; } @@ -805,10 +825,10 @@ export class SeedService { if ( (col.columnType === 'text' || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) + || col.columnType.startsWith('char') + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('binary') + || col.columnType.startsWith('varbinary')) && table.primaryKeys.includes(col.name) ) { const generator = new Generators.GenerateUniqueString({}); @@ -818,10 +838,10 @@ export class SeedService { if ( (col.columnType === 'text' || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) + || col.columnType.startsWith('char') + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('binary') + || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('name') ) { const generator = new Generators.GenerateFirstName({}); @@ -831,10 +851,10 @@ export class SeedService { if ( (col.columnType === 'text' || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary')) + || col.columnType.startsWith('char') + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('binary') + || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('email') ) { const generator = new Generators.GenerateEmail({}); @@ -844,10 +864,10 @@ export class SeedService { if ( col.columnType === 'text' || col.columnType === 'blob' - || col.columnType.includes('char') - || col.columnType.includes('varchar') - || col.columnType.includes('binary') - || col.columnType.includes('varbinary') + || col.columnType.startsWith('char') + || col.columnType.startsWith('varchar') + || col.columnType.startsWith('binary') + || col.columnType.startsWith('varbinary') ) { const generator = new Generators.GenerateString({}); return generator; @@ -949,7 +969,6 @@ export class SeedService { if ( col.columnType === 'integer' - || col.columnType === 'numeric' || (col.dataType === 'bigint' && col.columnType === 'blob') ) { const generator = new Generators.GenerateInt({}); @@ -957,16 +976,29 @@ export class SeedService { } // number section ------------------------------------------------------------------------------------ - if (col.columnType === 'real' || col.columnType === 'numeric') { + if (col.columnType.startsWith('real') || col.columnType.startsWith('numeric')) { + if (col.typeParams.precision !== undefined) { + const precision = col.typeParams.precision; + const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; + + const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); + const generator = new Generators.GenerateNumber({ + minValue: -maxAbsoluteValue, + maxValue: maxAbsoluteValue, + precision: Math.pow(10, scale), + }); + return generator; + } + const generator = new Generators.GenerateNumber({}); return generator; } // string section ------------------------------------------------------------------------------------ if ( - (col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob') + (col.columnType.startsWith('text') + || col.columnType.startsWith('numeric') + || col.columnType.startsWith('blob')) && table.primaryKeys.includes(col.name) ) { const generator = new Generators.GenerateUniqueString({}); @@ -974,9 +1006,9 @@ export class SeedService { } if ( - (col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob') + (col.columnType.startsWith('text') + || col.columnType.startsWith('numeric') + || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('name') ) { const generator = new Generators.GenerateFirstName({}); @@ -984,9 +1016,9 @@ export class SeedService { } if ( - (col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob') + (col.columnType.startsWith('text') + || col.columnType.startsWith('numeric') + || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('email') ) { const generator = new Generators.GenerateEmail({}); @@ -994,18 +1026,18 @@ export class SeedService { } if ( - col.columnType === 'text' - || col.columnType === 'numeric' - || col.columnType === 'blob' - || col.columnType === 'blobbuffer' + col.columnType.startsWith('text') + || col.columnType.startsWith('numeric') + || col.columnType.startsWith('blob') + || col.columnType.startsWith('blobbuffer') ) { const generator = new Generators.GenerateString({}); return generator; } if ( - (col.columnType === 'text' && col.dataType === 'json') - || (col.columnType === 'blob' && col.dataType === 'json') + (col.columnType.startsWith('text') && col.dataType === 'json') + || (col.columnType.startsWith('blob') && col.dataType === 'json') ) { const generator = new Generators.GenerateJson({}); return generator; From ec5d35eb459e0330894074889e0fdcd4da091158 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Fri, 20 Dec 2024 20:59:10 +0200 Subject: [PATCH 12/18] updated generator versioning --- drizzle-seed/src/index.ts | 5 +- drizzle-seed/src/services/GeneratorFuncs.ts | 227 +++++++++++--- drizzle-seed/src/services/Generators.ts | 238 ++++++++------ drizzle-seed/src/services/SeedService.ts | 295 +++++++++--------- drizzle-seed/src/services/versioning/v2.ts | 22 +- .../tests/benchmarks/generatorsBenchmark.ts | 8 +- .../tests/pg/allDataTypesTest/pgSchema.ts | 46 +-- 7 files changed, 514 insertions(+), 327 deletions(-) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index 50e40def2..48aad2973 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -1,4 +1,5 @@ -import { entityKind, getTableName, is, sql } from 'drizzle-orm'; +/* eslint-disable drizzle-internal/require-entity-kind */ +import { getTableName, is, sql } from 'drizzle-orm'; import type { MySqlColumn, MySqlSchema } from 'drizzle-orm/mysql-core'; import { getTableConfig as getMysqlTableConfig, MySqlDatabase, MySqlTable } from 'drizzle-orm/mysql-core'; @@ -131,7 +132,7 @@ class SeedPromise< [key: string]: PgTable | PgSchema | MySqlTable | MySqlSchema | SQLiteTable; }, > implements Promise { - static readonly [entityKind]: string = 'SeedPromise'; + static readonly entityKind: string = 'SeedPromise'; [Symbol.toStringTag] = 'SeedPromise'; diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts index 2810134f0..be715a33b 100644 --- a/drizzle-seed/src/services/GeneratorFuncs.ts +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -1,5 +1,6 @@ import type { AbstractGenerator } from './Generators.ts'; import { + GenerateArray, GenerateBoolean, GenerateCity, GenerateCompanyName, @@ -8,6 +9,7 @@ import { GenerateDatetime, GenerateDefault, GenerateEmail, + GenerateEnum, GenerateFirstName, GenerateFullName, GenerateInt, @@ -22,16 +24,34 @@ import { GeneratePhoneNumber, GeneratePoint, GeneratePostcode, + GenerateSelfRelationsValuesFromArray, GenerateState, - GenerateStreetAdddress, + GenerateStreetAddress, + GenerateString, GenerateTime, GenerateTimestamp, + GenerateUniqueCity, + GenerateUniqueCompanyName, + GenerateUniqueCountry, + GenerateUniqueFirstName, + GenerateUniqueFullName, + GenerateUniqueInt, + GenerateUniqueInterval, + GenerateUniqueLastName, + GenerateUniqueLine, + GenerateUniqueNumber, + GenerateUniquePoint, + GenerateUniquePostcode, + GenerateUniqueStreetAddress, + GenerateUniqueString, GenerateUUID, GenerateValuesFromArray, + GenerateWeightedCount, GenerateYear, + HollowGenerator, WeightedRandomGenerator, } from './Generators.ts'; -import { GenerateStringV2 } from './versioning/v2.ts'; +import { GenerateStringV2, GenerateUniqueIntervalV2, GenerateUniqueStringV2 } from './versioning/v2.ts'; function createGenerator, T>( generatorConstructor: new(params: T) => GeneratorType, @@ -359,7 +379,7 @@ export const generatorsFuncs = { * })); * ``` */ - string: createGenerator(GenerateStringV2), + string: createGenerator(GenerateString), // uniqueString: createGenerator(GenerateUniqueString), /** @@ -550,8 +570,8 @@ export const generatorsFuncs = { * })); * ``` */ - streetAddress: createGenerator(GenerateStreetAdddress), - // uniqueStreetAddress: createGenerator(GenerateUniqueStreetAdddress), + streetAddress: createGenerator(GenerateStreetAddress), + // uniqueStreetAddress: createGenerator(GenerateUniqueStreetAddress), /** * generates job titles. @@ -736,53 +756,162 @@ export const generatorsFuncs = { weightedRandom: createGenerator(WeightedRandomGenerator), }; -export const generatorsList: (new(params: any) => AbstractGenerator)[] = [ - GenerateBoolean, - GenerateCity, - GenerateCompanyName, - GenerateCountry, - GenerateDate, - GenerateDatetime, - GenerateDefault, - GenerateEmail, - GenerateFirstName, - GenerateFullName, - GenerateInt, - GenerateInterval, - GenerateIntPrimaryKey, - GenerateJobTitle, - GenerateJson, - GenerateLastName, - GenerateLine, - GenerateLoremIpsum, - GenerateNumber, - GeneratePhoneNumber, - GeneratePoint, - GeneratePostcode, - GenerateState, - GenerateStreetAdddress, - GenerateTime, - GenerateTimestamp, - GenerateUUID, - GenerateValuesFromArray, - GenerateYear, - WeightedRandomGenerator, -]; +// TODO: revise +// so far, version changes don’t affect generator parameters. +export const generatorsFuncsV2 = { ...generatorsFuncs }; -// -const generatorsObj = { - [entityKind]: [ - GenerateStreetAdddress, +export const generatorsMap = { + HollowGenerator: [ + HollowGenerator, + ], + GenerateDefault: [ + GenerateDefault, + ], + GenerateValuesFromArray: [ + GenerateValuesFromArray, + ], + GenerateSelfRelationsValuesFromArray: [ + GenerateSelfRelationsValuesFromArray, + ], + GenerateIntPrimaryKey: [ + GenerateIntPrimaryKey, + ], + GenerateNumber: [ + GenerateNumber, + ], + GenerateUniqueNumber: [ + GenerateUniqueNumber, + ], + GenerateInt: [ + GenerateInt, + ], + GenerateUniqueInt: [ + GenerateUniqueInt, + ], + GenerateBoolean: [ + GenerateBoolean, + ], + GenerateDate: [ + GenerateDate, + ], + GenerateTime: [ + GenerateTime, + ], + GenerateTimestamp: [ + GenerateTimestamp, + ], + GenerateDatetime: [ + GenerateDatetime, + ], + GenerateYear: [ + GenerateYear, + ], + GenerateJson: [ + GenerateJson, + ], + GenerateEnum: [ + GenerateEnum, + ], + GenerateInterval: [ + GenerateInterval, + ], + GenerateUniqueInterval: [ + GenerateUniqueInterval, + GenerateUniqueIntervalV2, + ], + GenerateString: [ + GenerateString, + GenerateStringV2, + ], + GenerateUniqueString: [ + GenerateUniqueString, + GenerateUniqueStringV2, + ], + GenerateUUID: [ GenerateUUID, ], - [entityKind]: [ - GenerateVarchar, - GenerateVarcharV2, - GenerateVarcharV3, + GenerateFirstName: [ + GenerateFirstName, + ], + GenerateUniqueFirstName: [ + GenerateUniqueFirstName, + ], + GenerateLastName: [ + GenerateLastName, + ], + GenerateUniqueLastName: [ + GenerateUniqueLastName, + ], + GenerateFullName: [ + GenerateFullName, + ], + GenerateUniqueFullName: [ + GenerateUniqueFullName, + ], + GenerateEmail: [ + GenerateEmail, + ], + GeneratePhoneNumber: [ + GeneratePhoneNumber, + ], + GenerateCountry: [ + GenerateCountry, + ], + GenerateUniqueCountry: [ + GenerateUniqueCountry, + ], + GenerateCity: [ + GenerateCity, + ], + GenerateUniqueCity: [ + GenerateUniqueCity, + ], + GenerateStreetAddress: [ + GenerateStreetAddress, + ], + GenerateUniqueStreetAddress: [ + GenerateUniqueStreetAddress, + ], + GenerateJobTitle: [ + GenerateJobTitle, + ], + GeneratePostcode: [ + GeneratePostcode, + ], + GenerateUniquePostcode: [ + GenerateUniquePostcode, + ], + GenerateState: [ + GenerateState, + ], + GenerateCompanyName: [ + GenerateCompanyName, + ], + GenerateUniqueCompanyName: [ + GenerateUniqueCompanyName, + ], + GenerateLoremIpsum: [ + GenerateLoremIpsum, + ], + GeneratePoint: [ + GeneratePoint, + ], + GenerateUniquePoint: [ + GenerateUniquePoint, + ], + GenerateLine: [ + GenerateLine, + ], + GenerateUniqueLine: [ + GenerateUniqueLine, + ], + WeightedRandomGenerator: [ + WeightedRandomGenerator, + ], + GenerateArray: [ + GenerateArray, ], - [entityKind]: [ - GenerateVarchar, - GenerateVarcharV2, - GenerateVarcharV3, + GenerateWeightedCount: [ + GenerateWeightedCount, ], }; diff --git a/drizzle-seed/src/services/Generators.ts b/drizzle-seed/src/services/Generators.ts index 629aff5a3..fff543739 100644 --- a/drizzle-seed/src/services/Generators.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -1,4 +1,4 @@ -import { entityKind } from 'drizzle-orm'; +/* eslint-disable drizzle-internal/require-entity-kind */ import prand from 'pure-rand'; import adjectives, { maxStringLength as maxAdjectiveLength } from '../datasets/adjectives.ts'; import cityNames, { maxStringLength as maxCityNameLength } from '../datasets/cityNames.ts'; @@ -15,22 +15,39 @@ import streetSuffix, { maxStringLength as maxStreetSuffixLength } from '../datas import { fastCartesianProduct, fillTemplate, getWeightedIndices, isObject } from './utils.ts'; export abstract class AbstractGenerator { - static readonly [entityKind]: string = 'AbstractGenerator'; + static readonly entityKind: string = 'AbstractGenerator'; static readonly version: number = 1; public isUnique = false; public notNull = false; + + // param for generators which have a unique version of themselves public uniqueVersionOfGen?: new(params: T) => AbstractGenerator; + public dataType?: string; public timeSpent?: number; + + // public arraySize?: number; public baseColumnDataType?: string; + + // param for text-like generators public stringLength?: number; + // params for GenerateValuesFromArray + public weightedCountSeed?: number | undefined; + public maxRepeatedValuesCount?: number | { weight: number; count: number | number[] }[] | undefined; + + // param for GenerateIntP + constructor(public params: T) {} init(params: { count: number | { weight: number; count: number | number[] }[]; seed: number }): void; init() { + this.updateParams(); + } + + updateParams() { if ((this.params as any).arraySize !== undefined) { this.arraySize = (this.params as any).arraySize; } @@ -48,10 +65,11 @@ export abstract class AbstractGenerator { getEntityKind(): string { const constructor = this.constructor as typeof AbstractGenerator; - return constructor[entityKind]; + return constructor.entityKind; } - replaceIfUnique({ count, seed }: { count: number; seed: number }) { + replaceIfUnique() { + this.updateParams(); if ( this.uniqueVersionOfGen !== undefined && this.isUnique === true @@ -59,10 +77,7 @@ export abstract class AbstractGenerator { const uniqueGen = new this.uniqueVersionOfGen({ ...this.params, }); - uniqueGen.init({ - count, - seed, - }); + uniqueGen.isUnique = this.isUnique; uniqueGen.dataType = this.dataType; @@ -71,9 +86,10 @@ export abstract class AbstractGenerator { return; } - replaceIfArray({ count, seed }: { count: number; seed: number }) { + replaceIfArray() { + this.updateParams(); if (!(this.getEntityKind() === 'GenerateArray') && this.arraySize !== undefined) { - const uniqueGen = this.replaceIfUnique({ count, seed }); + const uniqueGen = this.replaceIfUnique(); const baseColumnGen = uniqueGen === undefined ? this : uniqueGen; baseColumnGen.dataType = this.baseColumnDataType; const arrayGen = new GenerateArray( @@ -82,7 +98,6 @@ export abstract class AbstractGenerator { size: this.arraySize, }, ); - arrayGen.init({ count, seed }); return arrayGen; } @@ -93,7 +108,7 @@ export abstract class AbstractGenerator { // Generators Classes ----------------------------------------------------------------------------------------------------------------------- export class GenerateArray extends AbstractGenerator<{ baseColumnGen: AbstractGenerator; size?: number }> { - static override readonly [entityKind]: string = 'GenerateArray'; + static override readonly entityKind: string = 'GenerateArray'; public override arraySize = 10; override init({ count, seed }: { count: number; seed: number }) { @@ -113,7 +128,7 @@ export class GenerateArray extends AbstractGenerator<{ baseColumnGen: AbstractGe } export class GenerateWeightedCount extends AbstractGenerator<{}> { - static override readonly [entityKind]: string = 'GenerateWeightedCount'; + static override readonly entityKind: string = 'GenerateWeightedCount'; private state: { rng: prand.RandomGenerator; @@ -151,7 +166,7 @@ export class GenerateWeightedCount extends AbstractGenerator<{}> { } export class HollowGenerator extends AbstractGenerator<{}> { - static override readonly [entityKind]: string = 'HollowGenerator'; + static override readonly entityKind: string = 'HollowGenerator'; override init() {} @@ -162,7 +177,7 @@ export class GenerateDefault extends AbstractGenerator<{ defaultValue: unknown; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateDefault'; + static override readonly entityKind: string = 'GenerateDefault'; generate() { return this.params.defaultValue; @@ -178,10 +193,8 @@ export class GenerateValuesFromArray extends AbstractGenerator< arraySize?: number; } > { - static override readonly [entityKind]: string = 'GenerateValuesFromArray'; + static override readonly entityKind: string = 'GenerateValuesFromArray'; - public weightedCountSeed: number | undefined = undefined; - public maxRepeatedValuesCount?: number | { weight: number; count: number | number[] }[] = undefined; private state: { rng: prand.RandomGenerator; values: @@ -389,7 +402,7 @@ export class GenerateValuesFromArray extends AbstractGenerator< } export class GenerateSelfRelationsValuesFromArray extends AbstractGenerator<{ values: (number | string | boolean)[] }> { - static override readonly [entityKind]: string = 'GenerateSelfRelationsValuesFromArray'; + static override readonly entityKind: string = 'GenerateSelfRelationsValuesFromArray'; private state: { rng: prand.RandomGenerator; @@ -427,7 +440,7 @@ export class GenerateSelfRelationsValuesFromArray extends AbstractGenerator<{ va } export class GenerateIntPrimaryKey extends AbstractGenerator<{}> { - static override readonly [entityKind]: string = 'GenerateIntPrimaryKey'; + static override readonly entityKind: string = 'GenerateIntPrimaryKey'; public maxValue?: number | bigint; @@ -455,7 +468,7 @@ export class GenerateNumber extends AbstractGenerator< arraySize?: number; } > { - static override readonly [entityKind]: string = 'GenerateNumber'; + static override readonly entityKind: string = 'GenerateNumber'; private state: { rng: prand.RandomGenerator; @@ -510,7 +523,7 @@ export class GenerateUniqueNumber extends AbstractGenerator< isUnique?: boolean; } > { - static override readonly [entityKind]: string = 'GenerateUniqueNumber'; + static override readonly entityKind: string = 'GenerateUniqueNumber'; private state: { genUniqueIntObj: GenerateUniqueInt; @@ -562,7 +575,7 @@ export class GenerateInt extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateInt'; + static override readonly entityKind: string = 'GenerateInt'; private state: { rng: prand.RandomGenerator; @@ -630,7 +643,7 @@ export class GenerateUniqueInt extends AbstractGenerator<{ maxValue?: number | bigint; isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueInt'; + static override readonly entityKind: string = 'GenerateUniqueInt'; public genMaxRepeatedValuesCount: GenerateDefault | GenerateWeightedCount | undefined; public skipCheck?: boolean = false; @@ -798,7 +811,7 @@ export class GenerateUniqueInt extends AbstractGenerator<{ } export class GenerateBoolean extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateBoolean'; + static override readonly entityKind: string = 'GenerateBoolean'; private state: { rng: prand.RandomGenerator; @@ -829,7 +842,7 @@ export class GenerateDate extends AbstractGenerator<{ maxDate?: string | Date; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateDate'; + static override readonly entityKind: string = 'GenerateDate'; private state: { rng: prand.RandomGenerator; @@ -891,7 +904,7 @@ export class GenerateDate extends AbstractGenerator<{ } } export class GenerateTime extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateTime'; + static override readonly entityKind: string = 'GenerateTime'; private state: { rng: prand.RandomGenerator; @@ -927,7 +940,7 @@ export class GenerateTime extends AbstractGenerator<{ arraySize?: number }> { } } export class GenerateTimestampInt extends AbstractGenerator<{ unitOfTime?: 'seconds' | 'milliseconds' }> { - static override readonly [entityKind]: string = 'GenerateTimestampInt'; + static override readonly entityKind: string = 'GenerateTimestampInt'; private state: { generateTimestampObj: GenerateTimestamp; @@ -960,7 +973,7 @@ export class GenerateTimestampInt extends AbstractGenerator<{ unitOfTime?: 'seco } export class GenerateTimestamp extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateTimestamp'; + static override readonly entityKind: string = 'GenerateTimestamp'; private state: { rng: prand.RandomGenerator; @@ -1004,7 +1017,7 @@ export class GenerateTimestamp extends AbstractGenerator<{ arraySize?: number }> } export class GenerateDatetime extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateDatetime'; + static override readonly entityKind: string = 'GenerateDatetime'; private state: { rng: prand.RandomGenerator; @@ -1048,7 +1061,7 @@ export class GenerateDatetime extends AbstractGenerator<{ arraySize?: number }> } export class GenerateYear extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateYear'; + static override readonly entityKind: string = 'GenerateYear'; private state: { rng: prand.RandomGenerator; @@ -1083,7 +1096,7 @@ export class GenerateYear extends AbstractGenerator<{ arraySize?: number }> { } export class GenerateJson extends AbstractGenerator<{ arraySize?: number }> { - static override readonly [entityKind]: string = 'GenerateJson'; + static override readonly entityKind: string = 'GenerateJson'; private state: { emailGeneratorObj: GenerateEmail; @@ -1187,7 +1200,7 @@ export class GenerateJson extends AbstractGenerator<{ arraySize?: number }> { } export class GenerateEnum extends AbstractGenerator<{ enumValues: (string | number | boolean)[] }> { - static override readonly [entityKind]: string = 'GenerateEnum'; + static override readonly entityKind: string = 'GenerateEnum'; private state: { enumValuesGenerator: GenerateValuesFromArray; @@ -1227,7 +1240,7 @@ export class GenerateInterval extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateInterval'; + static override readonly entityKind: string = 'GenerateInterval'; private state: { rng: prand.RandomGenerator; @@ -1299,25 +1312,83 @@ export class GenerateInterval extends AbstractGenerator<{ // has a newer version export class GenerateUniqueInterval extends AbstractGenerator<{ + fields?: + | 'year' + | 'month' + | 'day' + | 'hour' + | 'minute' + | 'second' + | 'year to month' + | 'day to hour' + | 'day to minute' + | 'day to second' + | 'hour to minute' + | 'hour to second' + | 'minute to second'; isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueInterval'; + static override readonly 'entityKind': string = 'GenerateUniqueInterval'; private state: { rng: prand.RandomGenerator; + fieldsToGenerate: string[]; intervalSet: Set; } | undefined; public override isUnique = true; + private config: { [key: string]: { from: number; to: number } } = { + year: { + from: 0, + to: 5, + }, + month: { + from: 0, + to: 12, + }, + day: { + from: 1, + to: 29, + }, + hour: { + from: 0, + to: 24, + }, + minute: { + from: 0, + to: 60, + }, + second: { + from: 0, + to: 60, + }, + }; override init({ count, seed }: { count: number; seed: number }) { - const maxUniqueIntervalsNumber = 6 * 13 * 29 * 25 * 61 * 61; + const allFields = ['year', 'month', 'day', 'hour', 'minute', 'second']; + let fieldsToGenerate: string[] = allFields; + + if (this.params.fields !== undefined && this.params.fields?.includes(' to ')) { + const tokens = this.params.fields.split(' to '); + const endIdx = allFields.indexOf(tokens[1]!); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } else if (this.params.fields !== undefined) { + const endIdx = allFields.indexOf(this.params.fields); + fieldsToGenerate = allFields.slice(0, endIdx + 1); + } + + let maxUniqueIntervalsNumber = 1; + for (const field of fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + maxUniqueIntervalsNumber *= from - to + 1; + } + if (count > maxUniqueIntervalsNumber) { throw new RangeError(`count exceeds max number of unique intervals(${maxUniqueIntervalsNumber})`); } const rng = prand.xoroshiro128plus(seed); const intervalSet = new Set(); - this.state = { rng, intervalSet }; + this.state = { rng, fieldsToGenerate, intervalSet }; } generate() { @@ -1325,29 +1396,16 @@ export class GenerateUniqueInterval extends AbstractGenerator<{ throw new Error('state is not defined.'); } - let yearsNumb: number, - monthsNumb: number, - daysNumb: number, - hoursNumb: number, - minutesNumb: number, - secondsNumb: number; - - let interval = ''; + let interval, numb: number; for (;;) { - [yearsNumb, this.state.rng] = prand.uniformIntDistribution(0, 5, this.state.rng); - [monthsNumb, this.state.rng] = prand.uniformIntDistribution(0, 12, this.state.rng); - [daysNumb, this.state.rng] = prand.uniformIntDistribution(1, 29, this.state.rng); - [hoursNumb, this.state.rng] = prand.uniformIntDistribution(0, 24, this.state.rng); - [minutesNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - [secondsNumb, this.state.rng] = prand.uniformIntDistribution(0, 60, this.state.rng); - - interval = `${yearsNumb === 0 ? '' : `${yearsNumb} years `}` - + `${monthsNumb === 0 ? '' : `${monthsNumb} months `}` - + `${daysNumb === 0 ? '' : `${daysNumb} days `}` - + `${hoursNumb === 0 ? '' : `${hoursNumb} hours `}` - + `${minutesNumb === 0 ? '' : `${minutesNumb} minutes `}` - + `${secondsNumb === 0 ? '' : `${secondsNumb} seconds`}`; + interval = ''; + + for (const field of this.state.fieldsToGenerate) { + const from = this.config[field]!.from, to = this.config[field]!.to; + [numb, this.state.rng] = prand.uniformIntDistribution(from, to, this.state.rng); + interval += `${numb} ${field} `; + } if (!this.state.intervalSet.has(interval)) { this.state.intervalSet.add(interval); @@ -1364,7 +1422,7 @@ export class GenerateString extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateString'; + static override readonly entityKind: string = 'GenerateString'; private state: { rng: prand.RandomGenerator } | undefined; override uniqueVersionOfGen = GenerateUniqueString; @@ -1408,7 +1466,7 @@ export class GenerateString extends AbstractGenerator<{ // has a newer version export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueString'; + static override readonly entityKind: string = 'GenerateUniqueString'; private state: { rng: prand.RandomGenerator } | undefined; public override isUnique = true; @@ -1453,7 +1511,7 @@ export class GenerateUniqueString extends AbstractGenerator<{ isUnique?: boolean export class GenerateUUID extends AbstractGenerator<{ arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateUUID'; + static override readonly entityKind: string = 'GenerateUUID'; public override isUnique = true; @@ -1500,7 +1558,7 @@ export class GenerateFirstName extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateFirstName'; + static override readonly entityKind: string = 'GenerateFirstName'; override timeSpent: number = 0; private state: { @@ -1539,7 +1597,7 @@ export class GenerateFirstName extends AbstractGenerator<{ export class GenerateUniqueFirstName extends AbstractGenerator<{ isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueFirstName'; + static override readonly entityKind: string = 'GenerateUniqueFirstName'; private state: { genIndicesObj: GenerateUniqueInt; @@ -1580,7 +1638,7 @@ export class GenerateLastName extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateLastName'; + static override readonly entityKind: string = 'GenerateLastName'; private state: { rng: prand.RandomGenerator; @@ -1613,7 +1671,7 @@ export class GenerateLastName extends AbstractGenerator<{ } export class GenerateUniqueLastName extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueLastName'; + static override readonly entityKind: string = 'GenerateUniqueLastName'; private state: { genIndicesObj: GenerateUniqueInt; @@ -1653,7 +1711,7 @@ export class GenerateFullName extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateFullName'; + static override readonly entityKind: string = 'GenerateFullName'; private state: { rng: prand.RandomGenerator; @@ -1698,7 +1756,7 @@ export class GenerateFullName extends AbstractGenerator<{ export class GenerateUniqueFullName extends AbstractGenerator<{ isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueFullName'; + static override readonly entityKind: string = 'GenerateUniqueFullName'; private state: { fullnameSet: Set; @@ -1763,7 +1821,7 @@ export class GenerateUniqueFullName extends AbstractGenerator<{ export class GenerateEmail extends AbstractGenerator<{ arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateEmail'; + static override readonly entityKind: string = 'GenerateEmail'; private state: { genIndicesObj: GenerateUniqueInt; @@ -1830,7 +1888,7 @@ export class GeneratePhoneNumber extends AbstractGenerator<{ generatedDigitsNumbers?: number | number[]; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GeneratePhoneNumber'; + static override readonly entityKind: string = 'GeneratePhoneNumber'; private state: { rng: prand.RandomGenerator; @@ -2024,7 +2082,7 @@ export class GenerateCountry extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateCountry'; + static override readonly entityKind: string = 'GenerateCountry'; private state: { rng: prand.RandomGenerator; @@ -2060,7 +2118,7 @@ export class GenerateCountry extends AbstractGenerator<{ } export class GenerateUniqueCountry extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueCountry'; + static override readonly entityKind: string = 'GenerateUniqueCountry'; private state: { genIndicesObj: GenerateUniqueInt; @@ -2099,7 +2157,7 @@ export class GenerateUniqueCountry extends AbstractGenerator<{ isUnique?: boolea export class GenerateJobTitle extends AbstractGenerator<{ arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateJobTitle'; + static override readonly entityKind: string = 'GenerateJobTitle'; private state: { rng: prand.RandomGenerator; @@ -2131,17 +2189,17 @@ export class GenerateJobTitle extends AbstractGenerator<{ } } -export class GenerateStreetAdddress extends AbstractGenerator<{ +export class GenerateStreetAddress extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateStreetAdddress'; + static override readonly entityKind: string = 'GenerateStreetAddress'; private state: { rng: prand.RandomGenerator; possStreetNames: string[][]; } | undefined; - override uniqueVersionOfGen = GenerateUniqueStreetAdddress; + override uniqueVersionOfGen = GenerateUniqueStreetAddress; override init({ count, seed }: { count: number; seed: number }) { super.init({ count, seed }); @@ -2181,8 +2239,8 @@ export class GenerateStreetAdddress extends AbstractGenerator<{ } } -export class GenerateUniqueStreetAdddress extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueStreetAdddress'; +export class GenerateUniqueStreetAddress extends AbstractGenerator<{ isUnique?: boolean }> { + static override readonly entityKind: string = 'GenerateUniqueStreetAddress'; private state: { rng: prand.RandomGenerator; @@ -2278,7 +2336,7 @@ export class GenerateCity extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateCity'; + static override readonly entityKind: string = 'GenerateCity'; private state: { rng: prand.RandomGenerator; @@ -2312,7 +2370,7 @@ export class GenerateCity extends AbstractGenerator<{ } export class GenerateUniqueCity extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueCity'; + static override readonly entityKind: string = 'GenerateUniqueCity'; private state: { genIndicesObj: GenerateUniqueInt; @@ -2352,7 +2410,7 @@ export class GeneratePostcode extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GeneratePostcode'; + static override readonly entityKind: string = 'GeneratePostcode'; private state: { rng: prand.RandomGenerator; @@ -2406,7 +2464,7 @@ export class GeneratePostcode extends AbstractGenerator<{ } export class GenerateUniquePostcode extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniquePostcode'; + static override readonly entityKind: string = 'GenerateUniquePostcode'; private state: { rng: prand.RandomGenerator; @@ -2493,7 +2551,7 @@ export class GenerateUniquePostcode extends AbstractGenerator<{ isUnique?: boole export class GenerateState extends AbstractGenerator<{ arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateState'; + static override readonly entityKind: string = 'GenerateState'; private state: { rng: prand.RandomGenerator; @@ -2529,7 +2587,7 @@ export class GenerateCompanyName extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateCompanyName'; + static override readonly entityKind: string = 'GenerateCompanyName'; private state: { rng: prand.RandomGenerator; @@ -2598,7 +2656,7 @@ export class GenerateCompanyName extends AbstractGenerator<{ } export class GenerateUniqueCompanyName extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueCompanyName'; + static override readonly entityKind: string = 'GenerateUniqueCompanyName'; private state: { rng: prand.RandomGenerator; @@ -2709,7 +2767,7 @@ export class GenerateLoremIpsum extends AbstractGenerator<{ sentencesCount?: number; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateLoremIpsum'; + static override readonly entityKind: string = 'GenerateLoremIpsum'; private state: { rng: prand.RandomGenerator; @@ -2748,7 +2806,7 @@ export class GenerateLoremIpsum extends AbstractGenerator<{ } export class WeightedRandomGenerator extends AbstractGenerator<{ weight: number; value: AbstractGenerator }[]> { - static override readonly [entityKind]: string = 'WeightedRandomGenerator'; + static override readonly entityKind: string = 'WeightedRandomGenerator'; private state: { rng: prand.RandomGenerator; @@ -2818,7 +2876,7 @@ export class GeneratePoint extends AbstractGenerator<{ maxYValue?: number; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GeneratePoint'; + static override readonly entityKind: string = 'GeneratePoint'; private state: { xCoordinateGen: GenerateNumber; @@ -2872,7 +2930,7 @@ export class GenerateUniquePoint extends AbstractGenerator<{ maxYValue?: number; isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniquePoint'; + static override readonly entityKind: string = 'GenerateUniquePoint'; private state: { xCoordinateGen: GenerateUniqueNumber; @@ -2927,7 +2985,7 @@ export class GenerateLine extends AbstractGenerator<{ maxCValue?: number; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateLine'; + static override readonly entityKind: string = 'GenerateLine'; private state: { aCoefficientGen: GenerateNumber; @@ -2998,7 +3056,7 @@ export class GenerateUniqueLine extends AbstractGenerator<{ maxCValue?: number; isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueLine'; + static override readonly entityKind: string = 'GenerateUniqueLine'; private state: { aCoefficientGen: GenerateUniqueNumber; diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index f9da6a50a..35cba442f 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -1,3 +1,4 @@ +/* eslint-disable drizzle-internal/require-entity-kind */ import { entityKind, eq, is } from 'drizzle-orm'; import type { MySqlTable, MySqlTableWithColumns } from 'drizzle-orm/mysql-core'; import { MySqlDatabase } from 'drizzle-orm/mysql-core'; @@ -12,15 +13,14 @@ import type { TableGeneratorsType, } from '../types/seedService.ts'; import type { Column, Prettify, Relation, Table } from '../types/tables.ts'; -import type { AbstractGenerator } from './Generators.ts'; +import { generatorsMap } from './GeneratorFuncs.ts'; +import type { AbstractGenerator, GenerateArray, GenerateInterval, GenerateWeightedCount } from './Generators.ts'; import { latestVersion } from './apiVersion.ts'; -// import * as Generators from './Generators.ts'; import { equalSets, generateHashFromString } from './utils.ts'; -import * as GeneratorsV2 from './versioning/v2.ts'; export class SeedService { - static readonly [entityKind]: string = 'SeedService'; + static readonly entityKind: string = 'SeedService'; private defaultCountForTable = 10; private postgresPgLiteMaxParametersNumber = 32740; @@ -29,6 +29,7 @@ export class SeedService { private mysqlMaxParametersNumber = 100000; // SQLITE_MAX_VARIABLE_NUMBER, which by default equals to 999 for SQLite versions prior to 3.32.0 (2020-05-22) or 32766 for SQLite versions after 3.32.0. private sqliteMaxParametersNumber = 32766; + private version?: number; generatePossibleGenerators = ( connectionType: 'postgresql' | 'mysql' | 'sqlite', @@ -40,8 +41,8 @@ export class SeedService { let columnPossibleGenerator: Prettify; let tablePossibleGenerators: Prettify; const customSeed = options?.seed === undefined ? 0 : options.seed; - const version = options?.version === undefined ? latestVersion : options.version; - if (version < 1 || version > latestVersion) { + this.version = options?.version === undefined ? latestVersion : options.version; + if (this.version < 1 || this.version > latestVersion) { throw new Error(`Version should be in range [1, ${latestVersion}].`); } @@ -181,21 +182,15 @@ export class SeedService { && refinements[table.name]!.columns !== undefined && refinements[table.name]!.columns[col.name] !== undefined ) { - let genObj = refinements[table.name]!.columns[col.name]!; + const genObj = refinements[table.name]!.columns[col.name]!; - const genObjEntityKind = genObj.getEntityKind(); - const generatorConstructor = this.selectGeneratorOfVersion(version, genObjEntityKind); - genObj = new generatorConstructor(genObj.params); - - // TODO: for now only GenerateValuesFromArray support notNull property - genObj.notNull = col.notNull; if (col.columnType.match(/\[\w*]/g) !== null) { if ( (col.baseColumn?.dataType === 'array' && col.baseColumn.columnType.match(/\[\w*]/g) !== null) // studio case || (col.typeParams.dimensions !== undefined && col.typeParams.dimensions > 1) ) { - throw new Error("for now you can't specify generators for columns of dimensition greater than 1."); + throw new Error("for now you can't specify generators for columns of dimension greater than 1."); } genObj.baseColumnDataType = col.baseColumn?.dataType; @@ -217,29 +212,26 @@ export class SeedService { const predicate = cyclicRelation !== undefined && col.notNull === false; if (predicate === true) { - columnPossibleGenerator.generator = new Generators.GenerateDefault({ defaultValue: null }); + columnPossibleGenerator.generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: null }); columnPossibleGenerator.wasDefinedBefore = true; + } else { + columnPossibleGenerator.generator = new generatorsMap.HollowGenerator[0]!({}); } - - columnPossibleGenerator.generator = new Generators.HollowGenerator({}); } // TODO: rewrite pickGeneratorFor... using new col properties: isUnique and notNull else if (connectionType === 'postgresql') { columnPossibleGenerator.generator = this.selectGeneratorForPostgresColumn( table, col, - version, ); } else if (connectionType === 'mysql') { columnPossibleGenerator.generator = this.selectGeneratorForMysqlColumn( table, col, - version, ); } else if (connectionType === 'sqlite') { columnPossibleGenerator.generator = this.selectGeneratorForSqlite( table, col, - version, ); } @@ -249,9 +241,22 @@ export class SeedService { ); } - // check if new version exists + const arrayGen = columnPossibleGenerator.generator.replaceIfArray(); + if (arrayGen !== undefined) { + columnPossibleGenerator.generator = arrayGen; + } + + const uniqueGen = columnPossibleGenerator.generator.replaceIfUnique(); + if (uniqueGen !== undefined) { + columnPossibleGenerator.generator = uniqueGen; + } + + // selecting version of generator + columnPossibleGenerator.generator = this.selectVersionOfGenerator(columnPossibleGenerator.generator); columnPossibleGenerator.generator.isUnique = col.isUnique; + // TODO: for now only GenerateValuesFromArray support notNull property + columnPossibleGenerator.generator.notNull = col.notNull; columnPossibleGenerator.generator.dataType = col.dataType; columnPossibleGenerator.generator.stringLength = col.typeParams.length; @@ -264,6 +269,39 @@ export class SeedService { return tablesPossibleGenerators; }; + selectVersionOfGenerator = (generator: AbstractGenerator) => { + const entityKind = generator.getEntityKind(); + if (entityKind === 'GenerateArray') { + const oldBaseColumnGen = (generator as GenerateArray).params.baseColumnGen; + + const newBaseColumnGen = this.selectVersionOfGenerator(oldBaseColumnGen); + // newGenerator.baseColumnDataType = oldGenerator.baseColumnDataType; + + (generator as GenerateArray).params.baseColumnGen = newBaseColumnGen; + } + + let possibleGeneratorConstructors = + (generatorsMap as { [entityKind: string]: (typeof AbstractGenerator)[] })[entityKind]; + + possibleGeneratorConstructors = possibleGeneratorConstructors?.filter((possGenCon) => + possGenCon.version <= this.version! // sorting in ascending order by version + ).sort((a, b) => a.version - b.version); + const generatorConstructor = possibleGeneratorConstructors?.at(-1) as + | (new(params: any) => AbstractGenerator) + | undefined; + if (generatorConstructor === undefined) { + throw new Error(`Can't select ${entityKind} generator for ${this.version} version.`); + } + + const newGenerator = new generatorConstructor(generator.params); + newGenerator.baseColumnDataType = generator.baseColumnDataType; + newGenerator.isUnique = generator.isUnique; + newGenerator.dataType = generator.dataType; + newGenerator.stringLength = generator.stringLength; + + return newGenerator; + }; + cyclicTablesCompare = ( table1: Table, table2: Table, @@ -410,7 +448,9 @@ export class SeedService { count: number, seed: number, ) => { - const gen = new Generators.GenerateWeightedCount({}); + let gen = new generatorsMap.GenerateWeightedCount[0]!({}); + gen = this.selectVersionOfGenerator(gen) as GenerateWeightedCount; + // const gen = new GenerateWeightedCount({}); gen.init({ count: weightedCount, seed }); let weightedWithCount = 0; for (let i = 0; i < count; i++) { @@ -420,34 +460,10 @@ export class SeedService { return weightedWithCount; }; - selectGeneratorOfVersion = (version: number, generatorEntityKind: string) => { - type GeneratorConstructorT = new(params: any) => AbstractGenerator; - const GeneratorVersions = [...Object.values(GeneratorsV2), ...Object.values(Generators)]; - - let generatorConstructor: GeneratorConstructorT | undefined; - for (const gens of GeneratorVersions) { - for (const gen of Object.values(gens)) { - const abstractGen = gen as typeof AbstractGenerator; - if (abstractGen[entityKind] === generatorEntityKind) { - generatorConstructor = abstractGen as unknown as GeneratorConstructorT; - - return generatorConstructor; - } - } - } - - if (generatorConstructor === undefined) { - throw new Error(`Can't select ${generatorEntityKind} generator for ${version} version.`); - } - - return generatorConstructor; - }; - // TODO: revise serial part generators selectGeneratorForPostgresColumn = ( table: Table, col: Column, - version: number, ) => { const pickGenerator = (table: Table, col: Column) => { // ARRAY @@ -455,13 +471,22 @@ export class SeedService { const baseColumnGen = this.selectGeneratorForPostgresColumn( table, col.baseColumn!, - version, ) as AbstractGenerator; if (baseColumnGen === undefined) { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - const generator = new Generators.GenerateArray({ baseColumnGen, size: col.size }); + // const getBaseColumnDataType = (baseColumn: Column) => { + // if (baseColumn.baseColumn !== undefined) { + // return getBaseColumnDataType(baseColumn.baseColumn); + // } + + // return baseColumn.dataType; + // }; + // const baseColumnDataType = getBaseColumnDataType(col.baseColumn); + + const generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen, size: col.size }); + // generator.baseColumnDataType = baseColumnDataType; return generator; } @@ -475,15 +500,15 @@ export class SeedService { }; baseColumn.columnType = baseColumnType; - const baseColumnGen = this.selectGeneratorForPostgresColumn(table, baseColumn, version) as AbstractGenerator; + const baseColumnGen = this.selectGeneratorForPostgresColumn(table, baseColumn) as AbstractGenerator; if (baseColumnGen === undefined) { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - let generator = new Generators.GenerateArray({ baseColumnGen }); + let generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen }); for (let i = 0; i < col.typeParams.dimensions! - 1; i++) { - generator = new Generators.GenerateArray({ baseColumnGen: generator }); + generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen: generator }); } return generator; @@ -497,7 +522,7 @@ export class SeedService { || col.columnType.includes('bigint')) && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateIntPrimaryKey({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); return generator; } @@ -545,7 +570,7 @@ export class SeedService { && !col.columnType.includes('interval') && !col.columnType.includes('point') ) { - const generator = new Generators.GenerateInt({ + const generator = new generatorsMap.GenerateInt[0]!({ minValue, maxValue, }); @@ -554,9 +579,9 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new Generators.GenerateIntPrimaryKey({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); - (generator as Generators.GenerateIntPrimaryKey).maxValue = maxValue; + generator.maxValue = maxValue; return generator; } @@ -573,14 +598,14 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new Generators.GenerateNumber({ + const generator = new generatorsMap.GenerateNumber[0]!({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), }); return generator; } - const generator = new Generators.GenerateNumber({}); + const generator = new generatorsMap.GenerateNumber[0]!({}); return generator; } @@ -592,7 +617,7 @@ export class SeedService { || col.columnType.startsWith('char')) && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateUniqueString({}); + const generator = new generatorsMap.GenerateUniqueString[0]!({}); return generator; } @@ -603,7 +628,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('name') ) { - const generator = new Generators.GenerateFirstName({}); + const generator = new generatorsMap.GenerateFirstName[0]!({}); return generator; } @@ -614,7 +639,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('email') ) { - const generator = new Generators.GenerateEmail({}); + const generator = new generatorsMap.GenerateEmail[0]!({}); return generator; } @@ -624,47 +649,47 @@ export class SeedService { || col.columnType.startsWith('varchar') || col.columnType.startsWith('char') ) { - const generator = new Generators.GenerateString({}); + const generator = new generatorsMap.GenerateString[0]!({}); return generator; } // UUID if (col.columnType === 'uuid') { - const generator = new Generators.GenerateUUID({}); + const generator = new generatorsMap.GenerateUUID[0]!({}); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new Generators.GenerateBoolean({}); + const generator = new generatorsMap.GenerateBoolean[0]!({}); return generator; } // DATE, TIME, TIMESTAMP if (col.columnType.includes('date')) { - const generator = new Generators.GenerateDate({}); + const generator = new generatorsMap.GenerateDate[0]!({}); return generator; } if (col.columnType === 'time') { - const generator = new Generators.GenerateTime({}); + const generator = new generatorsMap.GenerateTime[0]!({}); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new Generators.GenerateTimestamp({}); + const generator = new generatorsMap.GenerateTimestamp[0]!({}); return generator; } // JSON, JSONB if (col.columnType === 'json' || col.columnType === 'jsonb') { - const generator = new Generators.GenerateJson({}); + const generator = new generatorsMap.GenerateJson[0]!({}); return generator; } @@ -676,7 +701,7 @@ export class SeedService { // ENUM if (col.enumValues !== undefined) { - const generator = new Generators.GenerateEnum({ + const generator = new generatorsMap.GenerateEnum[0]!({ enumValues: col.enumValues, }); @@ -686,32 +711,32 @@ export class SeedService { // INTERVAL if (col.columnType.startsWith('interval')) { if (col.columnType === 'interval') { - const generator = new Generators.GenerateInterval({}); + const generator = new generatorsMap.GenerateInterval[0]!({}); return generator; } - const fields = col.columnType.replace('interval ', '') as Generators.GenerateInterval['params']['fields']; - const generator = new Generators.GenerateInterval({ fields }); + const fields = col.columnType.replace('interval ', '') as GenerateInterval['params']['fields']; + const generator = new generatorsMap.GenerateInterval[0]!({ fields }); return generator; } // POINT, LINE if (col.columnType.includes('point')) { - const generator = new Generators.GeneratePoint({}); + const generator = new generatorsMap.GeneratePoint[0]!({}); return generator; } if (col.columnType.includes('line')) { - const generator = new Generators.GenerateLine({}); + const generator = new generatorsMap.GenerateLine[0]!({}); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new Generators.GenerateDefault({ + const generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: col.default, }); return generator; @@ -720,15 +745,8 @@ export class SeedService { return; }; - let generator = pickGenerator(table, col) as AbstractGenerator || undefined; + const generator = pickGenerator(table, col); if (generator !== undefined) { - const generatorConstructor = this.selectGeneratorOfVersion( - version, - generator.getEntityKind(), - ); - - generator = new generatorConstructor(generator.params); - generator.isUnique = col.isUnique; generator.dataType = col.dataType; generator.stringLength = col.typeParams.length; @@ -740,7 +758,6 @@ export class SeedService { selectGeneratorForMysqlColumn = ( table: Table, col: Column, - version: number, ) => { const pickGenerator = (table: Table, col: Column) => { // INT ------------------------------------------------------------------------------------------------------------ @@ -748,7 +765,7 @@ export class SeedService { (col.columnType.includes('serial') || col.columnType.includes('int')) && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateIntPrimaryKey({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); return generator; } @@ -783,7 +800,7 @@ export class SeedService { } if (col.columnType.includes('int')) { - const generator = new Generators.GenerateInt({ + const generator = new generatorsMap.GenerateInt[0]!({ minValue, maxValue, }); @@ -791,7 +808,7 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new Generators.GenerateIntPrimaryKey({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); generator.maxValue = maxValue; return generator; } @@ -809,7 +826,7 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new Generators.GenerateNumber({ + const generator = new generatorsMap.GenerateNumber[0]!({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), @@ -817,7 +834,7 @@ export class SeedService { return generator; } - const generator = new Generators.GenerateNumber({}); + const generator = new generatorsMap.GenerateNumber[0]!({}); return generator; } @@ -831,7 +848,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateUniqueString({}); + const generator = new generatorsMap.GenerateUniqueString[0]!({}); return generator; } @@ -844,7 +861,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('name') ) { - const generator = new Generators.GenerateFirstName({}); + const generator = new generatorsMap.GenerateFirstName[0]!({}); return generator; } @@ -857,7 +874,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('email') ) { - const generator = new Generators.GenerateEmail({}); + const generator = new generatorsMap.GenerateEmail[0]!({}); return generator; } @@ -869,58 +886,58 @@ export class SeedService { || col.columnType.startsWith('binary') || col.columnType.startsWith('varbinary') ) { - const generator = new Generators.GenerateString({}); + const generator = new generatorsMap.GenerateString[0]!({}); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new Generators.GenerateBoolean({}); + const generator = new generatorsMap.GenerateBoolean[0]!({}); return generator; } // DATE, TIME, TIMESTAMP, DATETIME, YEAR if (col.columnType.includes('datetime')) { - const generator = new Generators.GenerateDatetime({}); + const generator = new generatorsMap.GenerateDatetime[0]!({}); return generator; } if (col.columnType.includes('date')) { - const generator = new Generators.GenerateDate({}); + const generator = new generatorsMap.GenerateDate[0]!({}); return generator; } if (col.columnType === 'time') { - const generator = new Generators.GenerateTime({}); + const generator = new generatorsMap.GenerateTime[0]!({}); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new Generators.GenerateTimestamp({}); + const generator = new generatorsMap.GenerateTimestamp[0]!({}); return generator; } if (col.columnType === 'year') { - const generator = new Generators.GenerateYear({}); + const generator = new generatorsMap.GenerateYear[0]!({}); return generator; } // JSON if (col.columnType === 'json') { - const generator = new Generators.GenerateJson({}); + const generator = new generatorsMap.GenerateJson[0]!({}); return generator; } // ENUM if (col.enumValues !== undefined) { - const generator = new Generators.GenerateEnum({ + const generator = new generatorsMap.GenerateEnum[0]!({ enumValues: col.enumValues, }); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new Generators.GenerateDefault({ + const generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: col.default, }); return generator; @@ -929,15 +946,7 @@ export class SeedService { return; }; - let generator = pickGenerator(table, col) as AbstractGenerator || undefined; - if (generator !== undefined) { - const generatorConstructor = this.selectGeneratorOfVersion( - version, - generator.getEntityKind(), - ); - - generator = new generatorConstructor(generator.params); - } + const generator = pickGenerator(table, col); return generator; }; @@ -945,7 +954,6 @@ export class SeedService { selectGeneratorForSqlite = ( table: Table, col: Column, - version: number, ) => { const pickGenerator = (table: Table, col: Column) => { // int section --------------------------------------------------------------------------------------- @@ -953,17 +961,17 @@ export class SeedService { (col.columnType === 'integer' || col.columnType === 'numeric') && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateIntPrimaryKey({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); return generator; } if (col.columnType === 'integer' && col.dataType === 'boolean') { - const generator = new Generators.GenerateBoolean({}); + const generator = new generatorsMap.GenerateBoolean[0]!({}); return generator; } if ((col.columnType === 'integer' && col.dataType === 'date')) { - const generator = new Generators.GenerateTimestamp({}); + const generator = new generatorsMap.GenerateTimestamp[0]!({}); return generator; } @@ -971,7 +979,7 @@ export class SeedService { col.columnType === 'integer' || (col.dataType === 'bigint' && col.columnType === 'blob') ) { - const generator = new Generators.GenerateInt({}); + const generator = new generatorsMap.GenerateInt[0]!({}); return generator; } @@ -982,7 +990,7 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new Generators.GenerateNumber({ + const generator = new generatorsMap.GenerateNumber[0]!({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), @@ -990,7 +998,7 @@ export class SeedService { return generator; } - const generator = new Generators.GenerateNumber({}); + const generator = new generatorsMap.GenerateNumber[0]!({}); return generator; } @@ -1001,7 +1009,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && table.primaryKeys.includes(col.name) ) { - const generator = new Generators.GenerateUniqueString({}); + const generator = new generatorsMap.GenerateUniqueString[0]!({}); return generator; } @@ -1011,7 +1019,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('name') ) { - const generator = new Generators.GenerateFirstName({}); + const generator = new generatorsMap.GenerateFirstName[0]!({}); return generator; } @@ -1021,7 +1029,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('email') ) { - const generator = new Generators.GenerateEmail({}); + const generator = new generatorsMap.GenerateEmail[0]!({}); return generator; } @@ -1031,7 +1039,7 @@ export class SeedService { || col.columnType.startsWith('blob') || col.columnType.startsWith('blobbuffer') ) { - const generator = new Generators.GenerateString({}); + const generator = new generatorsMap.GenerateString[0]!({}); return generator; } @@ -1039,12 +1047,12 @@ export class SeedService { (col.columnType.startsWith('text') && col.dataType === 'json') || (col.columnType.startsWith('blob') && col.dataType === 'json') ) { - const generator = new Generators.GenerateJson({}); + const generator = new generatorsMap.GenerateJson[0]!({}); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new Generators.GenerateDefault({ + const generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: col.default, }); return generator; @@ -1053,15 +1061,7 @@ export class SeedService { return; }; - let generator = pickGenerator(table, col) as AbstractGenerator || undefined; - if (generator !== undefined) { - const generatorConstructor = this.selectGeneratorOfVersion( - version, - generator.getEntityKind(), - ); - - generator = new generatorConstructor(generator.params); - } + const generator = pickGenerator(table, col); return generator; }; @@ -1220,9 +1220,13 @@ export class SeedService { }))!.map((rows) => rows[refColName]) as (string | number | boolean)[]; hasSelfRelation = true; - genObj = new Generators.GenerateSelfRelationsValuesFromArray({ + genObj = new generatorsMap.GenerateSelfRelationsValuesFromArray[0]!({ values: refColumnValues, }); + genObj = this.selectVersionOfGenerator(genObj); + // genObj = new GenerateSelfRelationsValuesFromArray({ + // values: refColumnValues, + // }); } else if ( tableGenerators[rel.columns[colIdx]!]?.wasDefinedBefore === false && tableGenerators[rel.columns[colIdx]!]?.wasRefined === false @@ -1240,10 +1244,11 @@ export class SeedService { weightedCountSeed = table.withFromTable[rel.refTable]!.weightedCountSeed; } - genObj = new Generators.GenerateValuesFromArray({ values: refColumnValues }); - (genObj as Generators.GenerateValuesFromArray).notNull = tableGenerators[rel.columns[colIdx]!]!.notNull; - (genObj as Generators.GenerateValuesFromArray).weightedCountSeed = weightedCountSeed; - (genObj as Generators.GenerateValuesFromArray).maxRepeatedValuesCount = repeatedValuesCount; + // TODO: revise maybe need to select version of generator here too + genObj = new generatorsMap.GenerateValuesFromArray[0]!({ values: refColumnValues }); + genObj.notNull = tableGenerators[rel.columns[colIdx]!]!.notNull; + genObj.weightedCountSeed = weightedCountSeed; + genObj.maxRepeatedValuesCount = repeatedValuesCount; } if (genObj !== undefined) { @@ -1360,15 +1365,15 @@ export class SeedService { seed: columnGenerator.pRNGSeed, }); - const arrayGen = columnsGenerators[columnName]!.replaceIfArray({ count, seed: columnGenerator.pRNGSeed }); - if (arrayGen !== undefined) { - columnsGenerators[columnName] = arrayGen; - } + // const arrayGen = columnsGenerators[columnName]!.replaceIfArray({ count, seed: columnGenerator.pRNGSeed }); + // if (arrayGen !== undefined) { + // columnsGenerators[columnName] = arrayGen; + // } - const uniqueGen = columnsGenerators[columnName]!.replaceIfUnique({ count, seed: columnGenerator.pRNGSeed }); - if (uniqueGen !== undefined) { - columnsGenerators[columnName] = uniqueGen; - } + // const uniqueGen = columnsGenerators[columnName]!.replaceIfUnique({ count, seed: columnGenerator.pRNGSeed }); + // if (uniqueGen !== undefined) { + // columnsGenerators[columnName] = uniqueGen; + // } } let maxParametersNumber: number; if (is(db, PgDatabase)) { diff --git a/drizzle-seed/src/services/versioning/v2.ts b/drizzle-seed/src/services/versioning/v2.ts index 96451b2f8..e09cc45f6 100644 --- a/drizzle-seed/src/services/versioning/v2.ts +++ b/drizzle-seed/src/services/versioning/v2.ts @@ -1,12 +1,6 @@ -import { entityKind } from 'drizzle-orm'; +/* eslint-disable drizzle-internal/require-entity-kind */ import prand from 'pure-rand'; -import { AbstractGenerator, GenerateInterval } from '../Generators.ts'; - -export class GenerateIntervalV2 extends GenerateInterval { - static override readonly [entityKind]: string = 'GenerateInterval'; - override readonly version: number = 2; - override uniqueVersionOfGen = GenerateUniqueIntervalV2; -} +import { AbstractGenerator } from '../Generators.ts'; export class GenerateUniqueIntervalV2 extends AbstractGenerator<{ fields?: @@ -25,8 +19,8 @@ export class GenerateUniqueIntervalV2 extends AbstractGenerator<{ | 'minute to second'; isUnique?: boolean; }> { - static override readonly [entityKind]: string = 'GenerateUniqueInterval'; - override readonly version: number = 2; + static override readonly 'entityKind': string = 'GenerateUniqueInterval'; + static override readonly version: number = 2; private state: { rng: prand.RandomGenerator; @@ -119,8 +113,8 @@ export class GenerateStringV2 extends AbstractGenerator<{ isUnique?: boolean; arraySize?: number; }> { - static override readonly [entityKind]: string = 'GenerateString'; - override readonly version: number = 2; + static override readonly 'entityKind': string = 'GenerateString'; + static override readonly version: number = 2; private state: { rng: prand.RandomGenerator; @@ -175,8 +169,8 @@ export class GenerateStringV2 extends AbstractGenerator<{ } export class GenerateUniqueStringV2 extends AbstractGenerator<{ isUnique?: boolean }> { - static override readonly [entityKind]: string = 'GenerateUniqueString'; - override readonly version: number = 2; + static override readonly 'entityKind': string = 'GenerateUniqueString'; + static override readonly version: number = 2; private state: { rng: prand.RandomGenerator; diff --git a/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts b/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts index 3473e555a..23fca0c6c 100644 --- a/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts +++ b/drizzle-seed/tests/benchmarks/generatorsBenchmark.ts @@ -23,7 +23,7 @@ import { GeneratePoint, GeneratePostcode, GenerateState, - GenerateStreetAdddress, + GenerateStreetAddress, GenerateString, GenerateTime, GenerateTimestamp, @@ -35,7 +35,7 @@ import { GenerateUniqueNumber, GenerateUniquePoint, GenerateUniquePostcode, - GenerateUniqueStreetAdddress, + GenerateUniqueStreetAddress, GenerateUniqueString, GenerateValuesFromArray, GenerateYear, @@ -107,8 +107,8 @@ const generatorsFuncs = { // uniqueCountry: new GenerateUniqueCountry({}), city: new GenerateCity({}), // uniqueCity: new GenerateUniqueCity({}), - streetAddress: new GenerateStreetAdddress({}), - uniqueStreetAddress: new GenerateUniqueStreetAdddress({}), + streetAddress: new GenerateStreetAddress({}), + uniqueStreetAddress: new GenerateUniqueStreetAddress({}), jobTitle: new GenerateJobTitle({}), postcode: new GeneratePostcode({}), uniquePostcode: new GenerateUniquePostcode({}), diff --git a/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts b/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts index 16a55baf4..d4f45de22 100644 --- a/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts +++ b/drizzle-seed/tests/pg/allDataTypesTest/pgSchema.ts @@ -64,31 +64,31 @@ export const allDataTypes = schema.table('all_data_types', { }); export const allArrayDataTypes = schema.table('all_array_data_types', { - integerArray: integer('integer_array').array(), - smallintArray: smallint('smallint_array').array(), - bigintegerArray: bigint('bigint_array', { mode: 'bigint' }).array(), - bigintNumberArray: bigint('bigint_number_array', { mode: 'number' }).array(), - booleanArray: boolean('boolean_array').array(), - textArray: text('text_array').array(), - varcharArray: varchar('varchar_array', { length: 256 }).array(), - charArray: char('char_array', { length: 256 }).array(), - numericArray: numeric('numeric_array').array(), - decimalArray: decimal('decimal_array').array(), - realArray: real('real_array').array(), - doublePrecisionArray: doublePrecision('double_precision_array').array(), - jsonArray: json('json_array').array(), - jsonbArray: jsonb('jsonb_array').array(), - timeArray: time('time_array').array(), - timestampDateArray: timestamp('timestamp_date_array', { mode: 'date' }).array(), - timestampStringArray: timestamp('timestamp_string_array', { mode: 'string' }).array(), + // integerArray: integer('integer_array').array(), + // smallintArray: smallint('smallint_array').array(), + // bigintegerArray: bigint('bigint_array', { mode: 'bigint' }).array(), + // bigintNumberArray: bigint('bigint_number_array', { mode: 'number' }).array(), + // booleanArray: boolean('boolean_array').array(), + // textArray: text('text_array').array(), + // varcharArray: varchar('varchar_array', { length: 256 }).array(), + // charArray: char('char_array', { length: 256 }).array(), + // numericArray: numeric('numeric_array').array(), + // decimalArray: decimal('decimal_array').array(), + // realArray: real('real_array').array(), + // doublePrecisionArray: doublePrecision('double_precision_array').array(), + // jsonArray: json('json_array').array(), + // jsonbArray: jsonb('jsonb_array').array(), + // timeArray: time('time_array').array(), + // timestampDateArray: timestamp('timestamp_date_array', { mode: 'date' }).array(), + // timestampStringArray: timestamp('timestamp_string_array', { mode: 'string' }).array(), dateStringArray: date('date_string_array', { mode: 'string' }).array(), dateArray: date('date_array', { mode: 'date' }).array(), - intervalArray: interval('interval_array').array(), - pointArray: point('point_array', { mode: 'xy' }).array(), - pointTupleArray: point('point_tuple_array', { mode: 'tuple' }).array(), - lineArray: line('line_array', { mode: 'abc' }).array(), - lineTupleArray: line('line_tuple_array', { mode: 'tuple' }).array(), - moodEnumArray: moodEnum('mood_enum_array').array(), + // intervalArray: interval('interval_array').array(), + // pointArray: point('point_array', { mode: 'xy' }).array(), + // pointTupleArray: point('point_tuple_array', { mode: 'tuple' }).array(), + // lineArray: line('line_array', { mode: 'abc' }).array(), + // lineTupleArray: line('line_tuple_array', { mode: 'tuple' }).array(), + // moodEnumArray: moodEnum('mood_enum_array').array(), }); export const ndArrays = schema.table('nd_arrays', { From eb72873330b7822ff95cdfb7af4e9b24fb28b7fb Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Mon, 23 Dec 2024 15:51:18 +0200 Subject: [PATCH 13/18] bug fix, refine functions versioning --- changelogs/drizzle-seed/0.2.0.md | 5 ++- drizzle-seed/src/index.ts | 36 +++++++++++++++------ drizzle-seed/src/services/GeneratorFuncs.ts | 5 +-- drizzle-seed/src/services/SeedService.ts | 31 ++++++++++++++---- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/changelogs/drizzle-seed/0.2.0.md b/changelogs/drizzle-seed/0.2.0.md index be19a8949..5bb9d6007 100644 --- a/changelogs/drizzle-seed/0.2.0.md +++ b/changelogs/drizzle-seed/0.2.0.md @@ -3,7 +3,7 @@ We are introducing a new parameter, `version`, to the `seed` function options. This parameter, which controls generator versioning, has been added to make it easier to update deterministic generators in the future. Since values should remain consistent after each regeneration, it is crucial to provide a well-designed API for gradual updates ```ts -await seed(db, schema, { version: 2 }); +await seed(db, schema, { version: '2' }); ``` #### Example: @@ -121,3 +121,6 @@ However, after inserting the `1 minute 60 second` interval, PostgreSQL database **Generating strings based on text-like column length:** Now (in version 2), the maximum length of a string depends on the length of the text column (e.g., `varchar(20)`). + +## Bug fixes +- seeding table with foreign key referencing another table, without including the second table in schema, will cause seed process to get stuck. \ No newline at end of file diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index 48aad2973..de99eb746 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -10,7 +10,7 @@ import { getTableConfig as getPgTableConfig, PgDatabase, PgTable } from 'drizzle import type { SQLiteColumn } from 'drizzle-orm/sqlite-core'; import { BaseSQLiteDatabase, getTableConfig as getSqliteTableConfig, SQLiteTable } from 'drizzle-orm/sqlite-core'; -import { generatorsFuncs } from './services/GeneratorFuncs.ts'; +import { generatorsFuncs, generatorsFuncsV2 } from './services/GeneratorFuncs.ts'; import type { AbstractGenerator } from './services/Generators.ts'; import { SeedService } from './services/SeedService.ts'; import type { DrizzleStudioObjectType, DrizzleStudioRelationType } from './types/drizzleStudio.ts'; @@ -131,6 +131,7 @@ class SeedPromise< SCHEMA extends { [key: string]: PgTable | PgSchema | MySqlTable | MySqlSchema | SQLiteTable; }, + VERSION extends string | undefined, > implements Promise { static readonly entityKind: string = 'SeedPromise'; @@ -139,7 +140,7 @@ class SeedPromise< constructor( private db: DB, private schema: SCHEMA, - private options?: { count?: number; seed?: number; version?: number }, + private options?: { count?: number; seed?: number; version?: VERSION }, ) {} then( @@ -181,14 +182,23 @@ class SeedPromise< } async refine( - callback: (funcs: typeof generatorsFuncs) => InferCallbackType, + callback: ( + funcs: FunctionsVersioning, + ) => InferCallbackType, ): Promise { - const refinements = callback(generatorsFuncs) as RefinementsType; + const refinements = this.options?.version === undefined || this.options.version === '2' + ? callback(generatorsFuncsV2 as FunctionsVersioning) as RefinementsType + : callback(generatorsFuncs as FunctionsVersioning) as RefinementsType; + // const refinements = callback(generatorsFuncs) as RefinementsType; await seedFunc(this.db, this.schema, this.options, refinements); } } +type FunctionsVersioning = VERSION extends `1` ? typeof generatorsFuncs + : VERSION extends `2` ? typeof generatorsFuncsV2 + : typeof generatorsFuncsV2; + export function getGeneratorsFunctions() { return generatorsFuncs; } @@ -337,8 +347,9 @@ export function seed< | SQLiteTable | any; }, ->(db: DB, schema: SCHEMA, options?: { count?: number; seed?: number; version?: number }) { - return new SeedPromise(db, schema, options); + VERSION extends '2' | '1' | undefined, +>(db: DB, schema: SCHEMA, options?: { count?: number; seed?: number; version?: VERSION }) { + return new SeedPromise(db, schema, options); } const seedFunc = async ( @@ -352,21 +363,26 @@ const seedFunc = async ( | SQLiteTable | any; }, - options: { count?: number; seed?: number; version?: number } = {}, + options: { count?: number; seed?: number; version?: string } = {}, refinements?: RefinementsType, ) => { + let version: number | undefined; + if (options?.version !== undefined) { + version = Number(options?.version); + } + if (is(db, PgDatabase)) { const { pgSchema } = filterPgTables(schema); - await seedPostgres(db, pgSchema, options, refinements); + await seedPostgres(db, pgSchema, { ...options, version }, refinements); } else if (is(db, MySqlDatabase)) { const { mySqlSchema } = filterMySqlTables(schema); - await seedMySql(db, mySqlSchema, options, refinements); + await seedMySql(db, mySqlSchema, { ...options, version }, refinements); } else if (is(db, BaseSQLiteDatabase)) { const { sqliteSchema } = filterSqliteTables(schema); - await seedSqlite(db, sqliteSchema, options, refinements); + await seedSqlite(db, sqliteSchema, { ...options, version }, refinements); } else { throw new Error( 'The drizzle-seed package currently supports only PostgreSQL, MySQL, and SQLite databases. Please ensure your database is one of these supported types', diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts index be715a33b..f0687b5ed 100644 --- a/drizzle-seed/src/services/GeneratorFuncs.ts +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -756,9 +756,10 @@ export const generatorsFuncs = { weightedRandom: createGenerator(WeightedRandomGenerator), }; -// TODO: revise // so far, version changes don’t affect generator parameters. -export const generatorsFuncsV2 = { ...generatorsFuncs }; +export const generatorsFuncsV2 = { + ...generatorsFuncs, +}; export const generatorsMap = { HollowGenerator: [ diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 35cba442f..6c045f368 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -42,7 +42,7 @@ export class SeedService { let tablePossibleGenerators: Prettify; const customSeed = options?.seed === undefined ? 0 : options.seed; this.version = options?.version === undefined ? latestVersion : options.version; - if (this.version < 1 || this.version > latestVersion) { + if (Number.isNaN(this.version) || this.version < 1 || this.version > latestVersion) { throw new Error(`Version should be in range [1, ${latestVersion}].`); } @@ -209,9 +209,23 @@ export class SeedService { if (cyclicRelation !== undefined) { columnPossibleGenerator.isCyclic = true; } - const predicate = cyclicRelation !== undefined && col.notNull === false; + + if (foreignKeyColumns[col.name]?.table === undefined && col.notNull === true) { + throw new Error( + `Column '${col.name}' cannot be nullable, and you didn't specify a table for foreign key on column '${col.name}' in '${table.name}' table.`, + ); + } + + const predicate = (cyclicRelation !== undefined || foreignKeyColumns[col.name]?.table === undefined) + && col.notNull === false; if (predicate === true) { + if (foreignKeyColumns[col.name]?.table === undefined && col.notNull === false) { + console.warn( + `Column '${col.name}' in '${table.name}' table will be filled with null values` + + `\nbecause you specified neither a table for foreign key on column '${col.name}' nor a function for '${col.name}' column in refinements.`, + ); + } columnPossibleGenerator.generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: null }); columnPossibleGenerator.wasDefinedBefore = true; } else { @@ -417,7 +431,10 @@ export class SeedService { }; } - if (tablesInOutRelations[rel.refTable] === undefined) { + if ( + rel.refTable !== undefined + && tablesInOutRelations[rel.refTable] === undefined + ) { tablesInOutRelations[rel.refTable] = { out: 0, in: 0, @@ -428,13 +445,15 @@ export class SeedService { }; } - tablesInOutRelations[rel.table]!.out += 1; - tablesInOutRelations[rel.refTable]!.in += 1; + if (rel.refTable !== undefined) { + tablesInOutRelations[rel.table]!.out += 1; + tablesInOutRelations[rel.refTable]!.in += 1; + } if (rel.refTable === rel.table) { tablesInOutRelations[rel.table]!.selfRelation = true; tablesInOutRelations[rel.table]!.selfRelCount = rel.columns.length; - } else { + } else if (rel.refTable !== undefined) { tablesInOutRelations[rel.table]!.requiredTableNames.add(rel.refTable); tablesInOutRelations[rel.refTable]!.dependantTableNames.add(rel.table); } From 280720064f258b8cd83b5592deece91e5e2bc7e7 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Mon, 23 Dec 2024 16:04:08 +0200 Subject: [PATCH 14/18] fixes --- drizzle-seed/src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index de99eb746..e3fc5494a 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -587,10 +587,7 @@ const getPostgresInfo = (schema: { [key: string]: PgTable }) => { // might be empty list const newRelations = tableConfig.foreignKeys.map((fk) => { const table = dbToTsTableNamesMap[tableConfig.name] as string; - const refTableName0 = fk.reference(); - const refTableName1 = refTableName0.foreignTable; - const refTableName2 = getTableName(refTableName1); - const refTable = dbToTsTableNamesMap[refTableName2] as string; + const refTable = dbToTsTableNamesMap[getTableName(fk.reference().foreignTable)] as string; const dbToTsColumnNamesMapForRefTable = getDbToTsColumnNamesMap( fk.reference().foreignTable, From 0db3d1334263f6badc4ed9d9fd9a127ba0f90fad Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Mon, 23 Dec 2024 17:32:10 +0200 Subject: [PATCH 15/18] fixes --- drizzle-seed/src/index.ts | 1 - drizzle-seed/src/services/GeneratorFuncs.ts | 4 +- drizzle-seed/src/services/Generators.ts | 6 +- drizzle-seed/src/services/SeedService.ts | 129 ++++++++++---------- 4 files changed, 70 insertions(+), 70 deletions(-) diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index e3fc5494a..98c449095 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -189,7 +189,6 @@ class SeedPromise< const refinements = this.options?.version === undefined || this.options.version === '2' ? callback(generatorsFuncsV2 as FunctionsVersioning) as RefinementsType : callback(generatorsFuncs as FunctionsVersioning) as RefinementsType; - // const refinements = callback(generatorsFuncs) as RefinementsType; await seedFunc(this.db, this.schema, this.options, refinements); } diff --git a/drizzle-seed/src/services/GeneratorFuncs.ts b/drizzle-seed/src/services/GeneratorFuncs.ts index f0687b5ed..10d0d10f7 100644 --- a/drizzle-seed/src/services/GeneratorFuncs.ts +++ b/drizzle-seed/src/services/GeneratorFuncs.ts @@ -54,7 +54,7 @@ import { import { GenerateStringV2, GenerateUniqueIntervalV2, GenerateUniqueStringV2 } from './versioning/v2.ts'; function createGenerator, T>( - generatorConstructor: new(params: T) => GeneratorType, + generatorConstructor: new(params?: T) => GeneratorType, ) { return ( ...args: GeneratorType extends GenerateValuesFromArray | GenerateDefault | WeightedRandomGenerator ? [T] @@ -915,4 +915,4 @@ export const generatorsMap = { GenerateWeightedCount: [ GenerateWeightedCount, ], -}; +} as const; diff --git a/drizzle-seed/src/services/Generators.ts b/drizzle-seed/src/services/Generators.ts index fff543739..0d285540e 100644 --- a/drizzle-seed/src/services/Generators.ts +++ b/drizzle-seed/src/services/Generators.ts @@ -38,9 +38,11 @@ export abstract class AbstractGenerator { public weightedCountSeed?: number | undefined; public maxRepeatedValuesCount?: number | { weight: number; count: number | number[] }[] | undefined; - // param for GenerateIntP + public params: T; - constructor(public params: T) {} + constructor(params?: T) { + this.params = params === undefined ? {} as T : params as T; + } init(params: { count: number | { weight: number; count: number | number[] }[]; seed: number }): void; init() { diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 6c045f368..84165169e 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -212,7 +212,7 @@ export class SeedService { if (foreignKeyColumns[col.name]?.table === undefined && col.notNull === true) { throw new Error( - `Column '${col.name}' cannot be nullable, and you didn't specify a table for foreign key on column '${col.name}' in '${table.name}' table.`, + `Column '${col.name}' has no null contraint, and you didn't specify a table for foreign key on column '${col.name}' in '${table.name}' table. You should pass `, ); } @@ -222,14 +222,14 @@ export class SeedService { if (predicate === true) { if (foreignKeyColumns[col.name]?.table === undefined && col.notNull === false) { console.warn( - `Column '${col.name}' in '${table.name}' table will be filled with null values` + `Column '${col.name}' in '${table.name}' table will be filled with Null values` + `\nbecause you specified neither a table for foreign key on column '${col.name}' nor a function for '${col.name}' column in refinements.`, ); } - columnPossibleGenerator.generator = new generatorsMap.GenerateDefault[0]!({ defaultValue: null }); + columnPossibleGenerator.generator = new generatorsMap.GenerateDefault[0]({ defaultValue: null }); columnPossibleGenerator.wasDefinedBefore = true; } else { - columnPossibleGenerator.generator = new generatorsMap.HollowGenerator[0]!({}); + columnPossibleGenerator.generator = new generatorsMap.HollowGenerator[0](); } } // TODO: rewrite pickGeneratorFor... using new col properties: isUnique and notNull else if (connectionType === 'postgresql') { @@ -294,13 +294,12 @@ export class SeedService { (generator as GenerateArray).params.baseColumnGen = newBaseColumnGen; } - let possibleGeneratorConstructors = - (generatorsMap as { [entityKind: string]: (typeof AbstractGenerator)[] })[entityKind]; + const possibleGeneratorConstructors = generatorsMap[entityKind as keyof typeof generatorsMap]; - possibleGeneratorConstructors = possibleGeneratorConstructors?.filter((possGenCon) => + const possibleGeneratorConstructorsFiltered = possibleGeneratorConstructors?.filter((possGenCon) => possGenCon.version <= this.version! // sorting in ascending order by version ).sort((a, b) => a.version - b.version); - const generatorConstructor = possibleGeneratorConstructors?.at(-1) as + const generatorConstructor = possibleGeneratorConstructorsFiltered?.at(-1) as | (new(params: any) => AbstractGenerator) | undefined; if (generatorConstructor === undefined) { @@ -467,7 +466,7 @@ export class SeedService { count: number, seed: number, ) => { - let gen = new generatorsMap.GenerateWeightedCount[0]!({}); + let gen = new generatorsMap.GenerateWeightedCount[0](); gen = this.selectVersionOfGenerator(gen) as GenerateWeightedCount; // const gen = new GenerateWeightedCount({}); gen.init({ count: weightedCount, seed }); @@ -504,7 +503,7 @@ export class SeedService { // }; // const baseColumnDataType = getBaseColumnDataType(col.baseColumn); - const generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen, size: col.size }); + const generator = new generatorsMap.GenerateArray[0]({ baseColumnGen, size: col.size }); // generator.baseColumnDataType = baseColumnDataType; return generator; @@ -524,10 +523,10 @@ export class SeedService { throw new Error(`column with type ${col.baseColumn!.columnType} is not supported for now.`); } - let generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen }); + let generator = new generatorsMap.GenerateArray[0]({ baseColumnGen }); for (let i = 0; i < col.typeParams.dimensions! - 1; i++) { - generator = new generatorsMap.GenerateArray[0]!({ baseColumnGen: generator }); + generator = new generatorsMap.GenerateArray[0]({ baseColumnGen: generator }); } return generator; @@ -541,7 +540,7 @@ export class SeedService { || col.columnType.includes('bigint')) && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0](); return generator; } @@ -589,7 +588,7 @@ export class SeedService { && !col.columnType.includes('interval') && !col.columnType.includes('point') ) { - const generator = new generatorsMap.GenerateInt[0]!({ + const generator = new generatorsMap.GenerateInt[0]({ minValue, maxValue, }); @@ -598,7 +597,7 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0](); generator.maxValue = maxValue; @@ -617,14 +616,14 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new generatorsMap.GenerateNumber[0]!({ + const generator = new generatorsMap.GenerateNumber[0]({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), }); return generator; } - const generator = new generatorsMap.GenerateNumber[0]!({}); + const generator = new generatorsMap.GenerateNumber[0](); return generator; } @@ -636,7 +635,7 @@ export class SeedService { || col.columnType.startsWith('char')) && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateUniqueString[0]!({}); + const generator = new generatorsMap.GenerateUniqueString[0](); return generator; } @@ -647,7 +646,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('name') ) { - const generator = new generatorsMap.GenerateFirstName[0]!({}); + const generator = new generatorsMap.GenerateFirstName[0](); return generator; } @@ -658,7 +657,7 @@ export class SeedService { || col.columnType.startsWith('char')) && col.name.toLowerCase().includes('email') ) { - const generator = new generatorsMap.GenerateEmail[0]!({}); + const generator = new generatorsMap.GenerateEmail[0](); return generator; } @@ -668,47 +667,47 @@ export class SeedService { || col.columnType.startsWith('varchar') || col.columnType.startsWith('char') ) { - const generator = new generatorsMap.GenerateString[0]!({}); + const generator = new generatorsMap.GenerateString[0](); return generator; } // UUID if (col.columnType === 'uuid') { - const generator = new generatorsMap.GenerateUUID[0]!({}); + const generator = new generatorsMap.GenerateUUID[0](); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new generatorsMap.GenerateBoolean[0]!({}); + const generator = new generatorsMap.GenerateBoolean[0](); return generator; } // DATE, TIME, TIMESTAMP if (col.columnType.includes('date')) { - const generator = new generatorsMap.GenerateDate[0]!({}); + const generator = new generatorsMap.GenerateDate[0](); return generator; } if (col.columnType === 'time') { - const generator = new generatorsMap.GenerateTime[0]!({}); + const generator = new generatorsMap.GenerateTime[0](); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new generatorsMap.GenerateTimestamp[0]!({}); + const generator = new generatorsMap.GenerateTimestamp[0](); return generator; } // JSON, JSONB if (col.columnType === 'json' || col.columnType === 'jsonb') { - const generator = new generatorsMap.GenerateJson[0]!({}); + const generator = new generatorsMap.GenerateJson[0](); return generator; } @@ -720,7 +719,7 @@ export class SeedService { // ENUM if (col.enumValues !== undefined) { - const generator = new generatorsMap.GenerateEnum[0]!({ + const generator = new generatorsMap.GenerateEnum[0]({ enumValues: col.enumValues, }); @@ -730,32 +729,32 @@ export class SeedService { // INTERVAL if (col.columnType.startsWith('interval')) { if (col.columnType === 'interval') { - const generator = new generatorsMap.GenerateInterval[0]!({}); + const generator = new generatorsMap.GenerateInterval[0](); return generator; } const fields = col.columnType.replace('interval ', '') as GenerateInterval['params']['fields']; - const generator = new generatorsMap.GenerateInterval[0]!({ fields }); + const generator = new generatorsMap.GenerateInterval[0]({ fields }); return generator; } // POINT, LINE if (col.columnType.includes('point')) { - const generator = new generatorsMap.GeneratePoint[0]!({}); + const generator = new generatorsMap.GeneratePoint[0](); return generator; } if (col.columnType.includes('line')) { - const generator = new generatorsMap.GenerateLine[0]!({}); + const generator = new generatorsMap.GenerateLine[0](); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new generatorsMap.GenerateDefault[0]!({ + const generator = new generatorsMap.GenerateDefault[0]({ defaultValue: col.default, }); return generator; @@ -784,7 +783,7 @@ export class SeedService { (col.columnType.includes('serial') || col.columnType.includes('int')) && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0](); return generator; } @@ -819,7 +818,7 @@ export class SeedService { } if (col.columnType.includes('int')) { - const generator = new generatorsMap.GenerateInt[0]!({ + const generator = new generatorsMap.GenerateInt[0]({ minValue, maxValue, }); @@ -827,7 +826,7 @@ export class SeedService { } if (col.columnType.includes('serial')) { - const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0](); generator.maxValue = maxValue; return generator; } @@ -845,7 +844,7 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new generatorsMap.GenerateNumber[0]!({ + const generator = new generatorsMap.GenerateNumber[0]({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), @@ -853,7 +852,7 @@ export class SeedService { return generator; } - const generator = new generatorsMap.GenerateNumber[0]!({}); + const generator = new generatorsMap.GenerateNumber[0](); return generator; } @@ -867,7 +866,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateUniqueString[0]!({}); + const generator = new generatorsMap.GenerateUniqueString[0](); return generator; } @@ -880,7 +879,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('name') ) { - const generator = new generatorsMap.GenerateFirstName[0]!({}); + const generator = new generatorsMap.GenerateFirstName[0](); return generator; } @@ -893,7 +892,7 @@ export class SeedService { || col.columnType.startsWith('varbinary')) && col.name.toLowerCase().includes('email') ) { - const generator = new generatorsMap.GenerateEmail[0]!({}); + const generator = new generatorsMap.GenerateEmail[0](); return generator; } @@ -905,58 +904,58 @@ export class SeedService { || col.columnType.startsWith('binary') || col.columnType.startsWith('varbinary') ) { - const generator = new generatorsMap.GenerateString[0]!({}); + const generator = new generatorsMap.GenerateString[0](); return generator; } // BOOLEAN if (col.columnType === 'boolean') { - const generator = new generatorsMap.GenerateBoolean[0]!({}); + const generator = new generatorsMap.GenerateBoolean[0](); return generator; } // DATE, TIME, TIMESTAMP, DATETIME, YEAR if (col.columnType.includes('datetime')) { - const generator = new generatorsMap.GenerateDatetime[0]!({}); + const generator = new generatorsMap.GenerateDatetime[0](); return generator; } if (col.columnType.includes('date')) { - const generator = new generatorsMap.GenerateDate[0]!({}); + const generator = new generatorsMap.GenerateDate[0](); return generator; } if (col.columnType === 'time') { - const generator = new generatorsMap.GenerateTime[0]!({}); + const generator = new generatorsMap.GenerateTime[0](); return generator; } if (col.columnType.includes('timestamp')) { - const generator = new generatorsMap.GenerateTimestamp[0]!({}); + const generator = new generatorsMap.GenerateTimestamp[0](); return generator; } if (col.columnType === 'year') { - const generator = new generatorsMap.GenerateYear[0]!({}); + const generator = new generatorsMap.GenerateYear[0](); return generator; } // JSON if (col.columnType === 'json') { - const generator = new generatorsMap.GenerateJson[0]!({}); + const generator = new generatorsMap.GenerateJson[0](); return generator; } // ENUM if (col.enumValues !== undefined) { - const generator = new generatorsMap.GenerateEnum[0]!({ + const generator = new generatorsMap.GenerateEnum[0]({ enumValues: col.enumValues, }); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new generatorsMap.GenerateDefault[0]!({ + const generator = new generatorsMap.GenerateDefault[0]({ defaultValue: col.default, }); return generator; @@ -980,17 +979,17 @@ export class SeedService { (col.columnType === 'integer' || col.columnType === 'numeric') && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateIntPrimaryKey[0]!({}); + const generator = new generatorsMap.GenerateIntPrimaryKey[0](); return generator; } if (col.columnType === 'integer' && col.dataType === 'boolean') { - const generator = new generatorsMap.GenerateBoolean[0]!({}); + const generator = new generatorsMap.GenerateBoolean[0](); return generator; } if ((col.columnType === 'integer' && col.dataType === 'date')) { - const generator = new generatorsMap.GenerateTimestamp[0]!({}); + const generator = new generatorsMap.GenerateTimestamp[0](); return generator; } @@ -998,7 +997,7 @@ export class SeedService { col.columnType === 'integer' || (col.dataType === 'bigint' && col.columnType === 'blob') ) { - const generator = new generatorsMap.GenerateInt[0]!({}); + const generator = new generatorsMap.GenerateInt[0](); return generator; } @@ -1009,7 +1008,7 @@ export class SeedService { const scale = col.typeParams.scale === undefined ? 0 : col.typeParams.scale; const maxAbsoluteValue = Math.pow(10, precision - scale) - Math.pow(10, -scale); - const generator = new generatorsMap.GenerateNumber[0]!({ + const generator = new generatorsMap.GenerateNumber[0]({ minValue: -maxAbsoluteValue, maxValue: maxAbsoluteValue, precision: Math.pow(10, scale), @@ -1017,7 +1016,7 @@ export class SeedService { return generator; } - const generator = new generatorsMap.GenerateNumber[0]!({}); + const generator = new generatorsMap.GenerateNumber[0](); return generator; } @@ -1028,7 +1027,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && table.primaryKeys.includes(col.name) ) { - const generator = new generatorsMap.GenerateUniqueString[0]!({}); + const generator = new generatorsMap.GenerateUniqueString[0](); return generator; } @@ -1038,7 +1037,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('name') ) { - const generator = new generatorsMap.GenerateFirstName[0]!({}); + const generator = new generatorsMap.GenerateFirstName[0](); return generator; } @@ -1048,7 +1047,7 @@ export class SeedService { || col.columnType.startsWith('blob')) && col.name.toLowerCase().includes('email') ) { - const generator = new generatorsMap.GenerateEmail[0]!({}); + const generator = new generatorsMap.GenerateEmail[0](); return generator; } @@ -1058,7 +1057,7 @@ export class SeedService { || col.columnType.startsWith('blob') || col.columnType.startsWith('blobbuffer') ) { - const generator = new generatorsMap.GenerateString[0]!({}); + const generator = new generatorsMap.GenerateString[0](); return generator; } @@ -1066,12 +1065,12 @@ export class SeedService { (col.columnType.startsWith('text') && col.dataType === 'json') || (col.columnType.startsWith('blob') && col.dataType === 'json') ) { - const generator = new generatorsMap.GenerateJson[0]!({}); + const generator = new generatorsMap.GenerateJson[0](); return generator; } if (col.hasDefault && col.default !== undefined) { - const generator = new generatorsMap.GenerateDefault[0]!({ + const generator = new generatorsMap.GenerateDefault[0]({ defaultValue: col.default, }); return generator; @@ -1239,7 +1238,7 @@ export class SeedService { }))!.map((rows) => rows[refColName]) as (string | number | boolean)[]; hasSelfRelation = true; - genObj = new generatorsMap.GenerateSelfRelationsValuesFromArray[0]!({ + genObj = new generatorsMap.GenerateSelfRelationsValuesFromArray[0]({ values: refColumnValues, }); genObj = this.selectVersionOfGenerator(genObj); @@ -1264,7 +1263,7 @@ export class SeedService { } // TODO: revise maybe need to select version of generator here too - genObj = new generatorsMap.GenerateValuesFromArray[0]!({ values: refColumnValues }); + genObj = new generatorsMap.GenerateValuesFromArray[0]({ values: refColumnValues }); genObj.notNull = tableGenerators[rel.columns[colIdx]!]!.notNull; genObj.weightedCountSeed = weightedCountSeed; genObj.maxRepeatedValuesCount = repeatedValuesCount; From fedc800b2c66504562c2bd8247185929a8434636 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 25 Dec 2024 12:44:54 +0200 Subject: [PATCH 16/18] fixes --- .gitignore | 2 ++ drizzle-seed/src/index.ts | 31 ++++++++++++++++++++-- drizzle-seed/src/services/SeedService.ts | 4 ++- drizzle-seed/src/services/versioning/v2.ts | 4 +-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 570a706f8..c266f115f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ rollup.config-*.mjs *.log .DS_Store drizzle-seed/src/test.ts +drizzle-seed/src/testMysql.ts +drizzle-seed/src/testSqlite.ts drizzle-seed/src/schemaTest.ts \ No newline at end of file diff --git a/drizzle-seed/src/index.ts b/drizzle-seed/src/index.ts index 98c449095..c73e497cb 100644 --- a/drizzle-seed/src/index.ts +++ b/drizzle-seed/src/index.ts @@ -927,7 +927,8 @@ const getMySqlInfo = (schema: { [key: string]: MySqlTable }) => { typeParams['scale'] = Number(match[2]); } } else if ( - sqlType.startsWith('varchar') + sqlType.startsWith('char') + || sqlType.startsWith('varchar') || sqlType.startsWith('binary') || sqlType.startsWith('varbinary') ) { @@ -1129,12 +1130,38 @@ const getSqliteInfo = (schema: { [key: string]: SQLiteTable }) => { } tableRelations[dbToTsTableNamesMap[tableConfig.name] as string]!.push(...newRelations); + const getTypeParams = (sqlType: string) => { + // get type params and set only type + const typeParams: Column['typeParams'] = {}; + + if ( + sqlType.startsWith('decimal') + ) { + const match = sqlType.match(/\((\d+), *(\d+)\)/); + if (match) { + typeParams['precision'] = Number(match[1]); + typeParams['scale'] = Number(match[2]); + } + } else if ( + sqlType.startsWith('char') + || sqlType.startsWith('varchar') + || sqlType.startsWith('text') + ) { + const match = sqlType.match(/\((\d+)\)/); + if (match) { + typeParams['length'] = Number(match[1]); + } + } + + return typeParams; + }; + tables.push({ name: dbToTsTableNamesMap[tableConfig.name] as string, columns: tableConfig.columns.map((column) => ({ name: dbToTsColumnNamesMap[column.name] as string, columnType: column.getSQLType(), - typeParams: {}, + typeParams: getTypeParams(column.getSQLType()), dataType: column.dataType, hasDefault: column.hasDefault, default: column.default, diff --git a/drizzle-seed/src/services/SeedService.ts b/drizzle-seed/src/services/SeedService.ts index 84165169e..1370138b4 100644 --- a/drizzle-seed/src/services/SeedService.ts +++ b/drizzle-seed/src/services/SeedService.ts @@ -260,6 +260,7 @@ export class SeedService { columnPossibleGenerator.generator = arrayGen; } + columnPossibleGenerator.generator.isUnique = col.isUnique; const uniqueGen = columnPossibleGenerator.generator.replaceIfUnique(); if (uniqueGen !== undefined) { columnPossibleGenerator.generator = uniqueGen; @@ -268,7 +269,6 @@ export class SeedService { // selecting version of generator columnPossibleGenerator.generator = this.selectVersionOfGenerator(columnPossibleGenerator.generator); - columnPossibleGenerator.generator.isUnique = col.isUnique; // TODO: for now only GenerateValuesFromArray support notNull property columnPossibleGenerator.generator.notNull = col.notNull; columnPossibleGenerator.generator.dataType = col.dataType; @@ -309,6 +309,8 @@ export class SeedService { const newGenerator = new generatorConstructor(generator.params); newGenerator.baseColumnDataType = generator.baseColumnDataType; newGenerator.isUnique = generator.isUnique; + // TODO: for now only GenerateValuesFromArray support notNull property + newGenerator.notNull = generator.notNull; newGenerator.dataType = generator.dataType; newGenerator.stringLength = generator.stringLength; diff --git a/drizzle-seed/src/services/versioning/v2.ts b/drizzle-seed/src/services/versioning/v2.ts index e09cc45f6..f4dbf32f4 100644 --- a/drizzle-seed/src/services/versioning/v2.ts +++ b/drizzle-seed/src/services/versioning/v2.ts @@ -126,7 +126,7 @@ export class GenerateStringV2 extends AbstractGenerator<{ override init({ count, seed }: { count: number; seed: number }) { super.init({ count, seed }); - let minStringLength = 8; + let minStringLength = 7; let maxStringLength = 20; if (this.stringLength !== undefined) { maxStringLength = this.stringLength; @@ -182,7 +182,7 @@ export class GenerateUniqueStringV2 extends AbstractGenerator<{ isUnique?: boole override init({ seed, count }: { seed: number; count: number }) { const rng = prand.xoroshiro128plus(seed); - let minStringLength = 8; + let minStringLength = 7; let maxStringLength = 20; // TODO: revise later if (this.stringLength !== undefined) { From fa9f4b7acaeacc6f2eaa9fb19a5bd1c007bfe283 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 25 Dec 2024 12:53:05 +0200 Subject: [PATCH 17/18] updated changelogs --- changelogs/drizzle-seed/0.2.0.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelogs/drizzle-seed/0.2.0.md b/changelogs/drizzle-seed/0.2.0.md index 5bb9d6007..bd2a5260e 100644 --- a/changelogs/drizzle-seed/0.2.0.md +++ b/changelogs/drizzle-seed/0.2.0.md @@ -123,4 +123,5 @@ However, after inserting the `1 minute 60 second` interval, PostgreSQL database Now (in version 2), the maximum length of a string depends on the length of the text column (e.g., `varchar(20)`). ## Bug fixes -- seeding table with foreign key referencing another table, without including the second table in schema, will cause seed process to get stuck. \ No newline at end of file +- seeding table with foreign key referencing another table, without including the second table in schema, will cause seed process to get stuck. +- not setting unique generators for unique database columns. \ No newline at end of file From 6854ac6c5fda8d34f2ea9980fed24ceb6a3b04d4 Mon Sep 17 00:00:00 2001 From: OleksiiKH0240 Date: Wed, 25 Dec 2024 13:17:33 +0200 Subject: [PATCH 18/18] fix --- changelogs/drizzle-seed/0.2.0.md | 1 - 1 file changed, 1 deletion(-) diff --git a/changelogs/drizzle-seed/0.2.0.md b/changelogs/drizzle-seed/0.2.0.md index 6c8573ee4..0978c7a98 100644 --- a/changelogs/drizzle-seed/0.2.0.md +++ b/changelogs/drizzle-seed/0.2.0.md @@ -159,7 +159,6 @@ export const table = p.sqliteTable('table', { await seed(db, { table }) ``` -Functions from refine that will be affected by this change: `` ## Bug fixes - Seeding a table with a foreign key referencing another table, without including the second table in the schema, will cause the seeding process to get stuck