|
17 | 17 | *
|
18 | 18 | *
|
19 | 19 | * IDENTIFICATION
|
20 |
| - * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.122 2007/02/27 23:48:07 tgl Exp $ |
| 20 | + * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.123 2007/03/03 19:52:46 momjian Exp $ |
21 | 21 | *
|
22 | 22 | *-------------------------------------------------------------------------
|
23 | 23 | */
|
@@ -681,3 +681,166 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
|
681 | 681 |
|
682 | 682 | return nwritten;
|
683 | 683 | }
|
| 684 | + |
| 685 | +void |
| 686 | +inv_truncate(LargeObjectDesc *obj_desc, int len) |
| 687 | +{ |
| 688 | + int32 pageno = (int32) (len / LOBLKSIZE); |
| 689 | + int off; |
| 690 | + ScanKeyData skey[2]; |
| 691 | + IndexScanDesc sd; |
| 692 | + HeapTuple oldtuple; |
| 693 | + Form_pg_largeobject olddata; |
| 694 | + struct |
| 695 | + { |
| 696 | + bytea hdr; |
| 697 | + char data[LOBLKSIZE]; |
| 698 | + } workbuf; |
| 699 | + char *workb = VARDATA(&workbuf.hdr); |
| 700 | + HeapTuple newtup; |
| 701 | + Datum values[Natts_pg_largeobject]; |
| 702 | + char nulls[Natts_pg_largeobject]; |
| 703 | + char replace[Natts_pg_largeobject]; |
| 704 | + CatalogIndexState indstate; |
| 705 | + |
| 706 | + Assert(PointerIsValid(obj_desc)); |
| 707 | + |
| 708 | + /* enforce writability because snapshot is probably wrong otherwise */ |
| 709 | + if ((obj_desc->flags & IFS_WRLOCK) == 0) |
| 710 | + ereport(ERROR, |
| 711 | + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), |
| 712 | + errmsg("large object %u was not opened for writing", |
| 713 | + obj_desc->id))); |
| 714 | + |
| 715 | + open_lo_relation(); |
| 716 | + |
| 717 | + indstate = CatalogOpenIndexes(lo_heap_r); |
| 718 | + |
| 719 | + ScanKeyInit(&skey[0], |
| 720 | + Anum_pg_largeobject_loid, |
| 721 | + BTEqualStrategyNumber, F_OIDEQ, |
| 722 | + ObjectIdGetDatum(obj_desc->id)); |
| 723 | + |
| 724 | + ScanKeyInit(&skey[1], |
| 725 | + Anum_pg_largeobject_pageno, |
| 726 | + BTGreaterEqualStrategyNumber, F_INT4GE, |
| 727 | + Int32GetDatum(pageno)); |
| 728 | + |
| 729 | + sd = index_beginscan(lo_heap_r, lo_index_r, |
| 730 | + obj_desc->snapshot, 2, skey); |
| 731 | + |
| 732 | + /* |
| 733 | + * If possible, get the page the truncation point is in. |
| 734 | + * The truncation point may be beyond the end of the LO or |
| 735 | + * in a hole. |
| 736 | + */ |
| 737 | + olddata = NULL; |
| 738 | + if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) |
| 739 | + { |
| 740 | + olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple); |
| 741 | + Assert(olddata->pageno >= pageno); |
| 742 | + } |
| 743 | + |
| 744 | + /* |
| 745 | + * If we found the page of the truncation point we need to |
| 746 | + * truncate the data in it. Otherwise if we're in a hole, |
| 747 | + * we need to create a page to mark the end of data. |
| 748 | + */ |
| 749 | + if (olddata != NULL && olddata->pageno == pageno) |
| 750 | + { |
| 751 | + /* First, load old data into workbuf */ |
| 752 | + bytea *datafield = &(olddata->data); |
| 753 | + bool pfreeit = false; |
| 754 | + int pagelen; |
| 755 | + |
| 756 | + if (VARATT_IS_EXTENDED(datafield)) |
| 757 | + { |
| 758 | + datafield = (bytea *) |
| 759 | + heap_tuple_untoast_attr((varattrib *) datafield); |
| 760 | + pfreeit = true; |
| 761 | + } |
| 762 | + pagelen = getbytealen(datafield); |
| 763 | + Assert(pagelen <= LOBLKSIZE); |
| 764 | + memcpy(workb, VARDATA(datafield), pagelen); |
| 765 | + if (pfreeit) |
| 766 | + pfree(datafield); |
| 767 | + |
| 768 | + /* |
| 769 | + * Fill any hole |
| 770 | + */ |
| 771 | + off = len % LOBLKSIZE; |
| 772 | + if (off > pagelen) |
| 773 | + MemSet(workb + pagelen, 0, off - pagelen); |
| 774 | + |
| 775 | + /* compute length of new page */ |
| 776 | + SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); |
| 777 | + |
| 778 | + /* |
| 779 | + * Form and insert updated tuple |
| 780 | + */ |
| 781 | + memset(values, 0, sizeof(values)); |
| 782 | + memset(nulls, ' ', sizeof(nulls)); |
| 783 | + memset(replace, ' ', sizeof(replace)); |
| 784 | + values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); |
| 785 | + replace[Anum_pg_largeobject_data - 1] = 'r'; |
| 786 | + newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r), |
| 787 | + values, nulls, replace); |
| 788 | + simple_heap_update(lo_heap_r, &newtup->t_self, newtup); |
| 789 | + CatalogIndexInsert(indstate, newtup); |
| 790 | + heap_freetuple(newtup); |
| 791 | + } |
| 792 | + else |
| 793 | + { |
| 794 | + /* |
| 795 | + * If the first page we found was after the truncation |
| 796 | + * point, we're in a hole that we'll fill, but we need to |
| 797 | + * delete the later page. |
| 798 | + */ |
| 799 | + if (olddata != NULL && olddata->pageno > pageno) |
| 800 | + simple_heap_delete(lo_heap_r, &oldtuple->t_self); |
| 801 | + |
| 802 | + /* |
| 803 | + * Write a brand new page. |
| 804 | + * |
| 805 | + * Fill the hole up to the truncation point |
| 806 | + */ |
| 807 | + off = len % LOBLKSIZE; |
| 808 | + if (off > 0) |
| 809 | + MemSet(workb, 0, off); |
| 810 | + |
| 811 | + /* compute length of new page */ |
| 812 | + SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ); |
| 813 | + |
| 814 | + /* |
| 815 | + * Form and insert new tuple |
| 816 | + */ |
| 817 | + memset(values, 0, sizeof(values)); |
| 818 | + memset(nulls, ' ', sizeof(nulls)); |
| 819 | + values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id); |
| 820 | + values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); |
| 821 | + values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); |
| 822 | + newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls); |
| 823 | + simple_heap_insert(lo_heap_r, newtup); |
| 824 | + CatalogIndexInsert(indstate, newtup); |
| 825 | + heap_freetuple(newtup); |
| 826 | + } |
| 827 | + |
| 828 | + /* |
| 829 | + * Delete any pages after the truncation point |
| 830 | + */ |
| 831 | + while ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) |
| 832 | + { |
| 833 | + simple_heap_delete(lo_heap_r, &oldtuple->t_self); |
| 834 | + } |
| 835 | + |
| 836 | + index_endscan(sd); |
| 837 | + |
| 838 | + CatalogCloseIndexes(indstate); |
| 839 | + |
| 840 | + /* |
| 841 | + * Advance command counter so that tuple updates will be seen by later |
| 842 | + * large-object operations in this transaction. |
| 843 | + */ |
| 844 | + CommandCounterIncrement(); |
| 845 | +} |
| 846 | + |
0 commit comments