CREATE TABLE projects (
    id INTEGER NOT NULL PRIMARY KEY,
    parent_id INTEGER,
    name VARCHAR(40) NOT NULL,
    title VARCHAR(1024) NOT NULL,
    path VARCHAR collate nocase,
    status_id INTEGER NOT NULL DEFAULT -1,
    repo_id INTEGER,
    local INTEGER,
    hash VARCHAR,
    num_updates INTEGER,
    FOREIGN KEY(id) REFERENCES topics(id)
        ON DELETE CASCADE
    FOREIGN KEY(parent_id) REFERENCES projects(id)
        ON DELETE CASCADE,
    FOREIGN KEY(repo_id) REFERENCES repos(id)
        ON DELETE CASCADE
    FOREIGN KEY(status_id,id) REFERENCES project_status(id,project_id)
        DEFERRABLE INITIALLY DEFERRED
);

CREATE UNIQUE INDEX projects_path_repo_id ON projects(path,repo_id);

CREATE TRIGGER
    bd_projects_1
BEFORE DELETE ON
    projects
FOR EACH ROW
BEGIN
    SELECT debug(
        OLD.id
    );
--    select debug('select * from topics where id=?', OLD.status_id);
--    select debug('select ?', OLD.status_id);
END;

CREATE TRIGGER
    ad_projects_1
AFTER DELETE ON
    projects
FOR EACH ROW
BEGIN
    SELECT debug(
        OLD.id
    );

    DELETE FROM
        projects_tomerge
    WHERE
        project_id = OLD.id
    ;

    DELETE FROM
        updates
    WHERE
        id = (
            SELECT
                first_update_id
            FROM
                topics
            WHERE
                id = OLD.id
        )
    ;

END;

/*
    A hierachical data (tree) implementation in SQL using triggers as
    described here:

        http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/

    Generated by App::sqltree version 0.0.5_2 with the following
    paramters on Wed Jun 12 21:25:18 2013

        driver:     SQLite
        table:      projects
        pk:         id
        parent:     parent_id
        type:       INTEGER
        path:       path
        path_from:  name
        order:      
*/

/*
 Triggers in SQLite run in the reverse order to which they are defined.
 Actions happen from the bottom up.
 */

CREATE TRIGGER
    tree_ai_projects_3
AFTER INSERT ON
    projects
FOR EACH ROW WHEN
    NEW.parent_id IS NOT NULL
BEGIN

    UPDATE
        projects
    SET
        path = (
            SELECT
                path || '/' || NEW.name
            FROM
                projects
            WHERE
                id = NEW.parent_id
        )
    WHERE
        id = NEW.id
    ;

END;

CREATE TRIGGER
    tree_ai_projects_2
AFTER INSERT ON
    projects
FOR EACH ROW WHEN
    NEW.parent_id IS NULL
BEGIN

    UPDATE
        projects
    SET
        path = name
    WHERE
        id = NEW.id
    ;

END;

CREATE TRIGGER
    tree_ai_projects_1
AFTER INSERT ON
    projects
FOR EACH ROW 
BEGIN

    /*
     Insert a matching row in projects_tree where both parent and child
     are set to the id of the newly inserted object. Depth is set to 0
     as both child and parent are on the same level.
     */

    INSERT INTO
        projects_tree (
            parent,
            child,
            depth
        )
    VALUES (
        NEW.id,
        NEW.id,
        0
    );

    /*
     Copy all rows that our parent had as its parents, but we modify
     the child id in these rows to be the id of currently inserted row,
     and increase depth by one.
     */

    INSERT INTO
        projects_tree (
            parent,
            child,
            depth
        )
    SELECT
        x.parent,
        NEW.id,
        x.depth + 1
    FROM
        projects_tree x
    WHERE
        x.child = NEW.parent_id
    ;

END;

/*
    Handle parent_id changes

    4. Update all affected child rows with the new parent path
*/

CREATE TRIGGER
    tree_au_projects_5
AFTER UPDATE OF
    parent_id
ON
    projects
FOR EACH ROW WHEN
    NEW.parent_id IS NOT NULL AND
    ( OLD.parent_id IS NULL OR NEW.parent_id != OLD.parent_id )
BEGIN

    UPDATE
        projects
    SET
        path = (
            SELECT
                path
            FROM
                projects
            WHERE
                id = NEW.parent_id
        ) || '/' || path
    WHERE
        id IN (
            SELECT
                child
            FROM
                projects_tree
            WHERE parent = NEW.parent_id AND depth > 0
        )
    ;

END;

/*
    Handle parent_id changes

    3. Insert the new tree data relating to the new parent
*/

CREATE TRIGGER
    tree_au_projects_4
AFTER UPDATE OF
    parent_id
ON
    projects
FOR EACH ROW WHEN
    NEW.parent_id IS NOT NULL AND 
    ( OLD.parent_id IS NULL OR NEW.parent_id != OLD.parent_id )
BEGIN

    INSERT INTO
        projects_tree (parent, child, depth)
    SELECT
        r1.parent, r2.child, r1.depth + r2.depth + 1
    FROM
        projects_tree r1
    INNER JOIN
        projects_tree r2
    ON
        r2.parent = NEW.id
    WHERE
        r1.child = NEW.parent_id
    ;

END;

/*
    Handle parent_id changes

    2. Remove the tree data relating to the old parent
*/

CREATE TRIGGER
    tree_au_projects_3
AFTER UPDATE OF
    parent_id
ON
    projects
FOR EACH ROW WHEN
    OLD.parent_id IS NOT NULL AND 
    ( NEW.parent_id IS NULL OR NEW.parent_id != OLD.parent_id)
BEGIN
    DELETE FROM
        projects_tree
    WHERE
        treeid IN (
            SELECT
                r2.treeid
            FROM
                projects_tree r1
            INNER JOIN
                projects_tree r2
            ON
                r1.child = r2.child AND r2.depth > r1.depth
            WHERE
                r1.parent = NEW.id
        )
    ;
END;

/*
 FIXME: Also trigger when column 'path_from' changes. For the moment,
 the user work-around is to temporarily re-parent the row.
*/

/*
    Handle parent_id changes

    1. Remove parent part of the path
*/

CREATE TRIGGER
    tree_au_projects_2
AFTER UPDATE OF
    parent_id
ON
    projects
FOR EACH ROW WHEN
    OLD.parent_id IS NOT NULL
BEGIN
    UPDATE
        projects
    SET
        path = substr(path, (
            SELECT
                length(path || '/') + 1
            FROM
                projects
            WHERE
                id = OLD.parent_id
        ))
    WHERE
        id IN (
            SELECT
                child
            FROM
                projects_tree
            WHERE
                parent = OLD.parent_id AND depth > 0
        )
    ;

END;

/*
 Handle changes to the name column
*/

CREATE TRIGGER
    tree_au_projects_1
AFTER UPDATE OF
    name
ON
    projects
FOR EACH ROW WHEN
    OLD.name != NEW.name
BEGIN

    /*
        First of all the current row
    */

    UPDATE
        projects
    SET
        path = 
            CASE WHEN
                NEW.parent_id IS NOT NULL
            THEN
                (SELECT
                    path
                 FROM
                    projects
                 WHERE
                    id = NEW.parent_id
                ) || '/' || name
            ELSE
                name
            END
    WHERE
        id = OLD.id
    ;

    /*
        Then all of the child rows
    */

    UPDATE
        projects
    SET
        path = (
            SELECT
                path
            FROM
                projects
            WHERE
                id = OLD.id
        ) || SUBSTR(path, LENGTH(OLD.path) + 1)
    WHERE
        id IN (
            SELECT
                child
            FROM
                projects_tree
            WHERE
                parent = OLD.id AND depth > 0
        )
    ;

END;

/*
 Forbid moves that would create loops:
*/

CREATE TRIGGER
    tree_bu_projects_2
BEFORE UPDATE OF
    parent_id
ON
    projects
FOR EACH ROW WHEN
    NEW.parent_id IS NOT NULL AND
    (SELECT
        COUNT(child) > 0
     FROM
        projects_tree
     WHERE
        child = NEW.parent_id AND parent = NEW.id
    )
BEGIN
    SELECT RAISE (ABORT,
        'Update blocked, because it would create loop in tree.');
END;

/*
 This implementation doesn't support changes to the primary key
*/

CREATE TRIGGER
    tree_bu_projects_1
BEFORE UPDATE OF
    id
ON
    projects
FOR EACH ROW WHEN
    OLD.id != NEW.id
BEGIN
    SELECT RAISE (ABORT, 'Changing ids is forbidden.');
END;
