2002-07-30 18:40:34 +02:00
|
|
|
/*
|
2010-09-20 22:08:53 +02:00
|
|
|
* contrib/ltree/_ltree_op.c
|
2008-05-17 03:28:26 +02:00
|
|
|
*
|
|
|
|
*
|
2002-09-04 22:31:48 +02:00
|
|
|
* op function for ltree[]
|
2002-07-30 18:40:34 +02:00
|
|
|
* Teodor Sigaev <teodor@stack.net>
|
|
|
|
*/
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "postgres.h"
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
#include <ctype.h>
|
2008-05-12 02:00:54 +02:00
|
|
|
|
|
|
|
#include "ltree.h"
|
Split up guc.c for better build speed and ease of maintenance.
guc.c has grown to be one of our largest .c files, making it
a bottleneck for compilation. It's also acquired a bunch of
knowledge that'd be better kept elsewhere, because of our not
very good habit of putting variable-specific check hooks here.
Hence, split it up along these lines:
* guc.c itself retains just the core GUC housekeeping mechanisms.
* New file guc_funcs.c contains the SET/SHOW interfaces and some
SQL-accessible functions for GUC manipulation.
* New file guc_tables.c contains the data arrays that define the
built-in GUC variables, along with some already-exported constant
tables.
* GUC check/assign/show hook functions are moved to the variable's
home module, whenever that's clearly identifiable. A few hard-
to-classify hooks ended up in commands/variable.c, which was
already a home for miscellaneous GUC hook functions.
To avoid cluttering a lot more header files with #include "guc.h",
I also invented a new header file utils/guc_hooks.h and put all
the GUC hook functions' declarations there, regardless of their
originating module. That allowed removal of #include "guc.h"
from some existing headers. The fallout from that (hopefully
all caught here) demonstrates clearly why such inclusions are
best minimized: there are a lot of files that, for example,
were getting array.h at two or more levels of remove, despite
not having any connection at all to GUCs in themselves.
There is some very minor code beautification here, such as
renaming a couple of inconsistently-named hook functions
and improving some comments. But mostly this just moves
code from point A to point B and deals with the ensuing
needs for #include adjustments and exporting a few functions
that previously weren't exported.
Patch by me, per a suggestion from Andres Freund; thanks also
to Michael Paquier for the idea to invent guc_funcs.c.
Discussion: https://postgr.es/m/587607.1662836699@sss.pgh.pa.us
2022-09-13 17:05:07 +02:00
|
|
|
#include "utils/array.h"
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_isparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_r_isparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_risparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_r_risparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltq_regex);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltq_rregex);
|
2003-02-19 04:50:09 +01:00
|
|
|
PG_FUNCTION_INFO_V1(_lt_q_regex);
|
|
|
|
PG_FUNCTION_INFO_V1(_lt_q_rregex);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_FUNCTION_INFO_V1(_ltxtq_exec);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltxtq_rexec);
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_extract_isparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltree_extract_risparent);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltq_extract_regex);
|
|
|
|
PG_FUNCTION_INFO_V1(_ltxtq_extract_exec);
|
|
|
|
|
2002-08-04 07:02:50 +02:00
|
|
|
PG_FUNCTION_INFO_V1(_lca);
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
typedef Datum (*PGCALL2) (PG_FUNCTION_ARGS);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
|
|
|
|
|
|
|
|
static bool
|
2009-06-11 16:49:15 +02:00
|
|
|
array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
|
2002-09-04 22:31:48 +02:00
|
|
|
{
|
|
|
|
int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
|
|
|
|
ltree *item = (ltree *) ARR_DATA_PTR(la);
|
|
|
|
|
2010-02-24 19:02:24 +01:00
|
|
|
if (ARR_NDIM(la) > 1)
|
2003-07-24 19:52:50 +02:00
|
|
|
ereport(ERROR,
|
2003-08-04 02:43:34 +02:00
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("array must be one-dimensional")));
|
2011-01-09 19:09:07 +01:00
|
|
|
if (array_contains_nulls(la))
|
2005-11-19 03:08:45 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("array must not contain nulls")));
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
if (found)
|
|
|
|
*found = NULL;
|
|
|
|
while (num > 0)
|
|
|
|
{
|
|
|
|
if (DatumGetBool(DirectFunctionCall2(callback,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
PointerGetDatum(item), PointerGetDatum(param))))
|
2002-09-04 22:31:48 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
if (found)
|
2002-07-30 18:40:34 +02:00
|
|
|
*found = item;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
num--;
|
2002-09-04 22:31:48 +02:00
|
|
|
item = NEXTVAL(item);
|
2002-07-30 18:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltree_isparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltree *query = PG_GETARG_LTREE_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
bool res = array_iterator(la, ltree_isparent, (void *) query, NULL);
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltree_r_isparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
PG_RETURN_DATUM(DirectFunctionCall2(_ltree_isparent,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_GETARG_DATUM(0)
|
|
|
|
));
|
2002-07-30 18:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltree_risparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltree *query = PG_GETARG_LTREE_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
bool res = array_iterator(la, ltree_risparent, (void *) query, NULL);
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltree_r_risparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
PG_RETURN_DATUM(DirectFunctionCall2(_ltree_risparent,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_GETARG_DATUM(0)
|
|
|
|
));
|
2002-07-30 18:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltq_regex(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
lquery *query = PG_GETARG_LQUERY_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
bool res = array_iterator(la, ltq_regex, (void *) query, NULL);
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltq_rregex(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
PG_RETURN_DATUM(DirectFunctionCall2(_ltq_regex,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_GETARG_DATUM(0)
|
|
|
|
));
|
2002-07-30 18:40:34 +02:00
|
|
|
}
|
|
|
|
|
2003-02-19 04:50:09 +01:00
|
|
|
Datum
|
|
|
|
_lt_q_regex(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
ArrayType *_tree = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1);
|
|
|
|
lquery *query = (lquery *) ARR_DATA_PTR(_query);
|
|
|
|
bool res = false;
|
|
|
|
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
|
|
|
|
|
2010-02-24 19:02:24 +01:00
|
|
|
if (ARR_NDIM(_query) > 1)
|
2003-08-04 02:43:34 +02:00
|
|
|
ereport(ERROR,
|
2003-07-24 19:52:50 +02:00
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("array must be one-dimensional")));
|
2011-01-09 19:09:07 +01:00
|
|
|
if (array_contains_nulls(_query))
|
2005-11-19 03:08:45 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("array must not contain nulls")));
|
2003-02-19 04:50:09 +01:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
while (num > 0)
|
|
|
|
{
|
|
|
|
if (array_iterator(_tree, ltq_regex, (void *) query, NULL))
|
|
|
|
{
|
|
|
|
res = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
num--;
|
|
|
|
query = (lquery *) NEXTVAL(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(_tree, 0);
|
|
|
|
PG_FREE_IF_COPY(_query, 1);
|
|
|
|
PG_RETURN_BOOL(res);
|
2003-02-19 04:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
_lt_q_rregex(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
PG_RETURN_DATUM(DirectFunctionCall2(_lt_q_regex,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_GETARG_DATUM(0)
|
|
|
|
));
|
2003-02-19 04:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
Datum
|
|
|
|
_ltxtq_exec(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltxtquery *query = PG_GETARG_LTXTQUERY_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
bool res = array_iterator(la, ltxtq_exec, (void *) query, NULL);
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_BOOL(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_ltxtq_rexec(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
PG_RETURN_DATUM(DirectFunctionCall2(_ltxtq_exec,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_GETARG_DATUM(0)
|
|
|
|
));
|
2002-07-30 18:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
Datum
|
|
|
|
_ltree_extract_isparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltree *query = PG_GETARG_LTREE_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
ltree *found,
|
|
|
|
*item;
|
|
|
|
|
|
|
|
if (!array_iterator(la, ltree_isparent, (void *) query, &found))
|
|
|
|
{
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
2016-03-08 23:59:29 +01:00
|
|
|
item = (ltree *) palloc0(VARSIZE(found));
|
2007-02-28 23:44:38 +01:00
|
|
|
memcpy(item, found, VARSIZE(found));
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_POINTER(item);
|
|
|
|
}
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
Datum
|
|
|
|
_ltree_extract_risparent(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltree *query = PG_GETARG_LTREE_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
ltree *found,
|
|
|
|
*item;
|
|
|
|
|
|
|
|
if (!array_iterator(la, ltree_risparent, (void *) query, &found))
|
|
|
|
{
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
2016-03-08 23:59:29 +01:00
|
|
|
item = (ltree *) palloc0(VARSIZE(found));
|
2007-02-28 23:44:38 +01:00
|
|
|
memcpy(item, found, VARSIZE(found));
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_POINTER(item);
|
|
|
|
}
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
Datum
|
|
|
|
_ltq_extract_regex(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
lquery *query = PG_GETARG_LQUERY_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
ltree *found,
|
|
|
|
*item;
|
|
|
|
|
|
|
|
if (!array_iterator(la, ltq_regex, (void *) query, &found))
|
|
|
|
{
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
2016-03-08 23:59:29 +01:00
|
|
|
item = (ltree *) palloc0(VARSIZE(found));
|
2007-02-28 23:44:38 +01:00
|
|
|
memcpy(item, found, VARSIZE(found));
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_POINTER(item);
|
|
|
|
}
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
Datum
|
|
|
|
_ltxtq_extract_exec(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
2017-09-18 21:21:23 +02:00
|
|
|
ltxtquery *query = PG_GETARG_LTXTQUERY_P(1);
|
2002-09-04 22:31:48 +02:00
|
|
|
ltree *found,
|
|
|
|
*item;
|
|
|
|
|
|
|
|
if (!array_iterator(la, ltxtq_exec, (void *) query, &found))
|
|
|
|
{
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
2016-03-08 23:59:29 +01:00
|
|
|
item = (ltree *) palloc0(VARSIZE(found));
|
2007-02-28 23:44:38 +01:00
|
|
|
memcpy(item, found, VARSIZE(found));
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
PG_FREE_IF_COPY(la, 0);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
2002-07-30 18:40:34 +02:00
|
|
|
PG_RETURN_POINTER(item);
|
|
|
|
}
|
|
|
|
|
2002-08-04 07:02:50 +02:00
|
|
|
Datum
|
2002-09-04 22:31:48 +02:00
|
|
|
_lca(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
|
|
|
|
ltree *item = (ltree *) ARR_DATA_PTR(la);
|
|
|
|
ltree **a,
|
|
|
|
*res;
|
|
|
|
|
2010-02-24 19:02:24 +01:00
|
|
|
if (ARR_NDIM(la) > 1)
|
2005-11-19 03:08:45 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("array must be one-dimensional")));
|
2011-01-09 19:09:07 +01:00
|
|
|
if (array_contains_nulls(la))
|
2005-11-19 03:08:45 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("array must not contain nulls")));
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
a = (ltree **) palloc(sizeof(ltree *) * num);
|
|
|
|
while (num > 0)
|
|
|
|
{
|
2002-08-04 07:02:50 +02:00
|
|
|
num--;
|
|
|
|
a[num] = item;
|
|
|
|
item = NEXTVAL(item);
|
|
|
|
}
|
2002-09-04 22:31:48 +02:00
|
|
|
res = lca_inner(a, ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)));
|
2002-08-04 07:02:50 +02:00
|
|
|
pfree(a);
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
PG_FREE_IF_COPY(la, 0);
|
2002-08-04 07:02:50 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
if (res)
|
|
|
|
PG_RETURN_POINTER(res);
|
|
|
|
else
|
|
|
|
PG_RETURN_NULL();
|
2002-08-04 07:02:50 +02:00
|
|
|
}
|