UNPKG

tiesdb-client

Version:
254 lines (233 loc) 8.92 kB
const sqlParser = require('./sql.js'); const codec = require('../request/codec'); const Tag = codec.Tag; const Field = require('../request/field'); function compile(sql){ let ast; try { ast = sqlParser.parse(sql); }catch(e){ if(e.JisonParserError) throw new Error(e.hash.errStr); throw e; } return ast; } function makeRequest(requestTag, ast, defaultTablespace) { if(ast.nodeType !== 'tql' || ast.value.nodeType !== 'SELECT') throw new Error('Invalid request'); //Set FROM let from = ast.value.from; if(typeof from === 'string'){ if(!defaultTablespace) throw new Error('No tablespace specified and no default tablespace set'); requestTag.addChild("RecollectionTablespaceName", defaultTablespace); requestTag.addChild("RecollectionTableName", from); }else if(from.nodeType === 'QualifiedIdentifier') { requestTag.addChild("RecollectionTablespaceName", from.parent); requestTag.addChild("RecollectionTableName", from.value); } //Set COLUMNS to retrieve let columns = ast.value.columns; if(columns !== '*') { let columnTags = new Tag("RetrieveList"); for (let col of columns) { if (typeof(col.value) === 'string') { //Column identifier columnTags.addChild("RetField", col.value); } else { //Function let tag = new Tag("RetCompute"); if(col.alias) tag.addChild("RetComputeAlias",col.alias); if(col.type) tag.addChild("RetComputeType", col.type); makeFunction(tag, col.value); columnTags.addChild(tag); } } requestTag.addChild(columnTags); } //Set WHERE (Filters) let where = ast.value.where; if(where){ let filters = new Tag("FilterList"); for(let op of where){ let filter = makeFilter(op); filters.addChild(filter); } requestTag.addChild(filters); } } function makeFilter(op) { if(typeof op.left !== 'string') throw new Error('Operator lvalue should be field identifier!'); let filter = new Tag("Filter"); filter.addChild("FilterField", op.left); switch(op.nodeType) { case 'OPERATOR_IN': filter.addChild("FunctionName", 'IN'); for(let arg of op.right) { makeArgument(filter, arg); } break; case 'OPERATOR_COMPARE': filter.addChild("FunctionName", op.name); makeArgument(filter, op.right); break; default: throw new Error('Unknown operator: ' + op.nodeType); } return filter; } function makeFunction(tag, func) { if(func.nodeType === 'CAST') { tag.addChild("FunctionName", 'CAST'); makeArgument(tag, func.expression); makeArgument(tag, {nodeType: 'STRING', value: func.dataType}); }else if(func.nodeType === 'FUNCTION') { tag.addChild("FunctionName",func.name); if(func.args) for(let arg of func.args) { makeArgument(tag, arg); } }else{ throw new Error('Unknown function ' + func.nodeType); } } function makeArgument(tag, expression) { if(typeof(expression) === 'string') { //Identifier tag.addChild("FunArgumentReference", expression); }else if(expression.nodeType === 'FUNCTION' || expression.nodeType === 'CAST') { let _tag = new Tag("FunArgumentFunction"); makeFunction(_tag, expression); tag.addChild(_tag); }else if(['INTEGER', 'STRING', 'UUID', 'DECIMAL', 'BOOLEAN']) { let _tag = new Tag("FunArgumentStatic"); fillStatic(_tag, expression); tag.addChild(_tag); }else { throw new Error('Unknown function argument: ' + expression.nodeType); } } function convertTypeToUUID(type, value){ let buf = Field.encodeValue(type, value); if(buf.length > 16) buf = buf.slice(-16); if(buf.length < 16) buf = Buffer.concat([Buffer.alloc(16-buf.length, 0), buf]); return buf; } function fillStatic(tag, expression) { let type = expression.type && expression.type.toLowerCase(); let buf; switch(expression.nodeType) { case 'INTEGER': if(!type) type = 'integer'; if(['integer', 'long', 'bigint', 'float', 'double', 'decimal', 'time', 'duration', 'ascii', 'string', 'boolean'].indexOf(type) >= 0) { buf = Field.encodeValue(type, expression.value); break; } if(['binary'].indexOf(type) >= 0) { buf = Field.encodeValue('bigint', expression.value); break; } if(['uuid'].indexOf(type) >= 0) { buf = convertTypeToUUID('bigint', expression.value); break; } throw new Error('Can not convert INTEGER ' + expression.value + ' to type ' + type); case 'DECIMAL': if(!type) type = 'decimal'; if(['integer', 'long', 'bigint', 'float', 'double', 'decimal', 'time', 'duration', 'ascii', 'string', 'boolean'].indexOf(type) >= 0) { buf = Field.encodeValue(type, expression.value); break; } if(['binary'].indexOf(type) >= 0) { buf = Field.encodeValue('decimal', expression.value); break; } if(['uuid'].indexOf(type) >= 0) { buf = convertTypeToUUID('decimal', expression.value); break; } throw new Error('Can not convert DECIMAL ' + expression.value + ' to type ' + type); case 'UUID': if(!type) type = 'uuid'; if(['binary', 'uuid'].indexOf(type) >= 0) { buf = convertTypeToUUID('uuid', expression.value); break; } if(['ascii', 'string'].indexOf(type) >= 0) { buf = Field.encodeValue(type, expression.value); } throw new Error('Can not convert UUID ' + expression.value + ' to type ' + type); case 'BOOLEAN': if(!type) type = 'boolean'; if(['boolean', 'string', 'ascii'].indexOf(type) >= 0) { buf = Field.encodeValue(type, expression.value); break; } if(['integer', 'long', 'bigint', 'float', 'double', 'decimal'].indexOf(type) >= 0) { buf = Field.encodeValue('boolean', expression.value); buf = Field.encodeValue(type, buf[0] || 0); break; } if(['binary'].indexOf(type) >= 0) { buf = Field.encodeValue('boolean', expression.value); break; } throw new Error('Can not convert BOOLEAN ' + expression.value + ' to type ' + type); case 'STRING': if(!type) type = 'string'; if(type === 'binary') { buf = Buffer.from(expression.value, 'hex'); break; } if(type === 'time') { let time = Date.parse(expression.value); if(time === NaN) throw new Error('Can not parse ISO8601 TIME from ' + expression.value); buf = Field.encodeValue(type, time); break; } buf = Field.encodeValue(type, expression.value); break; default: throw new Error('Unknown static type: ' + expression.nodeType); } tag.addChild("ArgStaticType", type); tag.addChild("ArgStaticValue", buf); } //Get order of columns from original recollection request function getColumnsOrder(tagRecollectionRequest){ let retlist = tagRecollectionRequest.getChild("RetrieveList"); let columns = retlist && retlist.getChildren(); if(!columns || !columns.length) return; //No predefined order of columns let order = { fields: {}, computed: [], total: 0 }; for(let col of columns){ if(col.name === "RetField"){ order.fields[col.getValue()] = order.total++; }else if(col.name === "RetCompute"){ order.computed.push(order.total++); }else{ throw new Error("Unknown column in RetrieveList: " + col.name); } } return order; } module.exports = { compile, makeRequest, getColumnsOrder };