From b0025bd95703aaedc4ff23ddcfbde9597fa0919d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 11 Sep 2011 16:29:42 -0400
Subject: [PATCH] Invent a new memory context primitive,
 MemoryContextSetParent.

This function will be useful for altering the lifespan of a context after
creation (for example, by creating it under a transient context and later
reparenting it to belong to a long-lived context).  It costs almost no new
code, since we can refactor what was there.  Per my proposal of yesterday.
---
 src/backend/utils/mmgr/mcxt.c | 82 +++++++++++++++++++++++++++--------
 src/include/utils/memutils.h  |  2 +
 2 files changed, 65 insertions(+), 19 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 7ed35d5bdf1..4eab24c46c7 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -178,26 +178,8 @@ MemoryContextDelete(MemoryContext context)
 	 * there's an error we won't have deleted/busted contexts still attached
 	 * to the context tree.  Better a leak than a crash.
 	 */
-	if (context->parent)
-	{
-		MemoryContext parent = context->parent;
-
-		if (context == parent->firstchild)
-			parent->firstchild = context->nextchild;
-		else
-		{
-			MemoryContext child;
+	MemoryContextSetParent(context, NULL);
 
-			for (child = parent->firstchild; child; child = child->nextchild)
-			{
-				if (context == child->nextchild)
-				{
-					child->nextchild = context->nextchild;
-					break;
-				}
-			}
-		}
-	}
 	(*context->methods->delete_context) (context);
 	pfree(context);
 }
@@ -237,6 +219,67 @@ MemoryContextResetAndDeleteChildren(MemoryContext context)
 	MemoryContextReset(context);
 }
 
+/*
+ * MemoryContextSetParent
+ *		Change a context to belong to a new parent (or no parent).
+ *
+ * We provide this as an API function because it is sometimes useful to
+ * change a context's lifespan after creation.  For example, a context
+ * might be created underneath a transient context, filled with data,
+ * and then reparented underneath CacheMemoryContext to make it long-lived.
+ * In this way no special effort is needed to get rid of the context in case
+ * a failure occurs before its contents are completely set up.
+ *
+ * Callers often assume that this function cannot fail, so don't put any
+ * elog(ERROR) calls in it.
+ *
+ * A possible caller error is to reparent a context under itself, creating
+ * a loop in the context graph.  We assert here that context != new_parent,
+ * but checking for multi-level loops seems more trouble than it's worth.
+ */
+void
+MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
+{
+	AssertArg(MemoryContextIsValid(context));
+	AssertArg(context != new_parent);
+
+	/* Delink from existing parent, if any */
+	if (context->parent)
+	{
+		MemoryContext parent = context->parent;
+
+		if (context == parent->firstchild)
+			parent->firstchild = context->nextchild;
+		else
+		{
+			MemoryContext child;
+
+			for (child = parent->firstchild; child; child = child->nextchild)
+			{
+				if (context == child->nextchild)
+				{
+					child->nextchild = context->nextchild;
+					break;
+				}
+			}
+		}
+	}
+
+	/* And relink */
+	if (new_parent)
+	{
+		AssertArg(MemoryContextIsValid(new_parent));
+		context->parent = new_parent;
+		context->nextchild = new_parent->firstchild;
+		new_parent->firstchild = context;
+	}
+	else
+	{
+		context->parent = NULL;
+		context->nextchild = NULL;
+	}
+}
+
 /*
  * GetMemoryChunkSpace
  *		Given a currently-allocated chunk, determine the total space
@@ -489,6 +532,7 @@ MemoryContextCreate(NodeTag tag, Size size,
 	(*node->methods->init) (node);
 
 	/* OK to link node to parent (if any) */
+	/* Could use MemoryContextSetParent here, but doesn't seem worthwhile */
 	if (parent)
 	{
 		node->parent = parent;
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 7c1202478e5..94c78289b6e 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -90,6 +90,8 @@ extern void MemoryContextDelete(MemoryContext context);
 extern void MemoryContextResetChildren(MemoryContext context);
 extern void MemoryContextDeleteChildren(MemoryContext context);
 extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
+extern void MemoryContextSetParent(MemoryContext context,
+								   MemoryContext new_parent);
 extern Size GetMemoryChunkSpace(void *pointer);
 extern MemoryContext GetMemoryChunkContext(void *pointer);
 extern bool MemoryContextIsEmpty(MemoryContext context);
-- 
GitLab