1 /*
2  * contrib/ltree/_ltree_op.c
3  *
4  *
5  * op function for ltree[]
6  * Teodor Sigaev <teodor@stack.net>
7  */
8 #include "postgres.h"
9 
10 #include <ctype.h>
11 
12 #include "ltree.h"
13 
14 PG_FUNCTION_INFO_V1(_ltree_isparent);
15 PG_FUNCTION_INFO_V1(_ltree_r_isparent);
16 PG_FUNCTION_INFO_V1(_ltree_risparent);
17 PG_FUNCTION_INFO_V1(_ltree_r_risparent);
18 PG_FUNCTION_INFO_V1(_ltq_regex);
19 PG_FUNCTION_INFO_V1(_ltq_rregex);
20 PG_FUNCTION_INFO_V1(_lt_q_regex);
21 PG_FUNCTION_INFO_V1(_lt_q_rregex);
22 PG_FUNCTION_INFO_V1(_ltxtq_exec);
23 PG_FUNCTION_INFO_V1(_ltxtq_rexec);
24 
25 PG_FUNCTION_INFO_V1(_ltree_extract_isparent);
26 PG_FUNCTION_INFO_V1(_ltree_extract_risparent);
27 PG_FUNCTION_INFO_V1(_ltq_extract_regex);
28 PG_FUNCTION_INFO_V1(_ltxtq_extract_exec);
29 
30 PG_FUNCTION_INFO_V1(_lca);
31 
32 typedef Datum (*PGCALL2) (PG_FUNCTION_ARGS);
33 
34 #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
35 
36 static bool
array_iterator(ArrayType * la,PGCALL2 callback,void * param,ltree ** found)37 array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
38 {
39 	int			num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
40 	ltree	   *item = (ltree *) ARR_DATA_PTR(la);
41 
42 	if (ARR_NDIM(la) > 1)
43 		ereport(ERROR,
44 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
45 				 errmsg("array must be one-dimensional")));
46 	if (array_contains_nulls(la))
47 		ereport(ERROR,
48 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
49 				 errmsg("array must not contain nulls")));
50 
51 	if (found)
52 		*found = NULL;
53 	while (num > 0)
54 	{
55 		if (DatumGetBool(DirectFunctionCall2(callback,
56 											 PointerGetDatum(item), PointerGetDatum(param))))
57 		{
58 
59 			if (found)
60 				*found = item;
61 			return true;
62 		}
63 		num--;
64 		item = NEXTVAL(item);
65 	}
66 
67 	return false;
68 }
69 
70 Datum
_ltree_isparent(PG_FUNCTION_ARGS)71 _ltree_isparent(PG_FUNCTION_ARGS)
72 {
73 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
74 	ltree	   *query = PG_GETARG_LTREE_P(1);
75 	bool		res = array_iterator(la, ltree_isparent, (void *) query, NULL);
76 
77 	PG_FREE_IF_COPY(la, 0);
78 	PG_FREE_IF_COPY(query, 1);
79 	PG_RETURN_BOOL(res);
80 }
81 
82 Datum
_ltree_r_isparent(PG_FUNCTION_ARGS)83 _ltree_r_isparent(PG_FUNCTION_ARGS)
84 {
85 	PG_RETURN_DATUM(DirectFunctionCall2(_ltree_isparent,
86 										PG_GETARG_DATUM(1),
87 										PG_GETARG_DATUM(0)
88 										));
89 }
90 
91 Datum
_ltree_risparent(PG_FUNCTION_ARGS)92 _ltree_risparent(PG_FUNCTION_ARGS)
93 {
94 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
95 	ltree	   *query = PG_GETARG_LTREE_P(1);
96 	bool		res = array_iterator(la, ltree_risparent, (void *) query, NULL);
97 
98 	PG_FREE_IF_COPY(la, 0);
99 	PG_FREE_IF_COPY(query, 1);
100 	PG_RETURN_BOOL(res);
101 }
102 
103 Datum
_ltree_r_risparent(PG_FUNCTION_ARGS)104 _ltree_r_risparent(PG_FUNCTION_ARGS)
105 {
106 	PG_RETURN_DATUM(DirectFunctionCall2(_ltree_risparent,
107 										PG_GETARG_DATUM(1),
108 										PG_GETARG_DATUM(0)
109 										));
110 }
111 
112 Datum
_ltq_regex(PG_FUNCTION_ARGS)113 _ltq_regex(PG_FUNCTION_ARGS)
114 {
115 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
116 	lquery	   *query = PG_GETARG_LQUERY_P(1);
117 	bool		res = array_iterator(la, ltq_regex, (void *) query, NULL);
118 
119 	PG_FREE_IF_COPY(la, 0);
120 	PG_FREE_IF_COPY(query, 1);
121 	PG_RETURN_BOOL(res);
122 }
123 
124 Datum
_ltq_rregex(PG_FUNCTION_ARGS)125 _ltq_rregex(PG_FUNCTION_ARGS)
126 {
127 	PG_RETURN_DATUM(DirectFunctionCall2(_ltq_regex,
128 										PG_GETARG_DATUM(1),
129 										PG_GETARG_DATUM(0)
130 										));
131 }
132 
133 Datum
_lt_q_regex(PG_FUNCTION_ARGS)134 _lt_q_regex(PG_FUNCTION_ARGS)
135 {
136 	ArrayType  *_tree = PG_GETARG_ARRAYTYPE_P(0);
137 	ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
138 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
139 	bool		res = false;
140 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
141 
142 	if (ARR_NDIM(_query) > 1)
143 		ereport(ERROR,
144 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
145 				 errmsg("array must be one-dimensional")));
146 	if (array_contains_nulls(_query))
147 		ereport(ERROR,
148 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
149 				 errmsg("array must not contain nulls")));
150 
151 	while (num > 0)
152 	{
153 		if (array_iterator(_tree, ltq_regex, (void *) query, NULL))
154 		{
155 			res = true;
156 			break;
157 		}
158 		num--;
159 		query = (lquery *) NEXTVAL(query);
160 	}
161 
162 	PG_FREE_IF_COPY(_tree, 0);
163 	PG_FREE_IF_COPY(_query, 1);
164 	PG_RETURN_BOOL(res);
165 }
166 
167 Datum
_lt_q_rregex(PG_FUNCTION_ARGS)168 _lt_q_rregex(PG_FUNCTION_ARGS)
169 {
170 	PG_RETURN_DATUM(DirectFunctionCall2(_lt_q_regex,
171 										PG_GETARG_DATUM(1),
172 										PG_GETARG_DATUM(0)
173 										));
174 }
175 
176 
177 Datum
_ltxtq_exec(PG_FUNCTION_ARGS)178 _ltxtq_exec(PG_FUNCTION_ARGS)
179 {
180 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
181 	ltxtquery  *query = PG_GETARG_LTXTQUERY_P(1);
182 	bool		res = array_iterator(la, ltxtq_exec, (void *) query, NULL);
183 
184 	PG_FREE_IF_COPY(la, 0);
185 	PG_FREE_IF_COPY(query, 1);
186 	PG_RETURN_BOOL(res);
187 }
188 
189 Datum
_ltxtq_rexec(PG_FUNCTION_ARGS)190 _ltxtq_rexec(PG_FUNCTION_ARGS)
191 {
192 	PG_RETURN_DATUM(DirectFunctionCall2(_ltxtq_exec,
193 										PG_GETARG_DATUM(1),
194 										PG_GETARG_DATUM(0)
195 										));
196 }
197 
198 
199 Datum
_ltree_extract_isparent(PG_FUNCTION_ARGS)200 _ltree_extract_isparent(PG_FUNCTION_ARGS)
201 {
202 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
203 	ltree	   *query = PG_GETARG_LTREE_P(1);
204 	ltree	   *found,
205 			   *item;
206 
207 	if (!array_iterator(la, ltree_isparent, (void *) query, &found))
208 	{
209 		PG_FREE_IF_COPY(la, 0);
210 		PG_FREE_IF_COPY(query, 1);
211 		PG_RETURN_NULL();
212 	}
213 
214 	item = (ltree *) palloc0(VARSIZE(found));
215 	memcpy(item, found, VARSIZE(found));
216 
217 	PG_FREE_IF_COPY(la, 0);
218 	PG_FREE_IF_COPY(query, 1);
219 	PG_RETURN_POINTER(item);
220 }
221 
222 Datum
_ltree_extract_risparent(PG_FUNCTION_ARGS)223 _ltree_extract_risparent(PG_FUNCTION_ARGS)
224 {
225 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
226 	ltree	   *query = PG_GETARG_LTREE_P(1);
227 	ltree	   *found,
228 			   *item;
229 
230 	if (!array_iterator(la, ltree_risparent, (void *) query, &found))
231 	{
232 		PG_FREE_IF_COPY(la, 0);
233 		PG_FREE_IF_COPY(query, 1);
234 		PG_RETURN_NULL();
235 	}
236 
237 	item = (ltree *) palloc0(VARSIZE(found));
238 	memcpy(item, found, VARSIZE(found));
239 
240 	PG_FREE_IF_COPY(la, 0);
241 	PG_FREE_IF_COPY(query, 1);
242 	PG_RETURN_POINTER(item);
243 }
244 
245 Datum
_ltq_extract_regex(PG_FUNCTION_ARGS)246 _ltq_extract_regex(PG_FUNCTION_ARGS)
247 {
248 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
249 	lquery	   *query = PG_GETARG_LQUERY_P(1);
250 	ltree	   *found,
251 			   *item;
252 
253 	if (!array_iterator(la, ltq_regex, (void *) query, &found))
254 	{
255 		PG_FREE_IF_COPY(la, 0);
256 		PG_FREE_IF_COPY(query, 1);
257 		PG_RETURN_NULL();
258 	}
259 
260 	item = (ltree *) palloc0(VARSIZE(found));
261 	memcpy(item, found, VARSIZE(found));
262 
263 	PG_FREE_IF_COPY(la, 0);
264 	PG_FREE_IF_COPY(query, 1);
265 	PG_RETURN_POINTER(item);
266 }
267 
268 Datum
_ltxtq_extract_exec(PG_FUNCTION_ARGS)269 _ltxtq_extract_exec(PG_FUNCTION_ARGS)
270 {
271 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
272 	ltxtquery  *query = PG_GETARG_LTXTQUERY_P(1);
273 	ltree	   *found,
274 			   *item;
275 
276 	if (!array_iterator(la, ltxtq_exec, (void *) query, &found))
277 	{
278 		PG_FREE_IF_COPY(la, 0);
279 		PG_FREE_IF_COPY(query, 1);
280 		PG_RETURN_NULL();
281 	}
282 
283 	item = (ltree *) palloc0(VARSIZE(found));
284 	memcpy(item, found, VARSIZE(found));
285 
286 	PG_FREE_IF_COPY(la, 0);
287 	PG_FREE_IF_COPY(query, 1);
288 	PG_RETURN_POINTER(item);
289 }
290 
291 Datum
_lca(PG_FUNCTION_ARGS)292 _lca(PG_FUNCTION_ARGS)
293 {
294 	ArrayType  *la = PG_GETARG_ARRAYTYPE_P(0);
295 	int			num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
296 	ltree	   *item = (ltree *) ARR_DATA_PTR(la);
297 	ltree	  **a,
298 			   *res;
299 
300 	if (ARR_NDIM(la) > 1)
301 		ereport(ERROR,
302 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
303 				 errmsg("array must be one-dimensional")));
304 	if (array_contains_nulls(la))
305 		ereport(ERROR,
306 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
307 				 errmsg("array must not contain nulls")));
308 
309 	a = (ltree **) palloc(sizeof(ltree *) * num);
310 	while (num > 0)
311 	{
312 		num--;
313 		a[num] = item;
314 		item = NEXTVAL(item);
315 	}
316 	res = lca_inner(a, ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)));
317 	pfree(a);
318 
319 	PG_FREE_IF_COPY(la, 0);
320 
321 	if (res)
322 		PG_RETURN_POINTER(res);
323 	else
324 		PG_RETURN_NULL();
325 }
326