| 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 | * Martin Kersten |
| 11 | * Multiple association tables |
| 12 | * A MAT is a convenient way to deal represent horizontal fragmented |
| 13 | * tables. It combines the definitions of several, type compatible |
| 14 | * BATs under a single name. |
| 15 | * It is produced by the mitosis optimizer and the operations |
| 16 | * are the target of the mergetable optimizer. |
| 17 | * |
| 18 | * The MAT is materialized when the operations |
| 19 | * can not deal with the components individually, |
| 20 | * or the incremental operation is not supported. |
| 21 | * Normally all mat.new() operations are removed by the |
| 22 | * mergetable optimizer. |
| 23 | * In case a mat.new() is retained in the code, then it will |
| 24 | * behave as a mat.pack(); |
| 25 | * |
| 26 | * The primitives below are chosen to accomodate the SQL |
| 27 | * front-end to produce reasonable efficient code. |
| 28 | */ |
| 29 | #include "monetdb_config.h" |
| 30 | #include "mat.h" |
| 31 | |
| 32 | /* |
| 33 | * The pack is an ordinary multi BAT insert. Oid synchronistion |
| 34 | * between pieces should be ensured by the code generators. |
| 35 | * The pack operation could be quite expensive, because it |
| 36 | * may create a really large BAT. |
| 37 | * The slice over a mat helps to avoid constructing intermediates |
| 38 | * that are subsequently reduced. |
| 39 | * Contrary to most operations, NIL arguments are skipped and |
| 40 | * do not produce RUNTIME_OBJECT_MISSING. |
| 41 | */ |
| 42 | static str |
| 43 | MATpackInternal(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
| 44 | { |
| 45 | int i; |
| 46 | bat *ret = getArgReference_bat(stk,p,0); |
| 47 | BAT *b, *bn; |
| 48 | BUN cap = 0; |
| 49 | int tt = TYPE_any; |
| 50 | (void) cntxt; |
| 51 | (void) mb; |
| 52 | |
| 53 | for (i = 1; i < p->argc; i++) { |
| 54 | bat bid = stk->stk[getArg(p,i)].val.bval; |
| 55 | b = BBPquickdesc(bid, false); |
| 56 | if( b ){ |
| 57 | if (tt == TYPE_any) |
| 58 | tt = b->ttype; |
| 59 | if ((tt != TYPE_void && b->ttype != TYPE_void) && tt != b->ttype) |
| 60 | throw(MAL, "mat.pack" , "incompatible arguments" ); |
| 61 | cap += BATcount(b); |
| 62 | } |
| 63 | } |
| 64 | if (tt == TYPE_any){ |
| 65 | *ret = bat_nil; |
| 66 | return MAL_SUCCEED; |
| 67 | } |
| 68 | |
| 69 | bn = COLnew(0, tt, cap, TRANSIENT); |
| 70 | if (bn == NULL) |
| 71 | throw(MAL, "mat.pack" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 72 | |
| 73 | for (i = 1; i < p->argc; i++) { |
| 74 | b = BATdescriptor(stk->stk[getArg(p,i)].val.ival); |
| 75 | if( b ){ |
| 76 | if (BATcount(bn) == 0) { |
| 77 | BAThseqbase(bn, b->hseqbase); |
| 78 | BATtseqbase(bn, b->tseqbase); |
| 79 | } |
| 80 | if (BATappend(bn, b, NULL, false) != GDK_SUCCEED) { |
| 81 | BBPunfix(bn->batCacheid); |
| 82 | BBPunfix(b->batCacheid); |
| 83 | throw(MAL, "mat.pack" , GDK_EXCEPTION); |
| 84 | } |
| 85 | BBPunfix(b->batCacheid); |
| 86 | } |
| 87 | } |
| 88 | if( !(!bn->tnil || !bn->tnonil)){ |
| 89 | BBPkeepref(*ret = bn->batCacheid); |
| 90 | throw(MAL, "mat.pack" , "INTERNAL ERROR" "bn->tnil or bn->tnonil fails " ); |
| 91 | } |
| 92 | BBPkeepref(*ret = bn->batCacheid); |
| 93 | return MAL_SUCCEED; |
| 94 | } |
| 95 | |
| 96 | /* |
| 97 | * Enable incremental packing. The SQL front-end requires |
| 98 | * fixed oid sequences. |
| 99 | */ |
| 100 | str |
| 101 | MATpackIncrement(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
| 102 | { |
| 103 | bat *ret = getArgReference_bat(stk,p,0); |
| 104 | int pieces; |
| 105 | BAT *b, *bb, *bn; |
| 106 | size_t newsize; |
| 107 | |
| 108 | (void) cntxt; |
| 109 | b = BATdescriptor( stk->stk[getArg(p,1)].val.ival); |
| 110 | if ( b == NULL) |
| 111 | throw(MAL, "mat.pack" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); |
| 112 | |
| 113 | if ( getArgType(mb,p,2) == TYPE_int){ |
| 114 | /* first step, estimate with some slack */ |
| 115 | pieces = stk->stk[getArg(p,2)].val.ival; |
| 116 | bn = COLnew(b->hseqbase, ATOMtype(b->ttype), (BUN)(1.2 * BATcount(b) * pieces), TRANSIENT); |
| 117 | if (bn == NULL) { |
| 118 | BBPunfix(b->batCacheid); |
| 119 | throw(MAL, "mat.pack" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 120 | } |
| 121 | /* allocate enough space for the vheap, but not for strings, |
| 122 | * since BATappend does clever things for strings */ |
| 123 | if ( b->tvheap && bn->tvheap && ATOMstorage(b->ttype) != TYPE_str){ |
| 124 | newsize = b->tvheap->size * pieces; |
| 125 | if (HEAPextend(bn->tvheap, newsize, true) != GDK_SUCCEED) { |
| 126 | BBPunfix(b->batCacheid); |
| 127 | BBPunfix(bn->batCacheid); |
| 128 | throw(MAL, "mat.pack" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 129 | } |
| 130 | } |
| 131 | BATtseqbase(bn, b->tseqbase); |
| 132 | if (BATappend(bn, b, NULL, false) != GDK_SUCCEED) { |
| 133 | BBPunfix(bn->batCacheid); |
| 134 | BBPunfix(b->batCacheid); |
| 135 | throw(MAL, "mat.pack" , GDK_EXCEPTION); |
| 136 | } |
| 137 | bn->unused = (pieces-1); /* misuse "unused" field */ |
| 138 | BBPkeepref(*ret = bn->batCacheid); |
| 139 | BBPunfix(b->batCacheid); |
| 140 | if( !(!bn->tnil || !bn->tnonil)) |
| 141 | throw(MAL, "mat.packIncrement" , "INTERNAL ERROR" " bn->tnil %d bn->tnonil %d" , bn->tnil, bn->tnonil); |
| 142 | } else { |
| 143 | /* remaining steps */ |
| 144 | bb = BATdescriptor(stk->stk[getArg(p,2)].val.ival); |
| 145 | if ( bb ){ |
| 146 | if (BATcount(b) == 0) { |
| 147 | BAThseqbase(b, bb->hseqbase); |
| 148 | BATtseqbase(b, bb->tseqbase); |
| 149 | } |
| 150 | if (BATappend(b, bb, NULL, false) != GDK_SUCCEED) { |
| 151 | BBPunfix(bb->batCacheid); |
| 152 | BBPunfix(b->batCacheid); |
| 153 | throw(MAL, "mat.pack" , GDK_EXCEPTION); |
| 154 | } |
| 155 | BBPunfix(bb->batCacheid); |
| 156 | } |
| 157 | b->unused--; |
| 158 | if(b->unused == 0) |
| 159 | if (BATsetaccess(b, BAT_READ) != GDK_SUCCEED) { |
| 160 | BBPunfix(b->batCacheid); |
| 161 | throw(MAL, "mat.pack" , GDK_EXCEPTION); |
| 162 | } |
| 163 | if( !(!b->tnil || !b->tnonil)){ |
| 164 | BBPkeepref(*ret = b->batCacheid); |
| 165 | throw(MAL, "mat.pack" , "INTERNAL ERROR" " b->tnil or b->tnonil fails " ); |
| 166 | } |
| 167 | BBPkeepref(*ret = b->batCacheid); |
| 168 | } |
| 169 | return MAL_SUCCEED; |
| 170 | } |
| 171 | |
| 172 | str |
| 173 | MATpack(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
| 174 | { |
| 175 | return MATpackInternal(cntxt,mb,stk,p); |
| 176 | } |
| 177 | |
| 178 | str |
| 179 | MATpackValues(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
| 180 | { |
| 181 | int i, type, first = 1; |
| 182 | bat *ret; |
| 183 | BAT *bn; |
| 184 | |
| 185 | (void) cntxt; |
| 186 | type = getArgType(mb,p,first); |
| 187 | bn = COLnew(0, type, p->argc, TRANSIENT); |
| 188 | if( bn == NULL) |
| 189 | throw(MAL, "mat.pack" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 190 | |
| 191 | if (ATOMextern(type)) { |
| 192 | for(i = first; i < p->argc; i++) |
| 193 | if (BUNappend(bn, stk->stk[getArg(p,i)].val.pval, false) != GDK_SUCCEED) |
| 194 | goto bailout; |
| 195 | } else { |
| 196 | for(i = first; i < p->argc; i++) |
| 197 | if (BUNappend(bn, getArgReference(stk, p, i), false) != GDK_SUCCEED) |
| 198 | goto bailout; |
| 199 | } |
| 200 | ret= getArgReference_bat(stk,p,0); |
| 201 | BBPkeepref(*ret = bn->batCacheid); |
| 202 | return MAL_SUCCEED; |
| 203 | bailout: |
| 204 | BBPreclaim(bn); |
| 205 | throw(MAL, "mat.pack" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 206 | } |
| 207 | |