Jul 152016
 

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:

  1. ports_vulnerable
  2. ports_moved
  3. ports_updating_xref
  4. 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';
Website Pin Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google StumbleUpon Premium Responsive