Skip to content

Commit 5b12555

Browse files
committed
fix: add telemetry
1 parent 9d6bfc0 commit 5b12555

File tree

4 files changed

+118
-5
lines changed

4 files changed

+118
-5
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ set(EXTENSION_SOURCES
2525
src/textplot_density.cpp
2626
src/textplot_sparkline.cpp
2727
src/textplot_qr.cpp
28+
src/query_farm_telemetry.cpp
2829
)
2930

3031
build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES})

src/query_farm_telemetry.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include "query_farm_telemetry.hpp"
2+
#include <thread>
3+
#include "duckdb.hpp"
4+
#include "duckdb/common/http_util.hpp"
5+
#include "yyjson.hpp"
6+
#include "duckdb/main/extension_helper.hpp"
7+
#include "duckdb/main/config.hpp"
8+
#include <cstdlib>
9+
using namespace duckdb_yyjson; // NOLINT
10+
11+
namespace duckdb {
12+
13+
static constexpr const char *TARGET_URL = "https://duckdb-in.query-farm.services/";
14+
15+
// Function to send the actual HTTP request
16+
static void sendHTTPRequest(shared_ptr<DatabaseInstance> db, std::string json_body) {
17+
HTTPHeaders headers;
18+
headers.Insert("Content-Type", "application/json");
19+
20+
auto &http_util = HTTPUtil::Get(*db);
21+
unique_ptr<HTTPParams> params;
22+
auto target_url = string(TARGET_URL);
23+
params = http_util.InitializeParameters(*db, target_url);
24+
25+
PostRequestInfo post_request(target_url, headers, *params, reinterpret_cast<const_data_ptr_t>(json_body.data()),
26+
json_body.size());
27+
try {
28+
auto response = http_util.Request(post_request);
29+
} catch (const std::exception &e) {
30+
// ignore all errors.
31+
}
32+
33+
return;
34+
}
35+
36+
// Public function to start the request thread
37+
static void sendRequestAsync(shared_ptr<DatabaseInstance> db, std::string &json_body) {
38+
std::thread request_thread(sendHTTPRequest, db, std::move(json_body));
39+
request_thread.detach(); // Let the thread run independently
40+
}
41+
42+
INTERNAL_FUNC void QueryFarmSendTelemetry(ExtensionLoader &loader, const string &extension_name,
43+
const string &extension_version) {
44+
const char *opt_out = std::getenv("QUERY_FARM_TELEMETRY_OPT_OUT");
45+
if (opt_out != nullptr) {
46+
return;
47+
}
48+
49+
auto db = loader.GetDatabaseInstance().shared_from_this();
50+
51+
auto &dbconfig = DBConfig::GetConfig(*db);
52+
auto old_value = dbconfig.options.autoinstall_known_extensions;
53+
dbconfig.options.autoinstall_known_extensions = false;
54+
try {
55+
ExtensionHelper::AutoLoadExtension(loader.GetDatabaseInstance(), "httpfs");
56+
} catch (...) {
57+
dbconfig.options.autoinstall_known_extensions = old_value;
58+
return;
59+
}
60+
61+
dbconfig.options.autoinstall_known_extensions = old_value;
62+
if (!loader.GetDatabaseInstance().ExtensionIsLoaded("httpfs")) {
63+
return;
64+
}
65+
66+
// Initialize the telemetry sender
67+
auto doc = yyjson_mut_doc_new(nullptr);
68+
69+
auto result_obj = yyjson_mut_obj(doc);
70+
yyjson_mut_doc_set_root(doc, result_obj);
71+
72+
auto user_agent = "query-farm/20250915";
73+
auto platform = DuckDB::Platform();
74+
yyjson_mut_obj_add_str(doc, result_obj, "extension_name", extension_name.c_str());
75+
yyjson_mut_obj_add_str(doc, result_obj, "extension_version", extension_version.c_str());
76+
yyjson_mut_obj_add_str(doc, result_obj, "user_agent", user_agent);
77+
yyjson_mut_obj_add_str(doc, result_obj, "duckdb_platform", platform.c_str());
78+
yyjson_mut_obj_add_str(doc, result_obj, "duckdb_library_version", DuckDB::LibraryVersion());
79+
yyjson_mut_obj_add_str(doc, result_obj, "duckdb_release_codename", DuckDB::ReleaseCodename());
80+
yyjson_mut_obj_add_str(doc, result_obj, "duckdb_source_id", DuckDB::SourceID());
81+
82+
size_t telemetry_len;
83+
auto telemetry_data =
84+
yyjson_mut_val_write_opts(result_obj, YYJSON_WRITE_ALLOW_INF_AND_NAN, NULL, &telemetry_len, nullptr);
85+
86+
if (telemetry_data == nullptr) {
87+
throw SerializationException("Failed to serialize telemetry data.");
88+
}
89+
90+
auto telemetry_string = string(telemetry_data, (size_t)telemetry_len);
91+
92+
// Send request asynchronously
93+
sendRequestAsync(db, telemetry_string);
94+
}
95+
96+
} // namespace duckdb

src/query_farm_telemetry.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
#include <string>
3+
#include "duckdb.hpp"
4+
5+
#if defined(_WIN32) || defined(_WIN64)
6+
// Windows: functions are hidden by default unless exported
7+
#define INTERNAL_FUNC
8+
#elif defined(__GNUC__) || defined(__clang__)
9+
// Linux / macOS: hide symbol using visibility attribute
10+
#define INTERNAL_FUNC __attribute__((visibility("hidden")))
11+
#else
12+
#define INTERNAL_FUNC
13+
#endif
14+
15+
namespace duckdb {
16+
void QueryFarmSendTelemetry(ExtensionLoader &loader, const string &extension_name, const string &extension_version);
17+
}

src/textplot_extension.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "textplot_qr.hpp"
66
#include "duckdb.hpp"
77
#include "duckdb/function/scalar_function.hpp"
8+
#include "query_farm_telemetry.hpp"
89

910
namespace duckdb {
1011

@@ -27,6 +28,8 @@ static void LoadInternal(ExtensionLoader &loader) {
2728
LogicalType::VARCHAR, TextplotSparkline, TextplotSparklineBind, nullptr,
2829
nullptr, nullptr, LogicalType(LogicalTypeId::ANY));
2930
loader.RegisterFunction(sparkline_function);
31+
32+
QueryFarmSendTelemetry(loader, "textplot", TextplotExtension().Version());
3033
}
3134

3235
void TextplotExtension::Load(ExtensionLoader &loader) {
@@ -38,11 +41,7 @@ std::string TextplotExtension::Name() {
3841
}
3942

4043
std::string TextplotExtension::Version() const {
41-
#ifdef EXT_VERSION_QUACK
42-
return EXT_VERSION_QUACK;
43-
#else
44-
return "";
45-
#endif
44+
return "2025091501";
4645
}
4746

4847
} // namespace duckdb

0 commit comments

Comments
 (0)