Repository: duckdb/duckdb
File: src/execution/operator/scan/physical_table_scan.cpp (C++)

Code before:
SourceResultType PhysicalTableScan::GetData(ExecutionContext &context, DataChunk &chunk,
                                            OperatorSourceInput &input) const {
	D_ASSERT(!column_ids.empty());
	auto &g_state = input.global_state.Cast<TableScanGlobalSourceState>();
	auto &l_state = input.local_state.Cast<TableScanLocalSourceState>();

	TableFunctionInput data(bind_data.get(), l_state.local_state.get(), g_state.global_state.get());

	if (function.function) {
		data.async_result = AsyncResultType::IMPLICIT;
		data.results_execution_mode = AsyncResultsExecutionMode::TASK_EXECUTOR;

		switch (g_state.physical_table_scan_execution_strategy) {
		case PhysicalTableScanExecutionStrategy::DEFAULT:
		case PhysicalTableScanExecutionStrategy::TASK_EXECUTOR:
		case PhysicalTableScanExecutionStrategy::TASK_EXECUTOR_BUT_FORCE_SYNC_CHECKS:
			data.results_execution_mode = AsyncResultsExecutionMode::TASK_EXECUTOR;
			break;
		case PhysicalTableScanExecutionStrategy::SYNCHRONOUS:
			data.results_execution_mode = AsyncResultsExecutionMode::SYNCHRONOUS;
			break;
		}

		function.function(context.client, data, chunk);

		switch (g_state.physical_table_scan_execution_strategy) {
		case PhysicalTableScanExecutionStrategy::TASK_EXECUTOR_BUT_FORCE_SYNC_CHECKS:
			// This is a funny one, expected to crash on non-trivial workflows
		case PhysicalTableScanExecutionStrategy::SYNCHRONOUS:
			switch (data.async_result.GetResultType()) {
			case AsyncResultType::INVALID:
				throw InternalException("SYNCHRONOUS_PHYSICAL_TABLE_SCAN: found INVALID");
			case AsyncResultType::BLOCKED:
				throw InternalException("SYNCHRONOUS_PHYSICAL_TABLE_SCAN: found BLOCKED");
			case AsyncResultType::FINISHED:
				if (chunk.size() > 0) {
					throw InternalException("SYNCHRONOUS_PHYSICAL_TABLE_SCAN: found FINISHED with non-empty chunk");
				}
				break;
			case AsyncResultType::HAVE_MORE_OUTPUT:
				if (chunk.size() == 0) {
					throw InternalException("SYNCHRONOUS_PHYSICAL_TABLE_SCAN: found HAVE_MORE_OUTPUT with empty chunk");
				}
				break;
			case AsyncResultType::IMPLICIT:
				break;
			}
			break;
		default:
			break;
		}

Review comment (refactor):
Can we move this validation to a separate function, e.g., `ValidateAsyncStrategyResult`? This helps decluttering the `GetData` function

Code after:
                                        const idx_t output_chunk_size) {
	auto execution_mode_pre_computed = AsyncResult::ConvertToAsyncResultExecutionMode(strategy);
	if (execution_mode_pre_computed != execution_mode_pre) {
		throw InternalException("ValidateAsyncStrategyResult: invalid conversion PhysicalTableScanExecutionStrategy to "
		                        "AsyncResultsExecutionMode, from '%s', to '%s'",
		                        EnumUtil::ToChars(strategy), EnumUtil::ToChars(execution_mode_pre));
	}

	if (execution_mode_pre != execution_mode_post) {
		throw InternalException("ValidateAsyncStrategyResult: results_execution_mode changed within table API's "
		                        "`function` call, before '%s', after '%s'",
		                        EnumUtil::ToChars(execution_mode_pre), EnumUtil::ToChars(execution_mode_post));
	}
	if (result_pre != AsyncResultType::IMPLICIT) {
		throw InternalException("ValidateAsyncStrategyResult: async_result is supposed to be IMPLICIT, was '%s', "
		                        "before table API's `function` call",
		                        EnumUtil::ToChars(result_pre));
	}
	switch (strategy) {
	case PhysicalTableScanExecutionStrategy::TASK_EXECUTOR_BUT_FORCE_SYNC_CHECKS:
		// This is a funny one, expected to throw on non-trivial workflows in this function
	case PhysicalTableScanExecutionStrategy::SYNCHRONOUS:
		switch (result_post) {
		case AsyncResultType::INVALID:
			throw InternalException("ValidateAsyncStrategyResult: found INVALID");
		case AsyncResultType::BLOCKED:
			throw InternalException("ValidateAsyncStrategyResult: found BLOCKED");
		case AsyncResultType::FINISHED:
			if (output_chunk_size > 0) {
				throw InternalException("ValidateAsyncStrategyResult: found FINISHED with non-empty chunk");
			}
			break;
		case AsyncResultType::HAVE_MORE_OUTPUT:
			if (output_chunk_size == 0) {
				throw InternalException("ValidateAsyncStrategyResult: found HAVE_MORE_OUTPUT with empty chunk");
			}
			break;
		case AsyncResultType::IMPLICIT:
			break;
		}
		break;
	default:
		if (result_post == AsyncResultType::BLOCKED) {
			if (output_chunk_size > 0) {
				throw InternalException("ValidateAsyncStrategyResult: found BLOCKED with non-empty chunk");
			}
		}
		break;
	}
}