Compare commits

...

11 Commits
main ... v7.0.1

Author SHA1 Message Date
Burak Yucesoy 4873d771e8 Bump Citus version to 7.0.1 2017-09-12 17:25:38 -07:00
Burak Yucesoy e1a641fe9b Add CHANGELOG entry for 7.0.1 release 2017-09-12 17:13:52 -07:00
Marco Slot 6b5baf21fb Wait for I/O to finish after PQputCopyData 2017-09-12 17:13:52 -07:00
Marco Slot 8c0274cba6 Free per-tuple COPY memory in INSERT...SELECT 2017-09-12 17:13:52 -07:00
Marco Slot 907048ace8 Add volatile function in prepared statement regression test 2017-09-12 17:13:52 -07:00
Marco Slot d8bb32bd5a Always copy MultiPlan in GetMultiPlan 2017-09-12 17:13:52 -07:00
Jason Petersen 0012d70b1b Add clarifying comment to RngVarCallbackForDropIdx
We don't need the PARTITION-related logic recently added in PostgreSQL.
2017-09-12 17:13:52 -07:00
Jason Petersen 44ddef6fe8 Update ruleutils_10 with latest PostgreSQL changes
See:
	postgres/postgres@21d304dfed
	postgres/postgres@bb5d6e80b1
	postgres/postgres@d363d42bb9
	postgres/postgres@eb145fdfea
	postgres/postgres@decb08ebdf
	postgres/postgres@a3ca72ae9a
	postgres/postgres@bc2d716ad0
	postgres/postgres@382ceffdf7
	postgres/postgres@c7b8998ebb
	postgres/postgres@e3860ffa4d
	postgres/postgres@76a3df6e5e
2017-09-12 17:13:52 -07:00
Jason Petersen 0ec41de26c Update ruleutils_96 with latest PostgreSQL changes
See:
	postgres/postgres@41ada83774
	postgres/postgres@3b0c2dbed0
	postgres/postgres@ff2d537223
2017-09-12 17:13:52 -07:00
Burak Yucesoy 34c6bd4b44 Bump configure PACKAGE_VERSION 2017-08-28 16:45:20 +03:00
Burak Yucesoy d607368a9e Add CHANGELOG entry for 7.0 release 2017-08-28 16:41:14 +03:00
12 changed files with 520 additions and 148 deletions

View File

@ -1,6 +1,74 @@
### citus v7.0.0 (unreleased) ### ### citus v7.0.1 (September 12, 2017) ###
* Replaces pg_dist_shard_placement metadata table with pg_dist_placement * Fixes a bug that could cause memory leaks in `INSERT ... SELECT` queries
* Fixes a bug that could cause incorrect execution of prepared statements
* Fixes a bug that could cause excessive memory usage during COPY
* Incorporates latest changes from core PostgreSQL code
### citus v7.0.0 (August 28, 2017) ###
* Adds support for PostgreSQL 10
* Drops support for PostgreSQL 9.5
* Adds support for multi-row `INSERT`
* Adds support for router `UPDATE` and `DELETE` queries with subqueries
* Adds infrastructure for distributed deadlock detection
* Deprecates `enable_deadlock_prevention` flag
* Adds support for partitioned tables
* Adds support for creating `UNLOGGED` tables
* Adds support for `SAVEPOINT`
* Adds UDF `citus_create_restore_point` for taking distributed snapshots
* Adds support for evaluating non-pushable `INSERT ... SELECT` queries
* Adds support for subquery pushdown on reference tables
* Adds shard pruning support for `IN` and `ANY`
* Adds support for `UPDATE` and `DELETE` commands that prune down to 0 shard
* Enhances transaction support by relaxing some transaction restrictions
* Fixes a bug causing crash if distributed table has no shards
* Fixes a bug causing crash when removing inactive node
* Fixes a bug causing failure during `COPY` on tables with dropped columns
* Fixes a bug causing failure during `DROP EXTENSION`
* Fixes a bug preventing executing `VACUUM` and `INSERT` concurrently
* Fixes a bug in prepared `INSERT` statements containing an implicit cast
* Fixes several issues related to statement cancellations and connections
* Fixes several 2PC related issues
* Removes an unnecessary dependency causing warning messages in pg_dump
* Adds internal infrastructure for follower clusters
* Adds internal infrastructure for progress tracking
* Implements various performance improvements
* Adds internal infrastructures and tests to improve development process
* Addresses various race conditions and deadlocks
* Improves and standardizes error messages
### citus v6.2.3 (July 13, 2017) ### ### citus v6.2.3 (July 13, 2017) ###

32
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for Citus 7.0devel. # Generated by GNU Autoconf 2.69 for Citus 7.0.1.
# #
# #
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -579,8 +579,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='Citus' PACKAGE_NAME='Citus'
PACKAGE_TARNAME='citus' PACKAGE_TARNAME='citus'
PACKAGE_VERSION='7.0devel' PACKAGE_VERSION='7.0.1'
PACKAGE_STRING='Citus 7.0devel' PACKAGE_STRING='Citus 7.0.1'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='' PACKAGE_URL=''
@ -621,6 +621,7 @@ infodir
docdir docdir
oldincludedir oldincludedir
includedir includedir
runstatedir
localstatedir localstatedir
sharedstatedir sharedstatedir
sysconfdir sysconfdir
@ -693,6 +694,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc' sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com' sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var' localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include' includedir='${prefix}/include'
oldincludedir='/usr/include' oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@ -945,6 +947,15 @@ do
| -silent | --silent | --silen | --sile | --sil) | -silent | --silent | --silen | --sile | --sil)
silent=yes ;; silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;; ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1082,7 +1093,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \ datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir libdir localedir mandir runstatedir
do do
eval ac_val=\$$ac_var eval ac_val=\$$ac_var
# Remove trailing slashes. # Remove trailing slashes.
@ -1195,7 +1206,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures Citus 7.0devel to adapt to many kinds of systems. \`configure' configures Citus 7.0.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1235,6 +1246,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var] --localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib] --libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include] --includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include] --oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -1256,7 +1268,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of Citus 7.0devel:";; short | recursive ) echo "Configuration of Citus 7.0.1:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1344,7 +1356,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
Citus configure 7.0devel Citus configure 7.0.1
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
@ -1401,7 +1413,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by Citus $as_me 7.0devel, which was It was created by Citus $as_me 7.0.1, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $0 $@
@ -3564,7 +3576,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by Citus $as_me 7.0devel, which was This file was extended by Citus $as_me 7.0.1, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -3626,7 +3638,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
Citus config.status 7.0devel Citus config.status 7.0.1
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -5,7 +5,7 @@
# everyone needing autoconf installed, the resulting files are checked # everyone needing autoconf installed, the resulting files are checked
# into the SCM. # into the SCM.
AC_INIT([Citus], [7.0devel]) AC_INIT([Citus], [7.0.1])
AC_COPYRIGHT([Copyright (c) 2012-2017, Citus Data, Inc.]) AC_COPYRIGHT([Copyright (c) 2012-2017, Citus Data, Inc.])
# we'll need sed and awk for some of the version commands # we'll need sed and awk for some of the version commands

View File

@ -2010,6 +2010,14 @@ CitusCopyDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)
copyDest->tuplesSent++; copyDest->tuplesSent++;
/*
* Release per tuple memory allocated in this function. If we're writing
* the results of an INSERT ... SELECT then the SELECT execution will use
* its own executor state and reset the per tuple expression context
* separately.
*/
ResetPerTupleExprContext(executorState);
return true; return true;
} }

View File

@ -497,6 +497,7 @@ PutRemoteCopyData(MultiConnection *connection, const char *buffer, int nbytes)
{ {
PGconn *pgConn = connection->pgConn; PGconn *pgConn = connection->pgConn;
int copyState = 0; int copyState = 0;
bool allowInterrupts = true;
if (PQstatus(pgConn) != CONNECTION_OK) if (PQstatus(pgConn) != CONNECTION_OK)
{ {
@ -506,21 +507,22 @@ PutRemoteCopyData(MultiConnection *connection, const char *buffer, int nbytes)
Assert(PQisnonblocking(pgConn)); Assert(PQisnonblocking(pgConn));
copyState = PQputCopyData(pgConn, buffer, nbytes); copyState = PQputCopyData(pgConn, buffer, nbytes);
if (copyState == -1)
if (copyState == 1)
{
/* successful */
return true;
}
else if (copyState == -1)
{ {
return false; return false;
} }
else
{ /*
bool allowInterrupts = true; * PQputCopyData may have queued up part of the data even if it managed
return FinishConnectionIO(connection, allowInterrupts); * to send some of it succesfully. We provide back pressure by waiting
} * until the socket is writable to prevent the internal libpq buffers
* from growing excessively.
*
* In the future, we could reduce the frequency of these pushbacks to
* achieve higher throughput.
*/
return FinishConnectionIO(connection, allowInterrupts);
} }
@ -535,6 +537,7 @@ PutRemoteCopyEnd(MultiConnection *connection, const char *errormsg)
{ {
PGconn *pgConn = connection->pgConn; PGconn *pgConn = connection->pgConn;
int copyState = 0; int copyState = 0;
bool allowInterrupts = true;
if (PQstatus(pgConn) != CONNECTION_OK) if (PQstatus(pgConn) != CONNECTION_OK)
{ {
@ -544,21 +547,14 @@ PutRemoteCopyEnd(MultiConnection *connection, const char *errormsg)
Assert(PQisnonblocking(pgConn)); Assert(PQisnonblocking(pgConn));
copyState = PQputCopyEnd(pgConn, errormsg); copyState = PQputCopyEnd(pgConn, errormsg);
if (copyState == -1)
if (copyState == 1)
{
/* successful */
return true;
}
else if (copyState == -1)
{ {
return false; return false;
} }
else
{ /* see PutRemoteCopyData() */
bool allowInterrupts = true;
return FinishConnectionIO(connection, allowInterrupts); return FinishConnectionIO(connection, allowInterrupts);
}
} }

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

@ -467,7 +467,14 @@ GetMultiPlan(CustomScan *customScan)
node = CheckNodeCopyAndSerialization(node); node = CheckNodeCopyAndSerialization(node);
multiPlan = (MultiPlan *) node; /*
* When using prepared statements the same plan gets reused across
* multiple statements and transactions. We make several modifications
* to the MultiPlan during execution such as assigning task placements
* and evaluating functions and parameters. These changes should not
* persist, so we always work on a copy.
*/
multiPlan = (MultiPlan *) copyObject(node);
return multiPlan; return multiPlan;
} }

View File

@ -93,7 +93,7 @@
#define PRETTYINDENT_JOIN 4 #define PRETTYINDENT_JOIN 4
#define PRETTYINDENT_VAR 4 #define PRETTYINDENT_VAR 4
#define PRETTYINDENT_LIMIT 40 /* wrap limit */ #define PRETTYINDENT_LIMIT 40 /* wrap limit */
/* Pretty flags */ /* Pretty flags */
#define PRETTYFLAG_PAREN 1 #define PRETTYFLAG_PAREN 1
@ -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;
/* /*
@ -292,7 +292,7 @@ typedef struct
*/ */
typedef struct typedef struct
{ {
char name[NAMEDATALEN]; /* Hash key --- must be first */ char name[NAMEDATALEN]; /* Hash key --- must be first */
int counter; /* Largest addition used so far for name */ int counter; /* Largest addition used so far for name */
} NameHashEntry; } NameHashEntry;
@ -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);
@ -2148,19 +2151,19 @@ get_select_query_def(Query *query, deparse_context *context,
break; break;
case LCS_FORKEYSHARE: case LCS_FORKEYSHARE:
appendContextKeyword(context, " FOR KEY SHARE", appendContextKeyword(context, " FOR KEY SHARE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0); -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
break; break;
case LCS_FORSHARE: case LCS_FORSHARE:
appendContextKeyword(context, " FOR SHARE", appendContextKeyword(context, " FOR SHARE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0); -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
break; break;
case LCS_FORNOKEYUPDATE: case LCS_FORNOKEYUPDATE:
appendContextKeyword(context, " FOR NO KEY UPDATE", appendContextKeyword(context, " FOR NO KEY UPDATE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0); -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
break; break;
case LCS_FORUPDATE: case LCS_FORUPDATE:
appendContextKeyword(context, " FOR UPDATE", appendContextKeyword(context, " FOR UPDATE",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 0); -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
break; break;
} }
@ -3018,8 +3021,8 @@ get_insert_query_def(Query *query, deparse_context *context)
* tle->resname, since resname will fail to track RENAME. * tle->resname, since resname will fail to track RENAME.
*/ */
appendStringInfoString(buf, appendStringInfoString(buf,
quote_identifier(get_relid_attribute_name(rte->relid, quote_identifier(get_relid_attribute_name(rte->relid,
tle->resno))); tle->resno)));
/* /*
* Print any indirection needed (subfields or subscripts), and strip * Print any indirection needed (subfields or subscripts), and strip
@ -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;
} }
@ -3330,7 +3344,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
cur_ma_sublink = (SubLink *) lfirst(next_ma_cell); cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
next_ma_cell = lnext(next_ma_cell); next_ma_cell = lnext(next_ma_cell);
remaining_ma_columns = count_nonjunk_tlist_entries( remaining_ma_columns = count_nonjunk_tlist_entries(
((Query *) cur_ma_sublink->subselect)->targetList); ((Query *) cur_ma_sublink->subselect)->targetList);
Assert(((Param *) expr)->paramid == Assert(((Param *) expr)->paramid ==
((cur_ma_sublink->subLinkId << 16) | 1)); ((cur_ma_sublink->subLinkId << 16) | 1));
appendStringInfoChar(buf, '('); appendStringInfoChar(buf, '(');
@ -3342,8 +3356,8 @@ get_update_query_targetlist_def(Query *query, List *targetList,
* tle->resname, since resname will fail to track RENAME. * tle->resname, since resname will fail to track RENAME.
*/ */
appendStringInfoString(buf, appendStringInfoString(buf,
quote_identifier(get_relid_attribute_name(rte->relid, quote_identifier(get_relid_attribute_name(rte->relid,
tle->resno))); tle->resno)));
/* /*
* Print any indirection needed (subfields or subscripts), and strip * Print any indirection needed (subfields or subscripts), and strip
@ -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:
@ -4532,17 +4547,17 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false; return false;
return true; /* own parentheses */ return true; /* own parentheses */
} }
case T_BoolExpr: /* lower precedence */ case T_BoolExpr: /* lower precedence */
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */ case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_WindowFunc: /* own parentheses */ case T_WindowFunc: /* own parentheses */
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
return true; return true;
default: default:
return false; return false;
@ -4583,16 +4598,16 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false; return false;
return true; /* own parentheses */ return true; /* own parentheses */
} }
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */ case T_MinMaxExpr: /* own parentheses */
case T_XmlExpr: /* own parentheses */ case T_XmlExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_WindowFunc: /* own parentheses */ case T_WindowFunc: /* own parentheses */
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
return true; return true;
default: default:
return false; return false;
@ -4891,7 +4906,7 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfo(buf, " %s %s (", appendStringInfo(buf, " %s %s (",
generate_operator_name(expr->opno, generate_operator_name(expr->opno,
exprType(arg1), exprType(arg1),
get_base_element_type(exprType(arg2))), get_base_element_type(exprType(arg2))),
expr->useOr ? "ANY" : "ALL"); expr->useOr ? "ANY" : "ALL");
get_rule_expr_paren(arg2, context, true, node); get_rule_expr_paren(arg2, context, true, node);
@ -4911,7 +4926,7 @@ get_rule_expr(Node *node, deparse_context *context,
((SubLink *) arg2)->subLinkType == EXPR_SUBLINK) ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
appendStringInfo(buf, "::%s", appendStringInfo(buf, "::%s",
format_type_with_typemod(exprType(arg2), format_type_with_typemod(exprType(arg2),
exprTypmod(arg2))); exprTypmod(arg2)));
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
if (!PRETTY_PAREN(context)) if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
@ -5269,7 +5284,7 @@ get_rule_expr(Node *node, deparse_context *context,
*/ */
if (arrayexpr->elements == NIL) if (arrayexpr->elements == NIL)
appendStringInfo(buf, "::%s", appendStringInfo(buf, "::%s",
format_type_with_typemod(arrayexpr->array_typeid, -1)); format_type_with_typemod(arrayexpr->array_typeid, -1));
} }
break; break;
@ -5331,7 +5346,7 @@ get_rule_expr(Node *node, deparse_context *context,
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
if (rowexpr->row_format == COERCE_EXPLICIT_CAST) if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
appendStringInfo(buf, "::%s", appendStringInfo(buf, "::%s",
format_type_with_typemod(rowexpr->row_typeid, -1)); format_type_with_typemod(rowexpr->row_typeid, -1));
} }
break; break;
@ -5364,9 +5379,9 @@ get_rule_expr(Node *node, deparse_context *context,
* be perfect. * be perfect.
*/ */
appendStringInfo(buf, ") %s ROW(", appendStringInfo(buf, ") %s ROW(",
generate_operator_name(linitial_oid(rcexpr->opnos), generate_operator_name(linitial_oid(rcexpr->opnos),
exprType(linitial(rcexpr->largs)), exprType(linitial(rcexpr->largs)),
exprType(linitial(rcexpr->rargs)))); exprType(linitial(rcexpr->rargs))));
sep = ""; sep = "";
foreach(arg, rcexpr->rargs) foreach(arg, rcexpr->rargs)
{ {
@ -5564,7 +5579,7 @@ get_rule_expr(Node *node, deparse_context *context,
Assert(!con->constisnull); Assert(!con->constisnull);
if (DatumGetBool(con->constvalue)) if (DatumGetBool(con->constvalue))
appendStringInfoString(buf, appendStringInfoString(buf,
" PRESERVE WHITESPACE"); " PRESERVE WHITESPACE");
else else
appendStringInfoString(buf, appendStringInfoString(buf,
" STRIP WHITESPACE"); " STRIP WHITESPACE");
@ -5592,15 +5607,15 @@ get_rule_expr(Node *node, deparse_context *context,
{ {
case XML_STANDALONE_YES: case XML_STANDALONE_YES:
appendStringInfoString(buf, appendStringInfoString(buf,
", STANDALONE YES"); ", STANDALONE YES");
break; break;
case XML_STANDALONE_NO: case XML_STANDALONE_NO:
appendStringInfoString(buf, appendStringInfoString(buf,
", STANDALONE NO"); ", STANDALONE NO");
break; break;
case XML_STANDALONE_NO_VALUE: case XML_STANDALONE_NO_VALUE:
appendStringInfoString(buf, appendStringInfoString(buf,
", STANDALONE NO VALUE"); ", STANDALONE NO VALUE");
break; break;
default: default:
break; break;
@ -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;
@ -5786,7 +5817,7 @@ get_rule_expr(Node *node, deparse_context *context,
if (iexpr->infercollid) if (iexpr->infercollid)
appendStringInfo(buf, " COLLATE %s", appendStringInfo(buf, " COLLATE %s",
generate_collation_name(iexpr->infercollid)); generate_collation_name(iexpr->infercollid));
/* Add the operator class name, if not default */ /* Add the operator class name, if not default */
if (iexpr->inferopclass) if (iexpr->inferopclass)
@ -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
@ -6385,7 +6432,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
else else
{ {
appendStringInfo(buf, "'%s'", extval); appendStringInfo(buf, "'%s'", extval);
needlabel = true; /* we must attach a cast */ needlabel = true; /* we must attach a cast */
} }
break; break;
@ -6404,7 +6451,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
else else
{ {
appendStringInfo(buf, "'%s'", extval); appendStringInfo(buf, "'%s'", extval);
needlabel = true; /* we must attach a cast */ needlabel = true; /* we must attach a cast */
} }
break; break;
@ -6566,8 +6613,8 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
get_rule_expr(linitial(opexpr->args), context, true); get_rule_expr(linitial(opexpr->args), context, true);
if (!opname) if (!opname)
opname = generate_operator_name(opexpr->opno, opname = generate_operator_name(opexpr->opno,
exprType(linitial(opexpr->args)), exprType(linitial(opexpr->args)),
exprType(lsecond(opexpr->args))); exprType(lsecond(opexpr->args)));
sep = ", "; sep = ", ";
} }
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
@ -6581,7 +6628,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
get_rule_expr((Node *) rcexpr->largs, context, true); get_rule_expr((Node *) rcexpr->largs, context, true);
opname = generate_operator_name(linitial_oid(rcexpr->opnos), opname = generate_operator_name(linitial_oid(rcexpr->opnos),
exprType(linitial(rcexpr->largs)), exprType(linitial(rcexpr->largs)),
exprType(linitial(rcexpr->rargs))); exprType(linitial(rcexpr->rargs)));
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
} }
else else
@ -6598,7 +6645,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
break; break;
case ANY_SUBLINK: case ANY_SUBLINK:
if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */ if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
appendStringInfoString(buf, " IN "); appendStringInfoString(buf, " IN ");
else else
appendStringInfo(buf, " %s ANY ", opname); appendStringInfo(buf, " %s ANY ", opname);
@ -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 (;;)
{ {
@ -7404,7 +7460,7 @@ processIndirection(Node *node, deparse_context *context)
*/ */
Assert(list_length(fstore->fieldnums) == 1); Assert(list_length(fstore->fieldnums) == 1);
fieldname = get_relid_attribute_name(typrelid, fieldname = get_relid_attribute_name(typrelid,
linitial_int(fstore->fieldnums)); linitial_int(fstore->fieldnums));
appendStringInfo(buf, ".%s", quote_identifier(fieldname)); appendStringInfo(buf, ".%s", quote_identifier(fieldname));
/* /*
@ -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;
} }

View File

@ -129,7 +129,7 @@ ALTER EXTENSION citus UPDATE TO '7.0-15';
SHOW citus.version; SHOW citus.version;
citus.version citus.version
--------------- ---------------
7.0devel 7.0.1
(1 row) (1 row)
-- ensure no objects were created outside pg_catalog -- ensure no objects were created outside pg_catalog

View File

@ -998,6 +998,42 @@ SELECT key, value FROM domain_partition_column_table ORDER BY key;
(6 rows) (6 rows)
DROP TABLE domain_partition_column_table; DROP TABLE domain_partition_column_table;
-- verify we re-evaluate volatile functions every time
CREATE TABLE http_request (
site_id INT,
ingest_time TIMESTAMPTZ DEFAULT now(),
url TEXT,
request_country TEXT,
ip_address TEXT,
status_code INT,
response_time_msec INT
);
SELECT create_distributed_table('http_request', 'site_id');
create_distributed_table
--------------------------
(1 row)
PREPARE FOO AS INSERT INTO http_request (
site_id, ingest_time, url, request_country,
ip_address, status_code, response_time_msec
) VALUES (
1, clock_timestamp(), 'http://example.com/path', 'USA',
inet '88.250.10.123', 200, 10
);
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
SELECT count(distinct ingest_time) FROM http_request WHERE site_id = 1;
count
-------
6
(1 row)
DROP TABLE http_request;
-- verify placement state updates invalidate shard state -- verify placement state updates invalidate shard state
-- --
-- We use a immutable function to check for that. The planner will -- We use a immutable function to check for that. The planner will

View File

@ -533,6 +533,37 @@ SELECT key, value FROM domain_partition_column_table ORDER BY key;
DROP TABLE domain_partition_column_table; DROP TABLE domain_partition_column_table;
-- verify we re-evaluate volatile functions every time
CREATE TABLE http_request (
site_id INT,
ingest_time TIMESTAMPTZ DEFAULT now(),
url TEXT,
request_country TEXT,
ip_address TEXT,
status_code INT,
response_time_msec INT
);
SELECT create_distributed_table('http_request', 'site_id');
PREPARE FOO AS INSERT INTO http_request (
site_id, ingest_time, url, request_country,
ip_address, status_code, response_time_msec
) VALUES (
1, clock_timestamp(), 'http://example.com/path', 'USA',
inet '88.250.10.123', 200, 10
);
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
EXECUTE foo;
SELECT count(distinct ingest_time) FROM http_request WHERE site_id = 1;
DROP TABLE http_request;
-- verify placement state updates invalidate shard state -- verify placement state updates invalidate shard state
-- --
-- We use a immutable function to check for that. The planner will -- We use a immutable function to check for that. The planner will