-
Notifications
You must be signed in to change notification settings - Fork 18
preview capabilities #418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
preview capabilities #418
Changes from 2 commits
4fea333
fba36d9
555cade
4d2e91f
a6996b0
d625ec7
6ce9777
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| INSTALL EXTENSION vsql_preview_ping_test; | ||
| # ping() returns a positive integer | ||
| SELECT vsql_preview_ping_test.ping() > 0 AS ping_positive; | ||
| ping_positive | ||
| 1 | ||
| # Consecutive calls return strictly increasing values | ||
| SELECT vsql_preview_ping_test.ping() < vsql_preview_ping_test.ping() AS ping_increases; | ||
| ping_increases | ||
| 1 | ||
| UNINSTALL EXTENSION vsql_preview_ping_test; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| call mtr.add_suppression("required capability not registered"); | ||
| INSTALL EXTENSION vsql_preview_unknown_cap_test; | ||
| ERROR HY000: Failed to load VEF extension 'vsql_preview_unknown_cap_test': required capability not registered: vsql::nonexistent |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Test the preview ping capability for vsql_preview_ping_test extension. | ||
| # Verifies that the ping() VDF returns an incrementing counter, proving | ||
| # the server-side preview capability was populated correctly. | ||
|
|
||
| INSTALL EXTENSION vsql_preview_ping_test; | ||
|
|
||
| --echo # ping() returns a positive integer | ||
| SELECT vsql_preview_ping_test.ping() > 0 AS ping_positive; | ||
|
|
||
| --echo # Consecutive calls return strictly increasing values | ||
| SELECT vsql_preview_ping_test.ping() < vsql_preview_ping_test.ping() AS ping_increases; | ||
|
|
||
| UNINSTALL EXTENSION vsql_preview_ping_test; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Test that requesting an unknown preview capability fails at install time. | ||
| # The extension requests "vsql::nonexistent" which is never registered. | ||
| # INSTALL EXTENSION should fail with an error naming the missing capability. | ||
|
|
||
| call mtr.add_suppression("required capability not registered"); | ||
|
|
||
| --error ER_VILLAGESQL_GENERIC_ERROR | ||
| INSTALL EXTENSION vsql_preview_unknown_cap_test; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright (c) 2026 VillageSQL Contributors | ||
| // | ||
| // This program is free software; you can redistribute it and/or | ||
| // modify it under the terms of the GNU General Public License | ||
| // as published by the Free Software Foundation; either version 2 | ||
| // of the License, or (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License, version 2.0, for more details. | ||
| // | ||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program; if not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| #ifndef VILLAGESQL_ABI_PREVIEW_PING_H | ||
| #define VILLAGESQL_ABI_PREVIEW_PING_H | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| // Preview capability: "vsql::ping" | ||
| // | ||
| // A trivial capability used to exercise and test the preview capability | ||
| // registration system. The server provides a single ping() function that | ||
| // returns a monotonically incrementing counter. | ||
| // | ||
| // Capability name: VEF_PREVIEW_PING_NAME | ||
| // Capability version: VEF_PREVIEW_PING_VERSION | ||
|
|
||
| #define VEF_PREVIEW_PING_NAME "vsql::ping" | ||
| #define VEF_PREVIEW_PING_VERSION 1 | ||
|
|
||
| // Returns a monotonically incrementing counter. Used to verify that the | ||
| // capability system is wired up correctly end-to-end. | ||
| typedef uint64_t (*vef_ping_fn)(void); | ||
|
|
||
| typedef struct { | ||
| vef_ping_fn ping; | ||
| } vef_preview_ping_t; | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| #endif // VILLAGESQL_ABI_PREVIEW_PING_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,6 +173,12 @@ typedef enum : unsigned int { | |
| // + get_variable/set_variable/read_keyring/write_keyring | ||
| // function pointers in vef_register_arg_t: access to | ||
| // MySQL system variables and keyring component. | ||
| // + Preview capability system: extensions declare named | ||
| // capabilities they require in vef_registration_t; | ||
| // the server populates their function pointers before | ||
| // vef_register() returns. | ||
| // (vef_required_capability_t, required_capabilities, | ||
| // required_capability_count in vef_registration_t) | ||
| } vef_protocol_t; | ||
|
|
||
| // Max length of error messages in caller-provided buffers. | ||
|
|
@@ -872,6 +878,25 @@ typedef struct { | |
| }; | ||
| } vef_status_var_desc_t; | ||
|
|
||
| // A single capability request in vef_registration_t.required_capabilities. | ||
| // The extension sets name, version, and a receive callback. The server calls | ||
| // receive() with the vtable pointer if the capability is registered; the | ||
| // callback assigns it into the extension's struct in a type-safe way. | ||
| typedef struct { | ||
| // Capability name, e.g. "vsql::ping". Must remain valid for the lifetime | ||
| // of the extension (use a string literal). | ||
| const char *name; | ||
| // Version of the capability struct the extension was compiled against. | ||
| uint32_t version; | ||
| // Called by the server with the capability vtable pointer if registered. | ||
| // The extension assigns the vtable to its own capability struct. | ||
| void (*receive)(void *vtable); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need receive AND onload? Can't we just use 1?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. receive is internal plumbing, on_load is a user facing hook. Not used currently, but the thread PR will be using on_unload to clean up
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed |
||
| // Compile-time hash of the ABI struct type, computed via | ||
| // villagesql::detail::abi_type_hash<AbiType>(). The server compares this | ||
| // against its own hash for the same (name, version) to detect mismatches. | ||
| size_t abi_type_hash; | ||
| } vef_required_capability_t; | ||
|
|
||
| typedef struct { | ||
| // protocol >= VEF_PROTOCOL_1 | ||
| vef_protocol_t protocol; | ||
|
|
@@ -899,6 +924,15 @@ typedef struct { | |
| // protocol >= VEF_PROTOCOL_2 | ||
| unsigned int status_var_count; | ||
| vef_status_var_desc_t **status_vars; | ||
|
|
||
| // protocol >= VEF_PROTOCOL_2 | ||
| // Preview capabilities required by this extension. Each entry names a | ||
| // capability the extension needs (e.g. "vsql::ping"). The server populates | ||
| // the capability struct pointed to by each entry before vef_register() | ||
| // returns. If a capability is unavailable or the version is unsupported, | ||
| // its function pointers are left as nullptr. | ||
| unsigned int required_capability_count; | ||
| const vef_required_capability_t *required_capabilities; | ||
| } vef_registration_t; | ||
|
|
||
| // The returned objects can be freed when the registration is passed to the | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright (c) 2026 VillageSQL Contributors | ||
| // | ||
| // This program is free software; you can redistribute it and/or | ||
| // modify it under the terms of the GNU General Public License | ||
| // as published by the Free Software Foundation; either version 2 | ||
| // of the License, or (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program; if not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| #ifndef VILLAGESQL_DETAIL_CAPABILITY_HASH_H | ||
| #define VILLAGESQL_DETAIL_CAPABILITY_HASH_H | ||
|
|
||
| #include <cstddef> | ||
|
|
||
| namespace villagesql::detail { | ||
|
|
||
| // constexpr FNV-1a hash over a string. | ||
| constexpr size_t fnv1a_hash(const char *s, size_t len) { | ||
| size_t h = 14695981039346656037ULL; | ||
| for (size_t i = 0; i < len; ++i) | ||
| h = (h ^ static_cast<size_t>(s[i])) * 1099511628211ULL; | ||
| return h; | ||
| } | ||
|
|
||
| // Returns a compile-time hash of T's fully qualified type name via | ||
| // __PRETTY_FUNCTION__. Used to verify that the capability ABI struct type | ||
| // an extension was compiled against matches the type the server registered. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT - This doesn't include anything about the structure of the type, just the name; i.e. you could change the functions in the interface, and this would not change: > cat t.cc
#include <iostream>
#include <string_view>
template <typename T>
constexpr std::string_view abi_hash_this() {
return __PRETTY_FUNCTION__;
}
struct Foo {
int foo_A();
int foo_B(int);
};
int main() {
std::cout << abi_hash_this<Foo>() << std::endl;
return 0;
}
> clang++ t.cc -o t
> ./t
std::string_view abi_hash_this() [T = Foo]
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I asked Claude if there are C++ features that could help with this problem, C++26 seems nice, but I don't think we can use it yet:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a new proposal via Claude (though maybe this macros should have PREVIEW in the name?):
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. going with the sizeof(T) improvement — it catches the practically important cases (field additions/removals, type size changes) without external dependencies. Let me know if you think it is worth going further |
||
| template <typename T> | ||
| constexpr size_t abi_type_hash() { | ||
| const char *s = __PRETTY_FUNCTION__; | ||
| size_t len = 0; | ||
| while (s[len]) ++len; | ||
| return fnv1a_hash(s, len); | ||
| } | ||
|
|
||
| } // namespace villagesql::detail | ||
|
|
||
| #endif // VILLAGESQL_DETAIL_CAPABILITY_HASH_H | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit uneasy about adding yet another version field before we need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this the point of the preview feature? It is versioned independently of the rest of the ABI; I asked about this in your doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed version