From b67b8d8b545ca95d19260fe9e35a1f7f13d85152 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 28 Aug 2011 00:31:30 +0000 Subject: [PATCH] dbms: development [#CONV-2944]. --- dbms/include/DB/Columns/ColumnArray.h | 2 + dbms/include/DB/Columns/ColumnConst.h | 1 + dbms/include/DB/Columns/ColumnFixedArray.h | 2 + dbms/include/DB/Columns/ColumnFixedString.h | 2 + dbms/include/DB/Columns/ColumnString.h | 2 + dbms/include/DB/Columns/ColumnVector.h | 2 + dbms/include/DB/Columns/IColumn.h | 4 + dbms/include/DB/Core/Field.h | 19 +- dbms/include/DB/Core/Types.h | 35 +++- .../DB/Functions/FunctionsArithmetic.h | 9 +- .../DB/Functions/FunctionsComparison.h | 14 +- dbms/include/DB/Functions/FunctionsLogical.h | 12 +- dbms/include/DB/Parsers/ASTSelectQuery.h | 17 +- dbms/include/DB/Parsers/ParserSelectQuery.h | 32 +--- .../DataStreams/FilterBlockInputStream.cpp | 11 +- dbms/src/Parsers/ParserSelectQuery.cpp | 162 ++++++++++++++++++ dbms/src/Parsers/formatAST.cpp | 48 +++++- dbms/src/Parsers/tests/select_parser.cpp | 9 +- 18 files changed, 306 insertions(+), 77 deletions(-) create mode 100644 dbms/src/Parsers/ParserSelectQuery.cpp diff --git a/dbms/include/DB/Columns/ColumnArray.h b/dbms/include/DB/Columns/ColumnArray.h index 3ec0d03d71b..f482c454d3f 100644 --- a/dbms/include/DB/Columns/ColumnArray.h +++ b/dbms/include/DB/Columns/ColumnArray.h @@ -33,6 +33,8 @@ public: data->clear(); } + std::string getName() const { return "ColumnArray(" + data->getName() + ")"; } + ColumnPtr cloneEmpty() const { return new ColumnArray(data->cloneEmpty()); diff --git a/dbms/include/DB/Columns/ColumnConst.h b/dbms/include/DB/Columns/ColumnConst.h index a4589ad64b4..76c85043111 100644 --- a/dbms/include/DB/Columns/ColumnConst.h +++ b/dbms/include/DB/Columns/ColumnConst.h @@ -23,6 +23,7 @@ public: ColumnConst(size_t s_, const T & data_) : s(s_), data(data_) {} + std::string getName() const { return "ColumnConst<" + TypeName::get() + ">"; } bool isNumeric() const { return IsNumber::value; } ColumnPtr cloneEmpty() const { return new ColumnConst(0, data); } size_t size() const { return s; } diff --git a/dbms/include/DB/Columns/ColumnFixedArray.h b/dbms/include/DB/Columns/ColumnFixedArray.h index 3a50eec702a..d649792e289 100644 --- a/dbms/include/DB/Columns/ColumnFixedArray.h +++ b/dbms/include/DB/Columns/ColumnFixedArray.h @@ -26,6 +26,8 @@ public: clear(); } + std::string getName() const { return "ColumnFixedArray(" + data->getName() + ")"; } + ColumnPtr cloneEmpty() const { return new ColumnFixedArray(data->cloneEmpty(), n); diff --git a/dbms/include/DB/Columns/ColumnFixedString.h b/dbms/include/DB/Columns/ColumnFixedString.h index d43554cbb28..c23c68ec25b 100644 --- a/dbms/include/DB/Columns/ColumnFixedString.h +++ b/dbms/include/DB/Columns/ColumnFixedString.h @@ -26,6 +26,8 @@ public: { } + std::string getName() const { return "ColumnFixedString"; } + ColumnPtr cloneEmpty() const { return new ColumnFixedString(n); diff --git a/dbms/include/DB/Columns/ColumnString.h b/dbms/include/DB/Columns/ColumnString.h index 36f92a7cfa0..fe4f68cd888 100644 --- a/dbms/include/DB/Columns/ColumnString.h +++ b/dbms/include/DB/Columns/ColumnString.h @@ -26,6 +26,8 @@ public: { } + std::string getName() const { return "ColumnString"; } + ColumnPtr cloneEmpty() const { return new ColumnString; diff --git a/dbms/include/DB/Columns/ColumnVector.h b/dbms/include/DB/Columns/ColumnVector.h index eed7bda055b..e140a603a4b 100644 --- a/dbms/include/DB/Columns/ColumnVector.h +++ b/dbms/include/DB/Columns/ColumnVector.h @@ -24,6 +24,8 @@ public: ColumnVector() {} ColumnVector(size_t n) : data(n) {} + std::string getName() const { return "ColumnVector<" + TypeName::get() + ">"; } + bool isNumeric() const { return IsNumber::value; } ColumnPtr cloneEmpty() const diff --git a/dbms/include/DB/Columns/IColumn.h b/dbms/include/DB/Columns/IColumn.h index 55d55e7a055..ec7e6de647e 100644 --- a/dbms/include/DB/Columns/IColumn.h +++ b/dbms/include/DB/Columns/IColumn.h @@ -20,6 +20,10 @@ typedef std::vector Columns; class IColumn { public: + /** Имя столбца. Для информационных сообщений. + */ + virtual std::string getName() const = 0; + /** Столбец представляет собой вектор чисел или числовую константу. */ virtual bool isNumeric() const { return false; } diff --git a/dbms/include/DB/Core/Field.h b/dbms/include/DB/Core/Field.h index e08fe356b64..678d1dbcdd6 100644 --- a/dbms/include/DB/Core/Field.h +++ b/dbms/include/DB/Core/Field.h @@ -1,5 +1,4 @@ -#ifndef DBMS_CORE_FIELD_H -#define DBMS_CORE_FIELD_H +#pragma once #include #include @@ -151,20 +150,4 @@ template <> struct NearestFieldType { typedef Float64 Type; }; template <> struct NearestFieldType { typedef Float64 Type; }; template <> struct NearestFieldType { typedef String Type; }; - -template struct IsNumber { static const bool value = false; }; - -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; -template <> struct IsNumber { static const bool value = true; }; - } - -#endif diff --git a/dbms/include/DB/Core/Types.h b/dbms/include/DB/Core/Types.h index 457c052f018..4afcb5f092e 100644 --- a/dbms/include/DB/Core/Types.h +++ b/dbms/include/DB/Core/Types.h @@ -1,5 +1,4 @@ -#ifndef DBMS_CORE_TYPES_H -#define DBMS_CORE_TYPES_H +#pragma once #include #include @@ -30,6 +29,34 @@ typedef double Float64; typedef std::string String; -} -#endif +template struct IsNumber { static const bool value = false; }; + +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; +template <> struct IsNumber { static const bool value = true; }; + + +template struct TypeName; + +template <> struct TypeName { static std::string get() { return "Null"; } }; +template <> struct TypeName { static std::string get() { return "UInt8"; } }; +template <> struct TypeName { static std::string get() { return "UInt16"; } }; +template <> struct TypeName { static std::string get() { return "UInt32"; } }; +template <> struct TypeName { static std::string get() { return "UInt64"; } }; +template <> struct TypeName { static std::string get() { return "Int8"; } }; +template <> struct TypeName { static std::string get() { return "Int16"; } }; +template <> struct TypeName { static std::string get() { return "Int32"; } }; +template <> struct TypeName { static std::string get() { return "Int64"; } }; +template <> struct TypeName { static std::string get() { return "Float32"; } }; +template <> struct TypeName { static std::string get() { return "Float64"; } }; +template <> struct TypeName { static std::string get() { return "String"; } }; + +} diff --git a/dbms/include/DB/Functions/FunctionsArithmetic.h b/dbms/include/DB/Functions/FunctionsArithmetic.h index cdfec2fff2c..5793c96db0a 100644 --- a/dbms/include/DB/Functions/FunctionsArithmetic.h +++ b/dbms/include/DB/Functions/FunctionsArithmetic.h @@ -336,7 +336,8 @@ private: || executeRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (ColumnConst * col_left = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) @@ -353,7 +354,8 @@ private: || executeConstRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } @@ -412,7 +414,8 @@ public: || executeLeftType(block, arguments, result) || executeLeftType(block, arguments, result) || executeLeftType(block, arguments, result))) - throw Exception("Illegal column of first argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; diff --git a/dbms/include/DB/Functions/FunctionsComparison.h b/dbms/include/DB/Functions/FunctionsComparison.h index 89e2bccb501..8c09358cee8 100644 --- a/dbms/include/DB/Functions/FunctionsComparison.h +++ b/dbms/include/DB/Functions/FunctionsComparison.h @@ -1203,7 +1203,8 @@ private: || executeNumRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (ColumnConst * col_left = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) @@ -1220,7 +1221,8 @@ private: || executeNumConstRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } @@ -1293,7 +1295,10 @@ private: dynamic_cast(c1_fixed_string->getData()).getData(), c1_fixed_string->getN(), c_res->getData()); else - throw Exception("Illegal columns of arguments of function " + getName(), + throw Exception("Illegal columns " + + block.getByPosition(arguments[0]).column->getName() + " and " + + block.getByPosition(arguments[1]).column->getName() + + " of arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } } @@ -1345,7 +1350,8 @@ public: || executeNumLeftType(block, arguments, result) || executeNumLeftType(block, arguments, result) || executeNumLeftType(block, arguments, result))) - throw Exception("Illegal column of first argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else diff --git a/dbms/include/DB/Functions/FunctionsLogical.h b/dbms/include/DB/Functions/FunctionsLogical.h index a87f90c654c..e3a25c26576 100644 --- a/dbms/include/DB/Functions/FunctionsLogical.h +++ b/dbms/include/DB/Functions/FunctionsLogical.h @@ -200,7 +200,8 @@ private: || executeRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (ColumnConst * col_left = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) @@ -217,7 +218,8 @@ private: || executeConstRightType(block, arguments, result, col_left)) return true; else - throw Exception("Illegal column of second argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } @@ -267,7 +269,8 @@ public: || executeLeftType(block, arguments, result) || executeLeftType(block, arguments, result) || executeLeftType(block, arguments, result))) - throw Exception("Illegal column of first argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; @@ -349,7 +352,8 @@ public: || executeType(block, arguments, result) || executeType(block, arguments, result) || executeType(block, arguments, result))) - throw Exception("Illegal column of argument of function " + getName(), + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; diff --git a/dbms/include/DB/Parsers/ASTSelectQuery.h b/dbms/include/DB/Parsers/ASTSelectQuery.h index 52dfe1decae..44563a0f835 100644 --- a/dbms/include/DB/Parsers/ASTSelectQuery.h +++ b/dbms/include/DB/Parsers/ASTSelectQuery.h @@ -1,5 +1,4 @@ -#ifndef DBMS_PARSERS_ASTSELECTQUERY_H -#define DBMS_PARSERS_ASTSELECTQUERY_H +#pragma once #include @@ -13,8 +12,16 @@ namespace DB class ASTSelectQuery : public IAST { public: - ASTPtr select, from, where, group, having, order, limit; - + ASTPtr select_expression_list; + ASTPtr database; + ASTPtr table; + ASTPtr where_expression; + ASTPtr group_expression_list; + ASTPtr having_expression; + ASTPtr order_expression_list; + ASTPtr limit_offset; + ASTPtr limit_length; + ASTSelectQuery() {} ASTSelectQuery(StringRange range_) : IAST(range_) {} @@ -23,5 +30,3 @@ public: }; } - -#endif diff --git a/dbms/include/DB/Parsers/ParserSelectQuery.h b/dbms/include/DB/Parsers/ParserSelectQuery.h index bd226068b50..38082d4701a 100644 --- a/dbms/include/DB/Parsers/ParserSelectQuery.h +++ b/dbms/include/DB/Parsers/ParserSelectQuery.h @@ -1,9 +1,6 @@ #pragma once -#include #include -#include -#include namespace DB @@ -14,34 +11,7 @@ class ParserSelectQuery : public IParserBase { protected: String getName() { return "SELECT query"; } - - bool parseImpl(Pos & pos, Pos end, ASTPtr & node, String & expected) - { - Pos begin = pos; - - ASTPtr select_expression_list; - - ParserWhiteSpaceOrComments ws; - ParserString s("SELECT", true, true); - ParserNotEmptyExpressionList exp_list; - - ws.ignore(pos, end); - - if (!s.ignore(pos, end, expected)) - return false; - - ws.ignore(pos, end); - - if (!exp_list.parse(pos, end, select_expression_list, expected)) - return false; - - ASTSelectQuery * select_query = new ASTSelectQuery(StringRange(begin, pos)); - node = select_query; - select_query->select = select_expression_list; - select_query->children.push_back(select_query->select); - - return true; - } + bool parseImpl(Pos & pos, Pos end, ASTPtr & node, String & expected); }; } diff --git a/dbms/src/DataStreams/FilterBlockInputStream.cpp b/dbms/src/DataStreams/FilterBlockInputStream.cpp index f2de12ea784..059e70b0508 100644 --- a/dbms/src/DataStreams/FilterBlockInputStream.cpp +++ b/dbms/src/DataStreams/FilterBlockInputStream.cpp @@ -18,8 +18,9 @@ Block FilterBlockInputStream::read() return res; size_t columns = res.columns(); + ColumnPtr column = res.getByPosition(filter_column).column; - ColumnConstUInt8 * column_const = dynamic_cast(&*res.getByPosition(filter_column).column); + ColumnConstUInt8 * column_const = dynamic_cast(&*column); if (column_const) { return column_const->getData() @@ -27,11 +28,11 @@ Block FilterBlockInputStream::read() : Block(); } - ColumnUInt8 * column = dynamic_cast(&*res.getByPosition(filter_column).column); - if (!column) - throw Exception("Illegal type of column for filter. Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); + ColumnUInt8 * column_vec = dynamic_cast(&*column); + if (!column_vec) + throw Exception("Illegal type " + column->getName() + " of column for filter. Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); - IColumn::Filter & filter = column->getData(); + IColumn::Filter & filter = column_vec->getData(); for (size_t i = 0; i < columns; ++i) if (i != filter_column) diff --git a/dbms/src/Parsers/ParserSelectQuery.cpp b/dbms/src/Parsers/ParserSelectQuery.cpp new file mode 100644 index 00000000000..3a5deeaec6f --- /dev/null +++ b/dbms/src/Parsers/ParserSelectQuery.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + + +bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, String & expected) +{ + Pos begin = pos; + + ASTSelectQuery * select_query = new ASTSelectQuery(StringRange(begin, pos)); + node = select_query; + + ParserWhiteSpaceOrComments ws; + ParserString s_select("SELECT", true, true); + ParserString s_from("FROM", true, true); + ParserString s_where("WHERE", true, true); + ParserString s_group("GROUP", true, true); + ParserString s_by("BY", true, true); + ParserString s_having("HAVING", true, true); + ParserString s_order("ORDER", true, true); + ParserString s_limit("LIMIT", true, true); + ParserNotEmptyExpressionList exp_list; + ParserLogicalOrExpression exp_elem; + + ws.ignore(pos, end); + + /// SELECT expr list + { + if (!s_select.ignore(pos, end, expected)) + return false; + + ws.ignore(pos, end); + + if (!exp_list.parse(pos, end, select_query->select_expression_list, expected)) + return false; + + ws.ignore(pos, end); + } + + /// FROM database.table или FROM table TODO subquery + if (s_from.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + + ParserString s_dot("."); + ParserIdentifier ident; + + if (!ident.parse(pos, end, select_query->table, expected)) + return false; + + ws.ignore(pos, end); + + if (s_dot.ignore(pos, end, expected)) + { + select_query->database = select_query->table; + if (!ident.parse(pos, end, select_query->table, expected)) + return false; + + ws.ignore(pos, end); + } + } + + /// WHERE expr + if (s_where.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + + if (!exp_elem.parse(pos, end, select_query->where_expression, expected)) + return false; + + ws.ignore(pos, end); + } + + /// GROUP BY expr list + if (s_group.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + if (!s_by.ignore(pos, end, expected)) + return false; + + if (!exp_list.parse(pos, end, select_query->group_expression_list, expected)) + return false; + + ws.ignore(pos, end); + } + + /// HAVING expr + if (s_having.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + + if (!exp_elem.parse(pos, end, select_query->having_expression, expected)) + return false; + + ws.ignore(pos, end); + } + + /// ORDER BY expr list TODO ASC, DESC + if (s_order.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + if (!s_by.ignore(pos, end, expected)) + return false; + + if (!exp_list.parse(pos, end, select_query->order_expression_list, expected)) + return false; + + ws.ignore(pos, end); + } + + /// LIMIT length или LIMIT offset, length + if (s_limit.ignore(pos, end, expected)) + { + ws.ignore(pos, end); + + ParserString s_comma(","); + ParserNumber num; + + if (!num.parse(pos, end, select_query->limit_length, expected)) + return false; + + ws.ignore(pos, end); + + if (s_comma.ignore(pos, end, expected)) + { + select_query->limit_offset = select_query->limit_length; + if (!num.parse(pos, end, select_query->limit_length, expected)) + return false; + + ws.ignore(pos, end); + } + } + + select_query->children.push_back(select_query->select_expression_list); + if (select_query->database) + select_query->children.push_back(select_query->database); + if (select_query->table) + select_query->children.push_back(select_query->table); + if (select_query->where_expression) + select_query->children.push_back(select_query->where_expression); + if (select_query->group_expression_list) + select_query->children.push_back(select_query->group_expression_list); + if (select_query->having_expression) + select_query->children.push_back(select_query->having_expression); + if (select_query->order_expression_list) + select_query->children.push_back(select_query->order_expression_list); + if (select_query->limit_offset) + select_query->children.push_back(select_query->limit_offset); + if (select_query->limit_length) + select_query->children.push_back(select_query->limit_length); + + return true; +} + +} diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 61775b22daa..d31e0ef12de 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -73,7 +73,53 @@ void formatAST(const IAST & ast, std::ostream & s) void formatAST(const ASTSelectQuery & ast, std::ostream & s) { s << "SELECT "; - formatAST(*ast.select, s); + formatAST(*ast.select_expression_list, s); + + if (ast.table) + { + s << " FROM "; + if (ast.database) + { + formatAST(*ast.database, s); + s << "."; + } + formatAST(*ast.table, s); + } + + if (ast.where_expression) + { + s << " WHERE "; + formatAST(*ast.where_expression, s); + } + + if (ast.group_expression_list) + { + s << " GROUP BY "; + formatAST(*ast.group_expression_list, s); + } + + if (ast.having_expression) + { + s << " HAVING "; + formatAST(*ast.having_expression, s); + } + + if (ast.order_expression_list) + { + s << " ORDER BY "; + formatAST(*ast.order_expression_list, s); + } + + if (ast.limit_length) + { + s << " LIMIT "; + if (ast.limit_offset) + { + formatAST(*ast.limit_offset, s); + s << ", "; + } + formatAST(*ast.limit_length, s); + } } void formatAST(const ASTCreateQuery & ast, std::ostream & s) diff --git a/dbms/src/Parsers/tests/select_parser.cpp b/dbms/src/Parsers/tests/select_parser.cpp index e0d4504214b..86ec8f11556 100644 --- a/dbms/src/Parsers/tests/select_parser.cpp +++ b/dbms/src/Parsers/tests/select_parser.cpp @@ -11,7 +11,14 @@ int main(int argc, char ** argv) { DB::ParserSelectQuery parser; DB::ASTPtr ast; - std::string input = "SELECT 18446744073709551615, f(1), '\\\\', [a, b, c], (a, b, c), 1 + 2 * -3, a = b OR c > d.1 + 2 * -g[0] AND NOT e < f * (x + y)"; + std::string input = + " SELECT 18446744073709551615, f(1), '\\\\', [a, b, c], (a, b, c), 1 + 2 * -3, a = b OR c > d.1 + 2 * -g[0] AND NOT e < f * (x + y)" + " FROM default.hits" + " WHERE CounterID = 101500 AND UniqID % 3 = 0" + " GROUP BY UniqID" + " HAVING SUM(Refresh) > 100" + " ORDER BY Visits, PageViews" + " LIMIT 1000, 10"; std::string expected; const char * begin = input.data();