#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

u"""%(scriptname)s: Afficher une requête SQL d'un fichier de log WebObjects

USAGE
    %(scriptname)s 'line'
    cat /var/log/WebObjets/App-1 | %(scriptname)s

Les fichiers de logs WebObjects, quand ils contiennent des requêtes SQL, ne sont
pas exploitables directement: la requête et les arguments sont disjoints. Ce
script fusionne la requête et les arguments pour générer une requête SQL
utilisable directement.

Il faut spécifier en argument la ligne complète du fichier de log, e.g.
    %(scriptname)s '[2012-11-07 09:58:31 GST]... "SELECT..." withBindings:...'
Sinon, les lignes sont lues depuis l'entrée standard."""

import os, sys, re
from os import path

from ulib.all import *

def display_help():
    uprint(__doc__ % globals())

RE_NUMERIC = re.compile('\d+$')
RE_QUOTED = re.compile(r'".*"$')
RE_STRING = re.compile(r'[A-Za-z][A-Za-z0-9]*$')
def fixvalue(v):
    if RE_NUMERIC.match(v) is not None:
        # Les valeurs numériques sont données telles quelles
        return v
    elif RE_QUOTED.match(v) is not None:
        # Les valeurs chaines sont entre guillemets
        return "'" + v[1:-1] + "'"
    elif RE_STRING.match(v) is not None:
        # Certaines valeurs chaine ne sont pas entre guillemets
        return "'" + v + "'"
    # Sinon, on assume que c'est une date
    return "to_date('" + v + "', 'YYYY-MM-DD HH24:MI:SS')"

RE_LINE = re.compile(r'[^"]*"([^"]*)" withBindings: (.*)>')
RE_VAR = re.compile(r'\d+:(.*?)\(\w+\)')
def fixsql(line, raiseerror=True):
    mo = RE_LINE.match(line)
    if mo is None:
        if raiseerror: raise ValueError("%s: Chaine invalide" % line)
        else: return None

    sql = mo.group(1)
    vars = map(fixvalue, RE_VAR.findall(mo.group(2)))

    for var in vars:
        pos = sql.index('?')
        if pos == -1:
            if raiseerror: raise ValueError("%s: Chaine invalide (nombre de ? non cohérent)" % line)
            else: return None
        sql = sql[:pos] + var + sql[pos+1:]

    return sql + ';'

def run_wofixsql():
    options, longoptions = build_options([
        ('h', 'help', "Afficher l'aide"),
        ])
    options, args = get_args(None, options, longoptions)
    for option, value in options:
        if option in ('-h', '--help'):
            display_help()
            sys.exit(0)

    if args:
        for arg in args:
            print fixsql(arg)
    else:
        while True:
            try: line = raw_input()
            except EOFError: break
            sql = fixsql(line, False)
            if sql is not None: print sql

if __name__ == '__main__':
    run_wofixsql()

