diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 7e51ce3e2b..fef2d40f17 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -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 ----- diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c index aa1e9918be..cb59d21ced 100644 --- a/contrib/ltree/ltree_op.c +++ b/contrib/ltree/ltree_op.c @@ -402,22 +402,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) @@ -428,11 +440,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; @@ -443,6 +456,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++) { @@ -450,6 +465,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; diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index eb1beaef31..4dbc14d67a 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -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'); diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml index f42e0bf88c..77599adeaf 100644 --- a/doc/src/sgml/ltree.sgml +++ b/doc/src/sgml/ltree.sgml @@ -457,17 +457,17 @@ Europe & Russia*@ & !Transportation lca(ltree, ltree, ...)lca ltree - lowest common ancestor, i.e., longest common prefix of paths + longest common ancestor of paths (up to 8 arguments supported) - lca('1.2.2.3','1.2.3.4.5.6') + lca('1.2.3','1.2.3.4.5.6') 1.2 lca(ltree[]) ltree - lowest common ancestor, i.e., longest common prefix of paths - lca(array['1.2.2.3'::ltree,'1.2.3']) + longest common ancestor of paths in array + lca(array['1.2.3'::ltree,'1.2.3.4']) 1.2