added support for MariaDB; formating in some files
This commit is contained in:
parent
4bb989e8a9
commit
694025ceca
@ -16,6 +16,7 @@ env:
|
||||
- MSSQL_Skip=0 MSSQL_Host=localhost MSSQL_Port=1433 MSSQL_Username=sa MSSQL_Password=!Passw0rd MSSQL_Database=typeorm_mg
|
||||
- POSTGRES_Skip=0 POSTGRES_Host=localhost POSTGRES_Port=5432 POSTGRES_Username=postgres POSTGRES_Password=!Passw0rd POSTGRES_Database=typeorm_mg
|
||||
- MYSQL_Skip=0 MYSQL_Host=localhost MYSQL_Port=3306 MYSQL_Username=root MYSQL_Password=!Passw0rd MYSQL_Database=typeorm_mg
|
||||
- MARIADB_Skip=0 MARIADB_Host=localhost MARIADB_Port=3307 MARIADB_Username=root MARIADB_Password=!Passw0rd MARIADB_Database=typeorm_mg
|
||||
|
||||
before_install:
|
||||
- sudo service mysql stop
|
||||
@ -23,9 +24,11 @@ before_install:
|
||||
- docker pull microsoft/mssql-server-linux
|
||||
- docker pull postgres
|
||||
- docker pull mysql
|
||||
- docker pull mariadb
|
||||
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=!Passw0rd' -p 1433:1433 --name mssql -d microsoft/mssql-server-linux
|
||||
- docker run -e 'POSTGRES_PASSWORD=!Passw0rd' -p 5432:5432 --name postgres -d postgres
|
||||
- docker run -e MYSQL_ROOT_PASSWORD=!Passw0rd -p 3306:3306 --name mysql -d mysql
|
||||
- docker run -e MYSQL_ROOT_PASSWORD=!Passw0rd -p 3307:3306 --name mariadb -d mariadb
|
||||
|
||||
|
||||
before_script:
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { MssqlDriver } from './drivers/MssqlDriver'
|
||||
import { AbstractDriver } from "./drivers/AbstractDriver";
|
||||
import { DatabaseModel } from './models/DatabaseModel'
|
||||
import * as Handlebars from 'handlebars'
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {EntityInfo} from './../models/EntityInfo'
|
||||
import {DatabaseModel} from './../models/DatabaseModel'
|
||||
import { EntityInfo } from './../models/EntityInfo'
|
||||
import { DatabaseModel } from './../models/DatabaseModel'
|
||||
/**
|
||||
* AbstractDriver
|
||||
*/
|
||||
export abstract class AbstractDriver {
|
||||
async GetDataFromServer(database:string,server:string,port:number,user:string,password:string): Promise<DatabaseModel> {
|
||||
let dbModel=<DatabaseModel>{};
|
||||
await this.ConnectToServer(database,server,port,user,password);
|
||||
async GetDataFromServer(database: string, server: string, port: number, user: string, password: string): Promise<DatabaseModel> {
|
||||
let dbModel = <DatabaseModel>{};
|
||||
await this.ConnectToServer(database, server, port, user, password);
|
||||
dbModel.entities = await this.GetAllTables();
|
||||
await this.GetCoulmnsFromEntity(dbModel.entities);
|
||||
await this.GetIndexesFromEntity(dbModel.entities);
|
||||
@ -15,16 +15,16 @@ export abstract class AbstractDriver {
|
||||
this.FindPrimaryColumnsFromIndexes(dbModel)
|
||||
return dbModel;
|
||||
}
|
||||
abstract async ConnectToServer(database:string,server:string,port:number,user:string,password:string);
|
||||
abstract async ConnectToServer(database: string, server: string, port: number, user: string, password: string);
|
||||
abstract async GetAllTables(): Promise<EntityInfo[]>
|
||||
abstract async GetCoulmnsFromEntity(entities: EntityInfo[]):Promise<EntityInfo[]>;
|
||||
abstract async GetIndexesFromEntity(entities: EntityInfo[]):Promise<EntityInfo[]>;
|
||||
abstract async GetRelations(entities: EntityInfo[]):Promise<EntityInfo[]>;
|
||||
abstract async FindPrimaryColumnsFromIndexes(dbModel:DatabaseModel);
|
||||
abstract async GetCoulmnsFromEntity(entities: EntityInfo[]): Promise<EntityInfo[]>;
|
||||
abstract async GetIndexesFromEntity(entities: EntityInfo[]): Promise<EntityInfo[]>;
|
||||
abstract async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]>;
|
||||
abstract async FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel);
|
||||
abstract async DisconnectFromServer();
|
||||
|
||||
abstract async CreateDB(dbName:string);
|
||||
abstract async DropDB(dbName:string);
|
||||
abstract async UseDB(dbName:string);
|
||||
abstract async CheckIfDBExists(dbName:string):Promise<boolean>;
|
||||
abstract async CreateDB(dbName: string);
|
||||
abstract async DropDB(dbName: string);
|
||||
abstract async UseDB(dbName: string);
|
||||
abstract async CheckIfDBExists(dbName: string): Promise<boolean>;
|
||||
}
|
407
src/drivers/MariaDbDriver.ts
Normal file
407
src/drivers/MariaDbDriver.ts
Normal file
@ -0,0 +1,407 @@
|
||||
import { AbstractDriver } from './AbstractDriver'
|
||||
import * as MariaDb from 'mysql'
|
||||
import { ColumnInfo } from './../models/ColumnInfo'
|
||||
import { EntityInfo } from './../models/EntityInfo'
|
||||
import { RelationInfo } from './../models/RelationInfo'
|
||||
import { DatabaseModel } from './../models/DatabaseModel'
|
||||
/**
|
||||
* MariaDb
|
||||
*/
|
||||
export class MariaDbDriver extends AbstractDriver {
|
||||
FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel) {
|
||||
dbModel.entities.forEach(entity => {
|
||||
let primaryIndex = entity.Indexes.find(v => v.isPrimaryKey);
|
||||
if (!primaryIndex) {
|
||||
console.error(`Table ${entity.EntityName} has no PK.`)
|
||||
return;
|
||||
}
|
||||
entity.Columns.forEach(col => {
|
||||
if (primaryIndex!.columns.some(cIndex => cIndex.name == col.name)) col.isPrimary = true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async GetAllTables(): Promise<EntityInfo[]> {
|
||||
|
||||
let response = await this.ExecQuery<{ TABLE_SCHEMA: string, TABLE_NAME: string }>(`SELECT TABLE_SCHEMA, TABLE_NAME
|
||||
FROM information_schema.tables
|
||||
WHERE table_type='BASE TABLE'
|
||||
AND table_schema like DATABASE()`);
|
||||
let ret: EntityInfo[] = <EntityInfo[]>[];
|
||||
response.forEach((val) => {
|
||||
let ent: EntityInfo = new EntityInfo();
|
||||
ent.EntityName = val.TABLE_NAME;
|
||||
ent.Columns = <ColumnInfo[]>[];
|
||||
ent.Indexes = <IndexInfo[]>[];
|
||||
ret.push(ent);
|
||||
})
|
||||
return ret;
|
||||
}
|
||||
async GetCoulmnsFromEntity(entities: EntityInfo[]): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
TABLE_NAME: string, COLUMN_NAME: string, COLUMN_DEFAULT: string,
|
||||
IS_NULLABLE: string, DATA_TYPE: string, CHARACTER_MAXIMUM_LENGTH: number,
|
||||
NUMERIC_PRECISION: number, NUMERIC_SCALE: number, IsIdentity: number
|
||||
}>(`SELECT TABLE_NAME,COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,
|
||||
DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,
|
||||
CASE WHEN EXTRA like '%auto_increment%' THEN 1 ELSE 0 END IsIdentity FROM INFORMATION_SCHEMA.COLUMNS
|
||||
where TABLE_SCHEMA like DATABASE()`);
|
||||
entities.forEach((ent) => {
|
||||
response.filter((filterVal) => {
|
||||
return filterVal.TABLE_NAME == ent.EntityName;
|
||||
}).forEach((resp) => {
|
||||
let colInfo: ColumnInfo = new ColumnInfo();
|
||||
colInfo.name = resp.COLUMN_NAME;
|
||||
colInfo.is_nullable = resp.IS_NULLABLE == 'YES' ? true : false;
|
||||
colInfo.is_generated = resp.IsIdentity == 1 ? true : false;
|
||||
colInfo.default = resp.COLUMN_DEFAULT;
|
||||
switch (resp.DATA_TYPE) {
|
||||
case "int":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "int"
|
||||
break;
|
||||
case "tinyint":
|
||||
if (resp.NUMERIC_PRECISION == 3) {
|
||||
colInfo.ts_type = "boolean"
|
||||
colInfo.sql_type = "boolean"
|
||||
} else {
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "smallint"
|
||||
}
|
||||
break;
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "smallint"
|
||||
break;
|
||||
case "bit":
|
||||
colInfo.ts_type = "boolean"
|
||||
colInfo.sql_type = "boolean"
|
||||
break;
|
||||
case "float":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "float"
|
||||
break;
|
||||
case "bigint":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "bigint"
|
||||
break;
|
||||
case "date":
|
||||
colInfo.ts_type = "Date"
|
||||
colInfo.sql_type = "date"
|
||||
break;
|
||||
case "time":
|
||||
colInfo.ts_type = "Date"
|
||||
colInfo.sql_type = "time"
|
||||
break;
|
||||
case "datetime":
|
||||
colInfo.ts_type = "Date";
|
||||
colInfo.sql_type = "datetime"
|
||||
break;
|
||||
case "char":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "nchar":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "text":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "ntext":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "varchar":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "string"
|
||||
break;
|
||||
case "nvarchar":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "string"
|
||||
break;
|
||||
case "money":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "decimal"
|
||||
break;
|
||||
case "real":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "double"
|
||||
break;
|
||||
case "double":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "double"
|
||||
break;
|
||||
case "decimal":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "decimal"
|
||||
colInfo.numericPrecision = resp.NUMERIC_PRECISION
|
||||
colInfo.numericScale = resp.NUMERIC_SCALE
|
||||
break;
|
||||
case "xml":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown column type:" + resp.DATA_TYPE);
|
||||
break;
|
||||
}
|
||||
colInfo.char_max_lenght = resp.CHARACTER_MAXIMUM_LENGTH > 0 ? resp.CHARACTER_MAXIMUM_LENGTH : null;
|
||||
if (colInfo.sql_type) ent.Columns.push(colInfo);
|
||||
})
|
||||
})
|
||||
return entities;
|
||||
}
|
||||
async GetIndexesFromEntity(entities: EntityInfo[]): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
TableName: string, IndexName: string, ColumnName: string, is_unique: number,
|
||||
is_primary_key: number//, is_descending_key: number//, is_included_column: number
|
||||
}>(`SELECT TABLE_NAME TableName,INDEX_NAME IndexName,COLUMN_NAME ColumnName,CASE WHEN NON_UNIQUE=0 THEN 1 ELSE 0 END is_unique,
|
||||
CASE WHEN INDEX_NAME='PRIMARY' THEN 1 ELSE 0 END is_primary_key
|
||||
FROM information_schema.statistics sta
|
||||
WHERE table_schema like DATABASE();
|
||||
`);
|
||||
entities.forEach((ent) => {
|
||||
response.filter((filterVal) => {
|
||||
return filterVal.TableName == ent.EntityName;
|
||||
}).forEach((resp) => {
|
||||
let indexInfo: IndexInfo = <IndexInfo>{};
|
||||
let indexColumnInfo: IndexColumnInfo = <IndexColumnInfo>{};
|
||||
if (ent.Indexes.filter((filterVal) => {
|
||||
return filterVal.name == resp.IndexName
|
||||
}).length > 0) {
|
||||
indexInfo = ent.Indexes.filter((filterVal) => {
|
||||
return filterVal.name == resp.IndexName
|
||||
})[0];
|
||||
} else {
|
||||
indexInfo.columns = <IndexColumnInfo[]>[];
|
||||
indexInfo.name = resp.IndexName;
|
||||
indexInfo.isUnique = resp.is_unique == 1 ? true : false;
|
||||
indexInfo.isPrimaryKey = resp.is_primary_key == 1 ? true : false;
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.ColumnName;
|
||||
// indexColumnInfo.isIncludedColumn = resp.is_included_column == 1 ? true : false;
|
||||
// indexColumnInfo.isDescending = resp.is_descending_key == 1 ? true : false;
|
||||
indexInfo.columns.push(indexColumnInfo);
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]> {
|
||||
let response = await this.ExecQuery<{
|
||||
TableWithForeignKey: string, FK_PartNo: number, ForeignKeyColumn: string,
|
||||
TableReferenced: string, ForeignKeyColumnReferenced: string,
|
||||
onDelete: "RESTRICT" | "CASCADE" | "SET NULL",
|
||||
onUpdate: "RESTRICT" | "CASCADE" | "SET NULL", object_id: string
|
||||
}>(`SELECT
|
||||
CU.TABLE_NAME TableWithForeignKey,
|
||||
CU.ORDINAL_POSITION FK_PartNo,
|
||||
CU.COLUMN_NAME ForeignKeyColumn,
|
||||
CU.REFERENCED_TABLE_NAME TableReferenced,
|
||||
CU.REFERENCED_COLUMN_NAME ForeignKeyColumnReferenced,
|
||||
RC.DELETE_RULE onDelete,
|
||||
RC.UPDATE_RULE onUpdate,
|
||||
CU.CONSTRAINT_NAME object_id
|
||||
FROM
|
||||
INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
|
||||
JOIN
|
||||
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON CU.CONSTRAINT_NAME=RC.CONSTRAINT_NAME
|
||||
WHERE
|
||||
TABLE_SCHEMA = SCHEMA()
|
||||
AND CU.REFERENCED_TABLE_NAME IS NOT NULL;
|
||||
`);
|
||||
let relationsTemp: RelationTempInfo[] = <RelationTempInfo[]>[];
|
||||
response.forEach((resp) => {
|
||||
let rels = relationsTemp.find((val) => {
|
||||
return val.object_id == resp.object_id;
|
||||
})
|
||||
if (rels == undefined) {
|
||||
rels = <RelationTempInfo>{};
|
||||
rels.ownerColumnsNames = [];
|
||||
rels.referencedColumnsNames = [];
|
||||
rels.actionOnDelete = resp.onDelete;
|
||||
rels.actionOnUpdate = resp.onUpdate;
|
||||
rels.object_id = resp.object_id;
|
||||
rels.ownerTable = resp.TableWithForeignKey;
|
||||
rels.referencedTable = resp.TableReferenced;
|
||||
relationsTemp.push(rels);
|
||||
}
|
||||
rels.ownerColumnsNames.push(resp.ForeignKeyColumn);
|
||||
rels.referencedColumnsNames.push(resp.ForeignKeyColumnReferenced);
|
||||
})
|
||||
relationsTemp.forEach((relationTmp) => {
|
||||
let ownerEntity = entities.find((entitity) => {
|
||||
return entitity.EntityName == relationTmp.ownerTable;
|
||||
})
|
||||
if (!ownerEntity) {
|
||||
console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.ownerTable}.`)
|
||||
return;
|
||||
}
|
||||
let referencedEntity = entities.find((entitity) => {
|
||||
return entitity.EntityName == relationTmp.referencedTable;
|
||||
})
|
||||
if (!referencedEntity) {
|
||||
console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity model ${relationTmp.referencedTable}.`)
|
||||
return;
|
||||
}
|
||||
let ownerColumn = ownerEntity.Columns.find((column) => {
|
||||
return column.name == relationTmp.ownerColumnsNames[0];
|
||||
})
|
||||
if (!ownerColumn) {
|
||||
console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.ownerTable}.${ownerColumn}.`)
|
||||
return;
|
||||
}
|
||||
let relatedColumn = referencedEntity.Columns.find((column) => {
|
||||
return column.name == relationTmp.referencedColumnsNames[0];
|
||||
})
|
||||
if (!relatedColumn) {
|
||||
console.error(`Relation between tables ${relationTmp.ownerTable} and ${relationTmp.referencedTable} didn't found entity column ${relationTmp.referencedTable}.${relatedColumn}.`)
|
||||
return;
|
||||
}
|
||||
let ownColumn: ColumnInfo = ownerColumn;
|
||||
let isOneToMany: boolean;
|
||||
isOneToMany = false;
|
||||
let index = ownerEntity.Indexes.find(
|
||||
(index) => {
|
||||
return index.isUnique && index.columns.some(col => {
|
||||
return col.name == ownerColumn!.name
|
||||
})
|
||||
}
|
||||
)
|
||||
if (!index) {
|
||||
isOneToMany = true;
|
||||
} else {
|
||||
isOneToMany = false;
|
||||
}
|
||||
let ownerRelation = new RelationInfo()
|
||||
ownerRelation.actionOnDelete = relationTmp.actionOnDelete
|
||||
ownerRelation.actionOnUpdate = relationTmp.actionOnUpdate
|
||||
ownerRelation.isOwner = true
|
||||
ownerRelation.relatedColumn = relatedColumn.name.toLowerCase()
|
||||
ownerRelation.relatedTable = relationTmp.referencedTable
|
||||
ownerRelation.ownerTable = relationTmp.ownerTable
|
||||
ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase() + (isOneToMany ? 's' : '')
|
||||
ownerRelation.relationType = isOneToMany ? "ManyToOne" : "OneToOne"
|
||||
ownerColumn.relations.push(ownerRelation)
|
||||
if (isOneToMany) {
|
||||
let col = new ColumnInfo()
|
||||
col.name = ownerEntity.EntityName.toLowerCase() + 's'
|
||||
let referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation)
|
||||
referencedRelation.actionOnDelete = relationTmp.actionOnDelete
|
||||
referencedRelation.actionOnUpdate = relationTmp.actionOnUpdate
|
||||
referencedRelation.isOwner = false
|
||||
referencedRelation.relatedColumn = ownerColumn.name
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.relationType = "OneToMany"
|
||||
referencedEntity.Columns.push(col)
|
||||
} else {
|
||||
let col = new ColumnInfo()
|
||||
col.name = ownerEntity.EntityName.toLowerCase()
|
||||
let referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation)
|
||||
referencedRelation.actionOnDelete = relationTmp.actionOnDelete
|
||||
referencedRelation.actionOnUpdate = relationTmp.actionOnUpdate
|
||||
referencedRelation.isOwner = false
|
||||
referencedRelation.relatedColumn = ownerColumn.name
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.relationType = "OneToOne"
|
||||
|
||||
referencedEntity.Columns.push(col)
|
||||
}
|
||||
})
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => {
|
||||
this.Connection.end((err) => {
|
||||
if (!err) {
|
||||
//Connection successfull
|
||||
resolve(true)
|
||||
}
|
||||
else {
|
||||
console.error('Error disconnecting to MariaDb Server.')
|
||||
console.error(err.message)
|
||||
process.abort()
|
||||
reject(err)
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
if (this.Connection)
|
||||
await promise;
|
||||
|
||||
}
|
||||
|
||||
private Connection: MariaDb.IConnection;
|
||||
async ConnectToServer(database: string, server: string, port: number, user: string, password: string) {
|
||||
let config: MariaDb.IConnectionConfig = {
|
||||
database: database,
|
||||
host: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password,
|
||||
}
|
||||
|
||||
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => {
|
||||
this.Connection = MariaDb.createConnection(config)
|
||||
|
||||
this.Connection.connect((err) => {
|
||||
if (!err) {
|
||||
//Connection successfull
|
||||
resolve(true)
|
||||
}
|
||||
else {
|
||||
console.error('Error connecting to MariaDb Server.')
|
||||
console.error(err.message)
|
||||
process.abort()
|
||||
reject(err)
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
await promise;
|
||||
}
|
||||
async CreateDB(dbName: string) {
|
||||
let resp = await this.ExecQuery<any>(`CREATE DATABASE ${dbName}; `)
|
||||
}
|
||||
async UseDB(dbName: string) {
|
||||
let resp = await this.ExecQuery<any>(`USE ${dbName}; `)
|
||||
}
|
||||
async DropDB(dbName: string) {
|
||||
let resp = await this.ExecQuery<any>(`DROP DATABASE ${dbName}; `)
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let resp = await this.ExecQuery<any>(`SHOW DATABASES LIKE '${dbName}' `)
|
||||
return resp.length > 0;
|
||||
}
|
||||
async ExecQuery<T>(sql: string): Promise<Array<T>> {
|
||||
let ret: Array<T> = [];
|
||||
let that = this;
|
||||
let query = this.Connection.query(sql)
|
||||
let stream = query.stream({});
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => {
|
||||
stream.on('data',
|
||||
chunk => {
|
||||
ret.push(<T><any>chunk)
|
||||
});
|
||||
stream.on('end', () => resolve(true));
|
||||
})
|
||||
await promise;
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -189,13 +189,13 @@ ORDER BY
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.ColumnName;
|
||||
// indexColumnInfo.isIncludedColumn = resp.is_included_column == 1 ? true : false;
|
||||
// indexColumnInfo.isDescending = resp.is_descending_key == 1 ? true : false;
|
||||
// indexColumnInfo.isIncludedColumn = resp.is_included_column == 1 ? true : false;
|
||||
// indexColumnInfo.isDescending = resp.is_descending_key == 1 ? true : false;
|
||||
indexInfo.columns.push(indexColumnInfo);
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]> {
|
||||
@ -301,12 +301,12 @@ order by
|
||||
ownerRelation.relatedColumn = relatedColumn.name.toLowerCase()
|
||||
ownerRelation.relatedTable = relationTmp.referencedTable
|
||||
ownerRelation.ownerTable = relationTmp.ownerTable
|
||||
ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase()+(isOneToMany ? 's':'')
|
||||
ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase() + (isOneToMany ? 's' : '')
|
||||
ownerRelation.relationType = isOneToMany ? "ManyToOne" : "OneToOne"
|
||||
ownerColumn.relations.push(ownerRelation)
|
||||
if (isOneToMany) {
|
||||
let col = new ColumnInfo()
|
||||
col.name = ownerEntity.EntityName.toLowerCase() +'s'
|
||||
col.name = ownerEntity.EntityName.toLowerCase() + 's'
|
||||
let referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation)
|
||||
referencedRelation.actionOnDelete = relationTmp.actionOnDelete
|
||||
@ -329,7 +329,7 @@ order by
|
||||
referencedRelation.relatedColumn = ownerColumn.name
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.relationType = "OneToOne"
|
||||
|
||||
referencedEntity.Columns.push(col)
|
||||
@ -376,21 +376,21 @@ order by
|
||||
|
||||
await promise;
|
||||
}
|
||||
async CreateDB(dbName:string){
|
||||
async CreateDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp =await request.query(`CREATE DATABASE ${dbName}; `)
|
||||
let resp = await request.query(`CREATE DATABASE ${dbName}; `)
|
||||
}
|
||||
async UseDB(dbName:string){
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp =await request.query(`USE ${dbName}; `)
|
||||
}
|
||||
async DropDB(dbName:string){
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp =await request.query(`DROP DATABASE ${dbName}; `)
|
||||
}
|
||||
async CheckIfDBExists(dbName:string):Promise<boolean>{
|
||||
async UseDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp =await request.query(`SELECT name FROM master.sys.databases WHERE name = N'${dbName}' `)
|
||||
return resp.length>0;
|
||||
let resp = await request.query(`USE ${dbName}; `)
|
||||
}
|
||||
async DropDB(dbName: string) {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp = await request.query(`DROP DATABASE ${dbName}; `)
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let request = new MSSQL.Request(this.Connection);
|
||||
let resp = await request.query(`SELECT name FROM master.sys.databases WHERE name = N'${dbName}' `)
|
||||
return resp.length > 0;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { AbstractDriver } from './AbstractDriver'
|
||||
import * as PG from 'pg'
|
||||
|
||||
import { ColumnInfo } from './../models/ColumnInfo'
|
||||
import { EntityInfo } from './../models/EntityInfo'
|
||||
import { RelationInfo } from './../models/RelationInfo'
|
||||
@ -9,7 +8,7 @@ import { DatabaseModel } from './../models/DatabaseModel'
|
||||
* PostgresDriver
|
||||
*/
|
||||
export class PostgresDriver extends AbstractDriver {
|
||||
private Connection:PG.Client;
|
||||
private Connection: PG.Client;
|
||||
|
||||
FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel) {
|
||||
dbModel.entities.forEach(entity => {
|
||||
@ -25,10 +24,10 @@ export class PostgresDriver extends AbstractDriver {
|
||||
}
|
||||
|
||||
async GetAllTables(): Promise<EntityInfo[]> {
|
||||
|
||||
|
||||
let response: { table_schema: string, table_name: string }[]
|
||||
= (await this.Connection.query("SELECT table_schema,table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND table_schema = 'public' ")).rows;
|
||||
|
||||
|
||||
let ret: EntityInfo[] = <EntityInfo[]>[];
|
||||
response.forEach((val) => {
|
||||
let ent: EntityInfo = new EntityInfo();
|
||||
@ -58,22 +57,22 @@ export class PostgresDriver extends AbstractDriver {
|
||||
colInfo.name = resp.column_name;
|
||||
colInfo.is_nullable = resp.is_nullable == 'YES' ? true : false;
|
||||
colInfo.is_generated = resp.isidentity == 'YES' ? true : false;
|
||||
colInfo.default = colInfo.is_generated?'':resp.column_default;
|
||||
colInfo.default = colInfo.is_generated ? '' : resp.column_default;
|
||||
switch (resp.data_type) {
|
||||
//TODO:change types to postgres
|
||||
case "integer":
|
||||
case "integer":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "int"
|
||||
break;
|
||||
case "character varying":
|
||||
case "character varying":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "text":
|
||||
case "text":
|
||||
colInfo.ts_type = "string"
|
||||
colInfo.sql_type = "text"
|
||||
break;
|
||||
case "smallint":
|
||||
case "smallint":
|
||||
colInfo.ts_type = "number"
|
||||
colInfo.sql_type = "smallint"
|
||||
break;
|
||||
@ -181,8 +180,8 @@ export class PostgresDriver extends AbstractDriver {
|
||||
ent.Indexes.push(indexInfo);
|
||||
}
|
||||
indexColumnInfo.name = resp.columnname;
|
||||
if (resp.is_primary_key==0) {
|
||||
indexInfo.isPrimaryKey=false;
|
||||
if (resp.is_primary_key == 0) {
|
||||
indexInfo.isPrimaryKey = false;
|
||||
}
|
||||
// indexColumnInfo.isIncludedColumn = resp.is_included_column == 1 ? true : false;
|
||||
//indexColumnInfo.isDescending = resp.is_descending_key == 1 ? true : false;
|
||||
@ -190,7 +189,7 @@ export class PostgresDriver extends AbstractDriver {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return entities;
|
||||
}
|
||||
async GetRelations(entities: EntityInfo[]): Promise<EntityInfo[]> {
|
||||
@ -307,12 +306,12 @@ export class PostgresDriver extends AbstractDriver {
|
||||
ownerRelation.relatedColumn = relatedColumn.name.toLowerCase()
|
||||
ownerRelation.relatedTable = relationTmp.referencedTable
|
||||
ownerRelation.ownerTable = relationTmp.ownerTable
|
||||
ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase()+(isOneToMany ? 's':'')
|
||||
ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase() + (isOneToMany ? 's' : '')
|
||||
ownerRelation.relationType = isOneToMany ? "ManyToOne" : "OneToOne"
|
||||
ownerColumn.relations.push(ownerRelation)
|
||||
if (isOneToMany) {
|
||||
let col = new ColumnInfo()
|
||||
col.name = ownerEntity.EntityName.toLowerCase() +'s'
|
||||
col.name = ownerEntity.EntityName.toLowerCase() + 's'
|
||||
let referencedRelation = new RelationInfo();
|
||||
col.relations.push(referencedRelation)
|
||||
referencedRelation.actionondelete = relationTmp.actionOnDelete
|
||||
@ -335,7 +334,7 @@ export class PostgresDriver extends AbstractDriver {
|
||||
referencedRelation.relatedColumn = ownerColumn.name
|
||||
referencedRelation.relatedTable = relationTmp.ownerTable
|
||||
referencedRelation.ownerTable = relationTmp.referencedTable
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.ownerColumn = relatedColumn.name.toLowerCase()
|
||||
referencedRelation.relationType = "OneToOne"
|
||||
|
||||
referencedEntity.Columns.push(col)
|
||||
@ -344,36 +343,39 @@ export class PostgresDriver extends AbstractDriver {
|
||||
return entities;
|
||||
}
|
||||
async DisconnectFromServer() {
|
||||
if (this.Connection){
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => { this.Connection.end((err) => {
|
||||
if (!err) {
|
||||
//Connection successfull
|
||||
resolve(true)
|
||||
}
|
||||
else {
|
||||
console.error('Error connecting to Postgres Server.')
|
||||
console.error(err.message)
|
||||
process.abort()
|
||||
reject(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
await promise;
|
||||
if (this.Connection) {
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => {
|
||||
this.Connection.end((err) => {
|
||||
if (!err) {
|
||||
//Connection successfull
|
||||
resolve(true)
|
||||
}
|
||||
else {
|
||||
console.error('Error connecting to Postgres Server.')
|
||||
console.error(err.message)
|
||||
process.abort()
|
||||
reject(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
|
||||
async ConnectToServer(database: string, server: string, port: number, user: string, password: string) {
|
||||
this.Connection=new PG.Client({database: database,
|
||||
this.Connection = new PG.Client({
|
||||
database: database,
|
||||
host: server,
|
||||
port: port,
|
||||
user: user,
|
||||
password: password})
|
||||
|
||||
password: password
|
||||
})
|
||||
|
||||
|
||||
let promise = new Promise<boolean>(
|
||||
(resolve, reject) => {
|
||||
this.Connection.connect( (err) => {
|
||||
this.Connection.connect((err) => {
|
||||
if (!err) {
|
||||
//Connection successfull
|
||||
resolve(true)
|
||||
@ -391,18 +393,18 @@ export class PostgresDriver extends AbstractDriver {
|
||||
await promise;
|
||||
}
|
||||
|
||||
async CreateDB(dbName:string){
|
||||
|
||||
async CreateDB(dbName: string) {
|
||||
|
||||
let resp = await this.Connection.query(`CREATE DATABASE ${dbName}; `)
|
||||
}
|
||||
async UseDB(dbName:string){
|
||||
let resp =await this.Connection.query(`USE ${dbName}; `)
|
||||
}
|
||||
async DropDB(dbName:string){
|
||||
let resp =await this.Connection.query(`DROP DATABASE ${dbName}; `)
|
||||
async UseDB(dbName: string) {
|
||||
let resp = await this.Connection.query(`USE ${dbName}; `)
|
||||
}
|
||||
async CheckIfDBExists(dbName:string):Promise<boolean>{
|
||||
let resp =await this.Connection.query(`SELECT datname FROM pg_database WHERE datname ='${dbName}' `)
|
||||
return resp.rowCount>0;
|
||||
async DropDB(dbName: string) {
|
||||
let resp = await this.Connection.query(`DROP DATABASE ${dbName}; `)
|
||||
}
|
||||
async CheckIfDBExists(dbName: string): Promise<boolean> {
|
||||
let resp = await this.Connection.query(`SELECT datname FROM pg_database WHERE datname ='${dbName}' `)
|
||||
return resp.rowCount > 0;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { AbstractDriver } from "./drivers/AbstractDriver";
|
||||
import { MssqlDriver } from './drivers/MssqlDriver';
|
||||
import { PostgresDriver } from "./drivers/PostgresDriver";
|
||||
import { MysqlDriver } from "./drivers/MysqlDriver";
|
||||
import { MariaDbDriver } from "./drivers/MariaDbDriver";
|
||||
import { Engine } from './Engine'
|
||||
import * as Yargs from 'yargs'
|
||||
import path = require('path')
|
||||
@ -37,7 +38,7 @@ var argv = Yargs
|
||||
.option('e', {
|
||||
alias: 'engine',
|
||||
describe: 'Database engine.',
|
||||
choices: ['mssql', 'postgres', 'mysql'],
|
||||
choices: ['mssql', 'postgres', 'mysql', 'mariadb'],
|
||||
default: 'mssql'
|
||||
})
|
||||
.option('o', {
|
||||
@ -63,6 +64,10 @@ switch (argv.e) {
|
||||
driver = new MysqlDriver();
|
||||
standardPort = 3306;
|
||||
break;
|
||||
case 'mariadb':
|
||||
driver = new MysqlDriver();
|
||||
standardPort = 3306;
|
||||
break;
|
||||
default:
|
||||
console.error('Database engine not recognized.')
|
||||
process.abort();
|
||||
|
@ -15,13 +15,14 @@ var chaiSubset = require('chai-subset');
|
||||
import * as ts from "typescript";
|
||||
import { PostgresDriver } from "../../src/drivers/PostgresDriver";
|
||||
import { MysqlDriver } from "../../src/drivers/MysqlDriver";
|
||||
import { MariaDbDriver } from "../../src/drivers/MariaDbDriver";
|
||||
|
||||
|
||||
chai.use(chaiSubset);
|
||||
|
||||
|
||||
describe("integration tests", async function () {
|
||||
this.timeout(10000)
|
||||
this.timeout(20000)
|
||||
this.slow(5000)//compiling created models takes time
|
||||
let examplesPathJS = path.resolve(process.cwd(), 'dist/test/integration/examples')
|
||||
let examplesPathTS = path.resolve(process.cwd(), 'test/integration/examples')
|
||||
@ -31,6 +32,7 @@ describe("integration tests", async function () {
|
||||
if (process.env.MSSQL_Skip == '0') dbDrivers.push('mssql')
|
||||
if (process.env.POSTGRES_Skip == '0') dbDrivers.push('postgres')
|
||||
if (process.env.MYSQL_Skip == '0') dbDrivers.push('mysql')
|
||||
if (process.env.MARIADB_Skip == '0') dbDrivers.push('mariadb')
|
||||
|
||||
for (let folder of files) {
|
||||
|
||||
@ -54,6 +56,10 @@ describe("integration tests", async function () {
|
||||
case 'mysql':
|
||||
engine = await createMysqlModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
case 'mariadb':
|
||||
engine = await createMariaDBModels(filesOrgPathJS, resultsPath)
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`Unknown engine type`);
|
||||
engine = <Engine>{}
|
||||
@ -227,6 +233,49 @@ async function createMysqlModels(filesOrgPath: string, resultsPath: string): Pro
|
||||
|
||||
|
||||
|
||||
return engine;
|
||||
}
|
||||
async function createMariaDBModels(filesOrgPath: string, resultsPath: string): Promise<Engine> {
|
||||
let driver: AbstractDriver;
|
||||
driver = new MariaDbDriver();
|
||||
await driver.ConnectToServer(`mysql`, process.env.MARIADB_Host, process.env.MARIADB_Port, process.env.MARIADB_Username, process.env.MARIADB_Password);
|
||||
|
||||
if (! await driver.CheckIfDBExists(process.env.MARIADB_Database))
|
||||
await driver.CreateDB(process.env.MARIADB_Database);
|
||||
await driver.DisconnectFromServer();
|
||||
|
||||
let connOpt: ConnectionOptions = {
|
||||
driver: {
|
||||
database: process.env.MARIADB_Database,
|
||||
host: process.env.MARIADB_Host,
|
||||
password: process.env.MARIADB_Password,
|
||||
type: 'mariadb',
|
||||
username: process.env.MARIADB_Username,
|
||||
port: process.env.MARIADB_Port
|
||||
},
|
||||
dropSchemaOnConnection: true,
|
||||
autoSchemaSync: true,
|
||||
entities: [path.resolve(filesOrgPath, '*.js')],
|
||||
}
|
||||
let conn = await createConnection(connOpt)
|
||||
|
||||
if (conn.isConnected)
|
||||
await conn.close()
|
||||
|
||||
driver = new MariaDbDriver();
|
||||
let engine = new Engine(
|
||||
driver, {
|
||||
host: process.env.MARIADB_Host,
|
||||
port: process.env.MARIADB_Port,
|
||||
databaseName: process.env.MARIADB_Database,
|
||||
user: process.env.MARIADB_Username,
|
||||
password: process.env.MARIADB_Password,
|
||||
databaseType: 'mariadb',
|
||||
resultsPath: resultsPath
|
||||
});
|
||||
|
||||
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user