void registerStorageMerge(StorageFactory & factory) { factory.registerStorage("Merge", [](const StorageFactory::Arguments & args) { /** In query, the name of database is specified as table engine argument which contains source tables, * as well as regex for source-table names. */ ASTs & engine_args = args.engine_args; if (engine_args.size() != 2) throw Exception("Storage Merge requires exactly 2 parameters" " - name of source database and regexp for table names.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); engine_args[0] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[0], args.local_context); engine_args[1] = evaluateConstantExpressionAsLiteral(engine_args[1], args.local_context); String source_database = static_cast<const ASTLiteral &>(*engine_args[0]).value.safeGet<String>(); String table_name_regexp = static_cast<const ASTLiteral &>(*engine_args[1]).value.safeGet<String>(); return StorageMerge::create( args.table_name, args.columns, source_database, table_name_regexp, args.context); }); }
StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Context & context) const { const ASTFunction & args_func = typeid_cast<const ASTFunction &>(*ast_function); if (!args_func.arguments) throw Exception("Table function 'odbc' must have arguments.", ErrorCodes::LOGICAL_ERROR); ASTs & args = typeid_cast<ASTExpressionList &>(*args_func.arguments).children; if (args.size() != 2) throw Exception("Table function 'odbc' requires exactly 2 arguments: ODBC connection string and table name.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (int i = 0; i < 2; ++i) args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); std::string connection_string = static_cast<const ASTLiteral &>(*args[0]).value.safeGet<String>(); std::string table_name = static_cast<const ASTLiteral &>(*args[1]).value.safeGet<String>(); Poco::Data::ODBC::SessionImpl session(connection_string, DBMS_DEFAULT_CONNECT_TIMEOUT_SEC); SQLHDBC hdbc = session.dbc().handle(); SQLHSTMT hstmt = nullptr; if (Poco::Data::ODBC::Utility::isError(SQLAllocStmt(hdbc, &hstmt))) throw Poco::Data::ODBC::ODBCException("Could not allocate connection handle."); SCOPE_EXIT(SQLFreeStmt(hstmt, SQL_DROP)); /// TODO Why not do SQLColumns instead? std::string query = "SELECT * FROM " + table_name + " WHERE 1 = 0"; if (Poco::Data::ODBC::Utility::isError(Poco::Data::ODBC::SQLPrepare(hstmt, reinterpret_cast<SQLCHAR *>(&query[0]), query.size()))) throw Poco::Data::ODBC::DescriptorException(session.dbc()); if (Poco::Data::ODBC::Utility::isError(SQLExecute(hstmt))) throw Poco::Data::ODBC::StatementException(hstmt); SQLSMALLINT cols = 0; if (Poco::Data::ODBC::Utility::isError(SQLNumResultCols(hstmt, &cols))) throw Poco::Data::ODBC::StatementException(hstmt); /// TODO cols not checked NamesAndTypesList columns; for (SQLSMALLINT ncol = 1; ncol <= cols; ++ncol) { SQLSMALLINT type = 0; /// TODO Why 301? SQLCHAR column_name[301]; /// TODO Result is not checked. Poco::Data::ODBC::SQLDescribeCol(hstmt, ncol, column_name, sizeof(column_name), NULL, &type, NULL, NULL, NULL); columns.emplace_back(reinterpret_cast<char *>(column_name), getDataType(type)); } auto result = StorageODBC::create(table_name, connection_string, "", table_name, ColumnsDescription{columns}); result->startup(); return result; }
void registerStorageHDFS(StorageFactory & factory) { factory.registerStorage("HDFS", [](const StorageFactory::Arguments & args) { ASTs & engine_args = args.engine_args; if (!(engine_args.size() == 1 || engine_args.size() == 2)) throw Exception( "Storage HDFS requires exactly 2 arguments: url and name of used format.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); engine_args[0] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[0], args.local_context); String url = static_cast<const ASTLiteral &>(*engine_args[0]).value.safeGet<String>(); engine_args[1] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[1], args.local_context); String format_name = static_cast<const ASTLiteral &>(*engine_args[1]).value.safeGet<String>(); return StorageHDFS::create(url, args.table_name, format_name, args.columns, args.context); }); }
void registerStorageFile(StorageFactory & factory) { factory.registerStorage("File", [](const StorageFactory::Arguments & args) { ASTs & engine_args = args.engine_args; if (!(engine_args.size() == 1 || engine_args.size() == 2)) throw Exception( "Storage File requires 1 or 2 arguments: name of used format and source.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); engine_args[0] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[0], args.local_context); String format_name = static_cast<const ASTLiteral &>(*engine_args[0]).value.safeGet<String>(); int source_fd = -1; String source_path; if (engine_args.size() >= 2) { /// Will use FD if engine_args[1] is int literal or identifier with std* name if (const ASTIdentifier * identifier = typeid_cast<const ASTIdentifier *>(engine_args[1].get())) { if (identifier->name == "stdin") source_fd = STDIN_FILENO; else if (identifier->name == "stdout") source_fd = STDOUT_FILENO; else if (identifier->name == "stderr") source_fd = STDERR_FILENO; else throw Exception("Unknown identifier '" + identifier->name + "' in second arg of File storage constructor", ErrorCodes::UNKNOWN_IDENTIFIER); } else if (const ASTLiteral * literal = typeid_cast<const ASTLiteral *>(engine_args[1].get())) { auto type = literal->value.getType(); if (type == Field::Types::Int64) source_fd = static_cast<int>(literal->value.get<Int64>()); else if (type == Field::Types::UInt64) source_fd = static_cast<int>(literal->value.get<UInt64>()); else if (type == Field::Types::String) source_path = literal->value.get<String>(); } } return StorageFile::create( source_path, source_fd, args.data_path, args.table_name, format_name, args.columns, args.context); }); }
StoragePtr TableFunctionFile::executeImpl(const ASTPtr & ast_function, const Context & context) const { // Parse args ASTs & args_func = typeid_cast<ASTFunction &>(*ast_function).children; if (args_func.size() != 1) throw Exception("Table function '" + getName() + "' must have arguments.", ErrorCodes::LOGICAL_ERROR); ASTs & args = typeid_cast<ASTExpressionList &>(*args_func.at(0)).children; if (args.size() != 3) throw Exception("Table function '" + getName() + "' requires exactly 3 arguments: path, format and structure.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (size_t i = 0; i < 3; ++i) args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); std::string path = static_cast<const ASTLiteral &>(*args[0]).value.safeGet<String>(); std::string format = static_cast<const ASTLiteral &>(*args[1]).value.safeGet<String>(); std::string structure = static_cast<const ASTLiteral &>(*args[2]).value.safeGet<String>(); // Create sample block std::vector<std::string> structure_vals; boost::split(structure_vals, structure, boost::algorithm::is_any_of(" ,"), boost::algorithm::token_compress_on); if (structure_vals.size() % 2 != 0) throw Exception("Odd number of elements in section structure: must be a list of name type pairs", ErrorCodes::LOGICAL_ERROR); Block sample_block; const DataTypeFactory & data_type_factory = DataTypeFactory::instance(); for (size_t i = 0, size = structure_vals.size(); i < size; i += 2) { ColumnWithTypeAndName column; column.name = structure_vals[i]; column.type = data_type_factory.get(structure_vals[i + 1]); column.column = column.type->createColumn(); sample_block.insert(std::move(column)); } // Create table StoragePtr storage = StorageFile::create( path, -1, context.getUserFilesPath(), getName(), format, ColumnsDescription{sample_block.getNamesAndTypesList()}, const_cast<Context &>(context)); storage->startup(); return storage; }
void registerStorageODBC(StorageFactory & factory) { factory.registerStorage("ODBC", [](const StorageFactory::Arguments & args) { ASTs & engine_args = args.engine_args; if (engine_args.size() != 3) throw Exception( "Storage ODBC requires exactly 3 parameters: ODBC('DSN', database, table).", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (size_t i = 0; i < 2; ++i) engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); return StorageODBC::create(args.table_name, static_cast<const ASTLiteral &>(*engine_args[0]).value.safeGet<String>(), static_cast<const ASTLiteral &>(*engine_args[1]).value.safeGet<String>(), static_cast<const ASTLiteral &>(*engine_args[2]).value.safeGet<String>(), args.columns); }); }
UInt64 TableFunctionNumbers::evaluateArgument(const Context & context, ASTPtr & argument) const { return static_cast<const ASTLiteral &>(*evaluateConstantExpressionOrIdentifierAsLiteral(argument, context)).value.safeGet<UInt64>(); }
StoragePtr TableFunctionRemote::executeImpl(const ASTPtr & ast_function, const Context & context) const { ASTs & args_func = typeid_cast<ASTFunction &>(*ast_function).children; if (args_func.size() != 1) throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); ASTs & args = typeid_cast<ASTExpressionList &>(*args_func.at(0)).children; const size_t max_args = is_cluster_function ? 3 : 5; if (args.size() < 2 || args.size() > max_args) throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); String cluster_name; String cluster_description; String remote_database; String remote_table; String username; String password; size_t arg_num = 0; auto getStringLiteral = [](const IAST & node, const char * description) { const ASTLiteral * lit = typeid_cast<const ASTLiteral *>(&node); if (!lit) throw Exception(description + String(" must be string literal (in single quotes)."), ErrorCodes::BAD_ARGUMENTS); if (lit->value.getType() != Field::Types::String) throw Exception(description + String(" must be string literal (in single quotes)."), ErrorCodes::BAD_ARGUMENTS); return safeGet<const String &>(lit->value); }; if (is_cluster_function) { ASTPtr ast_name = evaluateConstantExpressionOrIdentifierAsLiteral(args[arg_num], context); cluster_name = static_cast<const ASTLiteral &>(*ast_name).value.safeGet<const String &>(); } else { if (auto ast_cluster = typeid_cast<const ASTIdentifier *>(args[arg_num].get())) cluster_name = ast_cluster->name; else cluster_description = getStringLiteral(*args[arg_num], "Hosts pattern"); } ++arg_num; args[arg_num] = evaluateConstantExpressionOrIdentifierAsLiteral(args[arg_num], context); remote_database = static_cast<const ASTLiteral &>(*args[arg_num]).value.safeGet<String>(); ++arg_num; size_t dot = remote_database.find('.'); if (dot != String::npos) { /// NOTE Bad - do not support identifiers in backquotes. remote_table = remote_database.substr(dot + 1); remote_database = remote_database.substr(0, dot); } else { if (arg_num >= args.size()) throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); args[arg_num] = evaluateConstantExpressionOrIdentifierAsLiteral(args[arg_num], context); remote_table = static_cast<const ASTLiteral &>(*args[arg_num]).value.safeGet<String>(); ++arg_num; } /// Username and password parameters are prohibited in cluster version of the function if (!is_cluster_function) { if (arg_num < args.size()) { username = getStringLiteral(*args[arg_num], "Username"); ++arg_num; } else username = "******"; if (arg_num < args.size()) { password = getStringLiteral(*args[arg_num], "Password"); ++arg_num; } } if (arg_num < args.size()) throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); /// ExpressionAnalyzer will be created in InterpreterSelectQuery that will meet these `Identifier` when processing the request. /// We need to mark them as the name of the database or table, because the default value is column. for (auto & arg : args) if (ASTIdentifier * id = typeid_cast<ASTIdentifier *>(arg.get())) id->kind = ASTIdentifier::Table; ClusterPtr cluster; if (!cluster_name.empty()) { /// Use an existing cluster from the main config cluster = context.getCluster(cluster_name); } else { /// Create new cluster from the scratch size_t max_addresses = context.getSettingsRef().table_function_remote_max_addresses; std::vector<String> shards = parseDescription(cluster_description, 0, cluster_description.size(), ',', max_addresses); std::vector<std::vector<String>> names; for (size_t i = 0; i < shards.size(); ++i) names.push_back(parseDescription(shards[i], 0, shards[i].size(), '|', max_addresses)); if (names.empty()) throw Exception("Shard list is empty after parsing first argument", ErrorCodes::BAD_ARGUMENTS); cluster = std::make_shared<Cluster>(context.getSettings(), names, username, password, context.getTCPPort(), false); } auto res = StorageDistributed::createWithOwnCluster( getName(), getStructureOfRemoteTable(*cluster, remote_database, remote_table, context), remote_database, remote_table, cluster, context); res->startup(); return res; }
StoragePtr TableFunctionMySQL::executeImpl(const ASTPtr & ast_function, const Context & context) const { const ASTFunction & args_func = typeid_cast<const ASTFunction &>(*ast_function); if (!args_func.arguments) throw Exception("Table function 'mysql' must have arguments.", ErrorCodes::LOGICAL_ERROR); ASTs & args = typeid_cast<ASTExpressionList &>(*args_func.arguments).children; if (args.size() < 5 || args.size() > 7) throw Exception("Table function 'mysql' requires 5-7 parameters: MySQL('host:port', database, table, 'user', 'password'[, replace_query, 'on_duplicate_clause']).", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (size_t i = 0; i < args.size(); ++i) args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); std::string host_port = static_cast<const ASTLiteral &>(*args[0]).value.safeGet<String>(); std::string database_name = static_cast<const ASTLiteral &>(*args[1]).value.safeGet<String>(); std::string table_name = static_cast<const ASTLiteral &>(*args[2]).value.safeGet<String>(); std::string user_name = static_cast<const ASTLiteral &>(*args[3]).value.safeGet<String>(); std::string password = static_cast<const ASTLiteral &>(*args[4]).value.safeGet<String>(); bool replace_query = false; std::string on_duplicate_clause; if (args.size() >= 6) replace_query = static_cast<const ASTLiteral &>(*args[5]).value.safeGet<UInt64>() > 0; if (args.size() == 7) on_duplicate_clause = static_cast<const ASTLiteral &>(*args[6]).value.safeGet<String>(); if (replace_query && !on_duplicate_clause.empty()) throw Exception( "Only one of 'replace_query' and 'on_duplicate_clause' can be specified, or none of them", ErrorCodes::BAD_ARGUMENTS); /// 3306 is the default MySQL port number auto parsed_host_port = parseAddress(host_port, 3306); mysqlxx::Pool pool(database_name, parsed_host_port.first, user_name, password, parsed_host_port.second); /// Determine table definition by running a query to INFORMATION_SCHEMA. Block sample_block { { std::make_shared<DataTypeString>(), "name" }, { std::make_shared<DataTypeString>(), "type" }, { std::make_shared<DataTypeUInt8>(), "is_nullable" }, { std::make_shared<DataTypeUInt8>(), "is_unsigned" }, { std::make_shared<DataTypeUInt64>(), "length" }, }; WriteBufferFromOwnString query; query << "SELECT" " COLUMN_NAME AS name," " DATA_TYPE AS type," " IS_NULLABLE = 'YES' AS is_nullable," " COLUMN_TYPE LIKE '%unsigned' AS is_unsigned," " CHARACTER_MAXIMUM_LENGTH AS length" " FROM INFORMATION_SCHEMA.COLUMNS" " WHERE TABLE_SCHEMA = " << quote << database_name << " AND TABLE_NAME = " << quote << table_name << " ORDER BY ORDINAL_POSITION"; MySQLBlockInputStream result(pool.Get(), query.str(), sample_block, DEFAULT_BLOCK_SIZE); NamesAndTypesList columns; while (Block block = result.read()) { size_t rows = block.rows(); for (size_t i = 0; i < rows; ++i) columns.emplace_back( (*block.getByPosition(0).column)[i].safeGet<String>(), getDataType( (*block.getByPosition(1).column)[i].safeGet<String>(), (*block.getByPosition(2).column)[i].safeGet<UInt64>() && context.getSettings().external_table_functions_use_nulls, (*block.getByPosition(3).column)[i].safeGet<UInt64>(), (*block.getByPosition(4).column)[i].safeGet<UInt64>())); } auto res = StorageMySQL::create( table_name, std::move(pool), database_name, table_name, replace_query, on_duplicate_clause, ColumnsDescription{columns}, context); res->startup(); return res; }