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 */
42static str
43MATpackInternal(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 */
100str
101MATpackIncrement(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
172str
173MATpack(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
174{
175 return MATpackInternal(cntxt,mb,stk,p);
176}
177
178str
179MATpackValues(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