be/src/service/arrow_flight/flight_sql_service.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "service/arrow_flight/flight_sql_service.h" |
19 | | |
20 | | #include <absl/strings/str_split.h> |
21 | | #include <arrow/status.h> |
22 | | |
23 | | #include <memory> |
24 | | |
25 | | #include "arrow/flight/sql/server.h" |
26 | | #include "format/arrow/arrow_utils.h" |
27 | | #include "service/arrow_flight/arrow_flight_batch_reader.h" |
28 | | #include "service/arrow_flight/flight_sql_info.h" |
29 | | #include "service/backend_options.h" |
30 | | #include "util/uid_util.h" |
31 | | #include "util/url_coding.h" |
32 | | |
33 | | namespace doris::flight { |
34 | | |
35 | | class FlightSqlServer::Impl { |
36 | | private: |
37 | | // Create a Ticket that combines a sql and a query ID. |
38 | | arrow::Result<arrow::flight::Ticket> encode_ticket(const std::string& sql, |
39 | 0 | const std::string& query_id) { |
40 | 0 | std::string query = query_id; |
41 | 0 | query += ':'; |
42 | 0 | query += sql; |
43 | 0 | ARROW_ASSIGN_OR_RAISE(auto ticket, arrow::flight::sql::CreateStatementQueryTicket(query)); |
44 | 0 | return arrow::flight::Ticket {std::move(ticket)}; |
45 | 0 | } |
46 | | |
47 | 1 | arrow::Result<std::shared_ptr<QueryStatement>> decode_ticket(const std::string& ticket) { |
48 | 1 | std::vector<std::string> fields = absl::StrSplit(ticket, "&"); |
49 | 1 | if (fields.size() != 4) { |
50 | 0 | return arrow::Status::Invalid( |
51 | 0 | fmt::format("Malformed ticket: {}, size: {}", ticket, fields.size())); |
52 | 0 | } |
53 | | |
54 | 1 | std::vector<std::string> str = absl::StrSplit(fields[0], "-"); |
55 | 1 | if (str.size() != 2) { |
56 | 0 | return arrow::Status::Invalid("Malformed ticket: {}, missing query id: {}", ticket, |
57 | 0 | fields[0]); |
58 | 0 | } |
59 | | |
60 | 1 | TUniqueId queryid; |
61 | 1 | from_hex(&queryid.hi, str[0]); |
62 | 1 | from_hex(&queryid.lo, str[1]); |
63 | 1 | TNetworkAddress result_addr; |
64 | 1 | result_addr.hostname = fields[1]; |
65 | 1 | result_addr.port = std::stoi(fields[2]); |
66 | 1 | const std::string& sql_base64 = fields[3]; |
67 | 1 | std::string sql; |
68 | 1 | base64_decode(sql_base64, &sql); |
69 | 1 | std::shared_ptr<QueryStatement> statement = |
70 | 1 | std::make_shared<QueryStatement>(queryid, result_addr, sql); |
71 | 1 | return statement; |
72 | 1 | } |
73 | | |
74 | | public: |
75 | | explicit Impl() = default; |
76 | | |
77 | | ~Impl() = default; |
78 | | |
79 | | arrow::Result<std::unique_ptr<arrow::flight::FlightDataStream>> DoGetStatement( |
80 | | const arrow::flight::ServerCallContext& context, |
81 | 1 | const arrow::flight::sql::StatementQueryTicket& command) { |
82 | 1 | ARROW_ASSIGN_OR_RAISE(auto statement, decode_ticket(command.statement_handle)); |
83 | | // if IP:BrpcPort in the Ticket is not current BE node, |
84 | | // pulls the query result Block from the BE node specified by IP:BrpcPort, |
85 | | // converts it to Arrow Batch and returns it to ADBC client. |
86 | | // use brpc to transmit blocks between BEs. |
87 | 1 | if (statement->result_addr.hostname == BackendOptions::get_localhost() && |
88 | 1 | statement->result_addr.port == config::brpc_port) { |
89 | 1 | std::shared_ptr<ArrowFlightBatchLocalReader> reader; |
90 | 1 | ARROW_ASSIGN_OR_RAISE( |
91 | 1 | reader, ArrowFlightBatchLocalReader::Create( |
92 | 1 | statement, [&context] { return context.is_cancelled(); })); |
93 | 1 | return std::make_unique<arrow::flight::RecordBatchStream>(reader); |
94 | 1 | } else { |
95 | 0 | std::shared_ptr<ArrowFlightBatchRemoteReader> reader; |
96 | 0 | ARROW_ASSIGN_OR_RAISE( |
97 | 0 | reader, ArrowFlightBatchRemoteReader::Create( |
98 | 0 | statement, [&context] { return context.is_cancelled(); })); |
99 | 0 | return std::make_unique<arrow::flight::RecordBatchStream>(reader); |
100 | 0 | } |
101 | 1 | } |
102 | | }; |
103 | | |
104 | 1 | FlightSqlServer::FlightSqlServer(std::shared_ptr<Impl> impl) : _impl(std::move(impl)) {} |
105 | | |
106 | 1 | arrow::Result<std::shared_ptr<FlightSqlServer>> FlightSqlServer::create() { |
107 | 1 | std::shared_ptr<Impl> impl = std::make_shared<Impl>(); |
108 | | |
109 | 1 | std::shared_ptr<FlightSqlServer> result(new FlightSqlServer(std::move(impl))); |
110 | 20 | for (const auto& id_to_result : GetSqlInfoResultMap()) { |
111 | 20 | result->RegisterSqlInfo(id_to_result.first, id_to_result.second); |
112 | 20 | } |
113 | | |
114 | 1 | return result; |
115 | 1 | } |
116 | | |
117 | 1 | FlightSqlServer::~FlightSqlServer() { |
118 | 1 | static_cast<void>(join()); |
119 | 1 | } |
120 | | |
121 | | arrow::Result<std::unique_ptr<arrow::flight::FlightDataStream>> FlightSqlServer::DoGetStatement( |
122 | | const arrow::flight::ServerCallContext& context, |
123 | 1 | const arrow::flight::sql::StatementQueryTicket& command) { |
124 | 1 | return _impl->DoGetStatement(context, command); |
125 | 1 | } |
126 | | |
127 | 1 | Status FlightSqlServer::init(int port) { |
128 | 1 | if (port == -1) { |
129 | 0 | LOG(INFO) << "Arrow Flight Service not start"; |
130 | 0 | return Status::OK(); |
131 | 0 | } |
132 | 1 | _inited = true; |
133 | 1 | arrow::flight::Location bind_location; |
134 | 1 | RETURN_DORIS_STATUS_IF_ERROR( |
135 | 1 | arrow::flight::Location::ForGrpcTcp(BackendOptions::get_service_bind_address(), port) |
136 | 1 | .Value(&bind_location)); |
137 | 1 | arrow::flight::FlightServerOptions flight_options(bind_location); |
138 | | |
139 | | // Not authenticated in BE flight server. |
140 | | // After the authentication between the ADBC Client and the FE flight server is completed, |
141 | | // the FE flight server will put the query id in the Ticket and send it back to the Client. |
142 | | // When the Client uses the Ticket to fetch data from the BE flight server, the BE flight |
143 | | // server will verify the query id, this step is equivalent to authentication. |
144 | 1 | _header_middleware = std::make_shared<NoOpHeaderAuthServerMiddlewareFactory>(); |
145 | 1 | _bearer_middleware = std::make_shared<NoOpBearerAuthServerMiddlewareFactory>(); |
146 | 1 | flight_options.auth_handler = std::make_unique<arrow::flight::NoOpAuthHandler>(); |
147 | 1 | flight_options.middleware.push_back({"header-auth-server", _header_middleware}); |
148 | 1 | flight_options.middleware.push_back({"bearer-auth-server", _bearer_middleware}); |
149 | | |
150 | 1 | RETURN_DORIS_STATUS_IF_ERROR(Init(flight_options)); |
151 | 1 | LOG(INFO) << "Arrow Flight Service bind to host: " << BackendOptions::get_service_bind_address() |
152 | 1 | << ", port: " << port; |
153 | 1 | return Status::OK(); |
154 | 1 | } |
155 | | |
156 | 2 | Status FlightSqlServer::join() { |
157 | 2 | if (!_inited) { |
158 | | // Flight not inited, not need shutdown |
159 | 0 | return Status::OK(); |
160 | 0 | } |
161 | 2 | RETURN_DORIS_STATUS_IF_ERROR(Shutdown()); |
162 | 2 | return Status::OK(); |
163 | 2 | } |
164 | | |
165 | | } // namespace doris::flight |