Skip to content

Commit 8560e75

Browse files
authored
Support parse_url function (apache#136)
* add parse_url * fix code err * fix code * add suite tests * add missing code * refresh code * add comment
1 parent 146c786 commit 8560e75

10 files changed

Lines changed: 447 additions & 2 deletions

cpp/src/arrow/util/uri.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,12 @@ bool Uri::has_host() const { return IsTextRangeSet(impl_->uri_.hostText); }
141141

142142
std::string Uri::port_text() const { return TextRangeToString(impl_->uri_.portText); }
143143

144+
bool Uri::has_port() const { return IsTextRangeSet(impl_->uri_.portText); }
145+
144146
int32_t Uri::port() const { return impl_->port_; }
145147

148+
std::string Uri::user_info() const { return TextRangeToString(impl_->uri_.userInfo); }
149+
146150
std::string Uri::username() const {
147151
auto userpass = TextRangeToView(impl_->uri_.userInfo);
148152
auto sep_pos = userpass.find_first_of(':');
@@ -194,6 +198,8 @@ std::string Uri::path() const {
194198

195199
std::string Uri::query_string() const { return TextRangeToString(impl_->uri_.query); }
196200

201+
bool Uri::has_query() const { return IsTextRangeSet(impl_->uri_.query); }
202+
197203
Result<std::vector<std::pair<std::string, std::string>>> Uri::query_items() const {
198204
const auto& query = impl_->uri_.query;
199205
UriQueryListA* query_list;

cpp/src/arrow/util/uri.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ class ARROW_EXPORT Uri {
5656
/// The URI port number, as a string such as "80", or the empty string is the URI
5757
/// does not have a port number component.
5858
std::string port_text() const;
59+
/// Whether the URI has an explicit port.
60+
bool has_port() const;
5961
/// The URI port parsed as an integer, or -1 if the URI does not have a port
6062
/// number component.
6163
int32_t port() const;
6264

65+
/// The userInfo specified in the URI.
66+
std::string user_info() const;
6367
/// The username specified in the URI.
6468
std::string username() const;
6569
/// The password specified in the URI.
@@ -70,6 +74,8 @@ class ARROW_EXPORT Uri {
7074

7175
/// The URI query string
7276
std::string query_string() const;
77+
/// Whether the URI has an explicit query.
78+
bool has_query() const;
7379

7480
/// The URI query items
7581
///

cpp/src/gandiva/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ set(SRC_FILES
9292
regex_util.cc
9393
replace_holder.cc
9494
extract_holder.cc
95+
parse_url_holder.cc
9596
selection_vector.cc
9697
tree_expr_builder.cc
9798
to_date_holder.cc
@@ -241,6 +242,7 @@ add_gandiva_test(internals-test
241242
like_holder_test.cc
242243
replace_holder_test.cc
243244
extract_holder_test.cc
245+
parse_url_holder_test.cc
244246
decimal_type_util_test.cc
245247
random_generator_holder_test.cc
246248
substr_index_holder_test.cc

cpp/src/gandiva/function_holder_registry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "gandiva/replace_holder.h"
3333
#include "gandiva/rlike_holder.h"
3434
#include "gandiva/extract_holder.h"
35+
#include "gandiva/parse_url_holder.h"
3536
#include "gandiva/to_date_holder.h"
3637
#include "gandiva/translate_holder.h"
3738
#include "gandiva/substr_index_holder.h"
@@ -76,6 +77,7 @@ class FunctionHolderRegistry {
7677
{"rand", LAMBDA_MAKER(RandomGeneratorHolder)},
7778
{"regexp_replace", LAMBDA_MAKER(ReplaceHolder)},
7879
{"regexp_extract", LAMBDA_MAKER(ExtractHolder)},
80+
{"parse_url", LAMBDA_MAKER(ParseUrlHolder)},
7981
{"translate", LAMBDA_MAKER(TranslateHolder)},
8082
{"substr_index", LAMBDA_MAKER(SubstrIndexHolder)}
8183
};

cpp/src/gandiva/function_registry_string.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,18 @@ std::vector<NativeFunction> GetStringFunctionRegistry() {
221221
NativeFunction::kNeedsFunctionHolder |
222222
NativeFunction::kCanReturnErrors),
223223

224+
NativeFunction("parse_url", {}, DataTypeVector{utf8(), utf8()}, utf8(),
225+
kResultNullIfNull, "gdv_fn_parse_url_utf8_utf8",
226+
NativeFunction::kNeedsContext |
227+
NativeFunction::kNeedsFunctionHolder |
228+
NativeFunction::kCanReturnErrors),
229+
230+
NativeFunction("parse_url", {}, DataTypeVector{utf8(), utf8(), utf8()}, utf8(),
231+
kResultNullIfNull, "gdv_fn_parse_url_utf8_utf8_utf8",
232+
NativeFunction::kNeedsContext |
233+
NativeFunction::kNeedsFunctionHolder |
234+
NativeFunction::kCanReturnErrors),
235+
224236
NativeFunction("lpad", {}, DataTypeVector{utf8(), int32(), utf8()}, utf8(),
225237
kResultNullIfNull, "lpad_utf8_int32_utf8",
226238
NativeFunction::kNeedsContext),

cpp/src/gandiva/gdv_function_stubs.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "gandiva/replace_holder.h"
3535
#include "gandiva/rlike_holder.h"
3636
#include "gandiva/extract_holder.h"
37+
#include "gandiva/parse_url_holder.h"
3738
#include "gandiva/to_date_holder.h"
3839
#include "gandiva/translate_holder.h"
3940
#include "gandiva/substr_index_holder.h"
@@ -125,6 +126,48 @@ const char* gdv_fn_regexp_extract_utf8_utf8_int32(
125126
return (*holder)(context, data, data_len, idx, out_length);
126127
}
127128

129+
const char* gdv_fn_parse_url_utf8_utf8(
130+
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len, bool in1_valid,
131+
const char* part, int32_t part_len, bool in2_valid, bool* out_valid, int32_t* out_length) {
132+
if (!in1_valid || !in2_valid) {
133+
*out_valid = false;
134+
*out_length = 0;
135+
return reinterpret_cast<const char*>("");
136+
}
137+
gandiva::ExecutionContext* context = reinterpret_cast<gandiva::ExecutionContext*>(ptr);
138+
gandiva::ParseUrlHolder* holder = reinterpret_cast<gandiva::ParseUrlHolder*>(holder_ptr);
139+
auto res = (*holder)(context, data, data_len, part, part_len, out_length);
140+
if (res == nullptr) {
141+
*out_valid = false;
142+
*out_length = 0;
143+
return reinterpret_cast<const char*>("");
144+
}
145+
*out_valid = true;
146+
return res;
147+
}
148+
149+
const char* gdv_fn_parse_url_utf8_utf8_utf8(
150+
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len, bool in1_valid,
151+
const char* part, int32_t part_len, bool in2_valid,
152+
const char* pattern, int32_t pattern_len, bool in3_valid,
153+
bool* out_valid, int32_t* out_length) {
154+
if (!in1_valid || !in2_valid || !in3_valid) {
155+
*out_valid = false;
156+
*out_length = 0;
157+
return reinterpret_cast<const char*>("");
158+
}
159+
gandiva::ExecutionContext* context = reinterpret_cast<gandiva::ExecutionContext*>(ptr);
160+
gandiva::ParseUrlHolder* holder = reinterpret_cast<gandiva::ParseUrlHolder*>(holder_ptr);
161+
auto res = (*holder)(context, data, data_len, part, part_len, pattern, pattern_len, out_length);
162+
if (res == nullptr) {
163+
*out_valid = false;
164+
*out_length = 0;
165+
return reinterpret_cast<const char*>("");
166+
}
167+
*out_valid = true;
168+
return res;
169+
}
170+
128171
double gdv_fn_random(int64_t ptr) {
129172
gandiva::RandomGeneratorHolder* holder =
130173
reinterpret_cast<gandiva::RandomGeneratorHolder*>(ptr);
@@ -749,6 +792,41 @@ void ExportedStubFunctions::AddMappings(Engine* engine) const {
749792
"gdv_fn_regexp_extract_utf8_utf8_int32", types->i8_ptr_type() /*return_type*/, args,
750793
reinterpret_cast<void*>(gdv_fn_regexp_extract_utf8_utf8_int32));
751794

795+
// gdv_fn_parse_url_utf8_utf8
796+
args = {types->i64_type(), // int64_t ptr
797+
types->i64_type(), // int64_t holder_ptr
798+
types->i8_ptr_type(), // const char* data
799+
types->i32_type(), // int data_len
800+
types->i1_type(), // bool in1_validity
801+
types->i8_ptr_type(), // const char* part
802+
types->i32_type(), // int part_len
803+
types->i1_type(), // bool in2_validity
804+
types->ptr_type(types->i8_type()), // bool* out_valid
805+
types->i32_ptr_type()}; // int32_t* out_length
806+
807+
engine->AddGlobalMappingForFunc(
808+
"gdv_fn_parse_url_utf8_utf8", types->i8_ptr_type() /*return_type*/, args,
809+
reinterpret_cast<void*>(gdv_fn_parse_url_utf8_utf8));
810+
811+
// gdv_fn_parse_url_utf8_utf8_utf8
812+
args = {types->i64_type(), // int64_t ptr
813+
types->i64_type(), // int64_t holder_ptr
814+
types->i8_ptr_type(), // const char* data
815+
types->i32_type(), // int data_len
816+
types->i1_type(), // bool in1_validity
817+
types->i8_ptr_type(), // const char* part
818+
types->i32_type(), // int part_len
819+
types->i1_type(), // bool in2_validity
820+
types->i8_ptr_type(), // const char* pattern
821+
types->i32_type(), // int pattern_len
822+
types->i1_type(), // bool in3_validity
823+
types->ptr_type(types->i8_type()), // bool* out_valid
824+
types->i32_ptr_type()}; // int32_t* out_length
825+
826+
engine->AddGlobalMappingForFunc(
827+
"gdv_fn_parse_url_utf8_utf8_utf8", types->i8_ptr_type() /*return_type*/, args,
828+
reinterpret_cast<void*>(gdv_fn_parse_url_utf8_utf8_utf8));
829+
752830
// gdv_fn_to_date_utf8_utf8
753831
args = {types->i64_type(), // int64_t execution_context
754832
types->i64_type(), // int64_t holder_ptr

cpp/src/gandiva/gdv_function_stubs.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,18 @@ const char* gdv_fn_regexp_extract_utf8_utf8_int32(
6969
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len,
7070
const char* /*pattern*/, int32_t /*pattern_len*/, const int32_t idx, int32_t* out_length);
7171

72-
const char* gdv_fn_substr_index_utf8_utf8_int32(int64_t ptr, int64_t holder_ptr, const char* input, int in_len,
73-
const char* delim, int delim_len, int count, int32_t* out_len);
72+
const char* gdv_fn_parse_url_utf8_utf8(
73+
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len,
74+
const char* part, int32_t part_len, int32_t* out_length);
75+
76+
const char* gdv_fn_parse_url_utf8_utf8(
77+
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len, bool in1_valid,
78+
const char* part, int32_t part_len, bool in2_valid, bool* out_valid, int32_t* out_length);
79+
80+
const char* gdv_fn_parse_url_utf8_utf8_utf8(
81+
int64_t ptr, int64_t holder_ptr, const char* data, int32_t data_len, bool in1_valid,
82+
const char* part, int32_t part_len, bool in2_valid,
83+
const char* pattern, int32_t pattern_len, bool in3_valid, bool* out_valid, int32_t* out_length);
7484

7585
int64_t gdv_fn_to_date_utf8_utf8_int32(int64_t context, int64_t ptr, const char* data,
7686
int data_len, bool in1_validity,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 "gandiva/parse_url_holder.h"
19+
#include "gandiva/node.h"
20+
21+
namespace gandiva {
22+
23+
Status ParseUrlHolder::Make(const FunctionNode &node, std::shared_ptr <ParseUrlHolder> *holder) {
24+
return Make(holder);
25+
}
26+
27+
Status ParseUrlHolder::Make(std::shared_ptr<ParseUrlHolder>* holder) {
28+
*holder = std::shared_ptr<ParseUrlHolder>(new ParseUrlHolder());
29+
return Status::OK();
30+
}
31+
} // namespace gandiva

cpp/src/gandiva/parse_url_holder.h

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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+
#pragma once
19+
20+
#include <memory>
21+
#include <string>
22+
#include <unordered_map>
23+
24+
#include "arrow/status.h"
25+
#include "arrow/util/uri.h"
26+
#include "gandiva/execution_context.h"
27+
#include "gandiva/function_holder.h"
28+
#include "gandiva/node.h"
29+
#include "gandiva/visibility.h"
30+
31+
namespace gandiva {
32+
33+
/// Function Holder for 'parse_url'
34+
class GANDIVA_EXPORT ParseUrlHolder: public FunctionHolder {
35+
public:
36+
~ParseUrlHolder() override = default;
37+
38+
// Invoked by function_holder_registry.h
39+
static Status Make(const FunctionNode& node, std::shared_ptr<ParseUrlHolder>* holder);
40+
41+
static Status Make(std::shared_ptr<ParseUrlHolder>* holder);
42+
43+
const char * operator()(
44+
ExecutionContext *ctx, const char * url, int32_t url_len,
45+
const char * part, int32_t part_len, int32_t *out_length) {
46+
std::string url_string(url, url_len);
47+
std::string part_string(part, part_len);
48+
arrow::internal::Uri uri;
49+
std::string out;
50+
51+
auto status = uri.Parse(url_string);
52+
if (!status.ok()) {
53+
return nullptr;
54+
}
55+
56+
if (part_string == "HOST") {
57+
out = uri.host();
58+
} else if (part_string == "PATH") {
59+
out = uri.path();
60+
} else if (part_string == "QUERY") {
61+
out = uri.query_string();
62+
} else if (part_string == "PROTOCOL") {
63+
out = uri.scheme();
64+
} else if (part_string == "FILE") {
65+
if (uri.has_query()) {
66+
out = uri.path() + "?" + uri.query_string();
67+
} else {
68+
out = uri.path();
69+
}
70+
} else if (part_string == "AUTHORITY") {
71+
if (uri.has_port()) {
72+
out = uri.host() + ":" + uri.port_text();
73+
} else {
74+
out = uri.host();
75+
}
76+
} else if (part_string == "USERINFO") {
77+
out = uri.user_info();
78+
} else {
79+
return nullptr;
80+
}
81+
82+
*out_length = static_cast<int32_t>(out.length());
83+
if (*out_length == 0) {
84+
return nullptr;
85+
}
86+
87+
char *result_buffer = reinterpret_cast<char *>(ctx->arena()->Allocate(*out_length));
88+
if (result_buffer == NULLPTR) {
89+
ctx->set_error_msg("Could not allocate memory for result! Wrong result may be returned!");
90+
*out_length = 0;
91+
return nullptr;
92+
}
93+
memcpy(result_buffer, out.data(), *out_length);
94+
95+
return result_buffer;
96+
}
97+
98+
// We only support plain pattern string here.
99+
const char * operator()(
100+
ExecutionContext *ctx, const char * url, int32_t url_len,
101+
const char * part, int32_t part_len,
102+
const char * pattern, int32_t pattern_len, int32_t *out_length) {
103+
std::string url_string(url, url_len);
104+
std::string part_string(part, part_len);
105+
std::string pattern_string(pattern, pattern_len);
106+
arrow::internal::Uri uri;
107+
std::string out;
108+
109+
auto status = uri.Parse(url_string);
110+
if (!status.ok()) {
111+
return nullptr;
112+
}
113+
114+
if (part_string != "QUERY" || !uri.has_query()) {
115+
return nullptr;
116+
} else {
117+
std::unordered_map<std::string, std::string> queries;
118+
const auto items = std::move(uri.query_items()).ValueUnsafe();
119+
for (const auto& query : items) {
120+
queries.emplace(query.first, query.second);
121+
}
122+
123+
auto out_query = queries.find(pattern_string);
124+
if (out_query == queries.end()) {
125+
return nullptr;
126+
}
127+
out = out_query->second;
128+
129+
*out_length = static_cast<int32_t>(out.length());
130+
if (*out_length == 0) {
131+
return nullptr;
132+
}
133+
134+
char *result_buffer = reinterpret_cast<char *>(ctx->arena()->Allocate(*out_length));
135+
if (result_buffer == NULLPTR) {
136+
ctx->set_error_msg("Could not allocate memory for result! Wrong result may be returned!");
137+
*out_length = 0;
138+
return nullptr;
139+
}
140+
memcpy(result_buffer, out.data(), *out_length);
141+
142+
return result_buffer;
143+
}
144+
}
145+
}; // namespace gandiva
146+
}

0 commit comments

Comments
 (0)