Formattazione di SQL in base a un AST

6

Sto lavorando al mio progetto .NET open source per generando SQL ...

Inizialmente, ho scritto il progetto in modo da sputare esattamente un formato SQL ... brutto. Ho iniziato a utilizzare questo progetto nel mondo reale e ho deciso che sarebbe stato bello poter generare abbastanza SQL in modalità di debug e brutto SQL in modalità di rilascio.

Il mio primo tentativo ha coinvolto molti loop if / else all'interno del mio codice di generazione dei comandi. In breve tempo doveva essere troppo da gestire. Così, ho iniziato a cercare un altro approccio che mi permettesse di inserire diverse strategie di formattazione. Ho anche guardato altre librerie di formattazione SQL, ma la maggior parte erano specifiche per un dialetto.

In questo momento, sto giocando con l'idea di generare un albero di token, simile a un albero sintattico astratto (AST) con cui lavora un compilatore. Speravo di navigare nell'albero e generare testo in base a dove sono nell'albero. Ora che ho costruito questi alberi, non sono sicuro di come scrivere il codice per visitare questi nodi.

Mi sento come se stessi andando indietro. Invece di analizzare SQL, generare un AST e sputare oggetti SQL, sto permettendo a qualcuno di creare oggetti SQL, quindi generare un AST e sputare fuori SQL. Quando qualcuno usa la mia libreria per costruire un'istruzione SELECT, specificano i valori da selezionare, le tabelle da cui selezionare, i filtri, l'ordinamento, ecc. Ma una volta generato un AST ... Ho meno informazioni di quelle originariamente iniziate con! Solo chiedendo "Il prossimo token è la parola chiave FROM?" posso avere una foto di dove sono. Devo scrivere un sacco di istruzioni if / elif / else per capire cosa verrà dopo. Almeno quando avevo gli oggetti SQL, potevo usare il polimorfismo per evitare brutte ramificazioni.

Quindi, forse ho perso un giorno nella teoria del compilatore o forse è perché sto cercando di tornare indietro. Ma, per qualunque ragione, il mio prossimo passo non mi è chiaro. Penso che il percorso AST fosse nella giusta direzione anche se ciò significasse perdere informazioni. Sono stato in grado di scrivere un formatter veramente stupido che mette gli spazi tra ogni token per ora. E 'davvero quando si tratta di avere fantasia che sono sopraffatto.

Se qualcuno ha delle buone risorse per tornare indietro, sarebbe probabilmente molto utile.

    
posta Travis Parks 30.01.2013 - 03:03
fonte

2 risposte

2

Ho avuto la possibilità di guardare il tuo codice attuale. Sembra che tu sia la maggior parte del modo lì, ma questo concetto di "albero dei gettoni" è di intralcio. Probabilmente non ne hai affatto bisogno (in particolare, mi riferisco alle cose nella cartella Expressions/ ).

Ad esempio, in Join , hai un metodo chiamato GetDeclarationExpression che restituisce un IExpressionItem che è un albero di token. Quello che sto suggerendo è che puoi semplificarlo e invece restituire direttamente un String che è l'attuale SQL generato dal join. In termini concreti, qualcosa di simile al seguente:

    String IJoinItem.GetDeclarationExpression(CommandOptions options)
    {
        // [ "(" ] <Left> <Combiner> <Right> [ "ON" <Filter> ] [ ")" ]
        StringBuilder expression = new StringBuilder();
        if (WrapInParentheses ?? options.WrapJoinsInParentheses)
        {
            expression.Append("(");
        }
        expression.Append(_leftHand.GetDeclarationExpression(options));
        expression.Append(" ")
        expression.Append(GetJoinNameExpression(options));
        expression.Append(" ")
        expression.Append(_rightHand.GetDeclarationExpression(options));
        expression.Append(" ")
        expression.Append(GetOnExpression(options));
        if (WrapInParentheses ?? options.WrapJoinsInParentheses)
        {
            expression.Append(")");
        }
        return expression.ToString();
    }

Naturalmente, tutti gli altri metodi utilizzati durante l'attraversamento di AST dovrebbero anche essere modificati per restituire String invece di IExpressionItem come necessario. Ogni metodo chiamato durante l'attraversamento dell'albero sarebbe responsabile della generazione della stringa SQL effettiva, che alla fine verrà concatenata insieme.

    
risposta data 30.01.2013 - 19:08
fonte
1

Hai considerato di avere un formattatore separato per ogni tipo di espressione? Potresti avere un select-statement-formatter , un column-list-formatter , un from-clause-formatter , ecc. Non so in modo specifico come sia strutturato il tuo AST, ma dovresti essere in grado di identificare le varie clausole ed espressioni e passarle a specifici formattatori .

    
risposta data 30.01.2013 - 19:01
fonte

Leggi altre domande sui tag