First version

This commit is contained in:
Yves
2025-02-17 13:07:01 +01:00
parent 4228845a08
commit 75f2880fab
12 changed files with 869 additions and 37 deletions

View File

@@ -1,47 +1,111 @@
#define DUCKDB_EXTENSION_MAIN
#include "utils/env.hpp"
#include "utils/helpers.hpp"
#include "ui_extension.hpp"
#include "duckdb.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/common/string_util.hpp"
#include "duckdb/function/scalar_function.hpp"
#include "duckdb/main/extension_util.hpp"
#include <duckdb/parser/parsed_data/create_scalar_function_info.hpp>
#include "http_server.hpp"
#include <duckdb.hpp>
#include <duckdb/common/string_util.hpp>
// OpenSSL linked through vcpkg
#include <openssl/opensslv.h>
#ifdef _WIN32
#define OPEN_COMMAND "start"
#elif __linux__
#define OPEN_COMMAND "xdg-open"
#else
#define OPEN_COMMAND "open"
#endif
#define UI_LOCAL_PORT_SETTING_NAME "ui_local_port"
#define UI_LOCAL_PORT_SETTING_DESCRIPTION "Local port on which the UI server listens"
#define UI_LOCAL_PORT_SETTING_DEFAULT 4213
#define UI_REMOTE_URL_SETTING_NAME "ui_remote_url"
#define UI_REMOTE_URL_SETTING_DESCRIPTION "Remote URL to which the UI server forwards GET requests"
#define UI_REMOTE_URL_SETTING_DEFAULT "https://app.motherduck.com"
namespace duckdb {
inline void UiScalarFun(DataChunk &args, ExpressionState &state, Vector &result) {
auto &name_vector = args.data[0];
UnaryExecutor::Execute<string_t, string_t>(
name_vector, result, args.size(),
[&](string_t name) {
return StringVector::AddString(result, "Ui "+name.GetString()+" 🐥");
});
namespace internal {
bool StartHttpServer(const ClientContext &context) {
const auto url = GetSetting<std::string>(context, UI_REMOTE_URL_SETTING_NAME,
GetEnvOrDefault(UI_REMOTE_URL_SETTING_NAME, UI_REMOTE_URL_SETTING_DEFAULT));
const uint16_t port = GetSetting(context, UI_LOCAL_PORT_SETTING_NAME, UI_LOCAL_PORT_SETTING_DEFAULT);;
return ui::HttpServer::instance()->Start(port, url, context.db);
}
inline void UiOpenSSLVersionScalarFun(DataChunk &args, ExpressionState &state, Vector &result) {
auto &name_vector = args.data[0];
UnaryExecutor::Execute<string_t, string_t>(
name_vector, result, args.size(),
[&](string_t name) {
return StringVector::AddString(result, "Ui " + name.GetString() +
", my linked OpenSSL version is " +
OPENSSL_VERSION_TEXT );
});
std::string GetHttpServerLocalURL() {
return StringUtil::Format("http://localhost:%d/", ui::HttpServer::instance()->LocalPort());
}
} // namespace internal
void OutputResult(const std::string &result, DataChunk &out_chunk) {
out_chunk.SetCardinality(1);
out_chunk.SetValue(0, 0, result);
}
void StartUIFunction(ClientContext &context, TableFunctionInput &input,
DataChunk &out_chunk) {
if (!ShouldRun(input)) {
return;
}
internal::StartHttpServer(context);
auto local_url = internal::GetHttpServerLocalURL();
const std::string command = StringUtil::Format("%s %s", OPEN_COMMAND, local_url);
std::string result = system(command.c_str()) ?
StringUtil::Format("Navigate browser to %s", local_url) // open command failed
: StringUtil::Format("MotherDuck UI started at %s", local_url);
OutputResult(result, out_chunk);
}
void StartUIServerFunction(ClientContext &context, TableFunctionInput &input,
DataChunk &out_chunk) {
if (!ShouldRun(input)) {
return;
}
const bool already = internal::StartHttpServer(context);
const char* already_str = already ? "already " : "";
auto result = StringUtil::Format("MotherDuck UI server %sstarted at %s", already_str, internal::GetHttpServerLocalURL());
OutputResult(result, out_chunk);
}
void StopUIServerFunction(ClientContext &, TableFunctionInput &input,
DataChunk &out_chunk) {
if (!ShouldRun(input)) {
return;
}
auto result = ui::HttpServer::instance()->Stop() ? "UI server stopped" : "UI server already stopped";
OutputResult(result, out_chunk);
}
// FIXME
// void HandleConnected(const std::string &token) {
// ui::HttpServer::instance()->SendConnectedEvent(token);
// }
// FIXME
// void HandleCatalogChanged() {
// ui::HttpServer::instance()->SendCatalogChangedEvent();
// }
static void LoadInternal(DatabaseInstance &instance) {
// Register a scalar function
auto ui_scalar_function = ScalarFunction("ui", {LogicalType::VARCHAR}, LogicalType::VARCHAR, UiScalarFun);
ExtensionUtil::RegisterFunction(instance, ui_scalar_function);
auto &config = DBConfig::GetConfig(instance);
// Register another scalar function
auto ui_openssl_version_scalar_function = ScalarFunction("ui_openssl_version", {LogicalType::VARCHAR},
LogicalType::VARCHAR, UiOpenSSLVersionScalarFun);
ExtensionUtil::RegisterFunction(instance, ui_openssl_version_scalar_function);
config.AddExtensionOption(UI_LOCAL_PORT_SETTING_NAME, UI_LOCAL_PORT_SETTING_DESCRIPTION,
LogicalType::USMALLINT, Value::USMALLINT(UI_LOCAL_PORT_SETTING_DEFAULT));
config.AddExtensionOption(
UI_REMOTE_URL_SETTING_NAME, UI_REMOTE_URL_SETTING_DESCRIPTION, LogicalType::VARCHAR,
Value(GetEnvOrDefault(UI_REMOTE_URL_SETTING_NAME, UI_REMOTE_URL_SETTING_DEFAULT)));
RegisterTF(instance, "start_ui", StartUIFunction);
RegisterTF(instance, "start_ui_server", StartUIServerFunction);
RegisterTF(instance, "stop_ui_server", StopUIServerFunction);
}
void UiExtension::Load(DuckDB &db) {