#include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int LOGICAL_ERROR; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int TOO_LARGE_ARRAY_SIZE; } namespace { class FunctionGeohashesInBox : public IFunction { public: static constexpr auto name = "geohashesInBox"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } size_t getNumberOfArguments() const override { return 5; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { validateArgumentType(*this, arguments, 0, isFloat, "float"); validateArgumentType(*this, arguments, 1, isFloat, "float"); validateArgumentType(*this, arguments, 2, isFloat, "float"); validateArgumentType(*this, arguments, 3, isFloat, "float"); validateArgumentType(*this, arguments, 4, isUInt8, "integer"); if (!(arguments[0]->equals(*arguments[1]) && arguments[0]->equals(*arguments[2]) && arguments[0]->equals(*arguments[3]))) { throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type of argument of {} all coordinate arguments must have the same type, " "instead they are:{}, {}, {}, {}.", getName(), arguments[0]->getName(), arguments[1]->getName(), arguments[2]->getName(), arguments[3]->getName()); } return std::make_shared(std::make_shared()); } bool useDefaultImplementationForConstants() const override { return true; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } template void execute( const IColumn * lon_min_column, const IColumn * lat_min_column, const IColumn * lon_max_column, const IColumn * lat_max_column, const IColumn * precision_column, ColumnPtr & result, size_t input_rows_count) const { static constexpr size_t max_array_size = 10'000'000; const auto * lon_min_const = typeid_cast(lon_min_column); const auto * lat_min_const = typeid_cast(lat_min_column); const auto * lon_max_const = typeid_cast(lon_max_column); const auto * lat_max_const = typeid_cast(lat_max_column); const auto * precision_const = typeid_cast(precision_column); if (lon_min_const) lon_min_column = &lon_min_const->getDataColumn(); if (lat_min_const) lat_min_column = &lat_min_const->getDataColumn(); if (lon_max_const) lon_max_column = &lon_max_const->getDataColumn(); if (lat_max_const) lat_max_column = &lat_max_const->getDataColumn(); if (precision_const) precision_column = &precision_const->getDataColumn(); const auto * lon_min = checkAndGetColumn>(lon_min_column); const auto * lat_min = checkAndGetColumn>(lat_min_column); const auto * lon_max = checkAndGetColumn>(lon_max_column); const auto * lat_max = checkAndGetColumn>(lat_max_column); const auto * precision = checkAndGetColumn>(precision_column); if (!lon_min || !lat_min || !lon_max || !lat_max || !precision) { throw Exception(ErrorCodes::LOGICAL_ERROR, "Unsupported argument types for function {} : {}, {}, {}, {}.", getName(), lon_min_column->getName(), lat_min_column->getName(), lon_max_column->getName(), lat_max_column->getName()); } auto col_res = ColumnArray::create(ColumnString::create()); ColumnString & res_strings = typeid_cast(col_res->getData()); ColumnArray::Offsets & res_offsets = col_res->getOffsets(); ColumnString::Chars & res_strings_chars = res_strings.getChars(); ColumnString::Offsets & res_strings_offsets = res_strings.getOffsets(); for (size_t row = 0; row < input_rows_count; ++row) { const Float64 lon_min_value = lon_min->getElement(lon_min_const ? 0 : row); const Float64 lat_min_value = lat_min->getElement(lat_min_const ? 0 : row); const Float64 lon_max_value = lon_max->getElement(lon_max_const ? 0 : row); const Float64 lat_max_value = lat_max->getElement(lat_max_const ? 0 : row); const PrecisionType precision_value = precision->getElement(precision_const ? 0 : row); const auto prepared_args = geohashesInBoxPrepare( lon_min_value, lat_min_value, lon_max_value, lat_max_value, precision_value); if (prepared_args.items_count > max_array_size) { throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "{} would produce {} array elements, " "which is bigger than the allowed maximum of {}", getName(), prepared_args.items_count, max_array_size); } res_strings_offsets.reserve(res_strings_offsets.size() + prepared_args.items_count); res_strings_chars.resize(res_strings_chars.size() + prepared_args.items_count * (prepared_args.precision + 1)); const auto starting_offset = res_strings_offsets.empty() ? 0 : res_strings_offsets.back(); char * out = reinterpret_cast(res_strings_chars.data() + starting_offset); // Actually write geohashes into preallocated buffer. geohashesInBox(prepared_args, out); for (UInt64 i = 1; i <= prepared_args.items_count ; ++i) res_strings_offsets.push_back(starting_offset + (prepared_args.precision + 1) * i); res_offsets.push_back(res_offsets.back() + prepared_args.items_count); } if (!res_strings_offsets.empty() && res_strings_offsets.back() != res_strings_chars.size()) { throw Exception(ErrorCodes::LOGICAL_ERROR, "String column size mismatch (internal logical error)"); } if (!res_offsets.empty() && res_offsets.back() != res_strings.size()) { throw Exception(ErrorCodes::LOGICAL_ERROR, "Array column size mismatch (internal logical error){} != {}", res_offsets.back(), std::to_string(res_strings.size())); } result = std::move(col_res); } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { const IColumn * lon_min = arguments[0].column.get(); const IColumn * lat_min = arguments[1].column.get(); const IColumn * lon_max = arguments[2].column.get(); const IColumn * lat_max = arguments[3].column.get(); const IColumn * precision = arguments[4].column.get(); ColumnPtr res; if (checkColumn>(lon_min)) execute(lon_min, lat_min, lon_max, lat_max, precision, res, input_rows_count); else execute(lon_min, lat_min, lon_max, lat_max, precision, res, input_rows_count); return res; } }; } REGISTER_FUNCTION(GeohashesInBox) { factory.registerFunction(); } }