Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions cpp/src/arrow/compute/exec/options.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,12 +27,20 @@
#include "arrow/compute/api_vector.h"
#include "arrow/compute/exec.h"
#include "arrow/compute/exec/expression.h"
#include "arrow/record_batch.h"
#include "arrow/result.h"
#include "arrow/util/async_generator.h"
#include "arrow/util/async_util.h"
#include "arrow/util/visibility.h"

namespace arrow {

namespace internal {

class Executor;

} // namespace internal

namespace compute {

using AsyncExecBatchGenerator = AsyncGenerator<std::optional<ExecBatch>>;
Expand DownExpand Up@@ -77,6 +85,49 @@ class ARROW_EXPORT TableSourceNodeOptions : public ExecNodeOptions {
int64_t max_batch_size;
};

/// \brief An extended Source node which accepts a schema
///
/// ItMaker is a maker of an iterator of tabular data.
template <typename ItMaker>
class ARROW_EXPORT SchemaSourceNodeOptions : public ExecNodeOptions {
Comment thread
rtpsw marked this conversation as resolved.
Outdated
public:
SchemaSourceNodeOptions(std::shared_ptr<Schema> schema, ItMaker it_maker,
arrow::internal::Executor* io_executor = NULLPTR)
: schema(schema), it_maker(std::move(it_maker)), io_executor(io_executor) {}

/// \brief The schema of the record batches from the iterator
std::shared_ptr<Schema> schema;

/// \brief A maker of an iterator which acts as the data source
ItMaker it_maker;

/// \brief The executor to use for scanning the iterator
///
/// Defaults to the default I/O executor.
arrow::internal::Executor* io_executor;
};

using ArrayVectorIteratorMaker = std::function<Iterator<std::shared_ptr<ArrayVector>>()>;
/// \brief An extended Source node which accepts a schema and array-vectors
class ARROW_EXPORT ArrayVectorSourceNodeOptions
: public SchemaSourceNodeOptions<ArrayVectorIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using ExecBatchIteratorMaker = std::function<Iterator<std::shared_ptr<ExecBatch>>()>;
/// \brief An extended Source node which accepts a schema and exec-batches
class ARROW_EXPORT ExecBatchSourceNodeOptions
: public SchemaSourceNodeOptions<ExecBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

using RecordBatchIteratorMaker = std::function<Iterator<std::shared_ptr<RecordBatch>>()>;
/// \brief An extended Source node which accepts a schema and record-batches
class ARROW_EXPORT RecordBatchSourceNodeOptions
: public SchemaSourceNodeOptions<RecordBatchIteratorMaker> {
using SchemaSourceNodeOptions::SchemaSourceNodeOptions;
};

/// \brief Make a node which excludes some rows from batches passed through it
///
/// filter_expression will be evaluated against each batch which is pushed to
Expand Down
79 changes: 79 additions & 0 deletions cpp/src/arrow/compute/exec/plan_test.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -295,6 +295,85 @@ TEST(ExecPlanExecution, TableSourceSinkError) {
Raises(StatusCode::Invalid, HasSubstr("batch_size > 0")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSinkError(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make());
std::shared_ptr<Schema> no_schema;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

auto null_executor_options = OptionsType{exp_batches.schema, element_it_maker};
ASSERT_OK(MakeExecNode(source_factory_name, plan.get(), {}, null_executor_options));

auto null_schema_options = OptionsType{no_schema, element_it_maker};
ASSERT_THAT(MakeExecNode(source_factory_name, plan.get(), {}, null_schema_options),
Raises(StatusCode::Invalid, HasSubstr("not null")));
}

template <typename ElementType, typename OptionsType>
void TestSourceSink(
std::string source_factory_name,
std::function<Result<std::vector<ElementType>>(const BatchesWithSchema&)>
to_elements) {
ASSERT_OK_AND_ASSIGN(auto io_executor, arrow::internal::ThreadPool::Make(1));
ExecContext exec_context(default_memory_pool(), io_executor.get());
ASSERT_OK_AND_ASSIGN(auto plan, ExecPlan::Make(&exec_context));
AsyncGenerator<std::optional<ExecBatch>> sink_gen;

auto exp_batches = MakeBasicBatches();
ASSERT_OK_AND_ASSIGN(auto elements, to_elements(exp_batches));
auto element_it_maker = [&elements]() {
return MakeVectorIterator<ElementType>(elements);
};

ASSERT_OK(Declaration::Sequence({
{source_factory_name,
OptionsType{exp_batches.schema, element_it_maker}},
{"sink", SinkNodeOptions{&sink_gen}},
})
.AddToPlan(plan.get()));

ASSERT_THAT(StartAndCollect(plan.get(), sink_gen),
Finishes(ResultWith(UnorderedElementsAreArray(exp_batches.batches))));
}

TEST(ExecPlanExecution, ArrayVectorSourceSink) {
TestSourceSink<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ArrayVectorSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ArrayVector>, ArrayVectorSourceNodeOptions>(
"array_vector_source", ToArrayVectors);
}

TEST(ExecPlanExecution, ExecBatchSourceSink) {
TestSourceSink<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, ExecBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<ExecBatch>, ExecBatchSourceNodeOptions>(
"exec_batch_source", ToExecBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSink) {
TestSourceSink<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, RecordBatchSourceSinkError) {
TestSourceSinkError<std::shared_ptr<RecordBatch>, RecordBatchSourceNodeOptions>(
"record_batch_source", ToRecordBatches);
}

TEST(ExecPlanExecution, SinkNodeBackpressure) {
std::optional<ExecBatch> batch =
ExecBatchFromJSON({int32(), boolean()},
Expand Down
136 changes: 136 additions & 0 deletions cpp/src/arrow/compute/exec/source_node.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,6 +26,7 @@
#include "arrow/compute/exec/util.h"
#include "arrow/compute/exec_internal.h"
#include "arrow/datum.h"
#include "arrow/io/util_internal.h"
#include "arrow/result.h"
#include "arrow/table.h"
#include "arrow/util/async_generator.h"
Expand DownExpand Up@@ -293,13 +294,148 @@ struct TableSourceNode : public SourceNode {
}
};

template <typename This, typename Options>
struct SchemaSourceNode : public SourceNode {
SchemaSourceNode(ExecPlan* plan, std::shared_ptr<Schema> schema,
arrow::AsyncGenerator<std::optional<ExecBatch>> generator)
: SourceNode(plan, schema, generator) {}

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
RETURN_NOT_OK(ValidateExecNodeInputs(plan, inputs, 0, This::kKindName));
const auto& cast_options = checked_cast<const Options&>(options);
auto& it_maker = cast_options.it_maker;
auto& schema = cast_options.schema;
auto io_executor = cast_options.io_executor;

if (io_executor == NULLPTR) {
io_executor = plan->exec_context()->executor();
}
auto it = it_maker();

if (schema == NULLPTR) {
return Status::Invalid(This::kKindName, " requires schema which is not null");
}
if (io_executor == NULLPTR) {
io_executor = io::internal::GetIOThreadPool();
}

ARROW_ASSIGN_OR_RAISE(auto generator, This::MakeGenerator(it, io_executor, schema));
return plan->EmplaceNode<This>(plan, schema, generator);
}
};

struct RecordBatchSourceNode
: public SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions> {
using RecordBatchSchemaSourceNode =
SchemaSourceNode<RecordBatchSourceNode, RecordBatchSourceNodeOptions>;

using RecordBatchSchemaSourceNode::RecordBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return RecordBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<RecordBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[schema](const std::shared_ptr<RecordBatch>& batch) -> std::optional<ExecBatch> {
if (batch == NULLPTR || *batch->schema() != *schema) {
return std::nullopt;
}
return std::optional<ExecBatch>(ExecBatch(*batch));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char RecordBatchSourceNode::kKindName[] = "RecordBatchSourceNode";

struct ExecBatchSourceNode
: public SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions> {
using ExecBatchSchemaSourceNode =
SchemaSourceNode<ExecBatchSourceNode, ExecBatchSourceNodeOptions>;

using ExecBatchSchemaSourceNode::ExecBatchSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ExecBatchSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ExecBatch>>& batch_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ExecBatch>& batch) -> std::optional<ExecBatch> {
return batch == NULLPTR ? std::nullopt : std::optional<ExecBatch>(*batch);
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(batch_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ExecBatchSourceNode::kKindName[] = "ExecBatchSourceNode";

struct ArrayVectorSourceNode
: public SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions> {
using ArrayVectorSchemaSourceNode =
SchemaSourceNode<ArrayVectorSourceNode, ArrayVectorSourceNodeOptions>;

using ArrayVectorSchemaSourceNode::ArrayVectorSchemaSourceNode;

static Result<ExecNode*> Make(ExecPlan* plan, std::vector<ExecNode*> inputs,
const ExecNodeOptions& options) {
return ArrayVectorSchemaSourceNode::Make(plan, inputs, options);
}

const char* kind_name() const override { return kKindName; }

static Result<arrow::AsyncGenerator<std::optional<ExecBatch>>> MakeGenerator(
Iterator<std::shared_ptr<ArrayVector>>& arrayvec_it,
arrow::internal::Executor* io_executor, const std::shared_ptr<Schema>& schema) {
auto to_exec_batch =
[](const std::shared_ptr<ArrayVector>& arrayvec) -> std::optional<ExecBatch> {
if (arrayvec == NULLPTR || arrayvec->size() == 0) {
return std::nullopt;
}
std::vector<Datum> datumvec;
for (const auto& array : *arrayvec) {
datumvec.push_back(Datum(array));
}
return std::optional<ExecBatch>(
ExecBatch(std::move(datumvec), (*arrayvec)[0]->length()));
};
auto exec_batch_it = MakeMapIterator(to_exec_batch, std::move(arrayvec_it));
return MakeBackgroundGenerator(std::move(exec_batch_it), io_executor);
}

static const char kKindName[];
};

const char ArrayVectorSourceNode::kKindName[] = "ArrayVectorSourceNode";

} // namespace

namespace internal {

void RegisterSourceNode(ExecFactoryRegistry* registry) {
DCHECK_OK(registry->AddFactory("source", SourceNode::Make));
DCHECK_OK(registry->AddFactory("table_source", TableSourceNode::Make));
DCHECK_OK(registry->AddFactory("record_batch_source", RecordBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("exec_batch_source", ExecBatchSourceNode::Make));
DCHECK_OK(registry->AddFactory("array_vector_source", ArrayVectorSourceNode::Make));
}

} // namespace internal
Expand Down
32 changes: 32 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -258,6 +258,38 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
return out_batches;
}

Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ArrayVector>> arrayvecs;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
arrayvecs.push_back(std::make_shared<ArrayVector>(record_batch->columns()));
}
return arrayvecs;
}

Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<ExecBatch>> exec_batches;
for (auto batch : batches_with_schema.batches) {
auto exec_batch = std::make_shared<ExecBatch>(batch);
exec_batches.push_back(exec_batch);
}
return exec_batches;
}

Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches_with_schema) {
std::vector<std::shared_ptr<RecordBatch>> record_batches;
for (auto batch : batches_with_schema.batches) {
ARROW_ASSIGN_OR_RAISE(auto record_batch,
batch.ToRecordBatch(batches_with_schema.schema));
record_batches.push_back(record_batch);
}
return record_batches;
}

Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab) {
std::vector<SortKey> sort_keys;
for (auto&& f : tab->schema()->fields()) {
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/arrow/compute/exec/test_util.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,30 @@ BatchesWithSchema MakeBatchesFromString(const std::shared_ptr<Schema>& schema,
const std::vector<std::string_view>& json_strings,
int multiplicity = 1);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ArrayVector>>> ToArrayVectors(
const BatchesWithSchema& batches_with_schema);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<ExecBatch>>> ToExecBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::vector<std::shared_ptr<RecordBatch>>> ToRecordBatches(
const BatchesWithSchema& batches);

ARROW_TESTING_EXPORT
Result<std::shared_ptr<Table>> SortTableOnAllFields(const std::shared_ptr<Table>& tab);

Expand Down