Files
komp_ac/common/proto/table_script.proto

243 lines
11 KiB
Protocol Buffer

// common/proto/table_script.proto
syntax = "proto3";
package komp_ac.table_script;
// Manages column-computation scripts for user-defined tables and supplies the
// dependency data used by the client-side Steel runtime.
// Each script belongs to a single table (table_definition_id) and populates
// exactly one target column in that table. The server:
// - Validates script syntax (non-empty, balanced parentheses, starts with '(')
// - Validates the target column (exists, not a system column, allowed type)
// - Validates column/type usage inside math expressions
// - Validates referenced tables/columns against the schema
// - Enforces link constraints for structured access (see notes below)
// - Analyzes and records dependencies across the schema
// - Transforms the script to decimal-safe math (steel_decimal)
// - Upserts into table_scripts and records dependencies in script_dependencies
// - Hydrates external column and aggregate inputs requested by the client
//
// The client fetches stored scripts and their declared dependencies, builds a
// restricted Steel context, and executes scripts for immediate form feedback.
// Current-table values come from the client's active row snapshot. External
// values must come from HydrateScriptDependencies; the client must not query
// arbitrary database data from inside the Steel VM.
//
// Server-side persistence remains authoritative and recalculates affected rows.
// Script creation and update are transactional.
service TableScript {
// Create or update a script for a specific table and target column.
//
// Behavior:
// - Fetches the table by table_definition_id (must exist)
// - Validates "script" (syntax), "target_column" (exists and type rules),
// and all referenced tables/columns (must exist in same schema)
// - Validates math operations: prohibits using certain data types in math
// - Enforces link constraints for structured table access:
// • Allowed always: self-references (same table)
// • Structured access via steel_get_column
// requires an explicit link in table_definition_links
// - Rejects raw SQL access; steel_query_sql is not part of the supported DSL
// - Records dependencies for hydration and future graph analysis; circular
// graphs are allowed because scripts do not recursively trigger each other
// - Transforms the script to decimal-safe operations (steel_decimal)
// - UPSERTS into table_scripts on (table_definitions_id, target_column)
// and saves a normalized dependency list into script_dependencies
rpc PostTableScript(PostTableScriptRequest) returns (TableScriptResponse);
// Fetch all stored scripts for a specific table.
//
// Behavior:
// - Resolves the table from (profile_name, table_name)
// - Returns the stored, transformed script from table_scripts
// - Includes normalized dependency metadata from script_dependencies
// - Returns an empty scripts list when the table has no scripts
//
// Client use:
// - Registers each client-evaluable script with the computed-field runtime
// - Uses dependencies as the allowlist for values exposed to the Steel VM
// - Uses target_column_type to validate and convert the script result
rpc GetTableScripts(GetTableScriptsRequest) returns (GetTableScriptsResponse);
// Build the external data snapshot needed to execute a table's scripts in
// the client-side Steel runtime.
//
// The server derives the required inputs from stored script_dependencies;
// callers do not choose arbitrary tables or columns. Direct related-column
// reads and related aggregates are evaluated through SQLx in one read-only
// PostgreSQL snapshot. Returned values include logical type and currency
// metadata so the client can create correctly typed ScriptValues.
//
// Current-table column values are intentionally not returned. The client
// supplies those directly from row_data so unsaved edits participate in
// immediate calculations. The response replaces the client's previous
// script dependency cache as one complete hydration snapshot.
rpc HydrateScriptDependencies(HydrateScriptDependenciesRequest)
returns (HydrateScriptDependenciesResponse);
}
// Request to create or update a script bound to a specific table and column.
message PostTableScriptRequest {
// Required. The metadata ID from table_definitions.id that identifies the
// table this script belongs to. The table must exist; its schema determines
// where referenced tables/columns are validated and where dependencies are stored.
int64 table_definition_id = 1;
// Required. The target column in the target table that this script computes.
// Must be an existing user-defined column in that table (not a system column).
// System columns are reserved: "id", "deleted", "created_at", "row_revision".
// The column's data type must NOT be one of the prohibited target types:
// BIGINT, DATE, TIMESTAMPTZ
// Note: BOOLEAN targets are allowed (values are converted to Steel #true/#false).
string target_column = 2;
// Required. The script in the Steel DSL (S-expression style).
// Syntax requirements:
// - Non-empty, must start with '('
// - Balanced parentheses
//
// Referencing data:
// - Structured table/column access (enforces link constraints):
// (steel_get_column "table_name" "column_name")
// • current-table references are read directly from the active row
// • other tables require an explicit link from the source table
// (table_definition_links) or the request fails
// - Related collections use allowlisted aggregate shorthand:
// @sum(table.column), @min(table.column), @max(table.column),
// @count(table.column), @count_distinct(table.column),
// @any(table.boolean), @all(table.boolean),
// @count_rows(table via anchor), @exists(table via anchor)
// Every related aggregate requires the `via anchor` clause. The related
// table must be reachable through that anchor by an unambiguous FK path;
// the path may span multiple FK hops.
// - Raw SQL access is not supported; steel_query_sql is rejected
//
// Math operations:
// - The script is transformed by steel_decimal; supported math forms include:
// +, -, *, /, ^, **, pow, sqrt, >, <, =, >=, <=, min, max, abs, round,
// ln, log, log10, exp, sin, cos, tan
// - Columns of the following types CANNOT be used inside math expressions:
// BIGINT, TEXT, BOOLEAN, DATE, TIMESTAMPTZ
//
// Dependency tracking:
// - Dependencies are extracted from steel_get_column calls and stored
// in script_dependencies with context
// - Circular graphs are retained as metadata and do not block this request
string script = 3;
// Optional. Free-text description stored alongside the script (no functional effect).
string description = 4;
}
// Response after creating or updating a script.
message TableScriptResponse {
// The ID of the script record in table_scripts (new or existing on upsert).
int64 id = 1;
// Human-readable warnings concatenated into a single string. Possible messages:
// - Warning if the script references itself (may affect first population)
// - Info about number of structured linked-table accesses
// - Warning if many dependencies may affect performance
string warnings = 2;
}
message GetTableScriptsRequest {
// Required. Profile (schema) name.
string profile_name = 1;
// Required. Table name within the profile.
string table_name = 2;
}
message GetTableScriptsResponse {
// Scripts and dependency allowlists used to configure the client Steel runtime.
repeated StoredTableScript scripts = 1;
}
message StoredTableScript {
// Persistent script identifier.
int64 id = 1;
// Display-name key of the current-table field populated by this script.
string target_column = 2;
// Logical type used by the client to validate and convert the result.
string target_column_type = 3;
// Validated and transformed Steel expression executed by the client for
// immediate feedback and by the server for authoritative persistence.
string script = 4;
string description = 5;
// Complete allowlist of data inputs that may be exposed to this script.
repeated ScriptDependency dependencies = 6;
}
message ScriptDependency {
// Logical table name referenced by the script.
string target_table = 1;
// Normalized dependency kind, such as column_access or related_aggregate.
string dependency_type = 2;
// Logical column name. Empty for aggregates that operate on rows only.
string column = 3;
// Aggregate operation name, such as sum, count_rows, or exists; empty for
// column_access dependencies.
string operation = 4;
// Relationship table used to match the owner row to the related collection.
string via_table = 5;
}
// Identifies the active form row whose external Steel inputs must be hydrated.
message HydrateScriptDependenciesRequest {
// Required profile/database schema containing the scripted table.
string profile_name = 1;
// Required logical name of the table owning the scripts.
string table_name = 2;
// Persisted owner-row ID, or zero for a new unsaved row.
int64 row_id = 3;
// Complete current client form snapshot keyed by logical column name.
// It contains unsaved current-table values and related foreign keys such as
// "customer_id". The server uses it to resolve related rows and new-row
// aggregate semantics; it is not an arbitrary dependency request.
map<string, string> row_data = 4;
}
// One declared cross-table column input for the client Steel context.
message HydratedColumnValue {
// Logical related-table name used by steel_get_column.
string target_table = 1;
// Related row from which the value was loaded.
int64 row_id = 2;
// Logical column name used by steel_get_column.
string column = 3;
// String-encoded database value. An empty string represents NULL/empty input.
string value = 4;
// Logical database type used to create a typed client ScriptValue.
string field_type = 5;
// Related table's base currency for MONEY values; otherwise empty.
string base_currency = 6;
}
// One declared related-collection aggregate input for the client Steel context.
message HydratedAggregateValue {
// Normalized aggregate operation: sum, min, max, count, count_distinct,
// any, all, count_rows, or exists.
string operation = 1;
// Logical table whose related rows were aggregated.
string target_table = 2;
// Logical aggregated column; empty for count_rows and exists.
string column = 3;
// Relationship anchor used to distinguish aggregate dependency paths.
string via_table = 4;
// String-encoded aggregate result.
string value = 5;
// Logical source-column type; empty for row-only aggregates.
string field_type = 6;
// Aggregate table's base currency for MONEY values; otherwise empty.
string base_currency = 7;
}
// Complete external dependency snapshot for client-side Steel execution.
message HydrateScriptDependenciesResponse {
// Exact related-column inputs declared by stored scripts.
repeated HydratedColumnValue columns = 1;
// Exact related aggregate inputs declared by stored scripts.
repeated HydratedAggregateValue aggregates = 2;
}