From 3ade5cf7825a0cdf400dfd37d46b077193391f4e Mon Sep 17 00:00:00 2001 From: Umed Khudoiberdiev Date: Sat, 27 Aug 2016 13:25:44 +0500 Subject: [PATCH] added table schema generation --- README.md | 4 +- package.json | 4 +- sample/sample16-indexes/app.ts | 3 + src/schema-builder/MysqlSchemaBuilder.ts | 214 ++++++++++++++------ src/schema-builder/PostgresSchemaBuilder.ts | 7 +- src/schema-builder/SchemaBuilder.ts | 2 + src/schema-creator/ColumnSchema.ts | 11 + src/schema-creator/ForeignKeySchema.ts | 9 + src/schema-creator/IndexSchema.ts | 11 + src/schema-creator/PrimaryKeySchema.ts | 9 + src/schema-creator/SchemaCreator.ts | 11 + src/schema-creator/TableSchema.ts | 20 ++ src/schema-creator/UniqueKeySchema.ts | 9 + 13 files changed, 245 insertions(+), 69 deletions(-) create mode 100644 src/schema-creator/ColumnSchema.ts create mode 100644 src/schema-creator/ForeignKeySchema.ts create mode 100644 src/schema-creator/IndexSchema.ts create mode 100644 src/schema-creator/PrimaryKeySchema.ts create mode 100644 src/schema-creator/TableSchema.ts create mode 100644 src/schema-creator/UniqueKeySchema.ts diff --git a/README.md b/README.md index 929a95816..78ec8f039 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TypeORM - -[![Join the chat at https://gitter.im/pleerock/typeorm](https://badges.gitter.im/pleerock/typeorm.svg)](https://gitter.im/pleerock/typeorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[![Join the chat at https://gitter.im/pleerock/typeorm](https://badges.gitter.im/pleerock/typeorm.svg)](https://gitter.im/pleerock/typeorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) > Note: docs are not always up to date because orm is in active development. > Samples are more up to date, try to find your questions there. diff --git a/package.json b/package.json index 48ad9c37e..b4e4cc396 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ }, "repository": { "type": "git", - "url": "https://github.com/pleerock/typeorm.git" + "url": "https://github.com/typeorm/typeorm.git" }, "bugs": { - "url": "https://github.com/pleerock/typeorm/issues" + "url": "https://github.com/typeorm/typeorm/issues" }, "tags": [ "orm", diff --git a/sample/sample16-indexes/app.ts b/sample/sample16-indexes/app.ts index 9a1528713..35d0070b2 100644 --- a/sample/sample16-indexes/app.ts +++ b/sample/sample16-indexes/app.ts @@ -11,6 +11,9 @@ const options: ConnectionOptions = { password: "admin", database: "test" }, + logging: { + logOnlyFailedQueries: true + }, autoSchemaCreate: true, entities: [Post] }; diff --git a/src/schema-builder/MysqlSchemaBuilder.ts b/src/schema-builder/MysqlSchemaBuilder.ts index 83374bc2a..57c1e665f 100644 --- a/src/schema-builder/MysqlSchemaBuilder.ts +++ b/src/schema-builder/MysqlSchemaBuilder.ts @@ -7,6 +7,12 @@ import {IndexMetadata} from "../metadata/IndexMetadata"; import {DatabaseConnection} from "../driver/DatabaseConnection"; import {ObjectLiteral} from "../common/ObjectLiteral"; import {DataTypeNotSupportedByDriverError} from "./error/DataTypeNotSupportedByDriverError"; +import {TableSchema} from "../schema-creator/TableSchema"; +import {ColumnSchema} from "../schema-creator/ColumnSchema"; +import {PrimaryKeySchema} from "../schema-creator/PrimaryKeySchema"; +import {ForeignKeySchema} from "../schema-creator/ForeignKeySchema"; +import {IndexSchema} from "../schema-creator/IndexSchema"; +import {UniqueKeySchema} from "../schema-creator/UniqueKeySchema"; /** * @internal @@ -18,6 +24,84 @@ export class MysqlSchemaBuilder extends SchemaBuilder { super(); } + async loadSchemaTables(tableNames: string[]): Promise { + + // if no tables given then no need to proceed + if (!tableNames) + return []; + + // load tables, columns, indices and foreign keys + const tablesString = tableNames.map(tableName => "'" + tableName + "'").join(","); + const tablesSql = `SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME IN (${tablesString})`; + const columnsSql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME IN (${tablesString})`; + const indicesSql = `SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME IN (${tablesString})`; + const foreignKeysSql = `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = '${this.dbName}' AND REFERENCED_COLUMN_NAME IS NOT NULL`; + const uniqueKeysSql = `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = '${this.dbName}' AND CONSTRAINT_TYPE = 'UNIQUE'`; + const primaryKeysSql = `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = '${this.dbName}' AND CONSTRAINT_TYPE = 'PRIMARY KEY'`; + const [dbTables, dbColumns, dbIndices, dbForeignKeys, dbUniqueKeys, primaryKeys]: ObjectLiteral[][] = await Promise.all([ + this.query(tablesSql), + this.query(columnsSql), + this.query(indicesSql), + this.query(foreignKeysSql), + this.query(uniqueKeysSql), + this.query(primaryKeysSql), + ]); + + // if tables were not found in the db, no need to proceed + if (!dbTables.length) + return []; + + // create table schemas for loaded tables + return dbTables.map(dbTable => { + const tableSchema = new TableSchema(dbTable["TABLE_NAME"]); + + // create column schemas from the loaded columns + tableSchema.columns = dbColumns + .filter(dbColumn => dbColumn["TABLE_NAME"] === tableSchema.name) + .map(dbColumn => { + const columnSchema = new ColumnSchema(); + columnSchema.name = dbColumn["COLUMN_NAME"]; + columnSchema.type = dbColumn["COLUMN_TYPE"].toLowerCase(); // todo: use normalize type? + columnSchema.default = dbColumn["COLUMN_DEFAULT"]; + columnSchema.isNullable = dbColumn["IS_NULLABLE"] === "YES"; + columnSchema.isPrimary = dbColumn["COLUMN_KEY"].indexOf("PRI") !== -1; + columnSchema.isGenerated = dbColumn["EXTRA"].indexOf("auto_increment") !== -1; + columnSchema.comment = dbColumn["COLUMN_COMMENT"]; + return columnSchema; + }); + + // create primary key schema + const primaryKey = primaryKeys.find(primaryKey => primaryKey["TABLE_NAME"] === tableSchema.name); + if (primaryKey) + tableSchema.primaryKey = new PrimaryKeySchema(primaryKey["CONSTRAINT_NAME"]); + + // create foreign key schemas from the loaded indices + tableSchema.foreignKeys = dbForeignKeys + .filter(dbForeignKey => dbForeignKey["TABLE_NAME"] === tableSchema.name) + .map(dbForeignKey => new ForeignKeySchema(dbForeignKey["CONSTRAINT_NAME"])); + + // create unique key schemas from the loaded indices + tableSchema.uniqueKeys = dbUniqueKeys + .filter(dbUniqueKey => dbUniqueKey["TABLE_NAME"] === tableSchema.name) + .map(dbUniqueKey => new UniqueKeySchema(dbUniqueKey["CONSTRAINT_NAME"])); + + // create index schemas from the loaded indices + tableSchema.indices = dbIndices + .filter(dbIndex => dbIndex["TABLE_NAME"] === tableSchema.name) + .map(dbIndex => dbIndex["INDEX_NAME"]) + .filter((value, index, self) => self.indexOf(value) === index) // unqiue + .map(dbIndexName => { + const columnNames = dbIndices + .filter(dbIndex => dbIndex["TABLE_NAME"] === tableSchema.name && dbIndex["INDEX_NAME"] === dbIndexName) + .map(dbIndex => dbIndex["COLUMN_NAME"]); + + return new IndexSchema(dbIndexName, columnNames); + }); + + return tableSchema; + }); + } + /*async getColumnProperties(tableName: string, columnName: string): Promise<{ isNullable: boolean, columnType: string, autoIncrement: boolean }|undefined> { const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}'` + ` AND TABLE_NAME = '${tableName}' AND COLUMN_NAME = '${columnName}'`; @@ -33,6 +117,71 @@ export class MysqlSchemaBuilder extends SchemaBuilder { }; }*/ + async addColumnQuery(tableName: string, column: ColumnMetadata): Promise { + const sql = `ALTER TABLE \`${tableName}\` ADD ${this.buildCreateColumnSql(column, false)}`; + await this.query(sql); + } + + async dropColumnQuery(tableName: string, columnName: string): Promise { + const sql = `ALTER TABLE \`${tableName}\` DROP \`${columnName}\``; + await this.query(sql); + } + + async addForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise { + const columnNames = foreignKey.columnNames.map(column => "`" + column + "`").join(", "); + const referencedColumnNames = foreignKey.referencedColumnNames.map(column => "`" + column + "`").join(","); + let sql = `ALTER TABLE ${foreignKey.tableName} ADD CONSTRAINT \`${foreignKey.name}\` ` + + `FOREIGN KEY (${columnNames}) ` + + `REFERENCES \`${foreignKey.referencedTable.name}\`(${referencedColumnNames})`; + if (foreignKey.onDelete) sql += " ON DELETE " + foreignKey.onDelete; + await this.query(sql); + } + + async dropForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise; + async dropForeignKeyQuery(tableName: string, foreignKeyName: string): Promise; + async dropForeignKeyQuery(tableNameOrForeignKey: string|ForeignKeyMetadata, foreignKeyName?: string): Promise { + let tableName = tableNameOrForeignKey; + if (tableNameOrForeignKey instanceof ForeignKeyMetadata) { + tableName = tableNameOrForeignKey.tableName; + foreignKeyName = tableNameOrForeignKey.name; + } + + const sql = `ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${foreignKeyName}\``; + await this.query(sql); + } + + async dropIndex(tableName: string, indexName: string): Promise { + const sql = `ALTER TABLE \`${tableName}\` DROP INDEX \`${indexName}\``; + await this.query(sql); + } + + async createIndex(tableName: string, index: IndexMetadata): Promise { + const columns = index.columns.map(column => "`" + column + "`").join(", "); + const sql = `CREATE ${index.isUnique ? "UNIQUE" : ""} INDEX \`${index.name}\` ON \`${tableName}\`(${columns})`; + await this.query(sql); + } + + async addUniqueKey(tableName: string, columnName: string, keyName: string): Promise { + const sql = `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${keyName}\` UNIQUE (\`${columnName}\`)`; + await this.query(sql); + } + + async renameColumnQuery(tableName: string, oldColumn: DatabaseColumnProperties, newColumn: ColumnMetadata): Promise { + const sql = `ALTER TABLE \`${tableName}\` CHANGE \`${oldColumn.name}\` \`${newColumn.name}\` ${oldColumn.type}`; + await this.query(sql); + } + + async changeColumnQuery(tableName: string, oldColumn: DatabaseColumnProperties, newColumn: ColumnMetadata): Promise { + const sql = `ALTER TABLE \`${tableName}\` CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(newColumn, oldColumn.hasPrimaryKey)}`; // todo: CHANGE OR MODIFY COLUMN ???? + await this.query(sql); + } + + async createTableQuery(table: TableMetadata, columns: ColumnMetadata[]): Promise { + const columnDefinitions = columns.map(column => this.buildCreateColumnSql(column, false)).join(", "); + const sql = `CREATE TABLE \`${table.name}\` (${columnDefinitions}) ENGINE=InnoDB;`; + await this.query(sql); + } + /** * todo: reuse getColumns */ @@ -54,7 +203,7 @@ export class MysqlSchemaBuilder extends SchemaBuilder { dbData.IS_NULLABLE !== isNullable || hasDbColumnAutoIncrement !== column.isGenerated || hasDbColumnPrimaryIndex !== column.isPrimary; - + }).map(column => { const dbData = results.find(result => result.COLUMN_NAME === column.name); const hasDbColumnPrimaryIndex = dbData.COLUMN_KEY.indexOf("PRI") !== -1; @@ -73,38 +222,6 @@ export class MysqlSchemaBuilder extends SchemaBuilder { return this.query(sql).then(results => !!(results && results.length)); } - addColumnQuery(tableName: string, column: ColumnMetadata): Promise { - const sql = `ALTER TABLE ${tableName} ADD ${this.buildCreateColumnSql(column, false)}`; - return this.query(sql).then(() => {}); - } - - dropColumnQuery(tableName: string, columnName: string): Promise { - const sql = `ALTER TABLE ${tableName} DROP ${columnName}`; - return this.query(sql).then(() => {}); - } - - addForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise { - let sql = `ALTER TABLE ${foreignKey.tableName} ADD CONSTRAINT \`${foreignKey.name}\` ` + - `FOREIGN KEY (${foreignKey.columnNames.join(", ")}) ` + - `REFERENCES ${foreignKey.referencedTable.name}(${foreignKey.referencedColumnNames.join(",")})`; - if (foreignKey.onDelete) - sql += " ON DELETE " + foreignKey.onDelete; - return this.query(sql).then(() => {}); - } - - dropForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise; - dropForeignKeyQuery(tableName: string, foreignKeyName: string): Promise; - dropForeignKeyQuery(tableNameOrForeignKey: string|ForeignKeyMetadata, foreignKeyName?: string): Promise { - let tableName = tableNameOrForeignKey; - if (tableNameOrForeignKey instanceof ForeignKeyMetadata) { - tableName = tableNameOrForeignKey.tableName; - foreignKeyName = tableNameOrForeignKey.name; - } - - const sql = `ALTER TABLE ${tableName} DROP FOREIGN KEY \`${foreignKeyName}\``; - return this.query(sql).then(() => {}); - } - getTableForeignQuery(tableName: string): Promise { const sql = `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = "${this.dbName}" ` + `AND TABLE_NAME = "${tableName}" AND REFERENCED_COLUMN_NAME IS NOT NULL`; @@ -140,21 +257,6 @@ export class MysqlSchemaBuilder extends SchemaBuilder { return this.query(sql).then(results => results && results.length ? results[0].CONSTRAINT_NAME : undefined); } - dropIndex(tableName: string, indexName: string): Promise { - const sql = `ALTER TABLE ${tableName} DROP INDEX \`${indexName}\``; - return this.query(sql).then(() => {}); - } - - createIndex(tableName: string, index: IndexMetadata): Promise { - const sql = `CREATE ${index.isUnique ? "UNIQUE" : ""} INDEX \`${index.name}\` ON ${tableName}(${index.columns.join(", ")})`; - return this.query(sql).then(() => {}); - } - - addUniqueKey(tableName: string, columnName: string, keyName: string): Promise { - const sql = `ALTER TABLE ${tableName} ADD CONSTRAINT ${keyName} UNIQUE (${columnName})`; - return this.query(sql).then(() => {}); - } - getTableColumns(tableName: string): Promise { const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${tableName}'`; return this.query(sql).then((results: any[]) => { @@ -170,22 +272,6 @@ export class MysqlSchemaBuilder extends SchemaBuilder { }); } - renameColumnQuery(tableName: string, oldColumn: DatabaseColumnProperties, newColumn: ColumnMetadata): Promise { - const sql = `ALTER TABLE ${tableName} CHANGE ${oldColumn.name} ${newColumn.name} ${oldColumn.type}`; - return this.query(sql).then(() => {}); - } - - changeColumnQuery(tableName: string, oldColumn: DatabaseColumnProperties, newColumn: ColumnMetadata): Promise { - const sql = `ALTER TABLE ${tableName} CHANGE ${oldColumn.name} ${this.buildCreateColumnSql(newColumn, oldColumn.hasPrimaryKey)}`; // todo: CHANGE OR MODIFY COLUMN ???? - return this.query(sql).then(() => {}); - } - - createTableQuery(table: TableMetadata, columns: ColumnMetadata[]): Promise { - const columnDefinitions = columns.map(column => this.buildCreateColumnSql(column, false)).join(", "); - const sql = `CREATE TABLE \`${table.name}\` (${columnDefinitions}) ENGINE=InnoDB;`; - return this.query(sql).then(() => {}); - } - // ------------------------------------------------------------------------- // Private Methods // ------------------------------------------------------------------------- diff --git a/src/schema-builder/PostgresSchemaBuilder.ts b/src/schema-builder/PostgresSchemaBuilder.ts index 218dcca51..21f14e3a6 100644 --- a/src/schema-builder/PostgresSchemaBuilder.ts +++ b/src/schema-builder/PostgresSchemaBuilder.ts @@ -7,6 +7,7 @@ import {IndexMetadata} from "../metadata/IndexMetadata"; import {DatabaseConnection} from "../driver/DatabaseConnection"; import {ObjectLiteral} from "../common/ObjectLiteral"; import {DataTypeNotSupportedByDriverError} from "./error/DataTypeNotSupportedByDriverError"; +import {TableSchema} from "../schema-creator/TableSchema"; /** * @internal @@ -17,7 +18,11 @@ export class PostgresSchemaBuilder extends SchemaBuilder { private dbConnection: DatabaseConnection) { super(); } - + + async loadSchemaTables(): Promise { + return Promise.resolve([]); + } + async getChangedColumns(tableName: string, columns: ColumnMetadata[]): Promise { const dbColumns = await this.getTableColumns(tableName); return dbColumns.filter(dbColumn => { diff --git a/src/schema-builder/SchemaBuilder.ts b/src/schema-builder/SchemaBuilder.ts index 67b64298c..29e545e08 100644 --- a/src/schema-builder/SchemaBuilder.ts +++ b/src/schema-builder/SchemaBuilder.ts @@ -2,6 +2,7 @@ import {ColumnMetadata} from "../metadata/ColumnMetadata"; import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata"; import {TableMetadata} from "../metadata/TableMetadata"; import {IndexMetadata} from "../metadata/IndexMetadata"; +import {TableSchema} from "../schema-creator/TableSchema"; export interface DatabaseColumnProperties { name: string; @@ -17,6 +18,7 @@ export interface DatabaseColumnProperties { */ export abstract class SchemaBuilder { + abstract loadSchemaTables(tableNames: string[]): Promise; // abstract getColumnProperties(tableName: string, columnName: string): Promise<{ isNullable: boolean, columnType: string, autoIncrement: boolean }|undefined>; abstract getChangedColumns(tableName: string, columns: ColumnMetadata[]): Promise; abstract checkIfTableExist(tableName: string): Promise; diff --git a/src/schema-creator/ColumnSchema.ts b/src/schema-creator/ColumnSchema.ts new file mode 100644 index 000000000..684936a53 --- /dev/null +++ b/src/schema-creator/ColumnSchema.ts @@ -0,0 +1,11 @@ +export class ColumnSchema { + + name: string; + type: string; + default: string; + isNullable: boolean; + isGenerated: boolean; + isPrimary: boolean; + comment: string|undefined; + +} \ No newline at end of file diff --git a/src/schema-creator/ForeignKeySchema.ts b/src/schema-creator/ForeignKeySchema.ts new file mode 100644 index 000000000..87712d64b --- /dev/null +++ b/src/schema-creator/ForeignKeySchema.ts @@ -0,0 +1,9 @@ +export class ForeignKeySchema { + + name: string; + + constructor(name: string) { + this.name = name; + } + +} \ No newline at end of file diff --git a/src/schema-creator/IndexSchema.ts b/src/schema-creator/IndexSchema.ts new file mode 100644 index 000000000..c00898517 --- /dev/null +++ b/src/schema-creator/IndexSchema.ts @@ -0,0 +1,11 @@ +export class IndexSchema { + + name: string; + columnNames: string[]; + + constructor(name: string, columnNames: string[]) { + this.name = name; + this.columnNames = columnNames; + } + +} \ No newline at end of file diff --git a/src/schema-creator/PrimaryKeySchema.ts b/src/schema-creator/PrimaryKeySchema.ts new file mode 100644 index 000000000..24e8c979b --- /dev/null +++ b/src/schema-creator/PrimaryKeySchema.ts @@ -0,0 +1,9 @@ +export class PrimaryKeySchema { + + name: string; + + constructor(name: string) { + this.name = name; + } + +} \ No newline at end of file diff --git a/src/schema-creator/SchemaCreator.ts b/src/schema-creator/SchemaCreator.ts index d44d472d5..75fb5c64b 100644 --- a/src/schema-creator/SchemaCreator.ts +++ b/src/schema-creator/SchemaCreator.ts @@ -5,6 +5,7 @@ import {EntityMetadata} from "../metadata/EntityMetadata"; import {SchemaBuilder} from "../schema-builder/SchemaBuilder"; import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection"; import {IndexMetadata} from "../metadata/IndexMetadata"; +import {TableSchema} from "./TableSchema"; /** * Creates indexes based on the given metadata. @@ -45,6 +46,8 @@ export class SchemaCreator { */ async create(): Promise { const metadatas = this.entityMetadatas; + const tableSchemas = await this.loadSchemaTables(metadatas); + console.log(tableSchemas); await this.dropForeignKeysForAll(metadatas); await this.createTablesForAll(metadatas); await this.updateOldColumnsForAll(metadatas); @@ -61,6 +64,14 @@ export class SchemaCreator { // Private Methods // ------------------------------------------------------------------------- + /** + * Loads all table schemas from the database. + */ + private loadSchemaTables(metadatas: EntityMetadata[]): Promise { + const tableNames = metadatas.map(metadata => metadata.table.name); + return this.schemaBuilder.loadSchemaTables(tableNames); + } + private dropForeignKeysForAll(metadatas: EntityMetadata[]) { return Promise.all(metadatas.map(metadata => this.dropForeignKeys(metadata.table, metadata.foreignKeys))); } diff --git a/src/schema-creator/TableSchema.ts b/src/schema-creator/TableSchema.ts new file mode 100644 index 000000000..47e9238ec --- /dev/null +++ b/src/schema-creator/TableSchema.ts @@ -0,0 +1,20 @@ +import {ColumnSchema} from "./ColumnSchema"; +import {IndexSchema} from "./IndexSchema"; +import {ForeignKeySchema} from "./ForeignKeySchema"; +import {UniqueKeySchema} from "./UniqueKeySchema"; +import {PrimaryKeySchema} from "./PrimaryKeySchema"; + +export class TableSchema { + + name: string; + columns: ColumnSchema[] = []; + indices: IndexSchema[] = []; + foreignKeys: ForeignKeySchema[] = []; + uniqueKeys: UniqueKeySchema[] = []; + primaryKey: PrimaryKeySchema; + + constructor(name: string) { + this.name = name; + } + +} \ No newline at end of file diff --git a/src/schema-creator/UniqueKeySchema.ts b/src/schema-creator/UniqueKeySchema.ts new file mode 100644 index 000000000..40c0ae7ef --- /dev/null +++ b/src/schema-creator/UniqueKeySchema.ts @@ -0,0 +1,9 @@ +export class UniqueKeySchema { + + name: string; + + constructor(name: string) { + this.name = name; + } + +} \ No newline at end of file