From 96154ca96a6e4d396ffe002bacb7c06f2ce71856 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Fri, 22 May 2026 15:07:23 -0700 Subject: [PATCH 1/3] Migrate to VEF Protocol V3 typed API Replace extension.h with vsql.h and update all function signatures to typed C++ wrappers. V1 VEF_RESULT_ERROR maps to .warning() in V3. Fix FindVillageSQL.cmake SDK selection. Update AGENTS.md VEF API examples to Protocol V3. AI=CLAUDE Co-Authored-By: Claude Sonnet 4.6 (1M context) --- AGENTS.md | 55 +++++---------- cmake/FindVillageSQL.cmake | 16 +++-- src/vsql_http.cc | 140 +++++++++++++++---------------------- 3 files changed, 85 insertions(+), 126 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 37d3916..9684246 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,7 +71,7 @@ All functions return NULL on connection failure or NULL input. **Error Handling:** - HTTP functions return NULL on curl-level failure (connection refused, DNS failure, timeout) - `url_encode`/`url_decode` return NULL for NULL input or curl init failure -- Exceptions are caught and returned as `VEF_RESULT_ERROR` +- Exceptions are caught and surfaced as Warning 3200 via `result.warning()` **Key Implementation Details:** - Thread-local curl handles with connection keep-alive (`curl_easy_reset()` between calls) @@ -83,7 +83,7 @@ All functions return NULL on connection failure or NULL input. - Options JSON supports: `timeout` (int), `proxy`, `user_agent`, `ssl_cert`, `ssl_key`, `ssl_ca_bundle` (strings) **Dependencies:** -- VillageSQL Extension Framework SDK (``) +- VillageSQL Extension Framework SDK (``, Protocol V3) - libcurl (system-provided) **Code Organization:** @@ -91,58 +91,37 @@ All functions return NULL on connection failure or NULL input. - Function naming: lowercase with underscores (e.g., `http_get_1_impl`) - Variable naming: lowercase with underscores (e.g., `response_body`) -## VillageSQL Extension Framework (VEF) API Pattern +## VillageSQL Extension Framework (VEF) API Pattern — Protocol V3 + +This extension uses the Protocol V3 stable SDK (``). ### Function Implementation Pattern ```cpp -void my_function_impl(vef_context_t* ctx, - vef_invalue_t* arg1, vef_invalue_t* arg2, - vef_vdf_result_t* result) { - // Check for NULL arguments - if (arg1->is_null || arg2->is_null) { - result->type = VEF_RESULT_NULL; - return; - } - - // Access argument values - const char* str_value = arg1->str_value; - size_t str_len = arg1->str_len; - - // Perform function logic - // ... +#include +using namespace vsql; - // Set result - result->type = VEF_RESULT_VALUE; - result->actual_len = result_length; - // Write to result->str_buf +void my_function_impl(StringArg arg1, StringArg arg2, StringResult result) { + if (arg1.is_null() || arg2.is_null()) { result.set_null(); return; } + std::string_view s1 = arg1.value(); + // ... + result.set(output); // or result.set_length(n) after writing to result.buffer() } ``` +Typed argument wrappers: `StringArg`, `IntArg`, `RealArg`. Result wrappers: `StringResult`, `IntResult`, `RealResult`. Use `result.set_null()` for NULL, `result.warning("msg")` for soft errors (Warning 3200), `result.error("msg")` for hard errors (ERROR 3200). + ### Function Registration Pattern ```cpp -#include - -using namespace villagesql::extension_builder; - VEF_GENERATE_ENTRY_POINTS( - make_extension("extension_name", "0.0.1") + make_extension() .func(make_func<&my_function_impl>("my_function") - .returns(STRING) // or INT, etc. - .param(STRING) // add .param() for each parameter - .param(INT) - .buffer_size(1024) // max result size - .build()) + .returns(STRING).param(STRING).param(INT).buffer_size(1024).build()) ) ``` -### Key Differences from Old MySQL UDF API: -- No separate init/main/deinit functions - single implementation function -- Arguments passed as `vef_invalue_t*` structs with `is_null`, `str_value`, `bin_value`, `int_value` fields -- Results set via `vef_vdf_result_t*` with `type`, `str_buf`, `bin_buf`, `actual_len` fields -- Function registration done declaratively in code using builder pattern -- No install.sql needed - functions registered at extension load time +Extension name and version come from `manifest.json`; `make_extension()` takes no arguments in V3. ## Testing diff --git a/cmake/FindVillageSQL.cmake b/cmake/FindVillageSQL.cmake index 1e9ca7b..1b2690d 100644 --- a/cmake/FindVillageSQL.cmake +++ b/cmake/FindVillageSQL.cmake @@ -98,10 +98,18 @@ set(_villagesql_found FALSE) # Method 1: Use VillageSQL_BUILD_DIR for development builds if(VillageSQL_BUILD_DIR AND NOT _villagesql_found) # Look for staged SDK in build directory - file(GLOB _sdk_dirs "${VillageSQL_BUILD_DIR}/villagesql-extension-sdk-*") + file(GLOB _sdk_dirs_all "${VillageSQL_BUILD_DIR}/villagesql-extension-sdk-*") + set(_sdk_dirs "") + foreach(_d IN LISTS _sdk_dirs_all) + if(IS_DIRECTORY "${_d}") + list(APPEND _sdk_dirs "${_d}") + endif() + endforeach() + unset(_sdk_dirs_all) if(_sdk_dirs) + list(SORT _sdk_dirs ORDER DESCENDING) list(GET _sdk_dirs 0 _sdk_dir) - if(EXISTS "${_sdk_dir}/include/villagesql/extension.h") + if(EXISTS "${_sdk_dir}/include/villagesql/vsql.h") set(VillageSQL_PREFIX "${_sdk_dir}") set(VillageSQL_INCLUDE_DIR "${_sdk_dir}/include") set(VillageSQL_CXX_FLAGS "-I${_sdk_dir}/include") @@ -136,7 +144,7 @@ endif() # Method 2: Use VillageSQL_SDK_DIR if explicitly set if(VillageSQL_SDK_DIR AND NOT _villagesql_found) - if(EXISTS "${VillageSQL_SDK_DIR}/include/villagesql/extension.h") + if(EXISTS "${VillageSQL_SDK_DIR}/include/villagesql/vsql.h") set(VillageSQL_PREFIX "${VillageSQL_SDK_DIR}") set(VillageSQL_INCLUDE_DIR "${VillageSQL_SDK_DIR}/include") set(VillageSQL_CXX_FLAGS "-I${VillageSQL_SDK_DIR}/include") @@ -205,7 +213,7 @@ endif() if(NOT _villagesql_found) set(_default_prefix "$ENV{HOME}/.villagesql") - if(EXISTS "${_default_prefix}/include/villagesql/extension.h") + if(EXISTS "${_default_prefix}/include/villagesql/vsql.h") set(VillageSQL_PREFIX "${_default_prefix}") set(VillageSQL_INCLUDE_DIR "${_default_prefix}/include") set(VillageSQL_CXX_FLAGS "-I${_default_prefix}/include") diff --git a/src/vsql_http.cc b/src/vsql_http.cc index 2c88b44..5f35182 100644 --- a/src/vsql_http.cc +++ b/src/vsql_http.cc @@ -23,7 +23,7 @@ // DNS failure, timeout). Use JSON_VALUE(result, '$.status') to inspect the // HTTP status code. -#include +#include #include @@ -33,7 +33,7 @@ #include #include -using namespace villagesql::extension_builder; +using namespace vsql; // ============================================================ // curl global init (process-wide, once) @@ -397,27 +397,6 @@ static std::string do_http(std::string_view method, std::string_view url, return out; } -// ============================================================ -// Result output -// -// The VEF provides a caller-managed str_buf of size max_str_len. -// Write into it with a bounds check. Responses larger than max_str_len are -// truncated — buffer_size() on each function registration controls the -// pre-allocated size. HTTP functions use 256KB; urlencode/urldecode use 8KB. -// ============================================================ - -static inline void set_result(vef_vdf_result_t *result, const std::string &s) { - size_t copy_len = s.size(); - if (copy_len > result->max_str_len) copy_len = result->max_str_len; - memcpy(result->str_buf, s.data(), copy_len); - result->actual_len = copy_len; - result->type = VEF_RESULT_VALUE; -} - -// Helpers to convert vef_invalue_t to string_view. -#define SV(arg) std::string_view{(arg)->str_value, (arg)->str_len} -#define SV_OR_EMPTY(arg) ((arg)->is_null ? std::string_view{} : SV(arg)) - // ============================================================ // VDF implementations // ============================================================ @@ -425,71 +404,72 @@ static inline void set_result(vef_vdf_result_t *result, const std::string &s) { // Shared wrapper: null-checks url, calls fn() to get the result string, // handles NULL/empty/error outcomes, and writes to the result buffer. template -static void http_call(vef_invalue_t *url, vef_vdf_result_t *result, +static void http_call(StringArg url, StringResult result, const char *func_name, Fn fn) { try { - if (url->is_null) { result->type = VEF_RESULT_NULL; return; } + if (url.is_null()) { result.set_null(); return; } std::string r = fn(); - if (r.empty()) { result->type = VEF_RESULT_NULL; return; } - set_result(result, r); + if (r.empty()) { result.set_null(); return; } + result.set(r); } catch (...) { - result->type = VEF_RESULT_ERROR; - snprintf(result->error_msg, VEF_MAX_ERROR_LEN, "%s: internal error", - func_name); + result.warning(std::string(func_name) + ": internal error"); } } -static void http_get_1_impl(vef_context_t *, vef_invalue_t *url, - vef_vdf_result_t *result) { +static void http_get_1_impl(StringArg url, StringResult result) { http_call(url, result, "http_get", - [&] { return do_http("GET", SV(url), {}, {}, {}); }); + [&] { return do_http("GET", url.value(), {}, {}, {}); }); } -static void http_post_3_impl(vef_context_t *, vef_invalue_t *url, - vef_invalue_t *ct, vef_invalue_t *body, - vef_vdf_result_t *result) { +static void http_post_3_impl(StringArg url, StringArg ct, StringArg body, + StringResult result) { http_call(url, result, "http_post", [&] { - return do_http("POST", SV(url), {}, SV_OR_EMPTY(body), SV_OR_EMPTY(ct)); + return do_http("POST", url.value(), {}, + body.is_null() ? std::string_view{} : body.value(), + ct.is_null() ? std::string_view{} : ct.value()); }); } -static void http_put_3_impl(vef_context_t *, vef_invalue_t *url, - vef_invalue_t *ct, vef_invalue_t *body, - vef_vdf_result_t *result) { +static void http_put_3_impl(StringArg url, StringArg ct, StringArg body, + StringResult result) { http_call(url, result, "http_put", [&] { - return do_http("PUT", SV(url), {}, SV_OR_EMPTY(body), SV_OR_EMPTY(ct)); + return do_http("PUT", url.value(), {}, + body.is_null() ? std::string_view{} : body.value(), + ct.is_null() ? std::string_view{} : ct.value()); }); } -static void http_delete_1_impl(vef_context_t *, vef_invalue_t *url, - vef_vdf_result_t *result) { +static void http_delete_1_impl(StringArg url, StringResult result) { http_call(url, result, "http_delete", - [&] { return do_http("DELETE", SV(url), {}, {}, {}); }); + [&] { return do_http("DELETE", url.value(), {}, {}, {}); }); } -static void http_patch_3_impl(vef_context_t *, vef_invalue_t *url, - vef_invalue_t *ct, vef_invalue_t *body, - vef_vdf_result_t *result) { +static void http_patch_3_impl(StringArg url, StringArg ct, StringArg body, + StringResult result) { http_call(url, result, "http_patch", [&] { - return do_http("PATCH", SV(url), {}, SV_OR_EMPTY(body), SV_OR_EMPTY(ct)); + return do_http("PATCH", url.value(), {}, + body.is_null() ? std::string_view{} : body.value(), + ct.is_null() ? std::string_view{} : ct.value()); }); } -// http(method, url, headers_json, body, content_type, options_json) +// http_request(method, url, headers_json, body, content_type, options_json) // options_json: NULL or JSON object with keys: timeout (int seconds), // proxy, user_agent, ssl_cert, ssl_key, ssl_ca_bundle (all strings). // Example: '{"timeout": 5, "proxy": "http://proxy:8080"}' -static void http_6_impl(vef_context_t *, vef_invalue_t *method, - vef_invalue_t *url, vef_invalue_t *hdrs, - vef_invalue_t *body, vef_invalue_t *ct, - vef_invalue_t *options, - vef_vdf_result_t *result) { +static void http_6_impl(StringArg method, StringArg url, StringArg hdrs, + StringArg body, StringArg ct, StringArg options, + StringResult result) { http_call(url, result, "http_request", [&] { - std::string_view m = method->is_null ? std::string_view{"GET"} : SV(method); - HttpOptions opts = options->is_null ? HttpOptions{} : - parse_options(SV(options)); - return do_http(m, SV(url), SV_OR_EMPTY(hdrs), SV_OR_EMPTY(body), - SV_OR_EMPTY(ct), opts); + std::string_view m = method.is_null() ? std::string_view{"GET"} + : method.value(); + HttpOptions opts = options.is_null() ? HttpOptions{} + : parse_options(options.value()); + return do_http(m, url.value(), + hdrs.is_null() ? std::string_view{} : hdrs.value(), + body.is_null() ? std::string_view{} : body.value(), + ct.is_null() ? std::string_view{} : ct.value(), + opts); }); } @@ -497,49 +477,41 @@ static void http_6_impl(vef_context_t *, vef_invalue_t *method, // the char* was allocated by curl (freed here via curl_free) and size_t is the // byte count. Returns NULL to the caller on curl init failure or op failure. template -static void curl_codec_impl(vef_invalue_t *input, vef_vdf_result_t *result, +static void curl_codec_impl(StringArg input, StringResult result, const char *func_name, Op op) { try { - if (input->is_null) { result->type = VEF_RESULT_NULL; return; } + if (input.is_null()) { result.set_null(); return; } CURL *curl = get_curl_handle(); - if (!curl) { result->type = VEF_RESULT_NULL; return; } - auto [buf, buf_len] = op(curl); - if (!buf) { result->type = VEF_RESULT_NULL; return; } - size_t copy_len = buf_len < result->max_str_len ? buf_len : result->max_str_len; - memcpy(result->str_buf, buf, copy_len); + if (!curl) { result.set_null(); return; } + auto [buf, buf_len] = op(curl, input.value()); + if (!buf) { result.set_null(); return; } + auto dst = result.buffer(); + size_t copy_len = buf_len < dst.size() ? buf_len : dst.size(); + memcpy(dst.data(), buf, copy_len); curl_free(buf); - result->actual_len = copy_len; - result->type = VEF_RESULT_VALUE; + result.set_length(copy_len); } catch (...) { - result->type = VEF_RESULT_ERROR; - snprintf(result->error_msg, VEF_MAX_ERROR_LEN, "%s: internal error", func_name); + result.warning(std::string(func_name) + ": internal error"); } } // urlencode(text) — percent-encode a string using curl_easy_escape. -static void urlencode_impl(vef_context_t *, vef_invalue_t *input, - vef_vdf_result_t *result) { - curl_codec_impl(input, result, "urlencode", [&](CURL *c) { - char *r = curl_easy_escape(c, input->str_value, - static_cast(input->str_len)); +static void urlencode_impl(StringArg input, StringResult result) { + curl_codec_impl(input, result, "urlencode", [&](CURL *c, std::string_view sv) { + char *r = curl_easy_escape(c, sv.data(), static_cast(sv.size())); return std::pair{r, r ? strlen(r) : 0}; }); } // urldecode(text) — decode a percent-encoded string using curl_easy_unescape. -static void urldecode_impl(vef_context_t *, vef_invalue_t *input, - vef_vdf_result_t *result) { - curl_codec_impl(input, result, "urldecode", [&](CURL *c) { +static void urldecode_impl(StringArg input, StringResult result) { + curl_codec_impl(input, result, "urldecode", [&](CURL *c, std::string_view sv) { int n = 0; - char *r = curl_easy_unescape(c, input->str_value, - static_cast(input->str_len), &n); + char *r = curl_easy_unescape(c, sv.data(), static_cast(sv.size()), &n); return std::pair{r, static_cast(n)}; }); } -#undef SV -#undef SV_OR_EMPTY - // ============================================================ // Registration // ============================================================ @@ -554,7 +526,7 @@ static constexpr size_t kEncodeBufSize = 8 * 1024; // Note: VEF does not support function overloading. Each function name maps // to exactly one signature. For HTTP calls with custom headers, use http(). VEF_GENERATE_ENTRY_POINTS( - make_extension("vsql_http", "0.0.1") + make_extension() .func(make_func<&http_get_1_impl>("http_get") .returns(STRING).param(STRING) .buffer_size(kHttpBufSize).build()) From 837173b1eb357233bbaa1f74bb2e8838c0798929 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Sat, 23 May 2026 09:25:40 -0700 Subject: [PATCH 2/3] Fix formatting per villint AI=CLAUDE Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/vsql_http.cc | 133 +++++++++++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/src/vsql_http.cc b/src/vsql_http.cc index 5f35182..c0e51f0 100644 --- a/src/vsql_http.cc +++ b/src/vsql_http.cc @@ -404,12 +404,18 @@ static std::string do_http(std::string_view method, std::string_view url, // Shared wrapper: null-checks url, calls fn() to get the result string, // handles NULL/empty/error outcomes, and writes to the result buffer. template -static void http_call(StringArg url, StringResult result, - const char *func_name, Fn fn) { +static void http_call(StringArg url, StringResult result, const char *func_name, + Fn fn) { try { - if (url.is_null()) { result.set_null(); return; } + if (url.is_null()) { + result.set_null(); + return; + } std::string r = fn(); - if (r.empty()) { result.set_null(); return; } + if (r.empty()) { + result.set_null(); + return; + } result.set(r); } catch (...) { result.warning(std::string(func_name) + ": internal error"); @@ -422,7 +428,7 @@ static void http_get_1_impl(StringArg url, StringResult result) { } static void http_post_3_impl(StringArg url, StringArg ct, StringArg body, - StringResult result) { + StringResult result) { http_call(url, result, "http_post", [&] { return do_http("POST", url.value(), {}, body.is_null() ? std::string_view{} : body.value(), @@ -431,7 +437,7 @@ static void http_post_3_impl(StringArg url, StringArg ct, StringArg body, } static void http_put_3_impl(StringArg url, StringArg ct, StringArg body, - StringResult result) { + StringResult result) { http_call(url, result, "http_put", [&] { return do_http("PUT", url.value(), {}, body.is_null() ? std::string_view{} : body.value(), @@ -445,7 +451,7 @@ static void http_delete_1_impl(StringArg url, StringResult result) { } static void http_patch_3_impl(StringArg url, StringArg ct, StringArg body, - StringResult result) { + StringResult result) { http_call(url, result, "http_patch", [&] { return do_http("PATCH", url.value(), {}, body.is_null() ? std::string_view{} : body.value(), @@ -458,18 +464,17 @@ static void http_patch_3_impl(StringArg url, StringArg ct, StringArg body, // proxy, user_agent, ssl_cert, ssl_key, ssl_ca_bundle (all strings). // Example: '{"timeout": 5, "proxy": "http://proxy:8080"}' static void http_6_impl(StringArg method, StringArg url, StringArg hdrs, - StringArg body, StringArg ct, StringArg options, - StringResult result) { + StringArg body, StringArg ct, StringArg options, + StringResult result) { http_call(url, result, "http_request", [&] { - std::string_view m = method.is_null() ? std::string_view{"GET"} - : method.value(); - HttpOptions opts = options.is_null() ? HttpOptions{} - : parse_options(options.value()); + std::string_view m = + method.is_null() ? std::string_view{"GET"} : method.value(); + HttpOptions opts = + options.is_null() ? HttpOptions{} : parse_options(options.value()); return do_http(m, url.value(), hdrs.is_null() ? std::string_view{} : hdrs.value(), body.is_null() ? std::string_view{} : body.value(), - ct.is_null() ? std::string_view{} : ct.value(), - opts); + ct.is_null() ? std::string_view{} : ct.value(), opts); }); } @@ -478,13 +483,22 @@ static void http_6_impl(StringArg method, StringArg url, StringArg hdrs, // byte count. Returns NULL to the caller on curl init failure or op failure. template static void curl_codec_impl(StringArg input, StringResult result, - const char *func_name, Op op) { + const char *func_name, Op op) { try { - if (input.is_null()) { result.set_null(); return; } + if (input.is_null()) { + result.set_null(); + return; + } CURL *curl = get_curl_handle(); - if (!curl) { result.set_null(); return; } + if (!curl) { + result.set_null(); + return; + } auto [buf, buf_len] = op(curl, input.value()); - if (!buf) { result.set_null(); return; } + if (!buf) { + result.set_null(); + return; + } auto dst = result.buffer(); size_t copy_len = buf_len < dst.size() ? buf_len : dst.size(); memcpy(dst.data(), buf, copy_len); @@ -497,19 +511,22 @@ static void curl_codec_impl(StringArg input, StringResult result, // urlencode(text) — percent-encode a string using curl_easy_escape. static void urlencode_impl(StringArg input, StringResult result) { - curl_codec_impl(input, result, "urlencode", [&](CURL *c, std::string_view sv) { - char *r = curl_easy_escape(c, sv.data(), static_cast(sv.size())); - return std::pair{r, r ? strlen(r) : 0}; - }); + curl_codec_impl( + input, result, "urlencode", [&](CURL *c, std::string_view sv) { + char *r = curl_easy_escape(c, sv.data(), static_cast(sv.size())); + return std::pair{r, r ? strlen(r) : 0}; + }); } // urldecode(text) — decode a percent-encoded string using curl_easy_unescape. static void urldecode_impl(StringArg input, StringResult result) { - curl_codec_impl(input, result, "urldecode", [&](CURL *c, std::string_view sv) { - int n = 0; - char *r = curl_easy_unescape(c, sv.data(), static_cast(sv.size()), &n); - return std::pair{r, static_cast(n)}; - }); + curl_codec_impl( + input, result, "urldecode", [&](CURL *c, std::string_view sv) { + int n = 0; + char *r = + curl_easy_unescape(c, sv.data(), static_cast(sv.size()), &n); + return std::pair{r, static_cast(n)}; + }); } // ============================================================ @@ -528,27 +545,53 @@ static constexpr size_t kEncodeBufSize = 8 * 1024; VEF_GENERATE_ENTRY_POINTS( make_extension() .func(make_func<&http_get_1_impl>("http_get") - .returns(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&http_post_3_impl>("http_post") - .returns(STRING).param(STRING).param(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&http_put_3_impl>("http_put") - .returns(STRING).param(STRING).param(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&http_delete_1_impl>("http_delete") - .returns(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&http_patch_3_impl>("http_patch") - .returns(STRING).param(STRING).param(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&http_6_impl>("http_request") - .returns(STRING).param(STRING).param(STRING).param(STRING) - .param(STRING).param(STRING).param(STRING) - .buffer_size(kHttpBufSize).build()) + .returns(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .param(STRING) + .buffer_size(kHttpBufSize) + .build()) .func(make_func<&urlencode_impl>("url_encode") - .returns(STRING).param(STRING) - .buffer_size(kEncodeBufSize).build()) + .returns(STRING) + .param(STRING) + .buffer_size(kEncodeBufSize) + .build()) .func(make_func<&urldecode_impl>("url_decode") - .returns(STRING).param(STRING) - .buffer_size(kEncodeBufSize).build())) + .returns(STRING) + .param(STRING) + .buffer_size(kEncodeBufSize) + .build())) From 1eec03ac63097324767a5a545ad25407c6d43c83 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Sat, 23 May 2026 09:32:37 -0700 Subject: [PATCH 3/3] Update CI to build against main only V3 SDK requires server main; 0.0.3 predates it. AI=CLAUDE Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 705fe76..14a9b38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,6 @@ jobs: strategy: matrix: vsql: - - "0.0.3" - "main" runs-on: ubuntu-latest steps: