PRAGMA application_id = 0x5a504442;
PRAGMA user_version = 1;

-- should be faster with rowid due to the blob content
-- these are really just blobs of data
-- TODO copyright and license information should probably
-- go here
CREATE TABLE files (
	hash text primary key, -- sha256 of (uncompressed) content
       	size integer, -- bigint?  certainly need > 2GB
       	compression text, -- always xz?
       	content blob
)
;

create view filerefs as
select F.hash,
count(PF.hash) + count(S.hash) + count(EL.file) + count(N.file) + count(EN.file) as refcount,
count(PF.hash) as pfrefs,
count(S.hash) as scriptrefs,
count(EL.file) as librefs,
count(EN.file) as needrefs,
count(N.file) as noterefs
from files F
left join packagefiles PF on PF.hash = F.hash
left join scripts S on S.hash = F.hash
left join elflibraries EL on EL.file = F.hash
left join elfneeded EN on EN.file = F.hash
left join notes N on N.file = F.hash
group by F.hash
;

-- information about packages
-- a package is identified by a package,version,release triple
create table packages (
	-- primary key columns
	package	text not null,
	version	text not null, -- the upstream version string
	release	integer not null, -- the local release number

	-- metadata columns
	description	text,
	architecture	text,
	url	text,
	status	text,
	licenses	text, -- hash of actual license?  need table for more than one?
	packager	text,
	build_time	integer default (strftime('%s', 'now')),
	install_time	integer,
	hash	text, -- see integ.c for package hash details
	primary key (package,version,release),
	check (typeof(package) = 'text'),
	check (typeof(version) = 'text'),
	check (typeof(release) = 'integer'),
	check (release > 0),
	-- enforce name and version conventions
	check(instr(package,' ') = 0),
	check(instr(package,'/') = 0),
	check(instr(package,':') = 0),
	check(instr(version,' ') = 0),
	check(instr(version,'-') = 0),
	check(instr(version,'/') = 0),
	check(instr(version,':') = 0),
	check(length(package) < 64),
	check(length(package) > 0),
	check(length(version) < 32),
	check(length(version) > 0)
)
without rowid
;

create index package_status_index on packages (status);
create index package_package_index on packages (package);

create view packages_pkgid as
select printf('%s-%s-%s', package, version, release) as pkgid, *
from packages;

create trigger packages_delete_trigger instead of
delete on packages_pkgid
begin
	delete from packages where package = OLD.package
	and version = OLD.version and release = OLD.release;
end;

create trigger packages_update_trigger instead of
update on packages_pkgid
begin
	update packages
	set package = NEW.package,
	version = NEW.version,
	release = NEW.release,
	description = NEW.description,
	architecture = NEW.architecture,
	url = NEW.url,
	status = NEW.status,
	licenses = NEW.licenses,
	packager = NEW.packager,
	build_time = NEW.build_time,
	install_time = NEW.install_time,
	hash = NEW.hash
	where package = OLD.package
	and version = OLD.version
	and release = OLD.release
	;
end
;

-- handle package status history with a logging trigger.
create trigger logpkgstatus after update of status on packages
begin insert into zpmlog (action,target,info)
       	values (printf('status change %s %s', OLD.status, NEW.status),
		printf('%s-%s-%s', NEW.package, NEW.version, NEW.release),
		NULL); END;

create table packagetags (
	-- package id triple
	package	text,
	version	text,
	release	integer,
	tag	text,
	set_time integer default (strftime('%s', 'now')),
	primary key (package,version,release,tag),
	foreign key (package,version,release) references packages (package,version,release) on delete cascade on update cascade
);

-- files contained in a package
create table packagefiles (
	-- package id triple
	package	text,
	version	text,
	release	integer,

	path	text, -- filesystem path
	mode	text not null default '0644', -- perms, use text for octal rep?
	username	text not null default 'root', -- name of owner
	groupname	text not null default 'root', -- group of owner
	uid	integer, -- numeric uid, generally ignored
	gid	integer, -- numeric gid, generally ignored
	configuration integer not null default 0, -- boolean if config file
	confhash text, -- last hash on disk
	filetype varchar not null default 'r',
	-- r regular file
	-- d directory
	-- l symlink
	-- h hard link -- not supported
	-- c character special -- not supported
	-- b block special -- not supported
	-- c and b device special files add dev number column
	-- p fifos (i.e. pipe) -- not supported
	-- s unix domain socket -- not supported
	target	text, -- link target for links
	device	integer, -- device file dev_t
	hash	text, -- null if not a regular file
	mtime	integer, -- seconds since epoch, finer resolution not needed
	primary key (package,version,release,path),
	foreign key (package,version,release)
		references packages (package,version,release)
		on delete cascade on update cascade,
	check (not (filetype = 'l' and target is null)),
	check (not (filetype = 'h' and target is null)),
	check (target is null or length(target) between 1 and 4095),
	check (hash is null or length(hash) between 1 and 1024),
	check (path not in ('.', '..')),
	check (not (filetype = 'r' and hash is null)),
	check (not (filetype = 'c' and device is null)),
	check (not (filetype = 'b' and device is null)),
	check (filetype in ('r','d','l','h','c','b','p')),
	check(length(username) between 1 and 256),
	check(length(groupname) between 1 and 256),
	check (configuration = 0 or configuration = 1)
)
without rowid
;

create index packagefile_package_index on packagefiles (package);
create index packagefile_path_index on packagefiles (path);
create index packagefile_hash_index on packagefiles (hash);
create index pkgfile_configuration on packagefiles (configuration) where configuration = 1;

create view packagefiles_pkgid as
select printf('%s-%s-%s', package, version, release) as pkgid, *,
printf('%s:%s:%s:%s', filetype, mode, username, groupname) as mds
from packagefiles
;

create trigger packagefiles_update_trigger instead of
update on packagefiles_pkgid
begin
	update packagefiles
	set package = NEW.package,
	version = NEW.version,
	release = NEW.release,
	path = NEW.path,
	mode = NEW.mode,
	username = NEW.username,
	groupname = NEW.groupname,
	uid = NEW.uid,
	gid = NEW.gid,
	configuration = NEW.configuration,
	filetype = NEW.filetype,
	target = NEW.target,
	device = NEW.device,
	hash = NEW.hash,
	mtime = NEW.mtime
	where package = OLD.package
	and version = OLD.version
	and release = OLD.release
	and path = OLD.path
	;
end
;

create trigger packagefiles_delete_trigger instead of
delete on packagefiles_pkgid
begin
	delete from packagefiles
	where package = OLD.package
	and version = OLD.version
	and release = OLD.release
	and path = OLD.path
	;
	update packages set hash = null
	where package = OLD.package
	and version = OLD.version
	and release = OLD.release
	;
end
;

create view installed_ref_count as
select I.path, count(*) as refcount
from installedfiles I
group by I.path
;

create view sync_status_ref_count as
select path, status, count(*) as refcount
from packagefiles_status
where status in ('installed', 'installing', 'removing')
group by path, status
;

create view packagefiles_status as
select P.status, PF.*
from packagefiles_pkgid PF
left join packages_pkgid P on P.pkgid = PF.pkgid
;

create view installedfiles as
select * from packagefiles_status
where status = 'installed'
;

create table pathtags (
	-- package id triple
	package	text,
	version	text,
	release	integer,

	path	text, -- filesystem path
	tag	text,
	primary key (package,version,release,path,tag),
	foreign key (package,version,release,path)
	references packagefiles on delete cascade on update cascade
)
without rowid
;

create view elfdeps as
select PF.pkgid, PF.status, PF.path, N.needed as needs,
PL.path as library, PL.pkgid provider, PL.status as library_status
from packagefiles_status PF
join elfneeded N on N.file = PF.hash
left join elflibraries L on N.needed = L.soname
left join packagefiles_status PL on PL.hash = L.file
;

-- TODO just elf information?
-- and just hash, not package?
create table elflibraries (
	file	text primary key,
	soname	text
)
without rowid
;
create index elf_library_name_index on elflibraries(soname);

create table elfneeded (
	file	text, -- hash of file
	needed	text, -- soname of dependency
	primary key (file, needed)
)
without rowid
;

create view package_libraries as
select distinct PF.pkgid, EL.soname
from packagefiles_pkgid PF join elflibraries EL on PF.hash = EL.file
;

create view package_libraries_needed as
with pkglibs as (
		select distinct EN.needed as soname, PF.pkgid
		from elfneeded EN
		join packagefiles_pkgid PF on PF.hash = EN.file
		),
     pkgprovides as (
		     select distinct EL.soname, PF.pkgid
		     from elflibraries EL
		     join packagefiles_pkgid PF on PF.hash = EL.file
		    )
     select distinct PL.pkgid,PL.soname, PP.soname is not null as selfsatisfied
     from pkglibs PL
     left join pkgprovides PP on PL.pkgid = PP.pkgid and PL.soname = PP.soname
;

-- package scripts: table of package, stage, file
create table scripts (
	package	text,
	version	text,
	release	integer,
	stage	text,
	hash	text,
	primary key (package,version,release,stage),
	foreign key (package,version,release) references packages (package,version,release) on delete cascade on update cascade
);

create view scripts_pkgid as
select printf('%s-%s-%s', package, version, release) as pkgid, *
from scripts
;

-- package dependencies: table of package, dependency, dep type (package,
	-- soname)
-- how to specify min/max/exact
create table packagedeps (
	package	text,
	version	text,
	release	integer,
	requires text, -- package, can be partial, minimum
	primary key (package,version,release,requires),
	foreign key (package,version,release) references packages (package,version,release) on delete cascade on update cascade
);

-- capability labels
create table provides (
	package	text,
	subpackage	text,
	label	text -- a capability label
);

create table requires (
	package	text,
	subpackage	text,
	label	text -- a capability label
);

create table packagegroups (
	package	text,
	"group"	text
);

-- zpm actions
-- not sure how machine readable this needs to be,
-- do not at all for now, figure it out later
-- could be worth logging all commands in a history table,
-- the zpm driver could do that and capture the exit status
-- as well
-- might want the history table to note a "group" to tie together
-- sub-invocations, probably an environment variable set if not
-- already set by zpm, probably a uuid or a timestamp
create table zpmlog (
	ts	text default (strftime('%Y-%m-%d %H:%M:%f', 'now')),
       	-- timestamp of action
	action	text,
	target	text, -- packagename, repo name, etc
	info	text -- human readable
);

create table notes (
	id	integer primary key, -- rowid alias
	ts	text default (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
	note	text not null,
	pkgid	text, -- package
	path	text, -- file path involved
	file	text, -- hash of file
	ack	integer default 0
);

create table history (
	ts	integer, -- again, probably needs timestamp sub second
	cmd	text,
	args	text,
	status	integer
);

create table repository (
	name	text primary key, -- our name for a repo
	url	text not null,
	priority	integer not null default 1,
	refreshed	integer -- last refresh time
);
-- force the url to be repourl/info.repo
-- package urls repourl/pkgid.zpm

-- urls for downloading packages.  possibly unneeded
create table repository_packages (
	repo	text,
	pkg	text, -- glob pattern?  in which case others not needed
	version	text,
	release	text,
	url	text
);

-- track which repository a package was cloned from, i.e. where we got it
create table packagesource (
	name	text,
	version	text,
	release	integer,
	repository	text references repository
);


create view needed_libraries as
with recursive
libs(file,needs,provider) as (
	select N.file, N.needed as needs, L.file as provider
	from elfneeded N left join elflibraries L on N.needed = L.soname
	union
	select L.file, N.needed as needs, EL.file as provider
	from libs L
	join elfneeded N on N.file = L.provider
	left join elflibraries EL on N.needed = EL.soname
)
select * from libs;

/*
 * tables for repository info, essentially materalized views
 */
create table repository_libs (
	pkgid text,
	soname text
);

create table repository_libsneeded (
	pkgid text,
	soname text,
	selfsat integer
);
