DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO public; create extension IF NOT EXISTS "ltree"; create extension if not exists "uuid-ossp"; create OR REPLACE function perm_to_numeric ( m_owner INT, m_group INT, m_other INT ) RETURNS INT AS $$ BEGIN RETURN cast((m_owner << 6) | (m_group << 3 | m_other << 0) AS INT); END $$ LANGUAGE plpgsql IMMUtable COST 1; create OR REPLACE function unix_to_numeric (unixperm VARCHAR) RETURNS INT AS $$ DECLARE txt CHAR(3); BEGIN txt := right(unixperm, 3); RETURN perm_to_numeric(cast(substring(txt, 1, 1) AS INT), cast(substring(txt, 2, 1) AS INT), cast(substring(txt, 3, 1) AS INT)); END $$ LANGUAGE plpgsql IMMUtable COST 1; create OR REPLACE function last_update_column() RETURNS trigger AS $$ BEGIN NEW.updated = now(); RETURN NEW; END; $$ -- $$ language 'plpgsql'; create table "system_info"( "id" serial not null, "name" text, "value" jsonb, "when" timestamp, constraint "pk_system_info" primary key ("id") ); create index "ix_system_info_name" on "system_info"("name") with ( FILLFACTOR=100 ); comment on table "system_info" is 'table introduced to save information about system e.g. actual db version / application version etc'; comment on column "system_info"."name" is ''; comment on column "system_info"."value" is ''; create table "group" ( "gid" serial not null, "name" text not null, "description" text, "created" timestamp DEFAULT now() not null, "updated" timestamp, constraint "pk_group_gid" primary key ("gid"), constraint "ux_group_name" unique ("name") ); create trigger "update_user_group_last_update" before update on "group" FOR EACH ROW EXECUTE PROCEDURE last_update_column(); insert into "group"("name") values ('nogroup'), ('admins'), ('moderators'), ('users'); create or replace function default_group_id() returns int as $$ declare id integer; BEGIN SELECT "group"."gid" FROM "group" WHERE "group"."name" = 'nogroup' limit 1 into id; return id; end $$ -- $$ language 'plpgsql'; create or replace function default_users_group_id() returns int as $$ declare id integer; BEGIN SELECT "group"."gid" FROM "group" WHERE "group"."name" = 'users' limit 1 into id; return id; end $$ -- $$ language 'plpgsql'; create or replace function default_admins_group_id() returns int as $$ declare id integer; BEGIN SELECT "group"."gid" FROM "group" WHERE "group"."name" = 'admins' limit 1 into id; return id; end $$ -- $$ language 'plpgsql'; -- $$ create table "auth_info" ( "id" serial not null, "group" int not null default default_users_group_id(), "created" timestamp DEFAULT now() not null, "updated" timestamp, "config" json not null DEFAULT ('{}'), "password_hash" varchar(100) not null, "password_method" varchar(20) not null, "password_salt" varchar(20) not null, "email" varchar(256) not null, "status" integer DEFAULT 0 NOT NULL, constraint "pk_auth_into_id" primary key("id"), constraint "ux_auth_info_email" unique ("email") ); create trigger "update_auth_info_updated" before update on "auth_info" FOR EACH ROW EXECUTE PROCEDURE last_update_column(); comment on table "auth_info" is ''; comment on column "auth_info"."id" is ''; comment on column "auth_info"."group" is 'default user group'; comment on column "auth_info"."created" is 'time of created'; comment on column "auth_info"."config" is 'user configuration'; create table "auth_identity" ( "id" serial not null, "auth_info_id" bigint, "provider" varchar(64) not null, "identity" varchar(512) not null, constraint "pk_auth_identity_id" primary key("id"), constraint "fk_auth_identity_auth_info_id" foreign key ("auth_info_id") references "auth_info" ("id") on delete cascade deferrable initially immediate ); comment on table "auth_identity" is ''; comment on column "auth_identity"."id" is ''; comment on column "auth_identity"."auth_info_id" is ''; comment on column "auth_identity"."provider" is ''; comment on column "auth_identity"."identity" is ''; create table "auth_token" ( "id" serial not null, "auth_info_id" bigint, "value" varchar(64) not null, "expires" timestamp not null, "role" smallint not null, constraint "pk_auth_token_id" primary key("id"), constraint "fk_auth_token_auth_info_id" foreign key ("auth_info_id") references "auth_info" ("id") on delete cascade deferrable initially immediate ); create index "ix_auth_token_value" ON "auth_token" ("value"); comment on table "auth_token" is ''; comment on column "auth_token"."id" is ''; comment on column "auth_token"."auth_info_id" is ''; comment on column "auth_token"."value" is ''; comment on column "auth_token"."expires" is ''; create table "user_audit_action" ( "id" serial not null, "name" text, constraint "pk_user_audit_action_id" primary key ("id"), constraint "ux_user_audit_action_name" unique ("name" ) ); comment on table "user_audit_action" is 'action that user can take (like login/logout/config change?)'; comment on column "user_audit_action"."id" is ''; comment on column "user_audit_action"."name" is 'action name'; insert into "user_audit_action"("name") values ('login'), ('logout'); create UNLOGGED table "user_audit" ( "id" serial not null, "auth_info_id" int not null, "action_id" int not null, "data" jsonb, "when_happened" timestamp DEFAULT(now()), constraint "pk_user_history_id" primary key ("id"), constraint "fk_user_history_auth_info_id" foreign key ("auth_info_id") references "auth_info"("id") on delete cascade deferrable initially immediate, constraint "fk_user_history_user_action" foreign key ("action_id") references "user_audit_action"("id") on delete cascade deferrable initially immediate ); create index "ix_user_history_data" ON "user_audit" ((("data" ->> 'status')::text)) WHERE ("data" ->> 'status') IS not null; comment on table "user_audit" IS 'saves user actions like login/logout'; comment on column "user_audit"."id" is ''; comment on column "user_audit"."auth_info_id" is ''; comment on column "user_audit"."action_id" is ''; comment on column "user_audit"."data" is 'data column contains information about taken action (if login was successful? if not, from what ip this action was taken?)'; comment on column "user_audit"."when_happened" is ''; --create table "user_groups"( -- "uid" integer not null, -- "gid" integer not null --); --comment on table "user_groups" is ''; --comment on column "user_groups"."uid" is ''; --comment on column "user_groups"."gid" is ''; create table "stat"( "id" serial not null, "owner" int not null default 1, "group" int not null default default_group_id(), "unixperms" int not null default unix_to_numeric('764'), "status" int not null default 0, "name" text not null, "created" timestamp DEFAULT now() not null, "updated" timestamp, constraint "pk_stat" primary key ("id"), constraint "fk_stat_user" foreign key ("owner") references "auth_info" ("id") on delete cascade deferrable initially immediate, constraint "fk_stat_primary_group" foreign key ("group") references "group" ("gid") on delete cascade deferrable initially immediate ); create trigger update_stat_last_update before update on stat FOR EACH ROW EXECUTE PROCEDURE last_update_column(); comment on table "stat" is ''; comment on column "stat"."id" is 'unique id for all objects in database'; comment on column "stat"."owner" is 'uid of object''s owner'; comment on column "stat"."group" is 'groups of object'; comment on column "stat"."unixperms" is 'Unixpermissions'; comment on column "stat"."status" is 'status in which object is in (login, logout, removed etc)'; comment on column "stat"."name" is ''; comment on column "stat"."created" is ''; comment on column "stat"."updated" is ''; --create table "action"( -- "title" text not null check( length(title) >= 3 AND length(title) < 100 ), -- "apply_object" boolean not null, -- constraint "pk_action" primary key ("title", "apply_object") --); --comment on table "action" is ''; --comment on column "action"."title" is 'column contains name of action'; --comment on column "action"."apply_object" is 'column specifies whether an action applies to objects or tables. Certain actions, like “create,” apply only to tables. I find the system is easier to manage if I choose my actions so they can only apply to one or the other, not both.'; --create table "implemented_action"( -- "table_name" text not null, -- "action" text not null, -- "status" int not null, -- constraint "pk_implemented_action" primary key ("table_name", "action", "status") --); --comment on table "implemented_action" is ''; --comment on column "implemented_action"."table_name" is ''; --comment on column "implemented_action"."action" is ''; --comment on column "implemented_action"."status" is ''; --create table "privilege" ( -- "role" varchar(30) not null, -- TODO change to enum -- "who" int not null default 0, -- "action" text not null, -- "type" varchar(30) not null, -- TODO change to enum -- "related_table_name" varchar(100) not null, -- "related_object_uid" int not null default 0, -- constraint "pk_privilege" primary key("role", "who", "action", "type", "related_table_name", "related_object_uid") --); --create index "ix_privilege_action_type" on "privilege" ( "action", "type") with ( FILLFACTOR=100 ); --create index "ix_privilege_related_table" on "privilege" ( "related_table_name" ) with ( FILLFACTOR=100 ); --create index "ix_privilege_action" on "privilege" ( "action" ) with ( FILLFACTOR=100 ); --create index "ix_privilege_type" on "privilege" ( "type" ) with ( FILLFACTOR=100 ); --comment on table "privilege" is ''; --comment on column "privilege"."role" is 'specifies whether the privilege is granted to a user, a group, or in the case of an “object” privilege, the object’s owner or owner_group. A further special case, in my system, is “self.”'; --comment on column "privilege"."who" is 'is needed if role is user or group, and holds the user or group ID to which the privilege is granted.'; --comment on column "privilege"."action" is 'is the action the privilege grants. This is always required.'; --comment on column "privilege"."type" is 'specifies whether the privilege is “object”, “table”, or “global.”'; --comment on column "privilege"."related_table_name" is 'holds the name of the table to which the privilege applies. This is always required, though in the case of a “self” privilege it’s redundant because a “self” privilege always applies to the t_user table.'; --comment on column "privilege"."related_object_uid" is 'stores the ID of the object to which the privilege applies, if it’s an object privilege. This has no meaning for table and global privileges, of course. The one applies to a table, not an object, and the second applies to all rows in a table, so an ID is immaterial. This is also not used for self privileges, because by definition a self privilege has to apply to the user requesting permission to do something.'; --------------------------------------------- --- CATEGORIES --------------------------------------------- create or replace function update_category_parent_path() returns trigger as $$ DECLARE path ltree; BEGIN IF NEW.parent_id IS NULL THEN NEW.parent_path = 'root'::ltree; ELSEIF TG_OP = 'INSERT' OR OLD.parent_id IS NULL OR OLD.parent_id != NEW.parent_id THEN -- SELECT parent_path || "id"::text FROM "category" WHERE "id" = NEW.parent_id INTO path; IF path IS NULL THEN RAISE exception 'Invalid parent_uid %', NEW.parent_id; END IF; NEW.parent_path = path; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; create table "category"( "parent_id" int, "parent_path" ltree, "description" text check(length(description) < 100000), "thumbnail" text, constraint "pk_category_uid" primary key ("id"), constraint "fk_category_parent_id" foreign key ("parent_id") references "category"("id") on delete cascade deferrable initially immediate, constraint "fk_category_stat_owner" foreign key ("owner") references "auth_info"("id") deferrable initially immediate ) INHERITS (stat); create index "ix_category_parent_path" on "category" using GIST ("parent_path"); create unique index "ux_category_name" on "category" ( "parent_id", "name" ); create trigger "update_category_last_update" before update on "category" for each row execute procedure last_update_column(); create trigger "update_category_parent_path" before insert or update on "category" for each row execute procedure update_category_parent_path(); comment on table "category" is 'categories of items'; comment on column "category"."parent_id" is ''; comment on column "category"."parent_path" is ''; comment on column "category"."description" is ''; comment on column "category"."thumbnail" is ''; --create table "measurand"( -- "id" serial not null unique primary key, -- "name" text not null unique, -- "description" text, -- "dimension_symbol" text --); --create table "metric_system"( -- "id" serial not null primary key, -- "name" VARCHAR(32) not null unique, -- "description" text --); --comment on table measurand IS 'Information about measured quantity length, time etc.'; --create table unit_of_measurement( -- symbol VARCHAR (20) not null, -- description TEXT check(length(description) < 100000), -- measurand_id int REFERENCES measurands(id), -- base_unit int REFERENCES units(uid), -- metric_system int REFERENCES metric_systems(id), -- constraint units_pkey primary key (uid), -- constraint unitowner_fk foreign key (owner) REFERENCES users (uid) deferrable initially IMMEDIATE, -- constraint units_unique UNIQUE(name, symbol) --) inherits(stat); --comment on table unit_of_measurement IS 'Table holds information about units used in application'; --comment on column unit_of_measurement.name IS 'Parameter name e.g. Ampere'; --comment on column unit_of_measurement.symbol IS 'Parameter symbol e.g. A'; --comment on column unit_of_measurement.description IS 'Simple description'; --create table units_conversions( -- from_unit INTEGER not null REFERENCES units(uid), -- to_unit INTEGER not null REFERENCES units(uid), -- equation TEXT not null, -- constraint unit_conversions_unique primary key (from_unit, to_unit) --); --comment on table units_conversions IS 'This table contains a mathematical equation for converting one unitl to other'; --comment on column units_conversions.equation IS 'this equation should be a proper script (mayby chaiscript?)'; --create table pointed_values( -- id serial primary key, -- data jsonb default('{}') --); --create INDEX items_pointed_values_idx on pointed_values USING GIN (data); --create table "item_namespaces"( --); --create table "item"( -- "category_id" int not null, -- "symbol" text not null, -- "original_symbol" text, -- "producer" text, -- "visibility" VARCHAR(64) DEFAULT 'global' not null, -- "attributes" jsonb not null DEFAULT('{}'), -- "description" TEXT, -- constraint "pk_items" primary key ("id"), -- constraint "fk_item_category" foreign key ("category_id") references "category"("id") on delete cascade deferrable initially immediate, -- constraint "fk_item_auth_info" foreign key ("owner") REFERENCES "auth_info"("id") deferrable initially immediate --) INHERITS (stat); --create index "ix_item_attributes" on "item" USING GIN ("attributes"); --create unique index "ux_item" on "item"("name", "symbol"); --create trigger update_item_last_update before update on item FOR EACH ROW EXECUTE PROCEDURE last_update_column(); --comment on table "item" is ''; --comment on column "item"."category_id" is ''; --comment on column "item"."symbol" is ''; --comment on column "item"."original_symbol" is ''; --comment on column "item"."producer" is ''; --comment on column "item"."visibility" is ''; --comment on column "item"."attributes" is ''; --comment on column "item"."description" is ''; --create table "inventory"( -- "description" TEXT check(length(description)< 100000), -- constraint "pk_inventory" primary key ("id"), -- constraint "fk_inventory_owner" foreign key ("owner") REFERENCES "auth_info" ("id") deferrable initially immediate, -- constraint "chk_inventory_name_length" check (length(name) < 100) --) INHERITS (stat); --create trigger update_inventory_last_update before update on inventory FOR EACH ROW EXECUTE PROCEDURE last_update_column(); --create table "user_inventory"( -- "auth_info_id" INTEGER not null REFERENCES "auth_info" on DELETE CASCADE, -- "inventory_id" INTEGER not null REFERENCES "inventory" on DELETE CASCADE, -- constraint user_inventory_pk primary key ("inventory_id", "auth_info_id") --); --create table in_stock( -- item_id INTEGER not null REFERENCES items, -- inventory_id INTEGER not null REFERENCES inventory, -- amount numeric(10,10) not null DEFAULT 0 --); --comment on table in_stock IS 'Table contains information about items being available in storage'; --create table inventory_operations( -- constraint inventory_operations_pkey primary key (uid), -- constraint OperationOwner_fk foreign key (owner) REFERENCES users (uid) deferrable initially IMMEDIATE, -- constraint inventory_operation_unique UNIQUE (name) --) INHERITS(stat); --create table inventory_history( -- inventory_from_id INTEGER not null REFERENCES inventory on DELETE CASCADE, -- inventory_to_id INTEGER not null REFERENCES inventory on DELETE CASCADE, -- operation_id INTEGER not null REFERENCES inventory_Operations on DELETE CASCADE , -- amount NUMERIC(10,10), -- date timestamp not null default now() --); -- create INDEX users_stat_index on users (uid, owner, group, unixperms, status) with ( FILLFACTOR=100 ); -- create INDEX categories_stat_index on categories (uid, owner, group, unixperms, status) with ( FILLFACTOR=100 ); -- create INDEX storages_stat_index on storages (uid, owner, group, unixperms, status) with ( FILLFACTOR=100 ); -- create INDEX files_stat_index on files (uid, owner, group, unixperms, status) with ( FILLFACTOR=100 ); -- create INDEX items_stat_index on items (uid, owner, group, unixperms, status) with ( FILLFACTOR=100 ); -- create trigger update_parameters_last_update before update on parameters FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_files_last_update before update on files FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_packages_last_update before update on packages FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_units_last_update before update on units FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_items_last_update before update on items FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_shelfs_last_update before update on shelfs FOR EACH ROW EXECUTE PROCEDURE last_update_column(); -- create trigger update_inventory_operations_lats_update before update on inventory_operations FOR EACH ROW EXECUTE PROCEDURE last_update_column();