How to save a data with comma in character varying that passes through a trigger?

家住魔仙堡 提交于 2019-12-12 06:59:42

问题


I have a field of type character varying but I get an error when trying to save a data that contains decimal. I want to save that data without problem.

This is my trigger:

CREATE TRIGGER "public.usuarios_trigger_process_audit"


BEFORE INSERT OR UPDATE OR DELETE
  ON usuarios

FOR EACH ROW
  EXECUTE PROCEDURE process_audit();

This the PROCEDURE:

DECLARE
    newtable text;
    col information_schema.columns %ROWTYPE;
    txtquery text;
    line_old TEXT;
    tmpquery text;
    i int;
    columns_old text[];
BEGIN
    IF ( TG_TABLE_SCHEMA = 'public' ) THEN
    SELECT TG_TABLE_NAME || '_actividad' INTO newtable;    /*  select TG_RELNAME || '_actividad' into newtable; */
    ELSE
    SELECT TG_TABLE_SCHEMA || '_' || TG_TABLE_NAME || '_actividad' INTO newtable;    /*  select TG_RELNAME || '_actividad' into newtable; */
    END IF;

    PERFORM creartablaactividad( TG_TABLE_SCHEMA, TG_TABLE_NAME );

    IF ( TG_OP = 'DELETE' ) THEN
    line_old := TRIM( substr(OLD::text,2,(select length(OLD::text)-2)) );
    columns_old := STRING_TO_ARRAY( line_old, ',' );
    i := 0;
    tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
    tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''D'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''D'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN OLD;
    ELSIF ( TG_OP = 'UPDATE' ) THEN
    line_old := TRIM( substr(OLD::text,2,(select length(OLD::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''ANT'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''ANT'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        line_old := TRIM( substr(NEW::text,2,(select length(NEW::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''U'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''U'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN NEW;
    ELSIF ( TG_OP = 'INSERT' ) THEN
    line_old := TRIM( substr(NEW::text,2,(select length(NEW::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''I'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''I'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN NEW;
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;

My table usuarios:

The error:


回答1:


You can use format() to make creating a dynamic SQL query much easier as it will automatically deal with identifiers and literals correctly. One thing that people usually overlook is that you can expand a single record expression to all its columns using (...).* - this also works for NEW and OLD record variables in a trigger, e.g. select (new).*

You can also pass variables to a dynamic SQL with the using keyword of the execute statement. There is no need to convert the record back and forth between a record and a text representation.

Using that possibility your trigger function can be simplified to:

DECLARE 
  l_sql text;
BEGIN
    IF TG_TABLE_SCHEMA = 'public' THEN
      newtable := TG_TABLE_NAME || '_actividad';
    ELSE
      newtable := TG_TABLE_SCHEMA || '_' || TG_TABLE_NAME || '_actividad';
    END IF;

    PERFORM creartablaactividad(TG_TABLE_SCHEMA, TG_TABLE_NAME);
    l_sql := 'INSERT INTO actividad.%I  SELECT current_user, current_timestamp, %L, ($1).*';

    IF TG_OP = 'DELETE' THEN
      execute format(l_sql, newtable, 'D') using OLD;
      RETURN OLD;
    ELSE
      -- covers UPDATE and INSERT
      execute format(l_sql, newtable, 'U') using NEW;
      RETURN NEW;
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;

Using placeholders like %I and %L also makes it possible to define the actual SQL only once and re-use it. Those "parameters" are replaced by the format() function (which preserves the $1)

Note the use of ($1).* inside the SQL string. That will make the execute statement expand the record parameter $1 to all its columns. The record itself is passed "natively" with the USING keyword.


The use of INSERT without a target column list (insert into some_table ... instead of insert into some_table (col1, col2, ...) ...) is a pretty fragile thing to do. If the source and the target don't match the insert can fail quite easily. .


If you don't run massive reporting on the audit tables (where having explicit column names would be much more efficient) you might want to think of a more generic audit trigger using a JSON or HSTORE column to store the whole record. There are several ready-made audit triggers available:

  • http://okbob.blogspot.de/2015/01/most-simply-implementation-of-history.html
  • https://github.com/wingspan/wingspan-auditing
  • https://www.cybertec-postgresql.com/en/tracking-changes-in-postgresql/
  • https://wiki.postgresql.org/wiki/Audit_trigger_91plus


来源:https://stackoverflow.com/questions/50322065/how-to-save-a-data-with-comma-in-character-varying-that-passes-through-a-trigger

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!