|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.27 2002/01/16 20:29:01 tgl Exp $ |
| 11 | + * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.28 2002/03/05 05:33:06 momjian Exp $ |
12 | 12 | *
|
13 | 13 | *
|
14 | 14 | * INTERFACE ROUTINES
|
@@ -47,6 +47,8 @@ static void toast_insert_or_update(Relation rel, HeapTuple newtup,
|
47 | 47 | HeapTuple oldtup);
|
48 | 48 | static Datum toast_save_datum(Relation rel, Datum value);
|
49 | 49 | static varattrib *toast_fetch_datum(varattrib *attr);
|
| 50 | +static varattrib *toast_fetch_datum_slice(varattrib *attr, |
| 51 | + int32 sliceoffset, int32 length); |
50 | 52 |
|
51 | 53 |
|
52 | 54 | /* ----------
|
@@ -162,6 +164,80 @@ heap_tuple_untoast_attr(varattrib *attr)
|
162 | 164 | }
|
163 | 165 |
|
164 | 166 |
|
| 167 | +/* ---------- |
| 168 | + * heap_tuple_untoast_attr_slice - |
| 169 | + * |
| 170 | + * Public entry point to get back part of a toasted value |
| 171 | + * from compression or external storage. |
| 172 | + * ---------- |
| 173 | + */ |
| 174 | +varattrib * |
| 175 | +heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelength) |
| 176 | +{ |
| 177 | + varattrib *preslice; |
| 178 | + varattrib *result; |
| 179 | + int32 attrsize; |
| 180 | + |
| 181 | + if (VARATT_IS_COMPRESSED(attr)) |
| 182 | + { |
| 183 | + varattrib *tmp; |
| 184 | + |
| 185 | + if (VARATT_IS_EXTERNAL(attr)) |
| 186 | + { |
| 187 | + tmp = toast_fetch_datum(attr); |
| 188 | + } |
| 189 | + else |
| 190 | + { |
| 191 | + tmp = attr; /* compressed in main tuple */ |
| 192 | + } |
| 193 | + |
| 194 | + preslice = (varattrib *) palloc(attr->va_content.va_external.va_rawsize |
| 195 | + + VARHDRSZ); |
| 196 | + VARATT_SIZEP(preslice) = attr->va_content.va_external.va_rawsize + VARHDRSZ; |
| 197 | + pglz_decompress((PGLZ_Header *) tmp, VARATT_DATA(preslice)); |
| 198 | + |
| 199 | + if (tmp != attr) |
| 200 | + pfree(tmp); |
| 201 | + } |
| 202 | + else |
| 203 | + { |
| 204 | + /* Plain value */ |
| 205 | + if (VARATT_IS_EXTERNAL(attr)) |
| 206 | + { |
| 207 | + /* fast path */ |
| 208 | + return (toast_fetch_datum_slice(attr, sliceoffset, slicelength)); |
| 209 | + } |
| 210 | + else |
| 211 | + { |
| 212 | + preslice = attr; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + /* slicing of datum for compressed cases and plain value */ |
| 217 | + |
| 218 | + attrsize = VARSIZE(preslice) - VARHDRSZ; |
| 219 | + if (sliceoffset >= attrsize) |
| 220 | + { |
| 221 | + sliceoffset = 0; |
| 222 | + slicelength = 0; |
| 223 | + } |
| 224 | + |
| 225 | + if (((sliceoffset + slicelength) > attrsize) || slicelength < 0) |
| 226 | + { |
| 227 | + slicelength = attrsize - sliceoffset; |
| 228 | + } |
| 229 | + |
| 230 | + result = (varattrib *) palloc(slicelength + VARHDRSZ); |
| 231 | + VARATT_SIZEP(result) = slicelength + VARHDRSZ; |
| 232 | + |
| 233 | + memcpy(VARDATA(result), VARDATA(preslice) + sliceoffset, slicelength); |
| 234 | + |
| 235 | + if (preslice != attr) pfree(preslice); |
| 236 | + |
| 237 | + return result; |
| 238 | +} |
| 239 | + |
| 240 | + |
165 | 241 | /* ----------
|
166 | 242 | * toast_raw_datum_size -
|
167 | 243 | *
|
@@ -981,7 +1057,7 @@ toast_fetch_datum(varattrib *attr)
|
981 | 1057 | VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED;
|
982 | 1058 |
|
983 | 1059 | /*
|
984 |
| - * Open the toast relation and it's index |
| 1060 | + * Open the toast relation and its index |
985 | 1061 | */
|
986 | 1062 | toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
|
987 | 1063 | AccessShareLock);
|
@@ -1081,4 +1157,198 @@ toast_fetch_datum(varattrib *attr)
|
1081 | 1157 | return result;
|
1082 | 1158 | }
|
1083 | 1159 |
|
| 1160 | +/* ---------- |
| 1161 | + * toast_fetch_datum_slice - |
| 1162 | + * |
| 1163 | + * Reconstruct a segment of a varattrib from the chunks saved |
| 1164 | + * in the toast relation |
| 1165 | + * ---------- |
| 1166 | + */ |
| 1167 | +static varattrib * |
| 1168 | +toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) |
| 1169 | +{ |
| 1170 | + Relation toastrel; |
| 1171 | + Relation toastidx; |
| 1172 | + ScanKeyData toastkey[3]; |
| 1173 | + IndexScanDesc toastscan; |
| 1174 | + HeapTupleData toasttup; |
| 1175 | + HeapTuple ttup; |
| 1176 | + TupleDesc toasttupDesc; |
| 1177 | + RetrieveIndexResult indexRes; |
| 1178 | + Buffer buffer; |
| 1179 | + |
| 1180 | + varattrib *result; |
| 1181 | + int32 attrsize; |
| 1182 | + int32 nscankeys; |
| 1183 | + int32 residx; |
| 1184 | + int32 nextidx; |
| 1185 | + int numchunks; |
| 1186 | + int startchunk; |
| 1187 | + int endchunk; |
| 1188 | + int32 startoffset; |
| 1189 | + int32 endoffset; |
| 1190 | + int totalchunks; |
| 1191 | + Pointer chunk; |
| 1192 | + bool isnull; |
| 1193 | + int32 chunksize; |
| 1194 | + int32 chcpystrt; |
| 1195 | + int32 chcpyend; |
| 1196 | + |
| 1197 | + attrsize = attr->va_content.va_external.va_extsize; |
| 1198 | + totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; |
| 1199 | + |
| 1200 | + if (sliceoffset >= attrsize) |
| 1201 | + { |
| 1202 | + sliceoffset = 0; |
| 1203 | + length = 0; |
| 1204 | + } |
| 1205 | + |
| 1206 | + if (((sliceoffset + length) > attrsize) || length < 0) |
| 1207 | + { |
| 1208 | + length = attrsize - sliceoffset; |
| 1209 | + } |
| 1210 | + |
| 1211 | + result = (varattrib *) palloc(length + VARHDRSZ); |
| 1212 | + VARATT_SIZEP(result) = length + VARHDRSZ; |
| 1213 | + |
| 1214 | + if (VARATT_IS_COMPRESSED(attr)) |
| 1215 | + VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED; |
| 1216 | + |
| 1217 | + if (length == 0) return (result); /* Can save a lot of work at this point! */ |
| 1218 | + |
| 1219 | + startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE; |
| 1220 | + endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE; |
| 1221 | + numchunks = (endchunk - startchunk ) + 1; |
| 1222 | + |
| 1223 | + startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE; |
| 1224 | + endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; |
| 1225 | + |
| 1226 | + /* |
| 1227 | + * Open the toast relation and it's index |
| 1228 | + */ |
| 1229 | + toastrel = heap_open(attr->va_content.va_external.va_toastrelid, |
| 1230 | + AccessShareLock); |
| 1231 | + toasttupDesc = toastrel->rd_att; |
| 1232 | + toastidx = index_open(toastrel->rd_rel->reltoastidxid); |
| 1233 | + |
| 1234 | + /* |
| 1235 | + * Setup a scan key to fetch from the index. This is either two keys |
| 1236 | + * or three depending on the number of chunks. |
| 1237 | + */ |
| 1238 | + ScanKeyEntryInitialize(&toastkey[0], |
| 1239 | + (bits16) 0, |
| 1240 | + (AttrNumber) 1, |
| 1241 | + (RegProcedure) F_OIDEQ, |
| 1242 | + ObjectIdGetDatum(attr->va_content.va_external.va_valueid)); |
| 1243 | + /* |
| 1244 | + * Now dependent on number of chunks: |
| 1245 | + */ |
| 1246 | + |
| 1247 | + if (numchunks == 1) |
| 1248 | + { |
| 1249 | + ScanKeyEntryInitialize(&toastkey[1], |
| 1250 | + (bits16) 0, |
| 1251 | + (AttrNumber) 2, |
| 1252 | + (RegProcedure) F_INT4EQ, |
| 1253 | + Int32GetDatum(startchunk)); |
| 1254 | + nscankeys = 2; |
| 1255 | + } |
| 1256 | + else |
| 1257 | + { |
| 1258 | + ScanKeyEntryInitialize(&toastkey[1], |
| 1259 | + (bits16) 0, |
| 1260 | + (AttrNumber) 2, |
| 1261 | + (RegProcedure) F_INT4GE, |
| 1262 | + Int32GetDatum(startchunk)); |
| 1263 | + ScanKeyEntryInitialize(&toastkey[2], |
| 1264 | + (bits16) 0, |
| 1265 | + (AttrNumber) 2, |
| 1266 | + (RegProcedure) F_INT4LE, |
| 1267 | + Int32GetDatum(endchunk)); |
| 1268 | + nscankeys = 3; |
| 1269 | + } |
| 1270 | + |
| 1271 | + /* |
| 1272 | + * Read the chunks by index |
| 1273 | + * |
| 1274 | + * The index is on (valueid, chunkidx) so they will come in order |
| 1275 | + */ |
| 1276 | + nextidx = startchunk; |
| 1277 | + toastscan = index_beginscan(toastidx, false, nscankeys, &toastkey[0]); |
| 1278 | + while ((indexRes = index_getnext(toastscan, ForwardScanDirection)) != NULL) |
| 1279 | + { |
| 1280 | + toasttup.t_self = indexRes->heap_iptr; |
| 1281 | + heap_fetch(toastrel, SnapshotToast, &toasttup, &buffer, toastscan); |
| 1282 | + pfree(indexRes); |
| 1283 | + |
| 1284 | + if (toasttup.t_data == NULL) |
| 1285 | + continue; |
| 1286 | + ttup = &toasttup; |
| 1287 | + |
| 1288 | + /* |
| 1289 | + * Have a chunk, extract the sequence number and the data |
| 1290 | + */ |
| 1291 | + residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull)); |
| 1292 | + Assert(!isnull); |
| 1293 | + chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull)); |
| 1294 | + Assert(!isnull); |
| 1295 | + chunksize = VARATT_SIZE(chunk) - VARHDRSZ; |
| 1296 | + |
| 1297 | + /* |
| 1298 | + * Some checks on the data we've found |
| 1299 | + */ |
| 1300 | + if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk)) |
| 1301 | + elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u", |
| 1302 | + residx, nextidx, |
| 1303 | + attr->va_content.va_external.va_valueid); |
| 1304 | + if (residx < totalchunks - 1) |
| 1305 | + { |
| 1306 | + if (chunksize != TOAST_MAX_CHUNK_SIZE) |
| 1307 | + elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", |
| 1308 | + chunksize, residx, |
| 1309 | + attr->va_content.va_external.va_valueid); |
| 1310 | + } |
| 1311 | + else |
| 1312 | + { |
| 1313 | + if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize) |
| 1314 | + elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", |
| 1315 | + chunksize, residx, |
| 1316 | + attr->va_content.va_external.va_valueid); |
| 1317 | + } |
| 1318 | + |
| 1319 | + /* |
| 1320 | + * Copy the data into proper place in our result |
| 1321 | + */ |
| 1322 | + chcpystrt = 0; |
| 1323 | + chcpyend = chunksize - 1; |
| 1324 | + if (residx == startchunk) chcpystrt = startoffset; |
| 1325 | + if (residx == endchunk) chcpyend = endoffset; |
| 1326 | + |
| 1327 | + memcpy(((char *) VARATT_DATA(result)) + |
| 1328 | + (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) +chcpystrt, |
| 1329 | + VARATT_DATA(chunk) + chcpystrt, |
| 1330 | + (chcpyend - chcpystrt) + 1); |
| 1331 | + |
| 1332 | + ReleaseBuffer(buffer); |
| 1333 | + nextidx++; |
| 1334 | + } |
| 1335 | + |
| 1336 | + /* |
| 1337 | + * Final checks that we successfully fetched the datum |
| 1338 | + */ |
| 1339 | + if ( nextidx != (endchunk + 1)) |
| 1340 | + elog(ERROR, "missing chunk number %d for toast value %u", |
| 1341 | + nextidx, |
| 1342 | + attr->va_content.va_external.va_valueid); |
| 1343 | + |
| 1344 | + /* |
| 1345 | + * End scan and close relations |
| 1346 | + */ |
| 1347 | + index_endscan(toastscan); |
| 1348 | + index_close(toastidx); |
| 1349 | + heap_close(toastrel, AccessShareLock); |
| 1350 | + |
| 1351 | + return result; |
| 1352 | +} |
| 1353 | + |
1084 | 1354 | #endif /* TUPLE_TOASTER_ACTIVE */
|
0 commit comments