5
5
#include <nodes/readfuncs.h>
6
6
#include <utils/rel.h>
7
7
#include <catalog/pg_type.h>
8
+ #include <rewrite/rewriteManip.h>
8
9
9
10
#include "chunk_dispatch_plan.h"
10
11
#include "chunk_dispatch_state.h"
@@ -27,6 +28,84 @@ static CustomScanMethods chunk_dispatch_plan_methods = {
27
28
.CreateCustomScanState = create_chunk_dispatch_state ,
28
29
};
29
30
31
+ /*
32
+ * Build a target list for a CustomScan node.
33
+ *
34
+ * The CustomScan node needs to provide a modified target list in case of
35
+ * subplan nodes that have target lists with expressions that involve, e.g.,
36
+ * aggregates. Such target lists need a matching parent node type (e.g., a
37
+ * target list with an AggDef needs a Agg node parent). If we simply use the
38
+ * subplan's target list on a CustomScan parent node, there might be a mismatch.
39
+ *
40
+ * The code here is similar to ExecCheckPlanOutput() in nodeModifyTable.c in the
41
+ * PostgreSQL source code, but here we construct a modified target list for the
42
+ * CustomScan node instead of simply just checking validity.
43
+ *
44
+ */
45
+ static List *
46
+ build_customscan_targetlist (Relation rel , List * targetlist )
47
+ {
48
+ TupleDesc tupdesc = RelationGetDescr (rel );
49
+ List * new_targetlist = NIL ;
50
+ ListCell * lc ;
51
+ int attno = 0 ;
52
+
53
+ foreach (lc , targetlist )
54
+ {
55
+ TargetEntry * te = lfirst (lc );
56
+ Form_pg_attribute attr ;
57
+ Expr * expr ;
58
+
59
+ /* Ignore junk items */
60
+ if (te -> resjunk )
61
+ continue ;
62
+
63
+ if (attno >= tupdesc -> natts )
64
+ ereport (ERROR ,
65
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
66
+ errmsg ("table row type and query-specified row type do not match" ),
67
+ errdetail ("Query has too many columns." )));
68
+
69
+ attr = tupdesc -> attrs [attno ++ ];
70
+
71
+ if (attr -> attisdropped )
72
+ expr = & makeConst (INT4OID ,
73
+ -1 ,
74
+ InvalidOid ,
75
+ sizeof (int32 ),
76
+ (Datum ) 0 ,
77
+ true,
78
+ true)-> xpr ;
79
+ else
80
+
81
+ /*
82
+ * According to nodes/primnodes.h, the INDEX_VAR Var type is
83
+ * abused in CustomScan nodes to reference custom scan tuple
84
+ * types.
85
+ */
86
+ expr = & makeVar (INDEX_VAR ,
87
+ attno ,
88
+ exprType ((Node * ) te -> expr ),
89
+ exprTypmod ((Node * ) te -> expr ),
90
+ exprCollation ((Node * ) te -> expr ),
91
+ 0 )-> xpr ;
92
+
93
+ new_targetlist = lappend (new_targetlist ,
94
+ makeTargetEntry (expr ,
95
+ attno ,
96
+ NULL ,
97
+ te -> resjunk ));
98
+ }
99
+
100
+ if (attno != tupdesc -> natts )
101
+ ereport (ERROR ,
102
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
103
+ errmsg ("table row type and query-specified row type do not match" ),
104
+ errdetail ("Query has too few columns." )));
105
+
106
+ return new_targetlist ;
107
+ }
108
+
30
109
/* Create a chunk dispatch plan node in the form of a CustomScan node. The
31
110
* purpose of this plan node is to dispatch (route) tuples to the correct chunk
32
111
* in a hypertable.
@@ -42,10 +121,11 @@ static CustomScanMethods chunk_dispatch_plan_methods = {
42
121
* node.
43
122
*/
44
123
CustomScan *
45
- chunk_dispatch_plan_create (Plan * subplan , Oid hypertable_relid , Query * parse )
124
+ chunk_dispatch_plan_create (Plan * subplan , Index hypertable_rti , Oid hypertable_relid , Query * parse )
46
125
{
47
126
CustomScan * cscan = makeNode (CustomScan );
48
127
ChunkDispatchInfo * info = chunk_dispatch_info_create (hypertable_relid , parse );
128
+ Relation rel ;
49
129
50
130
cscan -> custom_private = list_make1 (info );
51
131
cscan -> methods = & chunk_dispatch_plan_methods ;
@@ -60,11 +140,20 @@ chunk_dispatch_plan_create(Plan *subplan, Oid hypertable_relid, Query *parse)
60
140
cscan -> scan .plan .plan_width = subplan -> plan_width ;
61
141
62
142
/*
63
- * Copy target list from parent table. This should work since hypertables
64
- * mandate that chunks have identical column definitions
143
+ * Create a modified target list for the CustomScan based on the subplan's
144
+ * original target list
145
+ */
146
+ rel = relation_open (hypertable_relid , AccessShareLock );
147
+ cscan -> scan .plan .targetlist = build_customscan_targetlist (rel , subplan -> targetlist );
148
+ RelationClose (rel );
149
+
150
+ /*
151
+ * We need to set a custom_scan_tlist for EXPLAIN (verbose). But this
152
+ * target list needs to reference the proper rangetable entry so we must
153
+ * replace all INDEX_VAR attnos with the hypertable's rangetable index.
65
154
*/
66
- cscan -> scan . plan . targetlist = subplan -> targetlist ;
67
- cscan -> custom_scan_tlist = NIL ;
155
+ cscan -> custom_scan_tlist = copyObject ( cscan -> scan . plan . targetlist ) ;
156
+ ChangeVarNodes (( Node * ) cscan -> custom_scan_tlist , INDEX_VAR , hypertable_rti , 0 ) ;
68
157
69
158
return cscan ;
70
159
}
0 commit comments