| 1220 | } |
| 1221 | |
| 1222 | void PropagateNullsSpans(const ExecSpan& batch, ArraySpan* out) { |
| 1223 | if (out->type->id() == Type::NA) { |
| 1224 | // Null output type is a no-op (rare when this would happen but we at least |
| 1225 | // will test for it) |
| 1226 | return; |
| 1227 | } |
| 1228 | |
| 1229 | std::vector<const ArraySpan*> arrays_with_nulls; |
| 1230 | bool is_all_null = false; |
| 1231 | for (const ExecValue& value : batch.values) { |
| 1232 | auto null_generalization = NullGeneralization::Get(value); |
| 1233 | if (null_generalization == NullGeneralization::ALL_NULL) { |
| 1234 | is_all_null = true; |
| 1235 | } |
| 1236 | if (null_generalization != NullGeneralization::ALL_VALID && value.is_array()) { |
| 1237 | arrays_with_nulls.push_back(&value.array); |
| 1238 | } |
| 1239 | } |
| 1240 | uint8_t* out_bitmap = out->buffers[0].data; |
| 1241 | if (is_all_null) { |
| 1242 | // An all-null value (scalar null or all-null array) gives us a short |
| 1243 | // circuit opportunity |
| 1244 | // OK, the output should be all null |
| 1245 | out->null_count = out->length; |
| 1246 | bit_util::SetBitsTo(out_bitmap, out->offset, out->length, false); |
| 1247 | return; |
| 1248 | } |
| 1249 | |
| 1250 | out->null_count = kUnknownNullCount; |
| 1251 | if (arrays_with_nulls.empty()) { |
| 1252 | // No arrays with nulls case |
| 1253 | out->null_count = 0; |
| 1254 | if (out_bitmap != nullptr) { |
| 1255 | // An output buffer was allocated, so we fill it with all valid |
| 1256 | bit_util::SetBitsTo(out_bitmap, out->offset, out->length, true); |
| 1257 | } |
| 1258 | } else if (arrays_with_nulls.size() == 1) { |
| 1259 | // One array |
| 1260 | const ArraySpan& arr = *arrays_with_nulls[0]; |
| 1261 | |
| 1262 | // Reuse the null count if it's known |
| 1263 | out->null_count = arr.null_count; |
| 1264 | CopyBitmap(arr.buffers[0].data, arr.offset, arr.length, out_bitmap, out->offset); |
| 1265 | } else { |
| 1266 | // More than one array. We use BitmapAnd to intersect their bitmaps |
| 1267 | auto Accumulate = [&](const ArraySpan& left, const ArraySpan& right) { |
| 1268 | DCHECK(left.buffers[0].data != nullptr); |
| 1269 | DCHECK(right.buffers[0].data != nullptr); |
| 1270 | BitmapAnd(left.buffers[0].data, left.offset, right.buffers[0].data, right.offset, |
| 1271 | out->length, out->offset, out_bitmap); |
| 1272 | }; |
| 1273 | // Seed the output bitmap with the & of the first two bitmaps |
| 1274 | Accumulate(*arrays_with_nulls[0], *arrays_with_nulls[1]); |
| 1275 | |
| 1276 | // Accumulate the rest |
| 1277 | for (size_t i = 2; i < arrays_with_nulls.size(); ++i) { |
| 1278 | Accumulate(*out, *arrays_with_nulls[i]); |
| 1279 | } |