multiFK2
This commit is contained in:
2
client
2
client
Submodule client updated: 8538beba2a...da1557a2d4
@@ -53,16 +53,18 @@ message PostTableDefinitionRequest {
|
|||||||
// Table name to create inside the target profile.
|
// Table name to create inside the target profile.
|
||||||
// Must be lowercase, alphanumeric with underscores,
|
// Must be lowercase, alphanumeric with underscores,
|
||||||
// start with a letter, and be <= 63 chars.
|
// start with a letter, and be <= 63 chars.
|
||||||
// Forbidden names: "id", "deleted", "created_at", "row_revision", or ending in "_id".
|
// Forbidden names: "id", "deleted", "created_at", "row_revision" -- the
|
||||||
|
// system columns, which share one namespace with table names wherever the
|
||||||
|
// two are named side by side. The "_id" suffix is allowed.
|
||||||
string table_name = 1;
|
string table_name = 1;
|
||||||
|
|
||||||
// List of user-defined columns (adds to system/id/fk columns).
|
// The table's columns, including its links. System columns are added on top.
|
||||||
repeated ColumnDefinition columns = 3;
|
repeated ColumnDefinition columns = 3;
|
||||||
|
|
||||||
// List of column names to be indexed (must match existing user-defined columns).
|
// Column names to index, matching names declared above. System columns
|
||||||
// Indexes can target only user-defined columns; system columns ("id", "deleted",
|
// ("id", "deleted", "created_at", "row_revision") already have indexes, and
|
||||||
// "created_at", "row_revision") and automatically generated foreign key ("*_id") columns already
|
// a LINK column is indexed when it is created. Requests naming either are
|
||||||
// have indexes. Requests trying to index those columns are rejected.
|
// rejected.
|
||||||
repeated string indexes = 4;
|
repeated string indexes = 4;
|
||||||
|
|
||||||
// Name of profile (Postgres schema) where the table will be created.
|
// Name of profile (Postgres schema) where the table will be created.
|
||||||
@@ -142,9 +144,13 @@ enum MoneyRounding {
|
|||||||
|
|
||||||
// Describes one user-defined column for a table.
|
// Describes one user-defined column for a table.
|
||||||
message ColumnDefinition {
|
message ColumnDefinition {
|
||||||
// Column name that follows the same validation rules as table_name.
|
// Must be lowercase, start with a letter, and use only lowercase letters,
|
||||||
// Must be lowercase, start with a letter, no uppercase characters,
|
// digits and underscores.
|
||||||
// and cannot be "id", "deleted", "created_at", "row_revision", or end with "_id".
|
//
|
||||||
|
// Cannot be "id", "deleted", "created_at" or "row_revision": those are
|
||||||
|
// system columns, and a data request names system and user columns in one
|
||||||
|
// namespace. Any other name is free, including one ending in "_id" -- no
|
||||||
|
// column name is derived from a table name, so nothing collides.
|
||||||
string name = 1;
|
string name = 1;
|
||||||
|
|
||||||
// Logical column type. Supported values (case-insensitive):
|
// Logical column type. Supported values (case-insensitive):
|
||||||
|
|||||||
@@ -181,6 +181,16 @@ message ScriptDependency {
|
|||||||
string operation = 4;
|
string operation = 4;
|
||||||
// Relationship table used to match the owner row to the related collection.
|
// Relationship table used to match the owner row to the related collection.
|
||||||
string via_table = 5;
|
string via_table = 5;
|
||||||
|
// Column of the current row holding the referenced row's id: the link this
|
||||||
|
// dependency travels, resolved when the script was saved. Empty when no link
|
||||||
|
// is travelled -- a self-reference, or an aggregate, which follows a path
|
||||||
|
// instead. A table may link to one target several times, so this is the only
|
||||||
|
// thing that says which link was meant; never derive it from target_table.
|
||||||
|
string link_column = 6;
|
||||||
|
// The first argument exactly as the script wrote it: a link alias, or the
|
||||||
|
// target table's name where one link makes that unambiguous. The Steel
|
||||||
|
// runtime looks its inputs up by this text.
|
||||||
|
string name_in_script = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies the active form row whose external Steel inputs must be hydrated.
|
// Identifies the active form row whose external Steel inputs must be hydrated.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ message GetTableStructureResponse {
|
|||||||
message TableStructureResponse {
|
message TableStructureResponse {
|
||||||
// Columns of the physical table, including system columns (id, deleted,
|
// Columns of the physical table, including system columns (id, deleted,
|
||||||
// created_at, row_revision), user-defined columns, and any foreign-key columns such as
|
// created_at, row_revision), user-defined columns, and any foreign-key columns such as
|
||||||
// "<linked_table>_id", plus the dedicated "account_id" column on
|
// named by whoever declared each link, plus the dedicated "account_id" on
|
||||||
// ACCOUNTING-enabled tables. May be empty if the physical table is missing.
|
// ACCOUNTING-enabled tables. May be empty if the physical table is missing.
|
||||||
repeated TableColumn columns = 1;
|
repeated TableColumn columns = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ service TablesData {
|
|||||||
// - Validates profile and table definition
|
// - Validates profile and table definition
|
||||||
// - Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
// - Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
||||||
// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
||||||
// named "<linked_table>_id" for each table link, plus "account_id" on
|
// named by whoever declared each link, plus "account_id" on
|
||||||
// ACCOUNTING-enabled tables
|
// ACCOUNTING-enabled tables
|
||||||
// - Fails with NOT_FOUND if record does not exist or is soft-deleted
|
// - Fails with NOT_FOUND if record does not exist or is soft-deleted
|
||||||
// - If the physical table is missing but the definition exists, returns INTERNAL
|
// - If the physical table is missing but the definition exists, returns INTERNAL
|
||||||
@@ -101,7 +101,7 @@ message PostTableDataRequest {
|
|||||||
// - User-defined columns from the table definition
|
// - User-defined columns from the table definition
|
||||||
// - System/FK columns:
|
// - System/FK columns:
|
||||||
// • "deleted" (BOOLEAN), optional; default FALSE if not provided
|
// • "deleted" (BOOLEAN), optional; default FALSE if not provided
|
||||||
// • "<linked_table>_id" (BIGINT) for each table link
|
// • one BIGINT per link, named by whoever declared it
|
||||||
// • "account_id" (BIGINT) on ACCOUNTING-enabled tables
|
// • "account_id" (BIGINT) on ACCOUNTING-enabled tables
|
||||||
//
|
//
|
||||||
// Type expectations by SQL type:
|
// Type expectations by SQL type:
|
||||||
@@ -250,7 +250,7 @@ message GetTableDataResponse {
|
|||||||
// Map of column_name → stringified value for:
|
// Map of column_name → stringified value for:
|
||||||
// - id, deleted
|
// - id, deleted
|
||||||
// - all user-defined columns from the table definition
|
// - all user-defined columns from the table definition
|
||||||
// - FK columns named "<linked_table>_id" for each table link
|
// - one column per link, named by whoever declared it
|
||||||
// - account_id for ACCOUNTING-enabled tables
|
// - account_id for ACCOUNTING-enabled tables
|
||||||
//
|
//
|
||||||
// All values are returned as TEXT via col::TEXT and COALESCEed to empty string
|
// All values are returned as TEXT via col::TEXT and COALESCEed to empty string
|
||||||
|
|||||||
Binary file not shown.
@@ -6,16 +6,18 @@ pub struct PostTableDefinitionRequest {
|
|||||||
/// Table name to create inside the target profile.
|
/// Table name to create inside the target profile.
|
||||||
/// Must be lowercase, alphanumeric with underscores,
|
/// Must be lowercase, alphanumeric with underscores,
|
||||||
/// start with a letter, and be \<= 63 chars.
|
/// start with a letter, and be \<= 63 chars.
|
||||||
/// Forbidden names: "id", "deleted", "created_at", "row_revision", or ending in "\_id".
|
/// Forbidden names: "id", "deleted", "created_at", "row_revision" -- the
|
||||||
|
/// system columns, which share one namespace with table names wherever the
|
||||||
|
/// two are named side by side. The "\_id" suffix is allowed.
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub table_name: ::prost::alloc::string::String,
|
pub table_name: ::prost::alloc::string::String,
|
||||||
/// List of user-defined columns (adds to system/id/fk columns).
|
/// The table's columns, including its links. System columns are added on top.
|
||||||
#[prost(message, repeated, tag = "3")]
|
#[prost(message, repeated, tag = "3")]
|
||||||
pub columns: ::prost::alloc::vec::Vec<ColumnDefinition>,
|
pub columns: ::prost::alloc::vec::Vec<ColumnDefinition>,
|
||||||
/// List of column names to be indexed (must match existing user-defined columns).
|
/// Column names to index, matching names declared above. System columns
|
||||||
/// Indexes can target only user-defined columns; system columns ("id", "deleted",
|
/// ("id", "deleted", "created_at", "row_revision") already have indexes, and
|
||||||
/// "created_at", "row_revision") and automatically generated foreign key ("\*\_id") columns already
|
/// a LINK column is indexed when it is created. Requests naming either are
|
||||||
/// have indexes. Requests trying to index those columns are rejected.
|
/// rejected.
|
||||||
#[prost(string, repeated, tag = "4")]
|
#[prost(string, repeated, tag = "4")]
|
||||||
pub indexes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
pub indexes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||||
/// Name of profile (Postgres schema) where the table will be created.
|
/// Name of profile (Postgres schema) where the table will be created.
|
||||||
@@ -106,9 +108,13 @@ pub struct AddTableColumnsRequest {
|
|||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
pub struct ColumnDefinition {
|
pub struct ColumnDefinition {
|
||||||
/// Column name that follows the same validation rules as table_name.
|
/// Must be lowercase, start with a letter, and use only lowercase letters,
|
||||||
/// Must be lowercase, start with a letter, no uppercase characters,
|
/// digits and underscores.
|
||||||
/// and cannot be "id", "deleted", "created_at", "row_revision", or end with "\_id".
|
///
|
||||||
|
/// Cannot be "id", "deleted", "created_at" or "row_revision": those are
|
||||||
|
/// system columns, and a data request names system and user columns in one
|
||||||
|
/// namespace. Any other name is free, including one ending in "\_id" -- no
|
||||||
|
/// column name is derived from a table name, so nothing collides.
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub name: ::prost::alloc::string::String,
|
pub name: ::prost::alloc::string::String,
|
||||||
/// Logical column type. Supported values (case-insensitive):
|
/// Logical column type. Supported values (case-insensitive):
|
||||||
|
|||||||
@@ -127,6 +127,18 @@ pub struct ScriptDependency {
|
|||||||
/// Relationship table used to match the owner row to the related collection.
|
/// Relationship table used to match the owner row to the related collection.
|
||||||
#[prost(string, tag = "5")]
|
#[prost(string, tag = "5")]
|
||||||
pub via_table: ::prost::alloc::string::String,
|
pub via_table: ::prost::alloc::string::String,
|
||||||
|
/// Column of the current row holding the referenced row's id: the link this
|
||||||
|
/// dependency travels, resolved when the script was saved. Empty when no link
|
||||||
|
/// is travelled -- a self-reference, or an aggregate, which follows a path
|
||||||
|
/// instead. A table may link to one target several times, so this is the only
|
||||||
|
/// thing that says which link was meant; never derive it from target_table.
|
||||||
|
#[prost(string, tag = "6")]
|
||||||
|
pub link_column: ::prost::alloc::string::String,
|
||||||
|
/// The first argument exactly as the script wrote it: a link alias, or the
|
||||||
|
/// target table's name where one link makes that unambiguous. The Steel
|
||||||
|
/// runtime looks its inputs up by this text.
|
||||||
|
#[prost(string, tag = "7")]
|
||||||
|
pub name_in_script: ::prost::alloc::string::String,
|
||||||
}
|
}
|
||||||
/// Identifies the active form row whose external Steel inputs must be hydrated.
|
/// Identifies the active form row whose external Steel inputs must be hydrated.
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub struct GetTableStructureResponse {
|
|||||||
pub struct TableStructureResponse {
|
pub struct TableStructureResponse {
|
||||||
/// Columns of the physical table, including system columns (id, deleted,
|
/// Columns of the physical table, including system columns (id, deleted,
|
||||||
/// created_at, row_revision), user-defined columns, and any foreign-key columns such as
|
/// created_at, row_revision), user-defined columns, and any foreign-key columns such as
|
||||||
/// "\<linked_table>\_id", plus the dedicated "account_id" column on
|
/// named by whoever declared each link, plus the dedicated "account_id" on
|
||||||
/// ACCOUNTING-enabled tables. May be empty if the physical table is missing.
|
/// ACCOUNTING-enabled tables. May be empty if the physical table is missing.
|
||||||
#[prost(message, repeated, tag = "1")]
|
#[prost(message, repeated, tag = "1")]
|
||||||
pub columns: ::prost::alloc::vec::Vec<TableColumn>,
|
pub columns: ::prost::alloc::vec::Vec<TableColumn>,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub struct PostTableDataRequest {
|
|||||||
/// * User-defined columns from the table definition
|
/// * User-defined columns from the table definition
|
||||||
/// * System/FK columns:
|
/// * System/FK columns:
|
||||||
/// • "deleted" (BOOLEAN), optional; default FALSE if not provided
|
/// • "deleted" (BOOLEAN), optional; default FALSE if not provided
|
||||||
/// • "\<linked_table>\_id" (BIGINT) for each table link
|
/// • one BIGINT per link, named by whoever declared it
|
||||||
/// • "account_id" (BIGINT) on ACCOUNTING-enabled tables
|
/// • "account_id" (BIGINT) on ACCOUNTING-enabled tables
|
||||||
///
|
///
|
||||||
/// Type expectations by SQL type:
|
/// Type expectations by SQL type:
|
||||||
@@ -190,7 +190,7 @@ pub struct GetTableDataResponse {
|
|||||||
///
|
///
|
||||||
/// * id, deleted
|
/// * id, deleted
|
||||||
/// * all user-defined columns from the table definition
|
/// * all user-defined columns from the table definition
|
||||||
/// * FK columns named "\<linked_table>\_id" for each table link
|
/// * one column per link, named by whoever declared it
|
||||||
/// * account_id for ACCOUNTING-enabled tables
|
/// * account_id for ACCOUNTING-enabled tables
|
||||||
///
|
///
|
||||||
/// All values are returned as TEXT via col::TEXT and COALESCEed to empty string
|
/// All values are returned as TEXT via col::TEXT and COALESCEed to empty string
|
||||||
@@ -482,7 +482,7 @@ pub mod tables_data_client {
|
|||||||
/// * Validates profile and table definition
|
/// * Validates profile and table definition
|
||||||
/// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
/// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
||||||
/// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
/// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
||||||
/// named "\<linked_table>\_id" for each table link, plus "account_id" on
|
/// named by whoever declared each link, plus "account_id" on
|
||||||
/// ACCOUNTING-enabled tables
|
/// ACCOUNTING-enabled tables
|
||||||
/// * Fails with NOT_FOUND if record does not exist or is soft-deleted
|
/// * Fails with NOT_FOUND if record does not exist or is soft-deleted
|
||||||
/// * If the physical table is missing but the definition exists, returns INTERNAL
|
/// * If the physical table is missing but the definition exists, returns INTERNAL
|
||||||
@@ -675,7 +675,7 @@ pub mod tables_data_server {
|
|||||||
/// * Validates profile and table definition
|
/// * Validates profile and table definition
|
||||||
/// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
/// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col)
|
||||||
/// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
/// including: id, deleted, row_revision, all user-defined columns, and FK columns
|
||||||
/// named "\<linked_table>\_id" for each table link, plus "account_id" on
|
/// named by whoever declared each link, plus "account_id" on
|
||||||
/// ACCOUNTING-enabled tables
|
/// ACCOUNTING-enabled tables
|
||||||
/// * Fails with NOT_FOUND if record does not exist or is soft-deleted
|
/// * Fails with NOT_FOUND if record does not exist or is soft-deleted
|
||||||
/// * If the physical table is missing but the definition exists, returns INTERNAL
|
/// * If the physical table is missing but the definition exists, returns INTERNAL
|
||||||
|
|||||||
2
server
2
server
Submodule server updated: 79e64a2bd5...c5ff2c81f1
@@ -670,12 +670,11 @@ pub(crate) fn validate_identifier(
|
|||||||
"{label} may only use lowercase letters, digits and underscores."
|
"{label} may only use lowercase letters, digits and underscores."
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
// Only the system columns are reserved. The `_id` suffix is free: no
|
||||||
|
// column name is derived from a table name any more, so it collides with
|
||||||
|
// nothing.
|
||||||
if reject_table_reserved
|
if reject_table_reserved
|
||||||
&& (value == "id"
|
&& matches!(value, "id" | "deleted" | "created_at" | "row_revision")
|
||||||
|| value == "deleted"
|
|
||||||
|| value == "created_at"
|
|
||||||
|| value == "row_revision"
|
|
||||||
|| value.ends_with("_id"))
|
|
||||||
{
|
{
|
||||||
return Some(format!("{label} uses a reserved name."));
|
return Some(format!("{label} uses a reserved name."));
|
||||||
}
|
}
|
||||||
@@ -1127,8 +1126,6 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
draft.name_input = "Total".to_string();
|
draft.name_input = "Total".to_string();
|
||||||
assert!(draft.add_from_inputs().is_err());
|
assert!(draft.add_from_inputs().is_err());
|
||||||
draft.name_input = "customer_id".to_string();
|
|
||||||
assert!(draft.add_from_inputs().is_err());
|
|
||||||
draft.name_input = "created_at".to_string();
|
draft.name_input = "created_at".to_string();
|
||||||
assert!(draft.add_from_inputs().is_err());
|
assert!(draft.add_from_inputs().is_err());
|
||||||
|
|
||||||
@@ -1334,3 +1331,27 @@ pub(crate) mod tests {
|
|||||||
assert_eq!(draft.selected_index_names(), vec!["number"]);
|
assert_eq!(draft.selected_index_names(), vec!["number"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod link_alias_tests {
|
||||||
|
use super::validate_identifier;
|
||||||
|
|
||||||
|
/// The `_id` suffix is an ordinary part of a column's name: nothing is
|
||||||
|
/// derived from a table name any more.
|
||||||
|
#[test]
|
||||||
|
fn a_column_name_may_end_in_id() {
|
||||||
|
assert_eq!(validate_identifier("external_id", "Column name", true), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The system columns stay reserved, since they share one namespace with
|
||||||
|
/// user columns in a data request.
|
||||||
|
#[test]
|
||||||
|
fn the_system_columns_stay_reserved() {
|
||||||
|
for name in ["id", "deleted", "created_at", "row_revision"] {
|
||||||
|
assert!(
|
||||||
|
validate_identifier(name, "Column name", true).is_some(),
|
||||||
|
"`{name}` must stay reserved"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user