In GIN recompression code, use mmemove rather than memcpy, for vacuum.
authorHeikki Linnakangas <[email protected]>
Fri, 24 Jan 2014 08:42:38 +0000 (10:42 +0200)
committerHeikki Linnakangas <[email protected]>
Fri, 24 Jan 2014 08:48:45 +0000 (10:48 +0200)
When vacuuming a data leaf page, any compressed posting lists that are not
modified, are copied back to the buffer from a later location in the same
buffer rather than from  a palloc'd copy. IOW, they are just moved
downwards in the same buffer. Because the source and destination addresses
can overlap, we must use memmove rather than memcpy.

Report and fix by Alexander Korotkov.

src/backend/access/gin/gindatapage.c

index 8504f4c7afc8b9e47783e2f1953a5f07712c2bd0..ebdacd40c55f9846b04902f8d6f9466b15804577 100644 (file)
@@ -753,6 +753,13 @@ ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
  * *prdata is filled with WAL information about this operation. The caller
  * is responsible for inserting to the WAL, along with any other information
  * about the operation that triggered this recompression.
+ *
+ * NOTE: The segment pointers can point directly to the same buffer, with
+ * the limitation that any earlier segment must not overlap with an original,
+ * later segment. In other words, some segments may point the original buffer
+ * as long as you don't make any segments larger. Currently, leafRepackItems
+ * satisies this rule because it rewrites all segments after the first
+ * modified one, and vacuum can only make segments shorter.
  */
 static void
 dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf,
@@ -798,7 +805,13 @@ dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf,
                if (!modified)
                        unmodifiedsize += segsize;
                else
-                       memcpy(ptr, seginfo->seg, segsize);
+               {
+                       /*
+                        * Use memmove rather than memcpy, in case the segment points
+                        * to the same buffer
+                        */
+                       memmove(ptr, seginfo->seg, segsize);
+               }
                ptr += segsize;
                newsize += segsize;
        }