1515#include "catalog/namespace.h"
1616#include "lib/stringinfo.h"
1717#include "nodes/nodes.h"
18+ #include "parser/parse_expr.h"
1819#include "utils/builtins.h"
20+ #include "utils/lsyscache.h"
21+ #include "utils/ruleutils.h"
1922
2023#include "pg_version_constants.h"
2124
2225#include "distributed/citus_ruleutils.h"
26+ #include "distributed/commands.h"
2327#include "distributed/deparser.h"
2428#include "distributed/listutils.h"
2529#include "distributed/relay_utility.h"
@@ -231,6 +235,42 @@ AppendStatTypes(StringInfo buf, CreateStatsStmt *stmt)
231235}
232236
233237
238+ /* See ruleutils.c in postgres for the logic here. */
239+ static bool
240+ looks_like_function (Node * node )
241+ {
242+ if (node == NULL )
243+ {
244+ return false; /* probably shouldn't happen */
245+ }
246+ switch (nodeTag (node ))
247+ {
248+ case T_FuncExpr :
249+ {
250+ /* OK, unless it's going to deparse as a cast */
251+ return (((FuncExpr * ) node )-> funcformat == COERCE_EXPLICIT_CALL ||
252+ ((FuncExpr * ) node )-> funcformat == COERCE_SQL_SYNTAX );
253+ }
254+
255+ case T_NullIfExpr :
256+ case T_CoalesceExpr :
257+ case T_MinMaxExpr :
258+ case T_SQLValueFunction :
259+ case T_XmlExpr :
260+ {
261+ /* these are all accepted by func_expr_common_subexpr */
262+ return true;
263+ }
264+
265+ default :
266+ {
267+ break ;
268+ }
269+ }
270+ return false;
271+ }
272+
273+
234274static void
235275AppendColumnNames (StringInfo buf , CreateStatsStmt * stmt )
236276{
@@ -240,15 +280,54 @@ AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt)
240280 {
241281 if (!column -> name )
242282 {
243- ereport (ERROR ,
244- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
245- errmsg ("only simple column references are allowed "
246- "in CREATE STATISTICS" )));
283+ /*
284+ * Since these expressions are parser statements, we first call
285+ * transform to get the transformed Expr tree, and then deparse
286+ * the transformed tree. This is similar to the logic found in
287+ * deparse_table_stmts for check constraints.
288+ */
289+ if (list_length (stmt -> relations ) != 1 )
290+ {
291+ ereport (ERROR , (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
292+ errmsg (
293+ "cannot use expressions in CREATE STATISTICS with multiple relations" )));
294+ }
295+
296+ RangeVar * rel = (RangeVar * ) linitial (stmt -> relations );
297+ bool missingOk = false;
298+ Oid relOid = RangeVarGetRelid (rel , AccessShareLock , missingOk );
299+
300+ /* Add table name to the name space in parse state. Otherwise column names
301+ * cannot be found.
302+ */
303+ Relation relation = table_open (relOid , AccessShareLock );
304+ ParseState * pstate = make_parsestate (NULL );
305+ AddRangeTableEntryToQueryCompat (pstate , relation );
306+ Node * exprCooked = transformExpr (pstate , column -> expr ,
307+ EXPR_KIND_STATS_EXPRESSION );
308+
309+ char * relationName = get_rel_name (relOid );
310+ List * relationCtx = deparse_context_for (relationName , relOid );
311+
312+ char * exprSql = deparse_expression (exprCooked , relationCtx , false, false);
313+ relation_close (relation , NoLock );
314+
315+ /* Need parens if it's not a bare function call */
316+ if (looks_like_function (exprCooked ))
317+ {
318+ appendStringInfoString (buf , exprSql );
319+ }
320+ else
321+ {
322+ appendStringInfo (buf , "(%s)" , exprSql );
323+ }
247324 }
325+ else
326+ {
327+ const char * columnName = quote_identifier (column -> name );
248328
249- const char * columnName = quote_identifier (column -> name );
250-
251- appendStringInfoString (buf , columnName );
329+ appendStringInfoString (buf , columnName );
330+ }
252331
253332 if (column != llast (stmt -> exprs ))
254333 {
0 commit comments