//-< COMPILE.CPP >---------------------------------------------------*--------* // FastDB Version 1.0 (c) 1999 GARRET * ? * // (Main Memory Database Management System) * /\| * // * / \ * // Created: 20-Nov-98 K.A. Knizhnik * / [] \ * // Last update: 15-Jan-99 K.A. Knizhnik * GARRET * //-------------------------------------------------------------------*--------* // Conditional expresion compiler //-------------------------------------------------------------------*--------* #define INSIDE_FASTDB #include #include "database.h" #include "array.h" #include "query.h" #include "cursor.h" #include "compiler.h" #include "symtab.h" nat1 const dbExprNode::nodeTypes[] = { #define DBVM(cop, type, n_opernads, commutative) type, #include "compiler.d" 0}; nat1 const dbExprNode::nodeOperands[] = { #define DBVM(cop, type, n_operands, commutative) n_operands, #include "compiler.d" 0}; nat1 const dbExprNode::commutativeOperator[] = { #define DBVM(cop, type, n_operands, commutative) commutative, #include "compiler.d" 0}; dbExprNode* dbExprNode::freeNodeList; dbMutex& dbExprNode::mutex = *new dbMutex; bool dbCompiler::initialized; dbExprNodeSegment* dbExprNode::segmentList; dbExprNode::dbExprNode(dbExprNode* node) { memcpy(this, node, sizeof(*this)); for (int i = nodeOperands[cop]; --i >= 0;) { operand[i] = new dbExprNode(operand[i]); } if (cop == dbvmLoadStringConstant) { char* s = new char[strlen(svalue.str)+1]; strcpy(s, svalue.str); svalue.str = s; } } void* dbExprNode::operator new(size_t EXTRA_DEBUG_NEW_PARAMS) { dbCriticalSection cs(mutex); dbExprNode* node = freeNodeList; if (node == NULL) { dbExprNodeSegment* seg = new dbExprNodeSegment; seg->next = segmentList; segmentList = seg; node = (dbExprNode*)seg->buf; dbExprNode* freeNode = NULL; for (int i = dbExprNodeSegment::allocationQuantum; --i != 0;) { node->next = freeNode; freeNode = node++; } freeNodeList = freeNode; } else { freeNodeList = node->next; } return node; } void dbExprNode::cleanup() { dbExprNode* freeNode = NULL; for (dbExprNodeSegment* seg = segmentList; seg != NULL; seg = seg->next) { dbExprNode* node = (dbExprNode*)seg->buf; for (int i = dbExprNodeSegment::allocationQuantum; --i >= 0;) { node->next = freeNode; freeNode = node++; } } freeNodeList = freeNode; } dbExprNode::~dbExprNode() { if (cop == dbvmLoadStringConstant) { delete[] svalue.str; } else { for (int i = nodeOperands[cop]; --i >= 0; ) { delete operand[i]; } } } int dbCompiler::scan() { char ch; char *p, *q; int i, n, value; char buf[maxStrLen+1]; if (hasToken) { hasToken = false; return lex; } nextElement: if (queryElement == NULL) { return tkn_eof; } if (queryElement->type != dbQueryElement::qExpression) { varType = queryElement->type; varPtr = queryElement->ptr; varRefTable = queryElement->ref; queryElement = queryElement->next; return tkn_var; } p = q = (char*)queryElement->ptr + currPos; do { ch = *p++; if (ch == '\n') { offsetWithinStatement = (char*)queryElement->ptr - p; firstPos = 0; } } while (isspace(byte(ch))); currPos += p - q; switch (ch) { case '\0': if ((queryElement = queryElement->next) == NULL) { return tkn_eof; } currPos = 0; goto nextElement; case '+': return tkn_add; case '-': if (*p == '-') { // ANSI comments q = p; do { p += 1; } while (*p != '\n' && *p != '\0'); currPos += p - q; goto nextElement; } return tkn_sub; case '*': return tkn_mul; case '/': return tkn_div; case '.': return tkn_dot; case ',': return tkn_comma; case '(': return tkn_lpar; case ')': return tkn_rpar; case '[': return tkn_lbr; case ']': return tkn_rbr; case ':': return tkn_col; case '^': return tkn_power; case '<': if (*p == '=') { currPos += 1; return tkn_le; } else if (*p == '>') { currPos += 1; return tkn_ne; } return tkn_lt; case '>': if (*p == '=') { currPos += 1; return tkn_ge; } return tkn_gt; case '=': return tkn_eq; case '!': if (*p != '=') { error("Invalid token '!'"); } currPos += 1; return tkn_ne; case '|': if (*p != '|') { error("Invalid token '|'"); } currPos += 1; return tkn_add; case '\'': q = p; i = 0; while (true) { if (*p == '\'') { if (*++p != '\'') { break; } } else if (*p == '\0') { error("Unterminated character constant"); } if (i == maxStrLen) { error("String constant too long"); } buf[i++] = *p++; } buf[i++] = '\0'; currPos += p - q; svalue.str = new char[i]; strcpy(svalue.str, buf); svalue.len = i; return tkn_sconst; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = ch - '0'; for (q = p; isdigit(*(byte*)p); p++) { value = value*10 + *p - '0'; } if (*p == '.' || *p == 'e' || *p == 'E') { if (sscanf(q-1, "%lf%n", &fvalue, &n) != 1) { error("Bad floating point constant"); } currPos += n - 1; return tkn_fconst; } else if (p - q >= 9) { if (sscanf(q-1, INT8_FORMAT "%n", &ivalue, &n) != 1) { error("Bad integer constant"); } currPos += n - 1; return tkn_iconst; } else { currPos += p - q; ivalue = value; return tkn_iconst; } default: if (isalpha(ch) || ch == '$' || ch == '_') { i = 0; do { if (i == maxStrLen) { error("Name too long"); } buf[i++] = ch; ch = *p++; } while (isalnum(ch) || ch == '$' || ch == '_'); buf[i] = '\0'; name = buf; currPos += i - 1; } else { error("Invalid symbol"); } return dbSymbolTable::add(name, tkn_ident); } } dbExprNode* dbCompiler::disjunction() { dbExprNode* left = conjunction(); if (lex == tkn_or) { int pos = currPos; dbExprNode* right = disjunction(); if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(dbvmOrInt, left, right); } else if (left->type == tpBoolean && right->type == tpBoolean) { left = new dbExprNode(dbvmOrBool, left, right); } else { error("Bad operands for OR operator", pos); } } return left; } dbExprNode* dbCompiler::conjunction() { dbExprNode* left = comparison(); if (lex == tkn_and) { int pos = currPos; dbExprNode* right = conjunction(); if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(dbvmAndInt, left, right); } else if (left->type == tpBoolean && right->type == tpBoolean) { left = new dbExprNode(dbvmAndBool, left, right); } else { error("Bad operands for AND operator", pos); } } return left; } inline dbExprNode* int2real(dbExprNode* expr) { if (expr->cop == dbvmLoadIntConstant) { expr->fvalue = (real8)expr->ivalue; expr->cop = dbvmLoadRealConstant; expr->type = tpReal; } else { expr = new dbExprNode(dbvmIntToReal, expr); } return expr; } int dbCompiler::compare(dbExprNode* expr, dbExprNode* list) { int n = 1; if (list->operand[0] != NULL) { n = compare(expr, list->operand[0]); expr = new dbExprNode(expr); } dbExprNode* elem = list->operand[1]; int cop = dbvmVoid; int rawBinarySize = 0; void* rawBinaryComparator = NULL; if (expr->type == tpInteger) { if (elem->type == tpReal) { expr = int2real(expr); cop = dbvmEqReal; } else if (elem->type == tpReference) { expr = new dbExprNode(dbvmIntToReference, expr); cop = dbvmEqReference; } else if (elem->type == tpInteger) { cop = dbvmEqInt; } } else if (expr->type == tpReal) { if (elem->type == tpReal) { cop = dbvmEqReal; } else if (elem->type == tpInteger) { cop = dbvmEqReal; elem = int2real(elem); } } else if (expr->type == tpString && elem->type == tpString) { cop = dbvmEqString; } else if (expr->type == tpReference && (elem->type == tpReference || elem->type == tpInteger)) { if (elem->type == tpInteger) { elem = new dbExprNode(dbvmIntToReference, elem); } cop = dbvmEqReference; } else if (expr->type == tpBoolean && elem->type == tpBoolean) { cop = dbvmEqBool; } else if (expr->type == tpRawBinary && elem->type == tpRawBinary) { cop = dbvmEqBinary; if ((expr->cop == dbvmLoadSelfRawBinary || expr->cop == dbvmLoadRawBinary) && expr->ref.field != NULL) { rawBinarySize = expr->ref.field->dbsSize; rawBinaryComparator = (void*)expr->ref.field->comparator; } else if ((elem->cop == dbvmLoadSelfRawBinary || elem->cop == dbvmLoadRawBinary) && elem->ref.field != NULL) { rawBinarySize = elem->ref.field->dbsSize; rawBinaryComparator = (void*)elem->ref.field->comparator; } else { error("Operations with raw binary types should include at least one record field"); } } if (cop == dbvmVoid) { char buf[256]; sprintf(buf, "Expression %d in right part of IN operator have " "incompatible type", n); error(buf); } else { list->type = tpBoolean; if (list->operand[0] != NULL) { expr = new dbExprNode(cop, expr, elem, rawBinarySize); expr->func.fptr = rawBinaryComparator; list->operand[1] = expr; list->cop = dbvmOrBool; } else { list->operand[0] = expr; list->cop = cop; list->offs = rawBinarySize; list->func.fptr = rawBinaryComparator; } } return ++n; } dbExprNode* dbCompiler::comparison() { int leftPos = currPos; dbExprNode *left, *right; left = addition(); int cop = lex; if (cop == tkn_eq || cop == tkn_ne || cop == tkn_gt || cop == tkn_ge || cop == tkn_le || cop == tkn_lt || cop == tkn_between || cop == tkn_like || cop == tkn_not || cop == tkn_is || cop == tkn_in) { int rightPos = currPos; bool notOp = false; if (cop == tkn_not) { notOp = true; cop = scan(); if (cop != tkn_like && cop != tkn_between && cop != tkn_in) { error("LIKE, BETWEEN or IN expected", rightPos); } rightPos = currPos; } else if (cop == tkn_is) { if (left->type != tpReference) { error("IS [NOT] NULL predicate can be applied only to " "references", rightPos); } rightPos = currPos; if ((cop = scan()) == tkn_null) { left = new dbExprNode(dbvmIsNull, left); } else if (cop == tkn_not) { rightPos = currPos; if (scan() == tkn_null) { left = new dbExprNode(dbvmNotBool, new dbExprNode(dbvmIsNull, left)); } else { error("NULL expected", rightPos); } } else { error("[NOT] NULL expected", rightPos); } lex = scan(); return left; } right = addition(); if (cop == tkn_in) { int type; switch (right->type) { case tpArray: type = (right->cop == dbvmLoadVarArray || right->cop == dbvmLoadVarArrayPtr) ? dbField::tpReference : right->ref.field->components->type; if ((left->type == tpBoolean && type == dbField::tpBool) || (left->type == tpInteger && (type == dbField::tpInt1 || type == dbField::tpInt2 || type == dbField::tpInt4 || type == dbField::tpInt8)) || (left->type == tpReal && (type == dbField::tpReal4 || type == dbField::tpReal8)) || (left->type == tpString && type == dbField::tpString) || ((left->type == tpReference || left->type == tpInteger) && type == dbField::tpReference)) { if (left->type == tpInteger && type == dbField::tpReference) { left = new dbExprNode(dbvmIntToReference, left); } left = new dbExprNode(dbvmInArrayBool + type, left, right); } else { error("Type of selective expression of IN operator doesn't" " match type of the array"); } break; case tpString: if (left->type == tpString) { left = new dbExprNode(dbvmInString, left, right); } else { error("Left operand of IN expression hasn't string type"); } break; case tpList: compare(left, right); left = right; break; default: error("List of expressions or array expected", rightPos); } } else if (cop == tkn_between) { int andPos = currPos; if (lex != tkn_and) { error("AND expected"); } dbExprNode* right2 = addition(); if (left->type == tpReal || right->type == tpReal || right2->type == tpReal) { if (left->type == tpInteger) { left = int2real(left); } else if (left->type != tpReal) { error("operand of BETWEEN operator should be of " "integer, real or string type", leftPos); } if (right->type == tpInteger) { right = int2real(right); } else if (right->type != tpReal) { error("operand of BETWEEN operator should be of " "integer, real or string type", rightPos); } if (right2->type == tpInteger) { right2 = int2real(right2); } else if (right2->type != tpReal) { error("operand of BETWEEN operator should be of " "integer, real or string type", andPos); } left = new dbExprNode(dbvmBetweenReal, left, right, right2); } else if (left->type == tpInteger && right->type == tpInteger && right2->type == tpInteger) { left = new dbExprNode(dbvmBetweenInt, left, right, right2); } else if (left->type == tpString && right->type == tpString && right2->type == tpString) { left = new dbExprNode(dbvmBetweenString, left, right, right2); } else if (left->type == tpRawBinary && right->type == tpRawBinary && right2->type == tpRawBinary) { int rawBinarySize = 0; void* rawBinaryComparator = NULL; if ((left->cop == dbvmLoadSelfRawBinary || left->cop == dbvmLoadRawBinary) && left->ref.field != NULL) { rawBinarySize = left->ref.field->dbsSize; rawBinaryComparator = (void*)left->ref.field->comparator; } else { error("Operations with raw binary types should include at least one record field"); } left = new dbExprNode(dbvmBetweenBinary, left, right, right2); left->offs = rawBinarySize; left->func.fptr = rawBinaryComparator; } else { error("operands of BETWEEN operator should be of " "integer, real or string type", rightPos); } } else if (cop == tkn_like) { if (left->type != tpString || right->type != tpString) { error("operands of LIKE operator should be of " "string type", rightPos); } if (lex == tkn_escape) { rightPos = currPos; if (scan() != tkn_sconst) { error("String literal espected after ESCAPE", rightPos); } lex = scan(); left = new dbExprNode(dbvmLikeEscapeString, left, right, new dbExprNode(dbvmLoadStringConstant, svalue)); } else { left = new dbExprNode(dbvmLikeString, left, right); } } else { if (left->type == tpReal || right->type == tpReal) { if (left->type == tpInteger) { left = int2real(left); } else if (left->type != tpReal) { error("operands of relation operator should be of " "intger, real or string type", leftPos); } if (right->type == tpInteger) { right = int2real(right); } else if (right->type != tpReal) { error("operands of relation operator should be of " "intger, real or string type", rightPos); } left = new dbExprNode(dbvmEqReal + cop - tkn_eq, left, right); } else if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(dbvmEqInt + cop - tkn_eq, left, right); } else if (left->type == tpString && right->type == tpString) { left = new dbExprNode(dbvmEqString + cop-tkn_eq, left, right); } else if ((left->type == tpReference || left->type == tpInteger) && (right->type == tpReference || right->type == tpInteger)) { if (cop != tkn_eq && cop != tkn_ne) { error("References can be checked only for equality", rightPos); } if (left->type == tpInteger) { left = new dbExprNode(dbvmIntToReference, left); } else if (right->type == tpInteger) { right = new dbExprNode(dbvmIntToReference, right); } left = new dbExprNode(dbvmEqReference+cop-tkn_eq, left, right); } else if (left->type == tpBoolean && right->type == tpBoolean) { if (cop != tkn_eq && cop != tkn_ne) { error("Boolean variables can be checked only for equality", rightPos); } left = new dbExprNode(dbvmEqBool + cop - tkn_eq, left, right); } else if (left->type == tpRawBinary && right->type == tpRawBinary) { int rawBinarySize = 0; void* rawBinaryComparator = NULL; if ((left->cop == dbvmLoadSelfRawBinary || left->cop == dbvmLoadRawBinary) && left->ref.field != NULL) { rawBinarySize = left->ref.field->dbsSize; rawBinaryComparator = (void*)left->ref.field->comparator; } else if ((right->cop == dbvmLoadSelfRawBinary || right->cop == dbvmLoadRawBinary) && right->ref.field != NULL) { rawBinarySize = right->ref.field->dbsSize; rawBinaryComparator = (void*)right->ref.field->comparator; } else { error("Operations with raw binary types should include at least one record field"); } left = new dbExprNode(dbvmEqBinary + cop - tkn_eq, left, right, rawBinarySize); left->func.fptr = rawBinaryComparator; } else { error("operands of relation operator should be of " "integer, real or string type", rightPos); } // // Optimization for applying indices: if operation is // commuatative and left operand is constant then swap operands // if (IS_CONSTANT(left->operand[0]->cop)) { right = left->operand[1]; left->operand[1] = left->operand[0]; left->operand[0] = right; left->cop = dbExprNode::commutativeOperator[left->cop]; } } if (notOp) { left = new dbExprNode(dbvmNotBool, left); } } return left; } dbExprNode* dbCompiler::addition() { int leftPos = currPos; dbExprNode* left = multiplication(); while (lex == tkn_add || lex == tkn_sub) { int cop = lex; int rightPos = currPos; dbExprNode* right = multiplication(); if (left->type == tpReal || right->type == tpReal) { if (left->type == tpInteger) { left = int2real(left); } else if (left->type != tpReal) { error("operands of arithmetic operators should be of " "integer or real type", leftPos); } if (right->type == tpInteger) { right = int2real(right); } else if (right->type != tpReal) { error("operands of arithmetic operator should be of " "integer or real type", rightPos); } left = new dbExprNode(cop==tkn_add ? dbvmAddReal : dbvmSubReal, left, right); } else if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(cop==tkn_add ? dbvmAddInt : dbvmSubInt, left, right); } else if (left->type == tpString && right->type == tpString) { if (cop == tkn_add) { left = new dbExprNode(dbvmStringConcat, left, right); } else { error("Operation - is not defined for strings", rightPos); } } else { error("operands of arithmentic operator should be of " "integer or real type", rightPos); } leftPos = rightPos; } return left; } dbExprNode* dbCompiler::multiplication() { int leftPos = currPos; dbExprNode* left = power(); while (lex == tkn_mul || lex == tkn_div) { int cop = lex; int rightPos = currPos; dbExprNode* right = power(); if (left->type == tpReal || right->type == tpReal) { if (left->type == tpInteger) { left = int2real(left); } else if (left->type != tpReal) { error("operands of arithmetic operators should be of " "integer or real type", leftPos); } if (right->type == tpInteger) { right = int2real(right); } else if (right->type != tpReal) { error("operands of arithmetic operator should be of " "integer or real type", rightPos); } left = new dbExprNode(cop==tkn_mul ? dbvmMulReal : dbvmDivReal, left, right); } else if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(cop==tkn_mul ? dbvmMulInt : dbvmDivInt, left, right); } else { error("operands of arithmentic operator should be of " "integer or real type", rightPos); } leftPos = rightPos; } return left; } dbExprNode* dbCompiler::power() { int leftPos = currPos; dbExprNode* left = userDefinedOperator(); if (lex == tkn_power) { int rightPos = currPos; dbExprNode* right = power(); if (left->type == tpReal || right->type == tpReal) { int cop = dbvmPowerReal; if (left->type == tpInteger) { left = int2real(left); } else if (left->type != tpReal) { error("operands of arithmetic operators should be of " "integer or real type", leftPos); } if (right->type == tpInteger) { cop = dbvmPowerRealInt; } else if (right->type != tpReal) { error("operands of arithmetic operator should be of " "integer or real type", rightPos); } left = new dbExprNode(cop, left, right); } else if (left->type == tpInteger && right->type == tpInteger) { left = new dbExprNode(dbvmPowerInt, left, right); } else { error("operands of arithmentic operator should be of " "integer or real type", rightPos); } } return left; } dbExprNode* dbCompiler::userDefinedOperator() { dbExprNode* left = term(); while (lex == tkn_ident) { dbUserFunction* func = dbUserFunction::find(name); if (func != NULL) { int nParams = func->getNumberOfParameters(); if (nParams != 2) { error("Only function with two arguments can be used as operator", currPos); } int rightPos = currPos; dbExprNode* right = term(); if ((left->type != tpInteger && left->type != tpReal && left->type != tpString && left->type != tpReference && left->type != tpRawBinary && left->type != tpBoolean) || (right->type != tpInteger && right->type != tpReal && right->type != tpString && right->type != tpReference && right->type != tpRawBinary && right->type != tpBoolean)) { error("User function should receive parameter of boolean, integer, real, string, reference or user defined type", rightPos); } left = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, left, right); } else { break; } } return left; } dbExprNode* dbCompiler::field(dbExprNode* expr, dbTableDescriptor* refTable, dbFieldDescriptor* fd) { int pos; while (true) { switch (lex) { case tkn_dot: pos = currPos; if (scan() != tkn_ident) { error("identifier expected", pos); } if (fd != NULL && fd->type == dbField::tpStructure) { if ((fd = fd->find(name)) == NULL) { error("Field not found"); } } else { assert(expr != NULL); if (expr->type != tpReference) { error("Left operand of '.' should be " "structure or reference", pos); } if (refTable == NULL) { refTable = fd->refTable; } if (refTable == NULL) { refTable = fd->refTable; } if (refTable == NULL || (fd = refTable->findSymbol(name)) == NULL) { error("Field not found"); } refTable = NULL; expr = new dbExprNode(dbvmDeref, expr); } break; case tkn_lbr: if (expr == NULL || (expr->type != tpArray && expr->type != tpString)) { error("Index can be applied only to arrays"); } else { dbExprNode* index = disjunction(); if (lex != tkn_rbr) { error("']' expected"); } if (index->type != tpInteger && index->type != tpFreeVar) { error("Index should have integer type"); } if (expr->type == tpString) { lex = scan(); return new dbExprNode(dbvmCharAt, expr, index); } if (fd == NULL) { // variable of array of reference type expr = new dbExprNode(dbvmGetAt,expr,index,sizeof(oid_t)); } else { if (refTable == NULL) { refTable = fd->refTable; } fd = fd->components; expr = new dbExprNode(dbvmGetAt, expr, index, fd->dbsSize); } } break; default: if (expr == NULL) { error("'.' expected"); } return expr; } if (fd == NULL) { expr = new dbExprNode(dbvmLoadReference, expr, 0); } else if (fd->type == dbField::tpRawBinary) { expr = new dbExprNode(expr != NULL ? dbvmLoadRawBinary : dbvmLoadSelfRawBinary, fd, expr); } else if (fd->type != dbField::tpStructure) { expr = new dbExprNode((expr != NULL ? dbvmLoadBool : dbvmLoadSelfBool) + fd->type, fd, expr); } lex = scan(); } } dbExprNode* dbCompiler::term() { dbFieldDescriptor* fd; dbTableDescriptor* refTable; int cop; int pos = currPos; dbBinding* bp; dbExprNode* expr = NULL; dbUserFunction* func; if ((cop = scan()) == tkn_where) { if (firstPos == 0) { firstPos = currPos; } cop = scan(); // just ignore 'where' keyword } switch (cop) { case tkn_eof: case tkn_order: case tkn_start: lex = cop; return new dbExprNode(dbvmVoid); case tkn_current: lex = scan(); return field(new dbExprNode(dbvmCurrent), table, NULL); case tkn_ident: for (bp = bindings; bp != NULL; bp = bp->next) { if (name == bp->name) { bp->used = true; lex = scan(); return new dbExprNode(dbvmVariable, (dbExprNode*)0, bp->index); } } if ((fd = table->findSymbol(name)) == NULL) { if ((func = dbUserFunction::find(name)) != NULL) { pos = currPos; expr = term(); int argType = func->getParameterType(); int nParams = func->getNumberOfParameters(); if (nParams > 1) { if (expr->type != tpList) { error("Too few paramters for user defined function"); } dbExprNode* params[3]; do { if (nParams == 0) { error("Too many parameters for user defined function"); } dbExprNode* param = expr->operand[1]; if (param->type != tpInteger && param->type != tpReal && param->type != tpString && param->type != tpReference && param->type != tpRawBinary && param->type != tpBoolean) { error("User function should receive parameters of boolean, integer, real, string, reference or user defined type", pos); } params[--nParams] = param; expr = expr->operand[0]; } while (expr != NULL); if (nParams != 0) { error("Too few parameters for user defined function"); } expr = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, params[0], params[1], params[2]); } else { if (argType == tpReal && expr->type == tpInteger) { expr = int2real(expr); } else if (argType == tpList) { if (expr->type != tpInteger && expr->type != tpReal && expr->type != tpString && expr->type != tpReference && expr->type != tpRawBinary && expr->type != tpBoolean) { error("User function should receive parameter of boolean, integer, real, string, reference or user defined type", pos); } } else if (argType != expr->type) { error("Incompatible function argument", pos); } expr = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, expr); } return field(expr, NULL, NULL); } error("Field not found", pos); } if (fd->type == dbField::tpRawBinary) { expr = new dbExprNode(dbvmLoadSelfRawBinary, fd); } else if (fd->type != dbField::tpStructure) { expr = new dbExprNode(dbvmLoadSelfBool + fd->type, fd); } lex = scan(); return field(expr, NULL, fd); case tkn_exists: if (scan() == tkn_ident) { dbBinding var; var.next = bindings; var.name = name; var.used = false; var.index = nFreeVars++; bindings = &var; if (nFreeVars >= maxFreeVars) { error("Too many nested EXISTS clauses\n"); } pos = currPos; if (scan() != tkn_col) { error("':' expected", pos); } expr = term(); if (expr->type != tpBoolean) { error("Expresion in EXISTS clause should be of boolean type"); } if (var.used) { expr = new dbExprNode(dbvmExists, expr, nFreeVars-1); } nFreeVars -= 1; assert(bindings == &var); bindings = var.next; return expr; } else { error("Free variable name expected"); } break; case tkn_first: lex = scan(); return field(new dbExprNode(dbvmFirst), table, NULL); case tkn_last: lex = scan(); return field(new dbExprNode(dbvmLast), table, NULL); case tkn_false: expr = new dbExprNode(dbvmLoadFalse); break; case tkn_true: expr = new dbExprNode(dbvmLoadTrue); break; case tkn_null: expr = new dbExprNode(dbvmLoadNull); break; case tkn_iconst: expr = new dbExprNode(dbvmLoadIntConstant, ivalue); break; case tkn_fconst: expr = new dbExprNode(dbvmLoadRealConstant, fvalue); break; case tkn_sconst: expr = new dbExprNode(dbvmLoadStringConstant, svalue); lex = scan(); return field(expr, NULL, NULL); case tkn_var: expr = new dbExprNode(dbvmLoadVarBool + varType - dbQueryElement::qVarBool, varPtr); refTable = varRefTable; lex = scan(); return field(expr, refTable, NULL); case tkn_abs: case tkn_length: case tkn_lower: case tkn_upper: case tkn_integer: case tkn_real: case tkn_string: pos = currPos; expr = term(); switch (cop) { case tkn_abs: if (expr->type == tpInteger) { cop = dbvmAbsInt; } else if (expr->type == tpReal) { cop = dbvmAbsReal; } else { error("ABS function can be applied only " "to integer or real expression", pos); } break; case tkn_length: if (expr->type == tpArray) { cop = dbvmLength; } else if (expr->type == tpString) { cop = dbvmStringLength; } else { error("LENGTH function is defined only for arrays and strings", pos); } break; case tkn_integer: if (expr->type == tpReal) { cop = dbvmRealToInt; } else { error("INTEGER function can be applied only to " "expression of real type", pos); } break; case tkn_real: if (expr->type == tpInteger) { cop = dbvmIntToReal; } else { error("REAL function can be applied only to " "expression of integer type", pos); } break; case tkn_string: if (expr->type == tpInteger) { cop = dbvmIntToString; } else if (expr->type == tpReal) { cop = dbvmRealToString; } else { error("STRING function can be applied only " "to integer or real expression", pos); } break; case tkn_lower: if (expr->type != tpString) { error("LOWER function can be applied only to string argument", pos); } else { cop = dbvmLowerString; } break; case tkn_upper: if (expr->type != tpString) { error("UPPER function can be applied only to string argument", pos); } else { cop = dbvmUpperString; } break; default: assert(false); } return field(new dbExprNode(cop, expr), NULL, NULL); case tkn_lpar: { expr = disjunction(); dbExprNode* list = NULL; while (lex == tkn_comma) { list = new dbExprNode(dbvmList, list, expr); expr = disjunction(); } if (lex != tkn_rpar) { error("')' expected"); } if (list != NULL) { expr = new dbExprNode(dbvmList, list, expr); } } break; case tkn_not: pos = currPos; expr = comparison(); if (expr->type == tpInteger) { if (expr->cop == dbvmLoadIntConstant) { expr->ivalue = ~expr->ivalue; } else { expr = new dbExprNode(dbvmNotInt, expr); } return expr; } else if (expr->type == tpBoolean) { return new dbExprNode(dbvmNotBool, expr); } else { error("NOT operator can be applied only to " "integer or boolean expressions", pos); } break; case tkn_add: error("Using of unary plus operator has no sence"); break; case tkn_sub: pos = currPos; expr = term(); if (expr->type == tpInteger) { if (expr->cop == dbvmLoadIntConstant) { expr->ivalue = -expr->ivalue; } else { expr = new dbExprNode(dbvmNegInt, expr); } return expr; } else if (expr->type == tpReal) { if (expr->cop == dbvmLoadRealConstant) { expr->fvalue = -expr->fvalue; } else { expr = new dbExprNode(dbvmNegReal, expr); } return expr; } else { error("Unary minus can be applied only to " "integer or real expressions", pos); } default: error("operand expected"); } lex = scan(); return expr; } void dbCompiler::error(const char* msg, int pos) { if (pos < 0) { if ((pos = currPos-1) < 0) { pos = 0; } } else if (pos < firstPos) { pos = firstPos; } if (pos + offsetWithinStatement >= 0) { pos += offsetWithinStatement; } table->db->handleError(dbDatabase::QueryError, msg, pos); longjmp(abortCompilation, dbDatabase::QueryError); } void dbCompiler::compileStartFollowPart(dbQuery& query) { if (lex != tkn_start) { return; } int pos = currPos; if (scan() != tkn_from) { error("FROM expected after START", pos); } pos = currPos; switch (scan()) { case tkn_first: query.startFrom = dbCompiledQuery::StartFromFirst; break; case tkn_last: query.startFrom = dbCompiledQuery::StartFromLast; break; case tkn_var: if (varType == dbQueryElement::qVarReference) { if (varRefTable != table) { error("Incompatiable type of reference variable"); } query.startFrom = dbCompiledQuery::StartFromRef; } else if (varType == dbQueryElement::qVarArrayOfRef) { if (varRefTable != table) { error("Incompatiable type of array of reference variable"); } query.startFrom = dbCompiledQuery::StartFromArray; } else if (varType == dbQueryElement::qVarArrayOfRefPtr) { if (varRefTable != table) { error("Incompatiable type of array of reference variable"); } query.startFrom = dbCompiledQuery::StartFromArrayPtr; } else { error("Reference or array of reference variable expected"); } query.root = varPtr; break; default: error("FIRST, LAST or reference varaible expected", pos); } if ((lex = scan()) == tkn_follow) { pos = currPos; if (scan() != tkn_by) { error("BY expected after FOLLOW", pos); } do { pos = currPos; if (scan() != tkn_ident) { error("Field name expected", pos); } dbFieldDescriptor* fd; if ((fd = table->findSymbol(name)) == NULL) { error("Field not found"); } while (fd->type == dbField::tpStructure) { pos = currPos; if (scan() != tkn_dot) { error("'.' expected", pos); } pos = currPos; if (scan() != tkn_ident) { error("Field name expected", pos); } if ((fd = fd->find(name)) == NULL) { error("Field not found"); } } if (!(fd->type == dbField::tpReference && fd->refTable == table) && !(fd->type == dbField::tpArray && fd->components->type == dbField::tpReference && fd->components->refTable == table)) { error("Follow field should be of compatibale reference " "or array of reference type"); } dbFollowByNode* node = new dbFollowByNode; node->field = fd; node->next = query.follow; // list should be inverted query.follow = node; } while ((lex = scan()) == tkn_comma); } } void dbCompiler::compileOrderByPart(dbQuery& query) { if (lex == tkn_order) { dbOrderByNode** opp = &query.order; int pos = currPos; if (scan() != tkn_by) { error("BY expected after ORDER", pos); } int parentheses = 0; do { pos = currPos; int tkn = scan(); if (tkn == tkn_lpar) { parentheses += 1; } else { ungetToken(tkn); } dbExprNode* expr = disjunction(); dbOrderByNode* node = new dbOrderByNode; switch (expr->cop) { case dbvmLoadSelfBool: case dbvmLoadSelfInt1: case dbvmLoadSelfInt2: case dbvmLoadSelfInt4: case dbvmLoadSelfInt8: case dbvmLoadSelfReal4: case dbvmLoadSelfReal8: case dbvmLoadSelfString: case dbvmLoadSelfArray: case dbvmLoadSelfReference: case dbvmLoadSelfRawBinary: assert(expr->ref.field != NULL); node->field = expr->ref.field; node->expr = NULL; break; case dbvmLength: if (expr->operand[0]->cop == dbvmLoadSelfArray) { node->field = expr->operand[0]->ref.field; node->expr = NULL; break; } // no break default: if (expr->type > tpReference) { error("Expressions in ORDER BY part should have scalar type", pos); } node->field = NULL; node->expr = expr; node->table = table; } node->ascent = true; *opp = node; opp = &node->next; *opp = NULL; if (lex == tkn_desc) { node->ascent = false; lex = scan(); } else if (lex == tkn_asc) { lex = scan(); } if (lex == tkn_rpar) { if (--parentheses < 0) { error("Unbalanced parentheses "); } lex = scan(); } } while (lex == tkn_comma); } } dbExprNode* dbCompiler::compileExpression(dbTableDescriptor* table, char const* expr, int startPos) { TRACE_MSG(("Compile expression %s for table %s\n", table->name)); if (setjmp(abortCompilation) == 0) { this->table = table; bindings = NULL; nFreeVars = 0; dbQueryElement elem(dbQueryElement::qExpression, expr, NULL); queryElement = &elem; currPos = firstPos = 0; offsetWithinStatement = startPos; return disjunction(); } else { return NULL; } } bool dbCompiler::compile(dbTableDescriptor* table, dbQuery& query) { TRACE_MSG(("Compile query for table %s\n", table->name)); query.destroy(); if (setjmp(abortCompilation) == 0) { this->table = table; bindings = NULL; nFreeVars = 0; queryElement = query.elements; currPos = firstPos = 0; hasToken = false; offsetWithinStatement = query.pos; dbExprNode* expr = disjunction(); if (expr->type != tpBoolean && expr->type != tpVoid) { table->db->handleError(dbDatabase::QueryError, "Conditional " "expression should have boolean type\n", 0); delete expr; return false; } compileStartFollowPart(query); compileOrderByPart(query); query.tree = expr; query.table = table; return true; } else { for (dbOrderByNode *op = query.order, *nop; op != NULL; op = nop) { nop = op->next; delete op; } for (dbFollowByNode *fp = query.follow, *nfp; fp != NULL; fp = nfp) { nfp = fp->next; delete fp; } return false; } } dbCompiler::dbCompiler() { static struct { const char* name; int tag; } keywords[] = { {"all", tkn_all}, {"abs", tkn_abs}, {"and", tkn_and}, {"asc", tkn_asc}, {"between", tkn_between}, {"by", tkn_by}, {"current", tkn_current}, {"desc", tkn_desc}, {"escape", tkn_escape}, {"exists", tkn_exists}, {"first", tkn_first}, {"false", tkn_false}, {"follow", tkn_follow}, {"from", tkn_from}, {"in", tkn_in}, {"is", tkn_is}, {"integer", tkn_integer}, {"insert", tkn_insert}, {"into", tkn_into}, {"last", tkn_last}, {"length", tkn_length}, {"like", tkn_like}, {"lower", tkn_lower}, {"not", tkn_not}, {"null", tkn_null}, {"or", tkn_or}, {"order", tkn_order}, {"real", tkn_real}, {"select", tkn_select}, {"start", tkn_start}, {"string", tkn_string}, {"table", tkn_table}, {"true", tkn_true}, {"upper", tkn_upper}, {"where", tkn_where} }; if (!initialized) { for (unsigned i = 0; i < itemsof(keywords); i++) { dbSymbolTable::add((char*&)keywords[i].name, keywords[i].tag, FASTDB_CLONE_ANY_IDENTIFIER); } initialized = true; } } void dbInheritedAttribute::removeTemporaries() { for (dbStringValue *next, *s = tempStrings; s != NULL; s = next) { next = s->next; delete s; } } static void stderrTrace(char* msg) { fputs(msg, stderr); } dbTraceFunctionPtr dbTraceFunction = stderrTrace; void dbTrace(char* message, ...) { va_list args; va_start (args, message); char buffer[1024]; vsprintf(buffer, message, args); (*dbTraceFunction)(buffer); va_end(args); } byte* dbMalloc(size_t size) { #ifdef __BORLANDC__ // malloc from Borland runtime library returns value aligned only on 4 // And GigaBASE needs alignment on maximal size of scalar field = 8 byte* p = (byte*)malloc(size+8); byte* q = (byte*)(DOALIGN((long)p + 4, 8)); *((int*)q-1) = q - p; return q; #else byte* p = (byte*)malloc(size); assert(((long)p & 7) == 0); return p; #endif } void dbFree(void* p) { if (p != NULL) { #ifdef __BORLANDC__ free((byte*)p - *((int*)p-1)); #else free(p); #endif } }