About two weeks ago, I wrote a function for creating a port. This will take the port from head and copy it to a branch. This is vital because of the reasons discussed here: creating a new port and copying a port from head to a branch.
This function worked and was useful, but it was missed one vital part: population of the ports_categories table. This table tells us what categories the port is in. Now that I think of it, I may have to populate other tables, such as:
- ports_vulnerable
- ports_moved
- ports_updating_xref
- commit_log_ports_vuxml
Those can wait for another day.
Existing bugs fixed
While debugging this issue, I noticed that CopyPortFromHeadToBranch() was documented as returning the port id, but was returning the element id. The difference is that all ports are elements, but not all elements are ports. The code which invokes CopyPortFromHeadToBranch() actually creates the port, while the function just copies stuff around in the element table.
The diff is as follow:
-CREATE OR REPLACE FUNCTION CopyPortFromHeadToBranch(text, text, text) returns int4 AS $$ +CREATE OR REPLACE FUNCTION CopyPortFilesFromHeadToBranch(text, text, text) returns int4 AS $$ DECLARE a_category_name ALIAS for $1; a_port_name ALIAS for $2; a_branch_name ALIAS for $3; - l_r element_id_pathname%rowtype; - l_port_id int4; - + l_r element_id_pathname%rowtype; + l_element_id_of_new_port int4; + BEGIN - + + -- this query will add the new port... the key is the element_add() function call + FOR l_r IN WITH RECURSIVE all_descendents AS ( SELECT id, name, parent_id, directory_file_flag, status @@ -3243,20 +3248,22 @@ FROM element E JOIN all_descendents AD ON (E.parent_id = AD.id) -) - SELECT element_add(replace(element_pathname(id), '/ports/head/', '/ports/branches/' || a_branch_name || '/'), directory_file_flag), - replace(element_pathname(id), '/ports/head/', '/ports/branches/2016Q2/') + ) + SELECT element_add(replace(element_pathname(id), '/ports/head/', '/ports/branches/' || a_branch_name || '/'), directory_file_flag) FROM all_descendents WHERE status = 'A' LOOP END LOOP; - + IF FOUND THEN - SELECT pathname_id('/ports/branches/' || a_branch_name || '/' || a_category_name || '/' || a_port_name || '/') - INTO l_port_id; + -- based on the element_id of the pathname for newly created port, grab the port id + SELECT pathname_id('/ports/branches/' || a_branch_name || '/' || a_category_name || '/' || a_port_name || '/') + INTO l_element_id_of_new_port; + END IF; - - return l_port_id; + + return l_element_id_of_new_port; + END; $$ LANGUAGE 'plpgsql'; @@ -3278,12 +3285,27 @@ BEGIN - SELECT CopyPortFromHeadToBranch(a_category_name, a_port_name, a_branch_name) + SELECT CopyPortFilesFromHeadToBranch(a_category_name, a_port_name, a_branch_name) INTO l_element_id_of_new_port; IF FOUND THEN SELECT createport(l_element_id_of_new_port, getcategory(a_category_name)) INTO l_port_id; + + SELECT PA.id + FROM ports_active PA + WHERE PA.element_id = l_element_id_of_new_port + INTO l_port_id; + + -- we need to set up the port_categories table first... + + RAISE WARNING 'about to call GetPort and I only want stuff from head, so branch agnostic is OK.'; + + INSERT INTO ports_categories(port_id, category_id) + SELECT l_port_id, PC.category_id + FROM ports_categories PC + WHERE PC.port_id = GetPort(a_category_name, a_port_name); + END IF; RETURN l_port_id;
I will outline the problems fixed above:
- line 2 – Rename the function to reflect what it does
- line 11 – we are returning the element id of the new port
- line 28 – remove this extraneous parameter from the SELECT
- line 65 – grab the port id based on the element id and use that to copy values from the ports_categories from the old code to the new port
- line 74 – I had tried to do this INSERT within the CopyPortFilesFromHeadToBranch() function, but that can’t be done because the port is not created there, it is created on line 62. Once I realized that issue, which was perpetuated by the incorrectly named variable on line 39, it all became clear.
The working code
Here is the code which http://dev.freshports.org/ is using now.
-- -- this function copies a port files from /ports/head/CATEGORY/PORT to /ports/branches/BRANCH -- It returns the id of the new element for that port. -- This function DOES not create the port itself (i.e. no entries added to port table). -- CREATE OR REPLACE FUNCTION CopyPortFilesFromHeadToBranch(text, text, text) returns int4 AS $$ DECLARE a_category_name ALIAS for $1; a_port_name ALIAS for $2; a_branch_name ALIAS for $3; l_r element_id_pathname%rowtype; l_element_id_of_new_port int4; BEGIN -- this query will add the new port... the key is the element_add() function call FOR l_r IN WITH RECURSIVE all_descendents AS ( SELECT id, name, parent_id, directory_file_flag, status FROM element WHERE id = (select pathname_id('/ports/head/' || a_category_name || '/' || a_port_name || '/')) UNION SELECT E.id, E.name, E.parent_id, E.directory_file_flag, E.status FROM element E JOIN all_descendents AD ON (E.parent_id = AD.id) ) SELECT element_add(replace(element_pathname(id), '/ports/head/', '/ports/branches/' || a_branch_name || '/'), directory_file_flag) FROM all_descendents WHERE status = 'A' LOOP END LOOP; IF FOUND THEN -- based on the element_id of the pathname for newly created port, grab the port id SELECT pathname_id('/ports/branches/' || a_branch_name || '/' || a_category_name || '/' || a_port_name || '/') INTO l_element_id_of_new_port; END IF; return l_element_id_of_new_port; END; $$ LANGUAGE 'plpgsql'; -- -- this function create a port on a branch using head as a starting point. -- i.e. from /ports/head/CATEGORY/PORT to /ports/branches/BRANCH/CATEGORY/PORT -- is created. -- It returns the id of the new port. -- CREATE OR REPLACE FUNCTION CreatePort(text, text, text) returns int4 AS $$ DECLARE a_category_name ALIAS for $1; a_port_name ALIAS for $2; a_branch_name ALIAS for $3; l_element_id_of_new_port int4; l_port_id int4; BEGIN SELECT CopyPortFilesFromHeadToBranch(a_category_name, a_port_name, a_branch_name) INTO l_element_id_of_new_port; IF FOUND THEN SELECT createport(l_element_id_of_new_port, getcategory(a_category_name)) INTO l_port_id; SELECT PA.id FROM ports_active PA WHERE PA.element_id = l_element_id_of_new_port INTO l_port_id; -- we need to set up the port_categories table first... RAISE WARNING 'about to call GetPort and I only want stuff from head, so branch agnostic is OK.'; INSERT INTO ports_categories(port_id, category_id) SELECT l_port_id, PC.category_id FROM ports_categories PC WHERE PC.port_id = GetPort(a_category_name, a_port_name); END IF; RETURN l_port_id; END; $$ LANGUAGE 'plpgsql';