Advertisement
Guest User

function WRITE_MESSAGE

a guest
Mar 30th, 2018
385
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. \echo Start functions/WRITE_MESSAGE.pg_sql
  2.  
  3. /** Write given parameters to different targets.
  4.  *  The targets get defined in PROPERTY table.
  5.  *
  6.  *  @function WRITE_MESSAGE
  7.  *  @return void
  8.  *  @param in text I_MESSAGE Value for LOG.MESSAGE.
  9.  *  @param in text I_LEVEL Value for LOG.LEVEL.
  10.  *  @private
  11.  *  @info This function is meant to be private. Its use outside pglogger is
  12.  *        very limited at most. That's why it is marked as private eventhough
  13.  *        it is visible to the outside. All of this is due to the fact that
  14.  *        PostgreSQL 10 and earlier does not support packages.
  15.  *  @version $Id: WRITE_MESSAGE.pg_sql 5 2018-03-30 16:41:07Z thiemo $
  16.  *  @todo Once PostgreSQL supports packages, integrate this function into one.
  17.  *        For the time being, it should get installed within a schema on its
  18.  *        own and no execution grants should be given on it.
  19.  *  @todo Once HyperSQL supports a license tag, convert the corresponding
  20.  *        info tag into one.
  21.  *  @copyright Thiemo Kellner, 2018 -
  22.  *  @info License LGPLv3
  23.  *  @webpage https://www.sourceforge.net/projects/pglogger
  24.  */
  25. create or replace function WRITE_MESSAGE(
  26.     I_MESSAGE text,
  27.     I_LEVEL text
  28. )
  29.   returns void
  30.   language plpgsql
  31.   stable as
  32. $body$
  33.     declare
  34.         -- constants
  35.         C_CALLER_FUNCTION constant name :=
  36.           GET_CALLER_FUNCTION(3);
  37.         C_PRESENT_USER constant name := current_user;
  38.         C_SESSION_USER constant name := session_user;
  39.         C_TRANSACTION_TIMESTAMP constant timestamp with time zone :=
  40.           transaction_timestamp();
  41.         C_TRANSACTION_ID constant bigint :=
  42.           txid_current_if_assigned();
  43.         C_SERVER_PID constant bigint := pg_backend_pid();
  44.         C_REMOTE_ADDRESS constant inet := inet_client_addr();
  45.         C_REMOTE_PORT constant int := inet_client_port();
  46.         C_TIMESTAMPFORMAT constant text :=
  47.           GET_PROPERTY_VALUE_STRING(
  48.               I_PROPERTY_NAME => 'TIMESTAMP_DISPLAY_FORMAT'
  49.           );
  50.         C_INTEGERFORMAT constant text :=
  51.           GET_PROPERTY_VALUE_STRING(
  52.               I_PROPERTY_NAME => 'INTEGER_DISPLAY_FORMAT'
  53.         );
  54.         C_LB constant text :=
  55.           GET_PROPERTY_VALUE_STRING(
  56.               I_PROPERTY_NAME => 'LINEBREAK'
  57.           );
  58.  
  59.         -- variables
  60.         V_ENTRY text;
  61.         -- concatenation with null results in null
  62.         V_CALLER_FUNCTION text := coalesce(C_CALLER_FUNCTION, '');
  63.         V_TRANSACTION_ID text :=
  64.           trim(
  65.               coalesce(
  66.                   to_char(C_TRANSACTION_ID, C_INTEGERFORMAT),
  67.                   ''
  68.               )
  69.           );
  70.         V_SERVER_PID text := trim(to_char(C_SERVER_PID, C_INTEGERFORMAT));
  71.         V_REMOTE_ADDRESS text := coalesce(host(C_REMOTE_ADDRESS), '');
  72.         V_REMOTE_PORT text :=
  73.           trim(
  74.               coalesce(
  75.                   to_char(C_REMOTE_PORT, C_INTEGERFORMAT),
  76.                   ''
  77.               )
  78.           );
  79.         V_DBLINK_CONNECT_STRING text;
  80.         V_DBLINK_CONNECTION_NAME text;
  81.         V_DBLINK_CONNECTION_NAMES text[];
  82.         V_WAIT_FOR interval;
  83.         V_QUERY text;
  84.     begin
  85.         if IS_LOGGING_TO_STANDARD() then
  86.             V_ENTRY :=
  87.               to_char(clock_timestamp(), C_TIMESTAMPFORMAT)
  88.               || C_LB ||
  89.               '   - Calling function: ' || V_CALLER_FUNCTION || C_LB ||
  90.               '   - Current user: ' || C_PRESENT_USER || C_LB ||
  91.               '   - Session user: ' || C_SESSION_USER || C_LB ||
  92.               '   - Transaction timestamp: ' ||
  93.               to_char(C_TRANSACTION_TIMESTAMP, C_TIMESTAMPFORMAT) || C_LB ||
  94.               '   - Transaction ID: ' || V_TRANSACTION_ID || C_LB ||
  95.               '   - Server process ID: ' || C_SERVER_PID || C_LB ||
  96.               '   - Address of the remote connection: ' ||
  97.               V_REMOTE_ADDRESS || C_LB ||
  98.               '   - Port of the remote connection: ' ||
  99.               V_REMOTE_PORT || C_LB ||
  100.               '   - Message: ' || coalesce(I_MESSAGE, '');
  101.             case I_LEVEL
  102.                 when 'DEBUG' then
  103.                     raise debug '%', V_ENTRY;
  104.                 when 'LOG' then
  105.                     raise log '%', V_ENTRY;
  106.                 when 'INFO' then
  107.                     raise info '%', V_ENTRY;
  108.                 when 'NOTICE' then
  109.                     raise notice '%', V_ENTRY;
  110.                 when 'WARNING' then
  111.                     raise warning '%', V_ENTRY;
  112.                 when 'EXCEPTION' then
  113.                     raise exception '%', V_ENTRY;
  114.                 else
  115.                     raise exception '%', 'Unsupported level ' || I_LEVEL;
  116.             end case;
  117.         end if;
  118.         if IS_LOGGING_TO_TABLE() then
  119.             -- Background workers for autonomous transactions are not (yet)
  120.             -- even modules. One had to write/compile code like the one of
  121.             -- Robert Haas (available at
  122.             -- https://github.com/vibhorkum/pg_background)
  123.             -- As pglogger is meant to be usable out-of-the-box,
  124.             -- pg_background_launch et al. is not a way to go.
  125.  
  126.             -- open the dblink if it does not yet exist
  127.             V_DBLINK_CONNECTION_NAME :=
  128.               GET_PROPERTY_VALUE_STRING(
  129.                   I_PROPERTY_NAME => 'DBLINK_CONNECTION_NAME'
  130.               );
  131.             select dblink_get_connections() into V_DBLINK_CONNECTION_NAMES;
  132.             if (
  133.                 V_DBLINK_CONNECTION_NAMES is null
  134.              or V_DBLINK_CONNECTION_NAME != any(V_DBLINK_CONNECTION_NAMES)
  135.             ) then
  136.                 V_DBLINK_CONNECT_STRING :=
  137.                   GET_PROPERTY_VALUE_STRING(
  138.                       I_PROPERTY_NAME => 'DBLINK_CONNECT_STRING'
  139.                   );
  140.                 -- better to use dblink_connect_u with password file?
  141.                 perform dblink_connect(
  142.                     V_DBLINK_CONNECTION_NAME,
  143.                     V_DBLINK_CONNECT_STRING
  144.                 );
  145.             else
  146.                 -- As long as the query results are not retrieved, the
  147.                 -- connection is not ready for another query no matter
  148.                 -- what dblink_is_busy says - it only checks, whether
  149.                 -- dblink still is preparing the results.
  150.                 -- Being so it seems there is no real "fire and
  151.                 -- forget" here. We therefore moved the retrieval to
  152.                 -- the beginning of the next call hoping to get something
  153.                 -- like fire and forget and to not lose the last call
  154.                 -- result in when the calling session ends. However,
  155.                 -- the result is being discarded and we do not implement
  156.                 -- another variable.
  157.                 perform * from  dblink_get_result(V_DBLINK_CONNECTION_NAME) as t(ID bigint);
  158. --                 select * from dblink_get_result(V_DBLINK_CONNECTION_NAME) as T(ID bigint);
  159.             end if;
  160.  
  161.             -- Send query asynchronously
  162.             -- Use literal (%L) as it returns the value null as the unquoted
  163.             -- string NULL.
  164.             -- Is there a short way of casting timestamp with time zone?
  165.             -- ::timestamp_with_time_zone does not work.
  166.             V_QUERY := format(
  167.                 $s$select true $s$ || C_LB ||
  168.                 $s$  from %I( $s$ || C_LB ||
  169.                 $s$           I_FUNCTION => %L::text, $s$ || C_LB ||
  170.                 $s$           I_MESSAGE => %L::text, $s$ || C_LB ||
  171.                 $s$           I_LEVEL => %L::text, $s$ || C_LB ||
  172.                 $s$           I_PRESENT_USER => %L::name, $s$ || C_LB ||
  173.                 $s$           I_SESSION_USER => %L::name, $s$ || C_LB ||
  174.                 $s$           I_TRANSACTION_TIMESTAMP => $s$ ||
  175.                 $s$             cast(%L as timestamp with time zone), $s$ || C_LB ||
  176.                 $s$           I_TRANSACTION_ID => $s$ ||
  177.                 $s$             %L::bigint, $s$ || C_LB ||
  178.                 $s$           I_SERVER_PID => $s$ ||
  179.                 $s$             %L::bigint, $s$ || C_LB ||
  180.                 $s$           I_REMOTE_ADDRESS => $s$ ||
  181.                 $s$             %L::inet, $s$ || C_LB ||
  182.                 $s$           I_REMOTE_PORT => $s$ ||
  183.                 $s$             %L::int $s$ || C_LB ||
  184.                 $s$       ); $s$ || C_LB ||
  185.                 $s$commit $s$,
  186.                 'WRITE_MESSAGE_TO_TABLE',
  187.                 C_CALLER_FUNCTION,
  188.                 I_MESSAGE,
  189.                 I_LEVEL,
  190.                 C_PRESENT_USER,
  191.                 C_SESSION_USER,
  192.                 C_TRANSACTION_TIMESTAMP,
  193.                 C_TRANSACTION_ID,
  194.                 C_SERVER_PID,
  195.                 C_REMOTE_ADDRESS,
  196.                 C_REMOTE_PORT
  197.             );
  198.             -- send query when connection is ready
  199.             V_WAIT_FOR :=
  200.               GET_PROPERTY_VALUE_INTERVAL(
  201.                   I_PROPERTY_NAME => 'BUSY_WAIT_INTERVAL'
  202.               ); -- to avoid continuous re-querying, already queried here
  203.             -- surprisingly, dblink_is_busy does not return boolean, but 0 for
  204.             -- false
  205.             while dblink_is_busy(V_DBLINK_CONNECTION_NAME) != 0 loop
  206.                 perform pg_sleep_for(V_WAIT_FOR);
  207.             end loop;
  208.             perform dblink_send_query(
  209.                         V_DBLINK_CONNECTION_NAME,
  210.                         V_QUERY
  211.                     );
  212.         end if;
  213.     end;
  214. $body$;
  215.  
  216. comment on function WRITE_MESSAGE(
  217.     I_MESSAGE text,
  218.     I_LEVEL text
  219. ) is
  220.   'Write given parameters to different targets.
  221. The targets get defined in PROPERTY table.
  222. $Header: svn+ssh://thiemo@svn.code.sf.net/p/pglogger/code/functions/WRITE_MESSAGE.pg_sql 5 2018-03-30 16:41:07Z thiemo $';
  223.  
  224. commit; -- unlike Oracle not all ddl commit implicitly
  225.  
  226. \echo End functions/WRITE_MESSAGE.pg_sql
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement