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/*
10 * (c) M.L.Kersten, P. Boncz
11 * BAT Buffer Pool
12 * It is primarilly meant to ease inspection of the BAT collection managed
13 * by the server.
14 */
15#include "monetdb_config.h"
16#include "bbp.h"
17
18static int
19pseudo(bat *ret, BAT *b, str X1,str X2) {
20 char buf[BUFSIZ];
21 snprintf(buf,BUFSIZ,"%s_%s", X1,X2);
22 if ((BBPindex(buf) <= 0 && BBPrename(b->batCacheid, buf) != 0) || BATroles(b,X2) != GDK_SUCCEED) {
23 BBPunfix(b->batCacheid);
24 return -1;
25 }
26 *ret = b->batCacheid;
27 BBPkeepref(*ret);
28 return -0;
29}
30
31str
32CMDbbpbind(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
33{
34 str name;
35 ValPtr lhs;
36 bat i;
37 int tt;
38 BAT *b;
39
40 (void) cntxt;
41 (void) mb; /* fool compiler */
42 lhs = &stk->stk[pci->argv[0]];
43 name = *getArgReference_str(stk, pci, 1);
44 if (name == NULL || isIdentifier(name) < 0)
45 throw(MAL, "bbp.bind", IDENTIFIER_EXPECTED);
46 i = BBPindex(name);
47 if (i == 0)
48 throw(MAL, "bbp.bind", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
49 /* make sure you load the descriptors and heaps */
50 b = (BAT *) BATdescriptor(i);
51 if (b == 0)
52 /* Simple ignore the binding if you can't find the bat */
53 throw(MAL, "bbp.bind", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
54
55 /* check conformity of the actual type and the one requested */
56 tt= getBatType(getArgType(mb,pci,0));
57 if( b->ttype == TYPE_void && tt== TYPE_oid) tt= TYPE_void;
58
59 if( tt != b->ttype){
60 BBPunfix(i);
61 throw(MAL, "bbp.bind", SEMANTIC_TYPE_MISMATCH );
62 }
63 /* make sure we are not dealing with an about to be deleted bat */
64 if( BBP_refs(b->batCacheid) == 1 &&
65 BBP_lrefs(b->batCacheid) == 0){
66 BBPunfix(i);
67 throw(MAL, "bbp.bind", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
68 }
69
70 BBPkeepref(b->batCacheid);
71 lhs->vtype = TYPE_bat;
72 lhs->val.bval = i;
73 return MAL_SUCCEED;
74}
75/*
76 * BBP status
77 * The BAT buffer pool datastructures describe the memory resident information
78 * on the whereabouts of the BATs. The three predominant tables are made accessible
79 * for inspection.
80 *
81 * The most interesting system bat for end-users is the BID-> NAME mapping,
82 * because it provides access to the system guaranteed persistent BAT identifier.
83 * It may be the case that the user already introduced a BAT with this name,
84 * it is simply removed first
85 */
86
87str
88CMDbbpNames(bat *ret)
89{
90 BAT *b;
91 int i;
92
93 b = COLnew(0, TYPE_str, getBBPsize(), TRANSIENT);
94 if (b == 0)
95 throw(MAL, "catalog.bbpNames", SQLSTATE(HY001) MAL_MALLOC_FAIL);
96
97 BBPlock();
98 for (i = 1; i < getBBPsize(); i++)
99 if (i != b->batCacheid) {
100 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i)) ) {
101 if (BUNappend(b, BBP_logical(i), false) != GDK_SUCCEED) {
102 BBPunlock();
103 BBPreclaim(b);
104 throw(MAL, "catalog.bbpNames", SQLSTATE(HY001) MAL_MALLOC_FAIL);
105 }
106 }
107 }
108 BBPunlock();
109 if (pseudo(ret,b,"bbp","name"))
110 throw(MAL, "catalog.bbpNames", GDK_EXCEPTION);
111 return MAL_SUCCEED;
112}
113str
114CMDbbpDiskSpace(lng *ret)
115{
116 *ret= getDiskSpace();
117 return MAL_SUCCEED;
118}
119str
120CMDgetPageSize(int *ret)
121{
122 *ret= (int) MT_pagesize();
123 return MAL_SUCCEED;
124}
125
126str
127CMDbbpName(str *ret, bat *bid)
128{
129 *ret = (str) GDKstrdup(BBP_logical(*bid));
130 if (*ret == NULL)
131 throw(MAL, "catalog.bbpName", SQLSTATE(HY001) MAL_MALLOC_FAIL);
132 return MAL_SUCCEED;
133}
134
135str
136CMDbbpCount(bat *ret)
137{
138 BAT *b, *bn;
139 int i;
140 lng l;
141
142 b = COLnew(0, TYPE_lng, getBBPsize(), TRANSIENT);
143 if (b == 0)
144 throw(MAL, "catalog.bbpCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
145
146 for (i = 1; i < getBBPsize(); i++)
147 if (i != b->batCacheid) {
148 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
149 bn = BATdescriptor(i);
150 if (bn) {
151 l = BATcount(bn);
152 BBPunfix(bn->batCacheid);
153 if (BUNappend(b, &l, false) != GDK_SUCCEED) {
154 BBPreclaim(b);
155 throw(MAL, "catalog.bbpCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
156 }
157 }
158 }
159 }
160 if (pseudo(ret,b,"bbp","count"))
161 throw(MAL, "catalog.bbpCount", GDK_EXCEPTION);
162 return MAL_SUCCEED;
163}
164
165/*
166 * The BAT status is redundantly stored in CMDbat_info.
167 */
168str
169CMDbbpLocation(bat *ret)
170{
171 BAT *b;
172 int i;
173 char buf[FILENAME_MAX];
174 char cwd[FILENAME_MAX];
175
176 if (getcwd(cwd, FILENAME_MAX) == NULL)
177 throw(MAL, "catalog.bbpLocation", RUNTIME_DIR_ERROR);
178
179 b = COLnew(0, TYPE_str, getBBPsize(), TRANSIENT);
180 if (b == 0)
181 throw(MAL, "catalog.bbpLocation", SQLSTATE(HY001) MAL_MALLOC_FAIL);
182
183 BBPlock();
184 for (i = 1; i < getBBPsize(); i++)
185 if (i != b->batCacheid) {
186 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
187 int len = snprintf(buf,FILENAME_MAX,"%s/bat/%s",cwd,BBP_physical(i));
188 if (len == -1 || len >= FILENAME_MAX) {
189 BBPunlock();
190 BBPreclaim(b);
191 throw(MAL, "catalog.bbpLocation", SQLSTATE(HY001) "Could not write bpp filename path is too large");
192 }
193 if (BUNappend(b, buf, false) != GDK_SUCCEED) {
194 BBPunlock();
195 BBPreclaim(b);
196 throw(MAL, "catalog.bbpLocation", SQLSTATE(HY001) MAL_MALLOC_FAIL);
197 }
198 }
199 }
200 BBPunlock();
201 if (pseudo(ret,b,"bbp","location"))
202 throw(MAL, "catalog.bbpLocation", GDK_EXCEPTION);
203 return MAL_SUCCEED;
204}
205
206/*
207 * The BAT dirty status:dirty => (mem != disk); diffs = not-committed
208 */
209str
210CMDbbpDirty(bat *ret)
211{
212 BAT *b;
213 int i;
214
215 b = COLnew(0, TYPE_str, getBBPsize(), TRANSIENT);
216 if (b == 0)
217 throw(MAL, "catalog.bbpDirty", SQLSTATE(HY001) MAL_MALLOC_FAIL);
218
219 BBPlock();
220 for (i = 1; i < getBBPsize(); i++)
221 if (i != b->batCacheid)
222 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
223 BAT *bn = BBP_cache(i);
224
225 if (BUNappend(b, bn ? BATdirty(bn) ? "dirty" : DELTAdirty(bn) ? "diffs" : "clean" : (BBP_status(i) & BBPSWAPPED) ? "diffs" : "clean", false) != GDK_SUCCEED) {
226 BBPunlock();
227 BBPreclaim(b);
228 throw(MAL, "catalog.bbpDirty", SQLSTATE(HY001) MAL_MALLOC_FAIL);
229 }
230 }
231 BBPunlock();
232 if (pseudo(ret,b,"bbp","status"))
233 throw(MAL, "catalog.bbpDirty", GDK_EXCEPTION);
234 return MAL_SUCCEED;
235}
236
237/*
238 * The BAT status is redundantly stored in CMDbat_info.
239 */
240str
241CMDbbpStatus(bat *ret)
242{
243 BAT *b;
244 int i;
245
246 b = COLnew(0, TYPE_str, getBBPsize(), TRANSIENT);
247 if (b == 0)
248 throw(MAL, "catalog.bbpStatus", SQLSTATE(HY001) MAL_MALLOC_FAIL);
249
250 BBPlock();
251 for (i = 1; i < getBBPsize(); i++)
252 if (i != b->batCacheid)
253 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
254 char *loc = BBP_cache(i) ? "load" : "disk";
255
256 if (BUNappend(b, loc, false) != GDK_SUCCEED) {
257 BBPunlock();
258 BBPreclaim(b);
259 throw(MAL, "catalog.bbpStatus", SQLSTATE(HY001) MAL_MALLOC_FAIL);
260 }
261 }
262 BBPunlock();
263 if (pseudo(ret,b,"bbp","status"))
264 throw(MAL, "catalog.bbpStatus", GDK_EXCEPTION);
265 return MAL_SUCCEED;
266}
267
268str
269CMDbbpKind(bat *ret)
270{
271 BAT *b;
272 int i;
273
274 b = COLnew(0, TYPE_str, getBBPsize(), TRANSIENT);
275 if (b == 0)
276 throw(MAL, "catalog.bbpKind", SQLSTATE(HY001) MAL_MALLOC_FAIL);
277
278 BBPlock();
279 for (i = 1; i < getBBPsize(); i++)
280 if (i != b->batCacheid && BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
281 const char *mode;
282
283 if ((BBP_status(i) & BBPDELETED) || !(BBP_status(i) & BBPPERSISTENT))
284 mode = "transient";
285 else
286 mode = "persistent";
287 if (BUNappend(b, mode, false) != GDK_SUCCEED) {
288 BBPunlock();
289 BBPreclaim(b);
290 throw(MAL, "catalog.bbpKind", SQLSTATE(HY001) MAL_MALLOC_FAIL);
291 }
292 }
293 BBPunlock();
294 if (pseudo(ret,b,"bbp","kind"))
295 throw(MAL, "catalog.bbpKind", GDK_EXCEPTION);
296 return MAL_SUCCEED;
297}
298
299str
300CMDbbpRefCount(bat *ret)
301{
302 BAT *b;
303 int i;
304
305 b = COLnew(0, TYPE_int, getBBPsize(), TRANSIENT);
306 if (b == 0)
307 throw(MAL, "catalog.bbpRefCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
308
309 BBPlock();
310 for (i = 1; i < getBBPsize(); i++)
311 if (i != b->batCacheid && BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
312 int refs = BBP_refs(i);
313
314 if (BUNappend(b, &refs, false) != GDK_SUCCEED) {
315 BBPunlock();
316 BBPreclaim(b);
317 throw(MAL, "catalog.bbpRefCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
318 }
319 }
320 BBPunlock();
321 if (pseudo(ret,b,"bbp","refcnt"))
322 throw(MAL, "catalog.bbpRefCount", GDK_EXCEPTION);
323 return MAL_SUCCEED;
324}
325
326str
327CMDbbpLRefCount(bat *ret)
328{
329 BAT *b;
330 int i;
331
332 b = COLnew(0, TYPE_int, getBBPsize(), TRANSIENT);
333 if (b == 0)
334 throw(MAL, "catalog.bbpLRefCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
335
336 BBPlock();
337 for (i = 1; i < getBBPsize(); i++)
338 if (i != b->batCacheid && BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
339 int refs = BBP_lrefs(i);
340
341 if (BUNappend(b, &refs, false) != GDK_SUCCEED) {
342 BBPunlock();
343 BBPreclaim(b);
344 throw(MAL, "catalog.bbpLRefCount", SQLSTATE(HY001) MAL_MALLOC_FAIL);
345 }
346 }
347 BBPunlock();
348 if (pseudo(ret,b,"bbp","lrefcnt"))
349 throw(MAL, "catalog.bbpLRefCount", GDK_EXCEPTION);
350 return MAL_SUCCEED;
351}
352
353str
354CMDbbpgetIndex(int *res, bat *bid)
355{
356 *res= *bid;
357 return MAL_SUCCEED;
358}
359
360str
361CMDgetBATrefcnt(int *res, bat *bid)
362{
363 BAT *b;
364
365 if ((b = BATdescriptor(*bid)) == NULL) {
366 throw(MAL, "bbp.getRefCount", INTERNAL_BAT_ACCESS);
367 }
368 *res = BBP_refs(b->batCacheid);
369 BBPunfix(b->batCacheid);
370 return MAL_SUCCEED;
371}
372
373str
374CMDgetBATlrefcnt(int *res, bat *bid)
375{
376 BAT *b;
377
378 if ((b = BATdescriptor(*bid)) == NULL) {
379 throw(MAL, "bbp.getLRefCount", INTERNAL_BAT_ACCESS);
380 }
381 *res = BBP_lrefs(b->batCacheid);
382 BBPunfix(b->batCacheid);
383 return MAL_SUCCEED;
384}
385
386str CMDbbp(bat *ID, bat *NS, bat *TT, bat *CNT, bat *REFCNT, bat *LREFCNT, bat *LOCATION, bat *HEAT, bat *DIRTY, bat *STATUS, bat *KIND)
387{
388 BAT *id, *ns, *tt, *cnt, *refcnt, *lrefcnt, *location, *heat, *dirty, *status, *kind, *bn;
389 bat i;
390 char buf[FILENAME_MAX];
391 bat sz = getBBPsize();
392 str msg = MAL_SUCCEED;
393
394 id = COLnew(0, TYPE_int, (BUN) sz, TRANSIENT);
395 ns = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
396 tt = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
397 cnt = COLnew(0, TYPE_lng, (BUN) sz, TRANSIENT);
398 refcnt = COLnew(0, TYPE_int, (BUN) sz, TRANSIENT);
399 lrefcnt = COLnew(0, TYPE_int, (BUN) sz, TRANSIENT);
400 location = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
401 heat = COLnew(0, TYPE_int, (BUN) sz, TRANSIENT);
402 dirty = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
403 status = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
404 kind = COLnew(0, TYPE_str, (BUN) sz, TRANSIENT);
405
406 if (!id || !ns || !tt || !cnt || !refcnt || !lrefcnt || !location || !heat || !dirty || !status || !kind) {
407 goto bailout;
408 }
409 for (i = 1; i < sz; i++) {
410 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
411 bn = BATdescriptor(i);
412 if (bn) {
413 lng l = BATcount(bn);
414 int heat_ = 0, len;
415 char *loc = BBP_cache(i) ? "load" : "disk";
416 char *mode = "persistent";
417 int refs = BBP_refs(i);
418 int lrefs = BBP_lrefs(i);
419
420 if ((BBP_status(i) & BBPDELETED) || !(BBP_status(i) & BBPPERSISTENT))
421 mode = "transient";
422 len = snprintf(buf, FILENAME_MAX, "%s", BBP_physical(i));
423 if (len == -1 || len >= FILENAME_MAX) {
424 msg = createException(MAL, "catalog.bbp", SQLSTATE(HY001) "Could not bpp filename path is too large");
425 goto bailout;
426 }
427 if (BUNappend(id, &i, false) != GDK_SUCCEED ||
428 BUNappend(ns, BBP_logical(i), false) != GDK_SUCCEED ||
429 BUNappend(tt, BATatoms[BATttype(bn)].name, false) != GDK_SUCCEED ||
430 BUNappend(cnt, &l, false) != GDK_SUCCEED ||
431 BUNappend(refcnt, &refs, false) != GDK_SUCCEED ||
432 BUNappend(lrefcnt, &lrefs, false) != GDK_SUCCEED ||
433 BUNappend(location, buf, false) != GDK_SUCCEED ||
434 BUNappend(heat, &heat_, false) != GDK_SUCCEED ||
435 BUNappend(dirty, bn ? BATdirty(bn) ? "dirty" : DELTAdirty(bn) ? "diffs" : "clean" : (BBP_status(i) & BBPSWAPPED) ? "diffs" : "clean", false) != GDK_SUCCEED ||
436 BUNappend(status, loc, false) != GDK_SUCCEED ||
437 BUNappend(kind, mode, false) != GDK_SUCCEED) {
438 BBPunfix(bn->batCacheid);
439 msg = createException(MAL, "catalog.bbp", SQLSTATE(HY001) MAL_MALLOC_FAIL);
440 goto bailout;
441 }
442 BBPunfix(bn->batCacheid);
443 }
444 }
445 }
446 BBPkeepref(*ID = id->batCacheid);
447 BBPkeepref(*NS = ns->batCacheid);
448 BBPkeepref(*TT = tt->batCacheid);
449 BBPkeepref(*CNT = cnt->batCacheid);
450 BBPkeepref(*REFCNT = refcnt->batCacheid);
451 BBPkeepref(*LREFCNT = lrefcnt->batCacheid);
452 BBPkeepref(*LOCATION = location->batCacheid);
453 BBPkeepref(*HEAT = heat->batCacheid);
454 BBPkeepref(*DIRTY = dirty->batCacheid);
455 BBPkeepref(*STATUS = status->batCacheid);
456 BBPkeepref(*KIND = kind->batCacheid);
457 return MAL_SUCCEED;
458
459 bailout:
460 BBPreclaim(id);
461 BBPreclaim(ns);
462 BBPreclaim(tt);
463 BBPreclaim(cnt);
464 BBPreclaim(refcnt);
465 BBPreclaim(lrefcnt);
466 BBPreclaim(location);
467 BBPreclaim(heat);
468 BBPreclaim(dirty);
469 BBPreclaim(status);
470 BBPreclaim(kind);
471 return msg;
472}
473
474str
475CMDsetName(str *rname, const bat *bid, str *name)
476{
477 BAT *b;
478 if ((b = BATdescriptor(*bid)) == NULL) {
479 throw(MAL, "bbp.setName", INTERNAL_BAT_ACCESS);
480 }
481 if (BBPrename(b->batCacheid, *name) != 0) {
482 BBPunfix(b->batCacheid);
483 throw(MAL, "bbp.setName", GDK_EXCEPTION);
484 }
485 *rname = GDKstrdup(*name);
486 BBPunfix(b->batCacheid);
487 if (*rname == NULL)
488 throw(MAL, "bbp.setName", SQLSTATE(HY001) MAL_MALLOC_FAIL);
489 return MAL_SUCCEED;
490}
491