From 538b77c12309b38cd241ccee84fa0974213b0ae7 Mon Sep 17 00:00:00 2001 From: sundy-li <543950155@qq.com> Date: Fri, 25 Oct 2019 18:43:52 +0800 Subject: [PATCH] functions: add char function as mysql behavior --- dbms/src/Functions/FunctionsCoding.cpp | 1 + dbms/src/Functions/FunctionsCoding.h | 91 +++++++++++++++++++ .../0_stateless/00076_ip_coding_functions.sql | 2 +- .../0_stateless/01020_function_char.reference | 2 + .../0_stateless/01020_function_char.sql | 3 + .../functions/encoding_functions.md | 3 + 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 dbms/tests/queries/0_stateless/01020_function_char.reference create mode 100644 dbms/tests/queries/0_stateless/01020_function_char.sql diff --git a/dbms/src/Functions/FunctionsCoding.cpp b/dbms/src/Functions/FunctionsCoding.cpp index e168b8b61e4..934dfd150f9 100644 --- a/dbms/src/Functions/FunctionsCoding.cpp +++ b/dbms/src/Functions/FunctionsCoding.cpp @@ -25,6 +25,7 @@ void registerFunctionsCoding(FunctionFactory & factory) factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); + factory.registerFunction(FunctionFactory::CaseInsensitive); factory.registerFunction(); factory.registerFunction(); factory.registerFunction(); diff --git a/dbms/src/Functions/FunctionsCoding.h b/dbms/src/Functions/FunctionsCoding.h index 1ab00d725f6..23249621230 100644 --- a/dbms/src/Functions/FunctionsCoding.h +++ b/dbms/src/Functions/FunctionsCoding.h @@ -1276,6 +1276,97 @@ public: } }; +class FunctionChar : public IFunction +{ +public: + static constexpr auto name = "char"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + bool isVariadic() const override { return true; } + bool isInjective(const Block &) override { return true; } + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (arguments.empty()) + throw Exception("Number of arguments for function " + getName() + " can't be " + toString(arguments.size()) + + ", should be at least 1", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + for (const auto & arg : arguments) + { + WhichDataType which(arg); + if (!(which.isInt() || which.isUInt() || which.isFloat())) + throw Exception("Illegal type " + arg->getName() + " of argument of function " + getName() + + ", must be Int, UInt or Float number", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + return std::make_shared(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override + { + const size_t rows = block.getByPosition(arguments[0]).column.get()->size(); + auto col_str = ColumnString::create(); + ColumnString::Chars & out_vec = col_str->getChars(); + ColumnString::Offsets & out_offsets = col_str->getOffsets(); + + const auto size_per_row = arguments.size() + 1; + out_vec.resize(size_per_row * rows); + out_offsets.resize(rows); + + for (size_t row = 0; row < rows; ++row) + { + out_offsets[row] = size_per_row + out_offsets[row - 1]; + out_vec[row * size_per_row + size_per_row - 1] = '\0'; + } + + for (const size_t & column_idx : arguments) + { + const IColumn * column = block.getByPosition(column_idx).column.get(); + if (!(executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row) + || executeNumber(*column, out_vec, column_idx, rows, size_per_row))) + { + throw Exception{"Illegal column " + block.getByPosition(column_idx).column->getName() + + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN}; + } + } + + block.getByPosition(result).column = std::move(col_str); + } + +private: + template + bool executeNumber(const IColumn & src_data, ColumnString::Chars & out_vec, const size_t & column_idx, const size_t & rows, const size_t & size_per_row) + { + const ColumnVector * src_data_concrete = checkAndGetColumn>(&src_data); + + if (!src_data_concrete) + { + return false; + } + + for (size_t row = 0; row < rows; ++row) + { + out_vec[row * size_per_row + column_idx] = static_cast(src_data_concrete->getInt(row)); + } + return true; + } +}; class FunctionBitmaskToArray : public IFunction { diff --git a/dbms/tests/queries/0_stateless/00076_ip_coding_functions.sql b/dbms/tests/queries/0_stateless/00076_ip_coding_functions.sql index 659267c61ed..a2820b56ba6 100644 --- a/dbms/tests/queries/0_stateless/00076_ip_coding_functions.sql +++ b/dbms/tests/queries/0_stateless/00076_ip_coding_functions.sql @@ -125,4 +125,4 @@ CREATE TABLE addresses(addr String) ENGINE = Memory; INSERT INTO addresses(addr) VALUES ('00000000000000000000FFFFC1FC110A'), ('00000000000000000000FFFF4D583737'), ('00000000000000000000FFFF7F000001'); SELECT cutIPv6(toFixedString(unhex(addr), 16), 0, 3) FROM addresses ORDER BY addr ASC; -DROP TABLE addresses; +DROP TABLE addresses; \ No newline at end of file diff --git a/dbms/tests/queries/0_stateless/01020_function_char.reference b/dbms/tests/queries/0_stateless/01020_function_char.reference new file mode 100644 index 00000000000..9f055b585d4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01020_function_char.reference @@ -0,0 +1,2 @@ +ABCDabcdefg +ABC diff --git a/dbms/tests/queries/0_stateless/01020_function_char.sql b/dbms/tests/queries/0_stateless/01020_function_char.sql new file mode 100644 index 00000000000..ce099aa17a4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01020_function_char.sql @@ -0,0 +1,3 @@ +/* char function */ +SELECT char(65, 66.1, 67.2, 68.3, 97.4, 98.5, 99.6, 100.7, 101.0, 102.0, 103.0); +SELECT char(65 + 256, 66 + 1024, 66 + 1024 + 1); diff --git a/docs/en/query_language/functions/encoding_functions.md b/docs/en/query_language/functions/encoding_functions.md index 74ef53f82f7..9f7413795d8 100644 --- a/docs/en/query_language/functions/encoding_functions.md +++ b/docs/en/query_language/functions/encoding_functions.md @@ -1,5 +1,8 @@ # Encoding functions +## char +Accepts multiple arguments of `Number` types. Returns a string, each char of the results corresponds to the ascii character of the input numbers. It'll cast the first byte from the number, if the byte overflows the range of ascii(which is 127), it returns an unrecognized character(�). + ## hex Accepts arguments of types: `String`, `unsigned integer`, `Date`, or `DateTime`. Returns a string containing the argument's hexadecimal representation. Uses uppercase letters `A-F`. Does not use `0x` prefixes or `h` suffixes. For strings, all bytes are simply encoded as two hexadecimal numbers. Numbers are converted to big endian ("human readable") format. For numbers, older zeros are trimmed, but only by entire bytes. For example, `hex (1) = '01'`. `Date` is encoded as the number of days since the beginning of the Unix epoch. `DateTime` is encoded as the number of seconds since the beginning of the Unix epoch.