update databasing system with minimum API, schema parsing and FreeCAD
integration
This commit is contained in:
228
migrations/001_initial.sql
Normal file
228
migrations/001_initial.sql
Normal file
@@ -0,0 +1,228 @@
|
||||
-- Silo Database Schema
|
||||
-- Migration: 001_initial
|
||||
-- Date: 2026-01
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Enable extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For fuzzy text search
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Part Numbering Schemas
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE schemas (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
description TEXT,
|
||||
definition JSONB NOT NULL, -- Parsed YAML schema
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_schemas_name ON schemas(name);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Items (Core Entity)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE items (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
part_number TEXT UNIQUE NOT NULL,
|
||||
schema_id UUID REFERENCES schemas(id),
|
||||
item_type TEXT NOT NULL, -- 'part', 'assembly', 'drawing', etc.
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
archived_at TIMESTAMPTZ, -- Soft delete
|
||||
current_revision INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX idx_items_part_number ON items(part_number);
|
||||
CREATE INDEX idx_items_schema ON items(schema_id);
|
||||
CREATE INDEX idx_items_type ON items(item_type);
|
||||
CREATE INDEX idx_items_description_trgm ON items USING gin(description gin_trgm_ops);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Revisions (Append-Only History)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE revisions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
|
||||
revision_number INTEGER NOT NULL,
|
||||
properties JSONB NOT NULL DEFAULT '{}',
|
||||
file_key TEXT, -- MinIO object key
|
||||
file_version TEXT, -- MinIO version ID
|
||||
file_checksum TEXT, -- SHA256 of file
|
||||
file_size BIGINT, -- File size in bytes
|
||||
thumbnail_key TEXT, -- MinIO key for thumbnail
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
created_by TEXT, -- User identifier
|
||||
comment TEXT,
|
||||
UNIQUE(item_id, revision_number)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_revisions_item ON revisions(item_id);
|
||||
CREATE INDEX idx_revisions_item_rev ON revisions(item_id, revision_number DESC);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Relationships (BOM Structure)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TYPE relationship_type AS ENUM ('component', 'alternate', 'reference');
|
||||
|
||||
CREATE TABLE relationships (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
parent_item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
|
||||
child_item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
|
||||
rel_type relationship_type NOT NULL DEFAULT 'component',
|
||||
quantity DECIMAL(12, 4),
|
||||
unit TEXT, -- Unit of measure
|
||||
reference_designators TEXT[], -- e.g., ARRAY['R1', 'R2', 'R3']
|
||||
child_revision INTEGER, -- NULL means "latest"
|
||||
metadata JSONB, -- Assembly-specific relationship properties
|
||||
parent_revision_id UUID REFERENCES revisions(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT no_self_reference CHECK (parent_item_id != child_item_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_relationships_parent ON relationships(parent_item_id);
|
||||
CREATE INDEX idx_relationships_child ON relationships(child_item_id);
|
||||
CREATE INDEX idx_relationships_type ON relationships(rel_type);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Locations (Physical Inventory Hierarchy)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE locations (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
path TEXT UNIQUE NOT NULL, -- e.g., 'lab/shelf-a/bin-3'
|
||||
name TEXT NOT NULL,
|
||||
parent_id UUID REFERENCES locations(id),
|
||||
location_type TEXT NOT NULL,
|
||||
depth INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_locations_path ON locations(path);
|
||||
CREATE INDEX idx_locations_parent ON locations(parent_id);
|
||||
CREATE INDEX idx_locations_type ON locations(location_type);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Inventory (Item Quantities at Locations)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE inventory (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
|
||||
location_id UUID NOT NULL REFERENCES locations(id) ON DELETE CASCADE,
|
||||
quantity DECIMAL(12, 4) NOT NULL DEFAULT 0,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE(item_id, location_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_inventory_item ON inventory(item_id);
|
||||
CREATE INDEX idx_inventory_location ON inventory(location_id);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Sequence Counters (Part Number Generation)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE sequences (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
schema_id UUID NOT NULL REFERENCES schemas(id) ON DELETE CASCADE,
|
||||
scope TEXT NOT NULL, -- Scope key, e.g., 'PROTO-AS'
|
||||
current_value INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE(schema_id, scope)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sequences_schema_scope ON sequences(schema_id, scope);
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Functions
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Get next sequence value atomically
|
||||
CREATE OR REPLACE FUNCTION next_sequence_value(
|
||||
p_schema_id UUID,
|
||||
p_scope TEXT
|
||||
) RETURNS INTEGER AS $$
|
||||
DECLARE
|
||||
v_next INTEGER;
|
||||
BEGIN
|
||||
INSERT INTO sequences (schema_id, scope, current_value)
|
||||
VALUES (p_schema_id, p_scope, 1)
|
||||
ON CONFLICT (schema_id, scope) DO UPDATE
|
||||
SET current_value = sequences.current_value + 1
|
||||
RETURNING current_value INTO v_next;
|
||||
|
||||
RETURN v_next;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Update item's current revision
|
||||
CREATE OR REPLACE FUNCTION update_item_revision()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
UPDATE items
|
||||
SET current_revision = NEW.revision_number,
|
||||
updated_at = now()
|
||||
WHERE id = NEW.item_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_update_item_revision
|
||||
AFTER INSERT ON revisions
|
||||
FOR EACH ROW EXECUTE FUNCTION update_item_revision();
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Views
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Current item state (latest revision)
|
||||
CREATE VIEW items_current AS
|
||||
SELECT
|
||||
i.id,
|
||||
i.part_number,
|
||||
i.item_type,
|
||||
i.description,
|
||||
i.schema_id,
|
||||
i.current_revision,
|
||||
i.created_at,
|
||||
i.updated_at,
|
||||
r.properties,
|
||||
r.file_key,
|
||||
r.file_version,
|
||||
r.thumbnail_key,
|
||||
r.created_by AS last_modified_by,
|
||||
r.comment AS last_revision_comment
|
||||
FROM items i
|
||||
LEFT JOIN revisions r ON r.item_id = i.id AND r.revision_number = i.current_revision
|
||||
WHERE i.archived_at IS NULL;
|
||||
|
||||
-- BOM explosion (single level)
|
||||
CREATE VIEW bom_single_level AS
|
||||
SELECT
|
||||
rel.parent_item_id,
|
||||
parent.part_number AS parent_part_number,
|
||||
rel.child_item_id,
|
||||
child.part_number AS child_part_number,
|
||||
child.description AS child_description,
|
||||
rel.rel_type,
|
||||
rel.quantity,
|
||||
rel.unit,
|
||||
rel.reference_designators,
|
||||
rel.child_revision,
|
||||
COALESCE(rel.child_revision, child.current_revision) AS effective_revision
|
||||
FROM relationships rel
|
||||
JOIN items parent ON parent.id = rel.parent_item_id
|
||||
JOIN items child ON child.id = rel.child_item_id
|
||||
WHERE parent.archived_at IS NULL AND child.archived_at IS NULL;
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user