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 | |