mardi 4 février 2020

SQL Parser (Parseur de SQL) - Exemple Node JS

Après avoir essuyé quelques problèmes avec la librairie Ruby, j'ai décidé de me familiariser avec Node JS avec un exemple. Alors pourquoi pas avec le Parser Sql.

Quel est l'objectif d'un parseur SQL ?
L'objectif est simple. A partir des différentes requêtes, il est parfois utile de récupérer les différents éléments constituants les requêtes. Par exemple dans le cas d'un audit, savoir quels sont les données qui sont utilisées. On récupérera alors l'ensemble des champs de l'ensemble des requêtes.

Voici donc un exemple d'utilisation de la librairie. Avoir la liste des tables (la table et non l'alias) et des champs. Lien du package node-sql-parser.

//lire le fichier SQL
const DEBUG = true;
const fs = require('fs')
const path = '/'
const file = path + 'query.sql'

//récupérer le contenu du fichier dans une variable
if (fs.existsSync(file)) {
  var sql = fs.readFileSync(file).toString()//.split("\r\n");
}


//Parser le Sql
const opt = {
  database: 'MySQL' // MySQL is the default database
}
const { Parser } = require('node-sql-parser');
const parser = new Parser();

// {type}::{dbName}::{tableName} // type could be select, update, delete or insert
const { tableList, columnList, ast } = parser.parse(sql, opt);

//On ne s'intéresse qu'aux instructions select
//select::db::table => db.table
console.log('>> Tables :');
tableList.forEach(function(it, id) {
    if ('select::' == it.substring(0,8)) {
        x = it.substring(8).replace('::', '.');
        // console.log(x);
        tableList[id] = x;
    } else {
        tableList.splice(id, 1); //on enlève l'élément du tableau si ce n'est pas un select
    }
});

//select::table::champ => table.champ
console.log('\n>> Champs :');
columnList.forEach(function(it, id) {
    if ('select::' == it.substring(0,8)) {
        x = it.substring(8).replace('::', '.');
        // console.log(x);
        columnList[id] = x;
    } else {
        columnList.splice(id, 1);
    }
});

//Tri du tableau et valeur unique
tableList1 = tableList.toString().split(',').sort();
tableList1 = Array.from(new Set(tableList1)) //uniq
columnList1 = columnList.toString().split(',').sort();
columnList1 = Array.from(new Set(columnList1)) //uniq

//Affichage des tables et champs du fichier de requêtes
console.log(tableList1);
console.log(tableList1.length);
console.log(columnList1);
console.log(columnList1.length);

Remarque :
Je n'ai pas réussi à intégrer à mon script d'autres types d'instruction (en dehors de select). Cela produit une erreur.
Ce traitement ne gère pas le cas où l'on ne définit pas explicitement la table pour le champ, ce que permet SQL tant qu'il n'y a pas de nom identique.