-- -- A "project" is one instance of the bug-tracker. -- create table bt_projects ( project_id integer not null constraint bt_projects_apm_packages_fk references apm_packages(package_id) on delete cascade constraint bt_projects_pk primary key, workflow_id integer constraint bt_projects_workflow_id_fk references workflows(workflow_id) on delete cascade, description text, -- short string will be included in the subject line of emails email_subject_name text, maintainer integer constraint bt_projects_maintainer_fk references users(user_id), folder_id integer constraint bt_projects_folder_fk references cr_folders(folder_id), root_keyword_id integer constraint bt_projects_keyword_fk references cr_keywords(keyword_id) ); -- added select define_function_args('bt_project__new','package_id'); -- -- procedure bt_project__new/1 -- CREATE OR REPLACE FUNCTION bt_project__new( p_package_id integer ) RETURNS integer AS $$ DECLARE v_count integer; v_instance_name varchar; v_creation_user integer; v_creation_ip varchar; v_folder_id integer; v_keyword_id integer; BEGIN select count(*) into v_count from bt_projects where project_id = p_package_id; if v_count > 0 then return 0; end if; -- get instance name for the content folder select p.instance_name, o.creation_user, o.creation_ip into v_instance_name, v_creation_user, v_creation_ip from apm_packages p join acs_objects o on (p.package_id = o.object_id) where p.package_id = p_package_id; -- create a root CR folder v_folder_id := content_folder__new( 'bug_tracker_' || p_package_id, -- name v_instance_name, -- label null, -- description content_item_globals.c_root_folder_id, -- parent_id p_package_id, -- context_id null, -- folder_id now(), -- creation_date v_creation_user, -- creation_user v_creation_ip, -- creation_ip, 't', -- security_inherit_p p_package_id -- package_id ); -- Set package_id column. Oddly enoguh, there is no API to set it update cr_folders set package_id = p_package_id where folder_id = v_folder_id; -- register our content type PERFORM content_folder__register_content_type ( v_folder_id, -- folder_id 'bt_bug_revision', -- content_type 't' -- include_subtypes ); -- create the instance root keyword v_keyword_id := content_keyword__new( v_instance_name, -- heading null, -- description null, -- parent_id null, -- keyword_id current_timestamp, -- creation_date v_creation_user, -- creation_user v_creation_ip, -- creation_ip 'content_keyword' -- object_type ); -- insert the row into bt_projects insert into bt_projects (project_id, folder_id, root_keyword_id) values (p_package_id, v_folder_id, v_keyword_id); -- Create a General component to start with insert into bt_components (component_id, project_id, component_name) select acs_object_id_seq.nextval, p_package_id, 'General'; return 0; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_project__delete','project_id'); -- -- procedure bt_project__delete/1 -- CREATE OR REPLACE FUNCTION bt_project__delete( p_project_id integer ) RETURNS integer AS $$ DECLARE v_folder_id integer; v_root_keyword_id integer; v_workflow_id integer; rec record; BEGIN -- get the content folder and workflow_id for this instance select folder_id, root_keyword_id, workflow_id into v_folder_id, v_root_keyword_id, v_workflow_id from bt_projects where project_id = p_project_id; if v_workflow_id is not null then perform workflow__delete(v_workflow_id); end if; -- This gets done in tcl before we are called ... for now -- Delete the bugs -- for rec in select item_id from cr_items where parent_id = v_folder_id -- loop -- perform bt_bug__delete(rec.item_id); -- end loop; -- Delete the patches for rec in select patch_id from bt_patches where project_id = p_project_id loop perform bt_patch__delete(rec.patch_id); end loop; -- delete the content folder raise notice 'about to delete content_folder.'; perform content_folder__delete(v_folder_id); -- delete the projects keywords perform bt_project__keywords_delete(p_project_id, 't'); -- These tables should really be set up to cascade delete from bt_versions where project_id = p_project_id; delete from bt_components where project_id = p_project_id; delete from bt_user_prefs where project_id = p_project_id; delete from bt_projects where project_id = p_project_id; return 0; END; $$ LANGUAGE plpgsql; -- -- procedure bt_project__keywords_delete/1 -- CREATE OR REPLACE FUNCTION bt_project__keywords_delete( integer, -- project_id bool -- delete_root_p ) RETURNS integer AS $$ DECLARE p_project_id alias for $1; p_delete_root_p alias for $1; v_root_keyword_id integer; rec record; BEGIN -- get the content folder for this instance select root_keyword_id into v_root_keyword_id from bt_projects where project_id = p_project_id; -- if we are deleting the root, remove it from the project as well if p_delete_root_p = 1 then update bt_projects set root_keyword_id = null where project_id = p_project_id; end if; -- delete the projects keywords for rec in select k2.keyword_id from cr_keywords k1, cr_keywords k2 where k1.keyword_id = v_root_keyword_id and k2.tree_sortkey between k1.tree_sortkey and tree_right(k1.tree_sortkey) order by length(k2.tree_sortkey) desc loop if (p_delete_root_p = 1) or (rec.keyword_id != v_root_keyword_id) then perform content_keyword__delete(rec.keyword_id); end if; end loop; return 0; END; $$ LANGUAGE plpgsql; create table bt_versions ( version_id integer not null constraint bt_versions_pk primary key, project_id integer not null constraint bt_versions_projects_fk references bt_projects(project_id), -- Like apm_package_versions.version_name -- But can also be a human-readable name like "Future", "Milestone 3", etc. version_name varchar(500) not null, description text, anticipated_freeze_date date, actual_freeze_date date, anticipated_release_date date, actual_release_date date, maintainer integer constraint bt_versions_maintainer_fk references users(user_id), supported_platforms varchar(1000), active_version_p char(1) not null constraint bt_versions_active_version_p_ck check (active_version_p in ('t','f')) default 'f', -- Can we assign bugs to be fixed for this version? assignable_p char(1) constraint bt_versions_assignable_p_ck check (assignable_p in ('t','f')) ); -- should probably have a trigger to ensure that there's only one active version. -- but we just make a stored function that alters the active version -- added select define_function_args('bt_version__set_active','active_version_id'); -- -- procedure bt_version__set_active/1 -- CREATE OR REPLACE FUNCTION bt_version__set_active( new__active_version_id integer ) RETURNS integer AS $$ DECLARE v_project_id integer; BEGIN select project_id into v_project_id from bt_versions where version_id = new__active_version_id; if found then update bt_versions set active_version_p='f' where project_id = v_project_id; end if; update bt_versions set active_version_p='t' where version_id = new__active_version_id; return 0; END; $$ LANGUAGE plpgsql; create table bt_components ( component_id integer not null constraint bt_components_pk primary key, project_id integer not null constraint bt_components_projects_fk references bt_projects(project_id), component_name varchar(500) not null, description text, -- This is what the component can be referred to in the URL url_name text, -- a component can be without maintainer, in which case we just default to the project maintainer maintainer integer constraint bt_components_maintainer_fk references users(user_id) ); -- default keywords per keyword parent -- e.g. default priority, default severity, etc. create table bt_default_keywords ( project_id integer not null constraint bt_default_keywords_project_fk references bt_projects(project_id) on delete cascade, parent_id integer not null constraint bt_default_keyw_parent_keyw_fk references cr_keywords(keyword_id) on delete cascade, keyword_id integer not null constraint bt_default_keyw_keyword_fk references cr_keywords(keyword_id) on delete cascade, constraint bt_default_keywords_prj_par_un unique (project_id, parent_id) ); create index bt_default_keyw_parent_id_idx on bt_default_keywords(parent_id); create index bt_default_keyw_keyword_id_idx on bt_default_keywords(keyword_id); -- content_item subtype create table bt_bugs( bug_id integer constraint bt_bug_pk primary key constraint bt_bug_bt_bug_fk references cr_items(item_id) on delete cascade, -- this is the only column we really add here bug_number integer, -- the comment from the initial action -- denormalized from a far-fetched workflow join comment_content text, comment_format varchar(200), -- denormalized from cr_items parent_id integer, live_revision_id integer, -- denormalized from cr_revisions.title summary varchar(1000), -- denormalized from bt_projects project_id integer, -- denormalized from bt_bug_revisions component_id integer, resolution varchar(50), user_agent varchar(500), found_in_version integer, fix_for_version integer, fixed_in_version integer, -- denormalized from acs_objects creation_date timestamptz, creation_user integer, -- constraint constraint bt_bug_parent_id_bug_number_un unique (parent_id, bug_number) ); -- LARS: -- we need to figure out which ones of these will be used by the query optimizer create index bt_bugs_proj_id_bug_number_idx on bt_bugs(project_id, bug_number); create index bt_bugs_bug_number_idx on bt_bugs(bug_number); create index bt_bugs_proj_id_fix_for_idx on bt_bugs(project_id, fix_for_version); create index bt_bugs_fix_for_version_idx on bt_bugs(fix_for_version); create index bt_bugs_proj_id_crea_date_idx on bt_bugs(project_id, creation_date); create index bt_bugs_creation_date_idx on bt_bugs(creation_date); create index bt_bugs_creation_user_idx on bt_bugs(creation_user); -- Create the bug content item object type select acs_object_type__create_type ( 'bt_bug', 'Bug', 'Bugs', 'acs_object', 'bt_bugs', 'bug_id', null, 'f', null, 'bt_bug__name' ); -- content_revision specialization create table bt_bug_revisions ( bug_revision_id integer constraint bt_bug_rev_pk primary key constraint bt_bug_rev_bug_id_fk references cr_revisions(revision_id) on delete cascade, component_id integer constraint bt_bug_rev_components_fk references bt_components(component_id), resolution varchar(50) constraint bt_bug_rev_resolution_ck check (resolution is null or resolution in ('fixed','bydesign','wontfix','postponed','duplicate','norepro','needinfo')), user_agent varchar(500), found_in_version integer constraint bt_bug_rev_found_in_version_fk references bt_versions(version_id), fix_for_version integer constraint bt_bug_rev_fix_for_version_fk references bt_versions(version_id), fixed_in_version integer constraint bt_bug_rev_fixed_in_version_fk references bt_versions(version_id) ); -- Create the bug revision content type select content_type__create_type ( 'bt_bug_revision', 'content_revision', 'Bug Revision', 'Bug Revisions', 'btbug_revisions', 'bug_revision_id', 'content_revision.revision_name' ); -- old define_function_args ('bt_bug__new','bug_id,bug_number,package_id,component_id,found_in_version,summary,user_agent,comment_content,comment_formt,creation_date,creation_user,creation_ip,fix_for_version,item_subtype;bt_bug,content_type;bt_bug_revision') -- new select define_function_args('bt_bug__new','bug_id,bug_number,package_id,component_id,found_in_version,summary,user_agent,comment_content,comment_format,creation_date,creation_user,creation_ip,fix_for_version,item_subtype;bt_bug,content_type;bt_bug_revision'); -- -- procedure bt_bug__new/15 -- CREATE OR REPLACE FUNCTION bt_bug__new( p_bug_id integer, p_bug_number integer, p_package_id integer, p_component_id integer, p_found_in_version integer, p_summary varchar, p_user_agent varchar, p_comment_content text, p_comment_format varchar, p_creation_date timestamptz, p_creation_user integer, p_creation_ip varchar, p_fix_for_version integer, p_item_subtype varchar, -- default 'bt_bug' p_content_type varchar -- default 'bt_bug_revision' ) RETURNS int AS $$ DECLARE v_bug_id integer; v_revision_id integer; v_bug_number integer; v_folder_id integer; BEGIN -- get the content folder for this instance select folder_id into v_folder_id from bt_projects where project_id = p_package_id; -- get bug_number if p_bug_number is null then select coalesce(max(bug_number),0) + 1 into v_bug_number from bt_bugs where parent_id = v_folder_id; else v_bug_number := p_bug_number; end if; -- create the content item v_bug_id := content_item__new( v_bug_number::varchar, -- name v_folder_id, -- parent_id p_bug_id, -- item_id null, -- locale p_creation_date, -- creation_date p_creation_user, -- creation_user v_folder_id, -- context_id p_creation_ip, -- creation_ip p_item_subtype, -- item_subtype p_content_type, -- content_type null, -- title null, -- description null, -- mime_type null, -- nls_language null, -- data p_package_id ); -- create the item type row insert into bt_bugs (bug_id, bug_number, comment_content, comment_format, parent_id, project_id, creation_date, creation_user, fix_for_version) values (v_bug_id, v_bug_number, p_comment_content, p_comment_format, v_folder_id, p_package_id, p_creation_date, p_creation_user, p_fix_for_version); -- create the initial revision v_revision_id := bt_bug_revision__new( null, -- bug_revision_id v_bug_id, -- bug_id p_component_id, -- component_id p_found_in_version, -- found_in_version p_fix_for_version, -- fix_for_version null, -- fixed_in_version null, -- resolution p_user_agent, -- user_agent p_summary, -- summary p_creation_date, -- creation_date p_creation_user, -- creation_user p_creation_ip -- creation_ip ); return v_bug_id; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_bug__delete','bug_id'); -- -- procedure bt_bug__delete/1 -- CREATE OR REPLACE FUNCTION bt_bug__delete( p_bug_id integer ) RETURNS integer AS $$ DECLARE v_case_id integer; rec record; BEGIN -- Every bug is associated with a workflow case select case_id into v_case_id from workflow_cases where object_id = p_bug_id; perform workflow_case_pkg__delete(v_case_id); -- Every bug may have notifications attached to it -- and there is one column in the notificaitons datamodel that doesn't -- cascade for rec in select notification_id from notifications where response_id = p_bug_id loop perform notification__delete (rec.notification_id); end loop; -- unset live & latest revision -- update cr_items -- set live_revision = null, -- latest_revision = null -- where item_id = p_bug_id; perform content_item__delete(p_bug_id); return 0; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_bug_revision__new','bug_revision_id,bug_id,component_id,found_in_version,fix_for_version,fixed_in_version,resolution,user_agent,summary,creation_date,creation_user,creation_ip'); -- -- procedure bt_bug_revision__new/12 -- CREATE OR REPLACE FUNCTION bt_bug_revision__new( p_bug_revision_id integer, p_bug_id integer, p_component_id integer, p_found_in_version integer, p_fix_for_version integer, p_fixed_in_version integer, p_resolution varchar, p_user_agent varchar, p_summary varchar, p_creation_date timestamptz, p_creation_user integer, p_creation_ip varchar ) RETURNS int AS $$ DECLARE v_revision_id integer; BEGIN -- create the initial revision v_revision_id := content_revision__new( p_summary, -- title null, -- description current_timestamp, -- publish_date null, -- mime_type null, -- nls_language null, -- new_data p_bug_id, -- item_id p_bug_revision_id, -- revision_id p_creation_date, -- creation_date p_creation_user, -- creation_user p_creation_ip, -- creation_ip null, -- content_length null -- package_id ); -- insert into the bug-specific revision table insert into bt_bug_revisions (bug_revision_id, component_id, resolution, user_agent, found_in_version, fix_for_version, fixed_in_version) values (v_revision_id, p_component_id, p_resolution, p_user_agent, p_found_in_version, p_fix_for_version, p_fixed_in_version); -- make this revision live PERFORM content_item__set_live_revision(v_revision_id); -- update the cache update bt_bugs set live_revision_id = v_revision_id, summary = p_summary, component_id = p_component_id, resolution = p_resolution, user_agent = p_user_agent, found_in_version = p_found_in_version, fix_for_version = p_fix_for_version, fixed_in_version = p_fixed_in_version where bug_id = p_bug_id; -- update the title in acs_objects update acs_objects set title = bt_bug__name(p_bug_id) where object_id = p_bug_id; return v_revision_id; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_bug__name','bug_id'); -- -- procedure bt_bug__name/1 -- CREATE OR REPLACE FUNCTION bt_bug__name( p_bug_id integer ) RETURNS varchar AS $$ DECLARE v_name varchar; BEGIN select 'Bug #'||bug_number||': '||summary into v_name from bt_bugs where bug_id = p_bug_id; return v_name; END; $$ LANGUAGE plpgsql; create table bt_user_prefs ( user_id integer not null constraint bt_user_prefs_user_id_fk references users(user_id), project_id integer not null constraint bt_user_prefs_project_fk references bt_projects(project_id), user_version integer constraint bt_user_prefs_current_version_fk references bt_versions(version_id), constraint bt_user_prefs_pk primary key (user_id, project_id) ); create table bt_patches ( patch_id integer constraint bt_patches_pk primary key constraint bt_patches_pid_fk references acs_objects(object_id), patch_number integer not null, project_id integer constraint bt_patches_projects_fk references bt_projects(project_id), component_id integer constraint bt_patches_components_fk references bt_components(component_id), summary text, content text, generated_from_version integer constraint bt_patches_vid_fk references bt_versions(version_id), apply_to_version integer constraint bt_patchs_apply_to_version_fk references bt_versions(version_id), applied_to_version integer constraint bt_patchs_applied_to_version_fk references bt_versions(version_id), status varchar(50) not null constraint bt_patchs_status_ck check (status in ('open', 'accepted', 'refused', 'deleted')) default 'open', constraint bt_patches_un unique(patch_number, project_id) ); create table bt_patch_actions ( action_id integer not null constraint bt_patch_actions_pk primary key, patch_id integer not null constraint bt_patch_actions_patch_fk references bt_patches(patch_id) on delete cascade, action varchar(50) constraint bt_patch_actions_action_ck check (action in ('open', 'edit', 'comment', 'accept', 'reopen', 'refuse', 'delete')) default 'open', actor integer not null constraint bt_patch_actions_actor_fk references users(user_id), action_date timestamptz not null default current_timestamp, comment_text text, comment_format varchar(30) default 'plain' not null constraint bt_patch_actions_comment_format_ck check (comment_format in ('html', 'plain', 'pre')) ); -- Create the bt_patch object type select acs_object_type__create_type ( 'bt_patch', 'Patch', 'Patches', 'acs_object', 'bt_patches', 'patch_id', null, 'f', null, 'bt_patch__name' ); -- added select define_function_args('bt_patch__new','patch_id,project_id,component_id,summary,description,description_format,content,generated_from_version,creation_user,creation_ip'); -- -- procedure bt_patch__new/10 -- CREATE OR REPLACE FUNCTION bt_patch__new( p_patch_id integer, p_project_id integer, p_component_id integer, p_summary text, p_description text, p_description_format text, p_content text, p_generated_from_version integer, p_creation_user integer, p_creation_ip varchar ) RETURNS int AS $$ DECLARE v_patch_id integer; v_patch_number integer; v_action_id integer; BEGIN v_patch_id := acs_object__new( p_patch_id, -- object_id 'bt_patch', -- object_type current_timestamp, -- creation_date p_creation_user, -- creation_user p_creation_ip, -- creation_ip p_project_id, -- context_id null, -- title p_project_id -- package_id ); select coalesce(max(patch_number),0) + 1 into v_patch_number from bt_patches where project_id = p_project_id; insert into bt_patches (patch_id, project_id, component_id, summary, content, generated_from_version, patch_number) values (v_patch_id, p_project_id, p_component_id, p_summary, p_content, p_generated_from_version, v_patch_number); update acs_objects set title = bt_patch__name(v_patch_id) where object_id = v_patch_id; select nextval('t_acs_object_id_seq') into v_action_id; insert into bt_patch_actions (action_id, patch_id, action, actor, comment_text, comment_format) values (v_action_id, v_patch_id, 'open', p_creation_user, p_description, p_description_format); return v_patch_id; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_patch__name','patch_id'); -- -- procedure bt_patch__name/1 -- CREATE OR REPLACE FUNCTION bt_patch__name( p_patch_id integer ) RETURNS varchar AS $$ DECLARE v_name varchar; BEGIN select 'Patch #' || patch_number || ': ' || summary into v_name from bt_patches where patch_id = p_patch_id; return v_name; END; $$ LANGUAGE plpgsql; -- added select define_function_args('bt_patch__delete','patch_id'); -- -- procedure bt_patch__delete/1 -- CREATE OR REPLACE FUNCTION bt_patch__delete( p_patch_id integer ) RETURNS integer AS $$ DECLARE BEGIN perform acs_object__delete(p_patch_id); return 0; END; $$ LANGUAGE plpgsql; -- There is a many to many relationship between patches and bugs create table bt_patch_bug_map ( patch_id integer not null constraint bt_patch_bug_map_pid_fk references bt_patches(patch_id) on delete cascade, bug_id integer not null constraint bt_patch_bug_map_bid_fk references cr_items(item_id) on delete cascade, constraint bt_patch_bug_map_un unique (patch_id, bug_id) ); create index bt_patch_bug_map_patch_id_idx on bt_patch_bug_map(patch_id); create index bt_patch_bug_map_bug_id_idx on bt_patch_bug_map(bug_id); \i bug-tracker-search-triggers-create.sql -- AutoSubmition functionality \i auto-error-report.sql