query/binder.hpp (628 lines of code) (raw):

// Copyright (c) 2017-2018 Uber Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef QUERY_BINDER_HPP_ #define QUERY_BINDER_HPP_ #include <cuda_runtime.h> #include <cfloat> #include <cstdint> #include <tuple> #include <type_traits> #include <vector> #include "query/algorithm.hpp" #include "query/functor.hpp" #include "query/iterator.hpp" #include "query/memory.hpp" #include "query/time_series_aggregate.h" namespace ares { template<typename Value> ForeignTableIterator<Value> *prepareForeignTableIterators( int32_t numBatches, VectorPartySlice *vpSlices, size_t stepBytes, bool hasDefault, Value defaultValue, cudaStream_t stream); // Forward declaration; template<typename Context, int NumVectors, int NumUnboundIterators> class InputVectorBinderBase; // InputVectorBinder will bind NumVectors InputVector struct to different type // of iterators. When there is no more input vector to bind, it will call // context's run function with all the bound iterator. // A example usage is like: // ares::FilterContext<UnaryFunctorType> ctx(predicateVector, // indexVectorLength, // foreignTableRecordIDVectors, // numForeignTables, // functorType, // cudaStream); // std::vector<InputVector> inputVectors = {input}; // ares::InputVectorBinder<ares::FilterContext<UnaryFunctorType>, 1> // binder(ctx, inputVectors, indexVector, baseCounts, startCount); // resHandle.res = // reinterpret_cast<void *>(binder.bind()); // // If the caller need to specialize the binder with a context, they will always template<typename Context, int NumVectors> class InputVectorBinder : public InputVectorBinderBase<Context, NumVectors, NumVectors> { typedef InputVectorBinderBase<Context, NumVectors, NumVectors> super_t; public: explicit InputVectorBinder(Context context, std::vector<InputVector> inputVectors, uint32_t *indexVector, uint32_t *baseCounts, uint32_t startCount) : super_t(context, inputVectors, indexVector, baseCounts, startCount) { } }; // InputIteratorBinderBase is the class to bind InputVector structs into // individual input iterators. It will bind one input vector at one time until // N becomes zero. template<typename Context, int NumVectors, int NumUnboundIterators> class InputVectorBinderBase { public: explicit InputVectorBinderBase(Context context, std::vector<InputVector> inputVectors, uint32_t *indexVector, uint32_t *baseCounts, uint32_t startCount) : context(context), inputVectors(inputVectors), indexVector(indexVector), baseCounts(baseCounts), startCount(startCount) {} protected: Context context; std::vector<InputVector> inputVectors; uint32_t *indexVector; uint32_t *baseCounts; uint32_t startCount; template<typename ...InputIterators> int bindGeneric(InputIterators... boundInputIterators) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; #define BIND_CONSTANT_INPUT(defaultValue, isValid) \ return nextBinder.bind( \ boundInputIterators..., \ make_constant_iterator( \ defaultValue, isValid)); if (input.Type == ConstantInput) { ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstInt) { BIND_CONSTANT_INPUT(constant.Value.IntVal, constant.IsValid) } else if (constant.DataType == ConstFloat) { BIND_CONSTANT_INPUT(constant.Value.FloatVal, constant.IsValid) } } else if (input.Type == ScratchSpaceInput) { ScratchSpaceVector scratchSpace = input.Vector.ScratchSpace; uint32_t nullsOffset = scratchSpace.NullsOffset; #define BIND_SCRATCH_SPACE_INPUT(dataType) \ return nextBinder.bind( \ boundInputIterators..., \ make_scratch_space_input_iterator<dataType>( \ scratchSpace.Values, \ nullsOffset)); switch (scratchSpace.DataType) { case Int32: BIND_SCRATCH_SPACE_INPUT(int32_t) case Uint32: BIND_SCRATCH_SPACE_INPUT(uint32_t) case Float32: BIND_SCRATCH_SPACE_INPUT(float_t) case UUID: BIND_SCRATCH_SPACE_INPUT(UUIDT) case GeoPoint: BIND_SCRATCH_SPACE_INPUT(GeoPointT) default: throw std::invalid_argument( "Unsupported data type for ScratchSpaceInput"); } } else if (input.Type == ForeignColumnInput) { // Note: for now foreign vectors are dimension table columns // that are not compressed nor pre sliced RecordID *recordIDs = input.Vector.ForeignVP.RecordIDs; const int32_t numBatches = input.Vector.ForeignVP.NumBatches; const int32_t baseBatchID = input.Vector.ForeignVP.BaseBatchID; VectorPartySlice *vpSlices = input.Vector.ForeignVP.Batches; const int32_t numRecordsInLastBatch = input.Vector.ForeignVP.NumRecordsInLastBatch; int16_t *const timezoneLookup = input.Vector.ForeignVP.TimezoneLookup; int16_t timezoneLookupSize = input.Vector.ForeignVP.TimezoneLookupSize; DataType dataType = input.Vector.ForeignVP.DataType; bool hasDefault = input.Vector.ForeignVP.DefaultValue.HasDefault; DefaultValue defaultValueStruct = input.Vector.ForeignVP.DefaultValue; uint8_t stepInBytes = getStepInBytes(dataType); switch (dataType) { #define BIND_FOREIGN_COLUMN_INPUT(defaultValue, dataType) \ ForeignTableIterator<dataType> *vpIters = \ prepareForeignTableIterators(numBatches, vpSlices, stepInBytes, \ hasDefault, defaultValue, context.getStream()); \ int res = nextBinder.bind(boundInputIterators..., \ RecordIDJoinIterator<dataType>( \ recordIDs, numBatches, baseBatchID, \ vpIters, numRecordsInLastBatch, \ timezoneLookup, timezoneLookupSize)); \ deviceFree(vpIters); \ return res; case Bool: { BIND_FOREIGN_COLUMN_INPUT(defaultValueStruct.Value.BoolVal, bool) } case Int8: case Int16: case Int32: { BIND_FOREIGN_COLUMN_INPUT( defaultValueStruct.Value.Int32Val, int32_t) } case Uint8: case Uint16: case Uint32: { BIND_FOREIGN_COLUMN_INPUT( defaultValueStruct.Value.Uint32Val, uint32_t) } case Float32: { BIND_FOREIGN_COLUMN_INPUT( defaultValueStruct.Value.FloatVal, float_t) } default: throw std::invalid_argument( "Unsupported data type for VectorPartyInput: " + std::to_string(__LINE__)); } } VectorPartySlice inputVP = input.Vector.VP; bool hasDefault = inputVP.DefaultValue.HasDefault; bool isConstant = inputVP.BasePtr == nullptr; DefaultValue defaultValue = inputVP.DefaultValue; if (isConstant) { switch (inputVP.DataType) { case Bool: BIND_CONSTANT_INPUT(defaultValue.Value.BoolVal, hasDefault) case Int8: case Int16: case Int32: BIND_CONSTANT_INPUT(defaultValue.Value.Int32Val, hasDefault) case Uint8: case Uint16: case Uint32: BIND_CONSTANT_INPUT(defaultValue.Value.Uint32Val, hasDefault) case Float32: BIND_CONSTANT_INPUT(defaultValue.Value.FloatVal, hasDefault) default: throw std::invalid_argument( "Unsupported data type for VectorPartyInput: " + std::to_string(__LINE__)); } } // Non constant. uint8_t *basePtr = inputVP.BasePtr; uint32_t nullsOffset = inputVP.NullsOffset; uint32_t valuesOffset = inputVP.ValuesOffset; uint8_t startingIndex = inputVP.StartingIndex; uint8_t stepInBytes = getStepInBytes(inputVP.DataType); uint32_t length = inputVP.Length; switch (inputVP.DataType) { #define BIND_COLUMN_INPUT(dataType) \ return nextBinder.bind(boundInputIterators..., \ make_column_iterator<dataType>(indexVector, \ baseCounts, \ startCount, \ basePtr, \ nullsOffset, \ valuesOffset, \ length, \ stepInBytes, \ startingIndex)); case Bool: BIND_COLUMN_INPUT(bool) case Int8: case Int16: case Int32: BIND_COLUMN_INPUT(int32_t) case Uint8: case Uint16: case Uint32: BIND_COLUMN_INPUT(uint32_t) case Float32: BIND_COLUMN_INPUT(float_t) default: throw std::invalid_argument( "Unsupported data type for VectorPartyInput: " + std::to_string(__LINE__)); } } template<typename GeoIterator> int bindGeoPoint(GeoIterator geoIter) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == ConstantInput) { ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstGeoPoint) { return nextBinder.bind( geoIter, thrust::make_constant_iterator( thrust::make_tuple<GeoPointT, bool>( constant.Value.GeoPointVal, constant.IsValid))); } } throw std::invalid_argument( "Unsupported data type " + std::to_string(__LINE__) + "when value type of first input iterator is GeoPoint"); } template<typename UUIDIterator> int bindUUID(UUIDIterator uuidIter) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == ConstantInput) { ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstUUID) { return nextBinder.bind( uuidIter, thrust::make_constant_iterator( thrust::make_tuple<UUIDT, bool>( constant.Value.UUIDVal, constant.IsValid))); } } throw std::invalid_argument( "Unsupported data type " + std::to_string(__LINE__) + "when value type of first input iterator is UUID"); } public: template<typename ...InputIterators> int bind(InputIterators... boundInputIterators) { return bindGeneric(boundInputIterators...); } // when this is the first input iterator, we allow geo point iterator and uuid // iterator. int bind() { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == VectorPartyInput) { VectorPartySlice inputVP = input.Vector.VP; DataType dataType = inputVP.DataType; uint8_t *basePtr = inputVP.BasePtr; bool hasDefault = inputVP.DefaultValue.HasDefault; DefaultValue defaultValue = inputVP.DefaultValue; uint32_t nullsOffset = inputVP.NullsOffset; uint32_t valuesOffset = inputVP.ValuesOffset; uint8_t startingIndex = inputVP.StartingIndex; uint8_t stepInBytes = getStepInBytes(inputVP.DataType); uint32_t length = inputVP.Length; // This macro will bind column type with width > 4 bytes (GeoPoint, UUID // int64). Since our scratch space is always 4 bytes (int32, uint32, // float), parent nodes for those wider types must be a root node. #define BIND_WIDER_COLUMN_INPUT(dataType, defaultValue) \ if (basePtr == nullptr) { \ return nextBinder.bind(thrust::make_constant_iterator( \ thrust::make_tuple<dataType, bool>( \ defaultValue, hasDefault))); \ } \ return nextBinder.bind(make_column_iterator<dataType>( \ indexVector, baseCounts, startCount, basePtr, nullsOffset, \ valuesOffset, length, stepInBytes, startingIndex)); switch (dataType) { case GeoPoint: BIND_WIDER_COLUMN_INPUT(GeoPointT, defaultValue.Value.GeoPointVal) case UUID: BIND_WIDER_COLUMN_INPUT(UUIDT, defaultValue.Value.UUIDVal) case Int64: BIND_WIDER_COLUMN_INPUT(int64_t, defaultValue.Value.Int64Val) default: break; } } else if (input.Type == ForeignColumnInput) { RecordID *recordIDs = input.Vector.ForeignVP.RecordIDs; const int32_t numBatches = input.Vector.ForeignVP.NumBatches; const int32_t baseBatchID = input.Vector.ForeignVP.BaseBatchID; VectorPartySlice *vpSlices = input.Vector.ForeignVP.Batches; const int32_t numRecordsInLastBatch = input.Vector.ForeignVP.NumRecordsInLastBatch; DataType dataType = input.Vector.ForeignVP.DataType; bool hasDefault = input.Vector.ForeignVP.DefaultValue.HasDefault; DefaultValue defaultValueStruct = input.Vector.ForeignVP.DefaultValue; uint8_t stepInBytes = getStepInBytes(dataType); #define BIND_WIDER_FOREIGN_COLUMN_INPUT(defaultValue, dataType) { \ ForeignTableIterator<dataType> *vpIters = \ prepareForeignTableIterators(numBatches, vpSlices, stepInBytes, \ hasDefault, defaultValue, context.getStream()); \ int res = nextBinder.bind(RecordIDJoinIterator<dataType>( \ recordIDs, numBatches, baseBatchID, \ vpIters, numRecordsInLastBatch, \ nullptr, 0)); \ deviceFree(vpIters); \ return res; \ } switch (dataType) { case UUID: BIND_WIDER_FOREIGN_COLUMN_INPUT( defaultValueStruct.Value.UUIDVal, UUIDT) case Int64: BIND_WIDER_FOREIGN_COLUMN_INPUT( defaultValueStruct.Value.Int64Val, int64_t) default: break; } } else if (input.Type == ArrayVectorPartyInput) { // Array type can only bind to first argument for BinaryTransformer ArrayVectorPartySlice inputVP = input.Vector.ArrayVP; uint8_t *basePtr = inputVP.OffsetLengthVector; uint32_t length = inputVP.Length; int valueOffsetAdj = inputVP.ValueOffsetAdj; #define BIND_ARRAY_COLUMN_INPUT(dataType) \ return nextBinder.bind(make_array_column_iterator<dataType>( \ basePtr, valueOffsetAdj, length)); switch (inputVP.DataType) { case Bool: BIND_ARRAY_COLUMN_INPUT(bool) case Int8: BIND_ARRAY_COLUMN_INPUT(int8_t) case Int16: BIND_ARRAY_COLUMN_INPUT(int16_t) case Int32: BIND_ARRAY_COLUMN_INPUT(int32_t) case Uint8: BIND_ARRAY_COLUMN_INPUT(uint8_t) case Uint16: BIND_ARRAY_COLUMN_INPUT(uint16_t) case Uint32: BIND_ARRAY_COLUMN_INPUT(uint32_t) case Float32: BIND_ARRAY_COLUMN_INPUT(float_t) case Int64: BIND_ARRAY_COLUMN_INPUT(int64_t) case UUID: BIND_ARRAY_COLUMN_INPUT(UUIDT) case GeoPoint: BIND_ARRAY_COLUMN_INPUT(GeoPointT) default: throw std::invalid_argument( "Unsupported data type for ArrayVectorPartyInput: " + std::to_string(__LINE__)); } } return bindGeneric(); } // Int64 data type is only supported in UnaryTransform template <typename Int64Iterator> typename std::enable_if< std::is_same<typename Int64Iterator::value_type::head_type, int64_t>::value, int>::type bind(Int64Iterator int64Iter) { throw std::invalid_argument( "int64 data type is only supported in UnaryTransform" + std::to_string(__LINE__)); } // Special handling if the first input iter is a geo iter. template<typename GeoIterator> typename std::enable_if< std::is_same<typename GeoIterator::value_type::head_type, GeoPointT>::value, int>::type bind( GeoIterator geoIter) { return bindGeoPoint(geoIter); } // Special handling if the first input iter is a UUID iter. template<typename UUIDIterator> typename std::enable_if< std::is_same<typename UUIDIterator::value_type::head_type, UUIDT>::value, int>::type bind( UUIDIterator uuidIter) { return bindUUID(uuidIter); } // Array type only support UnaryTransform or first argument in BinaryTransform // for non-uuid/non-geopoint type template <typename ArrayColumnIterator> typename std::enable_if< std::is_pointer< typename ArrayColumnIterator::value_type::head_type>::value && !std::is_same<typename ArrayColumnIterator::value_type::head_type, GeoPointT*>::value && !std::is_same<typename ArrayColumnIterator::value_type::head_type, UUIDT*>::value, int>::type bind(ArrayColumnIterator arrayColumnIter) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == ConstantInput) { #define BIND_ARRAY_CONSTANT_INPUT(defaultValue, isValid) \ return nextBinder.bind( \ arrayColumnIter, \ make_constant_iterator( \ defaultValue, isValid)); ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstInt) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.IntVal, constant.IsValid) } else if (constant.DataType == ConstFloat) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.FloatVal, constant.IsValid) } } throw std::invalid_argument( "Unsupported data type " + std::to_string(__LINE__) + "when value type of first input iterator is ArrayVP Iterator"); } // Array type only support UnaryTransform or first argument in BinaryTransform // specialized for uuid template <typename ArrayColumnIterator> typename std::enable_if< std::is_pointer< typename ArrayColumnIterator::value_type::head_type>::value && std::is_same<typename ArrayColumnIterator::value_type::head_type, UUIDT*>::value, int>::type bind(ArrayColumnIterator arrayColumnIter) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == ConstantInput) { #define BIND_ARRAY_CONSTANT_INPUT(defaultValue, isValid) \ return nextBinder.bind( \ arrayColumnIter, \ make_constant_iterator( \ defaultValue, isValid)); ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstInt) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.IntVal, constant.IsValid) } else if (constant.DataType == ConstUUID) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.UUIDVal, constant.IsValid) } } throw std::invalid_argument( "Unsupported data type " + std::to_string(__LINE__) + "when value type of first input iterator is ArrayVP Iterator"); } // Array type only support UnaryTransform or first argument in BinaryTransform // specialized for geopoint template <typename ArrayColumnIterator> typename std::enable_if< std::is_pointer< typename ArrayColumnIterator::value_type::head_type>::value && std::is_same<typename ArrayColumnIterator::value_type::head_type, GeoPointT*>::value, int>::type bind(ArrayColumnIterator arrayColumnIter) { InputVectorBinderBase<Context, NumVectors, NumUnboundIterators - 1> nextBinder(context, inputVectors, indexVector, baseCounts, startCount); InputVector input = inputVectors[NumVectors - NumUnboundIterators]; if (input.Type == ConstantInput) { #define BIND_ARRAY_CONSTANT_INPUT(defaultValue, isValid) \ return nextBinder.bind( \ arrayColumnIter, \ make_constant_iterator( \ defaultValue, isValid)); ConstantVector constant = input.Vector.Constant; if (constant.DataType == ConstInt) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.IntVal, constant.IsValid) } else if (constant.DataType == ConstGeoPoint) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.GeoPointVal, constant.IsValid) } else if (constant.DataType == ConstUUID) { BIND_ARRAY_CONSTANT_INPUT(constant.Value.GeoPointVal, constant.IsValid) } } throw std::invalid_argument( "Unsupported data type " + std::to_string(__LINE__) + "when value type of first input iterator is ArrayVP Iterator"); } }; // This class is called when there is no more unbound iterators. It will just // call context.run to do actual calculation. template<typename Context, int NumVectors> class InputVectorBinderBase<Context, NumVectors, 0> { public: explicit InputVectorBinderBase(Context context, std::vector<InputVector> inputVectors, uint32_t *indexVector, uint32_t *baseCounts, uint32_t startCount) : context(context), inputVectors(inputVectors), indexVector(indexVector), baseCounts(baseCounts), startCount(startCount) {} protected: Context context; std::vector<InputVector> inputVectors; uint32_t *indexVector; uint32_t *baseCounts; uint32_t startCount; public: template<typename ...InputIterators> int bind(InputIterators... boundInputIterators) { return context.run(indexVector, boundInputIterators...); } }; template<typename Value> ForeignTableIterator<Value> *prepareForeignTableIterators( int32_t numBatches, VectorPartySlice *vpSlices, size_t stepBytes, bool hasDefault, Value defaultValue, cudaStream_t stream) { typedef ForeignTableIterator<Value> ValueIter; int totalSize = sizeof(ValueIter) * numBatches; ValueIter* batches; std::vector<ValueIter> batchesH(numBatches); for (int i = 0; i < numBatches; i++) { VectorPartySlice inputVP = vpSlices[i]; if (inputVP.BasePtr == nullptr) { batchesH[i] = ValueIter( make_constant_iterator(defaultValue, hasDefault)); } else { batchesH[i] = ValueIter( VectorPartyIterator<Value>( nullptr, 0, inputVP.BasePtr, inputVP.NullsOffset, inputVP.ValuesOffset, inputVP.Length, stepBytes, inputVP.StartingIndex)); } } // In host mode we actually don't need to make another copy // of the iterators but we still do here to keep the same code // for host and device mode. ValueIter *vpItersDevice; deviceMalloc(reinterpret_cast<void **>( &vpItersDevice), totalSize); ares::asyncCopyHostToDevice(reinterpret_cast<void *>(vpItersDevice), reinterpret_cast<void *>(&batchesH[0]), totalSize, stream); batches = vpItersDevice; return batches; } // IndexZipIteratorMapper is the mapper to map number of foreign tables to // the actual zip iterator template<int NumTotalForeignTables> struct IndexZipIteratorMapper { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*>> type; }; template<> struct IndexZipIteratorMapper<1> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<2> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<3> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<4> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<5> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<6> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<7> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*>> type; }; template<> struct IndexZipIteratorMapper<8> { typedef thrust::zip_iterator< thrust::tuple < thrust::counting_iterator < uint32_t>, uint32_t*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*, RecordID*>> type; }; // IndexZipIteratorMaker is the factory to make the index zip iterator given // a counting iterator, a main table index vector and 0 or more foreign table // record id vector. It binds one record id vector at one time from the // unboundForeignTableRecordIDVectors. If the NUnboundForeignTable is 0, it will // just return the tuple template<int NumTotalForeignTables, int NumUnboundForeignTables> struct IndexZipIteratorMakerBase { template<typename... RecordIDVector> typename IndexZipIteratorMapper<NumTotalForeignTables>::type make(uint32_t *index_vector, RecordID **unboundForeignTableRecordIDVectors, RecordIDVector... boundForeignRecordIDVectors) { IndexZipIteratorMakerBase<NumTotalForeignTables, NumUnboundForeignTables - 1> nextMaker; return nextMaker.make( index_vector, unboundForeignTableRecordIDVectors, boundForeignRecordIDVectors..., unboundForeignTableRecordIDVectors[ NumTotalForeignTables - NumUnboundForeignTables]); } }; // Specialized IndexZipIteratorMakerBase with NUnboundForeignTable to be zero. // Just bind everything together using thrust::make_tuple and return. template<int NumTotalForeignTables> struct IndexZipIteratorMakerBase<NumTotalForeignTables, 0> { template<typename... RecordIDVector> typename IndexZipIteratorMapper<NumTotalForeignTables>::type make(uint32_t *indexVector, RecordID **unboundForeignTableRecordIDVectors, RecordIDVector... boundForeignRecordIDVectors) { return thrust::make_zip_iterator(thrust::make_tuple( thrust::counting_iterator<uint32_t>(0), indexVector, boundForeignRecordIDVectors...)); } }; template<int NumTotalForeignTables> struct IndexZipIteratorMaker : public IndexZipIteratorMakerBase< NumTotalForeignTables, NumTotalForeignTables> { }; } // namespace ares #endif // QUERY_BINDER_HPP_