Repository navigation
[Proposal]: MySQL Package & Extension Manager #144
Description
Pre-flight Checklist
- I have searched existing GitHub issues and did not find a duplicate proposal.
- I have removed or redacted sensitive information.
Primary Contact Name
Alex Gaetano Padula
Primary Contact Email
Company / Organization
TidesDB Corp.
Role
Founder, CEO & Chief Architect
Additional Authors / Contributors
No response
Component
Server
Target Release (Optional)
MySQL 26.7.0 and later
Roadmap Section
Extensibility/Ecosystem
Related Issues / Pull Requests / References (Optional)
No response
Executive Summary
MySQL has one of the broadest extension surfaces of any relational database with eleven and growing implemented
plugin interfaces, pluggable storage engines with real alternatives, loadable functions, and a
component service registry in which one component may re-implement another's service. It has no
distribution mechanism for any of it.
Installing an out-of-tree extension today requires the operator to obtain the MySQL source tree at
the exact version they are running, place the extension inside it, configure and build the whole
server, and copy the resulting shared object into plugin_dir by hand. This is not a gap in
tooling; it follows from three properties of the server:
- Storage engine plugins are bound to one exact server version.
MYSQL_HANDLERTON_INTERFACE_VERSIONis defined as(MYSQL_VERSION_ID << 8), and
sql_plugin.ccsets both the lower and upper bound of its check from it, so the comparison
collapses to equality. A plugin built against 26.7.0 is refused by 26.7.1 with "API version for
STORAGE ENGINE plugin is too different". Patch releases count. - No development headers ship with the server. An installed 26.7.0 prefix contains only client
headers (mysql_com.h,errmsg.h,field_types.h,mysql/). There is nohandler.hand no
mysql/plugin.h, so no extension of any kind can be compiled against an installed server. - Plugins can only be loaded from one directory, by bare filename.
INSTALL PLUGIN ... SONAMEpasses the library name throughcheck_valid_path(), which rejects
any string containing a directory separator.
This proposal adds the missing layer, a package format, a compatibility key that makes artifact
reuse safe, a resolver that prefers verified pre-built binaries and falls back to building from
source, and a SQL surface for installing, updating and removing packages. It also proposes the two
small server changes without which none of it is possible - shipping the plugin SDK headers, and
publishing the build identity an artifact must match.
Two findings shape this proposals design:
- Only three of the eleven plugin interfaces are version-locked (storage engine, daemon,
information schema). The other eight carry their own interface-version constants that change only
when that interface changes. A package of a portable kind can be published once and serve many
server releases, so the expensive per-release build matrix applies to a minority of packages. - MySQL's component loader is already scheme-dispatched and the scheme handler is itself a
replaceable service.file://is implemented,builtin://is reserved with no implementation,
and the loader resolves a scheme by acquiring the service nameddynamic_loader_scheme_<scheme>.
Apkg://scheme therefore requires no change to the server at all. This has been prototyped and
works.
User / Developer Stories
-
As a DBA deploying an alternative storage engine, I want to install it with one statement
against the server I already run, rather than reproducing the vendor's build environment. -
As a DBA planning a minor upgrade, I want to know before I upgrade which of my installed
extensions have no artifact for the new server version, because today a 26.7.0 >> 26.7.1 upgrade
silently invalidates every version-locked plugin and the server starts with tables it cannot open. -
As an extension author, I want to build against an installed server the way a PostgreSQL
extension author builds againstpg_config/PGXS, instead of distributing instructions that begin
"clone the MySQL source". -
As a security-conscious operator, I want extension installation to be an audited, separately
privileged action against an allow-listed registry with signature verification, rather than "copy
this.sointoplugin_diras root". -
As a platform integrator, I want a declared dependency graph, because real engines are not
self-contained - TideSQL linkslibtidesdband pins it by major version, and a manager that ignores
native dependencies has solved the easy half of the problem. -
As a MySQL maintainer, I want the extension ecosystem to grow without the server taking on
responsibility for third-party code, so the trust boundary and the privilege model must be explicit.
Proposed Scope
- A package manifest format (TOML) describing a package's identity, kind, server compatibility,
native dependencies, prebuilt artifacts and source build recipe. - A compatibility key - server version, platform, toolchain ABI, build type - that determines
whether a given artifact may be loaded into a given server. - A resolver that selects a version satisfying both the request and the server, prefers a verified
pre-built artifact, and falls back to building from a tagged source checkout. - Installation support for the extension kinds MySQL already has registries for:
plugins (mysql.plugin), loadable functions (mysql.func) and components (mysql.component). - A SQL surface with
INSTALL PACKAGE,UPDATE PACKAGE,REMOVE PACKAGE,SHOW PACKAGES,
SHOW PACKAGE STATUS. - A
mysql.packagesystem table recording, per installed package, the resolved version, the full
key it was installed for, the constraint originally requested, and the artifact digest. - A
pkg://dynamic loader scheme service for components. - Pre-upgrade compatibility reporting driven by
mysql.package. - Two server-side prerequisites - ship the plugin SDK headers and a CMake package; publish the build
identity (including the numeric version id and the ABI-affecting flag set).
Out of Scope / Future Work
- Operating a public registry (maybe on https://planet.mysql.com/)?. This specifies the client/server mechanism and the manifest format, not who hosts an index or funds a build farm.
- Packages that carry SQL objects. PostgreSQL extensions install SQL objects with versioned
upgrade scripts. That would make loadable-function and information-schema packages considerably
more useful and is a natural second phase, but it reopens the upgrade-script design question and
is deliberately excluded here. - Sandboxing the build helper beyond process isolation. A first version runs builds
out-of-process and unprivileged; container or seccomp confinement is future work. - Cross-version artifact compatibility. Nothing here attempts to create a stable
storage-engine ABI. The proposal works within the exact-version rule rather than changing it. - Automatic rebuild on server upgrade. The proposal reports and stages; it does not silently
rebuild extensions during an upgrade. - Windows and macOS packaging specifics. The design is platform-neutral and the key has a
platform field, but only Linux x86-64 has been exercised in prototypes.
References
| Source | What it establishes |
|---|---|
include/mysql/plugin.h:665 |
MYSQL_HANDLERTON_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) |
include/mysql/plugin.h:115-127 |
The twelve plugin type slots; slot 0 (UDF) unimplemented |
sql/sql_plugin.cc:404-425,1063 |
Per-type version bounds; the "API version … too different" check |
sql/sql_plugin.cc:569,664 |
check_valid_path() rejecting directory separators |
components/libminchassis/dynamic_loader.cc:160-205 |
URN scheme://path; file:// and reserved builtin:// |
components/libminchassis/dynamic_loader.cc (get_scheme_service_from_urn) |
Scheme resolved as dynamic_loader_scheme_<scheme> |
components/libminchassis/dynamic_loader_scheme_file.cc:160-168 |
The duplicate-library guard a scheme must implement |
components/libminchassis/registry.cc:256 |
set_default() a component may re-implement another's service |
include/mysql/components/services/dynamic_loader.h:282-303 |
The dynamic_loader_scheme service definition |
scripts/mysql_system_tables.sql:195,203,361 |
mysql.func, mysql.plugin, mysql.component |
sql/sys_vars.cc:5147-5155 |
version_compile_machine, version_compile_os |
Functional Requirements
| # | Requirement |
|---|---|
| F1 | A package manifest shall declare name, version, kind, license, server compatibility, native dependencies, zero or more prebuilt artifacts keyed by compatibility key, and an optional source build recipe. |
| F2 | The resolver shall compute the running server's compatibility key from the server itself, not from configuration. |
| F3 | The resolver shall select the highest package version satisfying both the operator's constraint and the server's key; latest shall mean latest compatible, not latest published. |
| F4 | For a version-locked kind the compatibility specification shall be an exact server version; for a portable kind it shall be a range. |
| F5 | Where a prebuilt artifact matches the key, its digest shall be verified before use, and its manifest signature shall be verified against configured trust roots. |
| F6 | Where no prebuilt artifact matches and source fallback is permitted, the resolver shall obtain the declared source at the declared tag and run the manifest's named build recipe. |
| F7 | Installation shall dispatch by kind INSTALL PLUGIN for plugins and storage engines, CREATE FUNCTION … SONAME for loadable functions, INSTALL COMPONENT for components. |
| F8 | mysql.package shall record resolved version, full key, requested constraint, and artifact digest for every installed package. |
| F9 | UPDATE PACKAGE shall re-resolve under the recorded constraint unless the operator supplies a new one, so an update cannot cross a major boundary implicitly. |
| F10 | The prior artifact shall be retained across an update so a failed load can be rolled back to a known-good file. |
| F11 | Removing a storage engine package shall be refused while tables in that engine exist. |
| F12 | A pre-upgrade check shall resolve every row of mysql.package against a nominated target server version and report packages with no matching artifact. |
| F13 | A failed resolution shall report the compatibility key that was sought and the keys that exist, not merely "not found". |
| F14 | Native dependencies declared in [requires] shall be resolved, and a missing or incompatible dependency shall fail the install before anything is staged. |
Non-functional Requirements
| # | Requirement |
|---|---|
| N1 | No implicit network access. The registry allow-list shall be empty by default; a server never configured for packages shall not reach the network. |
| N2 | No unsigned code. An unsigned package or an unknown signer shall be refused, with no override option. |
| N3 | Separate privilege. Package installation shall require its own dynamic privilege, not SUPER and not INSTALL PLUGIN, default-granted to nobody. |
| N4 | No compilation inside mysqld. Source builds shall run in a separate unprivileged process with no database access. |
| N5 | Auditability. Every install, update and removal shall emit an audit event carrying the resolved key and artifact digest. |
| N6 | Failure containment. A failed build or a failed load shall leave the server running and the previous state intact. |
| N7 | No new steady-state cost. Resolution runs only on explicit operator action; there shall be no background polling or periodic registry contact. |
| N8 | Determinism. Given the same manifest, key and digest, installation shall be reproducible. |
Impact Areas
- SQL syntax or statements
- Configuration options or system variables
- Command-line options or utilities
- User-visible behavior
- Observability
- Security or privilege model
- Protocol or replication behavior
- Upgrade / downgrade compatibility
- Performance or resource usage
- Files, persistence, or metadata formats
- APIs or internal interfaces
- Testing or QA coverage needs
Summary of the Approach
An extension artifact is addressed by a compatibility key with four fields:
tidesdb/2.0.0 @ mysql-26.7.0 / linux-x86_64 / gcc13-cxx11abi / release
└─ server ─┘ └─ platform ─┘ └─ toolchain ─┘ └ build ┘
Each field corresponds to a demonstrated failure mode rather than defensive over-specification:
| Field | Why it is in the key |
|---|---|
server |
Exact MYSQL_VERSION_ID for version-locked kinds; a declared range for portable kinds. |
platform |
OS and architecture. |
toolchain |
C++ ABI generation and symbol floor. A plugin built on a current Ubuntu requires GLIBC_2.34 and will not load on RHEL 8 however correct its server version. |
build |
Release vs debug. NDEBUG changes server struct layouts the extension compiles against. |
Resolution prefers a verified prebuilt artifact and falls back to a source build. The server drives
the operation through SQL; the work of fetching, verifying and building is delegated to a separate
process. Installed state lives in mysql.package, which is what makes updates and pre-upgrade
checks possible.
User Interface
-- install
INSTALL PACKAGE 'tidesdb'; -- latest compatible with this server
INSTALL PACKAGE 'tidesdb' VERSION '2.0.0'; -- exact
INSTALL PACKAGE 'tidesdb' VERSION '^2.0'; -- range
INSTALL PACKAGE 'tidesdb' FROM 'vendor-registry';
-- update, re-resolving under the recorded constraint
UPDATE PACKAGE 'tidesdb';
UPDATE PACKAGE 'tidesdb' VERSION '2.1.0'; -- move the constraint deliberately
-- remove
REMOVE PACKAGE 'tidesdb';
-- inspect
SHOW PACKAGES;
SHOW PACKAGE STATUS 'tidesdb';
SELECT * FROM mysql.package;Components additionally become installable through the existing loader with no new syntax, by
registering a scheme service:
INSTALL COMPONENT 'pkg://some_component@1.2';Configuration / Knobs
| Variable | Scope | Default | Purpose |
|---|---|---|---|
package_registry_allowlist |
Global, dynamic | empty | Registries the server may contact. Empty means packages are disabled. |
package_trust_roots |
Global, startup | empty | Public keys that may sign manifests and artifacts. Never read from a registry. |
package_store_dir |
Global, startup | <basedir>/pkg |
Where verified component artifacts are staged. |
package_allow_source_build |
Global, dynamic | OFF |
Whether the source fallback may run on this host. |
package_build_timeout |
Global, dynamic | 600 s | Upper bound on a source build. |
New read-only variables publishing the build identity (prerequisite, see §5):
| Variable | Purpose |
|---|---|
version_id |
The numeric MYSQL_VERSION_ID the plugin loader actually compares. Not currently exposed - only @@version as a string, which may carry a suffix. |
build_toolchain |
Compiler and C++ ABI tag the server was built with. |
build_type |
release / debug. |
sdk_cxxflags, sdk_ldflags |
The ABI-affecting flag set an extension must be built with. |
Observability
mysql.package- one row per installed package >> name, resolved version, kind, compatibility
key, requested constraint, artifact digest, install timestamp, source (binaryorsource).SHOW PACKAGE STATUS <name>- resolved version against available versions, whether an update
is available under the recorded constraint, and whether a staged-but-pending artifact exists.- Audit events - one per install, update and removal, carrying resolved key and digest, so
extension installation is reviewable after the fact. - Error log - resolution failures name the key sought and the keys available; build failures
include the helper's captured output, truncated. - Performance Schema (optional) - stages for
resolve,fetch,verify,build,stage,
load, so a slow install is attributable.
User Procedure
Installing an engine on a server that has a matching prebuilt artifact:
mysql> SET GLOBAL package_registry_allowlist = 'https://pkg.example';
mysql> GRANT INSTALL_PACKAGE ON *.* TO 'dba'@'localhost';
mysql> INSTALL PACKAGE 'tidesdb';
mysql> SELECT * FROM mysql.package;
mysql> CREATE TABLE t (id INT PRIMARY KEY) ENGINE=TidesDB;Preparing for a server upgrade:
mysql> SHOW PACKAGE STATUS FOR SERVER '26.7.1';
+---------+---------+------------------+--------------------------------+
| package | version | target_available | note |
+---------+---------+------------------+--------------------------------+
| tidesdb | 2.0.0 | NO | no artifact for mysql-26.7.1 |
| connlog | 1.0.0 | YES | portable kind, range satisfied |
+---------+---------+------------------+--------------------------------+The operator stages the new-key artifacts before upgrading, so the upgraded server finds its
extensions already present rather than starting with unloadable plugins.
Security Considerations
Server-driven installation is the most useful shape and the most dangerous. It gives mysqld
outbound network access and, on the source path, a compiler. Four containments, all mandatory:
- Trust. Manifests and artifacts are signed; the server verifies against trust roots configured
at startup, never material supplied by the registry. An unsigned or unknown-signer package is
refused with no override flag - an override becomes the documented workaround. - Privilege.
INSTALL_PACKAGEis its own dynamic privilege, default-granted to nobody, and is
not implied bySUPERorINSTALL PLUGIN. Installing a package is strictly more dangerous than
loading a file an administrator already placed on the host. - Network.
package_registry_allowlistis empty by default. There is no implicit default
registry, so a server that is never configured for packages cannot be induced to fetch anything. - Build isolation. The source path never compiles inside
mysqld.mysql_package_buildruns
as a separate unprivileged process with no database connection, a scratch directory, and the
named recipe from the manifest. On hosts with no toolchain the helper is absent and the source
path reports that plainly.
Residual risks that should be stated rather than designed away, a signed package is still arbitrary
code in the server's address space; the registry operator is a trusted party; and the build helper
executes a recipe from a manifest, which is why package_allow_source_build defaults off and why a
binary-only first version is a reasonable scope reduction.
Compatibility and Behavior Changes
- No change to existing extension loading.
INSTALL PLUGIN,CREATE FUNCTION … SONAMEand
INSTALL COMPONENT 'file://…'behave exactly as before. - New system table.
mysql.packagerequires a dictionary upgrade step. A downgrade leaves the
table present and unused. - The plugin SDK becomes a published interface. This is the significant new commitment, once
headers ship, their contents become something third parties build against. The SDK should
therefore carry its own version and an explicit stability statement - in particular that it offers
no cross-version guarantee for the version-locked kinds, matching the loader's existing rule. - Replication. Unaffected; package state is local to a server.
- Upgrade hazard addressed, not introduced. A server version change already invalidates every
version-locked extension. This proposal makes that visible and recoverable instead of leaving it
to be discovered at startup.
Block Diagram
flowchart TD
A["INSTALL PACKAGE 'tidesdb'"] --> B["Privilege check: INSTALL_PACKAGE"]
B --> C["Read compatibility key from this server"]
C --> D["Resolve version: constraint AND server compat"]
D --> E["Verify manifest signature against trust roots"]
E --> F["Resolve native dependencies from [requires]"]
F --> G{"Prebuilt artifact<br/>for this key?"}
G -- yes --> H["Verify sha256"]
G -- no --> I{"package_allow_source_build?"}
I -- no --> J["Fail: report key sought + keys available"]
I -- yes --> K["mysql_package_build:<br/>unprivileged, no DB access"]
K --> L["Run named recipe against SDK"]
L --> H
H --> M["Stage into plugin_dir or package_store_dir"]
M --> N{"kind"}
N -- "plugin / storage_engine" --> O["INSTALL PLUGIN"]
N -- loadable_function --> P["CREATE FUNCTION ... SONAME"]
N -- component --> Q["INSTALL COMPONENT 'pkg://name'"]
O --> R["Record in mysql.package + audit event"]
P --> R
Q --> R
Interface Specification
Manifest format. TOML, one file per package version.
[package]
name = "tidesdb"
version = "2.0.0"
kind = "storage_engine" # selects the version-locking rule
plugin_name = "TidesDB" # what INSTALL PLUGIN registers
soname = "ha_tidesdb.so"
license = "GPL-2.0"
maturity = "beta"
[compat]
server = "=26.7.0" # exact storage engines are version-locked
# an audit plugin would write server = ">=8.0.0"
[requires]
libtidesdb = "^10.1.1" # native dependency, major pinned
[[binary]]
key = "mysql-26.7.0/linux-x86_64/gcc13-cxx11abi/release"
url = "https://pkg.example/tidesdb/2.0.0/…"
sha256 = "…"
# for multiplatform / architecture
[[binary]]
key = "mysql-26.7.0/linux-aarch64/gcc13-cxx11abi/release"
url = "https://pkg.example/tidesdb/2.0.0/linux-aarch64.so"
sha256 = "…"
[[binary]]
key = "mysql-26.7.0/macos-arm64/clang17-libc++/release"
url = "…"
key = "mysql-26.7.0/macos-arm64/clang17-libc++/release"
url = "…"
sha256 = "…"
[[binary]]
key = "mysql-26.7.0/windows-x64/msvc193/release"
url = "…"
sha256 = "…"
[source]
git = "https://github.com/tidesdb/tidesql-mysql"
tag = "v2.0.0"
build = "./install.sh --rebuild-plugin --mysql-src ${MYSQL_SDK_SRC}"
produces = "ha_tidesdb.so"kind is what lets the resolver apply the right version rule without the manifest restating it.
[source] is a named recipe, not an arbitrary shell string, so the build helper has something it
can constrain.
mysql.package system table.
| Column | Type | Meaning |
|---|---|---|
name |
varchar(64) PK |
Package name |
version |
varchar(32) |
Resolved version |
kind |
varchar(32) |
storage_engine, plugin, loadable_function, component |
compat_key |
varchar(255) |
Full key the artifact was installed for |
constraint_spec |
varchar(64) |
Constraint originally requested |
artifact_sha256 |
char(64) |
Digest of the staged artifact |
origin |
enum('binary','source') |
How it was acquired |
installed_at |
timestamp |
Install time |
pending_version |
varchar(32) NULL |
Staged update awaiting restart |
The pkg:// scheme service. No core change is required. The loader resolves a scheme by name:
/* components/libminchassis/dynamic_loader.cc, get_scheme_service_from_urn */
size_t scheme_end = urn.find("://");
my_string scheme(urn.begin(), urn.begin() + scheme_end);
my_service<SERVICE_TYPE(dynamic_loader_scheme)> service(
(my_string("dynamic_loader_scheme_") + scheme).c_str(),
&imp_mysql_minimal_chassis_registry);A component providing dynamic_loader_scheme_pkg therefore extends a running server. The service
name is declared as an alias of the service type, exactly as the built-in file scheme does:
typedef SERVICE_TYPE_NO_CONST(dynamic_loader_scheme)
SERVICE_TYPE_NO_CONST(dynamic_loader_scheme_pkg);
BEGIN_SERVICE_IMPLEMENTATION(pkg_scheme, dynamic_loader_scheme_pkg)
pkg_load, pkg_unload END_SERVICE_IMPLEMENTATION();The scheme contract carries an invariant the service definition does not express. The
definition is onlyloadandunload, but a scheme implementation must refuse to load the
same library twice. The built-in file scheme guards on the address of the library's
list_componentsentry point (dynamic_loader_scheme_file.cc:160-168); without that guard the
same component is registered twice and the second unload frees state the first still references.
A first prototype that omitted it crashed the server with SIGSEGV. If the scheme mechanism is to
be opened to third parties, this contract must be specified, or a helper supplied that enforces
it.
The plugin SDK. Measured from the compiler's own dependency output, not estimated:
storage engine header closure 366 headers, 5.1 MB
include/ 188 sql/ 97 libs/ 69 sql-common/ 4 extra/ 4 vector-common/ 1
generated 3 - my_config.h, mysql_version.h, mysqld_error.h (configuration-specific)
+ component service headers +161 headers
union across extension kinds 527 headers
Three properties of the SDK follow:
libmysqlservices.aalready ships. It suppliesmysql_malloc_serviceand
plugin_registry_service, and is already installed into the prefix. Only the headers are
missing, which makes this ask smaller than it appears.- The flag set is part of the ABI. A complete engine compiled and linked cleanly but would not
load until built with the server's own-ftls-model=initial-exec,-D_FILE_OFFSET_BITS=64and
-Wl,-Bsymbolic-functions. Publishing a toolchain tag is insufficient; the flags themselves
must be published. - The SDK must be a union, not a sample. The storage-engine closure appeared to be a superset -
it already containedplugin_audit.h,udf_registration_types.hand
component_implementation.h- yet a component failed to compile, because a storage engine never
includes anycomponents/services/*.h.
Proposed Implementation Plan
The ordering is chosen so that each phase is independently useful and the riskiest work comes last.
| Phase | Work item | Server change | Status |
|---|---|---|---|
| 1 | Ship the plugin SDK, 527 headers, the generated configuration headers, and a CMake package for out-of-tree builds. Publish the SDK's own version and stability statement. | Install rules + packaging | Prerequisite; validated by building a complete storage engine, an audit plugin, a loadable function and a component against a staged SDK with no source tree |
| 2 | Publish the build identity with numeric version_id, toolchain tag, build type, and the ABI-affecting compile/link flag set as read-only variables. |
New read-only sysvars | Gap confirmed - no numeric version id is currently exposed |
| 3 | pkg:// dynamic loader scheme service, shipped as a component, plus a written scheme contract or an enforcement helper. |
None | Prototyped and working; a component installed through pkg:// appears in mysql.component |
| 4 | mysql.package system table, the INSTALL_PACKAGE dynamic privilege, audit events, and the configuration variables. |
Dictionary + privilege + sysvars | Designed |
| 5 | Parser and runtime for INSTALL/UPDATE/REMOVE PACKAGE and SHOW PACKAGE[S], binary path only (verified prebuilt artifacts). |
Parser + runtime | Resolution logic prototyped client-side |
| 6 | mysql_package_build helper and the source fallback, behind package_allow_source_build=OFF. |
New helper binary | Prototyped client-side - git clone at tag >> build against SDK >> stage >> install |
| 7 | Pre-upgrade compatibility reporting (SHOW PACKAGE STATUS FOR SERVER). |
Runtime | Designed |
QA Notes
Correctness and refusal paths
| Scenario | Expected |
|---|---|
| Install a version-locked package whose artifact key names a different server patch release | Refused before staging, naming the key sought and the keys available |
| Artifact digest does not match the manifest | Refused; nothing staged |
Manifest unsigned, or signed by a key not in package_trust_roots |
Refused; no override path exists |
Registry not in package_registry_allowlist |
Refused without network contact |
package_allow_source_build=OFF and no binary matches |
Refused with a clear reason, not a silent build |
| Source build exits non-zero, times out, or produces no artifact | Statement fails; server unaffected; previous state intact |
REMOVE PACKAGE for a storage engine with live tables |
Refused - requires asking the engine, not the file system |
UPDATE PACKAGE where the newest version violates the recorded constraint |
No update performed; reported as already current |
| Update whose new artifact fails to load | Rolls back to the retained previous artifact |
| Install a package already installed | Idempotent or clearly refused; never double-registered |
Scheme-specific
| Scenario | Expected |
|---|---|
Same library loaded via file:// and pkg:// |
Second load refused by the duplicate-library guard |
pkg:// URN naming an absent artifact |
Clean failure, no crash |
UNINSTALL COMPONENT then reinstall through the same URN |
Succeeds; no stale registration |
Scheme component unloaded while a pkg:// component is loaded |
Defined behaviour required; currently undefined |
Cross-version and upgrade
| Scenario | Expected |
|---|---|
| Start a server one patch release newer with version-locked packages installed | Pre-upgrade check lists them; post-upgrade repair path reports them rather than failing silently |
Staged pending_version applied at restart |
New artifact loads; mysql.package updated |
Downgrade below the version that created mysql.package |
Table unused; no errors |
Automation opportunities
- The refusal matrix above is mostly table-driven and suits
mysql-test-runwith a fixture registry
served from the test's own directory, no network needed. - Cross-version refusal can be tested without a second server build by publishing a manifest whose
artifact key names a fabricated version; the loader's check is what is under test. - The duplicate-library guard deserves a dedicated crash-regression test, since it is an invariant
a third-party scheme must honour and a prototype has already violated it. - Digest and signature failures should be tested with deliberately corrupted artifacts rather than
mocks, since the whole value of the key is that it is checked against reality.
Known gaps in current validation
- Only Linux x86-64 has been exercised. The platform and toolchain fields of the key are designed but
untested on macOS and Windows. - Signature verification is specified but not implemented in the prototype; only digests are checked.
- The server-driven surface (
INSTALL PACKAGE) is designed and not implemented; the prototype drives
installation from a client, which is the same resolution logic in a different process. libtidesdb-style native dependency resolution is declared in the manifest format and checked for
presence only; version-range satisfaction across a dependency graph is not implemented.
Activity