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