From 120894be64d69391a3aa05a3fe477d86546498ca Mon Sep 17 00:00:00 2001 From: Kononnable Date: Mon, 30 Apr 2018 22:47:40 +0200 Subject: [PATCH] sqlite support #49 --- .travis.yml | 2 + package-lock.json | 410 +++++++++++++++ package.json | 1 + src/drivers/AbstractDriver.ts | 22 +- src/drivers/SqliteDriver.ts | 482 ++++++++++++++++++ src/index.ts | 7 +- test/integration/entityTypes.test.ts | 8 +- .../entityTypes/sqlite/entity/Post.ts | 92 ++++ test/integration/githubIssues.test.ts | 10 +- test/integration/integration.test.ts | 12 +- test/utils/GeneralTestUtils.ts | 64 ++- 11 files changed, 1086 insertions(+), 24 deletions(-) create mode 100644 src/drivers/SqliteDriver.ts create mode 100644 test/integration/entityTypes/sqlite/entity/Post.ts diff --git a/.travis.yml b/.travis.yml index 807e030..4fd15e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ env: ORACLE_Database=orclpdb1.localdomain ORACLE_Username=typeorm_mg ORACLE_Password=Passw0rd + SQLITE_Skip=0 + SQLITE_Database=temp/sqlitedb.db before_install: - sudo service mysql stop - sudo service postgresql stop diff --git a/package-lock.json b/package-lock.json index 79824ff..2c4b03b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5434,6 +5434,11 @@ "thenify-all": "^1.0.0" } }, + "nan": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", + "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==" + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -7306,6 +7311,411 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "sqlite3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.0.tgz", + "integrity": "sha512-6OlcAQNGaRSBLK1CuaRbKwlMFBb9DEhzmZyQP+fltNRF6XcIMpVIfXCBEcXPe1d4v9LnhkQUYkknDbA5JReqJg==", + "requires": { + "nan": "~2.9.2", + "node-pre-gyp": "~0.9.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "iconv-lite": { + "version": "0.4.19", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.2.1", + "bundled": true, + "requires": { + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.9.0", + "bundled": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "rc": { + "version": "1.2.6", + "bundled": true, + "requires": { + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.5", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "sax": { + "version": "1.2.4", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "4.4.0", + "bundled": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.3", + "minipass": "^2.2.1", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, "sqlstring": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz", diff --git a/package.json b/package.json index 3e844ac..4b89a44 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "typeorm": "^0.2.4", "typescript": "^2.8.3", "yargs": "^11.1.0", + "sqlite3": "^4.0.0", "yn": "^2.0.0" }, "devDependencies": { diff --git a/src/drivers/AbstractDriver.ts b/src/drivers/AbstractDriver.ts index 53a2380..21bb8b0 100644 --- a/src/drivers/AbstractDriver.ts +++ b/src/drivers/AbstractDriver.ts @@ -164,21 +164,25 @@ export abstract class AbstractDriver { FindPrimaryColumnsFromIndexes(dbModel: DatabaseModel) { dbModel.entities.forEach(entity => { let primaryIndex = entity.Indexes.find(v => v.isPrimaryKey); - if (!primaryIndex) { + + entity.Columns.forEach(col => { + if ( + primaryIndex && + primaryIndex.columns.some(cIndex => cIndex.name == col.name) + ) + col.isPrimary = true; + }); + if ( + !entity.Columns.some(v => { + return v.isPrimary; + }) + ) { TomgUtils.LogError( `Table ${entity.EntityName} has no PK.`, false ); return; } - entity.Columns.forEach(col => { - if ( - primaryIndex!.columns.some( - cIndex => cIndex.name == col.name - ) - ) - col.isPrimary = true; - }); }); } abstract async DisconnectFromServer(); diff --git a/src/drivers/SqliteDriver.ts b/src/drivers/SqliteDriver.ts new file mode 100644 index 0000000..4ae6ea9 --- /dev/null +++ b/src/drivers/SqliteDriver.ts @@ -0,0 +1,482 @@ +import { AbstractDriver } from "./AbstractDriver"; +import { ColumnInfo } from "./../models/ColumnInfo"; +import { EntityInfo } from "./../models/EntityInfo"; +import { RelationInfo } from "./../models/RelationInfo"; +import { DatabaseModel } from "./../models/DatabaseModel"; +import * as TomgUtils from "./../Utils"; +/** + * SqlLiteDriver + */ +export class SqliteDriver extends AbstractDriver { + sqlite = require("sqlite3").verbose(); + db: any; + tablesWithGeneratedPrimaryKey: String[] = new Array(); + async GetAllTables(schema: string): Promise { + let ret: EntityInfo[] = []; + + let rows = await this.ExecQuery<{ tbl_name: string; sql: string }>( + `SELECT tbl_name, sql FROM "sqlite_master" WHERE "type" = 'table' AND name NOT LIKE 'sqlite_%'` + ); + rows.forEach(val => { + let ent: EntityInfo = new EntityInfo(); + ent.EntityName = val.tbl_name; + ent.Columns = []; + ent.Indexes = []; + if (val.sql.includes("AUTOINCREMENT")) { + this.tablesWithGeneratedPrimaryKey.push(ent.EntityName); + } + ret.push(ent); + }); + + return ret; + } + async GetCoulmnsFromEntity( + entities: EntityInfo[], + schema: string + ): Promise { + for (const ent of entities) { + let response = await this.ExecQuery<{ + cid: number; + name: string; + type: string; + notnull: number; + dflt_value: string; + pk: number; + }>(`PRAGMA table_info('${ent.EntityName}');`); + response.forEach(resp => { + let colInfo: ColumnInfo = new ColumnInfo(); + colInfo.name = resp.name; + colInfo.is_nullable = resp.notnull == 0; + colInfo.isPrimary = resp.pk > 0; + colInfo.default = resp.dflt_value ? resp.dflt_value : null; + colInfo.sql_type = resp.type.replace(/\([0-9]+\)/g, ""); + colInfo.is_generated = + colInfo.isPrimary && + this.tablesWithGeneratedPrimaryKey.includes(ent.EntityName); + + switch (colInfo.sql_type) { + case "int": + colInfo.ts_type = "number"; + break; + case "integer": + colInfo.ts_type = "number"; + break; + case "int2": + colInfo.ts_type = "number"; + break; + case "int8": + colInfo.ts_type = "number"; + break; + case "tinyint": + colInfo.ts_type = "number"; + break; + case "smallint": + colInfo.ts_type = "number"; + break; + case "mediumint": + colInfo.ts_type = "number"; + break; + case "bigint": + colInfo.ts_type = "string"; + break; + case "unsigned big int": + colInfo.ts_type = "string"; + break; + case "character": + colInfo.ts_type = "string"; + break; + case "varchar": + colInfo.ts_type = "string"; + break; + case "varying character": + colInfo.ts_type = "string"; + break; + case "nchar": + colInfo.ts_type = "string"; + break; + case "native character": + colInfo.ts_type = "string"; + break; + case "nvarchar": + colInfo.ts_type = "string"; + break; + case "text": + colInfo.ts_type = "string"; + break; + case "blob": + colInfo.ts_type = "Buffer"; + break; + case "clob": + colInfo.ts_type = "string"; + break; + case "real": + colInfo.ts_type = "number"; + break; + case "double": + colInfo.ts_type = "number"; + break; + case "double precision": + colInfo.ts_type = "number"; + break; + case "float": + colInfo.ts_type = "number"; + break; + case "numeric": + colInfo.ts_type = "number"; + break; + case "decimal": + colInfo.ts_type = "number"; + break; + case "boolean": + colInfo.ts_type = "boolean"; + break; + case "date": + colInfo.ts_type = "string"; + break; + case "datetime": + colInfo.ts_type = "Date"; + break; + default: + TomgUtils.LogError( + `Unknown column type: ${ + colInfo.sql_type + } table name: ${resp.name} column name: ${ + ent.EntityName + }` + ); + break; + } + let options = resp.type.match(/\([0-9]+\)/g); + if ( + this.ColumnTypesWithPrecision.some( + v => v == colInfo.sql_type + ) && + options + ) { + colInfo.numericPrecision = options[0] + .substring(1, options[0].length - 1) + .split(",")[0]; + colInfo.numericScale = options[0] + .substring(1, options[0].length - 1) + .split(",")[1]; + } + if ( + this.ColumnTypesWithLength.some( + v => v == colInfo.sql_type + ) && + options + ) { + colInfo.lenght = options[0].substring( + 1, + options[0].length - 1 + ); + } + if ( + this.ColumnTypesWithWidth.some( + v => + v == colInfo.sql_type && + colInfo.ts_type != "boolean" + ) && + options + ) { + colInfo.width = options[0].substring( + 1, + options[0].length - 1 + ); + } + + if (colInfo.sql_type) ent.Columns.push(colInfo); + }); + } + + return entities; + } + async GetIndexesFromEntity( + entities: EntityInfo[], + schema: string + ): Promise { + for (const ent of entities) { + let response = await this.ExecQuery<{ + seq: number; + name: string; + unique: number; + origin: string; + partial: number; + }>(`PRAGMA index_list('${ent.EntityName}');`); + for (const resp of response) { + let indexColumnsResponse = await this.ExecQuery<{ + seqno: number; + cid: number; + name: string; + }>(`PRAGMA index_info('${resp.name}');`); + indexColumnsResponse.forEach(element => { + let indexInfo: IndexInfo = {}; + let indexColumnInfo: IndexColumnInfo = {}; + if ( + ent.Indexes.filter(filterVal => { + return filterVal.name == resp.name; + }).length > 0 + ) { + indexInfo = ent.Indexes.filter(filterVal => { + return filterVal.name == resp.name; + })[0]; + } else { + indexInfo.columns = []; + indexInfo.name = resp.name; + indexInfo.isUnique = resp.unique == 1; + // indexInfo.isPrimaryKey = + // resp.is_primary_key == 1 ? true : false; + ent.Indexes.push(indexInfo); + } + + indexColumnInfo.name = element.name; + if ( + indexColumnsResponse.length == 1 && + indexInfo.isUnique + ) { + ent.Columns.filter( + v => v.name == indexColumnInfo.name + ).map(v => (v.is_unique = true)); + } + indexInfo.columns.push(indexColumnInfo); + }); + } + } + + return entities; + } + async GetRelations( + entities: EntityInfo[], + schema: string + ): Promise { + for (const entity of entities) { + let response = await this.ExecQuery<{ + id: number; + seq: number; + table: string; + from: string; + to: string; + on_update: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION"; + on_delete: "RESTRICT" | "CASCADE" | "SET NULL" | "NO ACTION"; + match: string; + }>(`PRAGMA foreign_key_list('${entity.EntityName}');`); + let relationsTemp: RelationTempInfo[] = []; + response.forEach(resp => { + let rels = {}; + rels.ownerColumnsNames = []; + rels.referencedColumnsNames = []; + rels.actionOnDelete = + resp.on_delete == "NO ACTION" ? null : resp.on_delete; + rels.actionOnUpdate = + resp.on_update == "NO ACTION" ? null : resp.on_update; + rels.ownerTable = entity.EntityName; + rels.referencedTable = resp.table; + relationsTemp.push(rels); + rels.ownerColumnsNames.push(resp.from); + rels.referencedColumnsNames.push(resp.to); + }); + relationsTemp.forEach(relationTmp => { + let ownerEntity = entities.find(entitity => { + return entitity.EntityName == relationTmp.ownerTable; + }); + if (!ownerEntity) { + TomgUtils.LogError( + `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) { + TomgUtils.LogError( + `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) { + TomgUtils.LogError( + `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) { + TomgUtils.LogError( + `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(); + let columnName = + ownerEntity.EntityName.toLowerCase() + + (isOneToMany ? "s" : ""); + if ( + referencedEntity.Columns.filter(filterVal => { + return filterVal.name == columnName; + }).length > 0 + ) { + for (let i = 2; i <= ownerEntity.Columns.length; i++) { + columnName = + ownerEntity.EntityName.toLowerCase() + + (isOneToMany ? "s" : "") + + i.toString(); + if ( + referencedEntity.Columns.filter(filterVal => { + return filterVal.name == columnName; + }).length == 0 + ) + break; + } + } + 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 = columnName; + ownerRelation.relationType = isOneToMany + ? "ManyToOne" + : "OneToOne"; + ownerColumn.relations.push(ownerRelation); + if (isOneToMany) { + let col = new ColumnInfo(); + col.name = columnName; + 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 = columnName; + 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() { + this.db.close(); + } + + async ConnectToServer( + database: string, + server: string, + port: number, + user: string, + password: string, + ssl: boolean + ) { + await this.UseDB(database); + } + + async CreateDB(dbName: string) {} + async UseDB(dbName: string) { + let promise = new Promise((resolve, reject) => { + this.db = new this.sqlite.Database(dbName, err => { + if (err) { + console.error(err.message); + reject(err); + return; + } + resolve(); + }); + }); + return promise; + } + async DropDB(dbName: string) {} + async CheckIfDBExists(dbName: string): Promise { + return true; + } + + async ExecQuery(sql: string): Promise> { + let ret: any; + let promise = new Promise((resolve, reject) => { + this.db.serialize(() => { + this.db.all(sql, [], function(err, row) { + if (!err) { + //Connection successfull + ret = row; + resolve(true); + } else { + TomgUtils.LogError( + "Error executing query on SQLite.", + false, + err.message + ); + reject(err); + } + }); + }); + }); + + let x = await promise; + return ret; + } +} diff --git a/src/index.ts b/src/index.ts index 4c6573f..7eb176d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { AbstractDriver } from "./drivers/AbstractDriver"; import { MssqlDriver } from "./drivers/MssqlDriver"; import { PostgresDriver } from "./drivers/PostgresDriver"; +import { SqliteDriver } from "./drivers/SqliteDriver"; import { MysqlDriver } from "./drivers/MysqlDriver"; import { MariaDbDriver } from "./drivers/MariaDbDriver"; import { OracleDriver } from "./drivers/OracleDriver"; @@ -39,7 +40,7 @@ var argv = Yargs.usage( .option("e", { alias: "engine", describe: "Database engine.", - choices: ["mssql", "postgres", "mysql", "mariadb", "oracle"], + choices: ["mssql", "postgres", "mysql", "mariadb", "oracle", "sqlite"], default: "mssql" }) .option("o", { @@ -111,6 +112,10 @@ switch (argv.e) { driver = new OracleDriver(); standardPort = 1521; break; + case "sqlite": + driver = new SqliteDriver(); + standardPort = 0; + break; default: TomgUtils.LogError("Database engine not recognized.", false); throw new Error("Database engine not recognized."); diff --git a/test/integration/entityTypes.test.ts b/test/integration/entityTypes.test.ts index 9a7bd0d..9576e5f 100644 --- a/test/integration/entityTypes.test.ts +++ b/test/integration/entityTypes.test.ts @@ -20,6 +20,7 @@ describe("Platform specyfic types", async function () { this.slow(5000)//compiling created models takes time let dbDrivers: string[] = [] + if (process.env.SQLite_Skip == '0') dbDrivers.push('sqlite') 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') @@ -44,8 +45,8 @@ describe("Platform specyfic types", async function () { let engine: Engine; switch (dbDriver) { - case 'mssql': - engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + case 'sqlite': + engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath) break; case 'postgres': engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath) @@ -56,6 +57,9 @@ describe("Platform specyfic types", async function () { case 'mariadb': engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath) break; + case 'mssql': + engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + break; case 'oracle': engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath) break; diff --git a/test/integration/entityTypes/sqlite/entity/Post.ts b/test/integration/entityTypes/sqlite/entity/Post.ts new file mode 100644 index 0000000..73326e4 --- /dev/null +++ b/test/integration/entityTypes/sqlite/entity/Post.ts @@ -0,0 +1,92 @@ +import { Entity, PrimaryColumn, Column } from "typeorm"; + +@Entity("Post") +export class Post { + + @PrimaryColumn() + id: number; + + @Column() + name: string; + + @Column("int") + int: number; + + @Column("integer") + integer: number; + + @Column("int2") + int2: number; + + @Column("int8") + int8: number; + + @Column("tinyint") + tinyint: number; + + @Column("smallint") + smallint: number; + + @Column("mediumint") + mediumint: number; + + @Column("bigint") + bigint: string; + + @Column("unsigned big int") + unsigned_big_int: string; + + @Column("character") + character: string; + + @Column("varchar") + varchar: string; + + @Column("varying character") + varying_character: string; + + @Column("nchar") + nchar: string; + + @Column("native character") + native_character: string; + + @Column("nvarchar") + nvarchar: string; + + @Column("text") + text: string; + + @Column("blob") + blob: Buffer; + + @Column("clob") + clob: string; + + @Column("real") + real: number; + @Column("double") + double: number; + + @Column("double precision") + doublePrecision: number; + + @Column("float") + float: number; + + @Column("numeric") + numeric: number; + + @Column("decimal") + decimal: number; + + @Column("boolean") + boolean: boolean; + + @Column("date") + date: string; + + @Column("datetime") + datetime: Date; + +} diff --git a/test/integration/githubIssues.test.ts b/test/integration/githubIssues.test.ts index 2282f56..37277fa 100644 --- a/test/integration/githubIssues.test.ts +++ b/test/integration/githubIssues.test.ts @@ -21,6 +21,7 @@ describe("GitHub issues", async function () { this.slow(5000)//compiling created models takes time let dbDrivers: string[] = [] + if (process.env.SQLite_Skip == '0') dbDrivers.push('sqlite') 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') @@ -38,7 +39,7 @@ describe("GitHub issues", async function () { switch (folder) { case '39': - if (dbDriver == 'mysql' || dbDriver == 'mariadb' || dbDriver == 'oracle') + if (dbDriver == 'mysql' || dbDriver == 'mariadb' || dbDriver == 'oracle' || dbDriver == 'sqlite') continue; break; default: @@ -54,8 +55,8 @@ describe("GitHub issues", async function () { let engine: Engine; switch (dbDriver) { - case 'mssql': - engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + case 'sqlite': + engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath) break; case 'postgres': engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath) @@ -66,6 +67,9 @@ describe("GitHub issues", async function () { case 'mariadb': engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath) break; + case 'mssql': + engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + break; case 'oracle': engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath) break; diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index b05ce80..893b55d 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -21,6 +21,7 @@ describe("TypeOrm examples", async function () { this.slow(5000)//compiling created models takes time let dbDrivers: string[] = [] + if (process.env.SQLite_Skip == '0') dbDrivers.push('sqlite') 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') @@ -44,8 +45,8 @@ describe("TypeOrm examples", async function () { let engine: Engine; switch (dbDriver) { - case 'mssql': - engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + case 'sqlite': + engine = await GTU.createSQLiteModels(filesOrgPathJS, resultsPath) break; case 'postgres': engine = await GTU.createPostgresModels(filesOrgPathJS, resultsPath) @@ -56,6 +57,9 @@ describe("TypeOrm examples", async function () { case 'mariadb': engine = await GTU.createMariaDBModels(filesOrgPathJS, resultsPath) break; + case 'mssql': + engine = await GTU.createMSSQLModels(filesOrgPathJS, resultsPath) + break; case 'oracle': engine = await GTU.createOracleDBModels(filesOrgPathJS, resultsPath) break; @@ -65,8 +69,8 @@ describe("TypeOrm examples", async function () { engine = {} break; } - if (folder=='sample18-lazy-relations') { - engine.Options.lazy=true; + if (folder == 'sample18-lazy-relations') { + engine.Options.lazy = true; } let result = await engine.createModelFromDatabase() diff --git a/test/utils/GeneralTestUtils.ts b/test/utils/GeneralTestUtils.ts index 01b9924..1d19a35 100644 --- a/test/utils/GeneralTestUtils.ts +++ b/test/utils/GeneralTestUtils.ts @@ -5,6 +5,7 @@ import { PostgresDriver } from "./../../src/drivers/PostgresDriver"; import { MysqlDriver } from "../../src/drivers/MysqlDriver"; import { MariaDbDriver } from "../../src/drivers/MariaDbDriver"; import { OracleDriver } from "../../src/drivers/OracleDriver"; +import { SqliteDriver } from "../../src/drivers/SqliteDriver"; import { Engine } from "../../src/Engine"; import { createConnection, ConnectionOptions, Connection } from "typeorm"; import * as yn from "yn" @@ -63,7 +64,7 @@ export async function createMSSQLModels(filesOrgPath: string, resultsPath: strin convertCaseEntity: 'none', convertCaseFile: 'none', convertCaseProperty: 'none', - lazy:false + lazy: false }); conn = await createConnection(connOpt) @@ -127,7 +128,7 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st convertCaseEntity: 'none', convertCaseFile: 'none', convertCaseProperty: 'none', - lazy:false + lazy: false }); conn = await createConnection(connOpt) @@ -142,6 +143,59 @@ export async function createPostgresModels(filesOrgPath: string, resultsPath: st return engine; } +export async function createSQLiteModels(filesOrgPath: string, resultsPath: string): Promise { + let driver: AbstractDriver; + driver = new SqliteDriver(); + await driver.ConnectToServer(String(process.env.SQLITE_Database), '', 0, '', '', false); + + if (await driver.CheckIfDBExists(String(process.env.SQLITE_Database))) + await driver.DropDB(String(process.env.SQLITE_Database)); + await driver.CreateDB(String(process.env.SQLITE_Database)); + await driver.DisconnectFromServer(); + + let connOpt: ConnectionOptions = { + database: String(process.env.SQLITE_Database), + type: 'sqlite', + dropSchema: true, + synchronize: false, + entities: [path.resolve(filesOrgPath, '*.js')], + } + + let conn = await createConnection(connOpt) + let queryRunner = conn.createQueryRunner() + await conn.synchronize(); + + if (conn.isConnected) + await conn.close() + + driver = new SqliteDriver(); + let engine = new Engine( + driver, { + host: '', + port: 0, + databaseName: String(process.env.SQLITE_Database), + user: '', + password: '', + databaseType: 'sqlite', + resultsPath: resultsPath, + schemaName: '', + ssl: false, + noConfigs: false, + convertCaseEntity: 'none', + convertCaseFile: 'none', + convertCaseProperty: 'none', + lazy: false + }); + + conn = await createConnection(connOpt) + queryRunner = conn.createQueryRunner() + await conn.synchronize(); + if (conn.isConnected) + await conn.close() + + return engine; +} + export async function createMysqlModels(filesOrgPath: string, resultsPath: string): Promise { let driver: AbstractDriver; driver = new MysqlDriver(); @@ -184,7 +238,7 @@ export async function createMysqlModels(filesOrgPath: string, resultsPath: strin convertCaseEntity: 'none', convertCaseFile: 'none', convertCaseProperty: 'none', - lazy:false + lazy: false }); @@ -234,7 +288,7 @@ export async function createMariaDBModels(filesOrgPath: string, resultsPath: str convertCaseEntity: 'none', convertCaseFile: 'none', convertCaseProperty: 'none', - lazy:false + lazy: false }); @@ -286,7 +340,7 @@ export async function createOracleDBModels(filesOrgPath: string, resultsPath: st convertCaseEntity: 'none', convertCaseFile: 'none', convertCaseProperty: 'none', - lazy:false + lazy: false }); return engine;