summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndres Freund2019-09-20 02:41:23 +0000
committerAndres Freund2019-09-20 04:25:10 +0000
commit0198b4bbb660d23a20717fd315735bc2c6ccce73 (patch)
tree624bb2a6e325ffaeed8c8e81fb73901c0e7c758e
parent1b5bee11be1f685f6e53907e602b1be49501bccb (diff)
WIP: Add support for copying node tree into single allocation.header
As an example as to why one would want that, here's a MemoryContextStats for the same query being added to the plancache. The query is just a randomly selected one among the queries issued by \d+. PREPARE foo AS SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, false AS relhasoids, c.relispartition, pg_catalog.array_to_string(c.reloptions || array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', '), c.reltablespace, CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, c.relpersistence, c.relreplident, am.amname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid) LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid) WHERE c.oid = '1259'; EXECUTE foo ; With single-allocation: CachedPlan: 24504 total in 2 blocks; 664 free (0 chunks); 23840 used Grand total: 24504 bytes in 2 blocks; 664 free (0 chunks); 23840 used Default: CachedPlan: 65536 total in 7 blocks; 16016 free (0 chunks); 49520 used Grand total: 65536 bytes in 7 blocks; 16016 free (0 chunks); 49520 used Author: Reviewed-By: Discussion: https://postgr.es/m/ Backpatch:
-rw-r--r--src/backend/nodes/copyfuncs_new.c274
-rw-r--r--src/backend/utils/cache/plancache.c8
-rw-r--r--src/include/nodes/nodes.h3
3 files changed, 279 insertions, 6 deletions
diff --git a/src/backend/nodes/copyfuncs_new.c b/src/backend/nodes/copyfuncs_new.c
index 77cd6b6846..95532f4417 100644
--- a/src/backend/nodes/copyfuncs_new.c
+++ b/src/backend/nodes/copyfuncs_new.c
@@ -22,6 +22,12 @@ static void nodecopy_fields(CopyNodeContext *context, Node *dst, const Node *src
static List* nodecopy_list(CopyNodeContext *context, const List *obj, NodeTag tag);
static void nodecopy_value_union(CopyNodeContext *context, Value *dst, const Value *src);
+static void nodesize_rec(CopyNodeContext *context, const Node *obj);
+static void nodesize_fields(CopyNodeContext *context, const Node *obj, const TINodeType *type_info);
+static void nodesize_list(CopyNodeContext *context, const List *obj, NodeTag tag);
+static void nodesize_value_union(CopyNodeContext *context, const Value *src);
+static void nodesize_count(CopyNodeContext *context, size_t size, size_t align);
+
#define BITMAPSET_SIZE(nwords) \
(offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword))
@@ -40,16 +46,280 @@ copyObjectImpl(const void *from)
return nodecopy_new_rec(&context, from);
}
+void *
+copyObjectRoImpl(const void *obj)
+{
+#if 0
+ CopyNodeContext context = {0};
+
+ /* count space */
+ nodesize_rec(&context, obj);
+
+ /* allocate memory in one go */
+ context.space = palloc_extended(context.required_space,
+ MCXT_ALLOC_HUGE);
+
+ return nodecopy_new_rec(&context, obj);
+#else
+ return copyObjectImpl(obj);
+#endif
+}
+
+static void
+nodesize_rec(CopyNodeContext *context, const Node *obj)
+{
+ const TINodeType *type_info;
+ NodeTag tag;
+
+ if (obj == NULL)
+ return;
+
+ tag = nodeTag(obj);
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ switch (tag)
+ {
+ case T_List:
+ case T_OidList:
+ case T_IntList:
+ nodesize_list(context, (List *) obj, tag);
+ return;
+
+ default:
+ break;
+ }
+
+ type_info = &ti_node_types[tag];
+
+ Assert(type_info->size != TYPE_SIZE_UNKNOWN);
+
+ nodesize_count(context, type_info->size, MAXIMUM_ALIGNOF);
+ nodesize_fields(context, obj, type_info);
+}
+
+static void
+nodesize_count(CopyNodeContext *context, size_t size, size_t align)
+{
+ size_t alignup;
+
+ alignup = TYPEALIGN(align, context->required_space) - context->required_space;
+ Assert(alignup < MAXIMUM_ALIGNOF);
+ context->required_space += alignup;
+ context->required_space += size;
+}
+
+static void
+nodesize_fields(CopyNodeContext *context, const Node *obj, const TINodeType *type_info)
+{
+ const TIStructField *field_info = &ti_struct_fields[type_info->first_field_at];
+
+ for (int i = 0; i < type_info->num_fields; i++, field_info++)
+ {
+ const void *field_ptr;
+
+ // FIXME: ExtensibleNode needs to call callbacks, or be reimplemented
+
+ if (field_info->flags & TYPE_COPY_IGNORE)
+ continue;
+
+ field_ptr = ((char *) obj + field_info->offset);
+
+ switch (field_info->known_type_id)
+ {
+ case KNOWN_TYPE_NODE:
+ {
+ NodeTag sub_tag;
+ const TINodeType *sub_type_info;
+
+ Assert(field_info->type_id != TYPE_ID_UNKNOWN);
+
+ if (field_info->offset == 0)
+ sub_tag = field_info->type_id;
+ else
+ {
+ sub_tag = nodeTag(field_ptr);
+
+ Assert(ti_node_types[sub_tag].size ==
+ ti_node_types[field_info->type_id].size);
+ }
+
+ sub_type_info = &ti_node_types[sub_tag];
+
+ nodesize_fields(context,
+ (const Node *) field_ptr,
+ sub_type_info);
+
+ break;
+ }
+
+ case KNOWN_TYPE_DATUM:
+ {
+ const Const *cptr = castNode(Const, (Node *) obj);
+
+ if (!cptr->constbyval && cptr->constisnull)
+ {
+ /* passed by reference, account for space */
+ // FIXME: could reduce alignment, but would require additional information
+ nodesize_count(context,
+ datumGetSize(cptr->constvalue,
+ cptr->constbyval,
+ cptr->constlen),
+ MAXIMUM_ALIGNOF);
+ }
+ }
+
+ break;
+
+ case KNOWN_TYPE_VALUE_UNION:
+ Assert(IsA(obj, Integer) || IsA(obj, Float) ||
+ IsA(obj, String) || IsA(obj, BitString) ||
+ IsA(obj, Null));
+
+ nodesize_value_union(context, (const Value *) obj);
+ break;
+
+ case KNOWN_TYPE_P_PGARR:
+ if (*(PgArrBase **) field_ptr == NULL)
+ break;
+
+ Assert(field_info->elem_size > 0);
+ Assert(field_info->elem_known_type_id > 0);
+
+ nodesize_count(context,
+ MAXALIGN(sizeof(PgArrBase)) +
+ field_info->elem_size * (*(PgArrBase **) field_ptr)->size,
+ MAXIMUM_ALIGNOF);
+ break;
+
+ case KNOWN_TYPE_P_NODE:
+ if (*(Node **) field_ptr == NULL)
+ break;
+ nodesize_rec(context, *(Node **) field_ptr);
+ break;
+
+ case KNOWN_TYPE_P_CHAR:
+ if (*(char **) field_ptr == NULL)
+ break;
+ nodesize_count(context,
+ strlen(*(char **) field_ptr) + 1,
+ 1);
+ break;
+
+ case KNOWN_TYPE_P_BITMAPSET:
+ if (*(const Bitmapset **) field_ptr == NULL)
+ break;
+
+ nodesize_count(context,
+ BITMAPSET_SIZE((*(const Bitmapset **) field_ptr)->nwords),
+ MAXIMUM_ALIGNOF);
+ break;
+
+ default:
+ if (field_info->flags & (TYPE_COPY_FORCE_SCALAR ||
+ TYPE_CAT_SCALAR))
+ Assert(field_info->size != TYPE_SIZE_UNKNOWN);
+ else
+ elog(ERROR, "don't know how to copy field %s %s->%s",
+ ti_strings[field_info->type].string,
+ ti_strings[type_info->name].string,
+ ti_strings[field_info->name].string);
+ }
+ }
+}
+
+static void
+nodesize_list(CopyNodeContext *context, const List *obj, NodeTag tag)
+{
+ /* XXX: this is copying implementation details from new_list. */
+ nodesize_count(context,
+ offsetof(List, initial_elements) +
+ obj->length * sizeof(ListCell),
+ MAXIMUM_ALIGNOF);
+
+ switch (tag)
+ {
+ case T_List:
+ for (int i = 0; i < obj->length; i++)
+ {
+ nodesize_rec(context, lfirst(&obj->elements[i]));
+ }
+ break;
+
+ case T_OidList:
+ case T_IntList:
+ /* already accounted for */
+ break;
+
+ default:
+ pg_unreachable();
+ }
+}
+
+static void
+nodesize_value_union(CopyNodeContext *context, const Value *obj)
+{
+ switch (nodeTag(obj))
+ {
+ case T_Null:
+ case T_Integer:
+ /* part of struct Value itself */
+ break;
+
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ if (obj->val.str != NULL)
+ nodesize_count(context,
+ strlen(obj->val.str) + 1,
+ 1);
+ break;
+
+ default:
+ pg_unreachable();
+ break;
+ }
+}
+
static inline void*
nodecopy_alloc(CopyNodeContext *context, size_t size, size_t align)
{
- return palloc(size);
+ if (context->space == NULL)
+ return palloc(size);
+ else
+ {
+ void *ret;
+ size_t alignup;
+
+ alignup = TYPEALIGN(align, context->used_space) - context->used_space;
+ Assert(alignup < MAXIMUM_ALIGNOF);
+
+ Assert(context->used_space + alignup <= context->required_space);
+ context->used_space += alignup;
+
+ ret = context->space + context->used_space;
+
+ Assert(context->used_space + size <= context->required_space);
+ context->used_space += size;
+
+ return ret;
+ }
}
static inline void*
nodecopy_alloc0(CopyNodeContext *context, size_t size, size_t align)
{
- return palloc0(size);
+ if (context->space == NULL)
+ return palloc0(size);
+ else
+ {
+ void *alloc = nodecopy_alloc(context, size, align);
+
+ memset(alloc, 0, size);
+
+ return alloc;
+ }
}
static Node*
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index abc3062892..a41117963d 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -190,7 +190,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
plansource->magic = CACHEDPLANSOURCE_MAGIC;
- plansource->raw_parse_tree = copyObject(raw_parse_tree);
+ plansource->raw_parse_tree = copyObjectRo(raw_parse_tree);
plansource->query_string = pstrdup(query_string);
MemoryContextSetIdentifier(source_context, plansource->query_string);
plansource->commandTag = commandTag;
@@ -373,7 +373,7 @@ CompleteCachedPlan(CachedPlanSource *plansource,
"CachedPlanQuery",
ALLOCSET_START_SMALL_SIZES);
MemoryContextSwitchTo(querytree_context);
- querytree_list = copyObject(querytree_list);
+ querytree_list = copyObjectRo(querytree_list);
}
plansource->query_context = querytree_context;
@@ -954,7 +954,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
*/
MemoryContextSwitchTo(plan_context);
- plist = copyObject(plist);
+ plist = copyObjectRo(plist);
}
else
plan_context = CurrentMemoryContext;
@@ -1347,7 +1347,7 @@ CopyCachedPlan(CachedPlanSource *plansource)
newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
newsource->magic = CACHEDPLANSOURCE_MAGIC;
- newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
+ newsource->raw_parse_tree = copyObjectRo(plansource->raw_parse_tree);
newsource->query_string = pstrdup(plansource->query_string);
MemoryContextSetIdentifier(source_context, newsource->query_string);
newsource->commandTag = plansource->commandTag;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 563eeb40e6..ffe6d61f11 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -634,12 +634,15 @@ extern uintptr_t readDatum(bool typbyval);
* nodes/copyfuncs.c
*/
extern void *copyObjectImpl(const void *obj);
+extern void *copyObjectRoImpl(const void *obj);
/* cast result back to argument type, if supported by compiler */
#ifdef HAVE_TYPEOF
#define copyObject(obj) ((typeof(obj)) copyObjectImpl(obj))
+#define copyObjectRo(obj) ((typeof(obj)) copyObjectRoImpl(obj))
#else
#define copyObject(obj) copyObjectImpl(obj)
+#define copyObjectRo(obj) copyObjectRoImpl(obj)
#endif
/*