*
* pgsp_json_text.h: Text plan generator for pg_store_plan.
*
- * Copyright (c) 2012-2016, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ * Copyright (c) 2012-2019, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
* IDENTIFICATION
* pg_store_plan/pgsp_json_text.c
#include "miscadmin.h"
#include "nodes/nodes.h"
#include "nodes/bitmapset.h"
+#include "nodes/pg_list.h"
#include "utils/json.h"
#include "utils/jsonapi.h"
#include "utils/builtins.h"
static void json_text_scalar(void *state, char *token, JsonTokenType tokentype);
/* Parser callbacks for plan textization */
+
+/*
+ * This setter is used for field names that store_plans doesn't know of.
+ * Unlike the other setters, this holds a list of strings emitted as is in text
+ * explains.
+ */
+SETTERDECL(_undef)
+{
+ StringInfo s;
+
+ if(vals->_undef_newelem)
+ {
+ s = makeStringInfo();
+ vals->_undef = lappend(vals->_undef, s);
+ }
+ else
+ {
+ s = llast (vals->_undef);
+ }
+
+ appendStringInfoString(s, val);
+}
+
SETTERDECL(node_type)
{
word_table *p;
p = search_word_table(strategies, val, PGSP_JSON_TEXTIZE);
+ if (!p)
+ return;
+
switch (vals->nodetag)
{
case T_Agg:
vals->node_type = "HashAggregate"; break;
case S_Sorted:
vals->node_type = "GroupAggregate"; break;
+ case S_Mixed:
+ vals->node_type = "MixedAggregate"; break;
default:
break;
}
CONVERSION_SETTER(sort_method, conv_sortmethod);
LIST_SETTER(sort_key);
LIST_SETTER(group_key);
+LIST_SETTER(hash_key);
BOOL_SETTER(parallel_aware);
+CONVERSION_SETTER(partial_mode, conv_partialmode);
SQLQUOTE_SETTER(index_name);
DEFAULT_SETTER(startup_cost);
DEFAULT_SETTER(total_cost);
DEFAULT_SETTER(worker_number);
DEFAULT_SETTER(workers_planned);
DEFAULT_SETTER(workers_launched);
+BOOL_SETTER(inner_unique);
+DEFAULT_SETTER(table_func_name);
#define ISZERO(s) (!s || strcmp(s, "0") == 0 || strcmp(s, "0.000") == 0 )
#define HASSTRING(s) (s && strlen(s) > 0)
foreach (lcg, gs->group_keys)
{
const char *gk = (const char *)lfirst (lcg);
- print_prop_if_exists(s, "Group Key: ", gk, level, exind);
+ print_prop_if_exists(s, gs->key_type, gk, level, exind);
}
+
}
}
}
print_prop_if_exists(s, "Function Call: ", v->func_call, level, exind);
+
+ /*
+ * Emit unknown properties here. The properties are printed in the same
+ * shape with JSON properties as assumed by explain.c.
+ */
+ foreach (lc, v->_undef)
+ {
+ StringInfo str = (StringInfo) lfirst(lc);
+
+ appendStringInfoString(s, "\n");
+ appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
+ appendStringInfoString(s, str->data);
+ }
+ v->_undef = NULL;
+
print_prop_if_exists(s, "Filter: ", v->filter, level, exind);
print_prop_if_nz(s, "Rows Removed by Filter: ",
v->filter_removed, level, exind);
v->sort_key = makeStringInfo();
if (!v->group_key)
v->group_key = makeStringInfo();
+ if (!v->hash_key)
+ v->hash_key = makeStringInfo();
resetStringInfo(v->sort_key);
resetStringInfo(v->group_key);
+ resetStringInfo(v->hash_key);
}
}
* go into individual "Group Key" lines. Empty innermost list is
* represented as "()" there. See explain.c of PostgreSQL.
*/
- ctx->tmp_gset->group_keys =
- lappend(ctx->tmp_gset->group_keys,
- (v->group_key->data[0] ?
- pstrdup(v->group_key->data) : "()"));
+ ctx->tmp_gset->key_type = "Group Key: ";
+ if (v->group_key->data[0])
+ {
+ ctx->tmp_gset->group_keys =
+ lappend(ctx->tmp_gset->group_keys,
+ pstrdup(v->group_key->data));
+ }
+ else if (v->hash_key->data[0])
+ {
+ ctx->tmp_gset->group_keys =
+ lappend(ctx->tmp_gset->group_keys,
+ pstrdup(v->hash_key->data));
+ ctx->tmp_gset->key_type = "Hash Key: ";
+ }
+ else
+ ctx->tmp_gset->group_keys =
+ lappend(ctx->tmp_gset->group_keys, "()");
+
resetStringInfo(ctx->nodevals->group_key);
+ resetStringInfo(ctx->nodevals->hash_key);
}
ctx->wlist_level--;
}
if (!p)
{
- ereport(DEBUG1,
+ ereport(DEBUG2,
(errmsg("Short JSON parser encoutered unknown field name: \"%s\", skipped.", fname),
errdetail_log("INPUT: \"%s\"", ctx->org_string)));
+
+ /*
+ * Unknown properties may be put by foreign data wrappers and assumed
+ * to be printed in the same format to JSON properties. We store in
+ * nodevals a string emittable as-is in text explains.
+ */
+ ctx->setter = SETTER(_undef);
+ ctx->nodevals->_undef_newelem = true;
+ ctx->setter(ctx->nodevals, fname);
+ ctx->nodevals->_undef_newelem = false;
+ ctx->setter(ctx->nodevals, ": ");
}
else
{