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
0 commit comments