Merge pull request #1632 from citusdata/update_copied_code

Add latest PostgreSQL changes to copy-pasted code

cr: @mtuncer
pull/1635/merge
Jason Petersen 2017-09-08 14:59:14 -06:00 committed by GitHub
commit e29ebe57fd
3 changed files with 323 additions and 109 deletions

View File

@ -3111,7 +3111,9 @@ InterShardDDLTaskList(Oid leftRelationId, Oid rightRelationId,
* *
* This code is heavily borrowed from RangeVarCallbackForDropRelation() in * This code is heavily borrowed from RangeVarCallbackForDropRelation() in
* commands/tablecmds.c in Postgres source. We need this to ensure the right * commands/tablecmds.c in Postgres source. We need this to ensure the right
* order of locking while dealing with DROP INDEX statments. * order of locking while dealing with DROP INDEX statments. Because we are
* exclusively using this callback for INDEX processing, the PARTITION-related
* logic from PostgreSQL's similar callback has been omitted as unneeded.
*/ */
static void static void
RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg) RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)

View File

@ -125,8 +125,8 @@ typedef struct
bool varprefix; /* TRUE to print prefixes on Vars */ bool varprefix; /* TRUE to print prefixes on Vars */
Oid distrelid; /* the distributed table being modified, if valid */ Oid distrelid; /* the distributed table being modified, if valid */
int64 shardid; /* a distributed table's shardid, if positive */ int64 shardid; /* a distributed table's shardid, if positive */
ParseExprKind special_exprkind; /* set only for exprkinds needing ParseExprKind special_exprkind; /* set only for exprkinds needing special
* special handling */ * handling */
} deparse_context; } deparse_context;
/* /*
@ -390,6 +390,9 @@ static void get_rule_expr(Node *node, deparse_context *context,
bool showimplicit); bool showimplicit);
static void get_rule_expr_toplevel(Node *node, deparse_context *context, static void get_rule_expr_toplevel(Node *node, deparse_context *context,
bool showimplicit); bool showimplicit);
static void get_rule_expr_funccall(Node *node, deparse_context *context,
bool showimplicit);
static bool looks_like_function(Node *node);
static void get_oper_expr(OpExpr *expr, deparse_context *context); static void get_oper_expr(OpExpr *expr, deparse_context *context);
static void get_func_expr(FuncExpr *expr, deparse_context *context, static void get_func_expr(FuncExpr *expr, deparse_context *context,
bool showimplicit); bool showimplicit);
@ -3299,8 +3302,11 @@ get_update_query_targetlist_def(Query *query, List *targetList,
/* /*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
* Param. That could be buried under FieldStores and ArrayRefs * Param. That could be buried under FieldStores and ArrayRefs
* (cf processIndirection()), and underneath those there could be * and CoerceToDomains (cf processIndirection()), and underneath
* an implicit type coercion. * those there could be an implicit type coercion. Because we
* would ignore implicit type coercions anyway, we don't need to
* be as careful as processIndirection() is about descending past
* implicit CoerceToDomains.
*/ */
expr = (Node *) tle->expr; expr = (Node *) tle->expr;
while (expr) while (expr)
@ -3319,6 +3325,14 @@ get_update_query_targetlist_def(Query *query, List *targetList,
break; break;
expr = (Node *) aref->refassgnexpr; expr = (Node *) aref->refassgnexpr;
} }
else if (IsA(expr, CoerceToDomain))
{
CoerceToDomain *cdomain = (CoerceToDomain *) expr;
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
expr = (Node *) cdomain->arg;
}
else else
break; break;
} }
@ -4423,6 +4437,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_MinMaxExpr: case T_MinMaxExpr:
case T_SQLValueFunction: case T_SQLValueFunction:
case T_XmlExpr: case T_XmlExpr:
case T_NextValueExpr:
case T_NullIfExpr: case T_NullIfExpr:
case T_Aggref: case T_Aggref:
case T_WindowFunc: case T_WindowFunc:
@ -5752,6 +5767,22 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_NextValueExpr:
{
NextValueExpr *nvexpr = (NextValueExpr *) node;
/*
* This isn't exactly nextval(), but that seems close enough
* for EXPLAIN's purposes.
*/
appendStringInfoString(buf, "nextval(");
simple_quote_literal(buf,
generate_relation_name(nvexpr->seqid,
NIL));
appendStringInfoChar(buf, ')');
}
break;
case T_InferenceElem: case T_InferenceElem:
{ {
InferenceElem *iexpr = (InferenceElem *) node; InferenceElem *iexpr = (InferenceElem *) node;
@ -5810,12 +5841,11 @@ get_rule_expr(Node *node, deparse_context *context,
case PARTITION_STRATEGY_LIST: case PARTITION_STRATEGY_LIST:
Assert(spec->listdatums != NIL); Assert(spec->listdatums != NIL);
appendStringInfoString(buf, "FOR VALUES"); appendStringInfoString(buf, "FOR VALUES IN (");
appendStringInfoString(buf, " IN (");
sep = ""; sep = "";
foreach(cell, spec->listdatums) foreach(cell, spec->listdatums)
{ {
Const *val = lfirst(cell); Const *val = castNode(Const, lfirst(cell));
appendStringInfoString(buf, sep); appendStringInfoString(buf, sep);
get_const_expr(val, context, -1); get_const_expr(val, context, -1);
@ -5831,50 +5861,9 @@ get_rule_expr(Node *node, deparse_context *context,
list_length(spec->lowerdatums) == list_length(spec->lowerdatums) ==
list_length(spec->upperdatums)); list_length(spec->upperdatums));
appendStringInfoString(buf, "FOR VALUES"); appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
appendStringInfoString(buf, " FROM"); get_range_partbound_string(spec->lowerdatums),
appendStringInfoString(buf, " ("); get_range_partbound_string(spec->upperdatums));
sep = "";
foreach(cell, spec->lowerdatums)
{
PartitionRangeDatum *datum = lfirst(cell);
Const *val;
appendStringInfoString(buf, sep);
if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
appendStringInfoString(buf, "MINVALUE");
else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
appendStringInfoString(buf, "MAXVALUE");
else
{
val = (Const *) datum->value;
get_const_expr(val, context, -1);
}
sep = ", ";
}
appendStringInfoString(buf, ")");
appendStringInfoString(buf, " TO");
appendStringInfoString(buf, " (");
sep = "";
foreach(cell, spec->upperdatums)
{
PartitionRangeDatum *datum = lfirst(cell);
Const *val;
appendStringInfoString(buf, sep);
if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
appendStringInfoString(buf, "MINVALUE");
else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
appendStringInfoString(buf, "MAXVALUE");
else
{
val = (Const *) datum->value;
get_const_expr(val, context, -1);
}
sep = ", ";
}
appendStringInfoString(buf, ")");
break; break;
default: default:
@ -5931,6 +5920,64 @@ get_rule_expr_toplevel(Node *node, deparse_context *context,
get_rule_expr(node, context, showimplicit); get_rule_expr(node, context, showimplicit);
} }
/*
* get_rule_expr_funccall - Parse back a function-call expression
*
* Same as get_rule_expr(), except that we guarantee that the output will
* look like a function call, or like one of the things the grammar treats as
* equivalent to a function call (see the func_expr_windowless production).
* This is needed in places where the grammar uses func_expr_windowless and
* you can't substitute a parenthesized a_expr. If what we have isn't going
* to look like a function call, wrap it in a dummy CAST() expression, which
* will satisfy the grammar --- and, indeed, is likely what the user wrote to
* produce such a thing.
*/
static void
get_rule_expr_funccall(Node *node, deparse_context *context,
bool showimplicit)
{
if (looks_like_function(node))
get_rule_expr(node, context, showimplicit);
else
{
StringInfo buf = context->buf;
appendStringInfoString(buf, "CAST(");
/* no point in showing any top-level implicit cast */
get_rule_expr(node, context, false);
appendStringInfo(buf, " AS %s)",
format_type_with_typemod(exprType(node),
exprTypmod(node)));
}
}
/*
* Helper function to identify node types that satisfy func_expr_windowless.
* If in doubt, "false" is always a safe answer.
*/
static bool
looks_like_function(Node *node)
{
if (node == NULL)
return false; /* probably shouldn't happen */
switch (nodeTag(node))
{
case T_FuncExpr:
/* OK, unless it's going to deparse as a cast */
return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL);
case T_NullIfExpr:
case T_CoalesceExpr:
case T_MinMaxExpr:
case T_SQLValueFunction:
case T_XmlExpr:
/* these are all accepted by func_expr_common_subexpr */
return true;
default:
break;
}
return false;
}
/* /*
* get_oper_expr - Parse back an OpExpr node * get_oper_expr - Parse back an OpExpr node
@ -6921,7 +6968,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (list_length(rte->functions) == 1 && if (list_length(rte->functions) == 1 &&
(rtfunc1->funccolnames == NIL || !rte->funcordinality)) (rtfunc1->funccolnames == NIL || !rte->funcordinality))
{ {
get_rule_expr(rtfunc1->funcexpr, context, true); get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
/* we'll print the coldeflist below, if it has one */ /* we'll print the coldeflist below, if it has one */
} }
else else
@ -6984,7 +7031,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (funcno > 0) if (funcno > 0)
appendStringInfoString(buf, ", "); appendStringInfoString(buf, ", ");
get_rule_expr(rtfunc->funcexpr, context, true); get_rule_expr_funccall(rtfunc->funcexpr, context, true);
if (rtfunc->funccolnames != NIL) if (rtfunc->funccolnames != NIL)
{ {
/* Reconstruct the column definition list */ /* Reconstruct the column definition list */
@ -7177,6 +7224,11 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (!PRETTY_PAREN(context)) if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
else if (j->jointype != JOIN_INNER)
{
/* If we didn't say CROSS JOIN above, we must provide an ON */
appendStringInfoString(buf, " ON TRUE");
}
if (!PRETTY_PAREN(context) || j->alias != NULL) if (!PRETTY_PAREN(context) || j->alias != NULL)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
@ -7373,13 +7425,17 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
* *
* We strip any top-level FieldStore or assignment ArrayRef nodes that * We strip any top-level FieldStore or assignment ArrayRef nodes that
* appear in the input, printing them as decoration for the base column * appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression * name (which we assume the caller just printed). We might also need to
* that's to be assigned. * strip CoerceToDomain nodes, but only ones that appear above assignment
* nodes.
*
* Returns the subexpression that's to be assigned.
*/ */
static Node * static Node *
processIndirection(Node *node, deparse_context *context) processIndirection(Node *node, deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
CoerceToDomain *cdomain = NULL;
for (;;) for (;;)
{ {
@ -7427,10 +7483,28 @@ processIndirection(Node *node, deparse_context *context)
*/ */
node = (Node *) aref->refassgnexpr; node = (Node *) aref->refassgnexpr;
} }
else if (IsA(node, CoerceToDomain))
{
cdomain = (CoerceToDomain *) node;
/* If it's an explicit domain coercion, we're done */
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
/* Tentatively descend past the CoerceToDomain */
node = (Node *) cdomain->arg;
}
else else
break; break;
} }
/*
* If we descended past a CoerceToDomain whose argument turned out not to
* be a FieldStore or array assignment, back up to the CoerceToDomain.
* (This is not enough to be fully correct if there are nested implicit
* CoerceToDomains, but such cases shouldn't ever occur.)
*/
if (cdomain && node == (Node *) cdomain->arg)
node = (Node *) cdomain;
return node; return node;
} }
@ -7791,4 +7865,44 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
return buf.data; return buf.data;
} }
/*
* get_one_range_partition_bound_string
* A C string representation of one range partition bound
*/
char *
get_range_partbound_string(List *bound_datums)
{
deparse_context context;
StringInfo buf = makeStringInfo();
ListCell *cell;
char *sep;
memset(&context, 0, sizeof(deparse_context));
context.buf = buf;
appendStringInfoString(buf, "(");
sep = "";
foreach(cell, bound_datums)
{
PartitionRangeDatum *datum =
castNode(PartitionRangeDatum, lfirst(cell));
appendStringInfoString(buf, sep);
if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
appendStringInfoString(buf, "MINVALUE");
else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
appendStringInfoString(buf, "MAXVALUE");
else
{
Const *val = castNode(Const, datum->value);
get_const_expr(val, &context, -1);
}
sep = ", ";
}
appendStringInfoString(buf, ")");
return buf->data;
}
#endif /* (PG_VERSION_NUM >= 100000) */ #endif /* (PG_VERSION_NUM >= 100000) */

View File

@ -386,6 +386,9 @@ static void get_rule_expr(Node *node, deparse_context *context,
bool showimplicit); bool showimplicit);
static void get_rule_expr_toplevel(Node *node, deparse_context *context, static void get_rule_expr_toplevel(Node *node, deparse_context *context,
bool showimplicit); bool showimplicit);
static void get_rule_expr_funccall(Node *node, deparse_context *context,
bool showimplicit);
static bool looks_like_function(Node *node);
static void get_oper_expr(OpExpr *expr, deparse_context *context); static void get_oper_expr(OpExpr *expr, deparse_context *context);
static void get_func_expr(FuncExpr *expr, deparse_context *context, static void get_func_expr(FuncExpr *expr, deparse_context *context,
bool showimplicit); bool showimplicit);
@ -3282,8 +3285,11 @@ get_update_query_targetlist_def(Query *query, List *targetList,
/* /*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
* Param. That could be buried under FieldStores and ArrayRefs * Param. That could be buried under FieldStores and ArrayRefs
* (cf processIndirection()), and underneath those there could be * and CoerceToDomains (cf processIndirection()), and underneath
* an implicit type coercion. * those there could be an implicit type coercion. Because we
* would ignore implicit type coercions anyway, we don't need to
* be as careful as processIndirection() is about descending past
* implicit CoerceToDomains.
*/ */
expr = (Node *) tle->expr; expr = (Node *) tle->expr;
while (expr) while (expr)
@ -3302,6 +3308,14 @@ get_update_query_targetlist_def(Query *query, List *targetList,
break; break;
expr = (Node *) aref->refassgnexpr; expr = (Node *) aref->refassgnexpr;
} }
else if (IsA(expr, CoerceToDomain))
{
CoerceToDomain *cdomain = (CoerceToDomain *) expr;
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
expr = (Node *) cdomain->arg;
}
else else
break; break;
} }
@ -5763,6 +5777,63 @@ get_rule_expr_toplevel(Node *node, deparse_context *context,
get_rule_expr(node, context, showimplicit); get_rule_expr(node, context, showimplicit);
} }
/*
* get_rule_expr_funccall - Parse back a function-call expression
*
* Same as get_rule_expr(), except that we guarantee that the output will
* look like a function call, or like one of the things the grammar treats as
* equivalent to a function call (see the func_expr_windowless production).
* This is needed in places where the grammar uses func_expr_windowless and
* you can't substitute a parenthesized a_expr. If what we have isn't going
* to look like a function call, wrap it in a dummy CAST() expression, which
* will satisfy the grammar --- and, indeed, is likely what the user wrote to
* produce such a thing.
*/
static void
get_rule_expr_funccall(Node *node, deparse_context *context,
bool showimplicit)
{
if (looks_like_function(node))
get_rule_expr(node, context, showimplicit);
else
{
StringInfo buf = context->buf;
appendStringInfoString(buf, "CAST(");
/* no point in showing any top-level implicit cast */
get_rule_expr(node, context, false);
appendStringInfo(buf, " AS %s)",
format_type_with_typemod(exprType(node),
exprTypmod(node)));
}
}
/*
* Helper function to identify node types that satisfy func_expr_windowless.
* If in doubt, "false" is always a safe answer.
*/
static bool
looks_like_function(Node *node)
{
if (node == NULL)
return false; /* probably shouldn't happen */
switch (nodeTag(node))
{
case T_FuncExpr:
/* OK, unless it's going to deparse as a cast */
return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL);
case T_NullIfExpr:
case T_CoalesceExpr:
case T_MinMaxExpr:
case T_XmlExpr:
/* these are all accepted by func_expr_common_subexpr */
return true;
default:
break;
}
return false;
}
/* /*
* get_oper_expr - Parse back an OpExpr node * get_oper_expr - Parse back an OpExpr node
@ -6641,7 +6712,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (list_length(rte->functions) == 1 && if (list_length(rte->functions) == 1 &&
(rtfunc1->funccolnames == NIL || !rte->funcordinality)) (rtfunc1->funccolnames == NIL || !rte->funcordinality))
{ {
get_rule_expr(rtfunc1->funcexpr, context, true); get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
/* we'll print the coldeflist below, if it has one */ /* we'll print the coldeflist below, if it has one */
} }
else else
@ -6704,7 +6775,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (funcno > 0) if (funcno > 0)
appendStringInfoString(buf, ", "); appendStringInfoString(buf, ", ");
get_rule_expr(rtfunc->funcexpr, context, true); get_rule_expr_funccall(rtfunc->funcexpr, context, true);
if (rtfunc->funccolnames != NIL) if (rtfunc->funccolnames != NIL)
{ {
/* Reconstruct the column definition list */ /* Reconstruct the column definition list */
@ -6894,6 +6965,11 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
if (!PRETTY_PAREN(context)) if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
else if (j->jointype != JOIN_INNER)
{
/* If we didn't say CROSS JOIN above, we must provide an ON */
appendStringInfoString(buf, " ON TRUE");
}
if (!PRETTY_PAREN(context) || j->alias != NULL) if (!PRETTY_PAREN(context) || j->alias != NULL)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
@ -7090,13 +7166,17 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
* *
* We strip any top-level FieldStore or assignment ArrayRef nodes that * We strip any top-level FieldStore or assignment ArrayRef nodes that
* appear in the input, printing them as decoration for the base column * appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression * name (which we assume the caller just printed). We might also need to
* that's to be assigned. * strip CoerceToDomain nodes, but only ones that appear above assignment
* nodes.
*
* Returns the subexpression that's to be assigned.
*/ */
static Node * static Node *
processIndirection(Node *node, deparse_context *context) processIndirection(Node *node, deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
CoerceToDomain *cdomain = NULL;
for (;;) for (;;)
{ {
@ -7144,10 +7224,28 @@ processIndirection(Node *node, deparse_context *context)
*/ */
node = (Node *) aref->refassgnexpr; node = (Node *) aref->refassgnexpr;
} }
else if (IsA(node, CoerceToDomain))
{
cdomain = (CoerceToDomain *) node;
/* If it's an explicit domain coercion, we're done */
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
/* Tentatively descend past the CoerceToDomain */
node = (Node *) cdomain->arg;
}
else else
break; break;
} }
/*
* If we descended past a CoerceToDomain whose argument turned out not to
* be a FieldStore or array assignment, back up to the CoerceToDomain.
* (This is not enough to be fully correct if there are nested implicit
* CoerceToDomains, but such cases shouldn't ever occur.)
*/
if (cdomain && node == (Node *) cdomain->arg)
node = (Node *) cdomain;
return node; return node;
} }