Hi

I am sending little bit modified version.

1. sqlstate should be text, not boolean - a boolean is pretty useless
3. fixed formatting and code style

Questions:

I dislike the using empty message when message parameter is null. We have
to show some text or we have to disallow it?

Regards

Pavel
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index b3b78d2..c0b6a72
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** postgres=# SELECT * FROM pg_xlogfile_nam
*** 17925,17930 ****
--- 17925,17939 ----
          Return information about a file.
         </entry>
        </row>
+       <row>
+        <entry>
+         <literal><function>pg_report_log(<parameter>loglevel</><type>text</>, 
<parameter>message</> <type>anyelement</>[, <parameter>ishidestmt</> 
<type>boolean</> ] [, <parameter>detail</> <type> text</>] [, 
<parameter>hint</> <type>text</>] [, <parameter>sqlstate</> 
<type>text</>])</function></literal>
+        </entry>
+        <entry><type>void</type></entry>
+        <entry>
+         Report message or error.
+        </entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
*************** SELECT (pg_stat_file('filename')).modifi
*** 17993,17998 ****
--- 18002,18026 ----
  </programlisting>
     </para>
  
+    <indexterm>
+     <primary>pg_report_log</primary>
+    </indexterm>
+    <para>
+     <function>pg_report_log</> is useful to write custom messages
+     into current log destination and returns <type>void</type>.
+     This function don't support the PANIC, FATAL log levels due to their 
unique internal DB usage, which may cause the database instability. Using 
<parameter>ishidestmt</> which default values is true, function can write or 
ignore the current SQL statement into log destination. Also, we can have 
DETAIL, HINT log messages by provding <parameter>detail</>, <parameter>hint</> 
as function arguments, which are NULL by default. Using 
<parameter>iserrstate</> which default values is true, enables the function to 
raise the SQLSTATE as ERRCODE_RAISE_EXCEPTION for the only ERROR level.
+ 
+     Typical usages include:
+ <programlisting>
+ postgres=# SELECT pg_report_log('NOTICE', 'Custom Message', true);
+ NOTICE:  Custom Message
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ </programlisting>
+    </para>
+ 
    </sect2>
  
    <sect2 id="functions-advisory-locks">
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
new file mode 100644
index ccc030f..1755335
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** RETURNS jsonb
*** 940,942 ****
--- 940,961 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'jsonb_set';
+ 
+ CREATE OR REPLACE FUNCTION pg_report_log(loglevel text, message text,
+                                          ishidestmt boolean DEFAULT true, 
detail text DEFAULT NULL,
+                                          hint text DEFAULT NULL, sqlstate 
text DEFAULT NULL)
+ RETURNS VOID
+ LANGUAGE INTERNAL
+ VOLATILE
+ AS 'pg_report_log';
+ 
+ CREATE OR REPLACE FUNCTION pg_report_log(loglevel text, message anyelement,
+                                          ishidestmt boolean DEFAULT true, 
detail text DEFAULT NULL,
+                                          hint text DEFAULT NULL, sqlstate 
text DEFAULT NULL)
+ RETURNS VOID
+ VOLATILE
+ AS
+ $$
+ SELECT pg_report_log($1::pg_catalog.text, $2::pg_catalog.text, $3, $4, $5, $6)
+ $$
+ LANGUAGE SQL;
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644
index c0495d9..fd65aae
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
*************** current_query(PG_FUNCTION_ARGS)
*** 75,80 ****
--- 75,210 ----
                PG_RETURN_NULL();
  }
  
+ 
+ /*
+  * Parsing error levels
+  */
+ typedef struct
+ {
+       char *err_level;
+       int  ival;
+ } error_levels;
+ 
+ /*
+  * Translate text based elog level to integer value.
+  *
+  * Returns true, when it found known elog elevel else
+  * returns false;
+  */
+ static bool
+ parse_error_level(const char* err_level, int *ival)
+ {
+       error_levels err_levels[]={
+               {"DEBUG5", DEBUG5},
+               {"DEBUG4", DEBUG4},
+               {"DEBUG3", DEBUG3},
+               {"DEBUG2", DEBUG2},
+               {"DEBUG1", DEBUG1},
+               {"LOG", LOG},
+               {"INFO", INFO}, 
+               {"NOTICE", NOTICE},
+               {"WARNING", WARNING},
+               {"ERROR", ERROR},
+                       /*
+                        * Adding PGERROR to elevels if WIN32
+                        */
+                       #ifdef WIN32
+                       {"PGERROR", PGERROR},
+                       #endif
+               {NULL, 0}
+       };
+ 
+       error_levels *current;
+ 
+       for (current = err_levels; current->err_level != NULL; current++)
+       {
+               if (pg_strcasecmp(current->err_level, err_level) == 0)
+               {
+                       *ival = current->ival;
+ 
+                       return true;
+               }
+       }
+ 
+       return false;
+ }
+ 
+ /*
+  * pg_report_log()
+  *
+  * Printing custom log messages in log file.
+  */
+ Datum
+ pg_report_log(PG_FUNCTION_ARGS)
+ {
+       int      elog_level;
+       char     *elog_level_str;
+       int      sqlstate;
+       char     *sqlstate_str;
+       bool     ishidestmt = false;
+       char     *err_message = NULL;
+       char     *err_detail = NULL;
+       char     *err_hint = NULL;
+ 
+       /* log level */
+       if (PG_ARGISNULL(0))
+               ereport(ERROR,
+                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                errmsg("log level must not be null")));
+ 
+       elog_level_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
+       if (!parse_error_level(elog_level_str, &elog_level))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("invalid log or disallowed level: 
\'%s\'", elog_level_str)));
+ 
+       /* message */
+       if (PG_ARGISNULL(1))
+               err_message = "The message is null";
+       else
+               err_message = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ 
+       /* ishidestmt */
+       if (!PG_ARGISNULL(2))
+               ishidestmt = PG_GETARG_BOOL(2);
+ 
+       /* detail */
+       if (!PG_ARGISNULL(3))
+               err_detail = text_to_cstring(PG_GETARG_TEXT_PP(3));
+ 
+       /* hint */
+       if (!PG_ARGISNULL(4))
+               err_hint = text_to_cstring(PG_GETARG_TEXT_PP(4));
+ 
+       /* sqlstate */
+       if (!PG_ARGISNULL(5))
+       {
+               sqlstate_str = text_to_cstring(PG_GETARG_TEXT_PP(5));
+               if (strlen(sqlstate_str) != 5 ||
+                               strspn(sqlstate_str, 
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("invalid SQLSTATE code: 
\'%s\'", sqlstate_str)));
+ 
+               sqlstate = MAKE_SQLSTATE(sqlstate_str[0],
+                                                                
sqlstate_str[1],
+                                                                
sqlstate_str[2],
+                                                                
sqlstate_str[3],
+                                                                
sqlstate_str[4]);
+       }
+       else
+               sqlstate = (elog_level >= ERROR) ? ERRCODE_RAISE_EXCEPTION : 0;
+ 
+       ereport(elog_level,
+                       ((sqlstate != 0) ? errcode(sqlstate) : 0,
+                        errmsg_internal("%s", err_message),
+                        (err_detail != NULL) ? errdetail_internal("%s", 
err_detail) : 0,
+                        (err_hint != NULL) ? errhint("%s", err_hint) : 0,
+                        errhidestmt(ishidestmt)));
+ 
+       PG_RETURN_VOID();
+ }
+ 
  /*
   * Send a signal to another backend.
   *
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index ddf7c67..d82db09
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("row security for current context
*** 5349,5354 ****
--- 5349,5361 ----
  DATA(insert OID = 3299 (  row_security_active    PGNSP PGUID 12 1 0 0 0 f f f 
f t f s 1 0 16 "25" _null_ _null_ _null_ _null_ _null_  
row_security_active_name _null_ _null_ _null_ ));
  DESCR("row security for current context active on table by table name");
  
+ /* Logging function */
+ 
+ DATA(insert OID = 6015 (  pg_report_log               PGNSP PGUID 12 1 0 0 0 
f f f f f f v 6 0 2278 "25 25 16 25 25 25" _null_ _null_ "{loglevel, message, 
ishidestmt, detail, hint, sqlstate}" _null_ _null_ pg_report_log _null_ _null_ 
_null_ ));
+ DESCR("write message to log file");
+ DATA(insert OID = 6016 (  pg_report_log               PGNSP PGUID 14 1 0 0 0 
f f f f f f v 6 0 2278 "25 2283 16 25 25 25" _null_ _null_ "{loglevel, message, 
ishidestmt, detail, hint, sqlstate}" _null_ _null_ "SELECT pg_report_log($1, 
$2::pg_catalog.text, $3, $4, $5, $6)" _null_ _null_ _null_ ));
+ DESCR("write message to log file");
+ 
  /*
   * Symbolic values for proargmodes column.  Note that these must agree with
   * the FunctionParameterMode enum in parsenodes.h; we declare them here to
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index fc1679e..0dd1425
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_typeof(PG_FUNCTION_ARGS)
*** 495,500 ****
--- 495,501 ----
  extern Datum pg_collation_for(PG_FUNCTION_ARGS);
  extern Datum pg_relation_is_updatable(PG_FUNCTION_ARGS);
  extern Datum pg_column_is_updatable(PG_FUNCTION_ARGS);
+ extern Datum pg_report_log(PG_FUNCTION_ARGS);
  
  /* oid.c */
  extern Datum oidin(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
new file mode 100644
index 7684717..4ec2eb1
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 16,21 ****
--- 16,28 ----
  
  #include <setjmp.h>
  
+ /*
+  * XXX
+  *            If you are adding another elevel, make sure you update the
+  *            parse_error_level() in src/backend/utils/adt/misc.c, with the
+  *            new elevel
+  */
+ 
  /* Error level codes */
  #define DEBUG5                10                      /* Debugging messages, 
in categories of
                                                                 * decreasing 
detail. */
diff --git a/src/test/regress/expected/reportlog.out 
b/src/test/regress/expected/reportlog.out
new file mode 100644
index ...a02ed76
*** a/src/test/regress/expected/reportlog.out
--- b/src/test/regress/expected/reportlog.out
***************
*** 0 ****
--- 1,104 ----
+ --
+ -- Test for Report Log With WARNING
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Custom Message'); --OK
+ WARNING:  Custom Message
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
+ --
+ -- Test for ERROR with default ishidestmt
+ --
+ SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message'); --ERROR
+ ERROR:  Custom Message
+ --
+ -- Test for ERROR with ishidestmt
+ --
+ SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message', true); --ERROR
+ ERROR:  Custom Message
+ --
+ -- Test for anyelement
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', -1234.34); --OK
+ WARNING:  -1234.34
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
+ --
+ -- Test for denial of FATAL
+ --
+ SELECT pg_catalog.pg_report_log('FATAL', 'Fatal Message'); -- ERROR
+ ERROR:  invalid log or disallowed level: 'FATAL'
+ --
+ -- Test for optional arguements
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Warning Message', true, 'WARNING 
DETAIL'); --OK
+ WARNING:  Warning Message
+ DETAIL:  WARNING DETAIL
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
+ --
+ -- Test for NULL log level
+ --
+ SELECT pg_catalog.pg_report_log(NULL, NULL); --ERROR
+ ERROR:  log level must not be null
+ --
+ -- Test for NULL Message
+ --
+ SELECT pg_catalog.pg_report_log('NOTICE', NULL); --OK
+ NOTICE:  The message is null
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
+ --
+ -- Test for SQLSTATE. The below test should print P0001, 
+ -- which is an error code for ERRCODE_RAISE_EXCEPTION
+ --
+ DO $$
+ BEGIN
+   BEGIN
+     PERFORM pg_catalog.pg_report_log('ERROR', NULL, NULL, NULL, NULL);
+     EXCEPTION WHEN SQLSTATE 'P0001' THEN
+       RAISE NOTICE 'handled exception with SQLSTATE: %', SQLSTATE;
+   END;
+ 
+   BEGIN
+     PERFORM pg_catalog.pg_report_log('ERROR', 'custom error', sqlstate => 
'P1234');
+     EXCEPTION WHEN SQLSTATE 'P1234' THEN
+       RAISE NOTICE 'handled exception with SQLSTATE: %', SQLSTATE;
+   END;
+ 
+ END $$;
+ NOTICE:  handled exception with SQLSTATE: P0001
+ NOTICE:  handled exception with SQLSTATE: P1234
+ --
+ -- Test for all NULL inputs, except elevel
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', NULL, NULL, NULL, NULL, NULL); --OK
+ WARNING:  The message is null
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
+ --
+ -- Test for all NOT NULL arguments
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Warning Message', true, 'some 
detail', 'some hint', 'P1234'); --OK
+ WARNING:  Warning Message
+ DETAIL:  some detail
+ HINT:  some hint
+  pg_report_log 
+ ---------------
+  
+ (1 row)
+ 
diff --git a/src/test/regress/parallel_schedule 
b/src/test/regress/parallel_schedule
new file mode 100644
index 6fc5d1e..4cd193d
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: rules
*** 97,103 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views portals_p2 foreign_key cluster dependency guc bitmapops 
combocid tsearch tsdicts foreign_data window xmlmap functional_deps 
advisory_lock json jsonb indirect_toast equivclass
  # ----------
  # Another group of parallel tests
  # NB: temp.sql does a reconnect which transiently uses 2 connections,
--- 97,103 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views portals_p2 foreign_key cluster dependency guc bitmapops 
combocid tsearch tsdicts foreign_data window xmlmap functional_deps 
advisory_lock json jsonb indirect_toast reportlog equivclass
  # ----------
  # Another group of parallel tests
  # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 2ae51cf..6c9f5c3
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: with
*** 158,160 ****
--- 158,161 ----
  test: xml
  test: event_trigger
  test: stats
+ test: reportlog
diff --git a/src/test/regress/sql/reportlog.sql 
b/src/test/regress/sql/reportlog.sql
new file mode 100644
index ...2ac4bd5
*** a/src/test/regress/sql/reportlog.sql
--- b/src/test/regress/sql/reportlog.sql
***************
*** 0 ****
--- 1,70 ----
+ --
+ -- Test for Report Log With WARNING
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Custom Message'); --OK
+ 
+ --
+ -- Test for ERROR with default ishidestmt
+ --
+ SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message'); --ERROR
+ 
+ --
+ -- Test for ERROR with ishidestmt
+ --
+ SELECT pg_catalog.pg_report_log('ERROR', 'Custom Message', true); --ERROR
+ 
+ --
+ -- Test for anyelement
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', -1234.34); --OK
+ 
+ --
+ -- Test for denial of FATAL
+ --
+ SELECT pg_catalog.pg_report_log('FATAL', 'Fatal Message'); -- ERROR
+ 
+ --
+ -- Test for optional arguements
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Warning Message', true, 'WARNING 
DETAIL'); --OK
+ 
+ --
+ -- Test for NULL log level
+ --
+ SELECT pg_catalog.pg_report_log(NULL, NULL); --ERROR
+ 
+ 
+ --
+ -- Test for NULL Message
+ --
+ SELECT pg_catalog.pg_report_log('NOTICE', NULL); --OK
+ 
+ --
+ -- Test for SQLSTATE. The below test should print P0001, 
+ -- which is an error code for ERRCODE_RAISE_EXCEPTION
+ --
+ DO $$
+ BEGIN
+   BEGIN
+     PERFORM pg_catalog.pg_report_log('ERROR', NULL, NULL, NULL, NULL);
+     EXCEPTION WHEN SQLSTATE 'P0001' THEN
+       RAISE NOTICE 'handled exception with SQLSTATE: %', SQLSTATE;
+   END;
+ 
+   BEGIN
+     PERFORM pg_catalog.pg_report_log('ERROR', 'custom error', sqlstate => 
'P1234');
+     EXCEPTION WHEN SQLSTATE 'P1234' THEN
+       RAISE NOTICE 'handled exception with SQLSTATE: %', SQLSTATE;
+   END;
+ 
+ END $$;
+ 
+ --
+ -- Test for all NULL inputs, except elevel
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', NULL, NULL, NULL, NULL, NULL); --OK
+ 
+ --
+ -- Test for all NOT NULL arguments
+ --
+ SELECT pg_catalog.pg_report_log('WARNING', 'Warning Message', true, 'some 
detail', 'some hint', 'P1234'); --OK
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to