safer node casting
There is a common coding pattern that goes like this:
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
(Arguably, the Assert should come before the cast, but I guess it's done
this way out of convenience.)
(Not to mention the other common coding pattern of just doing the cast
and hoping for the best.)
I propose a macro castNode() that combines the assertion and the cast,
so this would become
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
This is inspired by the dynamic_cast operator in C++, but follows the
syntax of the well-known makeNode() macro.
Besides saving a bunch of code and making things safer, the function
syntax also makes some code easier to read by saving levels of
parentheses, for example:
- Assert(IsA(sstate->testexpr, BoolExprState));
- oplist = ((BoolExprState *) sstate->testexpr)->args;
+ oplist = castNode(BoolExprState, sstate->testexpr)->args;
Attached is a patch that shows how this would work. There is a lot more
that can be done, but I just stopped after a while for now.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
0001-Add-castNode-macro.patchtext/x-patch; name=0001-Add-castNode-macro.patchDownload
From fd1e766ff765ecc58380d09c657ca254a6bf5674 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Wed, 28 Dec 2016 12:00:00 -0500
Subject: [PATCH] Add castNode() macro
---
contrib/pg_stat_statements/pg_stat_statements.c | 10 ++++------
contrib/postgres_fdw/deparse.c | 5 +----
contrib/postgres_fdw/postgres_fdw.c | 12 ++++--------
src/backend/catalog/pg_proc.c | 3 +--
src/backend/commands/aggregatecmds.c | 4 ++--
src/backend/commands/analyze.c | 6 +++---
src/backend/commands/async.c | 8 ++++----
src/backend/commands/collationcmds.c | 2 +-
src/backend/commands/constraint.c | 2 +-
src/backend/commands/copy.c | 14 +++++++-------
src/backend/commands/createas.c | 5 ++---
src/backend/commands/dropcmds.c | 4 +---
src/backend/commands/explain.c | 16 +++++++---------
src/backend/commands/functioncmds.c | 7 ++-----
src/backend/commands/matview.c | 3 +--
src/backend/commands/opclasscmds.c | 9 +++------
src/backend/commands/tablecmds.c | 14 ++++----------
src/backend/commands/trigger.c | 4 +---
src/backend/commands/user.c | 4 +---
src/backend/commands/view.c | 3 +--
src/backend/executor/execAmi.c | 3 +--
src/backend/executor/execQual.c | 6 ++----
src/backend/executor/execTuples.c | 5 +----
src/backend/executor/functions.c | 6 ++----
src/backend/executor/nodeAgg.c | 6 ++----
src/backend/executor/nodeCtescan.c | 3 +--
src/backend/executor/nodeCustom.c | 3 +--
src/backend/executor/nodeHashjoin.c | 7 ++-----
src/backend/executor/nodeLockRows.c | 4 +---
src/backend/executor/nodeModifyTable.c | 4 +---
src/backend/executor/nodeSubplan.c | 15 +++++----------
src/backend/executor/nodeWorktablescan.c | 4 ++--
src/include/nodes/nodes.h | 2 ++
33 files changed, 74 insertions(+), 129 deletions(-)
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0401..d21f1d044c 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2336,9 +2336,8 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
foreach(lc, rtable)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+ RangeTblEntry *rte = castNode(RangeTblEntry, lfirst(lc));
- Assert(IsA(rte, RangeTblEntry));
APP_JUMB(rte->rtekind);
switch (rte->rtekind)
{
@@ -2524,7 +2523,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
APP_JUMB(sublink->subLinkType);
APP_JUMB(sublink->subLinkId);
JumbleExpr(jstate, (Node *) sublink->testexpr);
- JumbleQuery(jstate, (Query *) sublink->subselect);
+ JumbleQuery(jstate, castNode(Query, sublink->subselect));
}
break;
case T_FieldSelect:
@@ -2590,9 +2589,8 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) caseexpr->arg);
foreach(temp, caseexpr->args)
{
- CaseWhen *when = (CaseWhen *) lfirst(temp);
+ CaseWhen *when = castNode(CaseWhen, lfirst(temp));
- Assert(IsA(when, CaseWhen));
JumbleExpr(jstate, (Node *) when->expr);
JumbleExpr(jstate, (Node *) when->result);
}
@@ -2804,7 +2802,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
/* we store the string name because RTE_CTE RTEs need it */
APP_JUMB_STRING(cte->ctename);
- JumbleQuery(jstate, (Query *) cte->ctequery);
+ JumbleQuery(jstate, castNode(Query, cte->ctequery));
}
break;
case T_SetOperationStmt:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 66b059ac96..1453f49a46 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -1315,10 +1315,7 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs,
foreach(lc, tlist)
{
- TargetEntry *tle = (TargetEntry *) lfirst(lc);
-
- /* Extract expression if TargetEntry node */
- Assert(IsA(tle, TargetEntry));
+ TargetEntry *tle = castNode(TargetEntry, lfirst(lc));
if (i > 0)
appendStringInfoString(buf, ", ");
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fbe69295e7..de9d971892 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1159,9 +1159,7 @@ postgresGetForeignPlan(PlannerInfo *root,
*/
foreach(lc, scan_clauses)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
-
- Assert(IsA(rinfo, RestrictInfo));
+ RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
/* Ignore any pseudoconstants, they're dealt with elsewhere */
if (rinfo->pseudoconstant)
@@ -4958,14 +4956,12 @@ conversion_error_callback(void *arg)
{
/* error occurred in a scan against a foreign join */
ForeignScanState *fsstate = errpos->fsstate;
- ForeignScan *fsplan = (ForeignScan *) fsstate->ss.ps.plan;
+ ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
EState *estate = fsstate->ss.ps.state;
TargetEntry *tle;
- Assert(IsA(fsplan, ForeignScan));
- tle = (TargetEntry *) list_nth(fsplan->fdw_scan_tlist,
- errpos->cur_attno - 1);
- Assert(IsA(tle, TargetEntry));
+ tle = castNode(TargetEntry, list_nth(fsplan->fdw_scan_tlist,
+ errpos->cur_attno - 1));
/*
* Target list can have Vars and expressions. For Vars, we can get
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index c1d1505e64..d1e3c6eff1 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -513,8 +513,7 @@ ProcedureCreate(const char *procedureName,
Anum_pg_proc_proargdefaults,
&isnull);
Assert(!isnull);
- oldDefaults = (List *) stringToNode(TextDatumGetCString(proargdefaults));
- Assert(IsA(oldDefaults, List));
+ oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults)));
Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
/* new list can have more defaults than old, advance over 'em */
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 19db38d7fc..f338273a93 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -109,13 +109,13 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List
aggKind = AGGKIND_ORDERED_SET;
else
numDirectArgs = 0;
- args = (List *) linitial(args);
+ args = castNode(List, linitial(args));
}
/* Examine aggregate's definition clauses */
foreach(pl, parameters)
{
- DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem *defel = castNode(DefElem, lfirst(pl));
/*
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index f4afcd9aae..9f9c74cd7c 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -722,9 +722,9 @@ compute_index_stats(Relation onerel, double totalrows,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = castNode(List,
+ ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+ estate));
/* Compute and save index expression values */
exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 716f1c3318..6d9308ea81 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1636,7 +1636,7 @@ AtSubCommit_Notify(void)
List *parentPendingActions;
List *parentPendingNotifies;
- parentPendingActions = (List *) linitial(upperPendingActions);
+ parentPendingActions = castNode(List, linitial(upperPendingActions));
upperPendingActions = list_delete_first(upperPendingActions);
Assert(list_length(upperPendingActions) ==
@@ -1647,7 +1647,7 @@ AtSubCommit_Notify(void)
*/
pendingActions = list_concat(parentPendingActions, pendingActions);
- parentPendingNotifies = (List *) linitial(upperPendingNotifies);
+ parentPendingNotifies = castNode(List, linitial(upperPendingNotifies));
upperPendingNotifies = list_delete_first(upperPendingNotifies);
Assert(list_length(upperPendingNotifies) ==
@@ -1679,13 +1679,13 @@ AtSubAbort_Notify(void)
*/
while (list_length(upperPendingActions) > my_level - 2)
{
- pendingActions = (List *) linitial(upperPendingActions);
+ pendingActions = castNode(List, linitial(upperPendingActions));
upperPendingActions = list_delete_first(upperPendingActions);
}
while (list_length(upperPendingNotifies) > my_level - 2)
{
- pendingNotifies = (List *) linitial(upperPendingNotifies);
+ pendingNotifies = castNode(List, linitial(upperPendingNotifies));
upperPendingNotifies = list_delete_first(upperPendingNotifies);
}
}
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 9bba748708..7e20e1ada4 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -61,7 +61,7 @@ DefineCollation(ParseState *pstate, List *names, List *parameters)
foreach(pl, parameters)
{
- DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem *defel = castNode(DefElem, lfirst(pl));
DefElem **defelp;
if (pg_strcasecmp(defel->defname, "from") == 0)
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 26f9114f55..775cbbef88 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -37,7 +37,7 @@
Datum
unique_key_recheck(PG_FUNCTION_ARGS)
{
- TriggerData *trigdata = (TriggerData *) fcinfo->context;
+ TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
const char *funcname = "unique_key_recheck";
HeapTuple new_row;
ItemPointerData tmptid;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index aa25a23336..d5f8973431 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1017,7 +1017,7 @@ ProcessCopyOptions(ParseState *pstate,
/* Extract options from the statement node tree */
foreach(option, options)
{
- DefElem *defel = (DefElem *) lfirst(option);
+ DefElem *defel = castNode(DefElem, lfirst(option));
if (strcmp(defel->defname, "format") == 0)
{
@@ -1114,7 +1114,7 @@ ProcessCopyOptions(ParseState *pstate,
if (defel->arg && IsA(defel->arg, A_Star))
cstate->force_quote_all = true;
else if (defel->arg && IsA(defel->arg, List))
- cstate->force_quote = (List *) defel->arg;
+ cstate->force_quote = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1130,7 +1130,7 @@ ProcessCopyOptions(ParseState *pstate,
errmsg("conflicting or redundant options"),
parser_errposition(pstate, defel->location)));
if (defel->arg && IsA(defel->arg, List))
- cstate->force_notnull = (List *) defel->arg;
+ cstate->force_notnull = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1145,7 +1145,7 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
if (defel->arg && IsA(defel->arg, List))
- cstate->force_null = (List *) defel->arg;
+ cstate->force_null = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1167,7 +1167,7 @@ ProcessCopyOptions(ParseState *pstate,
parser_errposition(pstate, defel->location)));
cstate->convert_selectively = true;
if (defel->arg == NULL || IsA(defel->arg, List))
- cstate->convert_select = (List *) defel->arg;
+ cstate->convert_select = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1467,7 +1467,7 @@ BeginCopy(ParseState *pstate,
/* examine queries to determine which error message to issue */
foreach(lc, rewritten)
{
- Query *q = (Query *) lfirst(lc);
+ Query *q = castNode(Query, lfirst(lc));
if (q->querySource == QSRC_QUAL_INSTEAD_RULE)
ereport(ERROR,
@@ -1484,7 +1484,7 @@ BeginCopy(ParseState *pstate,
errmsg("multi-statement DO INSTEAD rules are not supported for COPY")));
}
- query = (Query *) linitial(rewritten);
+ query = castNode(Query, linitial(rewritten));
/* The grammar allows SELECT INTO, but we don't support that */
if (query->utilityStmt != NULL &&
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d6d52d9929..ffd6e8cab9 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -224,7 +224,7 @@ ObjectAddress
ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag)
{
- Query *query = (Query *) stmt->query;
+ Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
bool is_matview = (into->viewQuery != NULL);
DestReceiver *dest;
@@ -261,11 +261,10 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
* The contained Query could be a SELECT, or an EXECUTE utility command.
* If the latter, we just pass it off to ExecuteQuery.
*/
- Assert(IsA(query, Query));
if (query->commandType == CMD_UTILITY &&
IsA(query->utilityStmt, ExecuteStmt))
{
- ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
+ ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 61ff8f2190..fae045787f 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -222,12 +222,10 @@ type_in_list_does_not_exist_skipping(List *typenames, const char **msg,
foreach(l, typenames)
{
- TypeName *typeName = (TypeName *) lfirst(l);
+ TypeName *typeName = castNode(TypeName, lfirst(l));
if (typeName != NULL)
{
- Assert(IsA(typeName, TypeName));
-
if (!OidIsValid(LookupTypeNameOid(NULL, typeName, true)))
{
/* type doesn't exist, try to find why */
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0a669d9b43..f032ef87e3 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1466,25 +1466,25 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
break;
case T_Agg:
- show_agg_keys((AggState *) planstate, ancestors, es);
+ show_agg_keys(castNode(AggState, planstate), ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_Group:
- show_group_keys((GroupState *) planstate, ancestors, es);
+ show_group_keys(castNode(GroupState, planstate), ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_Sort:
- show_sort_keys((SortState *) planstate, ancestors, es);
- show_sort_info((SortState *) planstate, es);
+ show_sort_keys(castNode(SortState, planstate), ancestors, es);
+ show_sort_info(castNode(SortState, planstate), es);
break;
case T_MergeAppend:
- show_merge_append_keys((MergeAppendState *) planstate,
+ show_merge_append_keys(castNode(MergeAppendState, planstate),
ancestors, es);
break;
case T_Result:
@@ -1496,11 +1496,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
break;
case T_ModifyTable:
- show_modifytable_info((ModifyTableState *) planstate, ancestors,
+ show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
es);
break;
case T_Hash:
- show_hash_info((HashState *) planstate, es);
+ show_hash_info(castNode(HashState, planstate), es);
break;
default:
break;
@@ -2156,7 +2156,6 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
static void
show_sort_info(SortState *sortstate, ExplainState *es)
{
- Assert(IsA(sortstate, SortState));
if (es->analyze && sortstate->sort_Done &&
sortstate->tuplesortstate != NULL)
{
@@ -2190,7 +2189,6 @@ show_hash_info(HashState *hashstate, ExplainState *es)
{
HashJoinTable hashtable;
- Assert(IsA(hashstate, HashState));
hashtable = hashstate->hashtable;
if (hashtable)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index becafc3045..c2a33ec279 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -578,9 +578,8 @@ update_proconfig_value(ArrayType *a, List *set_items)
foreach(l, set_items)
{
- VariableSetStmt *sstmt = (VariableSetStmt *) lfirst(l);
+ VariableSetStmt *sstmt = castNode(VariableSetStmt, lfirst(l));
- Assert(IsA(sstmt, VariableSetStmt));
if (sstmt->kind == VAR_RESET_ALL)
a = NULL;
else
@@ -971,9 +970,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
{
ListCell *lc;
- Assert(IsA(transformDefElem, List));
-
- foreach(lc, (List *) transformDefElem)
+ foreach(lc, castNode(List, transformDefElem))
{
Oid typeid = typenameTypeId(NULL, lfirst(lc));
Oid elt = get_base_element_type(typeid);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 6cddcbd02c..c81df5173e 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -264,8 +264,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
* The stored query was rewritten at the time of the MV definition, but
* has not been scribbled on by the planner.
*/
- dataQuery = (Query *) linitial(actions);
- Assert(IsA(dataQuery, Query));
+ dataQuery = castNode(Query, linitial(actions));
/*
* Check for active uses of the relation in the current transaction, such
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index f4dfdb9642..b229846770 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -462,13 +462,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
*/
foreach(l, stmt->items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
@@ -847,13 +846,12 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
*/
foreach(l, items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
@@ -981,12 +979,11 @@ AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
*/
foreach(l, items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid lefttype,
righttype;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2f605ce83d..e7933e7172 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5932,11 +5932,10 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
colName)));
/* Generate new proposed attoptions (text array) */
- Assert(IsA(options, List));
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- (List *) options, NULL, NULL, false,
+ castNode(List, options), NULL, NULL, false,
isReset);
/* Validate new options */
(void) attribute_reloptions(newOptions, true);
@@ -7141,8 +7140,7 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
bool found = false;
ObjectAddress address;
- Assert(IsA(cmd->def, Constraint));
- cmdcon = (Constraint *) cmd->def;
+ cmdcon = castNode(Constraint, cmd->def);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
@@ -9290,9 +9288,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
IndexStmt *indstmt;
Oid indoid;
- Assert(IsA(cmd->def, IndexStmt));
-
- indstmt = (IndexStmt *) cmd->def;
+ indstmt = castNode(IndexStmt, cmd->def);
indoid = get_constraint_index(oldId);
if (!rewrite)
@@ -9315,9 +9311,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
{
Constraint *con;
- Assert(IsA(cmd->def, Constraint));
-
- con = (Constraint *) cmd->def;
+ con = castNode(Constraint, cmd->def);
con->old_pktable_oid = refRelId;
/* rewriting neither side of a FK */
if (con->contype == CONSTR_FOREIGN &&
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 02e9693f28..029425a12d 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -340,9 +340,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
foreach(lc, varList)
{
- TriggerTransition *tt = (TriggerTransition *) lfirst(lc);
-
- Assert(IsA(tt, TriggerTransition));
+ TriggerTransition *tt = castNode(TriggerTransition, lfirst(lc));
if (!(tt->isTable))
ereport(ERROR,
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index a3521e7757..6d2786ffef 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1396,11 +1396,9 @@ roleSpecsToIds(List *memberNames)
foreach(l, memberNames)
{
- RoleSpec *rolespec = (RoleSpec *) lfirst(l);
+ RoleSpec *rolespec = castNode(RoleSpec, lfirst(l));
Oid roleid;
- Assert(IsA(rolespec, RoleSpec));
-
roleid = get_rolespec_oid(rolespec, false);
result = lappend_oid(result, roleid);
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 414507f3b7..70e4bb48b0 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -511,9 +511,8 @@ DefineView(ViewStmt *stmt, const char *queryString)
foreach(targetList, viewParse->targetList)
{
- TargetEntry *te = (TargetEntry *) lfirst(targetList);
+ TargetEntry *te = castNode(TargetEntry, lfirst(targetList));
- Assert(IsA(te, TargetEntry));
/* junk columns don't get aliases */
if (te->resjunk)
continue;
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 2587ef7046..a8b3e6c4ce 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -399,8 +399,7 @@ ExecSupportsMarkRestore(Path *pathnode)
return true;
case T_CustomScan:
- Assert(IsA(pathnode, CustomPath));
- if (((CustomPath *) pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
+ if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
return true;
return false;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ec1ca01c5a..e333242b27 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -4940,10 +4940,9 @@ ExecInitExpr(Expr *node, PlanState *parent)
cstate->arg = ExecInitExpr(caseexpr->arg, parent);
foreach(l, caseexpr->args)
{
- CaseWhen *when = (CaseWhen *) lfirst(l);
+ CaseWhen *when = castNode(CaseWhen, lfirst(l));
CaseWhenState *wstate = makeNode(CaseWhenState);
- Assert(IsA(when, CaseWhen));
wstate->xprstate.evalfunc = NULL; /* not used */
wstate->xprstate.expr = (Expr *) when;
wstate->expr = ExecInitExpr(when->expr, parent);
@@ -5437,9 +5436,8 @@ ExecCleanTargetListLength(List *targetlist)
foreach(tl, targetlist)
{
- TargetEntry *curTle = (TargetEntry *) lfirst(tl);
+ TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
- Assert(IsA(curTle, TargetEntry));
if (!curTle->resjunk)
len++;
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 533050dc85..cf37747429 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -160,10 +160,7 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
foreach(lc, tupleTable)
{
- TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc);
-
- /* Sanity checks */
- Assert(IsA(slot, TupleTableSlot));
+ TupleTableSlot *slot = castNode(TupleTableSlot, lfirst(lc));
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 470db5bb4a..c80058ddb6 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -479,19 +479,17 @@ init_execution_state(List *queryTree_list,
foreach(lc1, queryTree_list)
{
- List *qtlist = (List *) lfirst(lc1);
+ List *qtlist = castNode(List, lfirst(lc1));
execution_state *firstes = NULL;
execution_state *preves = NULL;
ListCell *lc2;
foreach(lc2, qtlist)
{
- Query *queryTree = (Query *) lfirst(lc2);
+ Query *queryTree = castNode(Query, lfirst(lc2));
Node *stmt;
execution_state *newes;
- Assert(IsA(queryTree, Query));
-
/* Plan the query if needed */
if (queryTree->commandType == CMD_UTILITY)
stmt = queryTree->utilityStmt;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index ad5edbad29..8859b49fc0 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2607,8 +2607,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (phase > 0)
{
aggnode = list_nth(node->chain, phase - 1);
- sortnode = (Sort *) aggnode->plan.lefttree;
- Assert(IsA(sortnode, Sort));
+ sortnode = castNode(Sort, aggnode->plan.lefttree);
}
else
{
@@ -3044,10 +3043,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
*/
foreach(arg, pertrans->aggref->args)
{
- TargetEntry *source_tle = (TargetEntry *) lfirst(arg);
+ TargetEntry *source_tle = castNode(TargetEntry, lfirst(arg));
TargetEntry *tle;
- Assert(IsA(source_tle, TargetEntry));
tle = flatCopyTargetEntry(source_tle);
tle->resno += column_offset;
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 162650ad8a..1228601cfd 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -210,7 +210,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
prmdata = &(estate->es_param_exec_vals[node->cteParam]);
Assert(prmdata->execPlan == NULL);
Assert(!prmdata->isnull);
- scanstate->leader = (CteScanState *) DatumGetPointer(prmdata->value);
+ scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
if (scanstate->leader == NULL)
{
/* I am the leader */
@@ -223,7 +223,6 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
else
{
/* Not the leader */
- Assert(IsA(scanstate->leader, CteScanState));
/* Create my own read pointer, and ensure it is at start */
scanstate->readptr =
tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca282..e53fc35d5e 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -35,8 +35,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
* methods field correctly at this time. Other standard fields should be
* set to zero.
*/
- css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan);
- Assert(IsA(css, CustomScanState));
+ css = castNode(CustomScanState, cscan->methods->CreateCustomScanState(cscan));
/* ensure flags is filled correctly */
css->flags = cscan->flags;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666f88..bd869e9fc9 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -570,12 +570,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
hoperators = NIL;
foreach(l, hjstate->hashclauses)
{
- FuncExprState *fstate = (FuncExprState *) lfirst(l);
- OpExpr *hclause;
+ FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
+ OpExpr *hclause = castNode(OpExpr, fstate->xprstate.expr);
- Assert(IsA(fstate, FuncExprState));
- hclause = (OpExpr *) fstate->xprstate.expr;
- Assert(IsA(hclause, OpExpr));
lclauses = lappend(lclauses, linitial(fstate->args));
rclauses = lappend(rclauses, lsecond(fstate->args));
hoperators = lappend_oid(hoperators, hclause->opno);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 4ebcaffe69..3f97de635e 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -401,12 +401,10 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
epq_arowmarks = NIL;
foreach(lc, node->rowMarks)
{
- PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
+ PlanRowMark *rc = castNode(PlanRowMark, lfirst(lc));
ExecRowMark *erm;
ExecAuxRowMark *aerm;
- Assert(IsA(rc, PlanRowMark));
-
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 0d85b151c2..b534bd6134 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1901,11 +1901,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
foreach(l, node->rowMarks)
{
- PlanRowMark *rc = (PlanRowMark *) lfirst(l);
+ PlanRowMark *rc = castNode(PlanRowMark, lfirst(l));
ExecRowMark *erm;
- Assert(IsA(rc, PlanRowMark));
-
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index d3436000d0..adcc1240ba 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -814,8 +814,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
else if (and_clause((Node *) sstate->testexpr->expr))
{
/* multiple combining operators */
- Assert(IsA(sstate->testexpr, BoolExprState));
- oplist = ((BoolExprState *) sstate->testexpr)->args;
+ oplist = castNode(BoolExprState, sstate->testexpr)->args;
}
else
{
@@ -835,8 +834,8 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
i = 1;
foreach(l, oplist)
{
- FuncExprState *fstate = (FuncExprState *) lfirst(l);
- OpExpr *opexpr = (OpExpr *) fstate->xprstate.expr;
+ FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
+ OpExpr *opexpr = castNode(OpExpr, fstate->xprstate.expr);
ExprState *exstate;
Expr *expr;
TargetEntry *tle;
@@ -845,8 +844,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
Oid left_hashfn;
Oid right_hashfn;
- Assert(IsA(fstate, FuncExprState));
- Assert(IsA(opexpr, OpExpr));
Assert(list_length(fstate->args) == 2);
/* Process lefthand argument */
@@ -1226,10 +1223,8 @@ ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprDoneCond *isDone)
{
/* Just pass control to the active subplan */
- SubPlanState *activesp = (SubPlanState *) list_nth(node->subplans,
- node->active);
-
- Assert(IsA(activesp, SubPlanState));
+ SubPlanState *activesp = castNode(SubPlanState,
+ list_nth(node->subplans, node->active));
return ExecSubPlan(activesp,
econtext,
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6329..993d03563c 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -95,8 +95,8 @@ ExecWorkTableScan(WorkTableScanState *node)
param = &(estate->es_param_exec_vals[plan->wtParam]);
Assert(param->execPlan == NULL);
Assert(!param->isnull);
- node->rustate = (RecursiveUnionState *) DatumGetPointer(param->value);
- Assert(node->rustate && IsA(node->rustate, RecursiveUnionState));
+ node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
+ Assert(node->rustate);
/*
* The scan tuple type (ie, the rowtype we expect to find in the work
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 201f248774..39a3c3fb9e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -549,6 +549,8 @@ extern PGDLLIMPORT Node *newNodeMacroHolder;
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
+#define castNode(_type_,nodeptr) (AssertMacro(!nodeptr || IsA(nodeptr,_type_)), (_type_ *)(nodeptr))
+
/* ----------------------------------------------------------------
* extern declarations follow
* ----------------------------------------------------------------
--
2.11.0
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
I propose a macro castNode() that combines the assertion and the cast,
so this would become
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Seems like an OK idea, but I'm concerned by the implied multiple
evaluations, particularly if you're going to apply this to function
results --- as the above example does. I think you need to go the
extra mile to make it single-evaluation. See newNode() for ideas.
Just to bikeshed a bit ... would "castNode" be a better name?
Seems like a closer analogy to makeNode(), for instance.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
Just to bikeshed a bit ... would "castNode" be a better name?
Um ... -ENOCAFFEINE. Never mind that bit.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Dec 31, 2016 at 11:30 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
I propose a macro castNode() that combines the assertion and the cast,
so this would become
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
+1. That would be wonderful.
Seems like an OK idea, but I'm concerned by the implied multiple
evaluations, particularly if you're going to apply this to function
results --- as the above example does. I think you need to go the
extra mile to make it single-evaluation. See newNode() for ideas.
+1.
In case the Assert fails, the debugger would halt at castNode macro
and a first time reader would be puzzled to see no assert there.
Obviously looking at the #define should clarify the confusion. But I
don't see how that can be avoided. I was thinking of using a function
castNodeFunc(), but it can't accomodate Assert with _type_. Will
calling this function as checkAndCastNode() help?
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
There is a common coding pattern that goes like this:
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
I propose a macro castNode() that combines the assertion and the cast,
so this would becomeRestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
I'm quite a bit in favor of something like this, having proposed it
before ;)
+#define castNode(_type_,nodeptr) (AssertMacro(!nodeptr || IsA(nodeptr,_type_)), (_type_ *)(nodeptr))
ISTM that we need to do the core part of this in an inline function, to
avoid multiple evaluation hazards - which seem quite likely to occur
here - it's pretty common to cast the result of a function after all.
Something like
static inline Node*
castNodeImpl(void *c, enum NodeTag t)
{
Assert(c == NULL || IsA(c, t));
return c;
}
#define castNode(_type_, nodeptr) ((_type_ *) castNodeImpl(nodeptr, _type_))
should work without too much trouble afaics?
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Jan 2, 2017 at 2:10 PM, Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
There is a common coding pattern that goes like this:
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));I propose a macro castNode() that combines the assertion and the cast,
so this would becomeRestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
I'm quite a bit in favor of something like this, having proposed it
before ;)+#define castNode(_type_,nodeptr) (AssertMacro(!nodeptr || IsA(nodeptr,_type_)), (_type_ *)(nodeptr))
ISTM that we need to do the core part of this in an inline function, to
avoid multiple evaluation hazards - which seem quite likely to occur
here - it's pretty common to cast the result of a function after all.Something like
static inline Node*
castNodeImpl(void *c, enum NodeTag t)
{
Assert(c == NULL || IsA(c, t));
return c;
}#define castNode(_type_, nodeptr) ((_type_ *) castNodeImpl(nodeptr, _type_))
should work without too much trouble afaics?
I tried this quickly as per attached patch. It gave a compiler error
createplan.c: In function ‘castNodeImpl’:
createplan.c:340:2: error: ‘T_t’ undeclared (first use in this function)
createplan.c:340:2: note: each undeclared identifier is reported only
once for each function it appears in
createplan.c: In function ‘create_plan_recurse’:
createplan.c:445:13: error: expected expression before ‘AggPath’
Is the attached patch as per your suggestion?
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
castNode.patchapplication/x-download; name=castNode.patchDownload
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ad49674..3878d84 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -334,6 +334,13 @@ create_plan(PlannerInfo *root, Path *best_path)
return plan;
}
+static inline Node *
+castNodeImpl(void *c, enum NodeTag t)
+{
+ Assert(c == NULL || IsA(c, t));
+ return c;
+}
+
/*
* create_plan_recurse
* Recursive guts of create_plan().
@@ -343,6 +350,8 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
{
Plan *plan;
+#define castNode(_type_, nodeptr) ((_type_ *) castNodeImpl(nodeptr, _type_))
+
switch (best_path->pathtype)
{
case T_SeqScan:
@@ -433,7 +442,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
{
Assert(IsA(best_path, AggPath));
plan = (Plan *) create_agg_plan(root,
- (AggPath *) best_path);
+ castNode(AggPath, best_path));
}
break;
case T_WindowAgg:
Hi,
On 2017-01-03 11:00:47 +0530, Ashutosh Bapat wrote:
On Mon, Jan 2, 2017 at 2:10 PM, Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
There is a common coding pattern that goes like this:
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));I propose a macro castNode() that combines the assertion and the cast,
so this would becomeRestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
I'm quite a bit in favor of something like this, having proposed it
before ;)+#define castNode(_type_,nodeptr) (AssertMacro(!nodeptr || IsA(nodeptr,_type_)), (_type_ *)(nodeptr))
ISTM that we need to do the core part of this in an inline function, to
avoid multiple evaluation hazards - which seem quite likely to occur
here - it's pretty common to cast the result of a function after all.Something like
static inline Node*
castNodeImpl(void *c, enum NodeTag t)
{
Assert(c == NULL || IsA(c, t));
return c;
}#define castNode(_type_, nodeptr) ((_type_ *) castNodeImpl(nodeptr, _type_))
should work without too much trouble afaics?
I tried this quickly as per attached patch. It gave a compiler error
createplan.c: In function ‘castNodeImpl’:
createplan.c:340:2: error: ‘T_t’ undeclared (first use in this function)
createplan.c:340:2: note: each undeclared identifier is reported only
once for each function it appears in
createplan.c: In function ‘create_plan_recurse’:
createplan.c:445:13: error: expected expression before ‘AggPath’
Well, I wrote that just to outline my suggestion, not as a patch ;).
It's just that we have to replace IsA() with nodeTag(nodeptr) == t
(because IsA does string concat magic).
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi Peter,
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Are you planning to add this / update this patch? Because I really would
have liked this a number of times already... I can update it according
to my suggestions (to avoid multiple eval scenarios) if helpful
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Are you planning to add this / update this patch? Because I really would
have liked this a number of times already... I can update it according
to my suggestions (to avoid multiple eval scenarios) if helpful
Yeah, I'd like that in sooner rather than later, too. But we do need
it to be foolproof - no multiple evals. The first draft had
inadequate-parenthesization hazards, too.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2017-01-25 19:21:40 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Are you planning to add this / update this patch? Because I really would
have liked this a number of times already... I can update it according
to my suggestions (to avoid multiple eval scenarios) if helpfulYeah, I'd like that in sooner rather than later, too. But we do need
it to be foolproof - no multiple evals. The first draft had
inadequate-parenthesization hazards,
How about something like the attached? The first patch just contains
castNode(), the second one a rebased version of Peter's changes (with
some long lines broken up).
Andres
Attachments:
0001-Add-castNode-type-ptr-for-safe-casting-between-NodeT.patchtext/x-patch; charset=us-asciiDownload
From a2a2ef62ecb8a349eae0cd90df75c44f5e7e83a5 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Thu, 26 Jan 2017 13:13:18 -0800
Subject: [PATCH 1/2] Add castNode(type, ptr) for safe casting between NodeTag
based types.
Author: Peter Eisentraut and Andres Freund
Discussion: https://postgr.es/m/c5d387d9-3440-f5e0-f9d4-71d53b9fbe52@2ndquadrant.com
---
src/include/nodes/nodes.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fa4932a902..b462a5ffde 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -558,6 +558,26 @@ extern PGDLLIMPORT Node *newNodeMacroHolder;
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
+/*
+ * castNode(type, ptr) casts ptr to type and, if cassert is enabled, verifies
+ * that the the c actually has the appropriate type (using it's nodeTag()).
+ *
+ * Use an inline function when assertions are enabled, to avoid multiple
+ * evaluations of the ptr argument (which could e.g. be a function call).
+ */
+#ifdef USE_ASSERT_CHECKING
+static inline Node*
+castNodeImpl(enum NodeTag type, void *ptr)
+{
+ Assert(ptr == NULL || nodeTag(ptr) == type);
+ return ptr;
+}
+#define castNode(_type_, nodeptr) ((_type_ *) castNodeImpl(T_##_type_, nodeptr))
+#else
+#define castNode(_type_,nodeptr) ((_type_ *)(nodeptr))
+#endif
+
+
/* ----------------------------------------------------------------
* extern declarations follow
* ----------------------------------------------------------------
--
2.11.0.22.g8d7a455.dirty
0002-Use-the-new-castNode-macro-in-a-subset-of-places.patchtext/x-patch; charset=us-asciiDownload
From 732c0bfd2cbe6cb2e7b9730d5802be115713a350 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Thu, 26 Jan 2017 13:12:10 -0800
Subject: [PATCH 2/2] Use the new castNode() macro in a subset of places.
Author: Peter Eisentraut, with some minor changes by me
Discussion: https://postgr.es/m/c5d387d9-3440-f5e0-f9d4-71d53b9fbe52@2ndquadrant.com
---
contrib/pg_stat_statements/pg_stat_statements.c | 10 ++++------
contrib/postgres_fdw/deparse.c | 5 +----
contrib/postgres_fdw/postgres_fdw.c | 12 ++++--------
src/backend/catalog/pg_proc.c | 3 +--
src/backend/commands/aggregatecmds.c | 4 ++--
src/backend/commands/analyze.c | 6 +++---
src/backend/commands/async.c | 8 ++++----
src/backend/commands/collationcmds.c | 2 +-
src/backend/commands/constraint.c | 2 +-
src/backend/commands/copy.c | 14 +++++++-------
src/backend/commands/createas.c | 5 ++---
src/backend/commands/dropcmds.c | 4 +---
src/backend/commands/explain.c | 16 +++++++---------
src/backend/commands/functioncmds.c | 7 ++-----
src/backend/commands/matview.c | 3 +--
src/backend/commands/opclasscmds.c | 9 +++------
src/backend/commands/tablecmds.c | 16 +++++-----------
src/backend/commands/trigger.c | 4 +---
src/backend/commands/user.c | 4 +---
src/backend/commands/view.c | 3 +--
src/backend/executor/execAmi.c | 7 ++++---
src/backend/executor/execQual.c | 6 ++----
src/backend/executor/execTuples.c | 5 +----
src/backend/executor/functions.c | 6 ++----
src/backend/executor/nodeAgg.c | 6 ++----
src/backend/executor/nodeCtescan.c | 3 +--
src/backend/executor/nodeCustom.c | 4 ++--
src/backend/executor/nodeHashjoin.c | 7 ++-----
src/backend/executor/nodeLockRows.c | 4 +---
src/backend/executor/nodeModifyTable.c | 4 +---
src/backend/executor/nodeSubplan.c | 15 +++++----------
src/backend/executor/nodeWorktablescan.c | 4 ++--
32 files changed, 77 insertions(+), 131 deletions(-)
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6edc3d926b..a65b52968a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2382,9 +2382,8 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
foreach(lc, rtable)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+ RangeTblEntry *rte = castNode(RangeTblEntry, lfirst(lc));
- Assert(IsA(rte, RangeTblEntry));
APP_JUMB(rte->rtekind);
switch (rte->rtekind)
{
@@ -2570,7 +2569,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
APP_JUMB(sublink->subLinkType);
APP_JUMB(sublink->subLinkId);
JumbleExpr(jstate, (Node *) sublink->testexpr);
- JumbleQuery(jstate, (Query *) sublink->subselect);
+ JumbleQuery(jstate, castNode(Query, sublink->subselect));
}
break;
case T_FieldSelect:
@@ -2636,9 +2635,8 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) caseexpr->arg);
foreach(temp, caseexpr->args)
{
- CaseWhen *when = (CaseWhen *) lfirst(temp);
+ CaseWhen *when = castNode(CaseWhen, lfirst(temp));
- Assert(IsA(when, CaseWhen));
JumbleExpr(jstate, (Node *) when->expr);
JumbleExpr(jstate, (Node *) when->result);
}
@@ -2850,7 +2848,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
/* we store the string name because RTE_CTE RTEs need it */
APP_JUMB_STRING(cte->ctename);
- JumbleQuery(jstate, (Query *) cte->ctequery);
+ JumbleQuery(jstate, castNode(Query, cte->ctequery));
}
break;
case T_SetOperationStmt:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 6bdeda9824..d2b94aaf3b 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -1315,10 +1315,7 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs,
foreach(lc, tlist)
{
- TargetEntry *tle = (TargetEntry *) lfirst(lc);
-
- /* Extract expression if TargetEntry node */
- Assert(IsA(tle, TargetEntry));
+ TargetEntry *tle = castNode(TargetEntry, lfirst(lc));
if (i > 0)
appendStringInfoString(buf, ", ");
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ce1f443d55..f396a227f7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1159,9 +1159,7 @@ postgresGetForeignPlan(PlannerInfo *root,
*/
foreach(lc, scan_clauses)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
-
- Assert(IsA(rinfo, RestrictInfo));
+ RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
/* Ignore any pseudoconstants, they're dealt with elsewhere */
if (rinfo->pseudoconstant)
@@ -4958,14 +4956,12 @@ conversion_error_callback(void *arg)
{
/* error occurred in a scan against a foreign join */
ForeignScanState *fsstate = errpos->fsstate;
- ForeignScan *fsplan = (ForeignScan *) fsstate->ss.ps.plan;
+ ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
EState *estate = fsstate->ss.ps.state;
TargetEntry *tle;
- Assert(IsA(fsplan, ForeignScan));
- tle = (TargetEntry *) list_nth(fsplan->fdw_scan_tlist,
- errpos->cur_attno - 1);
- Assert(IsA(tle, TargetEntry));
+ tle = castNode(TargetEntry, list_nth(fsplan->fdw_scan_tlist,
+ errpos->cur_attno - 1));
/*
* Target list can have Vars and expressions. For Vars, we can get
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 7ae192a407..bb83a9a7c6 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -510,8 +510,7 @@ ProcedureCreate(const char *procedureName,
Anum_pg_proc_proargdefaults,
&isnull);
Assert(!isnull);
- oldDefaults = (List *) stringToNode(TextDatumGetCString(proargdefaults));
- Assert(IsA(oldDefaults, List));
+ oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults)));
Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
/* new list can have more defaults than old, advance over 'em */
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 02ea254cd4..2341129351 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -109,13 +109,13 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List
aggKind = AGGKIND_ORDERED_SET;
else
numDirectArgs = 0;
- args = (List *) linitial(args);
+ args = castNode(List, linitial(args));
}
/* Examine aggregate's definition clauses */
foreach(pl, parameters)
{
- DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem *defel = castNode(DefElem, lfirst(pl));
/*
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index e3e1a53072..c9f6afeb1a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -722,9 +722,9 @@ compute_index_stats(Relation onerel, double totalrows,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = castNode(List,
+ ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+ estate));
/* Compute and save index expression values */
exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 6d0ce7a358..e32d7a1d4e 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1636,7 +1636,7 @@ AtSubCommit_Notify(void)
List *parentPendingActions;
List *parentPendingNotifies;
- parentPendingActions = (List *) linitial(upperPendingActions);
+ parentPendingActions = castNode(List, linitial(upperPendingActions));
upperPendingActions = list_delete_first(upperPendingActions);
Assert(list_length(upperPendingActions) ==
@@ -1647,7 +1647,7 @@ AtSubCommit_Notify(void)
*/
pendingActions = list_concat(parentPendingActions, pendingActions);
- parentPendingNotifies = (List *) linitial(upperPendingNotifies);
+ parentPendingNotifies = castNode(List, linitial(upperPendingNotifies));
upperPendingNotifies = list_delete_first(upperPendingNotifies);
Assert(list_length(upperPendingNotifies) ==
@@ -1679,13 +1679,13 @@ AtSubAbort_Notify(void)
*/
while (list_length(upperPendingActions) > my_level - 2)
{
- pendingActions = (List *) linitial(upperPendingActions);
+ pendingActions = castNode(List, linitial(upperPendingActions));
upperPendingActions = list_delete_first(upperPendingActions);
}
while (list_length(upperPendingNotifies) > my_level - 2)
{
- pendingNotifies = (List *) linitial(upperPendingNotifies);
+ pendingNotifies = castNode(List, linitial(upperPendingNotifies));
upperPendingNotifies = list_delete_first(upperPendingNotifies);
}
}
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 8d4d5b7b63..e165d4b2a6 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -61,7 +61,7 @@ DefineCollation(ParseState *pstate, List *names, List *parameters)
foreach(pl, parameters)
{
- DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem *defel = castNode(DefElem, lfirst(pl));
DefElem **defelp;
if (pg_strcasecmp(defel->defname, "from") == 0)
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 77cf8ceee7..e9eeacd03a 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -37,7 +37,7 @@
Datum
unique_key_recheck(PG_FUNCTION_ARGS)
{
- TriggerData *trigdata = (TriggerData *) fcinfo->context;
+ TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
const char *funcname = "unique_key_recheck";
HeapTuple new_row;
ItemPointerData tmptid;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f9362be031..01a63c823e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1026,7 +1026,7 @@ ProcessCopyOptions(ParseState *pstate,
/* Extract options from the statement node tree */
foreach(option, options)
{
- DefElem *defel = (DefElem *) lfirst(option);
+ DefElem *defel = castNode(DefElem, lfirst(option));
if (strcmp(defel->defname, "format") == 0)
{
@@ -1123,7 +1123,7 @@ ProcessCopyOptions(ParseState *pstate,
if (defel->arg && IsA(defel->arg, A_Star))
cstate->force_quote_all = true;
else if (defel->arg && IsA(defel->arg, List))
- cstate->force_quote = (List *) defel->arg;
+ cstate->force_quote = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1139,7 +1139,7 @@ ProcessCopyOptions(ParseState *pstate,
errmsg("conflicting or redundant options"),
parser_errposition(pstate, defel->location)));
if (defel->arg && IsA(defel->arg, List))
- cstate->force_notnull = (List *) defel->arg;
+ cstate->force_notnull = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1154,7 +1154,7 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
if (defel->arg && IsA(defel->arg, List))
- cstate->force_null = (List *) defel->arg;
+ cstate->force_null = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1176,7 +1176,7 @@ ProcessCopyOptions(ParseState *pstate,
parser_errposition(pstate, defel->location)));
cstate->convert_selectively = true;
if (defel->arg == NULL || IsA(defel->arg, List))
- cstate->convert_select = (List *) defel->arg;
+ cstate->convert_select = castNode(List, defel->arg);
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1479,7 +1479,7 @@ BeginCopy(ParseState *pstate,
/* examine queries to determine which error message to issue */
foreach(lc, rewritten)
{
- Query *q = (Query *) lfirst(lc);
+ Query *q = castNode(Query, lfirst(lc));
if (q->querySource == QSRC_QUAL_INSTEAD_RULE)
ereport(ERROR,
@@ -1496,7 +1496,7 @@ BeginCopy(ParseState *pstate,
errmsg("multi-statement DO INSTEAD rules are not supported for COPY")));
}
- query = (Query *) linitial(rewritten);
+ query = castNode(Query, linitial(rewritten));
/* The grammar allows SELECT INTO, but we don't support that */
if (query->utilityStmt != NULL &&
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cee3b4d50b..02cfcd182d 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -224,7 +224,7 @@ ObjectAddress
ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag)
{
- Query *query = (Query *) stmt->query;
+ Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
bool is_matview = (into->viewQuery != NULL);
DestReceiver *dest;
@@ -261,11 +261,10 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
* The contained Query could be a SELECT, or an EXECUTE utility command.
* If the latter, we just pass it off to ExecuteQuery.
*/
- Assert(IsA(query, Query));
if (query->commandType == CMD_UTILITY &&
IsA(query->utilityStmt, ExecuteStmt))
{
- ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
+ ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
Assert(!is_matview); /* excluded by syntax */
ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 8cfbcf43f7..ff3108ce72 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -222,12 +222,10 @@ type_in_list_does_not_exist_skipping(List *typenames, const char **msg,
foreach(l, typenames)
{
- TypeName *typeName = (TypeName *) lfirst(l);
+ TypeName *typeName = castNode(TypeName, lfirst(l));
if (typeName != NULL)
{
- Assert(IsA(typeName, TypeName));
-
if (!OidIsValid(LookupTypeNameOid(NULL, typeName, true)))
{
/* type doesn't exist, try to find why */
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index f9fb27658f..5d61a0195e 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1493,25 +1493,25 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
break;
case T_Agg:
- show_agg_keys((AggState *) planstate, ancestors, es);
+ show_agg_keys(castNode(AggState, planstate), ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_Group:
- show_group_keys((GroupState *) planstate, ancestors, es);
+ show_group_keys(castNode(GroupState, planstate), ancestors, es);
show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_Sort:
- show_sort_keys((SortState *) planstate, ancestors, es);
- show_sort_info((SortState *) planstate, es);
+ show_sort_keys(castNode(SortState, planstate), ancestors, es);
+ show_sort_info(castNode(SortState, planstate), es);
break;
case T_MergeAppend:
- show_merge_append_keys((MergeAppendState *) planstate,
+ show_merge_append_keys(castNode(MergeAppendState, planstate),
ancestors, es);
break;
case T_Result:
@@ -1523,11 +1523,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
break;
case T_ModifyTable:
- show_modifytable_info((ModifyTableState *) planstate, ancestors,
+ show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
es);
break;
case T_Hash:
- show_hash_info((HashState *) planstate, es);
+ show_hash_info(castNode(HashState, planstate), es);
break;
default:
break;
@@ -2183,7 +2183,6 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
static void
show_sort_info(SortState *sortstate, ExplainState *es)
{
- Assert(IsA(sortstate, SortState));
if (es->analyze && sortstate->sort_Done &&
sortstate->tuplesortstate != NULL)
{
@@ -2217,7 +2216,6 @@ show_hash_info(HashState *hashstate, ExplainState *es)
{
HashJoinTable hashtable;
- Assert(IsA(hashstate, HashState));
hashtable = hashstate->hashtable;
if (hashtable)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 22aecb24f9..ec833c382d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -578,9 +578,8 @@ update_proconfig_value(ArrayType *a, List *set_items)
foreach(l, set_items)
{
- VariableSetStmt *sstmt = (VariableSetStmt *) lfirst(l);
+ VariableSetStmt *sstmt = castNode(VariableSetStmt, lfirst(l));
- Assert(IsA(sstmt, VariableSetStmt));
if (sstmt->kind == VAR_RESET_ALL)
a = NULL;
else
@@ -971,9 +970,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
{
ListCell *lc;
- Assert(IsA(transformDefElem, List));
-
- foreach(lc, (List *) transformDefElem)
+ foreach(lc, castNode(List, transformDefElem))
{
Oid typeid = typenameTypeId(NULL, lfirst(lc));
Oid elt = get_base_element_type(typeid);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 6b5a9b6fe8..b7daf1ca0a 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -264,8 +264,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
* The stored query was rewritten at the time of the MV definition, but
* has not been scribbled on by the planner.
*/
- dataQuery = (Query *) linitial(actions);
- Assert(IsA(dataQuery, Query));
+ dataQuery = castNode(Query, linitial(actions));
/*
* Check for active uses of the relation in the current transaction, such
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 7cfcc6de84..bc43483b94 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -462,13 +462,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
*/
foreach(l, stmt->items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
@@ -847,13 +846,12 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
*/
foreach(l, items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid operOid;
Oid funcOid;
Oid sortfamilyOid;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
@@ -981,12 +979,11 @@ AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
*/
foreach(l, items)
{
- CreateOpClassItem *item = lfirst(l);
+ CreateOpClassItem *item = castNode(CreateOpClassItem, lfirst(l));
Oid lefttype,
righttype;
OpFamilyMember *member;
- Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
{
case OPCLASS_ITEM_OPERATOR:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4b0011bdd..04b5d9a943 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5932,12 +5932,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
colName)));
/* Generate new proposed attoptions (text array) */
- Assert(IsA(options, List));
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- (List *) options, NULL, NULL, false,
- isReset);
+ castNode(List, options), NULL, NULL,
+ false, isReset);
/* Validate new options */
(void) attribute_reloptions(newOptions, true);
@@ -7141,8 +7140,7 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
bool found = false;
ObjectAddress address;
- Assert(IsA(cmd->def, Constraint));
- cmdcon = (Constraint *) cmd->def;
+ cmdcon = castNode(Constraint, cmd->def);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
@@ -9348,9 +9346,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
IndexStmt *indstmt;
Oid indoid;
- Assert(IsA(cmd->def, IndexStmt));
-
- indstmt = (IndexStmt *) cmd->def;
+ indstmt = castNode(IndexStmt, cmd->def);
indoid = get_constraint_index(oldId);
if (!rewrite)
@@ -9373,9 +9369,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
{
Constraint *con;
- Assert(IsA(cmd->def, Constraint));
-
- con = (Constraint *) cmd->def;
+ con = castNode(Constraint, cmd->def);
con->old_pktable_oid = refRelId;
/* rewriting neither side of a FK */
if (con->contype == CONSTR_FOREIGN &&
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 68fa7acfb1..f067d0a7bb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -340,9 +340,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
foreach(lc, varList)
{
- TriggerTransition *tt = (TriggerTransition *) lfirst(lc);
-
- Assert(IsA(tt, TriggerTransition));
+ TriggerTransition *tt = castNode(TriggerTransition, lfirst(lc));
if (!(tt->isTable))
ereport(ERROR,
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index e6fdac34ae..b746982d2e 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -1396,11 +1396,9 @@ roleSpecsToIds(List *memberNames)
foreach(l, memberNames)
{
- RoleSpec *rolespec = (RoleSpec *) lfirst(l);
+ RoleSpec *rolespec = castNode(RoleSpec, lfirst(l));
Oid roleid;
- Assert(IsA(rolespec, RoleSpec));
-
roleid = get_rolespec_oid(rolespec, false);
result = lappend_oid(result, roleid);
}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 1f008b0756..7d76f567a8 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -516,9 +516,8 @@ DefineView(ViewStmt *stmt, const char *queryString,
foreach(targetList, viewParse->targetList)
{
- TargetEntry *te = (TargetEntry *) lfirst(targetList);
+ TargetEntry *te = castNode(TargetEntry, lfirst(targetList));
- Assert(IsA(te, TargetEntry));
/* junk columns don't get aliases */
if (te->resjunk)
continue;
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 1ca4bcb117..d3802079f5 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -403,11 +403,12 @@ ExecSupportsMarkRestore(Path *pathnode)
return true;
case T_CustomScan:
- Assert(IsA(pathnode, CustomPath));
- if (((CustomPath *) pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
+ {
+ CustomPath *customPath = castNode(CustomPath, pathnode);
+ if (customPath->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
return true;
return false;
-
+ }
case T_Result:
/*
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 19dd0b264b..4566219ca8 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -4640,10 +4640,9 @@ ExecInitExpr(Expr *node, PlanState *parent)
cstate->arg = ExecInitExpr(caseexpr->arg, parent);
foreach(l, caseexpr->args)
{
- CaseWhen *when = (CaseWhen *) lfirst(l);
+ CaseWhen *when = castNode(CaseWhen, lfirst(l));
CaseWhenState *wstate = makeNode(CaseWhenState);
- Assert(IsA(when, CaseWhen));
wstate->xprstate.evalfunc = NULL; /* not used */
wstate->xprstate.expr = (Expr *) when;
wstate->expr = ExecInitExpr(when->expr, parent);
@@ -5137,9 +5136,8 @@ ExecCleanTargetListLength(List *targetlist)
foreach(tl, targetlist)
{
- TargetEntry *curTle = (TargetEntry *) lfirst(tl);
+ TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
- Assert(IsA(curTle, TargetEntry));
if (!curTle->resjunk)
len++;
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index cbb2bcb568..f002ee2561 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -160,10 +160,7 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
foreach(lc, tupleTable)
{
- TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc);
-
- /* Sanity checks */
- Assert(IsA(slot, TupleTableSlot));
+ TupleTableSlot *slot = castNode(TupleTableSlot, lfirst(lc));
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index e4a1da4dbb..15c709139a 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -479,19 +479,17 @@ init_execution_state(List *queryTree_list,
foreach(lc1, queryTree_list)
{
- List *qtlist = (List *) lfirst(lc1);
+ List *qtlist = castNode(List, lfirst(lc1));
execution_state *firstes = NULL;
execution_state *preves = NULL;
ListCell *lc2;
foreach(lc2, qtlist)
{
- Query *queryTree = (Query *) lfirst(lc2);
+ Query *queryTree = castNode(Query, lfirst(lc2));
PlannedStmt *stmt;
execution_state *newes;
- Assert(IsA(queryTree, Query));
-
/* Plan the query if needed */
if (queryTree->commandType == CMD_UTILITY)
{
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index e4992134bd..4109b6e32c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2573,8 +2573,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (phase > 0)
{
aggnode = list_nth(node->chain, phase - 1);
- sortnode = (Sort *) aggnode->plan.lefttree;
- Assert(IsA(sortnode, Sort));
+ sortnode = castNode(Sort, aggnode->plan.lefttree);
}
else
{
@@ -3010,10 +3009,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
*/
foreach(arg, pertrans->aggref->args)
{
- TargetEntry *source_tle = (TargetEntry *) lfirst(arg);
+ TargetEntry *source_tle = castNode(TargetEntry, lfirst(arg));
TargetEntry *tle;
- Assert(IsA(source_tle, TargetEntry));
tle = flatCopyTargetEntry(source_tle);
tle->resno += column_offset;
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 610797b36b..8f4e0f527e 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -210,7 +210,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
prmdata = &(estate->es_param_exec_vals[node->cteParam]);
Assert(prmdata->execPlan == NULL);
Assert(!prmdata->isnull);
- scanstate->leader = (CteScanState *) DatumGetPointer(prmdata->value);
+ scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
if (scanstate->leader == NULL)
{
/* I am the leader */
@@ -223,7 +223,6 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
else
{
/* Not the leader */
- Assert(IsA(scanstate->leader, CteScanState));
/* Create my own read pointer, and ensure it is at start */
scanstate->readptr =
tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index a27430242a..16343a56df 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -35,8 +35,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
* methods field correctly at this time. Other standard fields should be
* set to zero.
*/
- css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan);
- Assert(IsA(css, CustomScanState));
+ css = castNode(CustomScanState,
+ cscan->methods->CreateCustomScanState(cscan));
/* ensure flags is filled correctly */
css->flags = cscan->flags;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 6e576ad0b3..8a04294b40 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -519,12 +519,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
hoperators = NIL;
foreach(l, hjstate->hashclauses)
{
- FuncExprState *fstate = (FuncExprState *) lfirst(l);
- OpExpr *hclause;
+ FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
+ OpExpr *hclause = castNode(OpExpr, fstate->xprstate.expr);
- Assert(IsA(fstate, FuncExprState));
- hclause = (OpExpr *) fstate->xprstate.expr;
- Assert(IsA(hclause, OpExpr));
lclauses = lappend(lclauses, linitial(fstate->args));
rclauses = lappend(rclauses, lsecond(fstate->args));
hoperators = lappend_oid(hoperators, hclause->opno);
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index f1bf6fdf9f..b098034337 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -401,12 +401,10 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
epq_arowmarks = NIL;
foreach(lc, node->rowMarks)
{
- PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
+ PlanRowMark *rc = castNode(PlanRowMark, lfirst(lc));
ExecRowMark *erm;
ExecAuxRowMark *aerm;
- Assert(IsA(rc, PlanRowMark));
-
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index e35603964b..95e158970c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1958,11 +1958,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
foreach(l, node->rowMarks)
{
- PlanRowMark *rc = (PlanRowMark *) lfirst(l);
+ PlanRowMark *rc = castNode(PlanRowMark, lfirst(l));
ExecRowMark *erm;
- Assert(IsA(rc, PlanRowMark));
-
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index f8a2cd446a..8f419a13ac 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -808,8 +808,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
else if (and_clause((Node *) sstate->testexpr->expr))
{
/* multiple combining operators */
- Assert(IsA(sstate->testexpr, BoolExprState));
- oplist = ((BoolExprState *) sstate->testexpr)->args;
+ oplist = castNode(BoolExprState, sstate->testexpr)->args;
}
else
{
@@ -829,8 +828,8 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
i = 1;
foreach(l, oplist)
{
- FuncExprState *fstate = (FuncExprState *) lfirst(l);
- OpExpr *opexpr = (OpExpr *) fstate->xprstate.expr;
+ FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
+ OpExpr *opexpr = castNode(OpExpr, fstate->xprstate.expr);
ExprState *exstate;
Expr *expr;
TargetEntry *tle;
@@ -839,8 +838,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
Oid left_hashfn;
Oid right_hashfn;
- Assert(IsA(fstate, FuncExprState));
- Assert(IsA(opexpr, OpExpr));
Assert(list_length(fstate->args) == 2);
/* Process lefthand argument */
@@ -1218,10 +1215,8 @@ ExecAlternativeSubPlan(AlternativeSubPlanState *node,
bool *isNull)
{
/* Just pass control to the active subplan */
- SubPlanState *activesp = (SubPlanState *) list_nth(node->subplans,
- node->active);
-
- Assert(IsA(activesp, SubPlanState));
+ SubPlanState *activesp = castNode(SubPlanState,
+ list_nth(node->subplans, node->active));
return ExecSubPlan(activesp, econtext, isNull);
}
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index bdba9e0bfc..23b5b94985 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -95,8 +95,8 @@ ExecWorkTableScan(WorkTableScanState *node)
param = &(estate->es_param_exec_vals[plan->wtParam]);
Assert(param->execPlan == NULL);
Assert(!param->isnull);
- node->rustate = (RecursiveUnionState *) DatumGetPointer(param->value);
- Assert(node->rustate && IsA(node->rustate, RecursiveUnionState));
+ node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
+ Assert(node->rustate);
/*
* The scan tuple type (ie, the rowtype we expect to find in the work
--
2.11.0.22.g8d7a455.dirty
Andres Freund <andres@anarazel.de> writes:
How about something like the attached? The first patch just contains
castNode(), the second one a rebased version of Peter's changes (with
some long lines broken up).
Looks generally good. A couple quibbles from a quick read-through:
* All but the first change in ProcessCopyOptions seem rather pointless:
else if (defel->arg && IsA(defel->arg, List))
- cstate->force_quote = (List *) defel->arg;
+ cstate->force_quote = castNode(List, defel->arg);
In these places, castNode() isn't checking anything the if-condition
didn't. Maybe it's good style anyway, but I'm not really convinced.
* In ExecInitAgg:
aggnode = list_nth(node->chain, phase - 1);
- sortnode = (Sort *) aggnode->plan.lefttree;
- Assert(IsA(sortnode, Sort));
+ sortnode = castNode(Sort, aggnode->plan.lefttree);
it seems like the assignment to aggnode ought to have a castNode on it too
(the fact that it lacks any cast now is sloppy and not per project style,
IMO).
There were a bunch of places in ab1f0c822 where I wished I had this,
but I can go back and back-fill that later; doesn't need to be in the
first commit.
BTW, maybe it's just the first flush of enthusiasm, but I can see us
using this so much that the lack of it in back branches will become
a serious PITA for back-patching. So I'm strongly tempted to propose
that your 0001 should be back-patched. However, before 9.6 we didn't
have the compiler requirement that "static inline" in headers must be
handled sanely. Maybe a useful compromise would be to put 0001 in 9.6,
and before that just add
#define castNode(_type_,nodeptr) ((_type_ *)(nodeptr))
which would allow the notation to be used safely, albeit without
any assertion backup. Alternatively, we could enable the assertion
code only for gcc, which would probably be plenty good enough for
finding bugs in stable branches.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2017-01-26 16:55:33 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
How about something like the attached? The first patch just contains
castNode(), the second one a rebased version of Peter's changes (with
some long lines broken up).Looks generally good. A couple quibbles from a quick read-through:
* All but the first change in ProcessCopyOptions seem rather pointless:
else if (defel->arg && IsA(defel->arg, List)) - cstate->force_quote = (List *) defel->arg; + cstate->force_quote = castNode(List, defel->arg);In these places, castNode() isn't checking anything the if-condition
didn't. Maybe it's good style anyway, but I'm not really convinced.
Agreed that it's not not necessary - I didn't add this one (or any
castNode actually). But I don't think it matters much.
* In ExecInitAgg:
aggnode = list_nth(node->chain, phase - 1); - sortnode = (Sort *) aggnode->plan.lefttree; - Assert(IsA(sortnode, Sort)); + sortnode = castNode(Sort, aggnode->plan.lefttree);it seems like the assignment to aggnode ought to have a castNode on it
too
Yea, looks good.
(the fact that it lacks any cast now is sloppy and not per project style,
IMO).
There's a lot of these missing :(. This is one of these things that'd
be a lot easier to enforce if we'd be able to compile in a c++
compatible mode (-Wc++-compat), because there void * to X * casts
have to be done explicitly.
BTW, maybe it's just the first flush of enthusiasm, but I can see us
using this so much that the lack of it in back branches will become
a serious PITA for back-patching.
Might, yea.
So I'm strongly tempted to propose
that your 0001 should be back-patched. However, before 9.6 we didn't
have the compiler requirement that "static inline" in headers must be
handled sanely. Maybe a useful compromise would be to put 0001 in 9.6,
and before that just add#define castNode(_type_,nodeptr) ((_type_ *)(nodeptr))
which would allow the notation to be used safely, albeit without
any assertion backup. Alternatively, we could enable the assertion
code only for gcc, which would probably be plenty good enough for
finding bugs in stable branches.
#if defined(USE_ASSERT_CHECKING) && defined(PG_USE_INLINE)
is probably a better gatekeeper in the back-branches, than gcc? Then we
can just remove the defined(PG_USE_INLINE) and it's associated comment
in >= 9.6.
Regards,
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
#if defined(USE_ASSERT_CHECKING) && defined(PG_USE_INLINE)
is probably a better gatekeeper in the back-branches, than gcc?
Ah, yeah, that would work --- I'd already swapped out that business ;-)
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2017-01-26 17:27:45 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
#if defined(USE_ASSERT_CHECKING) && defined(PG_USE_INLINE)
is probably a better gatekeeper in the back-branches, than gcc?Ah, yeah, that would work --- I'd already swapped out that business ;-)
Done that way.
- Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
This is inspired by the dynamic_cast operator in C++, but follows the
syntax of the well-known makeNode() macro.
The analogy to dynamic_cast goes only so far, because we don't actually
support inheritance. I.e. in c++ we could successfully cast SeqScanState to a
PlanState, ScanState and SeqScanState - but with our model only
SeqScanState can be checked.
Greetings,
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
This is inspired by the dynamic_cast operator in C++, but follows the
syntax of the well-known makeNode() macro.
The analogy to dynamic_cast goes only so far, because we don't actually
support inheritance. I.e. in c++ we could successfully cast SeqScanState to a
PlanState, ScanState and SeqScanState - but with our model only
SeqScanState can be checked.
Yeah, I was thinking about that earlier --- this can only be used to cast
to a concrete node type, not one of the "abstract" types like Plan * or
Expr *. Not sure if that's worth worrying about though; I don't think
I've ever seen actual bugs in PG code from casting the wrong thing in that
direction. For the most part, passing the wrong thing would end up firing
a default: case in a switch, or some such, so we already do have some
defenses for that direction.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2017-01-26 20:29:06 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
This is inspired by the dynamic_cast operator in C++, but follows the
syntax of the well-known makeNode() macro.The analogy to dynamic_cast goes only so far, because we don't actually
support inheritance. I.e. in c++ we could successfully cast SeqScanState to a
PlanState, ScanState and SeqScanState - but with our model only
SeqScanState can be checked.Yeah, I was thinking about that earlier --- this can only be used to cast
to a concrete node type, not one of the "abstract" types like Plan * or
Expr *. Not sure if that's worth worrying about though; I don't think
I've ever seen actual bugs in PG code from casting the wrong thing in that
direction. For the most part, passing the wrong thing would end up firing
a default: case in a switch, or some such, so we already do have some
defenses for that direction.
Yea, I'm not actually worried about it - I was more generally remarking
on the analogy made by Peter. For a second I was considering bringing up
the analogy in a comment or such, and this was one of a number of
arguments that made me disregard that idea.
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 1/26/17 16:15, Andres Freund wrote:
On 2017-01-25 19:21:40 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Are you planning to add this / update this patch? Because I really would
have liked this a number of times already... I can update it according
to my suggestions (to avoid multiple eval scenarios) if helpfulYeah, I'd like that in sooner rather than later, too. But we do need
it to be foolproof - no multiple evals. The first draft had
inadequate-parenthesization hazards,How about something like the attached? The first patch just contains
castNode(), the second one a rebased version of Peter's changes (with
some long lines broken up).
Thanks for finishing that up. I have committed more uses that I had
lying around partially done. Looks nice now.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Feb 21, 2017 at 9:00 AM, Peter Eisentraut <
peter.eisentraut@2ndquadrant.com> wrote:
On 1/26/17 16:15, Andres Freund wrote:
On 2017-01-25 19:21:40 -0500, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
RestrictInfo *rinfo = castNode(RestrictInfo, lfirst(lc));
Are you planning to add this / update this patch? Because I really
would
have liked this a number of times already... I can update it according
to my suggestions (to avoid multiple eval scenarios) if helpfulYeah, I'd like that in sooner rather than later, too. But we do need
it to be foolproof - no multiple evals. The first draft had
inadequate-parenthesization hazards,How about something like the attached? The first patch just contains
castNode(), the second one a rebased version of Peter's changes (with
some long lines broken up).Thanks for finishing that up. I have committed more uses that I had
lying around partially done. Looks nice now.
With commit 38d103763d14baddf3cbfe4b00b501059fc9447f, I'm now getting a
compiler warning:
indxpath.c: In function 'generate_bitmap_or_paths':
indxpath.c:1312: warning: unused variable 'rinfo'
I have: gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
No arguments to ./configure are needed.
Cheers,
Jeff
Jeff Janes <jeff.janes@gmail.com> writes:
With commit 38d103763d14baddf3cbfe4b00b501059fc9447f, I'm now getting a
compiler warning:
indxpath.c: In function 'generate_bitmap_or_paths':
indxpath.c:1312: warning: unused variable 'rinfo'
Me too, without asserts. Fixed.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
I was about to add a few more uses of castNode, which made me think.
You proposed replacing:
On 2016-12-31 12:08:22 -0500, Peter Eisentraut wrote:
There is a common coding pattern that goes like this:
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
with
+#define castNode(_type_,nodeptr) (AssertMacro(!nodeptr || IsA(nodeptr,_type_)), (_type_ *)(nodeptr))
(now an inline function, but that's besides my point)
Those aren't actually equivalent, because of the !nodeptr. IsA() crashes
for NULL pointers, but the new code won't. Which means 9ba8a9ce4548b et
al actually weakened some asserts.
Should we perhaps have one NULL accepting version (castNodeNull?) and
one that separately asserts that ptr != NULL?
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
Those aren't actually equivalent, because of the !nodeptr. IsA() crashes
for NULL pointers, but the new code won't. Which means 9ba8a9ce4548b et
al actually weakened some asserts.
Should we perhaps have one NULL accepting version (castNodeNull?) and
one that separately asserts that ptr != NULL?
-1 ... if you're going to use something in a way that requires it not to
be null, your code will crash quite efficiently on a null, with or
without an assert. I don't think we need the extra cogitive burden of
two distinct macros for this.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2/24/17 10:54, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
Those aren't actually equivalent, because of the !nodeptr. IsA() crashes
for NULL pointers, but the new code won't. Which means 9ba8a9ce4548b et
al actually weakened some asserts.Should we perhaps have one NULL accepting version (castNodeNull?) and
one that separately asserts that ptr != NULL?-1 ... if you're going to use something in a way that requires it not to
be null, your code will crash quite efficiently on a null, with or
without an assert. I don't think we need the extra cogitive burden of
two distinct macros for this.
I think we should just add some Assert(thepointer) where necessary.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers