IslandOfCode / jVTLlib

A simple VTL (Validation and Transformation Language) Interpreter in java library form. Still in development, but usable for BIRD 1.0 database instruction.
Apache License 2.0
0 stars 2 forks source link

Clausola CALC ignora multiple condizioni #5

Closed IslandOfCode closed 6 years ago

IslandOfCode commented 6 years ago

Questa clausola ha un comportamento anomalo quando gli si viene specificata più di una condizione da verificare.

Ipotiziamo di avere un DataSet con due colonne: A (Integer) e B (Stringa). Se applichiamo una condizione singola, come: filter(A>0) oppure filter(B<>null) non ci sono problemi.

Se invece applico due condizioni: filter(A>0, B<>null) ritorna quasi tutte le righe.

Dico quasi perchè sembra che alcune vengano comunque scartate.

IslandOfCode commented 6 years ago

Il problema sembra essere causato da un errore nel meccanismo di gestione delle condizioni multiple. Il metodo visitClauseFilter si occupa di eseguire la clausola filter, usando un doppio ciclo for:

Questo secondo ciclio è qui di seguito evidenziato

for(int e=0;e<ctx.expr().size();e++) {
    VTLObj ret = this.visit(ctx.expr(e));
    if(ret==null)
        continue;

    if(ret instanceof Scalar) { //se  uno scalare
        Scalar s = (Scalar)ret;
        if(s.getScalarType().equals(Scalar.SCALARTYPE.Boolean)) {
            if(s.asBoolean()) {// true
                nds.setPoint(dp);
            }
            continue;
        } 
    } 
throw new IllegalArgumentException("FILTER need a boolean result for the expression.");
}

Si noti l'if più interno:

if(s.asBoolean()) {// true
    nds.setPoint(dp);
}

Palesemente non va bene, perchè così basta che la prima condizione sia vera per creare un'ambiguità nel resto del codice.

Si conseguenza si va a semplificare il codice, andando a cercare una condizione che ritorni false. In quel caso il Datapoint viene scartato. Se invece tutte le condizioni ritornano true, allora si aggiunge il Datapoint al DataSet risultate.

La correzzione in un seguente commento.

IslandOfCode commented 6 years ago

Allora, prima di tutto creiamo una variabile booleano condition settata a true di default. Poi si passa a valutare condizione per condizione. Se una di esse è false (o per qualche motivo a ritornato un null), allora si spezza il ciclo.

if(ret instanceof Scalar) { //se  uno scalare
    Scalar s = (Scalar)ret;
    if(s.getScalarType().equals(Scalar.SCALARTYPE.Boolean)) { //se  un booleano
        if(!s.asBoolean()) {// false
            condition = false;
            break;
        }
        //vado alla condizione successiva
        continue;
    } 
}

Subito dopo il ciclo for, un semplice if controlla se condition è uguale a true, nel qual caso inserisce il Datapoint nel Dataset. In caso contrario, passa al successivo DataPoint.

if(condition) {
    nds.setPoint(dp);
}

Adesso la clausola filter può gestire tutte le condizioni che si vuole, basta ricordarsi che esse vanno considerate tutte in AND tra di loro!