diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index b3b78d2..b7a2cc2
*** 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.
+
+
+ pg_report_log(loglevel>text>, message> anyelement>[, ishidestmt> boolean> ] [, detail> text>] [, hint> text>] [, sqlstate> text>])
+
+ void
+
+ Report message or error.
+
+
*************** SELECT (pg_stat_file('filename')).modifi
*** 17993,17998 ****
--- 18002,18033 ----
+
+ pg_report_log
+
+
+ pg_report_log> is useful to write custom messages
+ or raise exception. This function don't support the PANIC, FATAL
+ log levels due to their unique internal DB usage, which may cause
+ the database instability. Using 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 detail>, hint> as function
+ arguments, which are NULL by default. The parameter sqlstate>
+ allows to set a SQLSTATE of raised exception. Default value of this
+ parameter is 'P0001' for ERROR only level.
+
+ Typical usages include:
+
+ postgres=# SELECT pg_report_log('NOTICE', 'Custom Message', true);
+ NOTICE: Custom Message
+ pg_report_log
+ ---------------
+
+ (1 row)
+
+
+
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index ccc030f..7e551f2
*** 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 'P0001')
+ 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 'P0001')
+ 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..b1275bd
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
*************** current_query(PG_FUNCTION_ARGS)
*** 75,80 ****
--- 75,212 ----
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 = 0;
+ 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 or disallowed log 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 if (elog_level >= ERROR)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("sqlstate must not be null when loglevel is ERROR")));
+
+ 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
+ /*
+ * 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 ...9d1a7bc
*** 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 or disallowed log 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