нет такой переменной в триггере pltcl при использовании Django, таблицы аудита - PullRequest
0 голосов
/ 06 января 2012

У меня есть реализация аудита с помощью триггеров pltcl в базе данных postgresql в приложении Django, я хочу отслеживать каждое изменение данных в таблицах, и django-audit-log недостаточно, потому что нам также нужно отслеживать изменения напрямую через sql, в psql я могу вставить, обновить, удалить, и все идет хорошо, но в Django, когда приложение работает с тем же пользователем базы данных, выдается следующая ошибка:

DatabaseError at /recolecta/nuevo
can't read "tgname_act": no such variable
CONTEXT:  can't read "tgname_act": no such variable
    while executing
"spi_exec "SELECT a.attname AS pk_name
          FROM pg_class c, pg_attribute a, pg_index i
          WHERE c.relname = '$tgname_act' AND c.oid = i.in..."
    (procedure "__PLTcl_proc_71035_trigger" line 23)
    invoked from within
"__PLTcl_proc_71035_trigger tgrt_informationgathering_restitutionrequeststate 53594 informationgathering_restitutionrequeststate public {{} id restitut..."
in PL/Tcl function "rtaudit_function"
Request Method: POST
Request URL:  http://192.168.1.108:8001/recolecta/nuevo
Django Version: 1.3.1
Exception Type: DatabaseError
Exception Value:  
can't read "tgname_act": no such variable
CONTEXT:  can't read "tgname_act": no such variable
    while executing
"spi_exec "SELECT a.attname AS pk_name
          FROM pg_class c, pg_attribute a, pg_index i
          WHERE c.relname = '$tgname_act' AND c.oid = i.in..."
    (procedure "__PLTcl_proc_71035_trigger" line 23)
    invoked from within
"__PLTcl_proc_71035_trigger tgrt_informationgathering_restitutionrequeststate 53594 informationgathering_restitutionrequeststate public {{} id restitut..."
in PL/Tcl function "rtaudit_function"
Exception Location: /home/igor/.virtualenvs/registrotierras_app/local/lib/python2.7/site-packages/django/db/backends/postgresql_psycopg2/base.py in execute, line 44
Python Executable:  /home/igor/.virtualenvs/registrotierras_app/bin/python
Python Version: 2.7.2

определение триггера

CREATE OR REPLACE FUNCTION rtaudit_function() RETURNS "trigger" AS
$BODY$
set i 0
set thecounter 1
set pk_name ""
set pk_value ""
set theuser "registrotierras"

# user
spi_exec "SELECT CURRENT_USER AS tguser"
# tablename | table_rtaudit
spi_exec "SELECT relname AS tgname, relname || '_rtaudit' AS tgname_act FROM pg_class WHERE relfilenode = $TG_relid"
spi_exec "SELECT a.attname AS pk_name
          FROM pg_class c, pg_attribute a, pg_index i
          WHERE c.relname = '$tgname_act' AND c.oid = i.indrelid AND a.attnum > 0 AND a.attrelid = i.indexrelid AND i.indisprimary='t'"


# Make sure the audit table exists, if not, we create it
spi_exec "SELECT COUNT(*) AS cols FROM information_schema.tables WHERE table_name = '$tgname_act' AND table_schema = 'rtaudit'"
if {$cols == 0} {
  spi_exec "CREATE TABLE rtaudit.$tgname_act AS SELECT text('1') AS theuser_rtaudit, current_timestamp AS thetime_rtaudit, text('I') AS theactivity_rtaudit, * FROM $tgname WHERE 1 = 0"
  spi_exec "GRANT ALL ON rtaudit.$tgname_act TO $theuser"
}

set uni [concat "INSERT INTO rtaudit.$tgname_act" "(theuser_rtaudit, thetime_rtaudit, theactivity_rtaudit, "]
set uni1 ""

switch $TG_op {
  INSERT {
    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get NEW $field] 0] $pk_name] == 0} {
        incr i
      }
    }

    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get NEW $field] 0] $pk_name] == 0} {
        if {$thecounter < $i} {
          set uni [concat "$uni" "$field,"]
        } else {
          set uni [concat "$uni" "$field"]
        }
        incr thecounter
      }
    }
    set uni [concat "$uni" ") VALUES ('$tguser', now(), '$TG_op', "]
    set thecounter 1;

    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get NEW $field] 0] $pk_name] == 0} {
        set current_value [quote [lindex [array get NEW $field] 1]]
        if {$current_value == ""} {
          set current_value "NULL"
        } else {
          set current_value "'$current_value'"
        }

        if {$thecounter < $i} {
          set uni [concat "$uni" "$current_value,"]
        } else {
          set uni [concat "$uni" "$current_value"]
        }
        incr thecounter
      }
    }

    set uni [concat "$uni" ")"]
  }
  UPDATE {
    set thesize [llength $TG_relatts]
    set thesize [expr $thesize - 1]

    for {set i 1} {$i <= $thesize} {incr i} {
      set field [lindex $TG_relatts $i]
      if {$i < $thesize} {
        set uni [concat "$uni" "$field,"]
      } else {
        set uni [concat "$uni" "$field"]
      }
    }
    set uni1 $uni
    set uni [concat "$uni" ") VALUES ('$tguser', now(), '$TG_op', "]
    set uni1 [concat "$uni1" ") VALUES ('$tguser', now(), 'PREVIOUS', "]

    for {set i 1} {$i <= $thesize} {incr i} {
      set field [lindex $TG_relatts $i]
      set current_value [quote [lindex [array get NEW $field] 1]]

      if {$current_value == ""} {
        set current_value "NULL"
      } else {
        set current_value "'$current_value'"
      }

      if {$i < $thesize} {
        set uni [concat "$uni" "$current_value,"]
      } else {
        set uni [concat "$uni" "$current_value"]
      }
    }

    for {set i 1} {$i <= $thesize} {incr i} {
      set field [lindex $TG_relatts $i]
      set previous_value [quote [lindex [array get OLD $field] 1]]

      if {$previous_value == ""} {
        set previous_value "NULL"
      } else {
        set previous_value "'$previous_value'"
      }

      if {$i < $thesize} {
        set uni1 [concat "$uni1" "$previous_value,"]
      } else {
        set uni1 [concat "$uni1" "$previous_value"]
      }
    }

    set uni [concat "$uni" ")"]
    set uni1 [concat "$uni1" ")"]
  }
  DELETE {
    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get OLD $field] 0] $pk_name] == 0} {
        incr i
      }
    }

    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get OLD $field] 0] $pk_name] == 0} {
        if {$thecounter < $i} {
          set uni [concat "$uni" "$field,"]
        } else {
          set uni [concat "$uni" "$field"]
        }
        incr thecounter
      }
    }
    set uni [concat "$uni" ") VALUES ('$tguser', now(), '$TG_op', "]
    set thecounter 1;

    foreach field $TG_relatts {
      if {[string equal -nocase [lindex [array get OLD $field] 0] $pk_name] == 0} {
        set current_value [quote [lindex [array get OLD $field] 1]]

        if {$current_value == ""} {
          set current_value "NULL"
        } else {
          set current_value "'$current_value'"
        }

        if {$thecounter < $i} {
          set uni [concat "$uni" "$current_value,"]
        } else {
          set uni [concat "$uni" "$current_value"]
        }
        incr thecounter
      }
    }
    set uni [concat "$uni" ")"]
  }
}

# Execute the query y error management
if {[catch {spi_exec $uni} catchres]} {
        set uni2 "INSERT INTO error_rtaudit (thetime, thetable, error, query) VALUES (NOW(), '$tgname_act', '$catchres', '$uni')"
        if {[catch {spi_exec $uni2} catchres1]} {
                set uni3 "INSERT INTO error_rtaudit (thetime, error) VALUES (NOW(), '$catchres')"
                if {[catch {spi_exec $uni3} catchres]} {
                        set errormsg "ERROR 1"
                }
        } else {
                set errormsg "ERROR 2"
        }
} else {
        set errormsg "OK"
}

set thesize [string length $uni1]
if { $thesize > 0 } {
        if {[catch {spi_exec $uni1} catchres]} {
                set uni2 "INSERT INTO error_rtaudit (thetime, thetable, error, query) VALUES (NOW(), '$tgname_act', '$catchres', '$uni1')"
                if {[catch {spi_exec $uni2} catchres1]} {
                        set uni3 "INSERT INTO error_rtaudit (thetime, error) VALUES (NOW(), '$catchres')"
                        if {[catch {spi_exec $uni3} catchres]} {
                                set errormsg [concat "$errormsg" " | ERROR 1"]
                        }
                } else {
                        set errormsg [concat "$errormsg" " | ERROR 2"]
                }
        } else {
                set errormsg [concat "$errormsg" " | OK"]
        }
}

return OK
$BODY$
LANGUAGE 'pltcl' VOLATILE;

Что необходимо сделать в Джанго, чтобы это работало? или нужно будет избегать pltcl? База данных postgresql - это 9.1 на сервере Ubuntu, но я думаю, что она больше связана с Django и некоторыми разрешениями в postgresql

Ответы [ 2 ]

1 голос
/ 25 марта 2013

Проблема в том, что этот запрос ничего не находит:

spi_exec "SELECT relname AS tgname, relname || '_rtaudit' AS tgname_act
          FROM pg_class WHERE relfilenode = $TG_relid"

В любом случае это приводит к тому, что tgname_act не определено. Вы можете проверить, существует ли переменная с помощью info exists tgname_act и предоставить полезное значение по умолчанию.

Пример:

spi_exec "SELECT relname AS tgname, relname || '_rtaudit' AS tgname_act
          FROM pg_class WHERE relfilenode = $TG_relid"
if {![info exists tgname_act]} {set tgname_act "foo_rtaudit"}

Если вы не выполните такие проверки с помощью PL / Tcl, то ваш триггер через некоторое время взорвется.

0 голосов
/ 25 марта 2013

Вместо того, чтобы беспокоиться о вашем собственном коде, я предлагаю использовать tablelog: http://pgfoundry.org/projects/tablelog/

Это то, что мы делаем в LedgerSMB для аудита определенных таблиц.

Ваша текущая проблема, вероятно, в том, что вместо tgname_act вы, вероятно, имеете в виду либо TG_name, либо TG_table_name.

...