From badf05c8f2ed05a2ba76fea2e1a7e0698fccfb57 Mon Sep 17 00:00:00 2001 From: mertmit Date: Fri, 9 Dec 2022 14:26:47 +0300 Subject: [PATCH] feat: SnowflakeClient alfa Signed-off-by: mertmit --- .../nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts | 1981 +++++++++++++ .../nocodb-sdk/src/lib/sqlUi/SqlUiFactory.ts | 5 + packages/nocodb-sdk/src/lib/sqlUi/index.ts | 1 + .../lib/db/sql-client/lib/SqlClientFactory.ts | 5 + .../lib/snowflake/SnowflakeClient.ts | 2443 +++++++++++++++++ .../lib/snowflake/snowflake.queries.ts | 5 + .../sql-data-mapper/lib/sql/BaseModelSqlv2.ts | 10 +- .../db/sql-data-mapper/lib/sql/CustomKnex.ts | 5 +- .../code/models/xc/ModelXcMetaFactory.ts | 3 + .../code/models/xc/ModelXcMetaSnowflake.ts | 975 +++++++ 10 files changed, 5429 insertions(+), 4 deletions(-) create mode 100644 packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts create mode 100644 packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts create mode 100644 packages/nocodb/src/lib/db/sql-client/lib/snowflake/snowflake.queries.ts create mode 100644 packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaSnowflake.ts diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts new file mode 100644 index 0000000000..ce3fa80b9a --- /dev/null +++ b/packages/nocodb-sdk/src/lib/sqlUi/SnowflakeUi.ts @@ -0,0 +1,1981 @@ +import UITypes from '../UITypes'; +import { IDType } from './index'; + +const dbTypes = [ + 'int', + 'integer', + 'bigint', + 'bigserial', + 'char', + 'int2', + 'int4', + 'int8', + 'int4range', + 'int8range', + 'serial', + 'serial2', + 'serial8', + 'character', + 'bit', + 'bool', + 'boolean', + 'date', + 'double precision', + 'event_trigger', + 'fdw_handler', + 'float4', + 'float8', + 'uuid', + 'smallint', + 'smallserial', + 'character varying', + 'text', + 'real', + 'time', + 'time without time zone', + 'timestamp', + 'timestamp without time zone', + 'timestamptz', + 'timestamp with time zone', + 'timetz', + 'time with time zone', + 'daterange', + 'json', + 'jsonb', + 'gtsvector', + 'index_am_handler', + 'anyenum', + 'anynonarray', + 'anyrange', + 'box', + 'bpchar', + 'bytea', + 'cid', + 'cidr', + 'circle', + 'cstring', + 'inet', + 'internal', + 'interval', + 'language_handler', + 'line', + 'lsec', + 'macaddr', + 'money', + 'name', + 'numeric', + 'numrange', + 'oid', + 'opaque', + 'path', + 'pg_ddl_command', + 'pg_lsn', + 'pg_node_tree', + 'point', + 'polygon', + 'record', + 'refcursor', + 'regclass', + 'regconfig', + 'regdictionary', + 'regnamespace', + 'regoper', + 'regoperator', + 'regproc', + 'regpreocedure', + 'regrole', + 'regtype', + 'reltime', + 'smgr', + 'tid', + 'tinterval', + 'trigger', + 'tsm_handler', + 'tsquery', + 'tsrange', + 'tstzrange', + 'tsvector', + 'txid_snapshot', + 'unknown', + 'void', + 'xid', + 'xml', +]; + +export class SnowflakeUi { + static getNewTableColumns() { + return [ + { + column_name: 'id', + title: 'Id', + dt: 'int4', + dtx: 'integer', + ct: 'int(11)', + nrqd: false, + rqd: true, + ck: false, + pk: true, + un: false, + ai: true, + cdf: null, + clen: null, + np: 11, + ns: 0, + dtxp: '11', + dtxs: '', + altered: 1, + uidt: 'ID', + uip: '', + uicn: '', + }, + { + column_name: 'title', + title: 'Title', + dt: 'character varying', + dtx: 'specificType', + ct: 'varchar(45)', + nrqd: true, + rqd: false, + ck: false, + pk: false, + un: false, + ai: false, + cdf: null, + clen: 45, + np: null, + ns: null, + dtxp: '45', + dtxs: '', + altered: 1, + uidt: 'SingleLineText', + uip: '', + uicn: '', + }, + { + column_name: 'created_at', + title: 'CreatedAt', + dt: 'timestamp', + dtx: 'specificType', + ct: 'varchar(45)', + nrqd: true, + rqd: false, + ck: false, + pk: false, + un: false, + ai: false, + cdf: 'now()', + clen: 45, + np: null, + ns: null, + dtxp: '', + dtxs: '', + altered: 1, + uidt: UITypes.DateTime, + uip: '', + uicn: '', + }, + { + column_name: 'updated_at', + title: 'UpdatedAt', + dt: 'timestamp', + dtx: 'specificType', + ct: 'varchar(45)', + nrqd: true, + rqd: false, + ck: false, + pk: false, + un: false, + ai: false, + au: true, + cdf: 'now()', + clen: 45, + np: null, + ns: null, + dtxp: '', + dtxs: '', + altered: 1, + uidt: UITypes.DateTime, + uip: '', + uicn: '', + }, + ]; + } + + static getNewColumn(suffix) { + return { + column_name: 'title' + suffix, + dt: 'character varying', + dtx: 'specificType', + ct: 'varchar(45)', + nrqd: true, + rqd: false, + ck: false, + pk: false, + un: false, + ai: false, + cdf: null, + clen: 45, + np: null, + ns: null, + dtxp: '45', + dtxs: '', + altered: 1, + uidt: 'SingleLineText', + uip: '', + uicn: '', + }; + } + + // static getDefaultLengthForDatatype(type) { + // switch (type) { + // case "int": + // return 11; + // break; + // case "tinyint": + // return 1; + // break; + // case "smallint": + // return 5; + // break; + // + // case "mediumint": + // return 9; + // break; + // case "bigint": + // return 20; + // break; + // case "bit": + // return 64; + // break; + // case "boolean": + // return ''; + // break; + // case "float": + // return 12; + // break; + // case "decimal": + // return 10; + // break; + // case "double": + // return 22; + // break; + // case "serial": + // return 20; + // break; + // case "date": + // return ''; + // break; + // case "datetime": + // case "timestamp": + // return 6; + // break; + // case "time": + // return ''; + // break; + // case "year": + // return ''; + // break; + // case "char": + // return 255; + // break; + // case "varchar": + // return 45; + // break; + // case "nchar": + // return 255; + // break; + // case "text": + // return ''; + // break; + // case "tinytext": + // return ''; + // break; + // case "mediumtext": + // return ''; + // break; + // case "longtext": + // return '' + // break; + // case "binary": + // return 255; + // break; + // case "varbinary": + // return 65500; + // break; + // case "blob": + // return ''; + // break; + // case "tinyblob": + // return ''; + // break; + // case "mediumblob": + // return ''; + // break; + // case "longblob": + // return ''; + // break; + // case "enum": + // return '\'a\',\'b\''; + // break; + // case "set": + // return '\'a\',\'b\''; + // break; + // case "geometry": + // return ''; + // case "point": + // return ''; + // case "linestring": + // return ''; + // case "polygon": + // return ''; + // case "multipoint": + // return ''; + // case "multilinestring": + // return ''; + // case "multipolygon": + // return ''; + // case "json": + // return '' + // break; + // + // } + // + // } + + static getDefaultLengthForDatatype(type): any { + switch (type) { + case 'int': + return ''; + + case 'tinyint': + return ''; + + case 'smallint': + return ''; + + case 'mediumint': + return ''; + + case 'bigint': + return ''; + + case 'bit': + return ''; + + case 'boolean': + return ''; + + case 'float': + return ''; + + case 'decimal': + return ''; + + case 'double': + return ''; + + case 'serial': + return ''; + + case 'date': + return ''; + + case 'datetime': + case 'timestamp': + return ''; + + case 'time': + return ''; + + case 'year': + return ''; + + case 'char': + return ''; + + case 'varchar': + return ''; + + case 'nchar': + return ''; + + case 'text': + return ''; + + case 'tinytext': + return ''; + + case 'mediumtext': + return ''; + + case 'longtext': + return ''; + + case 'binary': + return ''; + + case 'varbinary': + return ''; + + case 'blob': + return ''; + + case 'tinyblob': + return ''; + + case 'mediumblob': + return ''; + + case 'longblob': + return ''; + + case 'enum': + return ''; + + case 'set': + return ''; + + case 'geometry': + return ''; + case 'point': + return ''; + case 'linestring': + return ''; + case 'polygon': + return ''; + case 'multipoint': + return ''; + case 'multilinestring': + return ''; + case 'multipolygon': + return ''; + case 'json': + return ''; + } + } + + static getDefaultLengthIsDisabled(type): any { + switch (type) { + case 'anyenum': + case 'anynonarray': + case 'anyrange': + case 'bigint': + case 'bigserial': + case 'bit': + case 'bool': + case 'box': + case 'bpchar': + case 'bytea': + case 'char': + case 'character': + case 'cid': + case 'cidr': + case 'circle': + case 'cstring': + case 'date': + case 'daterange': + case 'double precision': + case 'event_trigger': + case 'fdw_handler': + case 'float4': + case 'float8': + case 'gtsvector': + case 'index_am_handler': + case 'inet': + case 'int': + case 'int2': + case 'int4': + case 'int8': + case 'int4range': + case 'int8range': + case 'integer': + case 'internal': + case 'interval': + case 'jsonb': + case 'language_handler': + case 'line': + case 'lsec': + case 'macaddr': + case 'money': + case 'name': + case 'numeric': + case 'numrange': + case 'oid': + case 'opaque': + case 'path': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': + case 'real': + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': + case 'serial': + case 'serial2': + case 'serial8': + case 'smallint': + case 'smallserial': + case 'smgr': + case 'text': + case 'tid': + case 'time': + case 'time without time zone': + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + case 'timetz': + case 'time with time zone': + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'xml': + case 'character varying': + case 'tinyint': + case 'mediumint': + case 'float': + case 'decimal': + case 'double': + case 'boolean': + case 'datetime': + case 'uuid': + case 'year': + case 'varchar': + case 'nchar': + case 'tinytext': + case 'mediumtext': + case 'longtext': + case 'binary': + case 'varbinary': + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'enum': + case 'set': + case 'geometry': + case 'point': + case 'linestring': + case 'polygon': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': + case 'json': + return true; + } + } + + static getDefaultValueForDatatype(type): any { + switch (type) { + case 'anyenum': + return 'eg: '; + + case 'anynonarray': + return 'eg: '; + + case 'anyrange': + return 'eg: '; + + case 'bigint': + return 'eg: '; + + case 'bigserial': + return 'eg: '; + + case 'bit': + return 'eg: '; + + case 'bool': + return 'eg: '; + + case 'box': + return 'eg: '; + + case 'bpchar': + return 'eg: '; + + case 'bytea': + return 'eg: '; + + case 'char': + return 'eg: '; + + case 'character': + return "eg: 'sample'"; + + case 'cid': + return 'eg: '; + + case 'cidr': + return 'eg: '; + + case 'circle': + return 'eg: '; + + case 'cstring': + return 'eg: '; + + case 'date': + return "eg: '2020-09-09'"; + + case 'daterange': + return 'eg: '; + + case 'double precision': + return 'eg: 1.2'; + + case 'event_trigger': + return 'eg: '; + + case 'fdw_handler': + return 'eg: '; + + case 'float4': + return 'eg: 1.2'; + + case 'float8': + return 'eg: 1.2'; + + case 'gtsvector': + return 'eg: '; + + case 'index_am_handler': + return 'eg: '; + + case 'inet': + return 'eg: '; + + case 'int': + return 'eg: '; + + case 'int2': + return 'eg: '; + + case 'int4': + return 'eg: '; + + case 'int8': + return 'eg: '; + + case 'int4range': + return 'eg: '; + + case 'int8range': + return 'eg: '; + + case 'integer': + return 'eg: '; + + case 'internal': + return 'eg: '; + + case 'interval': + return 'eg: '; + + case 'json': + return 'eg: '; + + case 'jsonb': + return 'eg: '; + + case 'language_handler': + return 'eg: '; + + case 'line': + return 'eg: '; + + case 'lsec': + return 'eg: '; + + case 'macaddr': + return 'eg: '; + + case 'money': + return 'eg: '; + + case 'name': + return 'eg: '; + + case 'numeric': + return 'eg: '; + + case 'numrange': + return 'eg: '; + + case 'oid': + return 'eg: '; + + case 'opaque': + return 'eg: '; + + case 'path': + return 'eg: '; + + case 'pg_ddl_command': + return 'eg: '; + + case 'pg_lsn': + return 'eg: '; + + case 'pg_node_tree': + return 'eg: '; + + case 'point': + return 'eg: '; + + case 'polygon': + return 'eg: '; + + case 'real': + return 'eg: 1.2'; + + case 'record': + return 'eg: '; + + case 'refcursor': + return 'eg: '; + + case 'regclass': + return 'eg: '; + + case 'regconfig': + return 'eg: '; + + case 'regdictionary': + return 'eg: '; + + case 'regnamespace': + return 'eg: '; + + case 'regoper': + return 'eg: '; + + case 'regoperator': + return 'eg: '; + + case 'regproc': + return 'eg: '; + + case 'regpreocedure': + return 'eg: '; + + case 'regrole': + return 'eg: '; + + case 'regtype': + return 'eg: '; + + case 'reltime': + return 'eg: '; + + case 'serial': + return 'eg: '; + + case 'serial2': + return 'eg: '; + + case 'serial8': + return 'eg: '; + + case 'smallint': + return 'eg: '; + + case 'smallserial': + return 'eg: '; + + case 'smgr': + return 'eg: '; + + case 'text': + return "eg: 'sample text'"; + + case 'tid': + return 'eg: '; + + case 'time': + return "eg: now()\n\n'04:05:06.789'"; + + case 'time without time zone': + return "eg: now()\n\n'04:05:06.789'"; + + case 'timestamp': + return "eg: now()\n\n'2016-06-22 19:10:25-07'"; + + case 'timestamp without time zone': + return "eg: now()\n\n'2016-06-22 19:10:25-07'"; + + case 'timestamptz': + return "eg: timezone('America/New_York','2016-06-01 00:00')\n\nnow()\n\n'2016-06-22 19:10:25-07'"; + + case 'timestamp with time zone': + return "eg: now()\n\n'2016-06-22 19:10:25-07'"; + + case 'timetz': + return 'eg: now()'; + + case 'time with time zone': + return 'eg: now()'; + + case 'tinterval': + return 'eg: '; + + case 'trigger': + return 'eg: '; + + case 'tsm_handler': + return 'eg: '; + + case 'tsquery': + return 'eg: '; + + case 'tsrange': + return 'eg: '; + + case 'tstzrange': + return 'eg: '; + + case 'tsvector': + return 'eg: '; + + case 'txid_snapshot': + return 'eg: '; + + case 'unknown': + return 'eg: '; + + case 'void': + return 'eg: '; + + case 'xid': + return 'eg: '; + + case 'xml': + return 'eg: '; + + case 'character varying': + return "eg: 'sample text'"; + + case 'tinyint': + return 'eg: '; + + case 'mediumint': + return 'eg: '; + + case 'float': + return 'eg: '; + + case 'decimal': + return 'eg: '; + + case 'double': + return 'eg: 1.2'; + + case 'boolean': + return 'eg: true\n\nfalse'; + + case 'datetime': + return 'eg: '; + + case 'uuid': + return 'eg: '; + + case 'year': + return 'eg: '; + + case 'varchar': + return 'eg: '; + + case 'nchar': + return 'eg: '; + + case 'tinytext': + return 'eg: '; + + case 'mediumtext': + return 'eg: '; + + case 'longtext': + return 'eg: '; + + case 'binary': + return 'eg: '; + + case 'varbinary': + return 'eg: '; + + case 'blob': + return 'eg: '; + + case 'tinyblob': + return 'eg: '; + + case 'mediumblob': + return 'eg: '; + + case 'longblob': + return 'eg: '; + + case 'enum': + return 'eg: '; + + case 'set': + return 'eg: '; + + case 'geometry': + return 'eg: '; + + case 'linestring': + return 'eg: '; + + case 'multipoint': + return 'eg: '; + + case 'multilinestring': + return 'eg: '; + + case 'multipolygon': + return 'eg: '; + } + } + + static getDefaultScaleForDatatype(type): any { + switch (type) { + case 'int': + return ' '; + + case 'tinyint': + return ' '; + + case 'smallint': + return ' '; + + case 'mediumint': + return ' '; + + case 'bigint': + return ' '; + + case 'bit': + return ' '; + + case 'boolean': + return ' '; + + case 'float': + return '2'; + + case 'decimal': + return '2'; + + case 'double': + return '2'; + + case 'serial': + return ' '; + + case 'date': + case 'datetime': + case 'timestamp': + return ' '; + + case 'time': + return ' '; + + case 'year': + return ' '; + + case 'char': + return ' '; + + case 'varchar': + return ' '; + + case 'nchar': + return ' '; + + case 'text': + return ' '; + + case 'tinytext': + return ' '; + + case 'mediumtext': + return ' '; + + case 'longtext': + return ' '; + + case 'binary': + return ' '; + + case 'varbinary': + return ' '; + + case 'blob': + return ' '; + + case 'tinyblob': + return ' '; + + case 'mediumblob': + return ' '; + + case 'longblob': + return ' '; + + case 'enum': + return ' '; + + case 'set': + return ' '; + + case 'geometry': + return ' '; + case 'point': + return ' '; + case 'linestring': + return ' '; + case 'polygon': + return ' '; + case 'multipoint': + return ' '; + case 'multilinestring': + return ' '; + case 'multipolygon': + return ' '; + case 'json': + return ' '; + } + } + + static colPropAIDisabled(col, columns) { + // console.log(col); + if ( + col.dt === 'int4' || + col.dt === 'integer' || + col.dt === 'bigint' || + col.dt === 'smallint' + ) { + for (let i = 0; i < columns.length; ++i) { + if (columns[i].cn !== col.cn && columns[i].ai) { + return true; + } + } + return false; + } else { + return true; + } + } + + static colPropUNDisabled(_col) { + // console.log(col); + return true; + // if (col.dt === 'int' || + // col.dt === 'tinyint' || + // col.dt === 'smallint' || + // col.dt === 'mediumint' || + // col.dt === 'bigint') { + // return false; + // } else { + // return true; + // } + } + + static onCheckboxChangeAI(col) { + console.log(col); + if ( + col.dt === 'int' || + col.dt === 'bigint' || + col.dt === 'smallint' || + col.dt === 'tinyint' + ) { + col.altered = col.altered || 2; + } + + // if (!col.ai) { + // col.dtx = 'specificType' + // } else { + // col.dtx = '' + // } + } + + static onCheckboxChangeAU(col) { + console.log(col); + // if (1) { + col.altered = col.altered || 2; + // } + if (col.au) { + col.cdf = 'now()'; + } + + // if (!col.ai) { + // col.dtx = 'specificType' + // } else { + // col.dtx = '' + // } + } + + static showScale(_columnObj) { + return false; + } + + static removeUnsigned(columns) { + for (let i = 0; i < columns.length; ++i) { + if ( + columns[i].altered === 1 && + !( + columns[i].dt === 'int' || + columns[i].dt === 'bigint' || + columns[i].dt === 'tinyint' || + columns[i].dt === 'smallint' || + columns[i].dt === 'mediumint' + ) + ) { + columns[i].un = false; + console.log('>> resetting unsigned value', columns[i].cn); + } + console.log(columns[i].cn); + } + } + + static columnEditable(colObj) { + return colObj.tn !== '_evolutions' || colObj.tn !== 'nc_evolutions'; + } + + static extractFunctionName(query) { + const reg = + /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*FUNCTION\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; + const match = query.match(reg); + return match && match[1]; + } + + static extractProcedureName(query) { + const reg = + /^\s*CREATE\s+(?:OR\s+REPLACE\s*)?\s*PROCEDURE\s+(?:[\w\d_]+\.)?([\w_\d]+)/i; + const match = query.match(reg); + return match && match[1]; + } + + static handleRawOutput(result, headers) { + if (['DELETE', 'INSERT', 'UPDATE'].includes(result.command.toUpperCase())) { + headers.push({ text: 'Row count', value: 'rowCount', sortable: false }); + result = [ + { + rowCount: result.rowCount, + }, + ]; + } else { + result = result.rows; + if (Array.isArray(result) && result[0]) { + const keys = Object.keys(result[0]); + // set headers before settings result + for (let i = 0; i < keys.length; i++) { + const text = keys[i]; + headers.push({ text, value: text, sortable: false }); + } + } + } + return result; + } + + static splitQueries(query) { + /*** + * we are splitting based on semicolon + * there are mechanism to escape semicolon within single/double quotes(string) + */ + return query.match(/\b("[^"]*;[^"]*"|'[^']*;[^']*'|[^;])*;/g); + } + + /** + * if sql statement is SELECT - it limits to a number + * @param args + * @returns {string|*} + */ + sanitiseQuery(args) { + let q = args.query.trim().split(';'); + + if (q[0].startsWith('Select')) { + q = q[0] + ` LIMIT 0,${args.limit ? args.limit : 100};`; + } else if (q[0].startsWith('select')) { + q = q[0] + ` LIMIT 0,${args.limit ? args.limit : 100};`; + } else if (q[0].startsWith('SELECT')) { + q = q[0] + ` LIMIT 0,${args.limit ? args.limit : 100};`; + } else { + return args.query; + } + + return q; + } + + static getColumnsFromJson(json, tn) { + const columns = []; + + try { + if (typeof json === 'object' && !Array.isArray(json)) { + const keys = Object.keys(json); + for (let i = 0; i < keys.length; ++i) { + const column = { + dp: null, + tn, + column_name: keys[i], + cno: keys[i], + np: 10, + ns: 0, + clen: null, + cop: 1, + pk: false, + nrqd: false, + rqd: false, + un: false, + ct: 'int(11) unsigned', + ai: false, + unique: false, + cdf: null, + cc: '', + csn: null, + dtx: 'specificType', + dtxp: null, + dtxs: 0, + altered: 1, + }; + + switch (typeof json[keys[i]]) { + case 'number': + if (Number.isInteger(json[keys[i]])) { + if (SnowflakeUi.isValidTimestamp(keys[i], json[keys[i]])) { + Object.assign(column, { + dt: 'timestamp', + }); + } else { + Object.assign(column, { + dt: 'int', + np: 10, + ns: 0, + }); + } + } else { + Object.assign(column, { + dt: 'float4', + np: null, + ns: null, + dtxp: null, + dtxs: null, + }); + } + break; + case 'string': + if (SnowflakeUi.isValidDate(json[keys[i]])) { + Object.assign(column, { + dt: 'date', + }); + } else if (json[keys[i]].length <= 255) { + Object.assign(column, { + dt: 'character varying', + np: null, + ns: 0, + dtxp: null, + }); + } else { + Object.assign(column, { + dt: 'text', + }); + } + break; + case 'boolean': + Object.assign(column, { + dt: 'boolean', + np: 3, + ns: 0, + }); + break; + case 'object': + Object.assign(column, { + dt: 'json', + np: 3, + ns: 0, + }); + break; + default: + break; + } + columns.push(column); + } + } + } catch (e) { + console.log('Error in getColumnsFromJson', e); + } + + return columns; + } + + static isValidTimestamp(key, value) { + if (typeof value !== 'number') { + return false; + } + return new Date(value).getTime() > 0 && /(?:_|(?=A))[aA]t$/.test(key); + } + + static isValidDate(value) { + return new Date(value).getTime() > 0; + } + + static colPropAuDisabled(col) { + if (col.altered !== 1) { + return true; + } + + switch (col.dt) { + case 'time': + case 'time without time zone': + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + case 'timetz': + case 'time with time zone': + return false; + default: + return true; + } + } + + static getAbstractType(col): any { + switch ((col.dt || col.dt).toLowerCase()) { + case 'anyenum': + return 'enum'; + case 'anynonarray': + case 'anyrange': + return 'string'; + + case 'bit': + return 'integer'; + case 'bigint': + case 'bigserial': + return 'string'; + + case 'bool': + return 'boolean'; + + case 'box': + case 'bpchar': + case 'bytea': + case 'char': + case 'character': + return 'string'; + + case 'cid': + case 'cidr': + case 'circle': + case 'cstring': + return 'string'; + + case 'date': + return 'date'; + case 'daterange': + return 'string'; + case 'double precision': + return 'string'; + + case 'event_trigger': + case 'fdw_handler': + return 'string'; + + case 'float4': + case 'float8': + return 'float'; + + case 'gtsvector': + case 'index_am_handler': + case 'inet': + return 'string'; + + case 'int': + case 'int2': + case 'int4': + case 'int8': + case 'integer': + return 'integer'; + case 'int4range': + case 'int8range': + case 'internal': + case 'interval': + return 'string'; + case 'jsonb': + return 'string'; + + case 'language_handler': + case 'line': + case 'lsec': + case 'macaddr': + case 'money': + case 'name': + case 'numeric': + case 'numrange': + case 'oid': + case 'opaque': + case 'path': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': + case 'point': + case 'polygon': + return 'string'; + case 'real': + return 'float'; + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': + return 'string'; + case 'serial': + case 'serial2': + case 'serial8': + case 'smallint': + case 'smallserial': + return 'integer'; + case 'smgr': + return 'string'; + case 'text': + return 'text'; + case 'tid': + return 'string'; + case 'time': + case 'time without time zone': + return 'time'; + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + return 'datetime'; + case 'timetz': + case 'time with time zone': + return 'time'; + + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'character varying': + case 'xml': + return 'string'; + + case 'tinyint': + case 'mediumint': + return 'integer'; + + case 'float': + case 'decimal': + case 'double': + return 'float'; + case 'boolean': + return 'boolean'; + case 'datetime': + return 'datetime'; + + case 'uuid': + case 'year': + case 'varchar': + case 'nchar': + return 'string'; + case 'tinytext': + case 'mediumtext': + case 'longtext': + return 'text'; + case 'binary': + case 'varbinary': + return 'string'; + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + return 'blob'; + case 'enum': + return 'enum'; + case 'set': + return 'set'; + case 'geometry': + case 'linestring': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': + return 'string'; + case 'json': + return 'json'; + } + } + + static getUIType(col): any { + switch (this.getAbstractType(col)) { + case 'integer': + return 'Number'; + case 'boolean': + return 'Checkbox'; + case 'float': + return 'Decimal'; + case 'date': + return 'Date'; + case 'datetime': + return 'CreateTime'; + case 'time': + return 'Time'; + case 'year': + return 'Year'; + case 'string': + return 'SingleLineText'; + case 'text': + return 'LongText'; + case 'blob': + return 'Attachment'; + case 'enum': + return 'SingleSelect'; + case 'set': + return 'MultiSelect'; + case 'json': + return 'LongText'; + } + } + + static getDataTypeForUiType(col: { uidt: UITypes }, idType?: IDType) { + const colProp: any = {}; + switch (col.uidt) { + case 'ID': + { + const isAutoIncId = idType === 'AI'; + const isAutoGenId = idType === 'AG'; + colProp.dt = isAutoGenId ? 'character varying' : 'int4'; + colProp.pk = true; + colProp.un = isAutoIncId; + colProp.ai = isAutoIncId; + colProp.rqd = true; + colProp.meta = isAutoGenId ? { ag: 'nc' } : undefined; + } + break; + case 'ForeignKey': + colProp.dt = 'character varying'; + break; + case 'SingleLineText': + colProp.dt = 'character varying'; + break; + case 'LongText': + colProp.dt = 'text'; + break; + case 'Attachment': + colProp.dt = 'text'; + break; + case 'Checkbox': + colProp.dt = 'bool'; + break; + case 'MultiSelect': + colProp.dt = 'text'; + break; + case 'SingleSelect': + colProp.dt = 'text'; + break; + case 'Collaborator': + colProp.dt = 'character varying'; + break; + case 'Date': + colProp.dt = 'date'; + + break; + case 'Year': + colProp.dt = 'int'; + break; + case 'Time': + colProp.dt = 'time'; + break; + case 'PhoneNumber': + colProp.dt = 'character varying'; + colProp.validate = { + func: ['isMobilePhone'], + args: [''], + msg: ['Validation failed : isMobilePhone'], + }; + break; + case 'Email': + colProp.dt = 'character varying'; + colProp.validate = { + func: ['isEmail'], + args: [''], + msg: ['Validation failed : isEmail'], + }; + break; + case 'URL': + colProp.dt = 'character varying'; + colProp.validate = { + func: ['isURL'], + args: [''], + msg: ['Validation failed : isURL'], + }; + break; + case 'Number': + colProp.dt = 'bigint'; + break; + case 'Decimal': + colProp.dt = 'decimal'; + break; + case 'Currency': + colProp.dt = 'decimal'; + colProp.validate = { + func: ['isCurrency'], + args: [''], + msg: ['Validation failed : isCurrency'], + }; + break; + case 'Percent': + colProp.dt = 'double precision'; + break; + case 'Duration': + colProp.dt = 'decimal'; + break; + case 'Rating': + colProp.dt = 'smallint'; + break; + case 'Formula': + colProp.dt = 'character varying'; + break; + case 'Rollup': + colProp.dt = 'character varying'; + break; + case 'Count': + colProp.dt = 'int8'; + break; + case 'Lookup': + colProp.dt = 'character varying'; + break; + case 'DateTime': + colProp.dt = 'timestamp'; + break; + case 'CreateTime': + colProp.dt = 'timestamp'; + break; + case 'LastModifiedTime': + colProp.dt = 'timestamp'; + break; + case 'AutoNumber': + colProp.dt = 'int'; + break; + case 'Barcode': + colProp.dt = 'character varying'; + break; + case 'Button': + colProp.dt = 'character varying'; + break; + case 'JSON': + colProp.dt = 'json'; + break; + default: + colProp.dt = 'character varying'; + break; + } + return colProp; + } + + static getDataTypeListForUiType(col: { uidt: UITypes }, idType: IDType) { + switch (col.uidt) { + case 'ID': + if (idType === 'AG') { + return ['char', 'character', 'character varying']; + } else if (idType === 'AI') { + return [ + 'int', + 'integer', + 'bigint', + 'bigserial', + 'int2', + 'int4', + 'int8', + 'serial', + 'serial2', + 'serial8', + 'smallint', + 'smallserial', + ]; + } else { + return dbTypes; + } + case 'ForeignKey': + return dbTypes; + + case 'SingleLineText': + case 'LongText': + case 'Collaborator': + return ['char', 'character', 'character varying', 'text']; + + case 'Attachment': + return ['json', 'char', 'character', 'character varying', 'text']; + + case 'JSON': + return ['json', 'jsonb', 'text']; + case 'Checkbox': + return [ + 'bit', + 'bool', + 'int2', + 'int4', + 'int8', + 'boolean', + 'smallint', + 'int', + 'integer', + 'bigint', + 'char', + 'int4range', + 'int8range', + ]; + + case 'MultiSelect': + return ['text']; + + case 'SingleSelect': + return ['text']; + + case 'Year': + return ['int']; + + case 'Time': + return [ + 'time', + 'time without time zone', + 'timestamp', + 'timestamp without time zone', + 'timestamptz', + 'timestamp with time zone', + 'timetz', + 'time with time zone', + ]; + + case 'PhoneNumber': + case 'Email': + return ['character varying']; + + case 'URL': + return ['character varying', 'text']; + + case 'Number': + return [ + 'int', + 'integer', + 'bigint', + 'int2', + 'int4', + 'int8', + 'double precision', + 'float4', + 'float8', + 'smallint', + 'numeric', + ]; + + case 'Decimal': + return ['double precision', 'float4', 'float8', 'numeric']; + + case 'Currency': + return [ + 'int', + 'integer', + 'bigint', + 'int2', + 'int4', + 'int8', + 'double precision', + 'money', + 'float4', + 'float8', + 'numeric', + ]; + + case 'Percent': + return [ + 'int', + 'integer', + 'bigint', + 'int2', + 'int4', + 'int8', + 'double precision', + 'float4', + 'float8', + 'smallint', + 'numeric', + ]; + + case 'Duration': + return [ + 'int', + 'integer', + 'bigint', + 'int2', + 'int4', + 'int8', + 'double precision', + 'float4', + 'float8', + 'smallint', + 'smallserial', + 'numeric', + ]; + + case 'Rating': + return [ + 'int', + 'integer', + 'bigint', + 'bigserial', + 'int2', + 'int4', + 'int8', + 'serial', + 'serial2', + 'serial8', + 'double precision', + 'float4', + 'float8', + 'smallint', + 'smallserial', + 'numeric', + ]; + + case 'Formula': + return ['text', 'character varying']; + + case 'Rollup': + return ['character varying']; + + case 'Count': + return [ + 'int', + 'integer', + 'bigint', + 'bigserial', + 'int2', + 'int4', + 'int8', + 'serial', + 'serial2', + 'serial8', + 'smallint', + 'smallserial', + ]; + + case 'Lookup': + return ['character varying']; + + case 'Date': + return [ + 'date', + 'timestamp', + 'timestamp without time zone', + 'timestamptz', + 'timestamp with time zone', + ]; + + case 'DateTime': + case 'CreateTime': + case 'LastModifiedTime': + return [ + 'timestamp', + 'timestamp without time zone', + 'timestamptz', + 'timestamp with time zone', + ]; + + case 'AutoNumber': + return [ + 'int', + 'integer', + 'bigint', + 'bigserial', + 'int2', + 'int4', + 'int8', + 'serial', + 'serial2', + 'serial8', + 'smallint', + 'smallserial', + ]; + + case 'Barcode': + return ['character varying']; + + case 'Geometry': + return [ + 'polygon', + 'point', + 'circle', + 'box', + 'line', + 'lseg', + 'path', + 'circle', + ]; + + case 'Button': + default: + return dbTypes; + } + } + + static getUnsupportedFnList() { + return []; + } +} + +// module.exports = SnowflakeUiHelp; +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Naveen MR + * @author Pranav C Balan + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SqlUiFactory.ts b/packages/nocodb-sdk/src/lib/sqlUi/SqlUiFactory.ts index c48ae6c94c..6e79066cad 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SqlUiFactory.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SqlUiFactory.ts @@ -5,6 +5,7 @@ import { MysqlUi } from './MysqlUi'; import { OracleUi } from './OracleUi'; import { PgUi } from './PgUi'; import { SqliteUi } from './SqliteUi'; +import { SnowflakeUi } from './SnowflakeUi'; // import {YugabyteUi} from "./YugabyteUi"; // import {TidbUi} from "./TidbUi"; @@ -42,6 +43,10 @@ export class SqlUiFactory { return PgUi; } + if (connectionConfig.client === 'snowflake') { + return SnowflakeUi; + } + throw new Error('Database not supported'); } } diff --git a/packages/nocodb-sdk/src/lib/sqlUi/index.ts b/packages/nocodb-sdk/src/lib/sqlUi/index.ts index 5d3c4aecaa..bdc9600add 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/index.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/index.ts @@ -5,4 +5,5 @@ export * from './PgUi'; export * from './MssqlUi'; export * from './OracleUi'; export * from './SqliteUi'; +export * from './SnowflakeUi'; export * from './SqlUiFactory'; diff --git a/packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts b/packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts index cbc5d22a6c..a3a6af266e 100644 --- a/packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts +++ b/packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts @@ -6,6 +6,8 @@ import PgClient from './pg/PgClient'; import YugabyteClient from './pg/YugabyteClient'; import TidbClient from './mysql/TidbClient'; import VitessClient from './mysql/VitessClient'; +import SfClient from './snowflake/SnowflakeClient'; +import { SnowflakeClient } from 'knex-snowflake'; class SqlClientFactory { static create(connectionConfig) { @@ -31,6 +33,9 @@ class SqlClientFactory { if (connectionConfig.meta.dbtype === 'yugabyte') return new YugabyteClient(connectionConfig); return new PgClient(connectionConfig); + } else if (connectionConfig.client === 'snowflake') { + connectionConfig.client = SnowflakeClient; + return new SfClient(connectionConfig); } throw new Error('Database not supported'); diff --git a/packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts b/packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts new file mode 100644 index 0000000000..b47c50f62c --- /dev/null +++ b/packages/nocodb/src/lib/db/sql-client/lib/snowflake/SnowflakeClient.ts @@ -0,0 +1,2443 @@ +import { nanoid } from 'nanoid'; + +import _ from 'lodash'; +import KnexClient from '../KnexClient'; +import Debug from '../../../util/Debug'; +import Result from '../../../util/Result'; +import queries from './snowflake.queries'; +import lodash from 'lodash'; +const log = new Debug('SnowflakeClient'); + +const rowsToLower = (arr) => { + for(const a of arr) { + for(const [k, v] of Object.entries(a)) { + delete a[k]; + a[k.toLowerCase()] = v; + } + } + return arr; +} + +class SnowflakeClient extends KnexClient { + private queries: any; + private _version: any; + constructor(connectionConfig) { + super(connectionConfig); + // this.sqlClient = null; + this.queries = queries; + this._version = {}; + } + + /** + * + * + * @param {Object} args + * @returns {Object} result + * @returns {Number} code + * @returns {String} message + */ + async schemaCreateWithCredentials(args) { + const func = this.schemaCreateWithCredentials.name; + const result = new Result(); + log.api(`${func}:args:`, args); + + try { + if (!args.schema) { + args.schema = `nc${nanoid(8)}`; + } + + // const connectionParamsWithoutDb = JSON.parse( + // JSON.stringify(this.connectionConfig) + // ); + // + // delete connectionParamsWithoutDb.connection.database; + // + // const tempSqlClient = knex(connectionParamsWithoutDb); + + const data = await this.sqlClient.raw('create schema ?', [args.schema]); + + // postgres=# create database mydb; + // postgres=# create user myuser with encrypted password 'mypass'; + // postgres=# grant all privileges on database mydb to myuser; + + log.debug('Create database if not exists', data); + + // create new knex client + // this.sqlClient = knex(this.connectionConfig); + // tempSqlClient.destroy(); + result.object = args; + } catch (e) { + // log.ppe(e); + result.code = -1; + result.message = e.message; + result.object = e; + } + return result; + } + + /** + * + * @param {Object} - args + * @param args.sequence_name + * @returns {Promise<{upStatement, downStatement}>} + */ + async sequenceDelete(args: any = {}) { + const _func = this.sequenceDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + const query = `${this.querySeparator()}DROP SEQUENCE ${ + args.sequence_name + }`; + await this.sqlClient.raw(query); + result.data.object = { + upStatement: [{ sql: query }], + downStatement: [ + { + sql: `${this.querySeparator()}CREATE SEQUENCE ${ + args.sequence_name + }`, + }, + ], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @returns {Object[]} - sequences + * @property {String} - sequences[].sequence_name + * @property {String} - sequences[].type + * @property {String} - sequences[].definer + * @property {String} - sequences[].modified + * @property {String} - sequences[].created + * @property {String} - sequences[].security_type + * @property {String} - sequences[].comment + * @property {String} - sequences[].character_set_client + * @property {String} - sequences[].collation_connection + * @property {String} - sequences[].database collation + */ + async sequenceList(args: any = {}) { + const _func = this.sequenceList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + args.databaseName = this.connectionConfig.connection.database; + const { rows } = await this.raw(`select * + from INFORMATION_SCHEMA.sequences;`); + + result.data.list = rowsToLower(rows).map((seq) => { + return { + ...seq, + original_sequence_name: seq.sequence_name, + }; + }); + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.sequence_name + * @param {String} - args.start_value + * @param {String} - args.min_value + * @param {String} - args.max_value + * @param {String} - args.increment_by + * @returns {Object} - result + */ + async sequenceCreate(args: any = {}) { + const func = this.sequenceCreate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + const query = + this.querySeparator() + `CREATE SEQUENCE ${args.sequence_name}`; + await this.sqlClient.raw(query); + result.data.object = { + upStatement: [{ sql: query }], + downStatement: [ + { + sql: this.querySeparator() + `DROP SEQUENCE ${args.sequence_name}`, + }, + ], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.sequence_name + * @param {String} - args.start_value + * @param {String} - args.min_value + * @param {String} - args.max_value + * @param {String} - args.increment_by + * @returns {Object} - result + */ + async sequenceUpdate(args: any = {}) { + const func = this.sequenceUpdate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + const upQuery = + this.querySeparator() + + `ALTER SEQUENCE ${args.original_sequence_name} RENAME TO ${args.sequence_name};`; + const downQuery = + this.querySeparator() + + `ALTER SEQUENCE ${args.sequence_name} RENAME TO ${args.original_sequence_name};`; + + await this.sqlClient.raw(upQuery); + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: downQuery }], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * @param {Object} args + * @returns {Object} result + * @returns {Number} code + * @returns {String} message + */ + async testConnection(args: any = {}) { + const func = this.testConnection.name; + const result = new Result(); + log.api(`${func}:args:`, args); + + try { + await this.sqlClient.raw('SELECT 1+1 as data'); + } catch (e) { + // log.ppe(e); + result.code = -1; + result.message = e.message; + result.object = e; + } finally { + if (result.code) { + this.emitE(`TestConnection result: ${result.message}`); + } else { + this.emit(`TestConnection result: ${result.code}`); + } + } + return result; + } + + getKnexDataTypes() { + const result = new Result(); + + result.data.list = [ + 'NUMBER', + 'DECIMAL', + 'NUMERIC', + 'INT', + 'INTEGER', + 'BIGINT', + 'SMALLINT', + 'TINYINT', + 'BYTEINT', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'DOUBLE', + 'DOUBLE PRECISION', + 'REAL', + 'VARCHAR', + 'CHAR', + 'CHARACTER', + 'STRING', + 'TEXT', + 'BINARY', + 'VARBINARY', + 'BOOLEAN', + 'DATE', + 'DATETIME', + 'TIME', + 'TIMESTAMP', + 'TIMESTAMP_LTZ', + 'TIMESTAMP_NTZ', + 'TIMESTAMP_TZ', + 'VARIANT', + 'OBJECT', + 'ARRAY', + 'GEOGRAPHY', + ]; + + return result; + } + + /** + * + * + * @param {Object} args + * @returns {Object} result + * @returns {Number} code + * @returns {String} message + * @returns {Object} object - {version, primary, major, minor} + */ + async version(args: any = {}) { + const _func = this.version.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + result.data.object = {}; + const data = await this.sqlClient.raw('SELECT CURRENT_VERSION() as "server_version"'); + log.debug(data.rows[0]); + result.data.object.version = data.rows[0].server_version; + const versions = data.rows[0].server_version.split('.'); + + if (versions.length && (versions.length === 3 || versions.length === 2)) { + result.data.object.primary = versions[0]; + result.data.object.major = versions[1]; + result.data.object.minor = + versions.length > 2 ? versions[2] : versions[1]; + result.data.object.key = versions[0] + versions[1]; + } else { + result.code = -1; + result.message = `Invalid version : ${data.rows[0].server_version}`; + } + } catch (e) { + log.ppe(e); + result.code = -1; + result.message = e.message; + } finally { + log.api(`${_func} :result: %o`, result); + } + return result; + } + + /** + * + * @param {Object} args + * @param {String} args.database + * @returns {Result} + */ + async createDatabaseIfNotExists(args: any = {}) { + const _func = this.createDatabaseIfNotExists.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + let tempSqlClient; + + try { + let rows = []; + try { + log.debug('checking if db exists'); + rows = ( + await this.sqlClient.raw( + `SELECT DATABASE_NAME as database FROM information_schema.DATABASES WHERE DATABASE_NAME = '${args.database}'` + ) + ).rows; + } catch (e) { + log.debug('db does not exist'); + } + if (rows.length === 0) { + log.debug('creating database:', args); + await this.sqlClient.raw(`CREATE DATABASE ??`, [args.database]); + await this.sqlClient.raw(`USE ??`, [args.database]); + } + + await this.sqlClient.raw(`CREATE SCHEMA IF NOT EXISTS ??`,[this.schema]); + + } catch (e) { + log.ppe(e, _func); + throw e; + } finally { + if (tempSqlClient) { + await tempSqlClient.destroy(); + } + } + + log.api(`${_func}: result`, result); + return result; + } + + async dropDatabase(args) { + const _func = this.dropDatabase.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + await this.sqlClient.raw(`DROP DATABASE ${args.database};`); + } catch (e) { + log.ppe(e, _func); + // throw e; + } + + log.api(`${_func}: result`, result); + return result; + } + + /** + * + * @param args {tn} + * @returns + */ + async createTableIfNotExists(args) { + const _func = this.createTableIfNotExists.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + /** ************** START : create _evolution table if not exists *************** */ + const exists = await this.sqlClient.raw( + `SELECT table_schema,table_name as "tn", table_catalog + FROM information_schema.tables + where table_schema=? and table_name = ?`, + [this.schema, args.tn] + ); + + if (exists.rows.length === 0) { + const data = await this.sqlClient.schema.createTable( + args.tn, + function (table) { + table.increments(); + table.string('title').notNullable(); + table.string('titleDown').nullable(); + table.string('description').nullable(); + table.integer('batch').nullable(); + table.string('checksum').nullable(); + table.integer('status').nullable(); + table.dateTime('created'); + table.timestamps(); + } + ); + log.debug('Table created:', `${args.tn}`, data); + } else { + log.debug(`${args.tn} tables exists`); + } + /** ************** END : create _evolution table if not exists *************** */ + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async hasTable(args: any = {}) { + const _func = this.hasTable.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + const { rows } = await this.sqlClient.raw( + `SELECT table_schema,table_name as "tn", table_catalog FROM information_schema.tables where table_schema = ? and table_name = ? and table_catalog = ?`, + [this.schema, args.tn, this.connectionConfig.connection.database] + ); + result.data.value = rows.length > 0; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async hasDatabase(args: any = {}) { + const _func = this.hasDatabase.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + const { rows } = await this.sqlClient.raw( + `SELECT DATABASE_NAME as database FROM information_schema.DATABASES WHERE DATABASE_NAME = ?`, + [args.databaseName] + ); + result.data.value = rows.length > 0; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - for future reasons + * @returns {Object[]} - databases + * @property {String} - databases[].database_name + */ + async databaseList(args: any = {}) { + const _func = this.databaseList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + const { rows } = await this.sqlClient.raw( + `SELECT DATABASE_NAME as database_name FROM information_schema.DATABASES;` + ); + result.data.list = rows; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - for future reasons + * @returns {Object[]} - tables + * @property {String} - tables[].tn + */ + async tableList(args: any = {}) { + const _func = this.tableList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + const { rows } = await this.raw( + `SELECT table_schema as "ts", table_name as "tn", table_type as "table_type" + FROM information_schema.tables + where table_schema = ? + ORDER BY table_schema, table_name`, + [this.schema] + ); + + result.data.list = rows.filter( + ({ table_type }) => table_type.toLowerCase() === 'base table' + ); + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async schemaList(args: any = {}) { + const _func = this.schemaList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + const { rows } = await this + .raw(`SELECT SCHEMA_NAME as "schema_name" FROM information_schema.SCHEMATA order by schema_name;`); + + result.data.list = rows; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - columns + * @property {String} - columns[].tn + * @property {String} - columns[].cn + * @property {String} - columns[].dt + * @property {String} - columns[].dtx + * @property {String} - columns[].np + * @property {String} - columns[].ns - + * @property {String} - columns[].clen - + * @property {String} - columns[].dp - + * @property {String} - columns[].cop - + * @property {String} - columns[].pk - + * @property {String} - columns[].nrqd - + * @property {String} - columns[].not_nullable - + * @property {String} - columns[].ct - + * @property {String} - columns[].un - + * @property {String} - columns[].ai - + * @property {String} - columns[].unique - + * @property {String} - columns[].cdf - + * @property {String} - columns[].cc - + * @property {String} - columns[].csn - + */ + + async columnList(args: any = {}) { + const _func = this.columnList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + args.databaseName = this.connectionConfig.connection.database; + await this.sqlClient.raw(`SHOW PRIMARY KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`); + await this.sqlClient.raw(`SHOW UNIQUE KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`); + + const lastQueries = await this.sqlClient.raw(` + select * from table(information_schema.query_history()) + WHERE query_text like 'SHOW%' + ORDER BY start_time DESC + LIMIT 200` + ); + + let pk_query_id, uq_query_id; + for (const r of lastQueries.rows) { + if (r.QUERY_TEXT === `SHOW PRIMARY KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`) { + pk_query_id = r.QUERY_ID; + } else if (r.QUERY_TEXT === `SHOW UNIQUE KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`) { + uq_query_id = r.QUERY_ID; + } + if (pk_query_id && uq_query_id) { + break; + } + } + + const response = await this.sqlClient.raw( + `SELECT + cl.table_name as "tn", + column_name as "cn", + data_type as "dt", + is_identity as "au", + tc.constraint_type as "ck", + character_maximum_length as "clen", + numeric_precision as "np", + numeric_scale as "ns", + ordinal_position as "cop", + is_nullable as "nrqd", + column_default as "cdf", + identity_generation as "generation_expression", + character_octet_length as "character_octet_length", + character_set_name as "csn", + "PK"."key_sequence" as "pk_ordinal_position", + "PK"."constraint_name" as "pk_constraint_name", + udt_name + FROM information_schema.COLUMNS cl + LEFT JOIN (select * from table(result_scan('${pk_query_id}')) UNION select * from table(result_scan('${uq_query_id}'))) pk + LEFT JOIN information_schema.table_constraints tc ON tc.constraint_name = "PK"."constraint_name" + ON "PK"."schema_name" = cl.table_schema and "PK"."table_name" = cl.table_name and pk."column_name" = cl.column_name + WHERE cl.table_catalog = ? and cl.table_schema = ? and cl.table_name = ?;`, + [this.connectionConfig.connection.database, this.schema, args.tn] + ); + + const columns = []; + + for (let i = 0; i < response.rows.length; ++i) { + const column: any = {}; + + column.tn = response.rows[i].tn; + column.cn = response.rows[i].cn; + column.cno = response.rows[i].cn; + column.dt = response.rows[i].dt; + column.np = response.rows[i].np; + column.ns = response.rows[i].ns; + column.clen = response.rows[i].clen; + column.dp = response.rows[i].dp; + column.cop = response.rows[i].cop; + + // todo : there are lot of types in pg - handle them + //column.dtx = this.getKnexDataType(column.dt); + column.dtx = response.rows[i].dt; + column.pk = response.rows[i].pk_constraint_name !== null; + + column.nrqd = response.rows[i].nrqd !== 'NO'; + column.not_nullable = !column.nrqd; + column.rqd = !column.nrqd; + + // todo: there is no type of unsigned in postgres + response.rows[i].ct = response.rows[i].dt || ''; + column.un = response.rows[i].ct.indexOf('unsigned') !== -1; + + column.ai = false; + if (response.rows[i].cdf) { + column.ai = response.rows[i].cdf.indexOf('nextval') !== -1; + } + + // todo : need to find if column is unique or not + // column['unique'] = response.rows[i]['cst'].indexOf('UNIQUE') === -1 ? false : true; + + column.cdf = response.rows[i].cdf; + // ? response.rows[i].cdf.split("::")[0].replace(/'/g, "") + // : response.rows[i].cdf; + + // todo : need to find column comment + column.cc = response.rows[i].cc; + + column.csn = response.rows[i].csn; + column.dtxp = + response.rows[i].clen || response.rows[i].np || response.rows[i].dp; + column.dtxs = response.rows[i].ns; + column.au = response.rows[i].au; + column.data_type_custom = response.rows[i].udt_name; + if (column.dt === 'USER-DEFINED') { + column.dtxp = response.rows[i].enum_values; + } + + columns.push(column); + } + + result.data.list = columns; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - indexes + * @property {String} - indexes[].table - + * @property {String} - indexes[].cn - + * @property {String} - indexes[].key_name - + * @property {String} - indexes[].non_unique - + * @property {String} - indexes[].seq_in_index - + * @property {String} - indexes[].collation - + * @property {String} - indexes[].cardinality - + * @property {String} - indexes[].sub_part - + * @property {String} - indexes[].packed - + * @property {String} - indexes[].null - + * @property {String} - indexes[].index_type - + * @property {String} - indexes[].comment - + * @property {String} - indexes[].index_comment - + * @property {String} - indexes[].cstn - + * @property {String} - indexes[].cst - c = check constraint, f = foreign key constraint, p = primary key constraint, u = unique constraint, t = constraint trigger, x = exclusion constraint + */ + async indexList(args: any = {}) { + const _func = this.indexList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + // TODO indexList + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - indexes + * @property {String} - indexes[].cstn - + * @property {String} - indexes[].cn - + * @property {String} - indexes[].op - + * @property {String} - indexes[].puc - + * @property {String} - indexes[].cst - + */ + async constraintList(args: any = {}) { + const _func = this.constraintList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + await this.sqlClient.raw(`SHOW PRIMARY KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`); + await this.sqlClient.raw(`SHOW UNIQUE KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`); + + const lastQueries = await this.sqlClient.raw(` + select * from table(information_schema.query_history()) + WHERE query_text like 'SHOW%' + ORDER BY start_time DESC + LIMIT 200` + ); + + let pk_query_id, uq_query_id; + for (const r of lastQueries.rows) { + if (r.QUERY_TEXT === `SHOW PRIMARY KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`) { + pk_query_id = r.QUERY_ID; + } else if (r.QUERY_TEXT === `SHOW UNIQUE KEYS IN SCHEMA ${this.connectionConfig.connection.database}.${this.schema};`) { + uq_query_id = r.QUERY_ID; + } + if (pk_query_id && uq_query_id) { + break; + } + } + + const { rows } = await this.sqlClient.raw( + `SELECT + constraint_name as "cstn", + "PK"."column_name" as "cn", + constraint_type as "cst" + FROM information_schema.table_constraints tc + LEFT JOIN (select * from table(result_scan('${pk_query_id}')) UNION select * from table(result_scan('${uq_query_id}'))) pk + ON "PK"."constraint_name" = tc.constraint_name + WHERE tc.table_catalog = ? and tc.table_schema = ? and tc.table_name = ?;`, + [this.connectionConfig.connection.database, this.schema, args.tn]); + + result.data.list = rows; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - relations + * @property {String} - relations[].tn + * @property {String} - relations[].cstn - + * @property {String} - relations[].tn - + * @property {String} - relations[].cn - + * @property {String} - relations[].rtn - + * @property {String} - relations[].rcn - + * @property {String} - relations[].puc - + * @property {String} - relations[].ur - + * @property {String} - relations[].dr - + * @property {String} - relations[].mo - + */ + async relationList(args: any = {}) { + const _func = this.relationList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - relations + * @property {String} - relations[].tn + * @property {String} - relations[].cstn - + * @property {String} - relations[].tn - + * @property {String} - relations[].cn - + * @property {String} - relations[].rtn - + * @property {String} - relations[].rcn - + * @property {String} - relations[].puc - + * @property {String} - relations[].ur - + * @property {String} - relations[].dr - + * @property {String} - relations[].mo - + */ + async relationListAll(args: any = {}) { + const _func = this.relationList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.tn - + * @returns {Object[]} - triggers + * @property {String} - triggers[].trigger + * @property {String} - triggers[].event + * @property {String} - triggers[].table + * @property {String} - triggers[].statement + * @property {String} - triggers[].timing + * @property {String} - triggers[].created + * @property {String} - triggers[].sql_mode + * @property {String} - triggers[].definer + * @property {String} - triggers[].character_set_client + * @property {String} - triggers[].collation_connection + * @property {String} - triggers[].database collation + */ + async triggerList(args: any = {}) { + const _func = this.triggerList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @returns {Object[]} - functions + * @property {String} - functions[].function_name + * @property {String} - functions[].type + * @property {String} - functions[].definer + * @property {String} - functions[].modified + * @property {String} - functions[].created + * @property {String} - functions[].security_type + * @property {String} - functions[].comment + * @property {String} - functions[].character_set_client + * @property {String} - functions[].collation_connection + * @property {String} - functions[].database collation + */ + async functionList(args: any = {}) { + const _func = this.functionList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * @todo Remove the function - pg doesn't support procedure + * + * @param {Object} - args - For future reasons + * @returns {Object[]} - procedures + * @property {String} - procedures[].procedure_name + * @property {String} - procedures[].type + * @property {String} - procedures[].definer + * @property {String} - procedures[].modified + * @property {String} - procedures[].created + * @property {String} - procedures[].security_type + * @property {String} - procedures[].comment + * @property {String} - procedures[].definer + * @property {String} - procedures[].character_set_client + * @property {String} - procedures[].collation_connection + * @property {String} - procedures[].database collation + */ + async procedureList(args: any = {}) { + const _func = this.procedureList.name; + const result = new Result(); + result.data.list = []; + log.api(`${_func}:args:`, args); + + // todo: update query - https://dataedo.com/kb/query/postgresql/list-stored-procedures + try { + result.data.list = []; + } catch (e) { + // todo: enable log + // log.ppe(e, _func); + // throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @returns {Object[]} - views + * @property {String} - views[].sql_mode + * @property {String} - views[].create_function + * @property {String} - views[].database collation + * @property {String} - views[].collation_connection + * @property {String} - views[].character_set_client + */ + async viewList(args: any = {}) { + const _func = this.viewList.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + const { rows } = await this.sqlClient.raw( + `select * from INFORMATION_SCHEMA.views WHERE table_schema = ?;`, + [this.schema] + ); + + for (let i = 0; i < rows.length; ++i) { + rows[i].view_name = rows[i].TABLE_NAME; + } + + result.data.list = rows; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.function_name - + * @returns {Object[]} - functions + * @property {String} - create_function + * @property {String} - function_declaration + */ + async functionRead(args: any = {}) { + const _func = this.functionRead.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * @todo Remove the function - pg doesn't support procedure + * + * @param {Object} - args - Input arguments + * @param {Object} - args.procedure_name - + * @returns {Object[]} - functions + * @property {String} - sql_mode + * @property {String} - create_function + * @property {String} - database collation + * @property {String} - collation_connection + * @property {String} - character_set_client + */ + async procedureRead(args: any = {}) { + const _func = this.procedureRead.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {Object} - args.view_name - + * @returns {Object[]} - views + * @property {String} - views[].tn + */ + async viewRead(args: any = {}) { + const _func = this.viewRead.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + args.databaseName = this.connectionConfig.connection.database; + + const { rows } = await this.sqlClient.raw( + `select * from INFORMATION_SCHEMA.views WHERE table_name='${args.view_name}' and table_schema = ${this.schema};` + ); + + for (let i = 0; i < rows.length; ++i) { + rows[i].view_name = rows[i].TABLE_NAME; + // rows[i].view_definition = rows[i].view_definition; + } + + result.data.list = rows; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async triggerRead(args: any = {}) { + const _func = this.triggerRead.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data.list = []; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async schemaCreate(args: any = {}) { + const _func = this.schemaCreate.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + await this.sqlClient.raw(`create schema ${args.database_name}`); + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async schemaDelete(args: any = {}) { + const _func = this.schemaDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + await this.sqlClient.raw(`drop schema ${args.database_name}`); + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + async triggerDelete(args: any = {}) { + const _func = this.triggerDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + const upQuery = this.genQuery(`DROP TRIGGER IF EXISTS ?? ON ??`, [ + args.trigger_name, + args.tn, + ]); + await this.sqlClient.raw(upQuery); + result.data.object = { + upStatement: [{ sql: this.querySeparator() + upQuery }], + downStatement: [ + { + sql: + this.querySeparator() + + this.genQuery( + `CREATE TRIGGER ?? \n${args.timing} ${args.event}\nON ?? FOR EACH ROW\n${args.statement}`, + [args.trigger_name, args.tn] + ), + }, + ], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.function_name + * @param {String} - args.function_declaration + * @param {String} - args.create_function + * @returns {Object[]} - result rows + */ + async functionDelete(args: any = {}) { + const _func = this.functionDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + const upQuery = + this.querySeparator() + + `DROP FUNCTION IF EXISTS ${args.function_declaration}`; + const downQuery = this.querySeparator() + args.create_function; + try { + throw new Error('Function not supported for Snowflake yet'); + await this.sqlClient.raw(upQuery); + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: downQuery }], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + log.api(`${_func}: result`, result); + + return result; + } + + // @todo Remove the function - pg doesn't support procedure + async procedureDelete(args: any = {}) { + const _func = this.procedureDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + await this.sqlClient.raw( + `DROP PROCEDURE IF EXISTS ${args.procedure_name}` + ); + } catch (e) { + log.ppe(e, _func); + throw e; + } + log.api(`${_func}: result`, result); + + return result; + } + + /** + * + * @param {Object} args + * @param {String} func : function name + * @returns {Result} + * @returns {Object} - Result.data + * @returns {String} - Result.data.value - sql query + */ + async _getQuery(args) { + try { + if (_.isEmpty(this._version)) { + const result = await this.version(); + this._version = result.data.object; + log.debug( + `Version was empty for ${args.func}: population version for database as`, + this._version + ); + } + + // log.debug(this._version, args); + + if (this._version.key in this.queries[args.func]) { + return this.queries[args.func][this._version.key].sql; + } + return this.queries[args.func].default.sql; + } catch (error) { + log.ppe(error, this._getQuery.name); + throw error; + } + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.function_name + * @param {String} - args.create_function + * @returns {Object[]} - result rows + */ + async functionCreate(args: any = {}) { + const func = this.functionCreate.name; + const result = new Result(); + + log.api(`${func}:args:`, args); + + try { + throw new Error('Function not supported for Snowflake yet'); + const upQuery = this.querySeparator() + args.create_function; + + await this.sqlClient.raw(upQuery); + + const functionCreated = await this.functionRead({ + function_name: args.function_name, + }); + + const downQuery = + this.querySeparator() + + `DROP FUNCTION IF EXISTS ${functionCreated.data.list[0].function_declaration}`; + + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: downQuery }], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.tn + * @param {String} - args.function_name + * @param {String} - args.event + * @param {String} - args.timing + * @returns {Object[]} - result rows + */ + async functionUpdate(args: any = {}) { + const func = this.functionUpdate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + const upQuery = this.querySeparator() + args.create_function; + let downQuery = this.querySeparator() + args.oldCreateFunction; + + await this.sqlClient.raw( + `DROP FUNCTION IF EXISTS ${args.function_declaration};` + ); + await this.sqlClient.raw(upQuery); + + const functionCreated = await this.functionRead({ + function_name: args.function_name, + }); + + downQuery = + `DROP FUNCTION IF EXISTS ${functionCreated.data.list[0].function_declaration};` + + downQuery; + + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: downQuery }], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * @todo Remove the function - pg doesn't support procedure + * + * @param {Object} - args - Input arguments + * @param {String} - args.tn + * @param {String} - args.procedure_name + * @param {String} - args.event + * @param {String} - args.timing + * @returns {Object[]} - result rows + * + */ + async procedureCreate(args: any = {}) { + const func = this.procedureCreate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + const upQuery = + this.querySeparator() + + `CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.statement}`; + await this.sqlClient.raw(upQuery); + const downQuery = + this.querySeparator() + + `DROP PROCEDURE IF EXISTS ${args.procedure_name}`; + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: downQuery }], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * @todo Remove the function - pg doesn't support procedure + * + * @param {Object} - args - Input arguments + * @param {String} - args.tn + * @param {String} - args.procedure_name + * @param {String} - args.event + * @param {String} - args.timing + * @returns {Object[]} - result rows + */ + async procedureUpdate(args: any = {}) { + const func = this.procedureUpdate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + const query = + this.querySeparator() + `DROP TRIGGER ${args.procedure_name}`; + const upQuery = + this.querySeparator() + + `CREATE TRIGGER \`${args.procedure_name}\` \n${args.timing} ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.statement}`; + + await this.sqlClient.raw(query); + await this.sqlClient.raw(upQuery); + + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [{ sql: ';' }], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.tn + * @param {String} - args.trigger_name + * @param {String} - args.event + * @param {String} - args.timing + * @returns {Object[]} - result rows + */ + async triggerCreate(args: any = {}) { + const func = this.triggerCreate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + const upQuery = + this.querySeparator() + + `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.statement}`; + await this.sqlClient.raw(upQuery); + result.data.object = { + upStatement: [{ sql: upQuery }], + downStatement: [ + { sql: this.querySeparator() + `DROP TRIGGER ${args.trigger_name}` }, + ], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.tn + * @param {String} - args.trigger_name + * @param {String} - args.event + * @param {String} - args.timing + * @param {String} - args.oldStatement + * @returns {Object[]} - result rows + */ + async triggerUpdate(args: any = {}) { + const func = this.triggerUpdate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + throw new Error('Function not supported for Snowflake yet'); + await this.sqlClient.raw( + `DROP TRIGGER ${args.trigger_name} ON ${args.tn}` + ); + await this.sqlClient.raw( + `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.statement}` + ); + + result.data.object = { + upStatement: + this.querySeparator() + + `DROP TRIGGER ${args.trigger_name} ON ${ + args.tn + };${this.querySeparator()}CREATE TRIGGER ${args.trigger_name} \n${ + args.timing + } ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.statement}`, + downStatement: + this.querySeparator() + + `CREATE TRIGGER ${args.trigger_name} \n${args.timing} ${args.event}\nON "${args.tn}" FOR EACH ROW\n${args.oldStatement}`, + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.view_name + * @param {String} - args.view_definition + * @returns {Object} - up and down statements + */ + async viewCreate(args: any = {}) { + const func = this.viewCreate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + const query = args.view_definition; + + await this.sqlClient.raw(query); + result.data.object = { + upStatement: [{ sql: this.querySeparator() + query }], + downStatement: [ + { sql: this.querySeparator() + `DROP VIEW "${args.view_name}"` }, + ], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.view_name + * @param {String} - args.view_definition + * @param {String} - args.oldViewDefination + * @returns {Object} - up and down statements + */ + async viewUpdate(args: any = {}) { + const func = this.viewUpdate.name; + const result = new Result(); + log.api(`${func}:args:`, args); + try { + const query = `CREATE OR REPLACE VIEW "${args.view_name}" AS \n${args.view_definition}`; + + await this.sqlClient.raw(query); + result.data.object = { + upStatement: this.querySeparator() + query, + downStatement: + this.querySeparator() + + `CREATE VIEW "${args.view_name}" AS \n${args.oldViewDefination}`, + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args - Input arguments + * @param {String} - args.view_name + * @param {String} - args.view_definition + * @param {String} - args.oldViewDefination + * @returns {Object} - up and down statements + */ + async viewDelete(args: any = {}) { + const func = this.viewDelete.name; + const result = new Result(); + log.api(`${func}:args:`, args); + // `DROP TRIGGER ${args.view_name}` + try { + const query = `DROP VIEW ${args.view_name}`; + + await this.sqlClient.raw(query); + + result.data.object = { + upStatement: [{ sql: this.querySeparator() + query }], + downStatement: [ + { + sql: + this.querySeparator() + + `CREATE VIEW "${args.view_name}" AS \n${args.oldViewDefination}`, + }, + ], + }; + } catch (e) { + log.ppe(e, func); + throw e; + } + + log.api(`${func}: result`, result); + return result; + } + + /** + * + * @param {Object} - args + * @param {String} - args.tn + * @param {Object[]} - args.columns + * @param {String} - args.columns[].tn + * @param {String} - args.columns[].cn + * @param {String} - args.columns[].dt + * @param {String} - args.columns[].np + * @param {String} - args.columns[].ns - + * @param {String} - args.columns[].clen - + * @param {String} - args.columns[].dp - + * @param {String} - args.columns[].cop - + * @param {String} - args.columns[].pk - + * @param {String} - args.columns[].nrqd - + * @param {String} - args.columns[].not_nullable - + * @param {String} - args.columns[].ct - + * @param {String} - args.columns[].un - + * @param {String} - args.columns[].ai - + * @param {String} - args.columns[].unique - + * @param {String} - args.columns[].cdf - + * @param {String} - args.columns[].cc - + * @param {String} - args.columns[].csn - + * @param {String} - args.columns[].dtx + * - value will be 'specificType' for all cols except ai + * - for ai it will be integer, bigInteger + * - tiny, small and medium Int auto increement is not supported + * @param {String} - args.columns[].dtxp - to use in UI + * @param {String} - args.columns[].dtxs - to use in UI + * @returns {Promise<{upStatement, downStatement}>} + */ + async tableCreate(args) { + const _func = this.tableCreate.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + args.table = args.tn; + args.sqlClient = this.sqlClient; + + /**************** create table ****************/ + const upQuery = this.querySeparator() + this.createTable(args.tn, args); + await this.sqlClient.raw(upQuery); + + const downStatement = + this.querySeparator() + + this.sqlClient.schema.dropTable(args.table).toString(); + + this.emit(`Success : ${upQuery}`); + + const triggerStatements = await this.afterTableCreate(args); + + /**************** return files *************** */ + result.data.object = { + upStatement: [{ sql: upQuery }, ...triggerStatements.upStatement], + downStatement: [ + ...triggerStatements.downStatement, + { sql: downStatement }, + ], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + async afterTableCreate(args) { + const result = { upStatement: [], downStatement: [] }; + let upQuery = ''; + let downQuery = ''; + + // TODO handle + for (let i = 0; i < 0; i++) { + const column = args.columns[i]; + if (column.au) { + const triggerFnName = `xc_au_${args.tn}_${column.cn}`; + const triggerName = `xc_trigger_${args.tn}_${column.cn}`; + + const triggerFnQuery = this.genQuery( + `CREATE OR REPLACE FUNCTION ??() + RETURNS TRIGGER AS $$ + BEGIN + NEW.?? = NOW(); + RETURN NEW; + END; + $$ LANGUAGE plpgsql;`, + [triggerFnName, column.cn] + ); + + upQuery += + this.querySeparator() + + triggerFnQuery + + this.querySeparator() + + this.genQuery( + `CREATE TRIGGER ?? + BEFORE UPDATE ON ?? + FOR EACH ROW + EXECUTE PROCEDURE ??();`, + [triggerName, args.tn, triggerFnName] + ); + + downQuery += + this.querySeparator() + + this.genQuery(`DROP TRIGGER IF EXISTS ?? ON ??;`, [ + triggerName, + args.tn, + ]) + + this.querySeparator() + + this.genQuery(`DROP FUNCTION IF EXISTS ??()`, [triggerFnName]); + } + } + await this.sqlClient.raw(upQuery); + result.upStatement[0] = { sql: upQuery }; + result.downStatement[0] = { sql: downQuery }; + + return result; + } + + async afterTableUpdate(args) { + const result = { upStatement: [], downStatement: [] }; + let upQuery = ''; + let downQuery = ''; + + // TODO handle + for (let i = 0; i < 0; i++) { + const column = args.columns[i]; + if (column.au && column.altered === 1) { + const triggerFnName = `xc_au_${args.tn}_${column.cn}`; + const triggerName = `xc_trigger_${args.tn}_${column.cn}`; + + const triggerFnQuery = this.genQuery( + `CREATE OR REPLACE FUNCTION ??() + RETURNS TRIGGER AS $$ + BEGIN + NEW.?? = NOW(); + RETURN NEW; + END; + $$ LANGUAGE plpgsql;`, + [triggerFnName, column.cn] + ); + + upQuery += + this.querySeparator() + + triggerFnQuery + + this.querySeparator() + + this.genQuery( + `CREATE TRIGGER ?? + BEFORE UPDATE ON ?? + FOR EACH ROW + EXECUTE PROCEDURE ??();`, + [triggerName, args.tn, triggerFnName] + ); + + downQuery += + this.querySeparator() + + this.genQuery(`DROP TRIGGER IF EXISTS ?? ON ??;`, [ + triggerName, + args.tn, + ]) + + this.querySeparator() + + this.genQuery(`DROP FUNCTION IF EXISTS ??()`, [triggerFnName]); + } + } + await this.sqlClient.raw(upQuery); + result.upStatement[0] = { sql: upQuery }; + result.downStatement[0] = { sql: downQuery }; + + return result; + } + + /** + * + * @param {Object} - args + * @param {String} - args.table + * @param {String} - args.table + * @param {Object[]} - args.columns + * @param {String} - args.columns[].tn + * @param {String} - args.columns[].cn + * @param {String} - args.columns[].dt + * @param {String} - args.columns[].np + * @param {String} - args.columns[].ns - + * @param {String} - args.columns[].clen - + * @param {String} - args.columns[].dp - + * @param {String} - args.columns[].cop - + * @param {String} - args.columns[].pk - + * @param {String} - args.columns[].nrqd - + * @param {String} - args.columns[].not_nullable - + * @param {String} - args.columns[].ct - + * @param {String} - args.columns[].un - + * @param {String} - args.columns[].ai - + * @param {String} - args.columns[].unique - + * @param {String} - args.columns[].cdf - + * @param {String} - args.columns[].cc - + * @param {String} - args.columns[].csn - + * @param {Number} - args.columns[].altered - 1,2,4 = addition,edited,deleted + * @returns {Promise<{upStatement, downStatement}>} + */ + async tableUpdate(args) { + const _func = this.tableUpdate.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + args.table = args.tn; + const originalColumns = args.originalColumns; + args.connectionConfig = this._connectionConfig; + args.sqlClient = this.sqlClient; + + let upQuery = ''; + let downQuery = ''; + + for (let i = 0; i < args.columns.length; ++i) { + const oldColumn = lodash.find(originalColumns, { + cn: args.columns[i].cno, + }); + + if (args.columns[i].altered & 4) { + // col remove + upQuery += this.alterTableRemoveColumn( + args.table, + args.columns[i], + oldColumn, + upQuery + ); + downQuery += this.alterTableAddColumn( + args.table, + oldColumn, + args.columns[i], + downQuery + ); + } else if (args.columns[i].altered & 2 || args.columns[i].altered & 8) { + // col edit + upQuery += this.alterTableChangeColumn( + args.table, + args.columns[i], + oldColumn, + upQuery + ); + downQuery += this.alterTableChangeColumn( + args.table, + oldColumn, + args.columns[i], + downQuery + ); + } else if (args.columns[i].altered & 1) { + // col addition + upQuery += this.alterTableAddColumn( + args.table, + args.columns[i], + oldColumn, + upQuery + ); + downQuery += this.alterTableRemoveColumn( + args.table, + args.columns[i], + oldColumn, + downQuery + ); + } + } + + upQuery += + (upQuery ? ';' : '') + + this.alterTablePK( + args.table, + args.columns, + args.originalColumns, + upQuery + ); + downQuery += + (downQuery ? ';' : '') + + this.alterTablePK( + args.table, + args.originalColumns, + args.columns, + downQuery + ); + + if (upQuery) { + //upQuery = `ALTER TABLE "${args.columns[0].tn}" ${upQuery};`; + //downQuery = `ALTER TABLE "${args.columns[0].tn}" ${downQuery};`; + } + + await this.sqlClient.raw(upQuery); + + // console.log(upQuery); + + const afterUpdate = await this.afterTableUpdate(args); + + result.data.object = { + upStatement: [ + { sql: this.querySeparator() + upQuery }, + ...afterUpdate.upStatement, + ], + downStatement: [ + ...afterUpdate.downStatement, + { sql: this.querySeparator() + downQuery }, + ], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param {Object} - args + * @param args.tn + * @returns {Promise<{upStatement, downStatement}>} + */ + async tableDelete(args) { + const _func = this.tableDelete.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + // const { columns } = args; + args.sqlClient = this.sqlClient; + + /** ************** create up & down statements *************** */ + const upStatement = + this.querySeparator() + + this.sqlClient.schema.dropTable(args.tn).toString(); + let downQuery = this.createTable(args.tn, args); + + /** + + columnList + relationList + indexesList + createAggregatedIndexes + filterOutPkAndFk + + downQuery + create table - via columnList - we are doing this + + create fks - via relationList + + create indexes - slightly tricky + + */ + + let relationsList: any = await this.relationList(args); + + relationsList = relationsList.data.list; + + for (const relation of relationsList) { + downQuery += + this.querySeparator() + + (await this.sqlClient.schema + .table(relation.tn, function (table) { + table = table + .foreign(relation.cn, null) + .references(relation.rcn) + .on(relation.rtn); + + if (relation.ur) { + table = table.onUpdate(relation.ur); + } + if (relation.dr) { + table = table.onDelete(relation.dr); + } + }) + .toQuery()); + } + + let indexList: any = await this.indexList(args); + + indexList = indexList.data.list.filter( + ({ cst }) => cst !== 'p' && cst !== 'f' + ); + + const indexesMap: { [key: string]: any } = {}; + + for (const { key_name, non_unique, cn } of indexList) { + if (!(key_name in indexesMap)) { + indexesMap[key_name] = { + tn: args.tn, + indexName: key_name, + non_unique, + columns: [], + }; + } + indexesMap[key_name].columns.push(cn); + } + + for (const { non_unique, tn, columns, indexName } of Object.values( + indexesMap + )) { + downQuery += + this.querySeparator() + + this.sqlClient.schema + .table(tn, function (table) { + if (non_unique) { + table.index(columns, indexName); + } else { + table.unique(columns, indexName); + } + }) + .toQuery(); + } + + this.emit(`Success : ${upStatement}`); + + /** ************** drop tn *************** */ + await this.sqlClient.schema.dropTable(args.tn); + + /** ************** return files *************** */ + result.data.object = { + upStatement: [{ sql: upStatement }], + downStatement: [{ sql: this.querySeparator() + downQuery }], + }; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableCreateStatement(args) { + const _func = this.tableCreateStatement.name; + let result = new Result(); + log.api(`${_func}:args:`, args); + try { + result = await this.columnList(args); + const upQuery = this.createTable(args.tn, { + tn: args.tn, + columns: result.data.list, + }); + result.data = upQuery; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableInsertStatement(args) { + const _func = this.tableCreateStatement.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data = `INSERT INTO \`${args.tn}\` (`; + let values = ' VALUES ('; + const response = await this.columnList(args); + if (response.data && response.data.list) { + for (let i = 0; i < response.data.list.length; ++i) { + if (!i) { + result.data += `\n"${response.data.list[i].cn}"\n\t`; + values += `\n<${response.data.list[i].cn}>\n\t`; + } else { + result.data += `, \`"${response.data.list[i].cn}"\`\n\t`; + values += `, <${response.data.list[i].cn}>\n\t`; + } + } + } + + result.data += `)`; + values += `);`; + result.data += values; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableUpdateStatement(args) { + const _func = this.tableUpdateStatement.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + result.data = `UPDATE "${args.tn}" \nSET\n`; + const response = await this.columnList(args); + if (response.data && response.data.list) { + for (let i = 0; i < response.data.list.length; ++i) { + if (!i) { + result.data += `"${response.data.list[i].cn}" = <\`${response.data.list[i].cn}\`>\n\t`; + } else { + result.data += `,"${response.data.list[i].cn}" = <\`${response.data.list[i].cn}\`>\n\t`; + } + } + } + + result.data += ';'; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableDeleteStatement(args) { + const _func = this.tableDeleteStatement.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data = `DELETE FROM "${args.tn}" where ;`; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableTruncateStatement(args) { + const _func = this.tableTruncateStatement.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + try { + result.data = `TRUNCATE TABLE "${args.tn}";`; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + /** + * + * @param args + * @param args.tn + * @returns {Object} Result + * @returns {String} result.data + */ + async tableSelectStatement(args) { + const _func = this.tableSelectStatement.name; + const result = new Result(); + log.api(`${_func}:args:`, args); + + try { + result.data = `SELECT `; + const response = await this.columnList(args); + if (response.data && response.data.list) { + for (let i = 0; i < response.data.list.length; ++i) { + if (!i) { + result.data += `"${response.data.list[i].cn}"\n\t`; + } else { + result.data += `, "${response.data.list[i].cn}"\n\t`; + } + } + } + + result.data += ` FROM "${args.tn}";`; + } catch (e) { + log.ppe(e, _func); + throw e; + } + + return result; + } + + alterTablePK(t, n, o, _existingQuery, createTable = false) { + const numOfPksInOriginal = []; + const numOfPksInNew = []; + let pksChanged = 0; + + for (let i = 0; i < n.length; ++i) { + if (n[i].pk) { + if (n[i].altered !== 4) numOfPksInNew.push(n[i].cn); + } + } + + for (let i = 0; i < o.length; ++i) { + if (o[i].pk) { + numOfPksInOriginal.push(o[i].cn); + } + } + + if (numOfPksInNew.length === numOfPksInOriginal.length) { + for (let i = 0; i < numOfPksInNew.length; ++i) { + if (numOfPksInOriginal[i] !== numOfPksInNew[i]) { + pksChanged = 1; + break; + } + } + } else { + pksChanged = numOfPksInNew.length - numOfPksInOriginal.length; + } + + let query = ''; + if (!numOfPksInNew.length && !numOfPksInOriginal.length) { + // do nothing + } else if (pksChanged) { + query += numOfPksInOriginal.length + ? this.genQuery(`alter TABLE ?? drop constraint IF EXISTS ??;`, [ + t, + `${t}_pkey`, + ]) + : ''; + if (numOfPksInNew.length) { + if (createTable) { + query += this.genQuery(`, PRIMARY KEY(??)`, [numOfPksInNew]); + } else { + query += this.genQuery( + `alter TABLE ?? add constraint ?? PRIMARY KEY(??);`, + [t, `${t}_pkey`, numOfPksInNew] + ); + } + } + } + + return query; + } + + alterTableRemoveColumn(t, n, _o, existingQuery) { + const shouldSanitize = true; + let query = existingQuery ? ',' : ''; + query += this.genQuery( + `ALTER TABLE ?? DROP COLUMN ??`, + [t, n.cn], + shouldSanitize + ); + return query; + } + + createTableColumn(t, n, o, existingQuery) { + return this.alterTableColumn(t, n, o, existingQuery, 0); + } + + alterTableAddColumn(t, n, o, existingQuery) { + return this.alterTableColumn(t, n, o, existingQuery, 1); + } + + alterTableChangeColumn(t, n, o, existingQuery) { + return this.alterTableColumn(t, n, o, existingQuery, 2); + } + + createTable(table, args) { + let query = ''; + + for (let i = 0; i < args.columns.length; ++i) { + query += this.createTableColumn(table, args.columns[i], null, query); + } + + query += this.alterTablePK(table, args.columns, [], query, true); + + query = this.genQuery(`CREATE TABLE ?? (${query});`, [args.tn]); + + return query; + } + + alterTableColumn(t, n, o, existingQuery, change = 2) { + let query = ''; + + const defaultValue = getDefaultValue(n); + const shouldSanitize = true; + + if (change === 0) { + query = existingQuery ? ',' : ''; + if (n.ai) { + if (n.dt === 'int8' || n.dt.indexOf('bigint') > -1) { + query += this.genQuery(` ?? bigserial`, [n.cn], shouldSanitize); + } else if (n.dt === 'int2' || n.dt.indexOf('smallint') > -1) { + query += this.genQuery(` ?? smallserial`, [n.cn], shouldSanitize); + } else { + query += this.genQuery(` ?? serial`, [n.cn], shouldSanitize); + } + } else { + query += this.genQuery(` ?? ${n.dt}`, [n.cn], shouldSanitize); + query += n.rqd ? ' NOT NULL' : ' NULL'; + query += defaultValue ? ` DEFAULT ${defaultValue}` : ''; + } + } else if (change === 1) { + query += this.genQuery(` ADD ?? ${n.dt}`, [n.cn], shouldSanitize); + query += n.rqd ? ' NOT NULL' : ' NULL'; + query += defaultValue ? ` DEFAULT ${defaultValue}` : ''; + query = this.genQuery(`ALTER TABLE ?? ${query};`, [t], shouldSanitize); + } else { + if (n.cn !== o.cn) { + query += this.genQuery( + `\nALTER TABLE ?? RENAME COLUMN ?? TO ?? ;\n`, + [t, o.cn, n.cn], + shouldSanitize + ); + } + + if (n.dt !== o.dt) { + query += this.genQuery( + `\nALTER TABLE ?? ALTER COLUMN ?? TYPE ${n.dt} USING ??::${n.dt};\n`, + [t, n.cn, n.cn], + shouldSanitize + ); + } + + if (n.rqd !== o.rqd) { + query += this.genQuery( + `\nALTER TABLE ?? ALTER COLUMN ?? `, + [t, n.cn], + shouldSanitize + ); + query += n.rqd ? ` SET NOT NULL;\n` : ` DROP NOT NULL;\n`; + } + + if (n.cdf !== o.cdf) { + query += this.genQuery( + `\nALTER TABLE ?? ALTER COLUMN ?? `, + [t, n.cn], + shouldSanitize + ); + query += n.cdf ? ` SET DEFAULT ${n.cdf};\n` : ` DROP DEFAULT;\n`; + } + } + return query; + } + + get schema() { + return ( + (this.connectionConfig && + this.connectionConfig.connection.schema) + ); + } + + /** + * + * @param {Object} args + * @returns {Object} result + * @returns {Number} code + * @returns {String} message + */ + async totalRecords(args: any = {}) { + const func = this.totalRecords.name; + const result = new Result(); + log.api(`${func}:args:`, args); + + try { + const data = await this.sqlClient.raw( + `SELECT SUM(record_count) as "TotalRecords" FROM + (SELECT t.table_schema || '.' || t.table_name as "table_name",t.row_count as record_count + FROM information_schema.tables t + WHERE t.table_type = 'BASE TABLE and table_schema = ?' + )`, + [this.schema] + ); + result.data = data.rows[0]; + } catch (e) { + result.code = -1; + result.message = e.message; + result.object = e; + } finally { + log.api(`${func} :result: ${result}`); + } + return result; + } +} + +function getDefaultValue(n) { + if (n.cdf === undefined || n.cdf === null) return n.cdf; + switch (n.dt) { + case 'serial': + case 'bigserial': + case 'smallserial': + return ''; + break; + case 'boolean': + case 'bool': + case 'tinyint': + case 'int': + case 'samllint': + case 'bigint': + case 'integer': + case 'mediumint': + case 'int2': + case 'int4': + case 'int8': + case 'long': + case 'number': + case 'float': + case 'double': + case 'decimal': + case 'numeric': + case 'real': + case 'double precision': + case 'money': + case 'smallmoney': + case 'dec': + return n.cdf; + break; + + case 'datetime': + case 'timestamp': + case 'date': + case 'time': + if ( + n.cdf.indexOf('CURRENT_TIMESTAMP') > -1 || + /\(([\d\w'", ]*)\)$/.test(n.cdf) + ) { + return n.cdf; + } + // return JSON.stringify(n.cdf); + break; + default: + // return JSON.stringify(n.cdf); + break; + } + return n.cdf; +} + +export default SnowflakeClient; diff --git a/packages/nocodb/src/lib/db/sql-client/lib/snowflake/snowflake.queries.ts b/packages/nocodb/src/lib/db/sql-client/lib/snowflake/snowflake.queries.ts new file mode 100644 index 0000000000..0cf8d6a46e --- /dev/null +++ b/packages/nocodb/src/lib/db/sql-client/lib/snowflake/snowflake.queries.ts @@ -0,0 +1,5 @@ +// Snowflake queries + +const sfQueries = {}; + +export default sfQueries; diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts index 25d3461b84..12265b76c0 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts @@ -320,7 +320,7 @@ class BaseModelSqlv2 { as: 'count', }).first(); const res = (await this.dbDriver.raw(unsanitize(qb.toQuery()))) as any; - return (this.isPg ? res.rows[0] : res[0][0] ?? res[0]).count; + return ((this.isPg || this.isSnowflake) ? res.rows[0] : res[0][0] ?? res[0]).count; } // todo: add support for sortArrJson and filterArrJson @@ -1696,6 +1696,10 @@ class BaseModelSqlv2 { return this.clientType === 'mysql2' || this.clientType === 'mysql'; } + get isSnowflake() { + return this.clientType === 'snowflake'; + } + get clientType() { return this.dbDriver.clientType(); } @@ -2764,13 +2768,13 @@ class BaseModelSqlv2 { childTable?: Model ) { let query = qb.toQuery(); - if (!this.isPg && !this.isMssql) { + if (!this.isPg && !this.isMssql && !this.isSnowflake) { query = unsanitize(qb.toQuery()); } else { query = sanitize(query); } return this.convertAttachmentType( - this.isPg + this.isPg || this.isSnowflake ? (await this.dbDriver.raw(query))?.rows : query.slice(0, 6) === 'select' && !this.isMssql ? await this.dbDriver.from( diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts index b114d11526..426e0860c6 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/CustomKnex.ts @@ -1,4 +1,5 @@ import { Knex, knex } from 'knex'; +import { SnowflakeClient } from 'knex-snowflake'; const types = require('pg').types; // override parsing date column to Date() @@ -993,6 +994,8 @@ function CustomKnex(arg: string | Knex.Config | any): CustomKnex { arg.useNullAsDefault = true; } + if (arg?.client === 'snowflake') arg.client = SnowflakeClient; + const kn: any = knex(arg); const knexRaw = kn.raw; @@ -1019,7 +1022,7 @@ function CustomKnex(arg: string | Knex.Config | any): CustomKnex { value: () => { return typeof arg === 'string' ? arg.match(/^(\w+):/) ?? [1] - : arg.client; + : (arg.client?.name === 'SnowflakeClient') ? 'snowflake' : arg.client; }, }, searchPath: { diff --git a/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaFactory.ts b/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaFactory.ts index 62c4cfd28c..996a74beed 100644 --- a/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaFactory.ts +++ b/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaFactory.ts @@ -4,6 +4,7 @@ import ModelXcMetaMysql from './ModelXcMetaMysql'; import ModelXcMetaOracle from './ModelXcMetaOracle'; import ModelXcMetaPg from './ModelXcMetaPg'; import ModelXcMetaSqlite from './ModelXcMetaSqlite'; +import ModelXcMetaSnowflake from './ModelXcMetaSnowflake'; class ModelXcMetaFactory { public static create(connectionConfig, args): BaseModelXcMeta { @@ -20,6 +21,8 @@ class ModelXcMetaFactory { return new ModelXcMetaPg(args); } else if (connectionConfig.client === 'oracledb') { return new ModelXcMetaOracle(args); + } else if (connectionConfig.client === 'snowflake') { + return new ModelXcMetaSnowflake(args); } throw new Error('Database not supported'); diff --git a/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaSnowflake.ts b/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaSnowflake.ts new file mode 100644 index 0000000000..07ef5edd72 --- /dev/null +++ b/packages/nocodb/src/lib/db/sql-mgr/code/models/xc/ModelXcMetaSnowflake.ts @@ -0,0 +1,975 @@ +import BaseModelXcMeta from './BaseModelXcMeta'; + +class ModelXcMetaSnowflake extends BaseModelXcMeta { + /** + * @param dir + * @param filename + * @param ctx + * @param ctx.tn + * @param ctx.columns + * @param ctx.relations + */ + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); + } + + /** + * Prepare variables used in code template + */ + prepare() { + const data: any = {}; + + /* run of simple variable */ + data.tn = this.ctx.tn; + data.dbType = this.ctx.dbType; + + /* for complex code provide a func and args - do derivation within the func cbk */ + data.columns = { + func: this._renderXcColumns.bind(this), + args: { + tn: this.ctx.tn, + columns: this.ctx.columns, + relations: this.ctx.relations, + }, + }; + + /* for complex code provide a func and args - do derivation within the func cbk */ + data.hasMany = { + func: this._renderXcHasMany.bind(this), + args: { + tn: this.ctx.tn, + columns: this.ctx.columns, + hasMany: this.ctx.hasMany, + }, + }; + + /* for complex code provide a func and args - do derivation within the func cbk */ + data.belongsTo = { + func: this._renderXcBelongsTo.bind(this), + args: { + tn: this.ctx.tn, + columns: this.ctx.columns, + belongsTo: this.ctx.belongsTo, + }, + }; + + return data; + } + + _renderXcHasMany(args) { + return JSON.stringify(args.hasMany); + } + + _renderXcBelongsTo(args) { + return JSON.stringify(args.belongsTo); + } + + /** + * + * @param args + * @param args.columns + * @param args.relations + * @returns {string} + * @private + */ + _renderXcColumns(args) { + let str = '[\r\n'; + + for (let i = 0; i < args.columns.length; ++i) { + str += `{\r\n`; + str += `cn: '${args.columns[i].cn}',\r\n`; + str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; + str += `dt: '${args.columns[i].dt}',\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; + + if (args.columns[i].cdf) { + str += `default: "${args.columns[i].cdf}",\r\n`; + str += `columnDefault: "${args.columns[i].cdf}",\r\n`; + } + + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; + + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; + + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; + + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; + + str += `validate: { + func: [], + args: [], + msg: [] + },`; + str += `},\r\n`; + } + + str += ']\r\n'; + + return str; + } + + _getAbstractType(column) { + let str = ''; + switch (column.dt) { + case 'int': + str = 'integer'; + break; + case 'integer': + str = 'integer'; + break; + case 'bigint': + str = 'bigInteger'; + break; + case 'bigserial': + str = 'bigserial'; + break; + case 'char': + str = 'string'; + break; + case 'int2': + str = 'integer'; + break; + case 'int4': + str = 'integer'; + break; + case 'int8': + str = 'integer'; + break; + case 'int4range': + str = 'int4range'; + break; + case 'int8range': + str = 'int8range'; + break; + case 'serial': + str = 'serial'; + break; + case 'serial2': + str = 'serial2'; + break; + case 'serial8': + str = 'serial8'; + break; + case 'character': + str = 'string'; + break; + case 'bit': + str = 'bit'; + break; + case 'bool': + str = 'boolean'; + break; + case 'boolean': + str = 'boolean'; + break; + case 'date': + str = 'date'; + break; + case 'double precision': + str = 'double'; + break; + case 'event_trigger': + str = 'event_trigger'; + break; + case 'fdw_handler': + str = 'fdw_handler'; + break; + case 'float4': + str = 'float'; + break; + case 'float8': + str = 'float'; + break; + case 'uuid': + str = 'uuid'; + break; + case 'smallint': + str = 'integer'; + break; + case 'smallserial': + str = 'smallserial'; + break; + case 'character varying': + str = 'string'; + break; + case 'text': + str = 'text'; + break; + case 'real': + str = 'float'; + break; + case 'time': + str = 'time'; + break; + case 'time without time zone': + str = 'time'; + break; + case 'timestamp': + str = 'timestamp'; + break; + case 'timestamp without time zone': + str = 'timestamp'; + break; + case 'timestamptz': + str = 'timestampt'; + break; + case 'timestamp with time zone': + str = 'timestamp'; + break; + case 'timetz': + str = 'time'; + break; + case 'time with time zone': + str = 'time'; + break; + case 'daterange': + str = 'daterange'; + break; + case 'json': + str = 'json'; + break; + case 'jsonb': + str = 'jsonb'; + break; + case 'gtsvector': + str = 'gtsvector'; + break; + case 'index_am_handler': + str = 'index_am_handler'; + break; + case 'anyenum': + str = 'enum'; + break; + case 'anynonarray': + str = 'anynonarray'; + break; + case 'anyrange': + str = 'anyrange'; + break; + case 'box': + str = 'box'; + break; + case 'bpchar': + str = 'bpchar'; + break; + case 'bytea': + str = 'bytea'; + break; + case 'cid': + str = 'cid'; + break; + case 'cidr': + str = 'cidr'; + break; + case 'circle': + str = 'circle'; + break; + case 'cstring': + str = 'cstring'; + break; + case 'inet': + str = 'inet'; + break; + case 'internal': + str = 'internal'; + break; + case 'interval': + str = 'interval'; + break; + case 'language_handler': + str = 'language_handler'; + break; + case 'line': + str = 'line'; + break; + case 'lsec': + str = 'lsec'; + break; + case 'macaddr': + str = 'macaddr'; + break; + case 'money': + str = 'float'; + break; + case 'name': + str = 'name'; + break; + case 'numeric': + str = 'numeric'; + break; + case 'numrange': + str = 'numrange'; + break; + case 'oid': + str = 'oid'; + break; + case 'opaque': + str = 'opaque'; + break; + case 'path': + str = 'path'; + break; + case 'pg_ddl_command': + str = 'pg_ddl_command'; + break; + case 'pg_lsn': + str = 'pg_lsn'; + break; + case 'pg_node_tree': + str = 'pg_node_tree'; + break; + case 'point': + str = 'point'; + break; + case 'polygon': + str = 'polygon'; + break; + case 'record': + str = 'record'; + break; + case 'refcursor': + str = 'refcursor'; + break; + case 'regclass': + str = 'regclass'; + break; + case 'regconfig': + str = 'regconfig'; + break; + case 'regdictionary': + str = 'regdictionary'; + break; + case 'regnamespace': + str = 'regnamespace'; + break; + case 'regoper': + str = 'regoper'; + break; + case 'regoperator': + str = 'regoperator'; + break; + case 'regproc': + str = 'regproc'; + break; + case 'regpreocedure': + str = 'regpreocedure'; + break; + case 'regrole': + str = 'regrole'; + break; + case 'regtype': + str = 'regtype'; + break; + case 'reltime': + str = 'reltime'; + break; + case 'smgr': + str = 'smgr'; + break; + case 'tid': + str = 'tid'; + break; + case 'tinterval': + str = 'tinterval'; + break; + case 'trigger': + str = 'trigger'; + break; + case 'tsm_handler': + str = 'tsm_handler'; + break; + case 'tsquery': + str = 'tsquery'; + break; + case 'tsrange': + str = 'tsrange'; + break; + case 'tstzrange': + str = 'tstzrange'; + break; + case 'tsvector': + str = 'tsvector'; + break; + case 'txid_snapshot': + str = 'txid_snapshot'; + break; + case 'unknown': + str = 'unknown'; + break; + case 'void': + str = 'void'; + break; + case 'xid': + str = 'xid'; + break; + case 'xml': + str = 'xml'; + break; + default: + str += `"${column.dt}"`; + break; + } + return str; + } + + getUIDataType(col): any { + switch (this.getAbstractType(col)) { + case 'integer': + return 'Number'; + case 'boolean': + return 'Checkbox'; + case 'float': + return 'Decimal'; + case 'date': + return 'Date'; + case 'datetime': + return 'DateTime'; + case 'time': + return 'Time'; + case 'year': + return 'Year'; + case 'string': + return 'SingleLineText'; + case 'text': + return 'LongText'; + case 'enum': + return 'SingleSelect'; + case 'set': + return 'MultiSelect'; + case 'json': + return 'JSON'; + case 'blob': + return 'LongText'; + case 'geometry': + return 'Geometry'; + default: + return 'SpecificDBType'; + } + } + + getAbstractType(col): any { + const dt = col.dt.toLowerCase(); + switch (dt) { + case 'anyenum': + return 'enum'; + case 'anynonarray': + case 'anyrange': + return dt; + + case 'bit': + return 'integer'; + case 'bigint': + case 'bigserial': + return 'integer'; + + case 'bool': + return 'boolean'; + + case 'bpchar': + case 'bytea': + return dt; + case 'char': + case 'character': + case 'character varying': + return 'string'; + + case 'cid': + case 'cidr': + case 'cstring': + return dt; + + case 'date': + return 'date'; + case 'daterange': + return 'string'; + case 'double precision': + return 'string'; + + case 'event_trigger': + case 'fdw_handler': + return dt; + + case 'float4': + case 'float8': + return 'float'; + + case 'gtsvector': + case 'index_am_handler': + case 'inet': + return dt; + + case 'int': + case 'int2': + case 'int4': + case 'int8': + case 'integer': + return 'integer'; + case 'int4range': + case 'int8range': + case 'internal': + case 'interval': + return 'string'; + case 'json': + case 'jsonb': + return 'json'; + + case 'language_handler': + case 'lsec': + case 'macaddr': + case 'money': + case 'name': + case 'numeric': + case 'numrange': + case 'oid': + case 'opaque': + case 'path': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': + return dt; + case 'real': + return 'float'; + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': + return dt; + case 'serial': + case 'serial2': + case 'serial8': + case 'smallint': + case 'smallserial': + return 'integer'; + case 'smgr': + return dt; + case 'text': + return 'text'; + case 'tid': + return dt; + case 'time': + case 'time without time zone': + return 'time'; + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + return 'datetime'; + case 'timetz': + case 'time with time zone': + return 'time'; + + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'xml': + return dt; + + case 'tinyint': + case 'mediumint': + return 'integer'; + + case 'float': + case 'decimal': + case 'double': + return 'float'; + case 'boolean': + return 'boolean'; + case 'datetime': + return 'datetime'; + + case 'uuid': + case 'year': + case 'varchar': + case 'nchar': + return 'string'; + + case 'tinytext': + case 'mediumtext': + case 'longtext': + return 'text'; + + case 'binary': + case 'varbinary': + return 'text'; + + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + return 'blob'; + case 'enum': + return 'enum'; + case 'set': + return 'set'; + + case 'line': + case 'point': + case 'polygon': + case 'circle': + case 'box': + case 'geometry': + case 'linestring': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': + return 'geometry'; + } + } + + _sequelizeGetType(column) { + let str = ''; + switch (column.dt) { + case 'int': + str += `DataTypes.INTEGER(${column.dtxp})`; + if (column.un) str += `.UNSIGNED`; + break; + case 'tinyint': + str += `DataTypes.INTEGER(${column.dtxp})`; + if (column.un) str += `.UNSIGNED`; + + break; + case 'smallint': + str += `DataTypes.INTEGER(${column.dtxp})`; + if (column.un) str += `.UNSIGNED`; + + break; + case 'mediumint': + str += `DataTypes.INTEGER(${column.dtxp})`; + if (column.un) str += `.UNSIGNED`; + + break; + case 'bigint': + str += `DataTypes.BIGINT`; + if (column.un) str += `.UNSIGNED`; + + break; + case 'float': + str += `DataTypes.FLOAT`; + break; + case 'decimal': + str += `DataTypes.DECIMAL`; + break; + case 'double': + str += `"DOUBLE(${column.dtxp},${column.ns})"`; + break; + case 'real': + str += `DataTypes.FLOAT`; + break; + case 'bit': + str += `DataTypes.BOOLEAN`; + break; + case 'boolean': + str += `DataTypes.STRING(45)`; + break; + case 'serial': + str += `DataTypes.BIGINT`; + break; + case 'date': + str += `DataTypes.DATEONLY`; + break; + case 'datetime': + str += `DataTypes.DATE`; + break; + case 'timestamp': + str += `DataTypes.DATE`; + break; + case 'time': + str += `DataTypes.TIME`; + break; + case 'year': + str += `"YEAR"`; + break; + case 'char': + str += `DataTypes.CHAR(${column.dtxp})`; + break; + case 'varchar': + str += `DataTypes.STRING(${column.dtxp})`; + break; + case 'nchar': + str += `DataTypes.CHAR(${column.dtxp})`; + break; + case 'text': + str += `DataTypes.TEXT`; + break; + case 'tinytext': + str += `DataTypes.TEXT`; + break; + case 'mediumtext': + str += `DataTypes.TEXT`; + break; + case 'longtext': + str += `DataTypes.TEXT`; + break; + case 'binary': + str += `"BINARY(${column.dtxp})"`; + break; + case 'varbinary': + str += `"VARBINARY(${column.dtxp})"`; + break; + case 'blob': + str += `"BLOB"`; + break; + case 'tinyblob': + str += `"TINYBLOB"`; + break; + case 'mediumblob': + str += `"MEDIUMBLOB"`; + break; + case 'longblob': + str += `"LONGBLOB"`; + break; + case 'enum': + str += `DataTypes.ENUM(${column.dtxp})`; + break; + case 'set': + str += `"SET(${column.dtxp})"`; + break; + case 'geometry': + str += `DataTypes.GEOMETRY`; + break; + case 'point': + str += `"POINT"`; + break; + case 'linestring': + str += `"LINESTRING"`; + break; + case 'polygon': + str += `"POLYGON"`; + break; + case 'multipoint': + str += `"MULTIPOINT"`; + break; + case 'multilinestring': + str += `"MULTILINESTRING"`; + break; + case 'multipolygon': + str += `"MULTIPOLYGON"`; + break; + case 'json': + str += `DataTypes.JSON`; + break; + default: + str += `"${column.dt}"`; + break; + } + return str; + } + + _sequelizeGetDefault(column) { + let str = ''; + switch (column.dt) { + case 'int': + str += `'${column.cdf}'`; + break; + case 'tinyint': + str += `'${column.cdf}'`; + break; + case 'smallint': + str += `'${column.cdf}'`; + break; + case 'mediumint': + str += `'${column.cdf}'`; + break; + case 'bigint': + str += `'${column.cdf}'`; + break; + case 'float': + str += `'${column.cdf}'`; + break; + case 'decimal': + str += `'${column.cdf}'`; + break; + case 'double': + str += `'${column.cdf}'`; + break; + case 'real': + str += `'${column.cdf}'`; + break; + case 'bit': + str += column.cdf ? column.cdf.split('b')[1] : column.cdf; + break; + case 'boolean': + str += column.cdf; + break; + case 'serial': + str += column.cdf; + break; + case 'date': + str += `sequelize.literal('${column.cdf_sequelize}')`; + break; + case 'datetime': + str += `sequelize.literal('${column.cdf_sequelize}')`; + break; + case 'timestamp': + str += `sequelize.literal('${column.cdf_sequelize}')`; + break; + case 'time': + str += `'${column.cdf}'`; + break; + case 'year': + str += `'${column.cdf}'`; + break; + case 'char': + str += `'${column.cdf}'`; + break; + case 'varchar': + str += `'${column.cdf}'`; + break; + case 'nchar': + str += `'${column.cdf}'`; + break; + case 'text': + str += column.cdf; + break; + case 'tinytext': + str += column.cdf; + break; + case 'mediumtext': + str += column.cdf; + break; + case 'longtext': + str += column.cdf; + break; + case 'binary': + str += column.cdf; + break; + case 'varbinary': + str += column.cdf; + break; + case 'blob': + str += column.cdf; + break; + case 'tinyblob': + str += column.cdf; + break; + case 'mediumblob': + str += column.cdf; + break; + case 'longblob': + str += column.cdf; + break; + case 'enum': + str += `'${column.cdf}'`; + break; + case 'set': + str += `'${column.cdf}'`; + break; + case 'geometry': + str += `'${column.cdf}'`; + break; + case 'point': + str += `'${column.cdf}'`; + break; + case 'linestring': + str += `'${column.cdf}'`; + break; + case 'polygon': + str += `'${column.cdf}'`; + break; + case 'multipoint': + str += `'${column.cdf}'`; + break; + case 'multilinestring': + str += `'${column.cdf}'`; + break; + case 'multipolygon': + str += `'${column.cdf}'`; + break; + case 'json': + str += column.cdf; + break; + } + return str; + } + + /* getXcColumnsObject(args) { + const columnsArr = []; + + for (const column of args.columns) { + const columnObj = { + validate: { + func: [], + args: [], + msg: [] + }, + cn: column.cn, + _cn: column._cn || column.cn, + type: this._getAbstractType(column), + dt: column.dt, + uidt: column.uidt || this._getUIDataType(column), + uip: column.uip, + uicn: column.uicn, + ...column + }; + + if (column.rqd) { + columnObj.rqd = column.rqd; + } + + if (column.cdf) { + columnObj.default = column.cdf; + columnObj.columnDefault = column.cdf; + } + + if (column.un) { + columnObj.un = column.un; + } + + if (column.pk) { + columnObj.pk = column.pk; + } + + if (column.ai) { + columnObj.ai = column.ai; + } + + if (column.dtxp) { + columnObj.dtxp = column.dtxp; + } + + if (column.dtxs) { + columnObj.dtxs = column.dtxs; + } + + columnsArr.push(columnObj); + } + + this.mapDefaultPrimaryValue(columnsArr); + return columnsArr; + }*/ + + /* getObject() { + return { + tn: this.ctx.tn, + _tn: this.ctx._tn, + columns: this.getXcColumnsObject(this.ctx), + pks: [], + hasMany: this.ctx.hasMany, + belongsTo: this.ctx.belongsTo, + dbType: this.ctx.dbType, + type: this.ctx.type, + } + + }*/ +} + +export default ModelXcMetaSnowflake;