使用Django、audit表时,pltcl触发器中没有此类变量

使用Django、audit表时,pltcl触发器中没有此类变量,django,postgresql,triggers,tcl,Django,Postgresql,Triggers,Tcl,我在Django应用程序中的postgresql数据库中有一个通过pltcl触发器的审计实现,我想跟踪表上的每个数据更改,Django审计日志是不够的,因为我们也需要通过sql直接跟踪更改,在psql中我可以插入、更新、删除,一切正常,但在Django中,当应用程序与同一数据库用户一起运行时,会引发以下错误: DatabaseError at /recolecta/nuevo can't read "tgname_act": no such variable CONTEXT: can't re

我在Django应用程序中的postgresql数据库中有一个通过pltcl触发器的审计实现,我想跟踪表上的每个数据更改,Django审计日志是不够的,因为我们也需要通过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;

在Django需要做什么才能让它工作?还是有必要避免pltcl?postgresql数据库是Ubuntu服务器上的9.1版本,但我想它更多地与Django和postgresql中的一些权限有关,我的建议是使用tablelog,而不是担心您自己的代码:

这就是我们在LedgerSMB中审计某些表所做的


但是,您当前的问题可能是,您可能指的不是tgname\u act,而是TG\u name或TG\u table\u name。

问题是此查询没有找到任何内容:

spi_exec "SELECT relname AS tgname, relname || '_rtaudit' AS tgname_act
          FROM pg_class WHERE relfilenode = $TG_relid"
无论如何,这导致了tgname_act未定义的事实。 您可以检查变量exist with info exists tgname\u 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中执行此类检查,那么您的触发器将在某个时候爆炸。

错误指向触发器,变量tgname\u act似乎没有在任何地方定义,并且不是函数签名的一部分。我不知道pgtcl的范围规则,但如果没有什么神奇之处的话,那就是没有定义。您可以用一个简单的集合tgname\u act something来定义它,但这可能不是正确的修复方法。我更改了行以确保在数组中截获spi\u exec结果,但pltcl再次抱怨,但在通过Django视图调用时只是调用,如:spi\u exec-array C SELECT relname AS tgname,relname | | |“_rtaudit”作为pg_类中的tgname_act,其中relfilenode=$TG_relid set tgname_act$Ctgname_act set tgname$Ctgname