create table revision (
revnum int primary key
check (revnum >= 1),
committed_at timestamp not null
);
create index revision_committed_at on revision (committed_at);
-- If I could maintain this table automatically, it would avoid the risk of
-- someone failing to add a branch here before updating the file_path info,
-- which might conceivablt break the GUIDs. Perhaps I could have a file
-- with a special name, say '_top', to mark the top directory of each branch.
create table branch (
id serial primary key,
-- Relative path from repository root URL to right trunk/branch/whatever.
path text not null unique
check ((path = 'trunk' or path like 'branches/%' or path like 'tags/%')
and path not like '%/')
);
insert into branch (path) values ('trunk');
create table working_copy (
id serial primary key,
branch_id int not null references branch,
-- Revision currently represented by the working copy.
-- This is never less than 1, because the first revision (to create
-- the 'trunk' directory, etc.) has to be done before we can check out.
current_revision int not null references revision
check (current_revision >= 1)
);
create table file_guid (
id serial primary key,
is_dir boolean not null,
uri text not null unique
check (uri similar to '[a-z][-+.a-z0-9]*:%'),
-- If the 'daizu:guid' property is added to a file on the trunk then
-- the 'uri' field above is set to its value, but the standard URI
-- is saved in 'old_uri' so that if the property is removed then it
-- will go back to having the same URI as it used to. A standard
-- URI is always generated, even for files which had 'daizu:guid'
-- right from the start.
old_uri text
check (uri similar to '[a-z][-+.a-z0-9]*:%'),
custom_uri boolean not null default false,
constraint file_guid_old_uri_missing_chk
check (custom_uri = (old_uri is not null)),
first_revnum int not null references revision
check (first_revnum >= 1),
-- This ignores changes outside the trunk, and deletions.
last_changed_revnum int not null references revision
check (first_revnum >= 1),
constraint file_guid_revnum_chk
check (last_changed_revnum >= first_revnum)
);
create table file_path (
guid_id int not null references file_guid,
path text not null
check (path <> '' and path not like '/%' and path not like '%/'),
branch_id int not null references branch,
first_revnum int not null references revision
check (first_revnum >= 1),
last_revnum int references revision
check (last_revnum >= 1),
constraint file_path_bad_revnums_chk
check (last_revnum >= first_revnum),
primary key (guid_id, branch_id, first_revnum)
);
create index file_path_path_idx on file_path (path);
create unique index file_path_unique_idx on file_path (branch_id, path, first_revnum);
create table wc_file (
id serial primary key,
wc_id int not null references working_copy on delete cascade,
guid_id int not null references file_guid,
parent_id int references wc_file on delete cascade,
is_dir boolean not null,
name text not null
check (name <> '' and name <> '.' and name <> '..' and
name not like '%/%'),
path text not null -- redundant, but probably useful
check (path <> '' and path not like '/%' and path not like '%/'),
constraint wc_file_bad_path_and_name_chk
check (path = name or path like ('%/' || name)),
-- Revision number the file is based on and changes which have been made
-- in respect to what is in the repository for that revision.
-- cur_revnum is null for files which have been added but not yet committed.
cur_revnum int references revision,
modified boolean not null default false,
deleted boolean not null default false,
constraint wc_file_cur_revnum_missing_chk
check (cur_revnum is not null or (not modified and not deleted)),
-- Class name of this file's generator. Either directly from the
-- 'daizu:generator' property, or inherited from its parent, or taking
-- the default value.
generator text not null
check (generator similar to '[\\_a-zA-Z][-\\_:a-zA-Z0-9]*[\\_a-zA-Z0-9]'),
-- Files which are their own root file have this set to NULL. Other
-- files have it pointing to one of their ancestors, whichever is the
-- closest to have a 'daizu:generator' property.
root_file_id int references wc_file on delete cascade,
custom_url text -- daizu:url
check (custom_url similar to '[a-z][-+.a-z0-9]*:%'),
article boolean not null default false, -- 'article' type
retired boolean not null default false, -- 'retired' flag
no_index boolean not null default false, -- 'no-index' flag
issued_at timestamp not null,
modified_at timestamp not null,
title text,
short_title text,
description text,
content_type text
-- All ASCII characters allowed except 'tspecials' defined in RFC 2045.
check (content_type similar to '[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+/[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+'),
-- TODO - character encoding - for now just assume everything's UTF8
-- These two only apply to image files, and are here to avoid having
-- to look in the file's content everytime we want to generate a page
-- which references it.
image_width int check (image_width > 0),
image_height int check (image_height > 0),
-- The actual binary contents of the file, or a reference to another
-- file whose data is the same (to save space if there are multiple
-- working copies, since most files will have the same data in each WC).
-- The reference should be to the version of the file in the live WC.
--
-- If data_from_file_id is non-NULL, the file it references must have
-- a non-NULL 'data' field.
-- The live working copy must not use data_from_file_id.
--
-- For directories:
-- data_len must be 0 and the other two NULL.
-- For files:
-- exactly one of data and data_from_file_id must be non-NULL.
-- For empty files:
-- data must be '' and data_from_file_id NULL.
--
-- data_sha1 is the SHA1 digest of the data. It must be NULL for
-- directories, and non-NULL for files. The 160 bit digest must be
-- encoded as 27 characters in base 64 format, with the single
-- padding '=' stripped off.
data_from_file_id int references wc_file,
data bytea,
data_len int not null
check (data_len >= 0),
data_sha1 char(27)
check (length(data_sha1) = 27 and
data_sha1 similar to '[A-Za-z0-9+/]+'),
constraint wc_file_wrong_data_len_chk
check (data_len = length(data)),
constraint wc_file_bad_dir_data_chk
check (not is_dir or
(data_from_file_id is null and data is null and
data_len = 0 and data_sha1 is null)),
constraint wc_file_bad_file_data_chk
check (is_dir or
(data_sha1 is not null and
((data is not null and data_from_file_id is null) or
(data is null and data_from_file_id is not null)))),
constraint wc_file_bad_empty_file_data_chk
check (is_dir or data_len > 0 or data is not null),
-- These values are NULL for files which aren't articles.
article_pages_url text, -- absolute URL, can be used as permalink
article_content text,
constraint wc_file_article_loaded_chk
check ((article and article_content is not null and
article_pages_url is not null) or
(not article and article_content is null and
article_pages_url is null))
);
create unique index wc_file_path_idx on wc_file (wc_id, path);
create table wc_property (
file_id int not null references wc_file on delete cascade,
name text not null check (name <> ''),
value text not null,
modified boolean not null default false, -- modified or added
deleted boolean not null default false,
primary key (file_id, name)
);
create table tag (
tag text primary key
check (tag <> '')
);
create table wc_file_tag (
file_id int not null references wc_file on delete cascade,
tag text not null references tag, -- Canonicalized spelling.
original_spelling text not null, -- As specified in daizu:tags.
primary key (file_id, tag)
);
create table wc_article_extra_url (
file_id int not null references wc_file on delete cascade,
url text not null,
content_type text not null
-- All ASCII characters allowed except 'tspecials' defined in RFC 2045.
check (content_type similar to '[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+/[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+'),
generator text not null
check (generator similar to '[\\_a-zA-Z][-\\_:a-zA-Z0-9]*[\\_a-zA-Z0-9]'),
method text not null
check (method similar to '[\\_a-zA-Z0-9]+'),
argument text not null default ''
);
create table wc_article_extra_template (
file_id int not null references wc_file on delete cascade,
filename text not null
);
create table wc_article_included_files (
file_id int not null references wc_file on delete cascade,
included_file_id int not null
references wc_file deferrable initially deferred
);
create table url (
id serial primary key,
url text not null
check (url similar to '[a-z][-+.a-z0-9]*:%'),
wc_id int not null references working_copy on delete cascade,
guid_id int not null references file_guid,
generator text not null
check (generator similar to '[\\_a-zA-Z][-\\_:a-zA-Z0-9]*[\\_a-zA-Z0-9]'),
method text not null
check (method similar to '[\\_a-zA-Z0-9]+'),
argument text not null default '',
content_type text
-- All ASCII characters allowed except 'tspecials' defined in RFC 2045.
check (content_type similar to '[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+/[-!#$\\%&''*+.0-9A-Z^\\_`a-z{|}~]+'),
status char(1) not null
-- Active, Redirect, Gone
check (status in ('A', 'R', 'G')),
-- If the status is 'R' then this indicates which entry in the 'url'
-- table this one should redirect to. The target URL may be gone, but
-- it should not be another redirect.
redirect_to_id int references url,
constraint url_redirect_missing_chk
check ((status = 'R') = (redirect_to_id is not null))
);
create unique index url_unique_idx on url (url, wc_id);
create table live_revision (
revnum int not null references revision
);
-- TODO - allow for a seperate Latin transliterated name, e.g. for Chinese
create table person (
id serial primary key,
username text not null unique -- UTF-8
check (username !~ '\s')
);
create table person_info (
person_id int not null references person on delete cascade,
path text not null,
name text not null, -- UTF-8
email text,
uri text, -- person's homepage, or whatever
primary key (person_id, path)
);
create table file_author (
file_id int not null references wc_file on delete cascade,
person_id int not null references person,
pos int not null, -- sort on this to get order authors were specified in
primary key (file_id, person_id)
);
-- vi:ts=4 sw=4 expandtab