Как оценить или обработать операторы if в данных?

Фон

Я написал сценарий bash, который извлекает простые пользовательские функции из базы данных PostgreSQL, с помощью awk преобразует команды pgplsql в SQL (например, PERFORM function() в SELECT function(), удаляет комментарии --.* и т. д.), сохраняет команды SQL в файл (file.sql), считывает и выполняет их в базе:

$ psql ... -f file.sql db

Функции просты, в основном это просто вызов других пользовательских функций. Но как «оценить» или обработать оператор IF?:

IF $1 = 'customer1' THEN      -- THESE $1 MEANS ARGUMENT TO PGPL/SQL FUNCTION
  PERFORM subfunction1($1);   -- THAT THIS IF STATEMENT IS IN:
ELSE                          -- SELECT function('customer1'); 
  PERFORM subfunction2($1);   -- $1 = 'customer1'
END IF;

Tl;dr:

IFs и тому подобное не являются SQL, поэтому их следует предварительно оценить с помощью awk. Можно с уверенностью предположить, что приведенное выше уже обработано в одну запись с удаленными комментариями:

IF $1 = 'customer1' THEN PERFORM subfunction1($1); ELSE PERFORM subfunction2($1); END IF;

После «оценки» выше следует заменить на:

SELECT subfunction1('customer1');

если awk для его оценки был вызван:

$ awk -v arg1="customer1' -f program.awk file.sql

или если arg1 что-то еще, например для customer2:

SELECT subfunction2('customer2');

Изменить

expr первое, что пришло мне в голову, когда я проснулся:

$ awk -v arg="'customer1'" '
{
    gsub(/\$1/,arg)                                     # replace func arg with string
    n=split($0,a,"(IF|THEN|ELSE|ELSE?IF|END IF;)",seps) # seps to get ready for SQL CASE
    if(seps[1]=="IF") {
        # here should be while for ELSEIF
        c="expr " a[2]; c|getline r; close(c)           # use expr to solve 
        switch (r) {                                    # expr has 4 return values
        case "1":                                       # match
            print a[3]
            break
        case "0":                                       # no match
            print a[4]
            break
        default:                                        # (*) see below
            print r
            exit                                        # TODO
}   }   }' file.sql

(*) expr выводит 0,1,2 или 3:

$ expr 1 = 1
1
$ expr 1 = 2
0

Однако, если вы опустите пробелы:

$ expr 1=1
1=1

person James Brown    schedule 25.04.2017    source источник
comment
Это вопрос про awk или про postgresql?   -  person user31264    schedule 25.04.2017
comment
@user31264 user31264 Awk, в основном, препроцессирует сценарии plpgsql в sql перед их выполнением на сервере PostgreSQL с использованием psql. Решения Postgres также приветствуются, но меня больше всего интересует awk atm.   -  person James Brown    schedule 25.04.2017
comment
Это обычный комментарий при минусовании.   -  person James Brown    schedule 25.04.2017
comment
Я не минусовал, но я не понимаю, о чем вы спрашиваете. Я чувствую, что, возможно, вы предполагаете некоторое знание SQL, plpgsql, psql или чего-то еще с нашей стороны, чтобы прояснить ваши требования. Если да, то YMMV. Вы хотите сказать, что у вас есть скрипт со строками типа IF $1 = 'customer1' THEN PERFORM subfunction1($1); ELSE PERFORM subfunction2($1); END IF;, написанный на каком-то языке, и вы хотите написать awk-скрипт для разбора этого языка? Разве нет какого-то инструмента, который уже делает это?   -  person Ed Morton    schedule 25.04.2017
comment
@EdMorton В значительной степени, да. Сделал это на других языках сложным способом, но мне было интересно, есть ли у кого-нибудь простой способ сделать это в awk.   -  person James Brown    schedule 25.04.2017
comment
Да, может быть. Что ж, я оставлю его здесь на некоторое время, прежде чем сниму. Опять же, это всего лишь IF.   -  person James Brown    schedule 25.04.2017
comment
Я проголосовал, потому что не понял вопроса.   -  person user31264    schedule 25.04.2017
comment
@user31264 user31264 Когда TL; DR, вы всегда можете проверить теги, сэр.   -  person James Brown    schedule 26.04.2017
comment
@JamesBrown - даже после тегов я этого не понял. Наверное, я старческий.   -  person user31264    schedule 26.04.2017


Ответы (1)


Если вы ищете что-то дешевое и веселое, не написав полноценный синтаксический анализатор языка, то это может быть хорошей отправной точкой:

$ cat tst.awk
{ gsub(/\$1/,"\047"arg1"\047") }
match($0,/^IF\s+(\S+)\s+(\S+)\s+(\S+)\s+THEN\s+(\S+)\s+(\S+)\s+ELSE\s+(\S+)\s+(\S+)\s+END\s+IF/,a) {
    lhs = a[1]
    op  = a[2]
    rhs = a[3]
    trueAct  = (a[4] == "PERFORM" ? "SELECT" : a[4]) FS a[5]
    falseAct = (a[6] == "PERFORM" ? "SELECT" : a[6]) FS a[7]

    if (op == "=") {
        print (lhs == rhs ? trueAct : falseAct)
    }
}

$ awk -v arg1='customer1' -f tst.awk file
SELECT subfunction1('customer1');

$ awk -v arg1='bob' -f tst.awk file
SELECT subfunction2('bob');

В приведенном выше примере используется GNU awk в качестве третьего аргумента для match(). Надеюсь, достаточно легко понять, что вы можете массировать по мере необходимости, чтобы обращаться с другими конструкциями или другими вариантами этой конструкции.

person Ed Morton    schedule 25.04.2017
comment
Еще нет, я уснул (: это не конструктивная критика, я на GMT+2). Я посмотрю лучше в офисе - если доберусь туда, прошлой ночью шел снег... - person James Brown; 26.04.2017
comment
О да, это приятно. Но, должно быть, это были все эти sSSs, которые мой мозг воспроизвел как zZzZz... - person James Brown; 26.04.2017