#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int NUMBER_OF_COLUMNS_DOESNT_MATCH; } /// Allows processing results of a query to ODBC source as a sequence of Blocks, simplifies chaining class ODBCBlockInputStream final : public IProfilingBlockInputStream { public: ODBCBlockInputStream( Poco::Data::Session && session, const std::string & query_str, const Block & sample_block, const std::size_t max_block_size) : session{session}, statement{(this->session << query_str, Poco::Data::Keywords::now)}, result{statement}, iterator{result.begin()}, max_block_size{max_block_size} { if (sample_block.columns() != result.columnCount()) throw Exception{ "RecordSet contains " + toString(result.columnCount()) + " columns while " + toString(sample_block.columns()) + " expected", ErrorCodes::NUMBER_OF_COLUMNS_DOESNT_MATCH}; description.init(sample_block); } String getName() const override { return "ODBC"; } String getID() const override { return "ODBC(" + statement.toString() + ")"; } private: using ValueType = ExternalResultDescription::ValueType; Block readImpl() override { if (iterator == result.end()) return {}; auto block = description.sample_block.cloneEmpty(); /// cache pointers returned by the calls to getByPosition std::vector columns(block.columns()); for (const auto i : ext::range(0, columns.size())) columns[i] = block.getByPosition(i).column.get(); std::size_t num_rows = 0; while (iterator != result.end()) { Poco::Data::Row & row = *iterator; for (const auto idx : ext::range(0, row.fieldCount())) { const Poco::Dynamic::Var & value = row[idx]; if (!value.isEmpty()) insertValue(columns[idx], description.types[idx], value); else insertDefaultValue(columns[idx], *description.sample_columns[idx]); } ++num_rows; if (num_rows == max_block_size) break; ++iterator; } return block; } static void insertValue(IColumn * const column, const ValueType type, const Poco::Dynamic::Var & value) { switch (type) { case ValueType::UInt8: static_cast(column)->insert(value.convert()); break; case ValueType::UInt16: static_cast(column)->insert(value.convert()); break; case ValueType::UInt32: static_cast(column)->insert(value.convert()); break; case ValueType::UInt64: static_cast(column)->insert(value.convert()); break; case ValueType::Int8: static_cast(column)->insert(value.convert()); break; case ValueType::Int16: static_cast(column)->insert(value.convert()); break; case ValueType::Int32: static_cast(column)->insert(value.convert()); break; case ValueType::Int64: static_cast(column)->insert(value.convert()); break; case ValueType::Float32: static_cast(column)->insert(value.convert()); break; case ValueType::Float64: static_cast(column)->insert(value.convert()); break; case ValueType::String: static_cast(column)->insert(value.convert()); break; case ValueType::Date: static_cast(column)->insert(UInt16{LocalDate{value.convert()}.getDayNum()}); break; case ValueType::DateTime: static_cast(column)->insert(time_t{LocalDateTime{value.convert()}}); break; } } static void insertDefaultValue(IColumn * const column, const IColumn & sample_column) { column->insertFrom(sample_column, 0); } Poco::Data::Session session; Poco::Data::Statement statement; Poco::Data::RecordSet result; Poco::Data::RecordSet::Iterator iterator; const std::size_t max_block_size; ExternalResultDescription description; }; }