Skip to content

Commit f876fb6

Browse files
authored
Merge pull request #2 from ahuarte47/main_elegant-headers-params
Elegant headers & params syntax
2 parents efab896 + 12d94e6 commit f876fb6

File tree

3 files changed

+64
-22
lines changed

3 files changed

+64
-22
lines changed

docs/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ WITH __input AS (
4646
'https://httpbin.org/delay/0',
4747
headers => MAP {
4848
'accept': 'application/json',
49-
}::VARCHAR,
50-
params => MAP {}::VARCHAR
49+
},
50+
params => MAP {
51+
}
5152
) AS data
5253
),
5354
__features AS (

src/http_client_extension.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#define DUCKDB_EXTENSION_MAIN
22
#include "http_client_extension.hpp"
33
#include "duckdb.hpp"
4+
#include "duckdb/common/types.hpp"
5+
#include "duckdb/common/vector_operations/generic_executor.hpp"
46
#include "duckdb/function/scalar_function.hpp"
57
#include "duckdb/main/extension_util.hpp"
68
#include "duckdb/common/atomic.hpp"
@@ -118,14 +120,18 @@ static void HTTPGetRequestFunction(DataChunk &args, ExpressionState &state, Vect
118120
static void HTTPPostRequestFunction(DataChunk &args, ExpressionState &state, Vector &result) {
119121
D_ASSERT(args.data.size() == 3);
120122

123+
using STRING_TYPE = PrimitiveType<string_t>;
124+
using LENTRY_TYPE = PrimitiveType<list_entry_t>;
125+
121126
auto &url_vector = args.data[0];
122127
auto &headers_vector = args.data[1];
128+
auto &headers_entry = ListVector::GetEntry(headers_vector);
123129
auto &body_vector = args.data[2];
124130

125-
TernaryExecutor::Execute<string_t, string_t, string_t, string_t>(
131+
GenericExecutor::ExecuteTernary<STRING_TYPE, LENTRY_TYPE, STRING_TYPE, STRING_TYPE>(
126132
url_vector, headers_vector, body_vector, result, args.size(),
127-
[&](string_t url, string_t headers, string_t body) {
128-
std::string url_str = url.GetString();
133+
[&](STRING_TYPE url, LENTRY_TYPE headers, STRING_TYPE body) {
134+
std::string url_str = url.val.GetString();
129135

130136
// Use helper to setup client and parse URL
131137
auto client_and_path = SetupHttpClient(url_str);
@@ -134,24 +140,24 @@ static void HTTPPostRequestFunction(DataChunk &args, ExpressionState &state, Vec
134140

135141
// Prepare headers
136142
duckdb_httplib_openssl::Headers header_map;
137-
std::istringstream header_stream(headers.GetString());
138-
std::string header;
139-
while (std::getline(header_stream, header)) {
140-
size_t colon_pos = header.find(':');
141-
if (colon_pos != std::string::npos) {
142-
std::string key = header.substr(0, colon_pos);
143-
std::string value = header.substr(colon_pos + 1);
144-
// Trim leading and trailing whitespace
145-
key.erase(0, key.find_first_not_of(" \t"));
146-
key.erase(key.find_last_not_of(" \t") + 1);
147-
value.erase(0, value.find_first_not_of(" \t"));
148-
value.erase(value.find_last_not_of(" \t") + 1);
149-
header_map.emplace(key, value);
143+
auto header_list = headers.val;
144+
for (idx_t i = header_list.offset; i < header_list.offset + header_list.length; i++) {
145+
const auto &child_value = headers_entry.GetValue(i);
146+
147+
Vector tmp(child_value);
148+
auto &children = StructVector::GetEntries(tmp);
149+
150+
if (children.size() == 2) {
151+
auto name = FlatVector::GetData<string_t>(*children[0]);
152+
auto data = FlatVector::GetData<string_t>(*children[1]);
153+
std::string key = name->GetString();
154+
std::string val = data->GetString();
155+
header_map.emplace(key, val);
150156
}
151157
}
152158

153159
// Make the POST request with headers and body
154-
auto res = client.Post(path.c_str(), header_map, body.GetString(), "application/json");
160+
auto res = client.Post(path.c_str(), header_map, body.val.GetString(), "application/json");
155161
if (res) {
156162
if (res->status == 200) {
157163
return StringVector::AddString(result, res->body);
@@ -175,7 +181,7 @@ static void LoadInternal(DatabaseInstance &instance) {
175181

176182
ScalarFunctionSet http_post("http_post");
177183
http_post.AddFunction(ScalarFunction(
178-
{LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::VARCHAR},
184+
{LogicalType::VARCHAR, LogicalType::MAP(LogicalType::VARCHAR, LogicalType::VARCHAR), LogicalType::JSON()},
179185
LogicalType::VARCHAR, HTTPPostRequestFunction));
180186
ExtensionUtil::RegisterFunction(instance, http_post);
181187
}

test/sql/httpclient.test

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ WITH __input AS (
4444
'https://httpbin.org/delay/0',
4545
headers => MAP {
4646
'accept': 'application/json',
47-
}::VARCHAR,
48-
params => MAP {}::VARCHAR
47+
},
48+
params => MAP {
49+
}
4950
) AS data
5051
),
5152
__features AS (
@@ -62,3 +63,37 @@ WITH __input AS (
6263
;
6364
----
6465
httpbin.org
66+
67+
# Confirm the POST extension works with headers and params
68+
query I
69+
WITH __input AS (
70+
SELECT
71+
http_post(
72+
'https://earth-search.aws.element84.com/v0/search',
73+
headers => MAP {
74+
'Content-Type': 'application/json',
75+
'Accept-Encoding': 'gzip',
76+
'Accept': 'application/geo+json'
77+
},
78+
params => {
79+
'collections': ['sentinel-s2-l2a-cogs'],
80+
'ids': ['S2A_56LPN_20210930_0_L2A'],
81+
'datetime': '2021-09-30/2021-09-30',
82+
'limit': 10
83+
}
84+
) AS data
85+
),
86+
__features AS (
87+
SELECT
88+
unnest( from_json((data::JSON)->'features', '["json"]') )
89+
AS features
90+
FROM
91+
__input
92+
)
93+
SELECT
94+
features->>'id' AS id
95+
FROM
96+
__features
97+
;
98+
----
99+
S2A_56LPN_20210930_0_L2A

0 commit comments

Comments
 (0)