In my previous post, I wrote about the need to copy a port from head to a branch in some circumstances, specifically when a master-port is updated and the slave ports do not already exist in the FreshPorts database on that branch. This is a side-effect of FreshPorts only taking action for commits, and not repo-copies.
Get new pathnames
Expanding upon the recursive query from that post, I think I have a starting point for a copy-port function. Let me illustrate with this query. Assume we need to duplicate sysutils/bacula-client from head to branch 2016Q2.
WITH RECURSIVE all_descendents AS ( SELECT id, name, parent_id, directory_file_flag, status FROM element WHERE id = (select pathname_id('/ports/head/sysutils/bacula-client/')) 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 directory_file_flag, element_pathname(id) AS pathname, replace(element_pathname(id), '/ports/head/', '/ports/branches/2016Q2/') FROM all_descendents WHERE status = 'A' ORDER BY pathname; directory_file_flag | pathname | replace ---------------------+---------------------------------------------+-------------------------------------------------------- D | /ports/head/sysutils/bacula-client | /ports/branches/2016Q2/sysutils/bacula-client F | /ports/head/sysutils/bacula-client/Makefile | /ports/branches/2016Q2/sysutils/bacula-client/Makefile (2 rows)
The first column indicates file or directory. The second column is the name of the existing element in head. The third column is the name of the element to be created in the branch.
Add new pathnames to the database
Next, how do use that information to create the new elements?
We use the existing function: element_add($in_element_pathname, $element_directory_file_flag)
Let’s incorporate that into the query above and get:
WITH RECURSIVE all_descendents AS ( SELECT id, name, parent_id, directory_file_flag, status FROM element WHERE id = (select pathname_id('/ports/head/sysutils/bacula-client/')) 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/2016Q2/'), directory_file_flag), replace(element_pathname(id), '/ports/head/', '/ports/branches/2016Q2/'), directory_file_flag FROM all_descendents WHERE status = 'A'; element_add | replace | directory_file_flag -------------+--------------------------------------------------------+--------------------- 739280 | /ports/branches/2016Q2/sysutils/bacula-client | D 739281 | /ports/branches/2016Q2/sysutils/bacula-client/Makefile | F -- Let's confirm the new elements: WITH RECURSIVE all_descendents AS ( SELECT id, name, parent_id, directory_file_flag, status FROM element WHERE id = (select pathname_id('/ports/branches/2016Q2/sysutils/bacula-client/')) 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 id, name, parent_id, directory_file_flag, status, element_pathname(id) AS pathname FROM all_descendents WHERE status = 'A' ORDER BY pathname; id | name | parent_id | directory_file_flag | status | pathname --------+---------------+-----------+---------------------+--------+-------------------------------------------------------- 739280 | bacula-client | 732848 | D | A | /ports/branches/2016Q2/sysutils/bacula-client 739281 | Makefile | 739280 | F | A | /ports/branches/2016Q2/sysutils/bacula-client/Makefile (2 rows)
There they are!
Create the port
This creates the directory structure (i.e. pathnames) but it does not create the port. We can do that next with createport($port_element_id, $port_category_id). Let’s try this:
select createport(pathname_id(‘/ports/branches/2016Q2/sysutils/bacula-client’), getcategory(‘sysutils’));
Before we run that, let’s break down the individual components, because I found an error and I want to demonstrate it.
select pathname_id('/ports/branches/2016Q2/sysutils/bacula-client'); pathname_id ------------- 739280 (1 row)
You will see 739280 mentioned above in the output from creating the elements on the branch.
Next we grab the category details:
select getcategory('sysutils'); getcategory ------------- (1 row)
Eh? Woah, that’s not good.
Fix the category code
Let’s look at that code:
CREATE OR REPLACE FUNCTION GetCategory (text ) RETURNS int4 AS $$ DECLARE category_name ALIAS for $1; pathname text; category_element_id int4; category_id int4; BEGIN pathname := 'ports/' || category_name; category_element_id := Pathname_ID(pathname); if NOT category_element_id IS NULL THEN select id into category_id from categories where element_id = category_element_id; END IF; return category_id; END; $$ LANGUAGE 'plpgsql';
Ahh, look at line 8. That pathname needs to be prefixed with ‘ports/head/’. This change is related to Full pathnames, which was work carried out a few years ago. This function was used only by some one-off maintenance scripts.
After making that change, the results are as expected:
# select getcategory('sysutils'); getcategory ------------- 20 (1 row)
Back to creating the port
Let’s create that port:
# select createport(pathname_id('/ports/branches/2016Q2/sysutils/bacula-client'), getcategory('sysutils')); createport ------------ 41761 (1 row) select element_pathname(element_id) from ports where id = 41761; element_pathname ----------------------------------------------- /ports/branches/2016Q2/sysutils/bacula-client (1 row)
We have a port on the branch. It needs to be refreshed from the Makefile (via make -V), but that is for a later stage.
I think we are well on our way to copying a port upon demand.