Conversation
Tables can now carry consumer-facing metadata, decoupled from their storage
definition: an MCP descriptor (how a downstream MCP server should surface the
table) and named query presets (predefined filter sets). Extensions are typed
and validated against the live table schema.
Storage is a `tables://<name>/extensions.json` sidecar, separate from
`table.json`, so extensions apply uniformly to every table type, can be edited
without rebuilding the provider, survive provider re-registration (MV refresh,
Iceberg alter), and are removed automatically on DROP TABLE.
Manage via SQL:
SET EXTENSION '<kind>' FOR <table> TO '<json>'
DROP EXTENSION '<kind>' FOR <table>
SHOW EXTENSIONS FOR <table>
or REST: public GET /api/table-extensions; admin PUT/DELETE
/api/admin/table-extensions/{name}. All OpenAPI-documented.
Validation rejects unknown columns, unsupported operators, malformed
between/in values, and duplicate preset names.
Tests: extension validation + parser unit tests, a persistence round-trip and
cleanup test, and an end-to-end runtime test (CREATE TABLE -> SET/SHOW/DROP
EXTENSION -> validation rejection).
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a typed, consumer-facing “table extensions” document (MCP descriptor + query presets) stored as a tables://<name>/extensions.json sidecar, and wires it through SQL DDL, runtime APIs, and REST endpoints with schema-aware validation on writes.
Changes:
- Add object-store persistence helpers for an
extensions.jsonsidecar alongside existingtable.jsonpersistence. - Implement typed
TableExtensions(MCP + preset) contracts with schema validation, plus SQLSET/DROP EXTENSIONandSHOW EXTENSIONSplanning/execution. - Expose extensions via REST (public read + admin write/delete) and add unit/integration coverage.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| beacon-data-lake/src/table_runtime/schema_persistence.rs | Adds persist/load/remove helpers for the extensions.json sidecar + round-trip/cleanup test. |
| beacon-data-lake/src/lib.rs | Re-exports SchemaPersistenceService for downstream use. |
| beacon-core/src/statement_plan/query_planner.rs | Adds physical planning for new extension logical nodes. |
| beacon-core/src/statement_plan/physical.rs | Adds physical exec nodes for SET/DROP EXTENSION and SHOW EXTENSIONS. |
| beacon-core/src/statement_plan/mod.rs | Adds logical plan constructors for extension DDL statements. |
| beacon-core/src/statement_plan/logical.rs | Defines logical nodes and DF schema for SHOW EXTENSIONS. |
| beacon-core/src/runtime.rs | Adds runtime APIs for get/set/delete extensions + end-to-end SQL round-trip test. |
| beacon-core/src/parser/statement.rs | Adds statement types + Display for extension DDL and SQL literal escaping helper. |
| beacon-core/src/parser/beacon_parser.rs | Parses SET/DROP EXTENSION and SHOW EXTENSIONS without shadowing standard SQL. |
| beacon-core/src/lib.rs | Exposes new extensions module. |
| beacon-core/src/extensions.rs | Implements typed contracts, validation, persistence read/modify/write, and SHOW output batch. |
| beacon-core/src/api.rs | Re-exports extension types for OpenAPI/REST contracts. |
| beacon-api/src/axum/client/tables.rs | Adds public GET /api/table-extensions endpoint. |
| beacon-api/src/axum/client/mod.rs | Registers the new client route. |
| beacon-api/src/axum/admin/mod.rs | Registers new admin extensions routes. |
| beacon-api/src/axum/admin/extensions.rs | Adds admin PUT/DELETE endpoints for extensions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+185
to
+195
| match state.get_table_extensions(query.table_name.clone()).await { | ||
| Ok(extensions) => Ok(Json(extensions)), | ||
| Err(error) => { | ||
| tracing::error!(?error, "error listing table extensions"); | ||
| Err(( | ||
| StatusCode::NOT_FOUND, | ||
| format!("Table {} not found", query.table_name), | ||
| )) | ||
| } | ||
| } | ||
| } |
Comment on lines
+171
to
+175
| responses( | ||
| (status = 200, description = "The table's extensions", body = TableExtensions), | ||
| (status = 404, description = "Table not found"), | ||
| ), | ||
| security( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds typed, consumer-facing table extensions — metadata about how to use a table, decoupled from its storage definition and from format
options:mcp— descriptor for how a downstream MCP server should surface the table as a tool/resource (enabled,tool_name,description,exposed_columns).preset— named, predefined filter sets consumers can apply downstream (each a list of{column, op, value}filters).Both are typed and validated against the live table schema on every write.
Why
Tables previously exposed only structural metadata (Arrow schema) and storage config. There was no place to attach annotations for downstream tools. Stuffing them into
optionsis wrong — that's reserved for format options. Extensions are orthogonal annotations.Storage
A decoupled
tables://<name>/extensions.jsonsidecar, separate fromtable.json. This means extensions:DROP TABLE(the table directory is deleted).No change to any
TableDefinitionstruct.Interfaces
SQL DDL (distinct leading keywords; no collision with standard SQL):
REST:
GET /api/table-extensions?table_name=XPUT/DELETE /api/admin/table-extensions/{name}All endpoints and schemas are OpenAPI-documented.
Validation
Rejects unknown columns, unsupported operators (allowed:
= != < <= > >= between in), malformedbetween/invalues, and duplicate preset names.Tests
SET/DROP EXTENSIONandSHOW EXTENSIONS(incl. Display round-trip and non-shadowing of standard SQL).DROP TABLE).CREATE TABLE→SET/SHOW/DROP EXTENSION→ validation rejection.cargo build,cargo clippy(no new warnings), and all affected-crate tests pass.