@egi/smart-db 2.5.6 → 2.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@ fi
13
13
 
14
14
  if [[ -f $ROOT/helpers/extract-db-api.js ]]; then
15
15
  # distribution environment
16
- node --es-module-specifier-resolution=node "$ROOT/helpers/extract-db-api.js" "$@"
16
+ node --no-warnings --es-module-specifier-resolution=node "$ROOT/helpers/extract-db-api.js" "$@"
17
17
  else
18
18
  echo "missing smart-db-extract-api script"
19
19
  fi
@@ -7,12 +7,17 @@ declare type VariableArgFunction = (...params: any[]) => any;
7
7
  export declare class SmartDbBetterSqlite3 extends SmartDb {
8
8
  protected db: BetterSqlite3.Database;
9
9
  constructor(connectorOrDb: string | BetterSqlite3.Database, options?: BetterSqlite3.Options);
10
+ commitSync(): void;
11
+ rollbackSync(): void;
12
+ commit(): Promise<void>;
13
+ rollback(): Promise<void>;
10
14
  getDatabaseType(): string;
11
15
  getDbQuote(): string;
12
16
  getDbConnector(): string | SmartDbConnector;
13
17
  hasConcurrentTransactions(): boolean;
14
- supportSyncCalls(): boolean;
15
18
  supportAsyncCalls(): boolean;
19
+ supportSyncCalls(): boolean;
20
+ hasTransaction(): boolean;
16
21
  exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
17
22
  existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
18
23
  exec(script: string): Promise<any>;
@@ -1 +1 @@
1
- import BetterSqlite3 from"better-sqlite3";import _ from"lodash";import{SqliteMasterModel}from"../models/sqlite-master-model";import{SmartDb}from"../smart-db";export class SmartDbBetterSqlite3 extends SmartDb{constructor(t,e){_.isString(t)?(super(t),this.db=new BetterSqlite3(t,e)):(super(null),this.db=t,this.db.inTransaction)}getDatabaseType(){return"sqlite3"}getDbQuote(){return'"'}getDbConnector(){return this.dbConnector}hasConcurrentTransactions(){return!1}supportSyncCalls(){return!0}supportAsyncCalls(){return!1}exists(t,e,s){return new Promise(((r,n)=>{const a=_.isString(t)?t:t.getTableName();this.getFirst(SqliteMasterModel,{name:a,type:e,tblName:s}).then((t=>{null===t?n(this.getLastError()):r(!!t)})).catch((t=>{n(t)}))}))}existsSync(t,e,s){let r=!1;const n=_.isString(t)?t:t.getTableName(),a=this.getFirstSync(SqliteMasterModel,{name:n,type:e,tblName:s});if(!1===a)throw this.getLastError();return r=!!a,r}exec(t){return new Promise(((e,s)=>{const r=this.execSync(t);!1===r?s("unable to execute script"):e(r)}))}execSync(t){return this.saveExecute((()=>this.db.exec(t)))}transaction(t){return this.saveExecute((()=>this.db.transaction(t)))}pragma(t,e){return this.saveExecute((()=>this.db.pragma(t,e)))}func(t,e,s){return this.saveExecute((()=>{let r;return r=_.isFunction(e)?this.db.function(t,e):this.db.function(t,e,s),r}))}aggregate(t,e){return this.saveExecute((()=>this.db.aggregate(t,e)))}loadExtension(t){return this.saveExecute((()=>this.db.loadExtension(t)))}closeSync(){return this.saveExecute((()=>(this.smartDbLog.setDb(null),!!this.db.close())))}defaultSafeIntegers(t){return this.saveExecute((()=>this.db.defaultSafeIntegers(t)))}backup(t,e){return this.saveExecute((()=>this.db.backup(t,e)))}getTableInfo(t){return new Promise((e=>{const s=this.pragma(`table_info(${t})`);let r=[];s&&(r=s.map((t=>({cid:t.cid,name:t.name,type:t.type,notNull:0!==t.notnull,defaultValue:t.dflt_value,isPk:0!==t.pk}))),e({name:t,fields:r}))}))}statementRun(t,e=0){return new Promise(((s,r)=>{try{s(this.statementRunSync(t))}catch(n){"SQLITE_BUSY"==n.code&&e<10?setTimeout((()=>{this.statementRun(t,e+1).then((t=>{s(t)})).catch((t=>{r(t)}))}),100):r(n)}}))}statementRunSync(t){const e=this.db.prepare(t.sql),s=e&&e.run(t.values);return s&&{changes:s.changes,affected:s.changes,lastId:s.lastInsertRowid}}statementGet(t){return new Promise(((e,s)=>{try{e(this.db.prepare(t.sql).get(t.values))}catch(t){s(t)}}))}statementGetSync(t){const e=this.db.prepare(t.sql);return e&&e.get(t.values)}statementGetAll(t){return new Promise(((e,s)=>{try{e(this.db.prepare(t.sql).all(t.values))}catch(t){s(t)}}))}statementGetAllSync(t){const e=this.db.prepare(t.sql);return e&&e.all(t.values)}close(){return new Promise(((t,e)=>{try{this.smartDbLog.setDb(null),this.db.close(),t()}catch(t){e(t)}}))}}
1
+ import BetterSqlite3 from"better-sqlite3";import _ from"lodash";import{SqliteMasterModel}from"../models/sqlite-master-model";import{SmartDb}from"../smart-db";export class SmartDbBetterSqlite3 extends SmartDb{constructor(t,e){_.isString(t)?(super(t),this.db=new BetterSqlite3(t,e)):(super(null),this.db=t,this.db.inTransaction),this.isReady=!0}commitSync(){this.db.inTransaction&&this.db.exec("commit")}rollbackSync(){this.db.inTransaction&&this.db.exec("rollback")}commit(){return new Promise(((t,e)=>{try{this.commitSync(),t()}catch(t){e(t)}}))}rollback(){return new Promise(((t,e)=>{try{this.rollbackSync(),t()}catch(t){e(t)}}))}getDatabaseType(){return"sqlite3"}getDbQuote(){return'"'}getDbConnector(){return this.dbConnector}hasConcurrentTransactions(){return!1}supportAsyncCalls(){return!0}supportSyncCalls(){return!0}hasTransaction(){return this.db.inTransaction}exists(t,e,s){return new Promise(((r,n)=>{try{r(this.existsSync(t,e,s))}catch(t){n(t)}}))}existsSync(t,e,s){let r=!1;const n=_.isString(t)?t:t.getTableName(),a=this.getFirstSync(SqliteMasterModel,{name:n,type:e,tblName:s});if(!1===a)throw this.getLastError();return r=!!a,r}exec(t){return new Promise(((e,s)=>{const r=this.execSync(t);!1===r?s("unable to execute script"):e(r)}))}execSync(t){return this.saveExecute((()=>this.db.exec(t)))}transaction(t){return this.saveExecute((()=>this.db.transaction(t)))}pragma(t,e){return this.saveExecute((()=>this.db.pragma(t,e)))}func(t,e,s){return this.saveExecute((()=>{let r;return r=_.isFunction(e)?this.db.function(t,e):this.db.function(t,e,s),r}))}aggregate(t,e){return this.saveExecute((()=>this.db.aggregate(t,e)))}loadExtension(t){return this.saveExecute((()=>this.db.loadExtension(t)))}closeSync(){return this.saveExecute((()=>(this.smartDbLog.setDb(null),!!this.db.close())))}defaultSafeIntegers(t){return this.saveExecute((()=>this.db.defaultSafeIntegers(t)))}backup(t,e){return this.saveExecute((()=>this.db.backup(t,e)))}getTableInfo(t){return new Promise((e=>{const s=this.pragma(`table_info(${t})`);let r=[];s&&(r=s.map((t=>({cid:t.cid,name:t.name,type:t.type,notNull:0!==t.notnull,defaultValue:t.dflt_value,isPk:0!==t.pk}))),e({name:t,fields:r}))}))}statementRun(t,e=0){return new Promise(((s,r)=>{try{s(this.statementRunSync(t))}catch(n){"SQLITE_BUSY"==n.code&&e<10?setTimeout((()=>{this.statementRun(t,e+1).then((t=>{s(t)})).catch((t=>{r(t)}))}),100):r(n)}}))}statementRunSync(t){const e=this.db.prepare(t.sql),s=e&&e.run(t.values);return s&&{changes:s.changes,affected:s.changes,lastId:s.lastInsertRowid}}statementGet(t){return new Promise(((e,s)=>{try{e(this.db.prepare(t.sql).get(t.values))}catch(t){s(t)}}))}statementGetSync(t){const e=this.db.prepare(t.sql);return e&&e.get(t.values)}statementGetAll(t){return new Promise(((e,s)=>{try{e(this.db.prepare(t.sql).all(t.values))}catch(t){s(t)}}))}statementGetAllSync(t){const e=this.db.prepare(t.sql);return e&&e.all(t.values)}close(){return new Promise(((t,e)=>{try{this.smartDbLog.setDb(null),this.db.close(),t()}catch(t){e(t)}}))}}
@@ -12,7 +12,12 @@ export declare class SmartDbMysql extends SmartDb {
12
12
  hasConcurrentTransactions(): boolean;
13
13
  supportSyncCalls(): boolean;
14
14
  supportAsyncCalls(): boolean;
15
+ hasTransaction(): boolean;
15
16
  closeSync(): boolean;
17
+ commit(): Promise<void>;
18
+ rollback(): Promise<void>;
19
+ commitSync(): void;
20
+ rollbackSync(): void;
16
21
  exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
17
22
  existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
18
23
  exec(script: string): Promise<void>;
@@ -1 +1 @@
1
- import _ from"lodash";import Mysql from"mysql";import{SmartDb}from"../smart-db";export class SmartDbMysql extends SmartDb{constructor(e){e.config&&e.connect?(super(e.config),this.db=e):(super(e),this.db=Mysql.createConnection(e))}getDatabaseType(){return"mysql"}getDbConnector(){return this.dbConnector}getDbQuote(){return"`"}hasConcurrentTransactions(){return!0}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}closeSync(){throw new Error("Method not implemented.")}exists(e,t,r){return new Promise(((t,r)=>{const n=_.isString(e)?e:e.getTableName();this.db.query(`show tables like '${n}'`,((e,n,s)=>{e?r(e):t(n&&n.length>0)}))}))}existsSync(e,t,r){throw new Error("Method not implemented.")}exec(e){return new Promise(((t,r)=>{try{this.db.query(e,((e,n,s)=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise(((e,t)=>{}))}statementGet(e){return new Promise(((t,r)=>{try{this.db.query(e.sql,e.values,((e,n,s)=>{e?r(e):t(n&&n[0])}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,s)=>{e?r(e):(console.log(s),t(n&&n))}))}))}statementGetAllSync(e){throw new Error("Method not implemented.")}statementRun(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,s)=>{if(e)r(e);else{const e={changes:n.changedRows,affected:n.affectedRows,lastId:n.insertId};t(e)}}))}))}statementRunSync(e){throw new Error("Method not implemented.")}close(){return new Promise(((e,t)=>{this.db.end((r=>{r?t(r):e()}))}))}}
1
+ import _ from"lodash";import Mysql from"mysql";import{SmartDb}from"../smart-db";export class SmartDbMysql extends SmartDb{constructor(e){e.config&&e.connect?(super(e.config),this.db=e):(super(e),this.db=Mysql.createConnection(e))}getDatabaseType(){return"mysql"}getDbConnector(){return this.dbConnector}getDbQuote(){return"`"}hasConcurrentTransactions(){return!0}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}hasTransaction(){throw new Error("Method not implemented.")}closeSync(){throw new Error("Method not implemented.")}commit(){return new Promise(((e,t)=>{this.db.commit((r=>{r?t(r):e()}))}))}rollback(){return new Promise(((e,t)=>{this.db.rollback((r=>{r?t(r):e()}))}))}commitSync(){throw new Error("Method not implemented.")}rollbackSync(){throw new Error("Method not implemented.")}exists(e,t,r){return new Promise(((t,r)=>{const n=_.isString(e)?e:e.getTableName();this.db.query(`show tables like '${n}'`,((e,n,o)=>{e?r(e):t(n&&n.length>0)}))}))}existsSync(e,t,r){throw new Error("Method not implemented.")}exec(e){return new Promise(((t,r)=>{try{this.db.query(e,((e,n,o)=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise(((e,t)=>{}))}statementGet(e){return new Promise(((t,r)=>{try{this.db.query(e.sql,e.values,((e,n,o)=>{e?r(e):t(n&&n[0])}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,o)=>{e?r(e):(console.log(o),t(n&&n))}))}))}statementGetAllSync(e){throw new Error("Method not implemented.")}statementRun(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,o)=>{if(e)r(e);else{const e={changes:n.changedRows,affected:n.affectedRows,lastId:n.insertId};t(e)}}))}))}statementRunSync(e){throw new Error("Method not implemented.")}close(){return new Promise(((e,t)=>{this.db.end((r=>{r?t(r):e()}))}))}}
@@ -6,11 +6,16 @@ import { SmartDbSqlBuildData } from "../smart-db-sql-build-data";
6
6
  export declare class SmartDbMysql2 extends SmartDb {
7
7
  protected db: Mysql.Connection;
8
8
  constructor(connectorOrDb: string | ConnectionConfig | Connection);
9
+ commit(): Promise<void>;
10
+ rollback(): Promise<void>;
11
+ commitSync(): void;
12
+ rollbackSync(): void;
9
13
  getDatabaseType(): string;
10
14
  getDbConnector(): string | Mysql.ConnectionConfig;
11
15
  getDbQuote(): string;
12
16
  hasConcurrentTransactions(): boolean;
13
17
  closeSync(): boolean;
18
+ hasTransaction(): boolean;
14
19
  exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
15
20
  existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
16
21
  exec(script: string): Promise<void>;
@@ -1 +1 @@
1
- import _ from"lodash";import Mysql from"mysql";import{SmartDb}from"../smart-db";export class SmartDbMysql2 extends SmartDb{constructor(e){e.config&&e.connect?(super(e.config),this.db=e):(super(e),this.db=Mysql.createConnection(e))}getDatabaseType(){return"mysql"}getDbConnector(){return this.dbConnector}getDbQuote(){return"`"}hasConcurrentTransactions(){return!0}closeSync(){throw new Error("Method not implemented.")}exists(e,t,r){return new Promise(((t,r)=>{const n=_.isString(e)?e:e.getTableName();this.db.query(`show tables like '${n}'`,((e,n,s)=>{e?r(e):t(n&&n.length>0)}))}))}existsSync(e,t,r){throw new Error("Method not implemented.")}exec(e){return new Promise(((t,r)=>{try{this.db.query(e,((e,n,s)=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise(((e,t)=>{}))}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}statementGet(e){return new Promise(((t,r)=>{try{this.db.query(e.sql,e.values,((e,n,s)=>{e?r(e):t(n&&n[0])}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,s)=>{e?r(e):(console.log(s),t(n&&n))}))}))}statementGetAllSync(e){throw new Error("Method not implemented.")}statementRun(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,s)=>{if(e)r(e);else{const e={changes:n.changedRows,affected:n.affectedRows,lastId:n.insertId};t(e)}}))}))}statementRunSync(e){throw new Error("Method not implemented.")}close(){return new Promise(((e,t)=>{this.db.end((r=>{r?t(r):e()}))}))}}
1
+ import _ from"lodash";import Mysql from"mysql";import{SmartDb}from"../smart-db";export class SmartDbMysql2 extends SmartDb{constructor(e){e.config&&e.connect?(super(e.config),this.db=e):(super(e),this.db=Mysql.createConnection(e))}commit(){return new Promise(((e,t)=>{this.db.commit((r=>{r?t(r):e()}))}))}rollback(){return new Promise(((e,t)=>{this.db.rollback((r=>{r?t(r):e()}))}))}commitSync(){throw new Error("Method not implemented.")}rollbackSync(){throw new Error("Method not implemented.")}getDatabaseType(){return"mysql"}getDbConnector(){return this.dbConnector}getDbQuote(){return"`"}hasConcurrentTransactions(){return!0}closeSync(){throw new Error("Method not implemented.")}hasTransaction(){throw new Error("Method not implemented.")}exists(e,t,r){return new Promise(((t,r)=>{const n=_.isString(e)?e:e.getTableName();this.db.query(`show tables like '${n}'`,((e,n,o)=>{e?r(e):t(n&&n.length>0)}))}))}existsSync(e,t,r){throw new Error("Method not implemented.")}exec(e){return new Promise(((t,r)=>{try{this.db.query(e,((e,n,o)=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise(((e,t)=>{}))}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}statementGet(e){return new Promise(((t,r)=>{try{this.db.query(e.sql,e.values,((e,n,o)=>{e?r(e):t(n&&n[0])}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,o)=>{e?r(e):(console.log(o),t(n&&n))}))}))}statementGetAllSync(e){throw new Error("Method not implemented.")}statementRun(e){return new Promise(((t,r)=>{this.db.query(e.sql,e.values,((e,n,o)=>{if(e)r(e);else{const e={changes:n.changedRows,affected:n.affectedRows,lastId:n.insertId};t(e)}}))}))}statementRunSync(e){throw new Error("Method not implemented.")}close(){return new Promise(((e,t)=>{this.db.end((r=>{r?t(r):e()}))}))}}
@@ -12,6 +12,9 @@ export interface SmartDbOracleOptions {
12
12
  }
13
13
  export declare class SmartDbOracle extends SmartDb {
14
14
  protected db: Connection;
15
+ private connectorOrDb;
16
+ private options;
17
+ static libInit: boolean;
15
18
  constructor(connectorOrDb: string | Connection, options?: SmartDbOracleOptions);
16
19
  getDatabaseType(): string;
17
20
  getDbConnector(): string | SmartDbConnector;
@@ -19,11 +22,17 @@ export declare class SmartDbOracle extends SmartDb {
19
22
  hasConcurrentTransactions(): boolean;
20
23
  supportSyncCalls(): boolean;
21
24
  supportAsyncCalls(): boolean;
25
+ hasTransaction(): boolean;
22
26
  exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
23
27
  existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
24
28
  exec(script: string): Promise<void>;
25
29
  execSync(script: string): Connection | false;
30
+ reconnect(connectorOrDb?: string | Connection, options?: SmartDbOracleOptions): void;
26
31
  closeSync(): boolean;
32
+ commit(): Promise<void>;
33
+ commitSync(): void;
34
+ rollback(): Promise<void>;
35
+ rollbackSync(): void;
27
36
  getTableInfo(tableName: string): Promise<SmartDbTableInfo>;
28
37
  protected statementRun(buildData: SmartDbSqlBuildData): Promise<SmartDbRunResult>;
29
38
  protected statementRunSync(buildData: SmartDbSqlBuildData): SmartDbRunResult;
@@ -1 +1 @@
1
- import fs from"fs";import _ from"lodash";import process from"node:process";import oracleDb from"oracledb";import{take}from"rxjs";import{OracleCatModel}from"../models/oracle-cat-model";import{OracleUserTabColumnsViewModel}from"../models/oracle-user-tab-columns-view-model";import{SmartDb}from"../smart-db";export class SmartDbOracle extends SmartDb{constructor(e,t){if(_.isString(e)){let r;super(e,t?.noDbLogging),"darwin"===process.platform&&(r=process.env.ORACLE_HOME),r&&fs.existsSync(r)&&oracleDb.initOracleClient({libDir:r}),oracleDb.getConnection(Object.assign({connectString:e},t)).then((e=>{t&&t.onReady&&this.onReady().pipe(take(1)).subscribe((r=>{r&&t.onReady(e)})),this.db=e,this.exec("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'").then((e=>{this.exec("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY:MM:DD HH24:MI:SS.FF'").then((e=>{this.isReady=!0})).catch((e=>{throw e}))})).catch((e=>{throw e}))})).catch((e=>{t&&t.onReady&&t.onReady(null,e),t.silent||console.log("unable prepare session database connection",e),this.isReady=null}))}else t.silent||console.log("missing Oracle SID"),super(null),this.db=e}getDatabaseType(){return"oracle"}getDbConnector(){return this.dbConnector}getDbQuote(){return'"'}hasConcurrentTransactions(){return!0}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}exists(e,t,r){return new Promise(((r,s)=>{try{const o=_.isString(e)?e:e.getTableName();this.getFirst(OracleCatModel,{name:o.toUpperCase(),type:t?t.toUpperCase():void 0}).then((e=>{null===e?s(this.getLastError()):r(!!e)})).catch((e=>{s(e)}))}catch(e){s(e)}}))}existsSync(e,t,r){const s=_.isString(e)?e:e.getTableName();return!!this.getFirstSync(OracleCatModel,{name:s.toUpperCase(),type:t?t.toUpperCase():void 0})}exec(e){return new Promise(((t,r)=>{try{this.db.execute(e,(e=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}closeSync(){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise((async(t,r)=>{await this.getAll(OracleUserTabColumnsViewModel,{tableName:e.toUpperCase()}).then((s=>{if(!1!==s){let r=[];r=s.map((e=>({cid:e.columnId,name:e.columnName,type:e.dataType,notNull:"Y"!=e.nullable,defaultValue:e.defaultValue,isPk:"P"==e.constraintType}))),t({name:e,fields:r})}else r(this.getLastError())})).catch((e=>{r(e)}))}))}statementRun(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(e.sql),e.values,((e,s)=>{e?r(e):this.exec("COMMIT WORK").then((()=>{t({changes:s.rowsAffected,affected:s.rowsAffected,lastId:null})})).catch((e=>{r(e)}))}))}catch(e){r(e)}}))}statementRunSync(e){throw new Error("Method not implemented.")}statementGet(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(this.replacePlaceholders(e.sql)),e.values,{maxRows:1,outFormat:oracleDb.OUT_FORMAT_OBJECT},((e,s)=>{if(e)r(e);else{const e=s.rows[0],r={};Object.keys(e).forEach((t=>{r[t.toLowerCase()]=e[t]})),t(r)}}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(e.sql),e.values,{maxRows:0,outFormat:oracleDb.OUT_FORMAT_OBJECT}).then((e=>{t(e.rows)})).catch((e=>{r(e)}))}catch(e){r(e)}}))}statementGetAllSync(e){throw new Error("Method not implemented.")}replacePlaceholders(e){let t=1,r=!1;for(let s=0;s<e.length;s++)"'"==e[s]?r=!r:r||"?"!=e[s]||(e=e.substring(0,s)+":"+t+++e.substring(s+1));return e}close(){return new Promise(((e,t)=>{try{this.smartDbLog.setDb(null),this.db.close((r=>{r?t(r):e()}))}catch(e){t(e)}}))}}
1
+ import fs from"fs";import process from"node:process";import oracleDb from"oracledb";import{take}from"rxjs";import{OracleCatModel}from"../models/oracle-cat-model";import{OracleUserTabColumnsViewModel}from"../models/oracle-user-tab-columns-view-model";import{SmartDb}from"../smart-db";export class SmartDbOracle extends SmartDb{constructor(e,t){if(!SmartDbOracle.libInit){let e;"darwin"===process.platform&&(e=process.env.ORACLE_HOME),e&&fs.existsSync(e)&&oracleDb.initOracleClient({libDir:e}),SmartDbOracle.libInit=!0}"string"==typeof e?super(e,t?.noDbLogging):super(null),this.reconnect(e,t)}getDatabaseType(){return"oracle"}getDbConnector(){return this.dbConnector}getDbQuote(){return'"'}hasConcurrentTransactions(){return!0}supportSyncCalls(){return!1}supportAsyncCalls(){return!0}hasTransaction(){throw new Error("Method not implemented.")}exists(e,t,r){return new Promise(((r,o)=>{try{const n="string"==typeof e?e:e.getTableName();this.getFirst(OracleCatModel,{name:n.toUpperCase(),type:t?t.toUpperCase():void 0}).then((e=>{null===e?o(this.getLastError()):r(!!e)})).catch((e=>{o(e)}))}catch(e){o(e)}}))}existsSync(e,t,r){const o="string"==typeof e?e:e.getTableName();return!!this.getFirstSync(OracleCatModel,{name:o.toUpperCase(),type:t?t.toUpperCase():void 0})}exec(e){return new Promise(((t,r)=>{try{this.db.execute(e,(e=>{e?r(e):t()}))}catch(e){r(e)}}))}execSync(e){throw new Error("Method not implemented.")}reconnect(e,t){e?this.connectorOrDb=e:e=this.connectorOrDb,t?this.options=t:t=this.options,"string"==typeof e?oracleDb.getConnection(Object.assign({connectString:e},t??this.options)).then((e=>{this.db=e,t&&t.onReady&&this.onReady().pipe(take(1)).subscribe((r=>{r&&t.onReady(e)})),this.exec("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'").then((e=>{this.exec("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY:MM:DD HH24:MI:SS.FF'").then((e=>{this.isReady=!0})).catch((e=>{throw e}))})).catch((e=>{throw e}))})).catch((e=>{t&&t.onReady&&t.onReady(null,e),t.silent||console.log("unable prepare session database connection",e),this.isReady=null})):this.db=e}closeSync(){throw new Error("Method not implemented.")}commit(){return this.db.commit()}commitSync(){throw new Error("Method not implemented.")}rollback(){return this.db.rollback()}rollbackSync(){throw new Error("Method not implemented.")}getTableInfo(e){return new Promise((async(t,r)=>{await this.getAll(OracleUserTabColumnsViewModel,{tableName:e.toUpperCase()}).then((o=>{if(!1!==o){let r=[];r=o.map((e=>({cid:e.columnId,name:e.columnName,type:e.dataType,notNull:"Y"!=e.nullable,defaultValue:e.defaultValue,isPk:"P"==e.constraintType}))),t({name:e,fields:r})}else r(this.getLastError())})).catch((e=>{r(e)}))}))}statementRun(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(e.sql),e.values,((e,o)=>{e?r(e):this.commit().then((()=>{t({changes:o.rowsAffected,affected:o.rowsAffected,lastId:null})})).catch((e=>{r(e)}))}))}catch(e){r(e)}}))}statementRunSync(e){throw new Error("Method not implemented.")}statementGet(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(this.replacePlaceholders(e.sql)),e.values,{maxRows:1,outFormat:oracleDb.OUT_FORMAT_OBJECT},((e,o)=>{if(e)r(e);else{const e=o.rows[0];let r=null;if(e){r={};Object.keys(e).forEach((t=>{r[t.toLowerCase()]=e[t]}))}t(r)}}))}catch(e){r(e)}}))}statementGetSync(e){throw new Error("Method not implemented.")}statementGetAll(e){return new Promise(((t,r)=>{try{this.db.execute(this.replacePlaceholders(e.sql),e.values,{maxRows:0,outFormat:oracleDb.OUT_FORMAT_OBJECT}).then((e=>{t(e.rows)})).catch((e=>{r(e)}))}catch(e){r(e)}}))}statementGetAllSync(e){throw new Error("Method not implemented.")}replacePlaceholders(e){let t=1,r=!1;for(let o=0;o<e.length;o++)"'"==e[o]?r=!r:r||"?"!=e[o]||(e=e.substring(0,o)+":"+t+++e.substring(o+1));return e}close(){return new Promise(((e,t)=>{try{this.smartDbLog.setDb(null),this.db.close((r=>{r?t(r):(this._onReady.next(!1),e())}))}catch(e){t(e)}}))}}SmartDbOracle.libInit=!1;
@@ -16,6 +16,11 @@ export declare class SmartDbSqlite3 extends SmartDb {
16
16
  hasConcurrentTransactions(): boolean;
17
17
  supportSyncCalls(): boolean;
18
18
  supportAsyncCalls(): boolean;
19
+ hasTransaction(): boolean;
20
+ commit(): Promise<void>;
21
+ rollback(): Promise<void>;
22
+ commitSync(): void;
23
+ rollbackSync(): void;
19
24
  exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
20
25
  existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
21
26
  exec(script: string): Promise<void>;
@@ -1 +1 @@
1
- import _ from"lodash";import Sqlite3 from"sqlite3";import{SqliteMasterModel}from"../models/sqlite-master-model";import{SmartDb}from"../smart-db";export class SmartDbSqlite3 extends SmartDb{constructor(t,e){_.isString(t)?(super(t),this.db=new Sqlite3.Database(t,e&&e.mode,e&&e.callback)):(super(null),this.db=t)}getDatabaseType(){return"sqlite3"}getDbConnector(){return this.dbConnector}getDbQuote(){return'"'}hasConcurrentTransactions(){return!1}supportSyncCalls(){return!0}supportAsyncCalls(){return!1}exists(t,e,r){return new Promise(((n,s)=>{try{const o=_.isString(t)?t:t.getTableName();this.getFirst(SqliteMasterModel,{name:o,type:e,tblName:r}).then((t=>{null===t?s(this.getLastError()):n(!!t)})).catch((t=>{s(t)}))}catch(t){s(t)}}))}existsSync(t,e,r){throw new Error("Method not implemented.")}exec(t){return new Promise(((e,r)=>{try{this.db.exec(t,(function(t){t?r(t):e()}))}catch(t){r(t)}}))}execSync(t){throw new Error("Method not implemented.")}closeSync(){throw new Error("Method not implemented.")}getTableInfo(t){throw new Error("Method not implemented.")}statementRun(t){return new Promise(((e,r)=>{try{this.db.run(t.sql,t.values,(function(t){t?r(t):e({changes:this.changes,affected:this.changes,lastId:this.lastID})}))}catch(t){r(t)}}))}statementRunSync(t){throw new Error("Method not implemented.")}statementGet(t){return new Promise(((e,r)=>{try{this.db.prepare(t.sql,(function(n){n?r(n):this.get(t.values,(function(t,n){t?r(t):e(n)}))}))}catch(t){r(t)}}))}statementGetSync(t){throw new Error("Method not implemented.")}statementGetAll(t){return new Promise(((e,r)=>{try{this.db.prepare(t.sql,(function(n){n?r(n):this.all(t.values,(function(t,n){t?r(t):e(n)}))}))}catch(t){r(t)}}))}statementGetAllSync(t){throw new Error("Method not implemented.")}close(){return new Promise(((t,e)=>{try{this.smartDbLog.setDb(null),this.db.close((function(r){r?e(r):t()}))}catch(t){e(t)}}))}}
1
+ import _ from"lodash";import Sqlite3 from"sqlite3";import{SqliteMasterModel}from"../models/sqlite-master-model";import{SmartDb}from"../smart-db";export class SmartDbSqlite3 extends SmartDb{constructor(t,e){_.isString(t)?(super(t),this.db=new Sqlite3.Database(t,e&&e.mode,e&&e.callback)):(super(null),this.db=t)}getDatabaseType(){return"sqlite3"}getDbConnector(){return this.dbConnector}getDbQuote(){return'"'}hasConcurrentTransactions(){return!1}supportSyncCalls(){return!0}supportAsyncCalls(){return!1}hasTransaction(){throw new Error("Method not implemented.")}commit(){return this.exec("commit")}rollback(){return this.exec("rollback")}commitSync(){throw new Error("Method not implemented.")}rollbackSync(){throw new Error("Method not implemented.")}exists(t,e,r){return new Promise(((n,o)=>{try{const s=_.isString(t)?t:t.getTableName();this.getFirst(SqliteMasterModel,{name:s,type:e,tblName:r}).then((t=>{null===t?o(this.getLastError()):n(!!t)})).catch((t=>{o(t)}))}catch(t){o(t)}}))}existsSync(t,e,r){throw new Error("Method not implemented.")}exec(t){return new Promise(((e,r)=>{try{this.db.exec(t,(function(t){t?r(t):e()}))}catch(t){r(t)}}))}execSync(t){throw new Error("Method not implemented.")}closeSync(){throw new Error("Method not implemented.")}getTableInfo(t){throw new Error("Method not implemented.")}statementRun(t){return new Promise(((e,r)=>{try{this.db.run(t.sql,t.values,(function(t){t?r(t):e({changes:this.changes,affected:this.changes,lastId:this.lastID})}))}catch(t){r(t)}}))}statementRunSync(t){throw new Error("Method not implemented.")}statementGet(t){return new Promise(((e,r)=>{try{this.db.prepare(t.sql,(function(n){n?r(n):this.get(t.values,(function(t,n){t?r(t):e(n)}))}))}catch(t){r(t)}}))}statementGetSync(t){throw new Error("Method not implemented.")}statementGetAll(t){return new Promise(((e,r)=>{try{this.db.prepare(t.sql,(function(n){n?r(n):this.all(t.values,(function(t,n){t?r(t):e(n)}))}))}catch(t){r(t)}}))}statementGetAllSync(t){throw new Error("Method not implemented.")}close(){return new Promise(((t,e)=>{try{this.smartDbLog.setDb(null),this.db.close((function(r){r?e(r):t()}))}catch(t){e(t)}}))}}
@@ -1 +1 @@
1
- import fs from"fs";import _ from"lodash";import process from"process";import{SmartDbBetterSqlite3}from"../drivers/smart-db-better-sqlite3";import{SmartDbOracle}from"../drivers/smart-db-oracle";import{OracleCatModel}from"../models/oracle-cat-model";import{SmartDbCoreTableModel}from"../models/smart-db-core-table-model";import{SqliteMasterModel}from"../models/sqlite-master-model";import{IN,NE,NOT_LIKE}from"../smart-db-globals";function getType(e){let t;return t=e?e.match(/varchar/i)||e.match(/text/i)?"string":e.match(/integer\(1\)/i)?"boolean":e.match(/date/i)?"Date":e.match(/integer/i)||e.match(/number/i)?"number":e.match(/long/i)||e.match(/raw/i)?"any":"SqlValueType":"SqlValueType",t}function getRelations(e){return new Promise((async(t,r)=>{e instanceof SmartDbOracle?e.getAll(OracleCatModel,{type:IN(["TABLE","VIEW"]),name:NOT_LIKE("BIN$%")}).then((a=>{a?t(a.map((e=>e.name.toLowerCase()))):r(e.getLastError())})).catch((e=>{r(e)})):e instanceof SmartDbBetterSqlite3?e.getAll(SqliteMasterModel,{type:IN(["table","view"]),name:NE("sqlite_sequence")}).then((a=>{a?t(a.map((e=>e.name))):r(e.getLastError())})).catch((e=>{r(e)})):r("unimplemented database type")}))}async function extractDbApi(e){console.log(`extract interface from '${e.db.getDbConnector()}' to '${e.modelDirectory}'`);const t=["break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","If","import","in","instanceOf","interface","new","null","return","super","switch","this","throw","true","try","typeOf","var","void","while","with"];fs.existsSync(e.modelDirectory)||fs.mkdirSync(e.modelDirectory,{recursive:!0}),getRelations(e.db).then((async r=>{let a=[];if((e.skipCoreTables||e.justCoreTables)&&await e.db.exists(SmartDbCoreTableModel)){const t=await e.db.getAll(SmartDbCoreTableModel);if(!1===t)throw e.db.getLastError();t.forEach((e=>{a.push(e.name)}))}const n=[];for(const s of r){let r=!0;if(e.justCoreTables?r=a.includes(s):e.skipCoreTables&&(r=!a.includes(s)),r){const r=s[0].toUpperCase()+_.camelCase(s.substring(1))+"Model",a=[];console.log("working on",s),await e.db.getTableInfo(s).then((o=>{const i=o.fields;let l=!1,c="",m="",p="",d="",b="";const u=s.toLowerCase().replace(/[^[a-z0-9]/g,"-")+"-model";if(n.push({className:r,fileName:u}),c+="/* eslint-disable @typescript-eslint/member-ordering */\n",c+="// noinspection JSUnusedGlobalSymbols\n",i){e.abstractModelModule==e.smartDbInterfaces?e.separateApi?(p+=`import {GenericModelData} from "${e.abstractModelModule}";\n`,b+=`import {AbstractModel, ModelAttributeMap} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):b+=`import {AbstractModel, GenericModelData, ModelAttributeMap} from "${e.abstractModelModule}";\n`:e.separateApi?(p+=`import {GenericModelData} from "${e.smartDbInterfaces}";\n`,b+=`import {ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):(b+=`import {GenericModelData, ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`),m+=`export interface ${r}Data extends GenericModelData {\n`;let n,o="",f="",h=!1;const g=[];i.forEach((e=>{const t=e.name.match(/([a-z0-9]{1,4})_/i);e.isPk&&(o=e.name,t&&(f=t[1])),t?g.includes(t[1])||g.push(t[1]):h=!0})),f||h||1!=g.length||(f=g[0]),f&&(n=new RegExp(`^${f}_(.*)$`));const y={};i.forEach((t=>{const r=getType(t.type);let o;l||"SqlValueType"!=r||(p+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,b+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,l=!0);const i=n&&t.name.match(n);o=i?_.camelCase(i[1]):_.camelCase(t.name);let c=!0;if(o!=t.name&&(a.push({name:o,attribute:o,type:r,typeScriptStyle:c,alias:t.name}),c=!1),o!=t.name.toLowerCase()&&(a.push({name:t.name.toLowerCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toLowerCase(),typeScriptStyle:c}),c=!1),o!=t.name.toUpperCase()&&a.push({name:t.name.toUpperCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toUpperCase(),typeScriptStyle:c}),t.cid){const e=a.find((e=>e.physical&&e.name.toLowerCase()==t.name.toLowerCase()));e||a.push({name:t.name,attribute:o,type:r,physical:!0,typeScriptStyle:c})}y[o]=r,"user"==s&&"name"==o&&(m+=" // @ts-ignore"),m+=` ${o}?: ${r};\n`})),m+="}\n",d+=`export class ${r} extends AbstractModel<${r}, ${r}Data> {\n`,_.forEach(y,((e,t)=>{d+=` private _${t}?: ${e};\n`})),"user"==s&&(d+=" private _hash?: string;\n",d+=" private _privileges?: string[];\n"),d+="\n",d+=" static readonly attributeMap: ModelAttributeMap = {\n",a.forEach(((e,t)=>{t>0&&(d+=",\n"),d+=` ${e.name}: {\n`,e.physical&&(d+=" physical: true,\n"),e.alias&&(d+=` alias: "${e.alias}",\n`),e.virtual&&(d+=" virtual: true,\n"),e.typeScriptStyle&&(d+=" typeScriptStyle: true,\n"),d+=` type: "${e.type}",\n`,d+=` attribute: "_${e.attribute}"\n`,d+=" }"})),d+="\n",d+=" };\n",d+="\n",d+=" static getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" static getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" static getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=` static from(other: ${r} | ${r}Data): ${r} {\n`,d+=` let value: ${r} = null;\n`,d+=" if (other) {\n",d+=` value = new ${r}();\n`,d+=` if (other instanceof ${r}) {\n`,d+=" Object.assign(value, other);\n",d+=" } else {\n",d+=" value.assign(other);\n",d+=" }\n",d+=" }\n",d+=" return value;\n",d+=" }\n",d+="\n",d+=` constructor(data?: ${r} | ${r}Data) {\n`,d+=" super();\n",d+=" if (data) {\n",d+=" this.assign(data);\n",d+=" }\n",d+=" }\n",d+="\n",d+=` public clone(): ${r} {\n`,d+=` return ${r}.from(this);\n`,d+=" }\n",d+="\n",d+=" public getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" public getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" public getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=" public getAttributeMap(): ModelAttributeMap {\n",d+=` return ${r}.attributeMap;\n`,d+=" }\n",a.forEach((e=>{let r;d+="\n",r=e.name.length<3||e.name.match(/_/)?"FunctionNamingConventionJS":"",""!==r&&(d+=` // noinspection ${r}\n`),d+=` get ${e.name}(): ${e.type} {\n`,d+=` return this._${e.attribute};\n`,d+=" }\n\n",""!==r&&(d+=` // noinspection ${r}\n`);let a=e.attribute;t.includes(a)&&(a="value"),d+=` set ${e.name}(${a}: ${e.type}) {\n`,d+=` this._${e.attribute} = ${a};\n`,d+=" }\n"})),"user"==s&&(d+="\n",d+=" get hash(): string {\n",d+=" return this._hash;\n",d+=" }\n\n",d+=" set hash(hash: string) {\n",d+=" this._hash = hash;\n",d+=" }\n",d+="\n",d+=" get privileges(): string[] {\n",d+=" return this._privileges;\n",d+=" }\n\n",d+=" set privileges(p: string[]) {\n",d+=" this._privileges = p;\n",d+=" }\n"),d+="}\n",e.separateApi?(m=c+"\n"+p+"\n"+m,fs.writeFileSync(`${e.modelDirectory}/${u+"-api.ts"}`,m),d=c+"\n"+b+"\n"+d):d=c+"\n"+b+"\n"+m+"\n"+d,fs.writeFileSync(`${e.modelDirectory}/${u+".ts"}`,d)}else console.log("error: table has no fields")})).catch((e=>{console.error(e),process.abort()}))}}let s=`import {AbstractModel} from "${e.abstractModelModule}";\n`;_.forEach(n,(e=>{s+=`import {${e.className}} from "./${e.fileName}";\n`})),s+="\n",s+="interface SmartDbDictionaryEntry {\n",s+=" cls: "+n.map((e=>"typeof "+e.className)).join(" |\n "),s+=";\n",s+="}\n\n",s+="export class SmartDbDictionary {\n",s+=" static models: { [relation: string]: SmartDbDictionaryEntry; } = {\n",_.forEach(n,(e=>{s+=` ${e.className}: {\n`,s+=` cls: ${e.className}\n`,s+=" },\n"})),s=s.substring(0,s.length-2)+"\n",s+=" };\n",s+="}\n",fs.writeFileSync(`${e.modelDirectory}/smart-db-dictionary.ts`,s)})).catch((e=>{console.error("extraction error",e)}))}(async()=>{let e=!1;const t=Array.from(process.argv);let r,a,n,s=!1;for(t.shift(),t.shift();t[0]&&"--"==t[0].substring(0,2);)"--separate-api"==t[0]?(e=!0,t.shift()):"--connector"==t[0]?(r=t[1],t.shift(),t.shift()):"--username"==t[0]?(a=t[1],t.shift(),t.shift()):"--password"==t[0]?(r=t[1],t.shift(),t.shift()):"--create"==t[0]?(s=!0,t.shift()):(console.error(`unknown option '${t[2]}'`),process.exit(-1));if(r||(r=process.env.ORACLE_SID),a||(a=process.env.ORA_USER),n||(n=process.env.ORA_PASS),r&&t.length<=1){let o;a&&n?o=new SmartDbOracle(r,{user:a,password:n,noDbLogging:!0}):s||fs.existsSync(r)?o=new SmartDbBetterSqlite3(r,{verbose:(e,...t)=>{console.log("DB LOG",e,...t)}}):(console.error(`ERROR: missing sqlite3 database file ${r}`),process.exit(-1)),1==t.length?o.onReady().subscribe((async r=>{if(r){o.getLogger().setDbLogging(!1);const r={skipCoreTables:!0,modelDirectory:t[0],smartDbInterfaces:"@egi/smart-db",abstractModelModule:"@egi/smart-db",separateApi:e,db:o};await extractDbApi(r)}})):fs.existsSync("src/helpers/extract-db-api.ts")?o.onReady().subscribe((t=>{if(t){const t={justCoreTables:!0,modelDirectory:"src/models/generated",smartDbInterfaces:"../smart-db-interfaces",abstractModelModule:"./abstract-model",separateApi:e,db:o};t.db.initDb({module:"smart-db-core",sqlFilesDirectory:"assets/sqlite3"}).then((async()=>{await extractDbApi(t)}))}else console.error("unable to connect to database")})):console.log("command must be run at the root directory of the smart-db project")}else console.log("USAGE: extract-db-api --connector {db-connector|path-to-db} [{options}] {target-models-directory}\n"),console.log("Options are: [--username {username}]"),console.log(" [--password {password}]"),console.log(" [--create"),console.log(" [--separate-api]")})();
1
+ import fs from"fs";import _ from"lodash";import process from"process";import{SmartDbBetterSqlite3}from"../drivers/smart-db-better-sqlite3";import{SmartDbOracle}from"../drivers/smart-db-oracle";import{OracleCatModel}from"../models/oracle-cat-model";import{SmartDbCoreTableModel}from"../models/smart-db-core-table-model";import{SqliteMasterModel}from"../models/sqlite-master-model";import{IN,NE,NOT_LIKE}from"../smart-db-globals";function getType(e){let t;return t=e?e.match(/varchar/i)||e.match(/text/i)?"string":e.match(/integer\(1\)/i)?"boolean":e.match(/date/i)?"Date":e.match(/integer/i)||e.match(/number/i)?"number":e.match(/long/i)||e.match(/raw/i)?"any":"SqlValueType":"SqlValueType",t}function getRelations(e){return new Promise((async(t,r)=>{e instanceof SmartDbOracle?e.getAll(OracleCatModel,{type:IN(["TABLE","VIEW"]),name:NOT_LIKE("BIN$%")}).then((a=>{a?t(a.map((e=>e.name.toLowerCase()))):r(e.getLastError())})).catch((e=>{r(e)})):e instanceof SmartDbBetterSqlite3?e.getAll(SqliteMasterModel,{type:IN(["table","view"]),name:NE("sqlite_sequence")}).then((a=>{a?t(a.map((e=>e.name))):r(e.getLastError())})).catch((e=>{r(e)})):r("unimplemented database type")}))}async function extractDbApi(e){console.log(`extract interface from '${e.db.getDbConnector()}' to '${e.modelDirectory}'`);const t=["break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","If","import","in","instanceOf","interface","new","null","return","super","switch","this","throw","true","try","typeOf","var","void","while","with"];fs.existsSync(e.modelDirectory)||fs.mkdirSync(e.modelDirectory,{recursive:!0}),getRelations(e.db).then((async r=>{let a=[];if((e.skipCoreTables||e.justCoreTables)&&await e.db.exists(SmartDbCoreTableModel)){const t=await e.db.getAll(SmartDbCoreTableModel);if(!1===t)throw e.db.getLastError();t.forEach((e=>{a.push(e.name)}))}const n=[];for(const s of r){let r=!0;if(e.justCoreTables?r=a.includes(s):e.skipCoreTables&&(r=!a.includes(s)),r){const r=s[0].toUpperCase()+_.camelCase(s.substring(1))+"Model",a=[];console.log("working on",s),await e.db.getTableInfo(s).then((o=>{const i=o.fields;let l=!1,c="",m="",p="",d="",b="";const u=s.toLowerCase().replace(/[^[a-z0-9]/g,"-")+"-model";if(n.push({className:r,fileName:u}),c+="/* eslint-disable @typescript-eslint/member-ordering */\n",c+="// noinspection JSUnusedGlobalSymbols\n",i){e.abstractModelModule==e.smartDbInterfaces?e.separateApi?(p+=`import {GenericModelData} from "${e.abstractModelModule}";\n`,b+=`import {AbstractModel, ModelAttributeMap} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):b+=`import {AbstractModel, GenericModelData, ModelAttributeMap} from "${e.abstractModelModule}";\n`:e.separateApi?(p+=`import {GenericModelData} from "${e.smartDbInterfaces}";\n`,b+=`import {ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):(b+=`import {GenericModelData, ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`),m+=`export interface ${r}Data extends GenericModelData {\n`;let n,o="",f="",h=!1;const g=[];i.forEach((e=>{const t=e.name.match(/([a-z0-9]{1,4})_/i);e.isPk&&(o=e.name,t&&(f=t[1])),t?g.includes(t[1])||g.push(t[1]):h=!0})),f||h||1!=g.length||(f=g[0]),f&&(n=new RegExp(`^${f}_(.*)$`));const y={};i.forEach((t=>{const r=getType(t.type);let o;l||"SqlValueType"!=r||(p+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,b+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,l=!0);const i=n&&t.name.match(n);o=i?_.camelCase(i[1]):_.camelCase(t.name);let c=!0;if(o!=t.name&&(a.push({name:o,attribute:o,type:r,typeScriptStyle:c,alias:t.name}),c=!1),o!=t.name.toLowerCase()&&(a.push({name:t.name.toLowerCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toLowerCase(),typeScriptStyle:c}),c=!1),o!=t.name.toUpperCase()&&a.push({name:t.name.toUpperCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toUpperCase(),typeScriptStyle:c}),t.cid){const e=a.find((e=>e.physical&&e.name.toLowerCase()==t.name.toLowerCase()));e||a.push({name:t.name,attribute:o,type:r,physical:!0,typeScriptStyle:c})}y[o]=r,"user"==s&&"name"==o&&(m+=" // @ts-ignore"),m+=` ${o}?: ${r};\n`})),m+="}\n",d+=`export class ${r} extends AbstractModel<${r}, ${r}Data> {\n`,_.forEach(y,((e,t)=>{d+=` private _${t}?: ${e};\n`})),"user"==s&&(d+=" private _hash?: string;\n",d+=" private _privileges?: string[];\n"),d+="\n",d+=" static readonly attributeMap: ModelAttributeMap = {\n",a.forEach(((e,t)=>{t>0&&(d+=",\n"),d+=` ${e.name}: {\n`,e.physical&&(d+=" physical: true,\n"),e.alias&&(d+=` alias: "${e.alias}",\n`),e.virtual&&(d+=" virtual: true,\n"),e.typeScriptStyle&&(d+=" typeScriptStyle: true,\n"),d+=` type: "${e.type}",\n`,d+=` attribute: "_${e.attribute}"\n`,d+=" }"})),d+="\n",d+=" };\n",d+="\n",d+=" static getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" static getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" static getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=` static from(other: ${r} | ${r}Data): ${r} {\n`,d+=` let value: ${r} = null;\n`,d+=" if (other) {\n",d+=` value = new ${r}();\n`,d+=` if (other instanceof ${r}) {\n`,d+=" Object.assign(value, other);\n",d+=" } else {\n",d+=" value.assign(other);\n",d+=" }\n",d+=" }\n",d+=" return value;\n",d+=" }\n",d+="\n",d+=` constructor(data?: ${r} | ${r}Data) {\n`,d+=" super();\n",d+=" if (data) {\n",d+=" this.assign(data);\n",d+=" }\n",d+=" }\n",d+="\n",d+=` public clone(): ${r} {\n`,d+=` return ${r}.from(this);\n`,d+=" }\n",d+="\n",d+=" public getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" public getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" public getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=" public getAttributeMap(): ModelAttributeMap {\n",d+=` return ${r}.attributeMap;\n`,d+=" }\n",a.forEach((e=>{let r;d+="\n",r=e.name.length<3||e.name.match(/_/)?"FunctionNamingConventionJS":"",""!==r&&(d+=` // noinspection ${r}\n`),d+=` get ${e.name}(): ${e.type} {\n`,d+=` return this._${e.attribute};\n`,d+=" }\n\n",""!==r&&(d+=` // noinspection ${r}\n`);let a=e.attribute;t.includes(a)&&(a="value"),d+=` set ${e.name}(${a}: ${e.type}) {\n`,d+=` this._${e.attribute} = ${a};\n`,d+=" }\n"})),"user"==s&&(d+="\n",d+=" get hash(): string {\n",d+=" return this._hash;\n",d+=" }\n\n",d+=" set hash(hash: string) {\n",d+=" this._hash = hash;\n",d+=" }\n",d+="\n",d+=" get privileges(): string[] {\n",d+=" return this._privileges;\n",d+=" }\n\n",d+=" set privileges(p: string[]) {\n",d+=" this._privileges = p;\n",d+=" }\n"),d+="}\n",e.separateApi?(m=c+"\n"+p+"\n"+m,fs.writeFileSync(`${e.modelDirectory}/${u+"-api.ts"}`,m),d=c+"\n"+b+"\n"+d):d=c+"\n"+b+"\n"+m+"\n"+d,fs.writeFileSync(`${e.modelDirectory}/${u+".ts"}`,d)}else console.log("error: table has no fields")})).catch((e=>{console.error(e),process.abort()}))}}let s=`import {AbstractModel} from "${e.abstractModelModule}";\n`;_.forEach(n,(e=>{s+=`import {${e.className}} from "./${e.fileName}";\n`})),s+="\n",s+="interface SmartDbDictionaryEntry {\n",s+=" cls: "+n.map((e=>"typeof "+e.className)).join(" |\n "),s+=";\n",s+="}\n\n",s+="export class SmartDbDictionary {\n",s+=" static models: { [relation: string]: SmartDbDictionaryEntry; } = {\n",_.forEach(n,(e=>{s+=` ${e.className}: {\n`,s+=` cls: ${e.className}\n`,s+=" },\n"})),s=s.substring(0,s.length-2)+"\n",s+=" };\n",s+="}\n",fs.writeFileSync(`${e.modelDirectory}/smart-db-dictionary.ts`,s)})).catch((e=>{console.error("extraction error",e)}))}(async()=>{let e=!1;const t=Array.from(process.argv);let r,a,n,s=!1;for(t.shift(),t.shift();t[0]&&"--"==t[0].substring(0,2);)"--separate-api"==t[0]?(e=!0,t.shift()):"--connector"==t[0]?(r=t[1],t.shift(),t.shift()):"--username"==t[0]?(a=t[1],t.shift(),t.shift()):"--password"==t[0]?(r=t[1],t.shift(),t.shift()):"--create"==t[0]?(s=!0,t.shift()):(console.error(`unknown option '${t[2]}'`),process.exit(-1));if(r||(r=process.env.ORACLE_SID),a||(a=process.env.ORA_USER),n||(n=process.env.ORA_PASS),r&&t.length<=1){let o;a&&n?o=new SmartDbOracle(r,{user:a,password:n,noDbLogging:!0}):s||fs.existsSync(r)?o=new SmartDbBetterSqlite3(r,{}):(console.error(`ERROR: missing sqlite3 database file ${r}`),process.exit(-1)),1==t.length?o.onReady().subscribe((async r=>{if(r){o.getLogger().setDbLogging(!1);const r={skipCoreTables:!0,modelDirectory:t[0],smartDbInterfaces:"@egi/smart-db",abstractModelModule:"@egi/smart-db",separateApi:e,db:o};await extractDbApi(r)}})):fs.existsSync("src/helpers/extract-db-api.ts")?o.onReady().subscribe((t=>{if(t){const t={justCoreTables:!0,modelDirectory:"src/models/generated",smartDbInterfaces:"../smart-db-interfaces",abstractModelModule:"./abstract-model",separateApi:e,db:o};t.db.initDb({module:"smart-db-core",sqlFilesDirectory:"assets/sqlite3"}).then((async()=>{await extractDbApi(t)}))}else console.error("unable to connect to database")})):console.log("command must be run at the root directory of the smart-db project")}else console.log("USAGE: extract-db-api --connector {db-connector|path-to-db} [{options}] {target-models-directory}\n"),console.log("Options are: [--username {username}]"),console.log(" [--password {password}]"),console.log(" [--create"),console.log(" [--separate-api]")})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@egi/smart-db",
3
- "version": "2.5.6",
3
+ "version": "2.5.8",
4
4
  "description": "Unified Smart DB Access",
5
5
  "author": "Marcel Egloff",
6
6
  "type": "module",
@@ -25,6 +25,7 @@
25
25
  "build-test-models": "tsc --build tsconfig.test-model.json",
26
26
  "tsc": "tsc --build tsconfig.json",
27
27
  "tsc-pro": "tsc --build tsconfig.pro.json",
28
+ "tsc:pro:watch": "tsc --build tsconfig.pro.json --watch",
28
29
  "tsc-watch": "tsc --build tsconfig.json --watch",
29
30
  "terser": "node dist/helpers/terser-tree 'dist/**/*.js' && rm -f dist/helpers/terser-tree.*",
30
31
  "extract-db-api": "bin/extract-db-api && tsc --build tsconfig.test-model.json"
@@ -64,7 +65,6 @@
64
65
  "dependencies": {
65
66
  "lodash": "^4.17.21",
66
67
  "node-oracledb": "^1.0.2",
67
- "oracledb": "^5.5.0",
68
68
  "rxjs": "^7.8.0",
69
69
  "sqlite3": "^5.1.4"
70
70
  },
@@ -86,6 +86,7 @@
86
86
  "optionalDependencies": {
87
87
  "better-sqlite3": "^7.1.0",
88
88
  "mysql": "^2.18.1",
89
+ "oracledb": "^5.5.0",
89
90
  "mysql2": "^2.1.0"
90
91
  },
91
92
  "license": "UNLICENSED"
@@ -1,3 +1,4 @@
1
+ import { Observable } from "rxjs";
1
2
  import { AbstractModel } from "./models/abstract-model";
2
3
  import { SmartDbDictionary } from "./models/smart-db-dictionary";
3
4
  import { SmartDbVersionViewModel } from "./models/smart-db-version-view-model";
@@ -167,6 +168,12 @@ export interface SmartDbApi {
167
168
  supportSyncCalls(): boolean;
168
169
  supportAsyncCalls(): boolean;
169
170
  closeSync(): boolean;
171
+ onReady(): Observable<boolean>;
172
+ hasTransaction(): boolean;
173
+ commitSync(): void;
174
+ rollbackSync(): void;
175
+ commit(): Promise<void>;
176
+ rollback(): Promise<void>;
170
177
  close(): Promise<void>;
171
178
  }
172
179
  export interface SmartDbDatabase {
package/smart-db-log.d.ts CHANGED
@@ -39,6 +39,7 @@ export declare class SmartDbLog {
39
39
  private userId;
40
40
  private dbLogging;
41
41
  private isLogging;
42
+ private logFifo;
42
43
  constructor(db?: SmartDbApi);
43
44
  getConsoleLogLevel(): SeverityLevelValue;
44
45
  setConsoleLogLevel(consoleLogLevel: SeverityLevelValue): void;
@@ -53,6 +54,7 @@ export declare class SmartDbLog {
53
54
  logWarning(type: SmartDbLogLocationValue, location: string, message: string | Error, data?: any): void;
54
55
  logInfo(type: SmartDbLogLocationValue, location: string, message: string | Error, data?: any): void;
55
56
  logDebug(type: SmartDbLogLocationValue, location: string, message: string | Error, data?: any): void;
56
- writeLog(type: SmartDbLogLocationValue, location: string, severity: SeverityCodeValue, message: string | Error, data: unknown, timestamp?: Date | string | number): Promise<void>;
57
+ writeLog(type: SmartDbLogLocationValue, location: string, severity: SeverityCodeValue, message: string | Error, data: unknown, timestamp?: Date | string | number): void;
58
+ private nextStackEntry;
57
59
  }
58
60
  export declare const smartDbLog: SmartDbLog;
package/smart-db-log.js CHANGED
@@ -1 +1 @@
1
- import _ from"lodash";import{SmartDbLogModel}from"./models/smart-db-log-model";import{SmartDbDateRegexp,SmartDbTimestampRegexp,toSmartDbTimestamp}from"./smart-db-globals";export const SmartDbLogLocation={Frontend:"frontend",Backend:"backend",Database:"database",UpgradeManager:"upgrade-manager",Other:"other"};export const SeverityCode={Fatal:"F",Error:"E",Warning:"W",Info:"I",Debug:"D",Local:"L"};export const SeverityLevel={None:0,Fatal:1,Error:2,Warning:3,Info:4,Debug:5,All:9};export class SmartDbLog{constructor(e){this.userId=null,this.dbLogging=!0,this.isLogging=!1,this.db=e,this.dbLogging=!!e,this.dbLogLevel=SeverityLevel.Info,this.consoleLogLevel=SeverityLevel.Info}getConsoleLogLevel(){return this.consoleLogLevel}setConsoleLogLevel(e){this.consoleLogLevel=e}getDbLogLevel(){return this.dbLogLevel}setDbLogLevel(e){this.dbLogLevel=e}setDb(e){this.db=e,this.db?this.db.exists(SmartDbLogModel).then((e=>{this.dbLogging=e})).catch((e=>{throw e})):this.dbLogging=!1}setUserId(e){this.userId=e}setDbLogging(e){this.dbLogging=e}getDbLogging(){return this.dbLogging}logFatal(e,t,o,r){this.writeLog(e,t,SeverityCode.Fatal,o,r)}logError(e,t,o,r){this.writeLog(e,t,SeverityCode.Error,o,r)}logWarning(e,t,o,r){this.writeLog(e,t,SeverityCode.Warning,o,r)}logInfo(e,t,o,r){this.writeLog(e,t,SeverityCode.Info,o,r)}logDebug(e,t,o,r){this.writeLog(e,t,SeverityCode.Debug,o,r)}async writeLog(e,t,o,r,s,i){try{if(this.isLogging)console.error("recursive logging error:",e,t,o,r,s);else{this.isLogging=!0;let a,g,n=SeverityLevel.None;switch(o){case SeverityCode.Fatal:a=console.error,n=SeverityLevel.Fatal;break;case SeverityCode.Error:a=console.error,n=SeverityLevel.Error;break;case SeverityCode.Warning:a=console.warn,n=SeverityLevel.Warning;break;case SeverityCode.Info:a=console.info,n=SeverityLevel.Info;break;case SeverityCode.Debug:a=console.debug,n=SeverityLevel.Debug}if(_.isString(i)){const e=i.match(SmartDbTimestampRegexp),t=e&&i.match(SmartDbDateRegexp);g=e?e[1]:t?t[1]+".000":toSmartDbTimestamp(new Date)}else g=_.isDate(i)?toSmartDbTimestamp(i):_.isNumber(i)?toSmartDbTimestamp(new Date(i)):toSmartDbTimestamp(new Date);if(_.isObjectLike(r))if(r instanceof Error)s||(s=r),r=r.message;else try{r=JSON.stringify(r)}catch(e){r=r.toString()}else"boolean"==typeof r&&(r=r?"<true>":"<false>");if(s){if(s instanceof Error)s=s.stack;else if(_.isObject(s))try{s=JSON.stringify(s)}catch(e){s=s.toString(),this.db&&this.dbLogLevel>=n&&console.error("unable to stringify log data",s)}else s.toString?s=s.toString():(s=null,this.db&&this.dbLogLevel>=n&&console.error("unable to store log data",s));s&&(r+=` (${s})`)}if(this.consoleLogLevel>=n){const i=e.substring(0,1).toUpperCase();if(e==SmartDbLogLocation.Database&&this.db&&a(`${o}-${i}-${g}: last statement:`,this.db.getLastBuildData()),a(`${o}-${i}-${g}: ${r}`),this.dbLogging)try{e==SmartDbLogLocation.Database&&(s||(s=JSON.stringify(this.db.getLastBuildData()))),this.db.supportAsyncCalls()?await this.db.insert(SmartDbLogModel,{severity:o,type:e,location:t,info:r||"<empty>",data:s,user:this.userId,timestamp:new Date(g)}).then((()=>{this.db.exec("commit")})).catch((e=>{console.error(`F-B-${g}: unable to write the log statement below to database`,e,e.code,e.name,e.message)})):this.db.insertSync(SmartDbLogModel,{severity:o,type:e,location:t,info:r||"<empty>",data:s,user:this.userId,timestamp:new Date(g)})}catch(e){console.error(`F-B-${g}: unable to write the log statement below to database`,e,e.code,e.name,e.message);try{a(`F-B-${g}: last statement:`,this.db.getLastBuildData())}catch{a(`F-B-${g}: last statement not available`)}}}}this.isLogging=!1}catch(e){this.isLogging=!1,console.error("fatal logging error",e)}}}export const smartDbLog=new SmartDbLog;
1
+ import _ from"lodash";import{SmartDbLogModel}from"./models/smart-db-log-model";import{SmartDbDateRegexp,SmartDbTimestampRegexp,toSmartDbTimestamp}from"./smart-db-globals";export const SmartDbLogLocation={Frontend:"frontend",Backend:"backend",Database:"database",UpgradeManager:"upgrade-manager",Other:"other"};export const SeverityCode={Fatal:"F",Error:"E",Warning:"W",Info:"I",Debug:"D",Local:"L"};export const SeverityLevel={None:0,Fatal:1,Error:2,Warning:3,Info:4,Debug:5,All:9};export class SmartDbLog{constructor(e){this.userId=null,this.dbLogging=!0,this.isLogging=!1,this.logFifo=[],this.db=e,this.dbLogging=!!e,this.dbLogLevel=SeverityLevel.Info,this.consoleLogLevel=SeverityLevel.Info}getConsoleLogLevel(){return this.consoleLogLevel}setConsoleLogLevel(e){this.consoleLogLevel=e}getDbLogLevel(){return this.dbLogLevel}setDbLogLevel(e){this.dbLogLevel=e}setDb(e){this.db=e,this.db?this.db.exists(SmartDbLogModel).then((e=>{this.dbLogging=e})).catch((e=>{throw e})):this.dbLogging=!1}setUserId(e){this.userId=e}setDbLogging(e){this.dbLogging=e}getDbLogging(){return this.dbLogging}logFatal(e,t,o,i){this.writeLog(e,t,SeverityCode.Fatal,o,i)}logError(e,t,o,i){this.writeLog(e,t,SeverityCode.Error,o,i)}logWarning(e,t,o,i){this.writeLog(e,t,SeverityCode.Warning,o,i)}logInfo(e,t,o,i){this.writeLog(e,t,SeverityCode.Info,o,i)}logDebug(e,t,o,i){this.writeLog(e,t,SeverityCode.Debug,o,i)}writeLog(e,t,o,i,s,r){try{if(this.isLogging)this.logFifo.push({type:e,location:t,severity:o,message:i,data:s,timestamp:r??new Date});else{this.isLogging=!0;let a,n,g=SeverityLevel.None;switch(o){case SeverityCode.Fatal:a=console.error,g=SeverityLevel.Fatal;break;case SeverityCode.Error:a=console.error,g=SeverityLevel.Error;break;case SeverityCode.Warning:a=console.warn,g=SeverityLevel.Warning;break;case SeverityCode.Info:a=console.info,g=SeverityLevel.Info;break;case SeverityCode.Debug:a=console.debug,g=SeverityLevel.Debug}if(_.isString(r)){const e=r.match(SmartDbTimestampRegexp),t=e&&r.match(SmartDbDateRegexp);n=e?e[1]:t?t[1]+".000":toSmartDbTimestamp(new Date)}else n=_.isDate(r)?toSmartDbTimestamp(r):_.isNumber(r)?toSmartDbTimestamp(new Date(r)):toSmartDbTimestamp(new Date);if(_.isObjectLike(i))if(i instanceof Error)s||(s=i),i=i.message;else try{i=JSON.stringify(i)}catch(e){i=i.toString()}else"boolean"==typeof i&&(i=i?"<true>":"<false>");if(s){if(s instanceof Error)s=s.stack;else if(_.isObject(s))try{s=JSON.stringify(s)}catch(e){s=s.toString(),this.db&&this.dbLogLevel>=g&&console.error("unable to stringify log data",s)}else s.toString?s=s.toString():(s=null,this.db&&this.dbLogLevel>=g&&console.error("unable to store log data",s));s&&(i+=` (${s})`)}if(this.consoleLogLevel>=g){const r=e.substring(0,1).toUpperCase();if(e==SmartDbLogLocation.Database&&this.db&&a(`${o}-${r}-${n}: last statement:`,this.db.getLastBuildData()),a(`${o}-${r}-${n}: ${i}`),this.dbLogging)try{e==SmartDbLogLocation.Database&&(s||(s=JSON.stringify(this.db.getLastBuildData()))),this.db.supportAsyncCalls()?this.db.insert(SmartDbLogModel,{severity:o,type:e,location:t,info:i||"<empty>",data:s,user:this.userId,timestamp:new Date(n)}).then((()=>{this.db.commit().then((()=>{this.isLogging=!1,this.nextStackEntry()})).catch((e=>{console.error(`F-B-${n}: unable to commit log transaction`,e,e.code,e.name,e.message),this.isLogging=!1,this.nextStackEntry()}))})).catch((e=>{console.error(`F-B-${n}: unable to write the log statement below to database`,e,e.code,e.name,e.message),this.isLogging=!1,this.nextStackEntry()})):(this.db.insertSync(SmartDbLogModel,{severity:o,type:e,location:t,info:i||"<empty>",data:s,user:this.userId,timestamp:new Date(n)}),this.isLogging=!1,this.nextStackEntry())}catch(e){console.error(`F-B-${n}: unable to write the log statement below to database`,e,e.code,e.name,e.message);try{a(`F-B-${n}: last statement:`,this.db.getLastBuildData())}catch{a(`F-B-${n}: last statement not available`)}this.isLogging=!1,this.nextStackEntry()}else this.isLogging=!1,this.nextStackEntry()}else this.isLogging=!1,this.nextStackEntry()}}catch(e){this.isLogging=!1,console.error("fatal logging error",e)}}nextStackEntry(){if(this.logFifo.length>0){const e=this.logFifo.shift();this.writeLog(e.type,e.location,e.severity,e.message,e.data,e.timestamp)}}}export const smartDbLog=new SmartDbLog;
@@ -1 +1 @@
1
- import*as fs from"fs";import _ from"lodash";import{SmartDbVersionViewModel}from"./models/smart-db-version-view-model";import{SmartDbLogLocation}from"./smart-db-log";export class SmartDbUpgradeManager{constructor(e){this.db=e,this.log=e.getLogger()}hasDatabaseModule(e){return new Promise(((t,a)=>{this.db.exists(SmartDbVersionViewModel).then((o=>{o?this.db.getFirst(SmartDbVersionViewModel,{module:e}).then((e=>{t(!!e)})).catch((e=>{a(e)})):t(!1)})).catch((e=>{a(e)}))}))}prepareDatabase(e){return new Promise(((t,a)=>{try{const o=fs.existsSync(e.sqlFilesDirectory)&&fs.statSync(e.sqlFilesDirectory);if(o&&o.isDirectory()){const o=e.sqlFilesDirectory+`/${e.module}-init.sql`;fs.existsSync(o)?this.hasDatabaseModule(e.module).then((r=>{r?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`database module '${e.module}' exists`),this.upgradeDatabase(e).then((a=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`connected to module '${e.module}' version ${a.versionString}`),t(a)})).catch((e=>{a(e)}))):(this.log.setDbLogging(!1),this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`database '${e.module}' doesn't exists - begin creation`),this.executeSqlScript(o).then((()=>{this.log.setDbLogging(!0),this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase","database module creation complete"),this.hasDatabaseModule(e.module).then((o=>{o?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`successfully initialized database for new module ${e.module}`),this.upgradeDatabase(e).then((a=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`connected to module '${e.module}' version ${a.versionString}`),t(a)})).catch((e=>{a(e)}))):a("error running database initialization script")})).catch((e=>{a(e)}))})).catch((e=>{a(e)})))})).catch((e=>{a(e)})):a(`missing mandatory '${e.module}-init.sql' file in ${o}`)}else a(`option 'sqlFilesDirectory' (${e.sqlFilesDirectory}) must point to an existing directory (absolute or relative to ${fs.realpathSync(".")})`)}catch(e){a(e)}}))}executeSqlScript(e){const t=fs.readFileSync(e,"utf8");return this.db.exec(t)}upgradeDatabase(e){return new Promise(((t,a)=>{this.db.getFirst(SmartDbVersionViewModel,{module:e.module}).then((o=>{o?fs.readdir(e.sqlFilesDirectory,((r,s)=>{if(r)a(`unable to read database files from ${e.sqlFilesDirectory}`);else{let r=[];s.forEach((t=>{const a=new RegExp(`^${e.module}-update.([0-9]{3})\\..*\\.sql$`),s=t.match(a);if(s){parseInt(s[1],10)>o.sequence&&r.push(t)}})),r=_.sortBy(r),this.executeScriptSequentially(r,e).then((()=>{this.db.getFirst(SmartDbVersionViewModel,{module:e.module},"*","sequence DESC").then((e=>{e?t(SmartDbVersionViewModel.from(e)):a("unable to determine final smart db version")})).catch((e=>{a(e)}))})).catch((e=>{a(e)}))}})):a(`missing version entry for module ${e.module} - add the mandatory insert into smart_db_version statement to your database creation script!`)})).catch((e=>{a(e)}))}))}executeScriptSequentially(e,t){return new Promise((async(a,o)=>{e&&e.length>0?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"upgradeDatabase",`execute update script '${e[0]}' for module ${t.module}`),await this.executeSqlScript(`${t.sqlFilesDirectory}/${e[0]}`).then((()=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"upgradeDatabase",`successfully executed update script '${e[0]}' for module ${t.module}`),1==e.length&&a(!0)})).catch((e=>{o(e)})),e.length>1&&this.executeScriptSequentially(e.splice(1),t).then((e=>{a(e)})).catch((e=>{o(e)}))):a(!0)}))}}
1
+ import*as fs from"fs";import _ from"lodash";import{SmartDbVersionModel}from"./models/smart-db-version-model";import{SmartDbVersionViewModel}from"./models/smart-db-version-view-model";import{SmartDbLogLocation}from"./smart-db-log";export class SmartDbUpgradeManager{constructor(e){this.db=e,this.log=e.getLogger()}hasDatabaseModule(e){return new Promise(((t,a)=>{this.db.exists(SmartDbVersionModel).then((o=>{o?this.db.getFirst(SmartDbVersionModel,{module:e}).then((e=>{t(!!e)})).catch((e=>{a(e)})):t(!1)})).catch((e=>{a(e)}))}))}prepareDatabase(e){return new Promise(((t,a)=>{try{const o=fs.existsSync(e.sqlFilesDirectory)&&fs.statSync(e.sqlFilesDirectory);if(o&&o.isDirectory()){const o=e.sqlFilesDirectory+`/${e.module}-init.sql`;fs.existsSync(o)?this.hasDatabaseModule(e.module).then((r=>{r?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`database module '${e.module}' exists`),this.upgradeDatabase(e).then((a=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`connected to module '${e.module}' version ${a.versionString}`),t(a)})).catch((e=>{a(e)}))):(this.log.setDbLogging(!1),this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`database module '${e.module}' doesn't exists - begin creation`),this.executeSqlScript(o).then((()=>{this.log.setDbLogging(this.db.dbLogging),this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase","database module creation complete"),this.hasDatabaseModule(e.module).then((o=>{o?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`successfully initialized database for new module ${e.module}`),this.upgradeDatabase(e).then((a=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"prepareDatabase",`connected to module '${e.module}' version ${a.versionString}`),t(a)})).catch((e=>{a(e)}))):a("error running database initialization script")})).catch((e=>{a(e)}))})).catch((e=>{a(e)})))})).catch((e=>{a(e)})):a(`missing mandatory '${e.module}-init.sql' file in ${o}`)}else a(`option 'sqlFilesDirectory' (${e.sqlFilesDirectory}) must point to an existing directory (absolute or relative to ${fs.realpathSync(".")})`)}catch(e){a(e)}}))}executeSqlScript(e){return new Promise((async(t,a)=>{const o=fs.readFileSync(e,"utf8"),r=[];let s=0,i=0,n=!1,l=0,c=!1,d="";for(;i<o.length;)"'"==o[i]?n=!n:n||c||!o.substring(i,i+6).match(/^begin(\s|\n)/)?l>0&&!n&&!c&&o.substring(i,i+4).match(/^end;/)?(l-=1,i+=4):0!==l||n||"/"!=o[i]?n||"-"!=o[i]||"-"!=o[i+1]?c&&"\n"==o[i]?(c=!1,s=i+2):0!==l||n||c||";"!=o[i]||(r.push(d+o.substring(s,i).trim()),d="",s=i+2):(d+=o.substring(s,i),c=!0):(r.push(d+o.substring(s,i).trim()),d="",s=i+2):(l+=1,i+=6),i+=1;if(s<o.length){const e=o.substring(s,o.length).trim().replace(/;$/,"");console.log(e),""!==e&&r.push(e)}for(const e of r)try{await this.db.exec(e)}catch(t){if("drop "!=e.substring(0,5).toLowerCase())throw t}await this.db.exec("COMMIT WORK"),t()}))}upgradeDatabase(e){return new Promise(((t,a)=>{this.db.getFirst(SmartDbVersionViewModel,{module:e.module}).then((o=>{o?fs.readdir(e.sqlFilesDirectory,((r,s)=>{if(r)a(`unable to read database files from ${e.sqlFilesDirectory}`);else{let r=[];s.forEach((t=>{const a=new RegExp(`^${e.module}-update.([0-9]{3})\\..*\\.sql$`),s=t.match(a);if(s){parseInt(s[1],10)>o.sequence&&r.push(t)}})),r=_.sortBy(r),this.executeScriptSequentially(r,e).then((()=>{this.db.getFirst(SmartDbVersionViewModel,{module:e.module},"*","sequence DESC").then((e=>{e?t(SmartDbVersionViewModel.from(e)):a("unable to determine final smart db version")})).catch((e=>{a(e)}))})).catch((e=>{a(e)}))}})):a(`missing version entry for module ${e.module} - add the mandatory insert into smart_db_version statement to your database creation script!`)})).catch((e=>{a(e)}))}))}executeScriptSequentially(e,t){return new Promise((async(a,o)=>{e&&e.length>0?(this.log.logInfo(SmartDbLogLocation.UpgradeManager,"upgradeDatabase",`execute update script '${e[0]}' for module ${t.module}`),await this.executeSqlScript(`${t.sqlFilesDirectory}/${e[0]}`).then((()=>{this.log.logInfo(SmartDbLogLocation.UpgradeManager,"upgradeDatabase",`successfully executed update script '${e[0]}' for module ${t.module}`),1==e.length&&a(!0)})).catch((e=>{o(e)})),e.length>1&&this.executeScriptSequentially(e.splice(1),t).then((e=>{a(e)})).catch((e=>{o(e)}))):a(!0)}))}}
package/smart-db.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Observable, Subject } from "rxjs";
1
+ import { BehaviorSubject, Subject } from "rxjs";
2
2
  import { AbstractModel } from "./models/abstract-model";
3
3
  import { SmartDbVersionViewModel } from "./models/smart-db-version-view-model";
4
4
  import { GenericModelData, IndexedGenericModelData, SmartDbApi, SmartDbConnector, SmartDbDatabase, SmartDbOptions, SmartDbRunResult, SmartDbSqlOptions, SmartDbTableInfo, SqlFieldDescriptor, SqlLimit, SqlOrderBy, SqlUpdateValues, SqlValueType, SqlWhere } from "./smart-db-interfaces";
@@ -18,6 +18,11 @@ export declare abstract class SmartDb implements SmartDbApi {
18
18
  abstract getDbQuote(): string;
19
19
  abstract getTableInfo(table: string): Promise<SmartDbTableInfo>;
20
20
  abstract getDbConnector(): string | SmartDbConnector;
21
+ abstract hasTransaction(): boolean;
22
+ abstract commit(): Promise<void>;
23
+ abstract rollback(): Promise<void>;
24
+ abstract commitSync(): void;
25
+ abstract rollbackSync(): void;
21
26
  protected abstract statementRun(buildData: SmartDbSqlBuildData): Promise<SmartDbRunResult>;
22
27
  protected abstract statementRunSync(buildData: SmartDbSqlBuildData): SmartDbRunResult;
23
28
  protected abstract statementGet(buildData: SmartDbSqlBuildData): Promise<any>;
@@ -25,18 +30,18 @@ export declare abstract class SmartDb implements SmartDbApi {
25
30
  protected abstract statementGetAll(buildData: SmartDbSqlBuildData): Promise<any[]>;
26
31
  protected abstract statementGetAllSync(buildData: SmartDbSqlBuildData): any[];
27
32
  protected: any;
33
+ readonly dbLogging: boolean;
28
34
  protected dbConnector: SmartDbConnector;
29
35
  protected lastError: Error;
30
36
  protected lastBuildData: SmartDbSqlBuildData;
31
37
  protected smartDbLog: SmartDbLog;
32
- protected _onReady: Subject<boolean>;
38
+ protected _onReady: BehaviorSubject<boolean>;
33
39
  protected db: SmartDbDatabase;
34
40
  private dictionaries;
35
41
  private _isReady;
36
- private dbLogging;
37
42
  protected constructor(dbConnector: SmartDbConnector, noDbLogging?: boolean);
38
43
  initDb(appOptions: SmartDbOptions): Promise<SmartDbVersionViewModel[]>;
39
- onReady(): Observable<boolean>;
44
+ onReady(): Subject<boolean>;
40
45
  getLogger(): SmartDbLog;
41
46
  getDb(): SmartDbDatabase;
42
47
  getLastBuildData(): SmartDbSqlBuildData;
package/smart-db.js CHANGED
@@ -1 +1 @@
1
- import _ from"lodash";import{Subject}from"rxjs";import{AbstractModel}from"./models/abstract-model";import{SmartDbDictionary}from"./models/smart-db-dictionary";import{SmartDbLogModel}from"./models/smart-db-log-model";import{IN,smartDbToDate,toSmartDbDate,toSmartDbTimestamp}from"./smart-db-globals";import{FieldNamingStyle,SqlFieldOperationType,SqlOperationType}from"./smart-db-interfaces";import{SmartDbLog,SmartDbLogLocation}from"./smart-db-log";import{SmartDbSqlBuildData}from"./smart-db-sql-build-data";import{SmartDbUpgradeManager}from"./smart-db-upgrade-manager";export class SmartDb{constructor(e,t){this.dictionaries=[],this.dbConnector=e,this.smartDbLog=new SmartDbLog(t?null:this),this.dbLogging=!t,this.db=null,this._onReady=new Subject}initDb(e){return new Promise(((t,a)=>{this.dictionaries.push(SmartDbDictionary);const s=new SmartDbUpgradeManager(this);let r;r=import.meta&&import.meta.url?import.meta.url.replace(/file:\/\//,"").replace(/\/[^/]*$/,""):__dirname;const i={module:"smart-db-core",sqlFilesDirectory:r+"/assets/"+this.getDatabaseType()};s.prepareDatabase(i).then((r=>{this.exists(SmartDbLogModel).then((i=>{this.smartDbLog.setDbLogging(this.dbLogging&&i),s.prepareDatabase(e).then((e=>{t([r,e])}),(e=>{a(e)}))}))})).catch((e=>{a(e)}))}))}onReady(){return this._onReady.asObservable()}getLogger(){return this.smartDbLog}getDb(){return this.db}getLastBuildData(){return this.lastBuildData}get(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){t||(t={});const r=this.buildSelectStatement(e,t);t.firstOnly||t.count?this.statementGet(r).then((s=>{a(this.prepareResultRow(e,s,t))})).catch((e=>{s(e)})):this.statementGetAll(r).then((s=>{a(this.prepareResultRows(e,s,t))})).catch((e=>{s(e)}))}else s("get: this database driver doesn't support async calls")}))}getSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{t||(t={});const a=this.buildSelectStatement(e,t);let s;if(t.firstOnly||t.count){const r=this.statementGetSync(a);s=this.prepareResultRow(e,r,t)}else{const r=this.statementGetAllSync(a);s=this.prepareResultRows(e,r,t)}return s}));throw"getSync: this database driver doesn't support sync calls"}getFirst(e,t,a,s){return this.get(e,{firstOnly:!0,where:t,fields:a,orderBy:s})}getFirstSync(e,t,a,s){return this.getSync(e,{firstOnly:!0,where:t,fields:a,orderBy:s})}getAll(e,t,a,s,r){return this.get(e,{where:t,fields:a,orderBy:s,limit:r})}getAllSync(e,t,a,s,r){return this.getSync(e,{where:t,fields:a,orderBy:s,limit:r})}delete(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){const r=this.buildDeleteStatement(e,t);this.statementRun(r).then((e=>{e?a(e.changes):s(this.getLastError())})).catch((e=>{s(e)}))}else s("delete: this database driver doesn't support sync calls")}))}deleteSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{const a=this.buildDeleteStatement(e,t);return this.statementRunSync(a).changes}));throw"deleteSync: this database driver doesn't support sync calls"}update(e,t,a){return new Promise(((s,r)=>{if(this.supportAsyncCalls()){const i=this.buildUpdateStatement(e,t,a);this.statementRun(i).then((e=>{e?s(e.changes):r(this.getLastError())})).catch((e=>{r(e)}))}else r("update: this database driver doesn't support sync calls")}))}updateSync(e,t,a){if(this.supportSyncCalls())return this.saveExecute((()=>{const s=this.buildUpdateStatement(e,t,a);return this.statementRunSync(s).changes}));throw"updateSync: this database driver doesn't support sync calls"}insert(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){const r=this.buildInsertStatement(e,t);this.statementRun(r).then((e=>{e?a(e.lastId):s(this.getLastError())})).catch((e=>{s(e)}))}else s("insert: this database driver doesn't support sync calls")}))}insertSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{const a=this.buildInsertStatement(e,t);return this.statementRunSync(a).lastId}));throw"insertSync: this database driver doesn't support sync calls"}getLastError(){return this.lastError}buildSelectStatement(e,t){t||(t={});const a=this.buildSelectSectionStatement(e,t);if(_.forEach(["union","minus","intersect"],(e=>{let s=_.get(t,e);s&&(_.isArray(s)||(s=[s]),_.forEach(s,(t=>{const s=this.buildSelectSectionStatement(t.model,t);a.sql+=" "+e.toUpperCase()+" ",a.append(s)})))})),t.orderBy){a.sql+=" ORDER BY ";const s=_.isString(t.orderBy)?t.orderBy.split(/ *, */):t.orderBy;a.sql+=s.map((t=>{let a="";const s=t.match(/^(\w*) (asc|desc)$/i);return s&&(t=s[1],a=s[2].toUpperCase()),t=this.translateFieldName(e,t),a&&(t+=" "+a),t})).join(", ")}return t.limit&&(t.limit.limit&&(a.sql+=" LIMIT "+t.limit.limit.toString()),t.limit.offset&&(a.sql+=" OFFSET "+t.limit.offset.toString())),this.lastBuildData=a,a}toDate(e){return smartDbToDate(e)}toDbTimestamp(e){return toSmartDbTimestamp(e)}toDbDate(e){return toSmartDbDate(e)}buildWhere(e,t,a){const s=new SmartDbSqlBuildData;return t&&_.keys(t).length>0&&(a||(a="AND",s.sql+=" WHERE "),s.sql+=_.map(t,((t,r)=>{const i=r.toUpperCase();if("AND"==i||"OR"==i){let a="";return(_.isArray(t)?t:[t]).forEach(((t,r)=>{const l=this.buildWhere(e,t,i);r>0&&(a+=" "+i+" "),a+=l.sql,s.values=_.concat(s.values,l.values)})),a="("+a+")",a}if("EXPRESSION"==i){let r=[];return _.forEach(t,(t=>{const a=this.prepareField(e,t.compare,s.values),i=this.prepareField(e,t.with,s.values);let l;l=_.isString(t.operation)&&SqlOperationType[t.operation]?SqlOperationType[t.operation]:t.operation||SqlOperationType.EQ,r.push(`${a} ${l} ${i}`)})),r.join(" "+a+" ")}{let a,i,l=!0;if(_.isArray(t)&&(t=IN(t)),_.isObject(t)){const e=t;switch(_.isString(e.operation)&&SqlOperationType[e.operation]&&(e.operation=SqlOperationType[e.operation]),e.operation){case SqlOperationType.IN:case SqlOperationType.NOT_IN:const t=new Array(e.value.length);t.fill("?"),a=e.operation+" ("+t.join(", ")+")",s.values=_.concat(s.values,e.value);break;case SqlOperationType.IS_NULL:case SqlOperationType.IS_NOT_NULL:a=e.operation;break;case SqlOperationType.LITERAL:r=e.key;const i=e.literalOperation||SqlOperationType.EQ;if(i==SqlOperationType.IN||i==SqlOperationType.NOT_IN){const t=new Array(e.value.length);t.fill("?"),a=i+" ("+t.join(", ")+")",s.values=_.concat(s.values,e.value)}else _.isUndefined(e.value)?a=i:(a=i+" ?",s.values.push(this.makeDbValue(e.value)));l=!1;break;default:a=e.operation+" ?",s.values.push(this.makeDbValue(e.value))}}else null===t?a=SqlOperationType.IS_NULL:void 0===t?(r="1",a="= 1",l=!1):(a=_.isString(t)&&t.match(/[%_]/)?SqlOperationType.LIKE+" ?":SqlOperationType.EQ+" ?",s.values.push(this.makeDbValue(t)));return i=l?this.translateFieldName(e,r)+" "+a:r+" "+a,i}})).join(" "+a+" ")),s}buildDeleteStatement(e,t){const a=this.getTableName(e),s=new SmartDbSqlBuildData("DELETE FROM");return s.sql+=" "+a,t&&s.append(this.buildWhere(e,t)),this.lastBuildData=s,s}buildUpdateStatement(e,t,a){const s=this.getTableName(e),r=new SmartDbSqlBuildData("UPDATE");r.sql+=" "+s+" SET ";const i=[],l=t instanceof AbstractModel?t.getPlainObject(FieldNamingStyle.Database):t;return _.forOwn(l,((t,a)=>{i.push(this.translateFieldName(e,a)+" = ?"),r.values.push(this.makeDbValue(t))})),r.sql+=i.join(", "),a&&r.append(this.buildWhere(e,a)),this.lastBuildData=r,r}buildInsertStatement(e,t){const a=this.getTableName(e),s=new SmartDbSqlBuildData("INSERT");s.sql+=" INTO "+a;const r=[];t instanceof AbstractModel&&(t=t.getPlainObject(FieldNamingStyle.Database)),_.forOwn(t,((t,a)=>{a=this.translateFieldName(e,a),r.push(a),s.values.push(this.makeDbValue(t))}));const i=new Array(r.length);return i.fill("?"),s.sql+=" ("+r.join(", ")+") VALUES ("+i.join(", ")+")",this.lastBuildData=s,s}prepareFieldValue(e,t){let a="<undefined>";switch(t.operation){case SqlFieldOperationType.FIELD:a=this.translateFieldName(e,t.value);break;case SqlFieldOperationType.VALUE:null===t.value?a="NULL":t.literal?a=t.value:_.isString(t.value)?!isNaN(parseFloat(t.value))&&isFinite(t.value)||(a="'"+t.value+"'"):_.isNumber(t.value)?a=t.value.toString():_.isBoolean(t.value)?a=t.value?"1":"0":_.isDate(t.value)?a="'"+t.value.toISOString().substr(0,23).replace("T"," ")+"'":console.error("unhandled field data type",typeof t.value,t.value);break;case SqlFieldOperationType.COUNT:a=_.isArray(t.value)?"COUNT("+t.value.join(",")+")":"COUNT("+(t.value||"")+")";break;case SqlFieldOperationType.COALESCE:const s=_.isArray(t.value)?t.value:[t.value];a="COALESCE("+this.buildFieldList(e,s).join(",")+")"}if(t.alias){const e=this.getDbQuote();a+=` as ${e}${t.alias}${e}`}return a}prepareField(e,t,a){let s;if(null===t)s="NULL";else if(_.isString(t)){const r=t.match(/^'(.*)'$/);r?a?(s="?",a.push(r[1])):s=t:s=this.translateFieldName(e,t)}else t.operation?s=this.prepareFieldValue(e,t):a?(s="?",a.push(t)):s=this.makeArgumentDbValue(t).toString();return s}buildFieldList(e,t){const a=[];return _.forEach(t,(t=>{a.push(this.prepareField(e,t))})),a}buildSelectSectionStatement(e,t){const a=this.getTableName(e);let s,r;if(_.isArray(t.fields))s=t.fields;else if(_.isString(t.fields)){const e=t.fields.trim();s=""===e||"*"==e?[]:e.split(/,/)}else s=t.fields&&t.fields.operation?[t.fields]:[];if(s.length>0){r=this.buildFieldList(e,s).join(", ")}else r="*";t.distinct&&(r="DISTINCT "+r),t.count&&(r=`COUNT(${r})`);const i=new SmartDbSqlBuildData(`SELECT ${r} FROM ${a}`);if(t.where&&i.append(this.buildWhere(e,t.where)),t.groupBy){i.sql+=" GROUP BY ";const a=_.isArray(t.groupBy)?t.groupBy:[t.groupBy];i.sql+=a.map((t=>this.translateFieldName(e,t))).join(", ")}return i}translateFieldName(e,t){if(_.isString(e)){const t=e;let a=!1;_.forEach(this.dictionaries,(s=>(s.models&&s.models[t]&&(e=s.models[t].cls,a=!0),!a)))}if(!_.isString(e)){const a=e.attributeMap[t];if(a)a.alias&&(t=a.alias);else{const a=e.getTableName();this.lastError=new Error(`unknown field '${t}' in table '${a}'`),this.smartDbLog.logError(SmartDbLogLocation.Database,"translateFieldName",this.lastError)}}return t}makeDbValue(e){return _.isBoolean(e)?e=e?1:0:_.isDate(e)&&(e=toSmartDbDate(e)),e}makeArgumentDbValue(e){return _.isBoolean(e)?e=e?1:0:_.isDate(e)?e=`'${toSmartDbDate(e)}'`:_.isString(e)&&(e=`'${e.replace(/'/,"''").replace(/\\/,"\\\\")}'`),e}saveExecute(e){let t;try{t=e()}catch(e){this.lastError=e instanceof Error?e:new Error(e||"Unknown error"),this.smartDbLog.logFatal(SmartDbLogLocation.Database,"saveExecute-catch",this.lastError),t=!1}return t}prepareResultRow(e,t,a){let s;if(a.indexedBy&&this.smartDbLog.logWarning(SmartDbLogLocation.Database,"AbstractModel.get","option 'indexedBy' not supported without 'all'"),t)if(a.count)s=parseInt(_.values(t)[0],10);else if(a.collapseRow)s=_.values(t).join(",");else{const r=e.from(t);s=a.style==FieldNamingStyle.TypeScript?r.getPlainObject():r}return s}prepareResultRows(e,t,a){let s;if(!t||!a.indexedBy&&!a.collapseRow&&_.isString(e))s=t;else{const r=e;s=a.indexedBy?{}:new Array(t.length),_.forEach(t,((e,t)=>{let i=r.from?r.from(e):null,l=null;if(a.indexedBy&&(l=i?i.getValue(a.indexedBy):e[a.indexedBy]),a.collapseRow){const a=_.values(e).join(",");l?s[l]=a:s[t]=a}else i&&a.style==FieldNamingStyle.TypeScript&&(i=i.getPlainObject()),l?s[l]=i||e:s[t]=i||e}))}return s}getTableName(e){let t;if(_.isString(e)){let a=e;_.forEach(this.dictionaries,(e=>(e.models&&e.models[a]&&(t=e.models[a].cls.getTableName()),!t))),t||(t=a)}else{if(!e||!e.getTableName)throw new Error(`unknown model: ${e}`);t=e.getTableName()}return t}get isReady(){return this._isReady}set isReady(e){this._isReady=e,this._onReady.next(e)}}
1
+ import _ from"lodash";import{BehaviorSubject}from"rxjs";import{AbstractModel}from"./models/abstract-model";import{SmartDbDictionary}from"./models/smart-db-dictionary";import{SmartDbLogModel}from"./models/smart-db-log-model";import{IN,smartDbToDate,toSmartDbDate,toSmartDbTimestamp}from"./smart-db-globals";import{FieldNamingStyle,SqlFieldOperationType,SqlOperationType}from"./smart-db-interfaces";import{SmartDbLog,SmartDbLogLocation}from"./smart-db-log";import{SmartDbSqlBuildData}from"./smart-db-sql-build-data";import{SmartDbUpgradeManager}from"./smart-db-upgrade-manager";export class SmartDb{constructor(e,t){this.dictionaries=[],this.dbConnector=e,this.smartDbLog=new SmartDbLog(t?null:this),this.dbLogging=!t,this.db=null,this._isReady=null,this._onReady=new BehaviorSubject(!1)}initDb(e){return new Promise(((t,a)=>{this.dictionaries.push(SmartDbDictionary);const s=new SmartDbUpgradeManager(this);let r;r=import.meta&&import.meta.url?import.meta.url.replace(/file:\/\//,"").replace(/\/[^/]*$/,""):__dirname;const i={module:"smart-db-core",sqlFilesDirectory:r+"/assets/"+this.getDatabaseType()};s.prepareDatabase(i).then((r=>{this.exists(SmartDbLogModel).then((i=>{this.smartDbLog.setDbLogging(this.dbLogging&&i),s.prepareDatabase(e).then((e=>{t([r,e])}),(e=>{a(e)}))}))})).catch((e=>{a(e)}))}))}onReady(){return this._onReady}getLogger(){return this.smartDbLog}getDb(){return this.db}getLastBuildData(){return this.lastBuildData}get(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){t||(t={});const r=this.buildSelectStatement(e,t);t.firstOnly||t.count?this.statementGet(r).then((s=>{a(this.prepareResultRow(e,s,t))})).catch((e=>{s(e)})):this.statementGetAll(r).then((s=>{a(this.prepareResultRows(e,s,t))})).catch((e=>{s(e)}))}else s("get: this database driver doesn't support async calls")}))}getSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{t||(t={});const a=this.buildSelectStatement(e,t);let s;if(t.firstOnly||t.count){const r=this.statementGetSync(a);s=this.prepareResultRow(e,r,t)}else{const r=this.statementGetAllSync(a);s=this.prepareResultRows(e,r,t)}return s}));throw"getSync: this database driver doesn't support sync calls"}getFirst(e,t,a,s){return this.get(e,{firstOnly:!0,where:t,fields:a,orderBy:s})}getFirstSync(e,t,a,s){return this.getSync(e,{firstOnly:!0,where:t,fields:a,orderBy:s})}getAll(e,t,a,s,r){return this.get(e,{where:t,fields:a,orderBy:s,limit:r})}getAllSync(e,t,a,s,r){return this.getSync(e,{where:t,fields:a,orderBy:s,limit:r})}delete(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){const r=this.buildDeleteStatement(e,t);this.statementRun(r).then((e=>{e?a(e.changes):s(this.getLastError())})).catch((e=>{s(e)}))}else s("delete: this database driver doesn't support sync calls")}))}deleteSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{const a=this.buildDeleteStatement(e,t);return this.statementRunSync(a).changes}));throw"deleteSync: this database driver doesn't support sync calls"}update(e,t,a){return new Promise(((s,r)=>{if(this.supportAsyncCalls()){const i=this.buildUpdateStatement(e,t,a);this.statementRun(i).then((e=>{e?s(e.changes):r(this.getLastError())})).catch((e=>{r(e)}))}else r("update: this database driver doesn't support sync calls")}))}updateSync(e,t,a){if(this.supportSyncCalls())return this.saveExecute((()=>{const s=this.buildUpdateStatement(e,t,a);return this.statementRunSync(s).changes}));throw"updateSync: this database driver doesn't support sync calls"}insert(e,t){return new Promise(((a,s)=>{if(this.supportAsyncCalls()){const r=this.buildInsertStatement(e,t);this.statementRun(r).then((e=>{e?a(e.lastId):s(this.getLastError())})).catch((e=>{s(e)}))}else s("insert: this database driver doesn't support sync calls")}))}insertSync(e,t){if(this.supportSyncCalls())return this.saveExecute((()=>{const a=this.buildInsertStatement(e,t);return this.statementRunSync(a).lastId}));throw"insertSync: this database driver doesn't support sync calls"}getLastError(){return this.lastError}buildSelectStatement(e,t){t||(t={});const a=this.buildSelectSectionStatement(e,t);if(_.forEach(["union","minus","intersect"],(e=>{let s=_.get(t,e);s&&(_.isArray(s)||(s=[s]),_.forEach(s,(t=>{const s=this.buildSelectSectionStatement(t.model,t);a.sql+=" "+e.toUpperCase()+" ",a.append(s)})))})),t.orderBy){a.sql+=" ORDER BY ";const s=_.isString(t.orderBy)?t.orderBy.split(/ *, */):t.orderBy;a.sql+=s.map((t=>{let a="";const s=t.match(/^(\w*) (asc|desc)$/i);return s&&(t=s[1],a=s[2].toUpperCase()),t=this.translateFieldName(e,t),a&&(t+=" "+a),t})).join(", ")}return t.limit&&(t.limit.limit&&(a.sql+=" LIMIT "+t.limit.limit.toString()),t.limit.offset&&(a.sql+=" OFFSET "+t.limit.offset.toString())),this.lastBuildData=a,a}toDate(e){return smartDbToDate(e)}toDbTimestamp(e){return toSmartDbTimestamp(e)}toDbDate(e){return toSmartDbDate(e)}buildWhere(e,t,a){const s=new SmartDbSqlBuildData;return t&&_.keys(t).length>0&&(a||(a="AND",s.sql+=" WHERE "),s.sql+=_.map(t,((t,r)=>{const i=r.toUpperCase();if("AND"==i||"OR"==i){let a="";return(_.isArray(t)?t:[t]).forEach(((t,r)=>{const l=this.buildWhere(e,t,i);r>0&&(a+=" "+i+" "),a+=l.sql,s.values=_.concat(s.values,l.values)})),a="("+a+")",a}if("EXPRESSION"==i){let r=[];return _.forEach(t,(t=>{const a=this.prepareField(e,t.compare,s.values),i=this.prepareField(e,t.with,s.values);let l;l=_.isString(t.operation)&&SqlOperationType[t.operation]?SqlOperationType[t.operation]:t.operation||SqlOperationType.EQ,r.push(`${a} ${l} ${i}`)})),r.join(" "+a+" ")}{let a,i,l=!0;if(_.isArray(t)&&(t=IN(t)),_.isObject(t)){const e=t;switch(_.isString(e.operation)&&SqlOperationType[e.operation]&&(e.operation=SqlOperationType[e.operation]),e.operation){case SqlOperationType.IN:case SqlOperationType.NOT_IN:const t=new Array(e.value.length);t.fill("?"),a=e.operation+" ("+t.join(", ")+")",s.values=_.concat(s.values,e.value);break;case SqlOperationType.IS_NULL:case SqlOperationType.IS_NOT_NULL:a=e.operation;break;case SqlOperationType.LITERAL:r=e.key;const i=e.literalOperation||SqlOperationType.EQ;if(i==SqlOperationType.IN||i==SqlOperationType.NOT_IN){const t=new Array(e.value.length);t.fill("?"),a=i+" ("+t.join(", ")+")",s.values=_.concat(s.values,e.value)}else _.isUndefined(e.value)?a=i:(a=i+" ?",s.values.push(this.makeDbValue(e.value)));l=!1;break;default:a=e.operation+" ?",s.values.push(this.makeDbValue(e.value))}}else null===t?a=SqlOperationType.IS_NULL:void 0===t?(r="1",a="= 1",l=!1):(a=_.isString(t)&&t.match(/[%_]/)?SqlOperationType.LIKE+" ?":SqlOperationType.EQ+" ?",s.values.push(this.makeDbValue(t)));return i=l?this.translateFieldName(e,r)+" "+a:r+" "+a,i}})).join(" "+a+" ")),s}buildDeleteStatement(e,t){const a=this.getTableName(e),s=new SmartDbSqlBuildData("DELETE FROM");return s.sql+=" "+a,t&&s.append(this.buildWhere(e,t)),this.lastBuildData=s,s}buildUpdateStatement(e,t,a){const s=this.getTableName(e),r=new SmartDbSqlBuildData("UPDATE");r.sql+=" "+s+" SET ";const i=[],l=t instanceof AbstractModel?t.getPlainObject(FieldNamingStyle.Database):t;return _.forOwn(l,((t,a)=>{i.push(this.translateFieldName(e,a)+" = ?"),r.values.push(this.makeDbValue(t))})),r.sql+=i.join(", "),a&&r.append(this.buildWhere(e,a)),this.lastBuildData=r,r}buildInsertStatement(e,t){const a=this.getTableName(e),s=new SmartDbSqlBuildData("INSERT");s.sql+=" INTO "+a;const r=[];t instanceof AbstractModel&&(t=t.getPlainObject(FieldNamingStyle.Database)),_.forOwn(t,((t,a)=>{a=this.translateFieldName(e,a),r.push(a),s.values.push(this.makeDbValue(t))}));const i=new Array(r.length);return i.fill("?"),s.sql+=" ("+r.join(", ")+") VALUES ("+i.join(", ")+")",this.lastBuildData=s,s}prepareFieldValue(e,t){let a="<undefined>";switch(t.operation){case SqlFieldOperationType.FIELD:a=this.translateFieldName(e,t.value);break;case SqlFieldOperationType.VALUE:null===t.value?a="NULL":t.literal?a=t.value:_.isString(t.value)?!isNaN(parseFloat(t.value))&&isFinite(t.value)||(a="'"+t.value+"'"):_.isNumber(t.value)?a=t.value.toString():_.isBoolean(t.value)?a=t.value?"1":"0":_.isDate(t.value)?a="'"+t.value.toISOString().substr(0,23).replace("T"," ")+"'":console.error("unhandled field data type",typeof t.value,t.value);break;case SqlFieldOperationType.COUNT:a=_.isArray(t.value)?"COUNT("+t.value.join(",")+")":"COUNT("+(t.value||"")+")";break;case SqlFieldOperationType.COALESCE:const s=_.isArray(t.value)?t.value:[t.value];a="COALESCE("+this.buildFieldList(e,s).join(",")+")"}if(t.alias){const e=this.getDbQuote();a+=` as ${e}${t.alias}${e}`}return a}prepareField(e,t,a){let s;if(null===t)s="NULL";else if(_.isString(t)){const r=t.match(/^'(.*)'$/);r?a?(s="?",a.push(r[1])):s=t:s=this.translateFieldName(e,t)}else t.operation?s=this.prepareFieldValue(e,t):a?(s="?",a.push(t)):s=this.makeArgumentDbValue(t).toString();return s}buildFieldList(e,t){const a=[];return _.forEach(t,(t=>{a.push(this.prepareField(e,t))})),a}buildSelectSectionStatement(e,t){const a=this.getTableName(e);let s,r;if(_.isArray(t.fields))s=t.fields;else if(_.isString(t.fields)){const e=t.fields.trim();s=""===e||"*"==e?[]:e.split(/,/)}else s=t.fields&&t.fields.operation?[t.fields]:[];if(s.length>0){r=this.buildFieldList(e,s).join(", ")}else r="*";t.distinct&&(r="DISTINCT "+r),t.count&&(r=`COUNT(${r})`);const i=new SmartDbSqlBuildData(`SELECT ${r} FROM ${a}`);if(t.where&&i.append(this.buildWhere(e,t.where)),t.groupBy){i.sql+=" GROUP BY ";const a=_.isArray(t.groupBy)?t.groupBy:[t.groupBy];i.sql+=a.map((t=>this.translateFieldName(e,t))).join(", ")}return i}translateFieldName(e,t){if(_.isString(e)){const t=e;let a=!1;_.forEach(this.dictionaries,(s=>(s.models&&s.models[t]&&(e=s.models[t].cls,a=!0),!a)))}if(!_.isString(e)){const a=e.attributeMap[t];if(a)a.alias&&(t=a.alias);else{const a=e.getTableName();this.lastError=new Error(`unknown field '${t}' in table '${a}'`),this.smartDbLog.logError(SmartDbLogLocation.Database,"translateFieldName",this.lastError)}}return t}makeDbValue(e){return _.isBoolean(e)?e=e?1:0:_.isDate(e)&&(e=toSmartDbDate(e)),e}makeArgumentDbValue(e){return _.isBoolean(e)?e=e?1:0:_.isDate(e)?e=`'${toSmartDbDate(e)}'`:_.isString(e)&&(e=`'${e.replace(/'/,"''").replace(/\\/,"\\\\")}'`),e}saveExecute(e){let t;try{t=e()}catch(e){this.lastError=e instanceof Error?e:new Error(e||"Unknown error"),this.smartDbLog.logFatal(SmartDbLogLocation.Database,"saveExecute-catch",this.lastError),t=!1}return t}prepareResultRow(e,t,a){let s;if(a.indexedBy&&this.smartDbLog.logWarning(SmartDbLogLocation.Database,"AbstractModel.get","option 'indexedBy' not supported without 'all'"),t)if(a.count)s=parseInt(_.values(t)[0],10);else if(a.collapseRow)s=_.values(t).join(",");else{const r=e.from(t);s=a.style==FieldNamingStyle.TypeScript?r.getPlainObject():r}return s}prepareResultRows(e,t,a){let s;if(!t||!a.indexedBy&&!a.collapseRow&&_.isString(e))s=t;else{const r=e;s=a.indexedBy?{}:new Array(t.length),_.forEach(t,((e,t)=>{let i=r.from?r.from(e):null,l=null;if(a.indexedBy&&(l=i?i.getValue(a.indexedBy):e[a.indexedBy]),a.collapseRow){const a=_.values(e).join(",");l?s[l]=a:s[t]=a}else i&&a.style==FieldNamingStyle.TypeScript&&(i=i.getPlainObject()),l?s[l]=i||e:s[t]=i||e}))}return s}getTableName(e){let t;if(_.isString(e)){let a=e;_.forEach(this.dictionaries,(e=>(e.models&&e.models[a]&&(t=e.models[a].cls.getTableName()),!t))),t||(t=a)}else{if(!e||!e.getTableName)throw new Error(`unknown model: ${e}`);t=e.getTableName()}return t}get isReady(){return this._isReady}set isReady(e){this._isReady=e,this._onReady.next(e)}}