Schema 1.2.0: meta.series_schemas registry; lower-case name checks
- meta.series_schemas (name, description, created_at, created_by), seeded with 'series'. FK series_db_schema_fkey from meta.series.db_schema: a series may only live in a registered schema, for every client. Unregistered schemas belong to the user and are never touched. - CHECK series_schemas_name_check and series_table_name_check enforce the shared lower-case identifier rule in the database. - meta.find_schema_problems() covers the registry and reports a registered schema that no longer exists; user schemas are not examined. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
155fc7c3dd
commit
4c067bc4b9
7 changed files with 59 additions and 5 deletions
|
|
@ -9,7 +9,7 @@ timeseries database. Both client libraries target the schema defined here:
|
||||||
Neither project should keep its own copy of the DDL. When the schema changes,
|
Neither project should keep its own copy of the DDL. When the schema changes,
|
||||||
it changes **here**, and both projects re-provision / re-test against it.
|
it changes **here**, and both projects re-provision / re-test against it.
|
||||||
|
|
||||||
Current schema version: **1.1.0** (see [`VERSION`](VERSION); recorded at runtime
|
Current schema version: **1.2.0** (see [`VERSION`](VERSION); recorded at runtime
|
||||||
in the `meta.schema_version` table).
|
in the `meta.schema_version` table).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.1.0
|
1.2.0
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,16 @@ CREATE TABLE meta.sessions (
|
||||||
exchange_id character varying(12) NOT NULL
|
exchange_id character varying(12) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- Registry of series schemas. A series may only live in a registered schema
|
||||||
|
-- (FK from meta.series.db_schema, 04_indexes.sql). Every other schema in the
|
||||||
|
-- database belongs to the user and is never touched by the clients.
|
||||||
|
CREATE TABLE meta.series_schemas (
|
||||||
|
name text NOT NULL,
|
||||||
|
description text,
|
||||||
|
created_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
created_by text NOT NULL DEFAULT CURRENT_USER
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE meta.timezones (
|
CREATE TABLE meta.timezones (
|
||||||
iana_name character varying(50) NOT NULL,
|
iana_name character varying(50) NOT NULL,
|
||||||
display_name character varying(100) NOT NULL,
|
display_name character varying(100) NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,23 @@ ALTER TABLE meta.currencies ADD CONSTRAINT currencies_pkey PRIMARY KEY (code);
|
||||||
ALTER TABLE meta.security_types ADD CONSTRAINT security_types_pkey PRIMARY KEY (code);
|
ALTER TABLE meta.security_types ADD CONSTRAINT security_types_pkey PRIMARY KEY (code);
|
||||||
ALTER TABLE meta.timezones ADD CONSTRAINT timezones_pkey PRIMARY KEY (iana_name);
|
ALTER TABLE meta.timezones ADD CONSTRAINT timezones_pkey PRIMARY KEY (iana_name);
|
||||||
|
|
||||||
|
-- Series schema registry. Names follow the table-name rule shared by both
|
||||||
|
-- clients: lower case, [a-z_][a-z0-9_]*, at most 63 bytes, never quoted.
|
||||||
|
ALTER TABLE meta.series_schemas ADD CONSTRAINT series_schemas_pkey PRIMARY KEY (name);
|
||||||
|
ALTER TABLE meta.series_schemas ADD CONSTRAINT series_schemas_name_check
|
||||||
|
CHECK (name ~ '^[a-z_][a-z0-9_]*$' AND octet_length(name) <= 63);
|
||||||
|
|
||||||
|
-- A series may only live in a registered schema -- enforced here, so no
|
||||||
|
-- client can bypass it. RESTRICT: a schema cannot be unregistered while it
|
||||||
|
-- still holds series.
|
||||||
|
ALTER TABLE meta.series ADD CONSTRAINT series_db_schema_fkey
|
||||||
|
FOREIGN KEY (db_schema) REFERENCES meta.series_schemas (name)
|
||||||
|
ON UPDATE RESTRICT ON DELETE RESTRICT;
|
||||||
|
|
||||||
|
-- Table names follow the same rule (Core contract 6 in both clients).
|
||||||
|
ALTER TABLE meta.series ADD CONSTRAINT series_table_name_check
|
||||||
|
CHECK (table_name ~ '^[a-z_][a-z0-9_]*$' AND octet_length(table_name) <= 63);
|
||||||
|
|
||||||
CREATE INDEX idx_event_log_series_timestamp ON log.event_log USING btree (series_name, "timestamp" DESC);
|
CREATE INDEX idx_event_log_series_timestamp ON log.event_log USING btree (series_name, "timestamp" DESC);
|
||||||
|
|
||||||
CREATE INDEX idx_event_log_timestamp ON log.event_log USING btree ("timestamp" DESC);
|
CREATE INDEX idx_event_log_timestamp ON log.event_log USING btree ("timestamp" DESC);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
-- Reference data (timezones and currencies)
|
-- Reference data (series schema registry, timezones, currencies, security types)
|
||||||
-- Generated: 2025-10-15T12:24:56.582392
|
-- Generated: 2025-10-15T12:24:56.582392
|
||||||
|
|
||||||
-- Timezones
|
-- Timezones
|
||||||
|
|
@ -767,3 +767,7 @@ VALUES
|
||||||
('OPT', 'Option', 'Equity and index options', NULL),
|
('OPT', 'Option', 'Equity and index options', NULL),
|
||||||
('STK', 'Stock', 'Common and preferred equities', NULL),
|
('STK', 'Stock', 'Common and preferred equities', NULL),
|
||||||
('WAR', 'Warrant', 'Equity warrants', NULL);
|
('WAR', 'Warrant', 'Equity warrants', NULL);
|
||||||
|
|
||||||
|
-- Series schema registry: the default schema, created in 01_schemas.sql
|
||||||
|
INSERT INTO meta.series_schemas (name, description)
|
||||||
|
VALUES ('series', 'Default series schema');
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,10 @@ BEGIN
|
||||||
('meta.instruments', 'asset_class', 'character varying(30)', false),
|
('meta.instruments', 'asset_class', 'character varying(30)', false),
|
||||||
('meta.instruments', 'geography', 'character varying(30)', false),
|
('meta.instruments', 'geography', 'character varying(30)', false),
|
||||||
('meta.instruments', 'underlying', 'character varying(30)', false),
|
('meta.instruments', 'underlying', 'character varying(30)', false),
|
||||||
|
('meta.series_schemas', 'name', 'text', true),
|
||||||
|
('meta.series_schemas', 'description', 'text', false),
|
||||||
|
('meta.series_schemas', 'created_at', 'timestamp with time zone', true),
|
||||||
|
('meta.series_schemas', 'created_by', 'text', true),
|
||||||
('meta.security_types', 'code', 'character varying(10)', true),
|
('meta.security_types', 'code', 'character varying(10)', true),
|
||||||
('meta.security_types', 'name', 'character varying(50)', true),
|
('meta.security_types', 'name', 'character varying(50)', true),
|
||||||
('meta.security_types', 'description', 'text', false),
|
('meta.security_types', 'description', 'text', false),
|
||||||
|
|
@ -211,6 +215,10 @@ BEGIN
|
||||||
('meta.sessions', 'sessions_pkey'),
|
('meta.sessions', 'sessions_pkey'),
|
||||||
('meta.series', 'series_pkey'),
|
('meta.series', 'series_pkey'),
|
||||||
('meta.series', 'series_schema_table_key'),
|
('meta.series', 'series_schema_table_key'),
|
||||||
|
('meta.series', 'series_db_schema_fkey'),
|
||||||
|
('meta.series', 'series_table_name_check'),
|
||||||
|
('meta.series_schemas', 'series_schemas_pkey'),
|
||||||
|
('meta.series_schemas', 'series_schemas_name_check'),
|
||||||
('meta.currencies', 'currencies_pkey'),
|
('meta.currencies', 'currencies_pkey'),
|
||||||
('meta.security_types', 'security_types_pkey'),
|
('meta.security_types', 'security_types_pkey'),
|
||||||
('meta.timezones', 'timezones_pkey')
|
('meta.timezones', 'timezones_pkey')
|
||||||
|
|
@ -301,12 +309,27 @@ BEGIN
|
||||||
FROM (VALUES
|
FROM (VALUES
|
||||||
('meta.timezones', 563),
|
('meta.timezones', 563),
|
||||||
('meta.currencies', 179),
|
('meta.currencies', 179),
|
||||||
('meta.security_types', 13)
|
('meta.security_types', 13),
|
||||||
|
('meta.series_schemas', 1)
|
||||||
) AS e(tbl, expected)
|
) AS e(tbl, expected)
|
||||||
WHERE to_regclass(e.tbl) IS NOT NULL
|
WHERE to_regclass(e.tbl) IS NOT NULL
|
||||||
) AS r
|
) AS r
|
||||||
WHERE r.actual < r.expected;
|
WHERE r.actual < r.expected;
|
||||||
|
|
||||||
|
-- Series schema registry ------------------------------------------------
|
||||||
|
-- Only REGISTERED schemas are examined; user schemas are never looked at.
|
||||||
|
IF to_regclass('meta.series_schemas') IS NOT NULL THEN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT 'series schema series', 'not registered in meta.series_schemas'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM meta.series_schemas WHERE name = 'series');
|
||||||
|
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT 'series schema ' || r.name, 'registered but the schema does not exist'
|
||||||
|
FROM meta.series_schemas r
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = r.name)
|
||||||
|
ORDER BY r.name;
|
||||||
|
END IF;
|
||||||
|
|
||||||
IF to_regclass('meta.schema_version') IS NOT NULL THEN
|
IF to_regclass('meta.schema_version') IS NOT NULL THEN
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT 'schema version', 'not recorded in meta.schema_version'
|
SELECT 'schema version', 'not recorded in meta.schema_version'
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@
|
||||||
-- they were built for (Python: DB_SCHEMA_VERSION in tsapi_pgdb/_version.py).
|
-- they were built for (Python: DB_SCHEMA_VERSION in tsapi_pgdb/_version.py).
|
||||||
|
|
||||||
INSERT INTO meta.schema_version (version, description)
|
INSERT INTO meta.schema_version (version, description)
|
||||||
VALUES ('1.1.0', 'meta.find_schema_problems(); every install runs 00-10');
|
VALUES ('1.2.0', 'meta.series_schemas registry; lower-case table name check');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue