mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Extend EXPLAIN to support output in XML or JSON format.
There are probably still some adjustments to be made in the details of the output, but this gets the basic structure in place. Robert Haas
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.98 2009/07/26 23:34:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.99 2009/08/10 05:46:50 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -685,9 +685,6 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
|
||||
foreach(p, plan_list)
|
||||
{
|
||||
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
|
||||
bool is_last_query;
|
||||
|
||||
is_last_query = (lnext(p) == NULL);
|
||||
|
||||
if (IsA(pstmt, PlannedStmt))
|
||||
{
|
||||
@@ -714,9 +711,9 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
|
||||
|
||||
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
|
||||
|
||||
/* put a blank line between plans */
|
||||
if (!is_last_query)
|
||||
appendStringInfoChar(es->str, '\n');
|
||||
/* Separate plans with an appropriate separator */
|
||||
if (lnext(p) != NULL)
|
||||
ExplainSeparatePlans(es);
|
||||
}
|
||||
|
||||
if (estate)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.92 2009/06/11 14:49:04 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.93 2009/08/10 05:46:50 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1593,8 +1593,6 @@ map_xml_name_to_sql_identifier(char *name)
|
||||
char *
|
||||
map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
|
||||
{
|
||||
StringInfoData buf;
|
||||
|
||||
if (type_is_array(type))
|
||||
{
|
||||
ArrayType *array;
|
||||
@@ -1605,6 +1603,7 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
|
||||
int num_elems;
|
||||
Datum *elem_values;
|
||||
bool *elem_nulls;
|
||||
StringInfoData buf;
|
||||
int i;
|
||||
|
||||
array = DatumGetArrayTypeP(value);
|
||||
@@ -1638,8 +1637,7 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
|
||||
{
|
||||
Oid typeOut;
|
||||
bool isvarlena;
|
||||
char *p,
|
||||
*str;
|
||||
char *str;
|
||||
|
||||
/*
|
||||
* Special XSD formatting for some data types
|
||||
@@ -1788,35 +1786,50 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
|
||||
return str;
|
||||
|
||||
/* otherwise, translate special characters as needed */
|
||||
initStringInfo(&buf);
|
||||
|
||||
for (p = str; *p; p++)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '&':
|
||||
appendStringInfoString(&buf, "&");
|
||||
break;
|
||||
case '<':
|
||||
appendStringInfoString(&buf, "<");
|
||||
break;
|
||||
case '>':
|
||||
appendStringInfoString(&buf, ">");
|
||||
break;
|
||||
case '\r':
|
||||
appendStringInfoString(&buf, "
");
|
||||
break;
|
||||
default:
|
||||
appendStringInfoCharMacro(&buf, *p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buf.data;
|
||||
return escape_xml(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Escape characters in text that have special meanings in XML.
|
||||
*
|
||||
* Returns a palloc'd string.
|
||||
*
|
||||
* NB: this is intentionally not dependent on libxml.
|
||||
*/
|
||||
char *
|
||||
escape_xml(const char *str)
|
||||
{
|
||||
StringInfoData buf;
|
||||
const char *p;
|
||||
|
||||
initStringInfo(&buf);
|
||||
for (p = str; *p; p++)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '&':
|
||||
appendStringInfoString(&buf, "&");
|
||||
break;
|
||||
case '<':
|
||||
appendStringInfoString(&buf, "<");
|
||||
break;
|
||||
case '>':
|
||||
appendStringInfoString(&buf, ">");
|
||||
break;
|
||||
case '\r':
|
||||
appendStringInfoString(&buf, "
");
|
||||
break;
|
||||
default:
|
||||
appendStringInfoCharMacro(&buf, *p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
_SPI_strdup(const char *s)
|
||||
{
|
||||
|
||||
28
src/backend/utils/cache/lsyscache.c
vendored
28
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.162 2009/06/11 14:49:05 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.163 2009/08/10 05:46:50 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -1298,6 +1298,32 @@ get_func_name(Oid funcid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_namespace
|
||||
*
|
||||
* Returns the pg_namespace OID associated with a given function.
|
||||
*/
|
||||
Oid
|
||||
get_func_namespace(Oid funcid)
|
||||
{
|
||||
HeapTuple tp;
|
||||
|
||||
tp = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcid),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(tp))
|
||||
{
|
||||
Form_pg_proc functup = (Form_pg_proc) GETSTRUCT(tp);
|
||||
Oid result;
|
||||
|
||||
result = functup->pronamespace;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_rettype
|
||||
* Given procedure id, return the function's result type.
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.92 2009/08/01 20:59:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.93 2009/08/10 05:46:50 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2200,21 +2200,20 @@ tuplesort_restorepos(Tuplesortstate *state)
|
||||
}
|
||||
|
||||
/*
|
||||
* tuplesort_explain - produce a line of information for EXPLAIN ANALYZE
|
||||
* tuplesort_get_stats - extract summary statistics
|
||||
*
|
||||
* This can be called after tuplesort_performsort() finishes to obtain
|
||||
* printable summary information about how the sort was performed.
|
||||
*
|
||||
* The result is a palloc'd string.
|
||||
* spaceUsed is measured in kilobytes.
|
||||
*/
|
||||
char *
|
||||
tuplesort_explain(Tuplesortstate *state)
|
||||
void
|
||||
tuplesort_get_stats(Tuplesortstate *state,
|
||||
const char **sortMethod,
|
||||
const char **spaceType,
|
||||
long *spaceUsed)
|
||||
{
|
||||
char *result = (char *) palloc(100);
|
||||
long spaceUsed;
|
||||
|
||||
/*
|
||||
* Note: it might seem we should print both memory and disk usage for a
|
||||
* Note: it might seem we should provide both memory and disk usage for a
|
||||
* disk-based sort. However, the current code doesn't track memory space
|
||||
* accurately once we have begun to return tuples to the caller (since we
|
||||
* don't account for pfree's the caller is expected to do), so we cannot
|
||||
@@ -2223,38 +2222,34 @@ tuplesort_explain(Tuplesortstate *state)
|
||||
* tell us how much is actually used in sortcontext?
|
||||
*/
|
||||
if (state->tapeset)
|
||||
spaceUsed = LogicalTapeSetBlocks(state->tapeset) * (BLCKSZ / 1024);
|
||||
{
|
||||
*spaceType = "Disk";
|
||||
*spaceUsed = LogicalTapeSetBlocks(state->tapeset) * (BLCKSZ / 1024);
|
||||
}
|
||||
else
|
||||
spaceUsed = (state->allowedMem - state->availMem + 1023) / 1024;
|
||||
{
|
||||
*spaceType = "Memory";
|
||||
*spaceUsed = (state->allowedMem - state->availMem + 1023) / 1024;
|
||||
}
|
||||
|
||||
switch (state->status)
|
||||
{
|
||||
case TSS_SORTEDINMEM:
|
||||
if (state->boundUsed)
|
||||
snprintf(result, 100,
|
||||
"Sort Method: top-N heapsort Memory: %ldkB",
|
||||
spaceUsed);
|
||||
*sortMethod = "top-N heapsort";
|
||||
else
|
||||
snprintf(result, 100,
|
||||
"Sort Method: quicksort Memory: %ldkB",
|
||||
spaceUsed);
|
||||
*sortMethod = "quicksort";
|
||||
break;
|
||||
case TSS_SORTEDONTAPE:
|
||||
snprintf(result, 100,
|
||||
"Sort Method: external sort Disk: %ldkB",
|
||||
spaceUsed);
|
||||
*sortMethod = "external sort";
|
||||
break;
|
||||
case TSS_FINALMERGE:
|
||||
snprintf(result, 100,
|
||||
"Sort Method: external merge Disk: %ldkB",
|
||||
spaceUsed);
|
||||
*sortMethod = "external merge";
|
||||
break;
|
||||
default:
|
||||
snprintf(result, 100, "sort still in progress");
|
||||
*sortMethod = "still in progress";
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user