Druid SQLExpr: Sistema Completo de Expressões SQL e Hierarquia de Classes

Introdução ao SQLExpr

O SQLExpr no Druid é a interface raiz para todas as representações de expresões SQL. Ele encapsula desde campos simples até subconsultas complexas, condições e operações.

Hierarquia de Herança do SQLExpr

A estrutura de classes segue um padrão onde SQLExpr serve como base para todos os tipos de expressões:

SQLObject
  └── SQLExpr
       ├── SQLIdentifierExpr          // Identificadores como colunas
       ├── SQLIntegerExpr, SQLCharExpr, SQLNullExpr, SQLDecimalExpr // Literais
       ├── SQLBinaryOpExpr            // Operações binárias (ex: =, >, AND)
       ├── SQLUnaryExpr               // Operações unárias (ex: NOT, -)
       ├── SQLInExpr                  // Listagem IN
       ├── SQLInSubQueryExpr          // IN com subconsulta
       ├── SQLExistsExpr              // Cláusula EXISTS
       ├── SQLAnyExpr, SQLAllExpr     // Quantificadores ANY/ALL
       ├── SQLBetweenExpr             // BETWEEN
       ├── SQLMethodInvokeExpr        // Chamadas de funções
       ├── SQLCaseExpr                // Expressões CASE
       ├── SQLArrayExpr               // Arrays
       ├── SQLVariantRefExpr          // Variáveis ou placeholders
       └── SQLPropertyExpr            // Propriedades com prefixo (ex: alias.campo)

Subclasses Principais e Exemplos

SQLIdentifierExpr

Representa nomes de campos ou tabelas. Exemplo básico:

SQLIdentifierExpr campoId = new SQLIdentifierExpr("user_id");

Expressões Literais

SQLIntegerExpr numero = new SQLIntegerExpr(42);
SQLCharExpr texto = new SQLCharExpr("Exemplo");
SQLNullExpr nulo = SQLNullExpr.INSTANCE;
SQLDecimalExpr decimal = new SQLDecimalExpr("3.14");

SQLBinaryOpExpr

Usado para operações como comparações e lógica. Exemplo alterado:

SQLBinaryOpExpr condicao = new SQLBinaryOpExpr(
    new SQLIdentifierExpr("idade"),
    SQLBinaryOperator.GreaterThanOrEquals,
    new SQLIntegerExpr(25)
);

SQLUnaryExpr

Operações unárias como negação:

SQLUnaryExpr negacao = new SQLUnaryExpr(
    SQLUnaryOperator.Not,
    new SQLIdentifierExpr("ativo")
);

SQLInExpr

Expressão IN com lista de valores:

SQLInExpr listaIn = new SQLInExpr();
listaIn.setExpr(new SQLIdentifierExpr("categoria"));
listaIn.addItem(new SQLCharExpr("A"));
listaIn.addItem(new SQLCharExpr("B"));

SQLInSubQueryExpr

IN com subconsulta interna. Atributos: expr (campo esquerdo) e subQuery (subconsulta).

SQLInSubQueryExpr inSubConsulta = new SQLInSubQueryExpr();
inSubConsulta.setExpr(new SQLIdentifierExpr("produto_id"));
inSubConsulta.setSubQuery(consultaFilha); // Objeto SQLSelect representando a subconsulta

SQLExistsExpr

Representa EXISTS ou NOT EXISTS. O atributo not controla a negação.

SQLExistsExpr existe = new SQLExistsExpr();
existe.setSubQuery(outraConsulta);
existe.setNot(true); // NOT EXISTS

Outras Subclasses

Exemplos simplificados para variedade:

// SQLAnyExpr: idade > ANY (SELECT idade FROM usuarios)
SQLAnyExpr anyExpr = new SQLAnyExpr(consultaUsuarios);

// SQLBetweenExpr: salario BETWEEN 3000 AND 8000
SQLBetweenExpr entre = new SQLBetweenExpr(
    new SQLIdentifierExpr("salario"),
    new SQLIntegerExpr(3000),
    new SQLIntegerExpr(8000)
);

// SQLMethodInvokeExpr: SUM(vendas)
SQLMethodInvokeExpr soma = new SQLMethodInvokeExpr("SUM");
soma.addParam(new SQLIdentifierExpr("vendas"));

// SQLCaseExpr: CASE WHEN status='ativo' THEN 'Sim' ELSE 'Não' END
SQLCaseExpr caso = new SQLCaseExpr();
caso.addWhenNode(
    SQLUtils.toSQLExpr("status='ativo'"),
    new SQLCharExpr("Sim")
);
caso.setElseExpr(new SQLCharExpr("Não"));

SQLPropertyExpr

Campo com prefixo de tabela ou alias. Demonstração com análise de SQL:

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.util.JdbcConstants;

public class AnalisadorPropriedades {
    public static void main(String[] args) {
        String sql = "SELECT t.codigo, t.descricao, preco FROM produtos t WHERE t.id = 5 AND ativo = 1";
        SQLStatement declaracao = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
        SQLSelectStatement selectDecl = (SQLSelectStatement) declaracao;
        SQLSelectQueryBlock bloco = selectDecl.getSelect().getQueryBlock();

        System.out.println("Campos no SELECT:");
        for (SQLSelectItem item : bloco.getSelectList()) {
            extrairCampo(item.getExpr());
        }

        System.out.println("\nCondições no WHERE:");
        SQLExpr ondeExpr = bloco.getWhere();
        if (ondeExpr instanceof SQLBinaryOpExpr) {
            percorrerBinario((SQLBinaryOpExpr) ondeExpr);
        }
    }

    private static void extrairCampo(SQLExpr expr) {
        if (expr instanceof SQLPropertyExpr) {
            SQLPropertyExpr prop = (SQLPropertyExpr) expr;
            System.out.println("Campo com prefixo: " + prop.getOwnerName() + "." + prop.getName());
        } else if (expr instanceof SQLIdentifierExpr) {
            SQLIdentifierExpr id = (SQLIdentifierExpr) expr;
            System.out.println("Campo simples: " + id.getName());
        }
    }

    private static void percorrerBinario(SQLBinaryOpExpr binario) {
        if (binario.getLeft() instanceof SQLBinaryOpExpr) {
            percorrerBinario((SQLBinaryOpExpr) binario.getLeft());
        } else {
            extrairCampo(binario.getLeft());
        }
        if (binario.getRight() instanceof SQLBinaryOpExpr) {
            percorrerBinario((SQLBinaryOpExpr) binario.getRight());
        } else {
            extrairCampo(binario.getRight());
        }
    }
}

Saída esperada:

Campo com prefixo: t.codigo
Campo com prefixo: t.descricao
Campo simples: preco

Campo com prefixo: t.id
Campo simples: ativo

Exemplos Avançados

Parse de SQLInSubQueryExpr

String consulta = "SELECT * FROM pedidos WHERE cliente_id IN (SELECT id FROM clientes WHERE ativo=1)";
SQLStatement decl = SQLUtils.parseSingleStatement(consulta, JdbcConstants.MYSQL);
SQLSelectQueryBlock bloco = ((SQLSelectStatement) decl).getSelect().getQueryBlock();
SQLExpr condicao = bloco.getWhere();

if (condicao instanceof SQLInSubQueryExpr) {
    SQLInSubQueryExpr inSub = (SQLInSubQueryExpr) condicao;
    System.out.println("Campo esquerdo: " + inSub.getExpr());
    System.out.println("Subconsulta: " + inSub.getSubQuery());
}

Parse de SQLExistsExpr

String sqlExists = "SELECT * FROM funcionarios WHERE EXISTS (SELECT 1 FROM projetos WHERE projetos.lider = funcionarios.id)";
SQLStatement stmtExists = SQLUtils.parseSingleStatement(sqlExists, JdbcConstants.MYSQL);
SQLExpr exprWhere = ((SQLSelectStatement) stmtExists).getSelect().getQueryBlock().getWhere();

if (exprWhere instanceof SQLExistsExpr) {
    SQLExistsExpr existeExpr = (SQLExistsExpr) exprWhere;
    System.out.println("Subconsulta: " + existeExpr.getSubQuery());
    System.out.println("Negado: " + existeExpr.isNot());
}

Construção de Condições Complexas

SQLExpr condA = SQLUtils.toSQLExpr("departamento IN (10,20)");
SQLExpr condB = SQLUtils.toSQLExpr("EXISTS (SELECT 1 FROM avaliacoes WHERE avaliacoes.func = funcionario.id)");
SQLExpr condicaoCompleta = SQLBinaryOpExpr.and(condA, condB);
System.out.println("Condição gerada: " + SQLUtils.toSQLString(condicaoCompleta));

Verificação de Tipo de Expressão

Um método utilitário para identificar o tipo de uma expressão:

public static void classificarExpressao(SQLExpr expr) {
    if (expr instanceof SQLIdentifierExpr) {
        System.out.println("Tipo: Identificador");
    } else if (expr instanceof SQLInSubQueryExpr) {
        System.out.println("Tipo: IN com Subconsulta");
    } else if (expr instanceof SQLExistsExpr) {
        System.out.println("Tipo: EXISTS");
    } else if (expr instanceof SQLInExpr) {
        System.out.println("Tipo: IN com Lista");
    } else if (expr instanceof SQLBinaryOpExpr) {
        System.out.println("Tipo: Operação Binária");
    } else if (expr instanceof SQLSubqueryExpr) {
        System.out.println("Tipo: Subconsulta Genérica");
    } else if (expr instanceof SQLCaseExpr) {
        System.out.println("Tipo: Expressão CASE");
    } else {
        System.out.println("Tipo Desconhecido: " + expr.getClass().getSimpleName());
    }
}

Tags: Druid SQLParser java SQLExpressions DatabaseAbstraction

Publicado em 6-26 02:12