]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix crash in contrib/ltree's lca() function for empty input array.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Jul 2018 22:45:30 +0000 (18:45 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Jul 2018 22:45:30 +0000 (18:45 -0400)
lca_inner() wasn't prepared for the possibility of getting no inputs.
Fix that, and make some cosmetic improvements to the code while at it.

Also, I thought the documentation of this function as returning the
"longest common prefix" of the paths was entirely misleading; it really
returns a path one shorter than the longest common prefix, for the typical
definition of "prefix".  Don't use that term in the docs, and adjust the
examples to clarify what really happens.

This has been broken since its beginning, so back-patch to all supported
branches.

Per report from Hailong Li.  Thanks to Pierre Ducroquet for diagnosing
and for the initial patch, though I whacked it around some and added
test cases.

Discussion: https://postgr.es/m/5b0d8e4f-f2a3-1305-d612-e00e35a7be66@qunar.com

contrib/ltree/expected/ltree.out
contrib/ltree/ltree_op.c
contrib/ltree/sql/ltree.sql
doc/src/sgml/ltree.sgml

index 7e51ce3e2ba72d5d8efb065bc928fdd8f1cfe7b5..fef2d40f173752b420ef69a2bdb3ee628ccf4ed2 100644 (file)
@@ -251,6 +251,24 @@ SELECT lca('{1.2.3,1.2.3.4.5.6}');
  1.2
 (1 row)
 
+SELECT lca('{1.2.3}');
+ lca 
+-----
+ 1.2
+(1 row)
+
+SELECT lca('{1}'), lca('{1}') IS NULL;
+ lca | ?column? 
+-----+----------
+     | f
+(1 row)
+
+SELECT lca('{}') IS NULL;
+ ?column? 
+----------
+ t
+(1 row)
+
 SELECT lca('1.la.2.3','1.2.3.4.5.6');
  lca 
 -----
index 88f6d5a85c21d1b46d411b798b8ee5aff7e5b4c4..b08f8192a8b22f3dd9c2d773669042fbc4abccd3 100644 (file)
@@ -421,22 +421,34 @@ ltree_textadd(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(r);
 }
 
+/*
+ * Common code for variants of lca(), find longest common ancestor of inputs
+ *
+ * Returns NULL if there is no common ancestor, ie, the longest common
+ * prefix is empty.
+ */
 ltree *
 lca_inner(ltree **a, int len)
 {
        int                     tmp,
-                               num = ((*a)->numlevel) ? (*a)->numlevel - 1 : 0;
-       ltree     **ptr = a + 1;
-       int                     i,
-                               reslen = LTREE_HDRSIZE;
+                               num,
+                               i,
+                               reslen;
+       ltree     **ptr;
        ltree_level *l1,
                           *l2;
        ltree      *res;
 
-
+       if (len <= 0)
+               return NULL;                    /* no inputs? */
        if ((*a)->numlevel == 0)
-               return NULL;
+               return NULL;                    /* any empty input means NULL result */
+
+       /* num is the length of the longest common ancestor so far */
+       num = (*a)->numlevel - 1;
 
+       /* Compare each additional input to *a */
+       ptr = a + 1;
        while (ptr - a < len)
        {
                if ((*ptr)->numlevel == 0)
@@ -447,11 +459,12 @@ lca_inner(ltree **a, int len)
                {
                        l1 = LTREE_FIRST(*a);
                        l2 = LTREE_FIRST(*ptr);
-                       tmp = num;
+                       tmp = Min(num, (*ptr)->numlevel - 1);
                        num = 0;
-                       for (i = 0; i < Min(tmp, (*ptr)->numlevel - 1); i++)
+                       for (i = 0; i < tmp; i++)
                        {
-                               if (l1->len == l2->len && memcmp(l1->name, l2->name, l1->len) == 0)
+                               if (l1->len == l2->len &&
+                                       memcmp(l1->name, l2->name, l1->len) == 0)
                                        num = i + 1;
                                else
                                        break;
@@ -462,6 +475,8 @@ lca_inner(ltree **a, int len)
                ptr++;
        }
 
+       /* Now compute size of result ... */
+       reslen = LTREE_HDRSIZE;
        l1 = LTREE_FIRST(*a);
        for (i = 0; i < num; i++)
        {
@@ -469,6 +484,7 @@ lca_inner(ltree **a, int len)
                l1 = LEVEL_NEXT(l1);
        }
 
+       /* ... and construct it by copying from *a */
        res = (ltree *) palloc0(reslen);
        SET_VARSIZE(res, reslen);
        res->numlevel = num;
index eb1beaef31f89cd793640bc889fc3c0df6e5f6da..4dbc14d67a3f28668bb4b6ba25fcdf799e6de643 100644 (file)
@@ -49,6 +49,9 @@ SELECT lca('{la.2.3,1.2.3.4.5.6,""}') IS NULL;
 SELECT lca('{la.2.3,1.2.3.4.5.6}') IS NULL;
 SELECT lca('{1.la.2.3,1.2.3.4.5.6}');
 SELECT lca('{1.2.3,1.2.3.4.5.6}');
+SELECT lca('{1.2.3}');
+SELECT lca('{1}'), lca('{1}') IS NULL;
+SELECT lca('{}') IS NULL;
 SELECT lca('1.la.2.3','1.2.3.4.5.6');
 SELECT lca('1.2.3','1.2.3.4.5.6');
 SELECT lca('1.2.2.3','1.2.3.4.5.6');
index 9855d2c9c89fb9312d4a3dec0339939551f95da1..6f284257f5130cd57764db06ae79efe49055789e 100644 (file)
@@ -457,17 +457,17 @@ Europe &amp; Russia*@ &amp; !Transportation
      <row>
       <entry><function>lca(ltree, ltree, ...)</function><indexterm><primary>lca</primary></indexterm></entry>
       <entry><type>ltree</type></entry>
-      <entry>lowest common ancestor, i.e., longest common prefix of paths
+      <entry>longest common ancestor of paths
        (up to 8 arguments supported)</entry>
-      <entry><literal>lca('1.2.2.3','1.2.3.4.5.6')</literal></entry>
+      <entry><literal>lca('1.2.3','1.2.3.4.5.6')</literal></entry>
       <entry><literal>1.2</literal></entry>
      </row>
 
      <row>
       <entry><function>lca(ltree[])</function></entry>
       <entry><type>ltree</type></entry>
-      <entry>lowest common ancestor, i.e., longest common prefix of paths</entry>
-      <entry><literal>lca(array['1.2.2.3'::ltree,'1.2.3'])</literal></entry>
+      <entry>longest common ancestor of paths in array</entry>
+      <entry><literal>lca(array['1.2.3'::ltree,'1.2.3.4'])</literal></entry>
       <entry><literal>1.2</literal></entry>
      </row>