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;
}
Exemple #3
0
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;
}