1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* monetdb_config.h must be the first include in each .c file */ |
10 | #include "monetdb_config.h" |
11 | #include "udf.h" |
12 | |
13 | /* Reverse a string */ |
14 | |
15 | /* actual implementation */ |
16 | /* all non-exported functions must be declared static */ |
17 | static char * |
18 | UDFreverse_(char **ret, const char *src) |
19 | { |
20 | size_t len = 0; |
21 | char *dst = NULL; |
22 | |
23 | /* assert calling sanity */ |
24 | assert(ret != NULL); |
25 | |
26 | /* handle NULL pointer and NULL value */ |
27 | if (src == NULL || strcmp(src, str_nil) == 0) { |
28 | *ret = GDKstrdup(str_nil); |
29 | if (*ret == NULL) |
30 | throw(MAL, "udf.reverse" , |
31 | "failed to create copy of str_nil" ); |
32 | |
33 | return MAL_SUCCEED; |
34 | } |
35 | |
36 | /* allocate result string */ |
37 | len = strlen(src); |
38 | *ret = dst = GDKmalloc(len + 1); |
39 | if (dst == NULL) |
40 | throw(MAL, "udf.reverse" , |
41 | "failed to allocate string of length %zu" , len + 1); |
42 | |
43 | /* copy characters from src to dst in reverse order */ |
44 | dst[len] = 0; |
45 | while (len > 0) |
46 | *dst++ = src[--len]; |
47 | |
48 | return MAL_SUCCEED; |
49 | } |
50 | |
51 | /* MAL wrapper */ |
52 | char * |
53 | UDFreverse(char **ret, const char **arg) |
54 | { |
55 | /* assert calling sanity */ |
56 | assert(ret != NULL && arg != NULL); |
57 | |
58 | return UDFreverse_ ( ret, *arg ); |
59 | } |
60 | |
61 | |
62 | /* Reverse a BAT of strings */ |
63 | /* |
64 | * Generic "type-oblivious" version, |
65 | * using generic "type-oblivious" BAT access interface. |
66 | */ |
67 | |
68 | /* actual implementation */ |
69 | static char * |
70 | UDFBATreverse_(BAT **ret, BAT *src) |
71 | { |
72 | BATiter li; |
73 | BAT *bn = NULL; |
74 | BUN p = 0, q = 0; |
75 | |
76 | /* assert calling sanity */ |
77 | assert(ret != NULL); |
78 | |
79 | /* handle NULL pointer */ |
80 | if (src == NULL) |
81 | throw(MAL, "batudf.reverse" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
82 | |
83 | /* check tail type */ |
84 | if (src->ttype != TYPE_str) { |
85 | throw(MAL, "batudf.reverse" , |
86 | "tail-type of input BAT must be TYPE_str" ); |
87 | } |
88 | |
89 | /* allocate void-headed result BAT */ |
90 | bn = COLnew(src->hseqbase, TYPE_str, BATcount(src), TRANSIENT); |
91 | if (bn == NULL) { |
92 | throw(MAL, "batudf.reverse" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
93 | } |
94 | |
95 | /* create BAT iterator */ |
96 | li = bat_iterator(src); |
97 | |
98 | /* the core of the algorithm, expensive due to malloc/frees */ |
99 | BATloop(src, p, q) { |
100 | char *tr = NULL, *err = NULL; |
101 | |
102 | const char *t = (const char *) BUNtvar(li, p); |
103 | |
104 | /* revert tail value */ |
105 | err = UDFreverse_(&tr, t); |
106 | if (err != MAL_SUCCEED) { |
107 | /* error -> bail out */ |
108 | BBPunfix(bn->batCacheid); |
109 | return err; |
110 | } |
111 | |
112 | /* assert logical sanity */ |
113 | assert(tr != NULL); |
114 | |
115 | /* append reversed tail in result BAT */ |
116 | if (BUNappend(bn, tr, false) != GDK_SUCCEED) { |
117 | BBPunfix(bn->batCacheid); |
118 | throw(MAL, "batudf.reverse" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
119 | } |
120 | |
121 | /* free memory allocated in UDFreverse_() */ |
122 | GDKfree(tr); |
123 | } |
124 | |
125 | *ret = bn; |
126 | |
127 | return MAL_SUCCEED; |
128 | } |
129 | |
130 | /* MAL wrapper */ |
131 | char * |
132 | UDFBATreverse(bat *ret, const bat *arg) |
133 | { |
134 | BAT *res = NULL, *src = NULL; |
135 | char *msg = NULL; |
136 | |
137 | /* assert calling sanity */ |
138 | assert(ret != NULL && arg != NULL); |
139 | |
140 | /* bat-id -> BAT-descriptor */ |
141 | if ((src = BATdescriptor(*arg)) == NULL) |
142 | throw(MAL, "batudf.reverse" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
143 | |
144 | /* do the work */ |
145 | msg = UDFBATreverse_ ( &res, src ); |
146 | |
147 | /* release input BAT-descriptor */ |
148 | BBPunfix(src->batCacheid); |
149 | |
150 | if (msg == MAL_SUCCEED) { |
151 | /* register result BAT in buffer pool */ |
152 | BBPkeepref((*ret = res->batCacheid)); |
153 | } |
154 | |
155 | return msg; |
156 | } |
157 | |
158 | |
159 | |
160 | /* fuse */ |
161 | |
162 | /* instantiate type-specific functions */ |
163 | |
164 | #define UI bte |
165 | #define UU unsigned char |
166 | #define UO sht |
167 | #include "udf_impl.h" |
168 | #undef UI |
169 | #undef UU |
170 | #undef UO |
171 | |
172 | #define UI sht |
173 | #define UU unsigned short |
174 | #define UO int |
175 | #include "udf_impl.h" |
176 | #undef UI |
177 | #undef UU |
178 | #undef UO |
179 | |
180 | #define UI int |
181 | #define UU unsigned int |
182 | #define UO lng |
183 | #include "udf_impl.h" |
184 | #undef UI |
185 | #undef UU |
186 | #undef UO |
187 | |
188 | #ifdef HAVE_HGE |
189 | #define UI lng |
190 | #define UU ulng |
191 | #define UO hge |
192 | #include "udf_impl.h" |
193 | #undef UI |
194 | #undef UU |
195 | #undef UO |
196 | #endif |
197 | |
198 | /* BAT fuse */ |
199 | |
200 | /* actual implementation */ |
201 | static char * |
202 | UDFBATfuse_(BAT **ret, const BAT *bone, const BAT *btwo) |
203 | { |
204 | BAT *bres = NULL; |
205 | bit two_tail_sorted_unsigned = FALSE; |
206 | bit two_tail_revsorted_unsigned = FALSE; |
207 | BUN n; |
208 | char *msg = NULL; |
209 | |
210 | /* assert calling sanity */ |
211 | assert(ret != NULL); |
212 | |
213 | /* handle NULL pointer */ |
214 | if (bone == NULL || btwo == NULL) |
215 | throw(MAL, "batudf.fuse" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
216 | |
217 | /* check for aligned heads */ |
218 | if (BATcount(bone) != BATcount(btwo) || |
219 | bone->hseqbase != btwo->hseqbase) { |
220 | throw(MAL, "batudf.fuse" , |
221 | "heads of input BATs must be aligned" ); |
222 | } |
223 | n = BATcount(bone); |
224 | |
225 | /* check tail types */ |
226 | if (bone->ttype != btwo->ttype) { |
227 | throw(MAL, "batudf.fuse" , |
228 | "tails of input BATs must be identical" ); |
229 | } |
230 | |
231 | /* allocate result BAT */ |
232 | switch (bone->ttype) { |
233 | case TYPE_bte: |
234 | bres = COLnew(bone->hseqbase, TYPE_sht, n, TRANSIENT); |
235 | break; |
236 | case TYPE_sht: |
237 | bres = COLnew(bone->hseqbase, TYPE_int, n, TRANSIENT); |
238 | break; |
239 | case TYPE_int: |
240 | bres = COLnew(bone->hseqbase, TYPE_lng, n, TRANSIENT); |
241 | break; |
242 | #ifdef HAVE_HGE |
243 | case TYPE_lng: |
244 | bres = COLnew(bone->hseqbase, TYPE_hge, n, TRANSIENT); |
245 | break; |
246 | #endif |
247 | default: |
248 | throw(MAL, "batudf.fuse" , |
249 | "tails of input BATs must be one of {bte, sht, int" |
250 | #ifdef HAVE_HGE |
251 | ", lng" |
252 | #endif |
253 | "}" ); |
254 | } |
255 | if (bres == NULL) |
256 | throw(MAL, "batudf.fuse" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
257 | |
258 | /* call type-specific core algorithm */ |
259 | switch (bone->ttype) { |
260 | case TYPE_bte: |
261 | msg = UDFBATfuse_bte_sht ( bres, bone, btwo, n, |
262 | &two_tail_sorted_unsigned, &two_tail_revsorted_unsigned ); |
263 | break; |
264 | case TYPE_sht: |
265 | msg = UDFBATfuse_sht_int ( bres, bone, btwo, n, |
266 | &two_tail_sorted_unsigned, &two_tail_revsorted_unsigned ); |
267 | break; |
268 | case TYPE_int: |
269 | msg = UDFBATfuse_int_lng ( bres, bone, btwo, n, |
270 | &two_tail_sorted_unsigned, &two_tail_revsorted_unsigned ); |
271 | break; |
272 | #ifdef HAVE_HGE |
273 | case TYPE_lng: |
274 | msg = UDFBATfuse_lng_hge ( bres, bone, btwo, n, |
275 | &two_tail_sorted_unsigned, &two_tail_revsorted_unsigned ); |
276 | break; |
277 | #endif |
278 | default: |
279 | BBPunfix(bres->batCacheid); |
280 | throw(MAL, "batudf.fuse" , |
281 | "tails of input BATs must be one of {bte, sht, int" |
282 | #ifdef HAVE_HGE |
283 | ", lng" |
284 | #endif |
285 | "}" ); |
286 | } |
287 | |
288 | if (msg != MAL_SUCCEED) { |
289 | BBPunfix(bres->batCacheid); |
290 | } else { |
291 | /* set number of tuples in result BAT */ |
292 | BATsetcount(bres, n); |
293 | |
294 | /* Result tail is sorted, if the left/first input tail is |
295 | * sorted and key (unique), or if the left/first input tail is |
296 | * sorted and the second/right input tail is sorted and the |
297 | * second/right tail values are either all >= 0 or all < 0; |
298 | * otherwise, we cannot tell. |
299 | */ |
300 | if (BATtordered(bone) |
301 | && (BATtkey(bone) || two_tail_sorted_unsigned)) |
302 | bres->tsorted = true; |
303 | else |
304 | bres->tsorted = (BATcount(bres) <= 1); |
305 | if (BATtrevordered(bone) |
306 | && (BATtkey(bone) || two_tail_revsorted_unsigned)) |
307 | bres->trevsorted = true; |
308 | else |
309 | bres->trevsorted = (BATcount(bres) <= 1); |
310 | /* result tail is key (unique), iff both input tails are */ |
311 | BATkey(bres, BATtkey(bone) || BATtkey(btwo)); |
312 | |
313 | *ret = bres; |
314 | } |
315 | |
316 | return msg; |
317 | } |
318 | |
319 | /* MAL wrapper */ |
320 | char * |
321 | UDFBATfuse(bat *ires, const bat *ione, const bat *itwo) |
322 | { |
323 | BAT *bres = NULL, *bone = NULL, *btwo = NULL; |
324 | char *msg = NULL; |
325 | |
326 | /* assert calling sanity */ |
327 | assert(ires != NULL && ione != NULL && itwo != NULL); |
328 | |
329 | /* bat-id -> BAT-descriptor */ |
330 | if ((bone = BATdescriptor(*ione)) == NULL) |
331 | throw(MAL, "batudf.fuse" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
332 | |
333 | /* bat-id -> BAT-descriptor */ |
334 | if ((btwo = BATdescriptor(*itwo)) == NULL) { |
335 | BBPunfix(bone->batCacheid); |
336 | throw(MAL, "batudf.fuse" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
337 | } |
338 | |
339 | /* do the work */ |
340 | msg = UDFBATfuse_ ( &bres, bone, btwo ); |
341 | |
342 | /* release input BAT-descriptors */ |
343 | BBPunfix(bone->batCacheid); |
344 | BBPunfix(btwo->batCacheid); |
345 | |
346 | if (msg == MAL_SUCCEED) { |
347 | /* register result BAT in buffer pool */ |
348 | BBPkeepref((*ires = bres->batCacheid)); |
349 | } |
350 | |
351 | return msg; |
352 | } |
353 | |