Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
129 views
in Technique[技术] by (71.8m points)

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

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:

enter image description here

The error:

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...