QgsSqlExpressionCompiler::Result QgsOracleExpressionCompiler::compileNode( const QgsExpressionNode *node, QString &result ) { if ( node->nodeType() == QgsExpressionNode::ntBinaryOperator ) { const QgsExpressionNodeBinaryOperator *bin( static_cast<const QgsExpressionNodeBinaryOperator *>( node ) ); switch ( bin->op() ) { case QgsExpressionNodeBinaryOperator::boConcat: // oracle's handling of || WRT null is not standards compliant return Fail; case QgsExpressionNodeBinaryOperator::boPow: case QgsExpressionNodeBinaryOperator::boRegexp: case QgsExpressionNodeBinaryOperator::boILike: case QgsExpressionNodeBinaryOperator::boNotILike: case QgsExpressionNodeBinaryOperator::boMod: { QString op1, op2; if ( compileNode( bin->opLeft(), op1 ) != Complete || compileNode( bin->opRight(), op2 ) != Complete ) return Fail; switch ( bin->op() ) { case QgsExpressionNodeBinaryOperator::boPow: result = QString( "power(%1,%2)" ).arg( op1, op2 ); return Complete; case QgsExpressionNodeBinaryOperator::boRegexp: result = QString( "regexp_like(%1,%2)" ).arg( op1, op2 ); return Complete; case QgsExpressionNodeBinaryOperator::boILike: result = QString( "lower(%1) LIKE lower(%2)" ).arg( op1, op2 ); return Complete; case QgsExpressionNodeBinaryOperator::boNotILike: result = QString( "NOT lower(%1) LIKE lower(%2)" ).arg( op1, op2 ); return Complete; case QgsExpressionNodeBinaryOperator::boMod : result = QString( "MOD(%1,%2)" ).arg( op1, op2 ); return Complete; default: break; } } default: break; } } //fallback to default handling return QgsSqlExpressionCompiler::compileNode( node, result ); }
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compile( const QgsExpression* exp ) { if ( exp->rootNode() ) return compileNode( exp->rootNode(), mResult ); else return Fail; }
void DFABytecodeCompiler::compile() { ASSERT(!m_bytecode.size()); m_nodeStartOffsets.resize(m_dfa.size()); // Make sure the root is always at the beginning of the bytecode. compileNode(m_dfa.root()); for (unsigned i = 0; i < m_dfa.size(); i++) { if (i != m_dfa.root()) compileNode(i); } // Link. for (const auto& linkRecord : m_linkRecords) set32Bits(m_bytecode, linkRecord.first, m_nodeStartOffsets[linkRecord.second]); }
QgsSqlExpressionCompiler::Result QgsOracleExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result ) { if ( node->nodeType() == QgsExpression::ntBinaryOperator ) { const QgsExpression::NodeBinaryOperator *bin( static_cast<const QgsExpression::NodeBinaryOperator*>( node ) ); switch ( bin->op() ) { case QgsExpression::boPow: case QgsExpression::boRegexp: { QString op1, op2; if ( compileNode( bin->opLeft(), op1 ) != Complete || compileNode( bin->opRight(), op2 ) != Complete ) return Fail; switch ( bin->op() ) { case QgsExpression::boPow: result = QString( "power(%1,%2)" ).arg( op1, op2 ); return Complete; case QgsExpression::boRegexp: result = QString( "regexp_like(%1,%2)" ).arg( op1, op2 ); return Complete; default: break; } } default: break; } } //fallback to default handling return QgsSqlExpressionCompiler::compileNode( node, result ); }
QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result ) { switch ( node->nodeType() ) { case QgsExpression::ntUnaryOperator: { const QgsExpression::NodeUnaryOperator* n = static_cast<const QgsExpression::NodeUnaryOperator*>( node ); switch ( n->op() ) { case QgsExpression::uoNot: { QString right; if ( compileNode( n->operand(), right ) == Complete ) { result = "( NOT " + right + ')'; return Complete; } return Fail; } case QgsExpression::uoMinus: { if ( mFlags.testFlag( NoUnaryMinus ) ) return Fail; QString right; if ( compileNode( n->operand(), right ) == Complete ) { result = "( - (" + right + "))"; return Complete; } return Fail; } } break; } case QgsExpression::ntBinaryOperator: { const QgsExpression::NodeBinaryOperator* n = static_cast<const QgsExpression::NodeBinaryOperator*>( node ); QString op; bool partialCompilation = false; bool failOnPartialNode = false; switch ( n->op() ) { case QgsExpression::boEQ: if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->opLeft()->nodeType() == QgsExpression::ntColumnRef && n->opRight()->nodeType() == QgsExpression::ntColumnRef ) { // equality between column refs results in a partial compilation, since provider is performing // case-insensitive matches between strings partialCompilation = true; } op = QStringLiteral( "=" ); break; case QgsExpression::boGE: op = QStringLiteral( ">=" ); break; case QgsExpression::boGT: op = QStringLiteral( ">" ); break; case QgsExpression::boLE: op = QStringLiteral( "<=" ); break; case QgsExpression::boLT: op = QStringLiteral( "<" ); break; case QgsExpression::boIs: op = QStringLiteral( "IS" ); break; case QgsExpression::boIsNot: op = QStringLiteral( "IS NOT" ); failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch ); break; case QgsExpression::boLike: op = QStringLiteral( "LIKE" ); partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive ); break; case QgsExpression::boILike: if ( mFlags.testFlag( LikeIsCaseInsensitive ) ) op = QStringLiteral( "LIKE" ); else op = QStringLiteral( "ILIKE" ); break; case QgsExpression::boNotLike: op = QStringLiteral( "NOT LIKE" ); partialCompilation = mFlags.testFlag( LikeIsCaseInsensitive ); failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch ); break; case QgsExpression::boNotILike: failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch ); if ( mFlags.testFlag( LikeIsCaseInsensitive ) ) op = QStringLiteral( "NOT LIKE" ); else op = QStringLiteral( "NOT ILIKE" ); break; case QgsExpression::boOr: if ( mFlags.testFlag( NoNullInBooleanLogic ) ) { if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) ) return Fail; } op = QStringLiteral( "OR" ); break; case QgsExpression::boAnd: if ( mFlags.testFlag( NoNullInBooleanLogic ) ) { if ( nodeIsNullLiteral( n->opLeft() ) || nodeIsNullLiteral( n->opRight() ) ) return Fail; } op = QStringLiteral( "AND" ); break; case QgsExpression::boNE: failOnPartialNode = mFlags.testFlag( CaseInsensitiveStringMatch ); op = QStringLiteral( "<>" ); break; case QgsExpression::boMul: op = QStringLiteral( "*" ); break; case QgsExpression::boPlus: op = QStringLiteral( "+" ); break; case QgsExpression::boMinus: op = QStringLiteral( "-" ); break; case QgsExpression::boDiv: return Fail; // handle cast to real case QgsExpression::boMod: op = QStringLiteral( "%" ); break; case QgsExpression::boConcat: op = QStringLiteral( "||" ); break; case QgsExpression::boIntDiv: return Fail; // handle cast to int case QgsExpression::boPow: op = QStringLiteral( "^" ); break; case QgsExpression::boRegexp: op = QStringLiteral( "~" ); break; } if ( op.isNull() ) return Fail; QString left; Result lr( compileNode( n->opLeft(), left ) ); QString right; Result rr( compileNode( n->opRight(), right ) ); if ( failOnPartialNode && ( lr == Partial || rr == Partial ) ) return Fail; result = '(' + left + ' ' + op + ' ' + right + ')'; if ( lr == Complete && rr == Complete ) return ( partialCompilation ? Partial : Complete ); else if (( lr == Partial && rr == Complete ) || ( lr == Complete && rr == Partial ) || ( lr == Partial && rr == Partial ) ) return Partial; else return Fail; } case QgsExpression::ntLiteral: { const QgsExpression::NodeLiteral* n = static_cast<const QgsExpression::NodeLiteral*>( node ); bool ok = false; if ( mFlags.testFlag( CaseInsensitiveStringMatch ) && n->value().type() == QVariant::String ) { // provider uses case insensitive matching, so if literal was a string then we only have a Partial compilation and need to // double check results using QGIS' expression engine result = quotedValue( n->value(), ok ); return ok ? Partial : Fail; } else { result = quotedValue( n->value(), ok ); return ok ? Complete : Fail; } } case QgsExpression::ntColumnRef: { const QgsExpression::NodeColumnRef* n = static_cast<const QgsExpression::NodeColumnRef*>( node ); if ( mFields.indexFromName( n->name() ) == -1 ) // Not a provider field return Fail; result = quotedIdentifier( n->name() ); return Complete; } case QgsExpression::ntInOperator: { const QgsExpression::NodeInOperator* n = static_cast<const QgsExpression::NodeInOperator*>( node ); QStringList list; Result inResult = Complete; Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() ) { QString s; Result r = compileNode( ln, s ); if ( r == Complete || r == Partial ) { list << s; if ( r == Partial ) inResult = Partial; } else return r; } QString nd; Result rn = compileNode( n->node(), nd ); if ( rn != Complete && rn != Partial ) return rn; result = QStringLiteral( "%1 %2IN(%3)" ).arg( nd, n->isNotIn() ? "NOT " : "", list.join( QStringLiteral( "," ) ) ); return ( inResult == Partial || rn == Partial ) ? Partial : Complete; } case QgsExpression::ntFunction: case QgsExpression::ntCondition: break; } return Fail; }
QgsSqlExpressionCompiler::Result QgsDb2ExpressionCompiler::compileNode( const QgsExpressionNode *node, QString &result ) { QgsDebugMsg( QStringLiteral( "nodeType: %1" ).arg( nodeType( node ) ) ); if ( node->nodeType() == QgsExpressionNode::ntColumnRef ) { const QgsExpressionNodeColumnRef *n( static_cast<const QgsExpressionNodeColumnRef *>( node ) ); QgsDebugMsg( QStringLiteral( "column ref node: " ) + n->dump() ); // TODO - consider escaped names - not sure how to handle QString upperName = n->name().toUpper(); int idx = mFields.indexFromName( upperName ); QgsDebugMsg( QStringLiteral( "%1 - %2" ).arg( idx ).arg( upperName ) ); if ( idx > -1 ) { result = upperName; QgsDebugMsg( QStringLiteral( "return Complete" ) ); return Complete; } QgsDebugMsg( QStringLiteral( "return Fail" ) ); return Fail; } // Seemed necessary in initial Python testing but can't identify failing case now #if 0 if ( node->nodeType() == QgsExpressionNode::ntLiteral ) { const QgsExpression::NodeLiteral *n = static_cast<const QgsExpression::NodeLiteral *>( node ); bool ok = false; if ( n->dump().toUpper() == "NULL" ) // expression compiler doesn't handle this correctly { result = "NULL"; ok = true; } else { result = quotedValue( n->value(), ok ); } QgsDebugMsg( QStringLiteral( "ok: %1; literal node: " ).arg( ok ) + n->value().toString() + "; result: " + result ); QgsDebugMsg( QStringLiteral( "n->dump: " ) + n->dump() ); QgsDebugMsg( QStringLiteral( "type: %1; typeName: %2" ).arg( n->value().type() ).arg( n->value().typeName() ) ); if ( ok ) { QgsDebugMsg( QStringLiteral( "return Complete" ) ); return Complete; } else { QgsDebugMsg( QStringLiteral( "return Fail" ) ); return Fail; } } #endif if ( node->nodeType() == QgsExpressionNode::ntUnaryOperator ) { const QgsExpressionNodeUnaryOperator *n = static_cast<const QgsExpressionNodeUnaryOperator *>( node ); Result rr = Fail; switch ( n->op() ) { case QgsExpressionNodeUnaryOperator::uoNot: rr = compileNode( n->operand(), result ); if ( "NULL" == result.toUpper() ) { result.clear(); return Fail; } result = "NOT " + result; QgsDebugMsg( QStringLiteral( "NOT; result: %1; right: %2" ).arg( resultType( rr ), result ) ); return rr; case QgsExpressionNodeUnaryOperator::uoMinus: break; } } if ( node->nodeType() == QgsExpressionNode::ntBinaryOperator ) { const QgsExpressionNodeBinaryOperator *bin( static_cast<const QgsExpressionNodeBinaryOperator *>( node ) ); QString left, right; Result lr = compileNode( bin->opLeft(), left ); Result rr = compileNode( bin->opRight(), right ); Result compileResult; QgsDebugMsg( "left: '" + left + "'; right: '" + right + QString( "'; op: %1; lr: %2; rr: %3" ).arg( bin->op() ).arg( lr ).arg( rr ) ); if ( lr == Fail || rr == Fail ) return Fail; // NULL can not appear on the left, only as part of IS NULL or IS NOT NULL if ( "NULL" == left.toUpper() ) return Fail; // NULL can only be on the right for IS and IS NOT if ( "NULL" == right.toUpper() && ( bin->op() != QgsExpressionNodeBinaryOperator::boIs && bin->op() != QgsExpressionNodeBinaryOperator::boIsNot ) ) return Fail; switch ( bin->op() ) { case QgsExpressionNodeBinaryOperator::boMod: result = QStringLiteral( "MOD(%1,%2)" ).arg( left, right ); compileResult = ( lr == Partial || rr == Partial ) ? Partial : Complete; QgsDebugMsg( QStringLiteral( "MOD compile status: %1" ).arg( compileResult ) + "; " + result ); return compileResult; case QgsExpressionNodeBinaryOperator::boPow: result = QStringLiteral( "power(%1,%2)" ).arg( left, right ); compileResult = ( lr == Partial || rr == Partial ) ? Partial : Complete; QgsDebugMsg( QStringLiteral( "POWER compile status: %1" ).arg( compileResult ) + "; " + result ); return compileResult; case QgsExpressionNodeBinaryOperator::boRegexp: return Fail; //not supported, regexp syntax is too different to Qt case QgsExpressionNodeBinaryOperator::boConcat: result = QStringLiteral( "%1 || %2" ).arg( left, right ); compileResult = ( lr == Partial || rr == Partial ) ? Partial : Complete; QgsDebugMsg( QStringLiteral( "CONCAT compile status: %1" ).arg( compileResult ) + "; " + result ); return compileResult; case QgsExpressionNodeBinaryOperator::boILike: QgsDebugMsg( QStringLiteral( "ILIKE is not supported by DB2" ) ); return Fail; /* result = QString( "%1 LIKE %2" ).arg( left, right ); compileResult = (lr == Partial || rr == Partial) ? Partial : Complete; QgsDebugMsg(QString("ILIKE compile status: %1").arg(compileResult) + "; " + result); return compileResult; */ case QgsExpressionNodeBinaryOperator::boNotILike: QgsDebugMsg( QStringLiteral( "NOT ILIKE is not supported by DB2" ) ); return Fail; /* result = QString( "%1 NOT LIKE %2" ).arg( left, right ); compileResult = (lr == Partial || rr == Partial) ? Partial : Complete; QgsDebugMsg(QString("NOT ILIKE compile status: %1").arg(compileResult) + "; " + result); return compileResult; */ // We only support IS NULL if the operand on the left is a column case QgsExpressionNodeBinaryOperator::boIs: if ( "NULL" == right.toUpper() ) { if ( bin->opLeft()->nodeType() != QgsExpressionNode::ntColumnRef ) { QgsDebugMsg( "Failing IS NULL with non-column on left: " + left ); return Fail; } } break; // We only support IS NULL if the operand on the left is a column case QgsExpressionNodeBinaryOperator::boIsNot: if ( "NULL" == right.toUpper() ) { if ( bin->opLeft()->nodeType() != QgsExpressionNode::ntColumnRef ) { QgsDebugMsg( "Failing IS NOT NULL with non-column on left: " + left ); return Fail; } } break; default: break; } } //fallback to default handling QgsDebugMsg( QStringLiteral( "fallback: %1 - " ).arg( nodeType( node ) ) ); QgsSqlExpressionCompiler::Result rc = QgsSqlExpressionCompiler::compileNode( node, result ); QgsDebugMsg( QStringLiteral( "fallback: %1 - " ).arg( resultType( rc ) ) + result ); return rc; }