Skip to content

Commit dea7d54

Browse files
committed
If a field is incompressible ('compressed' data is actually larger than
source, due to addition of header overhead), store it as plain data rather than pseudo-compressed data. This saves a few microseconds when reading it out, but much more importantly guarantees that the toaster won't actually expand tuples that contain incompressible data. That's essential to avoid 'Tuple too big' failures with large objects.
1 parent 62bc33d commit dea7d54

File tree

1 file changed

+65
-29
lines changed

1 file changed

+65
-29
lines changed

src/backend/access/heap/tuptoaster.c

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.12 2000/08/04 04:16:07 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.13 2000/10/23 23:42:04 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -247,6 +247,11 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
247247

248248
/* ----------
249249
* Then collect information about the values given
250+
*
251+
* NOTE: toast_action[i] can have these values:
252+
* ' ' default handling
253+
* 'p' already processed --- don't touch it
254+
* 'x' incompressible, but OK to move off
250255
* ----------
251256
*/
252257
memset(toast_action, ' ', numAttrs * sizeof(char));
@@ -397,14 +402,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
397402
int biggest_attno = -1;
398403
int32 biggest_size = MAXALIGN(sizeof(varattrib));
399404
Datum old_value;
405+
Datum new_value;
400406

401407
/* ----------
402408
* Search for the biggest yet uncompressed internal attribute
403409
* ----------
404410
*/
405411
for (i = 0; i < numAttrs; i++)
406412
{
407-
if (toast_action[i] == 'p')
413+
if (toast_action[i] != ' ')
408414
continue;
409415
if (VARATT_IS_EXTENDED(toast_values[i]))
410416
continue;
@@ -421,20 +427,29 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
421427
break;
422428

423429
/* ----------
424-
* Compress it inline
430+
* Attempt to compress it inline
425431
* ----------
426432
*/
427433
i = biggest_attno;
428434
old_value = toast_values[i];
435+
new_value = toast_compress_datum(old_value);
429436

430-
toast_values[i] = toast_compress_datum(toast_values[i]);
431-
if (toast_free[i])
432-
pfree(DatumGetPointer(old_value));
433-
toast_free[i] = true;
434-
toast_sizes[i] = VARATT_SIZE(toast_values[i]);
435-
436-
need_change = true;
437-
need_free = true;
437+
if (DatumGetPointer(new_value) != NULL)
438+
{
439+
/* successful compression */
440+
if (toast_free[i])
441+
pfree(DatumGetPointer(old_value));
442+
toast_values[i] = new_value;
443+
toast_free[i] = true;
444+
toast_sizes[i] = VARATT_SIZE(toast_values[i]);
445+
need_change = true;
446+
need_free = true;
447+
}
448+
else
449+
{
450+
/* incompressible data, ignore on subsequent compression passes */
451+
toast_action[i] = 'x';
452+
}
438453
}
439454

440455
/* ----------
@@ -504,14 +519,15 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
504519
int biggest_attno = -1;
505520
int32 biggest_size = MAXALIGN(sizeof(varattrib));
506521
Datum old_value;
522+
Datum new_value;
507523

508524
/* ----------
509525
* Search for the biggest yet uncompressed internal attribute
510526
* ----------
511527
*/
512528
for (i = 0; i < numAttrs; i++)
513529
{
514-
if (toast_action[i] == 'p')
530+
if (toast_action[i] != ' ')
515531
continue;
516532
if (VARATT_IS_EXTENDED(toast_values[i]))
517533
continue;
@@ -528,22 +544,29 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
528544
break;
529545

530546
/* ----------
531-
* Compress it inline
547+
* Attempt to compress it inline
532548
* ----------
533549
*/
534550
i = biggest_attno;
535551
old_value = toast_values[i];
552+
new_value = toast_compress_datum(old_value);
536553

537-
toast_values[i] = toast_compress_datum(toast_values[i]);
538-
539-
if (toast_free[i])
540-
pfree(DatumGetPointer(old_value));
541-
542-
toast_free[i] = true;
543-
toast_sizes[i] = VARATT_SIZE(toast_values[i]);
544-
545-
need_change = true;
546-
need_free = true;
554+
if (DatumGetPointer(new_value) != NULL)
555+
{
556+
/* successful compression */
557+
if (toast_free[i])
558+
pfree(DatumGetPointer(old_value));
559+
toast_values[i] = new_value;
560+
toast_free[i] = true;
561+
toast_sizes[i] = VARATT_SIZE(toast_values[i]);
562+
need_change = true;
563+
need_free = true;
564+
}
565+
else
566+
{
567+
/* incompressible data, ignore on subsequent compression passes */
568+
toast_action[i] = 'x';
569+
}
547570
}
548571

549572
/* ----------
@@ -690,20 +713,33 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
690713
* toast_compress_datum -
691714
*
692715
* Create a compressed version of a varlena datum
716+
*
717+
* If we fail (ie, compressed result is actually bigger than original)
718+
* then return NULL. We must not use compressed data if it'd expand
719+
* the tuple!
693720
* ----------
694721
*/
695722
static Datum
696723
toast_compress_datum(Datum value)
697724
{
698725
varattrib *tmp;
699726

700-
tmp = (varattrib *)palloc(sizeof(PGLZ_Header) + VARATT_SIZE(value));
727+
tmp = (varattrib *) palloc(sizeof(PGLZ_Header) + VARATT_SIZE(value));
701728
pglz_compress(VARATT_DATA(value), VARATT_SIZE(value) - VARHDRSZ,
702-
(PGLZ_Header *)tmp,
703-
PGLZ_strategy_default);
704-
VARATT_SIZEP(tmp) |= VARATT_FLAG_COMPRESSED;
705-
706-
return PointerGetDatum(tmp);
729+
(PGLZ_Header *) tmp,
730+
PGLZ_strategy_default);
731+
if (VARATT_SIZE(tmp) < VARATT_SIZE(value))
732+
{
733+
/* successful compression */
734+
VARATT_SIZEP(tmp) |= VARATT_FLAG_COMPRESSED;
735+
return PointerGetDatum(tmp);
736+
}
737+
else
738+
{
739+
/* incompressible data */
740+
pfree(tmp);
741+
return PointerGetDatum(NULL);
742+
}
707743
}
708744

709745

0 commit comments

Comments
 (0)