34
34
#include "utils/lsyscache.h"
35
35
36
36
37
- /* This is actually a postgres version of a one dimensional array */
38
-
37
+ /*
38
+ * This is actually a postgres version of a one dimensional array.
39
+ * We cheat a little by using the lower-bound field as an indicator
40
+ * of the physically allocated size, while the dimensionality is the
41
+ * count of items accumulated so far.
42
+ */
39
43
typedef struct
40
44
{
41
45
ArrayType a ;
@@ -56,7 +60,7 @@ typedef struct callContext
56
60
#define START_NUM 8 /* initial size of arrays */
57
61
#define PGARRAY_SIZE (n ) (sizeof(PGARRAY) + (((n)-1)*sizeof(int4)))
58
62
59
- static PGARRAY * GetPGArray (PGARRAY * p , int fAdd );
63
+ static PGARRAY * GetPGArray (PGARRAY * p , AggState * aggstate , bool fAdd );
60
64
static PGARRAY * ShrinkPGArray (PGARRAY * p );
61
65
62
66
Datum int_agg_state (PG_FUNCTION_ARGS );
@@ -68,72 +72,68 @@ PG_FUNCTION_INFO_V1(int_agg_final_array);
68
72
PG_FUNCTION_INFO_V1 (int_enum );
69
73
70
74
/*
71
- * Manage the aggregation state of the array
75
+ * Manage the allocation state of the array
72
76
*
73
- * Need to specify a suitably long-lived memory context, or it will vanish!
74
- * PortalContext isn't really right, but it 's close enough .
77
+ * Note that the array needs to be in a reasonably long-lived context,
78
+ * ie the Agg node 's aggcontext .
75
79
*/
76
80
static PGARRAY *
77
- GetPGArray (PGARRAY * p , int fAdd )
81
+ GetPGArray (PGARRAY * p , AggState * aggstate , bool fAdd )
78
82
{
79
83
if (!p )
80
84
{
81
85
/* New array */
82
86
int cb = PGARRAY_SIZE (START_NUM );
83
87
84
- p = (PGARRAY * ) MemoryContextAlloc (PortalContext , cb );
88
+ p = (PGARRAY * ) MemoryContextAlloc (aggstate -> aggcontext , cb );
85
89
p -> a .size = cb ;
86
- p -> a .ndim = 0 ;
90
+ p -> a .ndim = 1 ;
87
91
p -> a .flags = 0 ;
88
92
p -> a .elemtype = INT4OID ;
89
93
p -> items = 0 ;
90
94
p -> lower = START_NUM ;
91
95
}
92
96
else if (fAdd )
93
- { /* Ensure array has space */
97
+ {
98
+ /* Ensure array has space for another item */
94
99
if (p -> items >= p -> lower )
95
100
{
96
- PGARRAY * pn ;
97
- int n = p -> lower + p -> lower ;
101
+ PGARRAY * pn ;
102
+ int n = p -> lower * 2 ;
98
103
int cbNew = PGARRAY_SIZE (n );
99
104
100
- pn = (PGARRAY * ) repalloc (p , cbNew );
105
+ pn = (PGARRAY * ) MemoryContextAlloc (aggstate -> aggcontext , cbNew );
106
+ memcpy (pn , p , p -> a .size );
101
107
pn -> a .size = cbNew ;
102
108
pn -> lower = n ;
103
- return pn ;
109
+ /* do not pfree(p), because nodeAgg.c will */
110
+ p = pn ;
104
111
}
105
112
}
106
113
return p ;
107
114
}
108
115
109
- /* Shrinks the array to its actual size and moves it into the standard
110
- * memory allocation context, frees working memory
116
+ /*
117
+ * Shrinks the array to its actual size and moves it into the standard
118
+ * memory allocation context
111
119
*/
112
120
static PGARRAY *
113
- ShrinkPGArray (PGARRAY * p )
121
+ ShrinkPGArray (PGARRAY * p )
114
122
{
115
- PGARRAY * pnew = NULL ;
123
+ PGARRAY * pnew ;
124
+ /* get target size */
125
+ int cb = PGARRAY_SIZE (p -> items );
126
+
127
+ /* use current transaction context */
128
+ pnew = palloc (cb );
129
+ memcpy (pnew , p , cb );
130
+
131
+ /* fix up the fields in the new array to match normal conventions */
132
+ pnew -> a .size = cb ;
133
+ pnew -> lower = 1 ;
134
+
135
+ /* do not pfree(p), because nodeAgg.c will */
116
136
117
- if (p )
118
- {
119
- /* get target size */
120
- int cb = PGARRAY_SIZE (p -> items );
121
-
122
- /* use current transaction context */
123
- pnew = palloc (cb );
124
-
125
- /*
126
- * Fix up the fields in the new structure, so Postgres understands
127
- */
128
- memcpy (pnew , p , cb );
129
- pnew -> a .size = cb ;
130
- pnew -> a .ndim = 1 ;
131
- pnew -> a .flags = 0 ;
132
- pnew -> a .elemtype = INT4OID ;
133
- pnew -> lower = 1 ;
134
-
135
- pfree (p );
136
- }
137
137
return pnew ;
138
138
}
139
139
@@ -144,11 +144,18 @@ int_agg_state(PG_FUNCTION_ARGS)
144
144
PGARRAY * state ;
145
145
PGARRAY * p ;
146
146
147
+ /*
148
+ * As of PG 8.1 we can actually verify that we are being used as an
149
+ * aggregate function, and so it is safe to scribble on our left input.
150
+ */
151
+ if (!(fcinfo -> context && IsA (fcinfo -> context , AggState )))
152
+ elog (ERROR , "int_agg_state may only be used as an aggregate" );
153
+
147
154
if (PG_ARGISNULL (0 ))
148
- state = NULL ;
155
+ state = NULL ; /* first time through */
149
156
else
150
157
state = (PGARRAY * ) PG_GETARG_POINTER (0 );
151
- p = GetPGArray (state , 1 );
158
+ p = GetPGArray (state , ( AggState * ) fcinfo -> context , true );
152
159
153
160
if (!PG_ARGISNULL (1 ))
154
161
{
@@ -164,22 +171,38 @@ int_agg_state(PG_FUNCTION_ARGS)
164
171
PG_RETURN_POINTER (p );
165
172
}
166
173
167
- /* This is the final function used for the integer aggregator. It returns all
174
+ /*
175
+ * This is the final function used for the integer aggregator. It returns all
168
176
* the integers collected as a one dimensional integer array
169
177
*/
170
178
Datum
171
179
int_agg_final_array (PG_FUNCTION_ARGS )
172
180
{
173
- PGARRAY * state = (PGARRAY * ) PG_GETARG_POINTER (0 );
174
- PGARRAY * pnew = ShrinkPGArray (GetPGArray (state , 0 ));
181
+ PGARRAY * state ;
182
+ PGARRAY * p ;
183
+ PGARRAY * pnew ;
175
184
176
- if (pnew )
177
- PG_RETURN_POINTER (pnew );
185
+ /*
186
+ * As of PG 8.1 we can actually verify that we are being used as an
187
+ * aggregate function, and so it is safe to scribble on our left input.
188
+ */
189
+ if (!(fcinfo -> context && IsA (fcinfo -> context , AggState )))
190
+ elog (ERROR , "int_agg_final_array may only be used as an aggregate" );
191
+
192
+ if (PG_ARGISNULL (0 ))
193
+ state = NULL ; /* zero items in aggregation */
178
194
else
179
- PG_RETURN_NULL ();
195
+ state = (PGARRAY * ) PG_GETARG_POINTER (0 );
196
+ p = GetPGArray (state , (AggState * ) fcinfo -> context , false);
197
+
198
+ pnew = ShrinkPGArray (p );
199
+ PG_RETURN_POINTER (pnew );
180
200
}
181
201
182
- /* This function accepts an array, and returns one item for each entry in the array */
202
+ /*
203
+ * This function accepts an array, and returns one item for each entry in the
204
+ * array
205
+ */
183
206
Datum
184
207
int_enum (PG_FUNCTION_ARGS )
185
208
{
0 commit comments