|
135 | 135 | // Aggregate VDFs (like SQL SUM, COUNT, etc.) accumulate state across rows |
136 | 136 | // within each GROUP BY group, then return a final result per group. |
137 | 137 | // |
138 | | -// The recommended approach uses .state<T>() with typed callbacks. Define a |
139 | | -// state type, then write clear, accumulate, and result functions using C++ |
140 | | -// types: |
| 138 | +// Use make_aggregate_func<State, &result_fn>() as the entry point. The State |
| 139 | +// type is explicit, and all three callback signatures are validated against it |
| 140 | +// at compile time. |
| 141 | +// |
| 142 | +// The result function always uses an output parameter, consistent with normal |
| 143 | +// VDFs. All three callbacks follow the same pattern: |
| 144 | +// |
| 145 | +// void my_clear(State &s) // reset state |
| 146 | +// void my_acc(State &s, TypedArg v, ...) // accumulate one row |
| 147 | +// void my_result(const State &s, ResultWrapper out) // produce final value |
| 148 | +// |
| 149 | +// Example — nullable integer sum: |
141 | 150 | // |
142 | 151 | // using SumState = std::optional<long long>; |
143 | 152 | // |
144 | | -// void my_clear(SumState &s) { s = std::nullopt; } |
| 153 | +// void my_clear(SumState &s) { s = std::nullopt; } |
145 | 154 | // void my_acc(SumState &s, IntArg v) { |
146 | 155 | // if (!v.is_null()) s = s.value_or(0) + v.value(); |
147 | 156 | // } |
148 | | -// std::optional<long long> my_result(const SumState &s) { return s; } |
| 157 | +// void my_result(const SumState &s, IntResult out) { |
| 158 | +// if (!s.has_value()) { out.set_null(); return; } |
| 159 | +// out.set(s.value()); |
| 160 | +// } |
149 | 161 | // |
150 | | -// make_func<&my_result>("my_sum") |
| 162 | +// make_aggregate_func<SumState, &my_result>("my_sum") |
151 | 163 | // .returns(INT) |
152 | 164 | // .param(INT) |
153 | | -// .state<SumState>() |
154 | 165 | // .clear<&my_clear>() |
155 | 166 | // .accumulate<&my_acc>() |
156 | 167 | // .build() |
157 | 168 | // |
158 | 169 | // How it works: |
159 | | -// - .state<T>() generates prerun (allocates T via value-initialization) and |
160 | | -// postrun (deletes T) automatically. T is stored in user_data. |
161 | | -// - .clear<&fn>() wraps void(State&) -> vef_vdf_clear_func_t |
162 | | -// - .accumulate<&fn>() wraps void(State&, TypedArgs...) -> |
163 | | -// vef_vdf_accumulate_func_t. TypedArgs are deduced from the function |
164 | | -// signature (IntArg, StringArg, etc.). |
165 | | -// - The result function (make_func template parameter) can return T directly |
166 | | -// (never NULL) or std::optional<T> (nullopt -> SQL NULL). |
167 | | -// |
168 | | -// For results that are never NULL (e.g., COUNT), use a plain state type: |
| 170 | +// - prerun/postrun are auto-generated: prerun allocates State via |
| 171 | +// value-initialization, postrun deletes it. |
| 172 | +// - .clear<&fn>() void(State&) |
| 173 | +// - .accumulate<&fn>() void(State&, TypedArgs...) — TypedArgs deduced from |
| 174 | +// the function signature (IntArg, StringArg, CustomArg, etc.). |
| 175 | +// Call .accumulate() after all .param() calls. |
| 176 | +// - Result function uses void(const State&, ResultWrapper) where |
| 177 | +// ResultWrapper is IntResult, RealResult, StringResult, CustomResult, or |
| 178 | +// CustomResultWith<P>. Use out.set_null() to return SQL NULL. |
| 179 | +// |
| 180 | +// Example — non-nullable count: |
169 | 181 | // |
170 | 182 | // using CountState = long long; |
171 | 183 | // void count_clear(CountState &s) { s = 0; } |
172 | | -// void count_acc(CountState &s, IntArg v) { if (!v.is_null()) s++; } |
173 | | -// long long count_result(const CountState &s) { return s; } |
174 | | -// |
175 | | -// You can also use the raw ABI directly for full control: |
| 184 | +// void count_acc(CountState &s, IntArg v) { if (!v.is_null()) ++s; } |
| 185 | +// void count_result(const CountState &s, IntResult out) { out.set(s); } |
176 | 186 | // |
177 | | -// make_func<&raw_result>("my_agg") |
| 187 | +// make_aggregate_func<CountState, &count_result>("my_count") |
178 | 188 | // .returns(INT).param(INT) |
179 | | -// .prerun<&my_prerun>() // void(ctx, prerun_args, prerun_result) |
180 | | -// .postrun<&my_postrun>() // void(ctx, postrun_args, postrun_result) |
181 | | -// .clear<&my_clear>() // void(ctx, vdf_args) |
182 | | -// .accumulate<&my_acc>() // void(ctx, vdf_args, vdf_result) |
| 189 | +// .clear<&count_clear>().accumulate<&count_acc>() |
| 190 | +// .build() |
| 191 | +// |
| 192 | +// Example — aggregate returning a custom (binary) type: |
| 193 | +// |
| 194 | +// using SumState = std::optional<MyType>; |
| 195 | +// void my_clear(SumState &s) { s = std::nullopt; } |
| 196 | +// void my_acc(SumState &s, CustomArg v) { /* update s */ } |
| 197 | +// void my_result(const SumState &s, CustomResult out) { |
| 198 | +// if (!s.has_value()) { out.set_null(); return; } |
| 199 | +// store_mytype(out.buffer().data(), s.value()); |
| 200 | +// out.set_length(kMyTypeSize); |
| 201 | +// } |
| 202 | +// |
| 203 | +// make_aggregate_func<SumState, &my_result>("my_agg") |
| 204 | +// .returns(MYTYPE).param(MYTYPE) |
| 205 | +// .clear<&my_clear>().accumulate<&my_acc>() |
183 | 206 | // .build() |
184 | 207 | // |
185 | | -// See aggregate_vdf.cc in the test suite for complete examples of both styles. |
| 208 | +// See aggregate_vdf.cc in the test suite for complete examples. |
186 | 209 | // |
187 | 210 | // |
188 | 211 | // DEFINING TYPES |
|
0 commit comments