mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1273 lines
33 KiB
1273 lines
33 KiB
"use strict"; |
|
|
|
const mysql = require("mysql"); |
|
const dataHelp = require("./util/data.helper.js"); |
|
const whereHelp = require("./util/whereClause.helper.js"); |
|
const assert = require("assert"); |
|
|
|
//define class§ |
|
class Xsql { |
|
constructor(sqlConfig, pool) { |
|
//define this variables |
|
this.sqlConfig = {}; |
|
this.pool = {}; |
|
this.metaDb = {}; |
|
this.metaDb.tables = {}; |
|
this.metaDb.routines = {} |
|
|
|
this.sqlConfig = sqlConfig; |
|
this.pool = pool; |
|
} |
|
|
|
/**************** START : Cache functions ****************/ |
|
init(cbk) { |
|
this.dbCacheInitAsync((err, results) => { |
|
cbk(err, results); |
|
}); |
|
} |
|
|
|
dbCacheInitAsync(cbk) { |
|
let self = this; |
|
|
|
self.pool.query( |
|
dataHelp.getSchemaQuery(), |
|
[this.sqlConfig.database], |
|
(err, results) => { |
|
if (err) { |
|
console.log("Cache init failed during database reading"); |
|
console.log(err, results); |
|
cbk(err, results); |
|
} else { |
|
for (var i = 0; i < results.length; ++i) { |
|
let keys = Object.keys(results[i]); |
|
|
|
for (var j = 0; j < keys.length; ++j) { |
|
let value = results[i][keys[j]]; |
|
|
|
results[i][keys[j].toLowerCase()] = value; |
|
|
|
//console.log(value); |
|
} |
|
} |
|
|
|
self.iterateToCacheTables(results) |
|
self.iterateToCacheTablePks(results) |
|
self.iterateToCacheTableColumns(results) |
|
self.iterateToCacheTableFks(results) |
|
self.iterateToCacheRoutines(results) |
|
|
|
// osx mysql server has limitations related to open_tables |
|
self.pool.query('FLUSH TABLES', [], (err, results) => { |
|
self.pool.query(dataHelp.getRoutines(), [this.sqlConfig.database], (err, results) => { |
|
if (err) { |
|
cbk(err, results) |
|
} else { |
|
self.iterateToCacheRoutines(results) |
|
cbk(null, null) |
|
} |
|
}) |
|
}) |
|
} |
|
); |
|
} |
|
|
|
iterateToCacheTables(schemaResults) { |
|
for (let i = 0; i < schemaResults.length; ++i) { |
|
let schemaRow = schemaResults[i]; |
|
|
|
let tableName = schemaRow["table_name"]; |
|
|
|
if (!(tableName in this.metaDb.tables)) { |
|
this.metaDb.tables[tableName] = {}; |
|
this.metaDb.tables[tableName]["primaryKeys"] = []; |
|
this.metaDb.tables[tableName]["foreignKeys"] = []; |
|
this.metaDb.tables[tableName]["columns"] = []; |
|
this.metaDb.tables[tableName]["indicies"] = []; |
|
this.metaDb.tables[tableName]["isView"] = schemaRow["isView"]; |
|
} |
|
} |
|
} |
|
|
|
iterateToCacheRoutines(routineResults) { |
|
for (let i = 0; i < routineResults.length; i++) { |
|
const routine = routineResults[i] |
|
const routineName = routine['routine_name'] |
|
this.metaDb.routines[routineName] = {} |
|
} |
|
} |
|
|
|
iterateToCacheTableColumns(schemaResults) { |
|
for (let i = 0; i < schemaResults.length; ++i) { |
|
let schemaRow = schemaResults[i]; |
|
let tableName = schemaRow["table_name"]; |
|
let col = {}; |
|
col["column_name"] = schemaRow["column_name"]; |
|
col["ordinal_position"] = schemaRow["ordinal_position"]; |
|
col["column_key"] = schemaRow["column_key"]; |
|
col["data_type"] = schemaRow["data_type"]; |
|
col["column_type"] = schemaRow["column_type"]; |
|
|
|
dataHelp.findOrInsertObjectArrayByKey( |
|
col, |
|
"column_name", |
|
this.metaDb.tables[tableName]["columns"] |
|
); |
|
} |
|
} |
|
|
|
iterateToCacheTablePks(schemaResults) { |
|
for (let i = 0; i < schemaResults.length; ++i) { |
|
let schemaRow = schemaResults[i]; |
|
let tableName = schemaRow["table_name"]; |
|
|
|
if (schemaRow["column_key"] === "PRI") { |
|
let pk = {}; |
|
pk["column_name"] = schemaRow["column_name"]; |
|
pk["ordinal_position"] = schemaRow["ordinal_position"]; |
|
pk["column_key"] = schemaRow["column_key"]; |
|
pk["data_type"] = schemaRow["data_type"]; |
|
pk["column_type"] = schemaRow["column_type"]; |
|
|
|
dataHelp.findOrInsertObjectArrayByKey( |
|
pk, |
|
"column_name", |
|
this.metaDb.tables[tableName]["primaryKeys"] |
|
); |
|
} |
|
} |
|
} |
|
|
|
iterateToCacheTableFks(schemaResults) { |
|
for (let i = 0; i < schemaResults.length; ++i) { |
|
let schemaRow = schemaResults[i]; |
|
let tableName = schemaRow["table_name"]; |
|
|
|
if (schemaRow["referenced_table_name"]) { |
|
let fk = {}; |
|
|
|
fk["column_name"] = schemaRow["column_name"]; |
|
fk["table_name"] = schemaRow["table_name"]; |
|
fk["referenced_table_name"] = schemaRow["referenced_table_name"]; |
|
fk["referenced_column_name"] = schemaRow["referenced_column_name"]; |
|
fk["data_type"] = schemaRow["data_type"]; |
|
fk["column_type"] = schemaRow["column_type"]; |
|
|
|
dataHelp.findOrInsertObjectArrayByKey( |
|
fk, |
|
"column_name", |
|
this.metaDb.tables[tableName]["foreignKeys"] |
|
); |
|
|
|
//console.log(fk['referenced_table_name'],fk['referenced_column_name'],tableName, schemaRow['column_name'], this.metaDb.tables[tableName]['foreignKeys'].length) |
|
} |
|
} |
|
} |
|
|
|
/**************** END : Cache functions ****************/ |
|
|
|
exec(query, params) { |
|
let _this = this; |
|
return new Promise(function(resolve, reject) { |
|
//console.log('mysql>', query, params); |
|
_this.pool.query(query, params, function(error, rows, _fields) { |
|
if (error) { |
|
console.log("mysql> ", error); |
|
return reject(error); |
|
} |
|
return resolve(rows); |
|
}); |
|
}); |
|
} |
|
|
|
typeOfColumn(Type) { |
|
//TODO: Im sure there are more types to handle here |
|
const strTypes = [ |
|
"varchar", |
|
"text", |
|
"char", |
|
"tinytext", |
|
"mediumtext", |
|
"longtext", |
|
"blob", |
|
"mediumblob", |
|
"longblob" |
|
]; |
|
const intTypes = [ |
|
"int", |
|
"long", |
|
"smallint", |
|
"mediumint", |
|
"bigint", |
|
"tinyint" |
|
]; |
|
const flatTypes = ["float", "double", "decimal"]; |
|
const dateTypes = ["date", "datetime", "timestamp", "time", "year"]; |
|
|
|
if (dataHelp.getType(Type, strTypes)) { |
|
return "string"; |
|
} else if (dataHelp.getType(Type, intTypes)) { |
|
return "int"; |
|
} else if (dataHelp.getType(Type, flatTypes)) { |
|
return "float"; |
|
} else if (dataHelp.getType(Type, dateTypes)) { |
|
return "date"; |
|
} else { |
|
return "unknown"; |
|
} |
|
} |
|
|
|
isTypeOfColumnNumber(Type) { |
|
//console.log(Type, this.typeOfColumn(Type)); |
|
return ( |
|
"int" === this.typeOfColumn(Type) || "float" === this.typeOfColumn(Type) |
|
); |
|
} |
|
|
|
getLimitClause(reqParams) { |
|
//defaults |
|
reqParams._index = 0; |
|
reqParams._len = 20; |
|
|
|
if ("_size" in reqParams) { |
|
if (parseInt(reqParams._size) > 0 && parseInt(reqParams._size) <= 100) { |
|
reqParams._len = parseInt(reqParams._size); |
|
} else if (parseInt(reqParams._size) > 100) { |
|
reqParams._len = 100; |
|
} |
|
} |
|
|
|
if ("_p" in reqParams && parseInt(reqParams._p) > 0) { |
|
reqParams._index = parseInt(reqParams._p) * reqParams._len; |
|
} |
|
|
|
//console.log(reqParams._index, reqParams._len); |
|
|
|
return [reqParams._index, reqParams._len]; |
|
} |
|
|
|
prepareBulkInsert(tableName, objectArray, queryParamsObj) { |
|
if (tableName in this.metaDb.tables && objectArray) { |
|
let insObj = objectArray[0]; |
|
|
|
// goal => insert into ?? (?,?..?) values ? [tablName, col1,col2...coln,[[ObjValues_1],[ObjValues_2],...[ObjValues_N]] |
|
queryParamsObj.query = " INSERT INTO ?? ( "; |
|
queryParamsObj.params.push(tableName); |
|
|
|
let cols = []; |
|
let colPresent = false; |
|
|
|
/**************** START : prepare column names to be inserted ****************/ |
|
// iterate over all column in table and have only ones existing in objects to be inserted |
|
for ( |
|
var i = 0; |
|
i < this.metaDb.tables[tableName]["columns"].length; |
|
++i |
|
) { |
|
let colName = this.metaDb.tables[tableName]["columns"][i][ |
|
"column_name" |
|
]; |
|
|
|
if (colName in insObj) { |
|
if (colPresent) { |
|
queryParamsObj.query += ","; |
|
} |
|
|
|
queryParamsObj.query += colName; |
|
|
|
colPresent = true; |
|
} |
|
|
|
cols.push(colName); |
|
|
|
//console.log('> > ', queryParamsObj.query); |
|
} |
|
|
|
queryParamsObj.query += " ) values ?"; |
|
/**************** END : prepare column names to be inserted ****************/ |
|
|
|
/**************** START : prepare value object in prepared statement ****************/ |
|
// iterate over sent object array |
|
let arrOfArr = []; |
|
for (var i = 0; i < objectArray.length; ++i) { |
|
let arrValues = []; |
|
for (var j = 0; j < cols.length; ++j) { |
|
if (cols[j] in objectArray[i]) |
|
arrValues.push(objectArray[i][cols[j]]); |
|
} |
|
arrOfArr.push(arrValues); |
|
} |
|
queryParamsObj.params.push(arrOfArr); |
|
/**************** END : prepare value object in prepared statement ****************/ |
|
} |
|
} |
|
|
|
getGroupByClause(_groupby, tableName, queryParamsObj) { |
|
if (_groupby) { |
|
queryParamsObj.query += " group by " + _groupby + " "; |
|
return _groupby; |
|
} |
|
} |
|
|
|
getHavingClause(_having, tableName, queryParamsObj) { |
|
if (_having) { |
|
let whereClauseObj = whereHelp.getConditionClause(_having, "having"); |
|
|
|
if (whereClauseObj.err === 0) { |
|
queryParamsObj.query = |
|
queryParamsObj.query + " having " + whereClauseObj.query; |
|
queryParamsObj.params = queryParamsObj.params.concat( |
|
whereClauseObj.params |
|
); |
|
} |
|
|
|
//console.log('> > > after where clause filling up:', queryParamsObj.query, queryParamsObj.params); |
|
} |
|
} |
|
|
|
getWhereClause(queryparams, tableName, queryParamsObj, appendToWhere) { |
|
if (queryparams) { |
|
let whereClauseObj = whereHelp.getConditionClause(queryparams); |
|
|
|
if (whereClauseObj.err === 0) { |
|
queryParamsObj.query = |
|
queryParamsObj.query + appendToWhere + whereClauseObj.query; |
|
queryParamsObj.params = queryParamsObj.params.concat( |
|
whereClauseObj.params |
|
); |
|
} |
|
} |
|
} |
|
|
|
getOrderByClause(queryparams, tableName, queryParamsObj) { |
|
if (queryparams._sort) { |
|
queryParamsObj.query += " ORDER BY "; |
|
|
|
let orderByCols = queryparams._sort.split(","); |
|
|
|
for (let i = 0; i < orderByCols.length; ++i) { |
|
if (i) { |
|
queryParamsObj.query += ", "; |
|
} |
|
if (orderByCols[i][0] === "-") { |
|
let len = orderByCols[i].length; |
|
queryParamsObj.query += " ?? DESC"; |
|
queryParamsObj.params.push(orderByCols[i].substring(1, len)); |
|
} else { |
|
queryParamsObj.query += " ?? ASC"; |
|
queryParamsObj.params.push(orderByCols[i]); |
|
} |
|
} |
|
} |
|
} |
|
|
|
getColumnsForSelectStmtWithGrpBy(reqQueryParams, tableName, queryParamsObj) { |
|
let grpByCols = reqQueryParams._groupby.split(","); |
|
|
|
for (var i = 0; i < grpByCols.length; ++i) { |
|
if (i) { |
|
queryParamsObj.query += ","; |
|
} |
|
queryParamsObj.query += " ??"; |
|
queryParamsObj.params.push(grpByCols[i]); |
|
} |
|
|
|
queryParamsObj.query += ",count(1) as _count "; |
|
} |
|
|
|
getColumnsForSelectStmt(tableName, reqQueryParams, queryParamsObj) { |
|
let table = this.metaDb.tables[tableName]; |
|
let cols = []; |
|
let _fieldsInQuery = []; |
|
let removeFieldsObj = {}; |
|
|
|
// populate _fields array from query params |
|
if ("_fields" in reqQueryParams) { |
|
_fieldsInQuery = reqQueryParams["_fields"].split(","); |
|
} else { |
|
queryParamsObj.query += " * "; |
|
return " * "; |
|
} |
|
|
|
// get column name in _fields and mark column name which start with '-' |
|
for (let i = 0; i < _fieldsInQuery.length; ++i) { |
|
if (_fieldsInQuery[i][0] === "-") { |
|
removeFieldsObj[ |
|
_fieldsInQuery[i].substring(1, _fieldsInQuery[i].length) |
|
] = 1; |
|
} else { |
|
cols.push(_fieldsInQuery[i]); |
|
} |
|
} |
|
|
|
if (!cols.length) { |
|
// for each column in table - add only which are not in removeFieldsObj |
|
for (let i = 0; i < table["columns"].length; ++i) { |
|
if (!(table["columns"][i]["column_name"] in removeFieldsObj)) { |
|
cols.push(table["columns"][i]["column_name"]); |
|
} |
|
} |
|
} else { |
|
cols = this.removeUnknownColumns(cols, tableName); |
|
} |
|
|
|
for (var i = 0; i < cols.length; ++i) { |
|
if (i) { |
|
queryParamsObj.query += ","; |
|
} |
|
queryParamsObj.query += "??"; |
|
queryParamsObj.params.push(cols[i]); |
|
} |
|
|
|
return cols.join(","); |
|
} |
|
|
|
removeUnknownColumns(inputColumns, tableName) { |
|
let cols = inputColumns; |
|
let unknown_cols_in_input = []; |
|
let shadowCols = []; |
|
let tableColumns = this.metaDb.tables[tableName]["columns"]; |
|
|
|
// find unknown fields if any |
|
for (var j = 0; j < cols.length; ++j) { |
|
let found = 0; |
|
|
|
for (var i = 0; i < tableColumns.length; ++i) { |
|
if (tableColumns[i]["column_name"] === cols[j]) { |
|
found = 1; |
|
break; |
|
} |
|
} |
|
|
|
if (!found) { |
|
unknown_cols_in_input.push(j); |
|
} |
|
} |
|
|
|
// if there are unknown fields - remove and ignore 'em |
|
if (unknown_cols_in_input.length) { |
|
for (var i = 0; i < cols.length; ++i) { |
|
if (unknown_cols_in_input.indexOf(i) === -1) { |
|
shadowCols.push(cols[i]); |
|
} |
|
} |
|
|
|
cols = []; |
|
cols = shadowCols; |
|
} |
|
|
|
return cols; |
|
} |
|
|
|
getPrimaryKeyName(tableName) { |
|
let pk = null; |
|
if (tableName in this.metaDb.tables) { |
|
pk = this.metaDb.tables[tableName].primaryKeys[0]["column_name"]; |
|
} |
|
return pk; |
|
} |
|
|
|
getPrimaryKeyWhereClause(tableName, pksValues) { |
|
let whereClause = ""; |
|
let whereCol = ""; |
|
let whereValue = ""; |
|
let pks = []; |
|
|
|
if (tableName in this.metaDb.tables) { |
|
pks = this.metaDb.tables[tableName].primaryKeys; |
|
} else { |
|
return null; |
|
} |
|
|
|
// number of primary keys in table and one sent should be same |
|
if (pksValues.length !== pks.length) { |
|
return null; |
|
} |
|
|
|
// get a where clause out of the above columnNames and their values |
|
for (let i = 0; i < pks.length; ++i) { |
|
let type = dataHelp.getColumnType(pks[i]); |
|
|
|
whereCol = pks[i]["column_name"]; |
|
|
|
if (type === "string") { |
|
whereValue = mysql.escape(pksValues[i]); |
|
} else if (type === "int") { |
|
whereValue = parseInt(pksValues[i]); |
|
} else if (type === "float") { |
|
whereValue = parseFloat(pksValues[i]); |
|
} else if (type === "date") { |
|
whereValue = Date(pksValues[i]); |
|
} else { |
|
console.error(pks[i]); |
|
assert(false, "Unhandled type of primary key"); |
|
} |
|
|
|
if (i) { |
|
whereClause += " and "; |
|
} |
|
|
|
whereClause += whereCol + " = " + whereValue; |
|
} |
|
|
|
return whereClause; |
|
} |
|
|
|
getForeignKeyWhereClause(parentTable, parentId, childTable) { |
|
let whereValue = ""; |
|
|
|
//get all foreign keys of child table |
|
let fks = this.metaDb.tables[childTable].foreignKeys; |
|
let fk = dataHelp.findObjectInArrayByKey( |
|
"referenced_table_name", |
|
parentTable, |
|
fks |
|
); |
|
let whereCol = fk["column_name"]; |
|
let colType = dataHelp.getColumnType(fk); |
|
|
|
if (colType === "string") { |
|
whereValue = mysql.escape(parentId); |
|
} else if (colType === "int") { |
|
whereValue = mysql.escape(parseInt(parentId)); |
|
} else if (colType === "float") { |
|
whereValue = mysql.escape(parseFloat(parentId)); |
|
} else if (colType === "date") { |
|
whereValue = mysql.escape(Date(parentId)); |
|
} else { |
|
console.error(pks[i]); |
|
assert(false, "Unhandled column type in foreign key handling"); |
|
} |
|
|
|
return whereCol + " = " + whereValue; |
|
} |
|
|
|
prepareRoute(internal, httpType, apiPrefix, urlRoute, routeType) { |
|
let route = {}; |
|
route["httpType"] = httpType; |
|
route["routeUrl"] = apiPrefix + urlRoute; |
|
if (internal) { |
|
route["routeType"] = routeType; |
|
} |
|
return route; |
|
} |
|
|
|
getSchemaRoutes(internal, apiPrefix) { |
|
let schemaRoutes = []; |
|
|
|
for (var tableName in this.metaDb.tables) { |
|
if (tableName in this.sqlConfig.ignoreTables) { |
|
//console.log('ignore table', tableName); |
|
} else { |
|
let routes = []; |
|
let tableObj = {}; |
|
let table = this.metaDb.tables[tableName]; |
|
let isView = this.metaDb.tables[tableName]["isView"]; |
|
|
|
tableObj["resource"] = tableName; |
|
|
|
// order of routes is important for express routing - DO NOT CHANGE ORDER |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/describe", |
|
"describe" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/count", |
|
"count" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/groupby", |
|
"groupby" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/distinct", |
|
"distinct" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/ugroupby", |
|
"ugroupby" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/chart", |
|
"chart" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/aggregate", |
|
"aggregate" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/findOne", |
|
"findOne" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/autoChart", |
|
"autoChart" |
|
) |
|
); |
|
|
|
if (!isView && !this.sqlConfig.readOnly) { |
|
routes.push( |
|
this.prepareRoute(internal, "post", apiPrefix, tableName, "create") |
|
); |
|
} |
|
routes.push( |
|
this.prepareRoute(internal, "get", apiPrefix, tableName, "list") |
|
); |
|
|
|
if (!isView && !this.sqlConfig.readOnly) { |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"post", |
|
apiPrefix, |
|
tableName + "/bulk", |
|
"bulkInsert" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"delete", |
|
apiPrefix, |
|
tableName + "/bulk", |
|
"bulkDelete" |
|
) |
|
); |
|
} |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/bulk", |
|
"bulkRead" |
|
) |
|
); |
|
|
|
if (!isView && !this.sqlConfig.readOnly) { |
|
routes.push( |
|
this.prepareRoute(internal, "put", apiPrefix, tableName, "update") |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"patch", |
|
apiPrefix, |
|
tableName + "/:id", |
|
"patch" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"delete", |
|
apiPrefix, |
|
tableName + "/:id", |
|
"delete" |
|
) |
|
); |
|
} |
|
|
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/:id", |
|
"read" |
|
) |
|
); |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
tableName + "/:id/exists", |
|
"exists" |
|
) |
|
); |
|
|
|
for (var j = 0; j < table["foreignKeys"].length; ++j) { |
|
let fk = table["foreignKeys"][j]; |
|
|
|
if (fk["referenced_table_name"] in this.sqlConfig.ignoreTables) { |
|
//console.log('ignore table',fk['referenced_table_name']); |
|
} else { |
|
routes.push( |
|
this.prepareRoute( |
|
internal, |
|
"get", |
|
apiPrefix, |
|
fk["referenced_table_name"] + "/:id/" + fk["table_name"], |
|
"relational" |
|
) |
|
); |
|
} |
|
} |
|
|
|
tableObj["routes"] = routes; |
|
|
|
schemaRoutes.push(tableObj); |
|
} |
|
} |
|
|
|
return schemaRoutes; |
|
} |
|
|
|
getProcList() { |
|
let procRoutes = [] |
|
for (var procName in this.metaDb.routines) { |
|
procRoutes.push(this.metaDb.routines[procName]) |
|
} |
|
return procRoutes |
|
} |
|
|
|
getJoinType(joinInQueryParams) { |
|
//console.log('joinInQueryParams',joinInQueryParams); |
|
|
|
switch (joinInQueryParams) { |
|
case "_lj": |
|
return " left join "; |
|
break; |
|
|
|
case "_rj": |
|
return " right join "; |
|
break; |
|
|
|
// case '_fj': |
|
// return ' full join ' |
|
// break; |
|
|
|
case "_ij": |
|
return " inner join "; |
|
break; |
|
|
|
case "_j": |
|
return " join "; |
|
break; |
|
} |
|
|
|
return " join "; |
|
} |
|
|
|
globalRoutesPrint(apiPrefix) { |
|
let r = []; |
|
|
|
r.push(apiPrefix + "tables"); |
|
r.push(apiPrefix + "xjoin"); |
|
|
|
if (this.sqlConfig.dynamic) { |
|
r.push(apiPrefix + "dynamic"); |
|
r.push("/upload"); |
|
r.push("/uploads"); |
|
r.push("/download"); |
|
} |
|
|
|
return r; |
|
} |
|
|
|
getChartQueryAndParamsFromStepPair( |
|
tableName, |
|
columnName, |
|
stepArray, |
|
isRange = false |
|
) { |
|
let obj = {}; |
|
|
|
obj.query = ""; |
|
obj.params = []; |
|
|
|
//console.log('getChartQueryAndParamsFromStepArray',isRange); |
|
|
|
//select ? as ??, count(*) as _count from ?? where ?? between ? and ? |
|
|
|
if ( |
|
stepArray.length && |
|
stepArray.length >= 2 && |
|
stepArray.length % 2 === 0 |
|
) { |
|
for ( |
|
let i = 0; |
|
i < stepArray.length && stepArray.length >= 2; |
|
i = i + 2 |
|
) { |
|
obj.query = obj.query + dataHelp.getChartQuery(); |
|
|
|
if (i + 2 < stepArray.length) { |
|
obj.query = obj.query + " union "; |
|
} |
|
|
|
obj.params.push(stepArray[i] + " to " + stepArray[i + 1]); |
|
obj.params.push(columnName); |
|
obj.params.push(tableName); |
|
obj.params.push(columnName); |
|
obj.params.push(stepArray[i]); |
|
obj.params.push(stepArray[i + 1]); |
|
} |
|
} |
|
|
|
//console.log('step spread query', obj); |
|
|
|
return obj; |
|
} |
|
|
|
getChartQueryAndParamsFromStepArray( |
|
tableName, |
|
columnName, |
|
stepArray, |
|
isRange = false |
|
) { |
|
let obj = {}; |
|
|
|
obj.query = ""; |
|
obj.params = []; |
|
|
|
//console.log('getChartQueryAndParamsFromStepArray',isRange); |
|
|
|
if (stepArray.length && stepArray.length >= 2) { |
|
for (let i = 0; i < stepArray.length - 1; i = i + 1) { |
|
obj.query = obj.query + dataHelp.getChartQuery(); |
|
if (i + 2 < stepArray.length) { |
|
obj.query = obj.query + " union "; |
|
} |
|
|
|
if (i && isRange === false) { |
|
stepArray[i] = stepArray[i] + 1; |
|
} |
|
|
|
if (isRange === false) { |
|
obj.params.push(stepArray[i] + " to " + stepArray[i + 1]); |
|
} else { |
|
obj.params.push(stepArray[0] + " to " + stepArray[i + 1]); |
|
} |
|
|
|
obj.params.push(columnName); |
|
obj.params.push(tableName); |
|
obj.params.push(columnName); |
|
|
|
if (isRange === false) { |
|
obj.params.push(stepArray[i]); |
|
obj.params.push(stepArray[i + 1]); |
|
} else { |
|
obj.params.push(stepArray[0]); |
|
obj.params.push(stepArray[i + 1]); |
|
} |
|
} |
|
} |
|
|
|
//console.log('step spread query', obj); |
|
|
|
return obj; |
|
} |
|
|
|
getChartQueryAndParamsFromMinMaxStddev( |
|
tableName, |
|
columnName, |
|
min, |
|
max, |
|
stddev, |
|
isRange = false |
|
) { |
|
let stepArray = dataHelp.getStepArray(min, max, stddev); |
|
|
|
//console.log('steparray', stepArray); |
|
|
|
let obj = this.getChartQueryAndParamsFromStepArray( |
|
tableName, |
|
columnName, |
|
stepArray, |
|
isRange |
|
); |
|
|
|
//console.log('steparray', obj); |
|
|
|
return obj; |
|
} |
|
|
|
getChartQueryAndParamsFromMinMaxStep( |
|
tableName, |
|
columnName, |
|
min, |
|
max, |
|
step, |
|
isRange = false |
|
) { |
|
let stepArray = dataHelp.getStepArraySimple(min, max, step); |
|
|
|
//console.log('steparray', stepArray); |
|
|
|
let obj = this.getChartQueryAndParamsFromStepArray( |
|
tableName, |
|
columnName, |
|
stepArray, |
|
isRange |
|
); |
|
|
|
//console.log('steparray', obj); |
|
|
|
return obj; |
|
} |
|
|
|
_getGrpByHavingOrderBy(req, tableName, queryParamsObj, listType) { |
|
/**************** add group by ****************/ |
|
this.getGroupByClause( |
|
req.query._groupby, |
|
req.app.locals._tableName, |
|
queryParamsObj |
|
); |
|
|
|
/**************** add having ****************/ |
|
this.getHavingClause( |
|
req.query._having, |
|
req.app.locals._tableName, |
|
queryParamsObj |
|
); |
|
|
|
/**************** add order clause ****************/ |
|
this.getOrderByClause(req.query, req.app.locals._tableName, queryParamsObj); |
|
|
|
/**************** add limit clause ****************/ |
|
if (listType === 2) { |
|
//nested |
|
queryParamsObj.query += " limit 1 "; |
|
} else { |
|
queryParamsObj.query += " limit ?,? "; |
|
queryParamsObj.params = queryParamsObj.params.concat( |
|
this.getLimitClause(req.query) |
|
); |
|
} |
|
} |
|
|
|
/** |
|
* |
|
* @param req |
|
* @param res |
|
* @param queryParamsObj : {query, params} |
|
* @param listType : 0:list, 1:nested, 2:findOne, 3:bulkRead, 4:distinct, 5:xjoin |
|
* |
|
* Updates query, params for query of type listType |
|
*/ |
|
prepareListQuery(req, res, queryParamsObj, listType = 0) { |
|
queryParamsObj.query = "select "; |
|
queryParamsObj.params = []; |
|
|
|
if (listType === 4) { |
|
//list type distinct |
|
queryParamsObj.query += " distinct "; |
|
} |
|
|
|
/**************** select columns ****************/ |
|
if (req.query._groupby) { |
|
this.getColumnsForSelectStmtWithGrpBy( |
|
req.query, |
|
req.app.locals._tableName, |
|
queryParamsObj |
|
); |
|
} else { |
|
this.getColumnsForSelectStmt( |
|
req.app.locals._tableName, |
|
req.query, |
|
queryParamsObj |
|
); |
|
} |
|
|
|
/**************** add tableName ****************/ |
|
queryParamsObj.query += " from ?? "; |
|
|
|
if (listType === 1) { |
|
//nested list |
|
|
|
req.app.locals._tableName = req.app.locals._childTable; |
|
|
|
queryParamsObj.params.push(req.app.locals._childTable); |
|
|
|
queryParamsObj.query += " where "; |
|
|
|
/**************** add where foreign key ****************/ |
|
let whereClause = this.getForeignKeyWhereClause( |
|
req.app.locals._parentTable, |
|
req.params.id, |
|
req.app.locals._childTable |
|
); |
|
|
|
if (!whereClause) { |
|
return res.status(400).send({ |
|
error: |
|
"Table is made of composite primary keys - all keys were not in input" |
|
}); |
|
} |
|
queryParamsObj.query += whereClause; |
|
|
|
this.getWhereClause( |
|
req.query._where, |
|
req.app.locals._tableName, |
|
queryParamsObj, |
|
" and " |
|
); |
|
} else if (listType === 3) { |
|
//bulkRead |
|
|
|
// select * from table where pk in (ids) and whereConditions |
|
queryParamsObj.params.push(req.app.locals._tableName); |
|
queryParamsObj.query += " where ?? in "; |
|
queryParamsObj.params.push( |
|
this.getPrimaryKeyName(req.app.locals._tableName) |
|
); |
|
|
|
queryParamsObj.query += "("; |
|
|
|
if (req.query && req.query._ids) { |
|
let ids = req.query._ids.split(","); |
|
for (var i = 0; i < ids.length; ++i) { |
|
if (i) { |
|
queryParamsObj.query += ","; |
|
} |
|
queryParamsObj.query += "?"; |
|
queryParamsObj.params.push(ids[i]); |
|
} |
|
} |
|
queryParamsObj.query += ") "; |
|
this.getWhereClause( |
|
req.query._where, |
|
req.app.locals._tableName, |
|
queryParamsObj, |
|
" and " |
|
); |
|
} else { |
|
queryParamsObj.params.push(req.app.locals._tableName); |
|
|
|
/**************** add where clause ****************/ |
|
this.getWhereClause( |
|
req.query._where, |
|
req.app.locals._tableName, |
|
queryParamsObj, |
|
" where " |
|
); |
|
} |
|
|
|
this._getGrpByHavingOrderBy(req, req.app.locals._tableName, queryParamsObj); |
|
|
|
//console.log(queryParamsObj.query, queryParamsObj.params); |
|
} |
|
|
|
_joinTableNames(isSecondJoin, joinTables, index, queryParamsObj) { |
|
if (isSecondJoin) { |
|
/** |
|
* in second join - there will be ONE table and an ON condition |
|
* if clause deals with this |
|
* |
|
*/ |
|
|
|
// add : join / left join / right join / full join / inner join |
|
queryParamsObj.query += this.getJoinType(joinTables[index]); |
|
queryParamsObj.query += " ?? as ?? "; |
|
|
|
// eg: tbl.tableName |
|
let tableNameAndAs = joinTables[index + 1].split("."); |
|
|
|
if ( |
|
tableNameAndAs.length === 2 && |
|
!(tableNameAndAs[1] in this.sqlConfig.ignoreTables) |
|
) { |
|
queryParamsObj.params.push(tableNameAndAs[1]); |
|
queryParamsObj.params.push(tableNameAndAs[0]); |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
console.log("there was no dot for tableName ", joinTables[index + 1]); |
|
} |
|
} else { |
|
/** |
|
* in first join - there will be TWO tables and an ON condition |
|
* else clause deals with this |
|
*/ |
|
|
|
// first table |
|
queryParamsObj.query += " ?? as ?? "; |
|
// add : join / left join / right join / full join / inner join |
|
queryParamsObj.query += this.getJoinType(joinTables[index + 1]); |
|
// second table |
|
queryParamsObj.query += " ?? as ?? "; |
|
|
|
let tableNameAndAs = joinTables[index].split("."); |
|
if ( |
|
tableNameAndAs.length === 2 && |
|
!(tableNameAndAs[1] in this.sqlConfig.ignoreTables) |
|
) { |
|
queryParamsObj.params.push(tableNameAndAs[1]); |
|
queryParamsObj.params.push(tableNameAndAs[0]); |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
console.log("there was no dot for tableName ", joinTables[index]); |
|
} |
|
|
|
tableNameAndAs = []; |
|
tableNameAndAs = joinTables[index + 2].split("."); |
|
if ( |
|
tableNameAndAs.length === 2 && |
|
!(tableNameAndAs[1] in this.sqlConfig.ignoreTables) |
|
) { |
|
queryParamsObj.params.push(tableNameAndAs[1]); |
|
queryParamsObj.params.push(tableNameAndAs[0]); |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
console.log("there was no dot for tableName ", joinTables[index]); |
|
} |
|
} |
|
} |
|
|
|
prepareJoinQuery(req, res, queryParamsObj) { |
|
queryParamsObj.query = "SELECT "; |
|
queryParamsObj.grammarErr = 0; |
|
|
|
while (1) { |
|
/**************** START : get fields ****************/ |
|
if (req.query._fields) { |
|
let fields = req.query._fields.split(","); |
|
|
|
// from _fields to - ??, ??, ?? [col1,col2,col3] |
|
for (var i = 0; i < fields.length && !queryParamsObj.grammarErr; ++i) { |
|
if (i) { |
|
queryParamsObj.query += ","; |
|
} |
|
queryParamsObj.query += " ?? "; |
|
queryParamsObj.params.push(fields[i]); |
|
let aliases = fields[i].split("."); |
|
if (aliases.length === 2) { |
|
queryParamsObj.query += "as " + aliases[0] + "_" + aliases[1]; |
|
//console.log(queryParamsObj.query); |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
} |
|
} |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
} |
|
|
|
queryParamsObj.query += " from "; |
|
|
|
if (queryParamsObj.grammarErr) { |
|
break; |
|
} |
|
|
|
/**************** END : get fields ****************/ |
|
|
|
/**************** START : get join + on ****************/ |
|
let joinTables = req.query._join.split(","); |
|
if (joinTables.length < 3) { |
|
//console.log('grammar error ', joinTables.length); |
|
queryParamsObj.grammarErr = 1; |
|
break; |
|
} |
|
|
|
//console.log('jointables.length', joinTables); |
|
|
|
let onCondnCount = 0; |
|
|
|
for ( |
|
let i = 0; |
|
i < joinTables.length - 1 && queryParamsObj.grammarErr === 0; |
|
i = i + 2 |
|
) { |
|
onCondnCount++; |
|
|
|
this._joinTableNames(i, joinTables, i, queryParamsObj); |
|
|
|
if (queryParamsObj.grammarErr) { |
|
console.log("failed at _joinTableNames", queryParamsObj); |
|
break; |
|
} |
|
|
|
//console.log('after join tables', queryParamsObj); |
|
|
|
let onCondn = "_on" + onCondnCount; |
|
let onCondnObj = {}; |
|
if (onCondn in req.query) { |
|
//console.log(onCondn, req.query[onCondn]); |
|
onCondnObj = whereHelp.getConditionClause(req.query[onCondn], " on "); |
|
//console.log('onCondnObj', onCondnObj); |
|
queryParamsObj.query += " on " + onCondnObj.query; |
|
queryParamsObj.params = queryParamsObj.params.concat( |
|
onCondnObj.params |
|
); |
|
} else { |
|
queryParamsObj.grammarErr = 1; |
|
//console.log('No on condition: ', onCondn); |
|
break; |
|
} |
|
|
|
if (i === 0) { |
|
i = i + 1; |
|
} |
|
} |
|
/**************** END : get join + on ****************/ |
|
|
|
if (queryParamsObj.grammarErr) { |
|
break; |
|
} else { |
|
this.getWhereClause( |
|
req.query._where, |
|
" ignore ", |
|
queryParamsObj, |
|
" where " |
|
); |
|
this._getGrpByHavingOrderBy(req, "ignore", queryParamsObj, 5); |
|
//console.log('after where',queryParamsObj); |
|
} |
|
|
|
break; |
|
} |
|
|
|
if (queryParamsObj.grammarErr) { |
|
queryParamsObj.query = ""; |
|
queryParamsObj.params = []; |
|
} |
|
|
|
return queryParamsObj; |
|
} |
|
} |
|
|
|
//expose class |
|
module.exports = Xsql;
|
|
|