// common/proto/table_structure.proto syntax = "proto3"; package komp_ac.table_structure; import "common.proto"; // Introspects the physical PostgreSQL tables for one or more logical tables // (defined in table_definitions) and returns their column structures. // The server validates that: // - The profile (schema) exists in `schemas` // - Every table is defined for that profile in `table_definitions` // It then queries information_schema for the physical tables and returns // normalized column metadata. service TableStructureService { // Return the physical column list (name, normalized data_type, // nullability, primary key flag) for one or more tables in a profile. // // Behavior: // - NOT_FOUND if profile doesn't exist in `schemas` // - NOT_FOUND if any table is not defined for that profile in `table_definitions` // - Queries information_schema.columns ordered by ordinal position // - Normalizes data_type text (details under TableColumn.data_type) // - Returns an error if any validated table has no visible columns in // information_schema (e.g., physical table missing) rpc GetTableStructure(GetTableStructureRequest) returns (GetTableStructureResponse); // Describes the public values accepted by staged table-data import. Unlike // GetTableStructure, this is an input contract: it includes logical types, // omission/null behavior, stable identities and link targets. rpc GetTableImportDescriptor(GetTableImportDescriptorRequest) returns (GetTableImportDescriptorResponse); } // Request identifying the profile (schema) and tables to inspect. message GetTableStructureRequest { // Required. Profile (PostgreSQL schema) name. Must exist in `schemas`. string profile_name = 1; // Required. Table names within the profile. Each must exist in // `table_definitions` for the given profile. The physical tables are then // introspected via information_schema. repeated string table_names = 2; } // Batched response keyed by table name. message GetTableStructureResponse { // Per-table physical column lists keyed by requested table name. map table_structures = 1; } // Response with the ordered list of columns (by ordinal position) for one table. message TableStructureResponse { // Columns of the physical table, including system columns (id, deleted, // created_at, row_revision), user-defined columns, and any foreign-key columns such as // named by whoever declared each link, plus the dedicated "account" on // ACCOUNTING-enabled tables. May be empty if the physical table is missing. repeated TableColumn columns = 1; } // One physical column entry as reported by information_schema. message TableColumn { // Public column name. Physical account_id columns are exposed as account. string name = 1; // Normalized data type string derived from information_schema: // - VARCHAR(n) when udt_name='varchar' with character_maximum_length // - CHAR(n) when udt_name='bpchar' with character_maximum_length // - NUMERIC(p,s) when udt_name='numeric' with precision and scale // - NUMERIC(p) when udt_name='numeric' with precision only // - [] for array types (udt_name starting with '_', e.g., INT[] ) // - Otherwise UPPER(udt_name), e.g., TEXT, BIGINT, TIMESTAMPTZ // Examples: "TEXT", "BIGINT", "VARCHAR(255)", "TIMESTAMPTZ", "NUMERIC(14,4)" string data_type = 2; // True if information_schema reports the column as nullable. bool is_nullable = 3; // True if the column is part of the table's PRIMARY KEY. // Typically true for the "id" column created by the system. bool is_primary_key = 4; // True when this column is projected from the profile quantity ledger. bool quantity_ledger = 5; // True when the server created this as a companion of another logical column. bool generated = 6; // True when clients must not submit edits for this column. bool read_only = 7; // Logical source column name, empty for ordinary and system columns. string generated_from = 8; // True when clients may offer this column in an alias rename picker. bool renameable = 9; // Stable public identity of a managed user column. Zero for system columns. int64 column_id = 10; } message GetTableImportDescriptorRequest { string profile_name = 1; string table_name = 2; } enum ImportValueKind { IMPORT_VALUE_KIND_UNSPECIFIED = 0; IMPORT_VALUE_KIND_NULL = 1; IMPORT_VALUE_KIND_STRING = 2; IMPORT_VALUE_KIND_BOOLEAN = 3; IMPORT_VALUE_KIND_NUMBER = 4; IMPORT_VALUE_KIND_INTEGER_STRING = 5; IMPORT_VALUE_KIND_LINK_DISPLAY = 6; } message ImportLinkDescriptor { int64 target_table_id = 1; string target_profile_name = 2; string target_table_name = 3; repeated string target_row_display_columns = 4; } message ImportFieldDescriptor { // Stable identity of a managed user column. System columns have zero here // and are identified by their server-owned name. int64 column_id = 1; string name = 2; string logical_type = 3; string storage_type = 4; bool writable = 5; bool required = 6; bool may_omit = 7; bool accepts_null = 8; bool system = 9; bool generated = 10; string generated_from = 11; optional ImportLinkDescriptor link = 12; repeated ImportValueKind accepted_value_kinds = 13; bool account_reference = 14; } message GetTableImportDescriptorResponse { string requested_profile_name = 1; string storage_profile_name = 2; string table_name = 3; int64 table_id = 4; // Version of this import contract. Send it as expected_table_revision on // every staged chunk so schema/rule changes refuse stale prepared data. int64 table_revision = 5; repeated ImportFieldDescriptor fields = 6; }