v3-0002-Add-a-test-module-to-exercise-dsa.c.patch
application/x-patch
Filename: v3-0002-Add-a-test-module-to-exercise-dsa.c.patch
Type: application/x-patch
Part: 1
Message:
Re: DSA failed to allocate memory
Patch
Format: format-patch
Series: patch v3-0002
Subject: Add a test module to exercise dsa.c.
| File | + | − |
|---|---|---|
| src/test/modules/Makefile | 1 | 0 |
| src/test/modules/meson.build | 1 | 0 |
| src/test/modules/test_dsa/expected/test_dsa.out | 55 | 0 |
| src/test/modules/test_dsa/.gitignore | 4 | 0 |
| src/test/modules/test_dsa/Makefile | 19 | 0 |
| src/test/modules/test_dsa/meson.build | 36 | 0 |
| src/test/modules/test_dsa/sql/test_dsa.sql | 13 | 0 |
| src/test/modules/test_dsa/test_dsa--1.0.sql | 21 | 0 |
| src/test/modules/test_dsa/test_dsa.c | 407 | 0 |
| src/test/modules/test_dsa/test_dsa.control | 5 | 0 |
| src/tools/pgindent/typedefs.list | 4 | 0 |
From 664968277c9a071a83f79edb1c768de0649889ed Mon Sep 17 00:00:00 2001
From: Liu Dongming <ldming101@gmail.com>
Date: Fri, 18 Mar 2022 13:26:11 +0800
Subject: [PATCH v3 2/2] Add a test module to exercise dsa.c.
Code originally developed along with dsa.c back in 2016, now extended
with a capped allocate-then-free-twice test by Dongming Liu, as part of
a bug investigation and fix.
Author: Dongming Liu <ldming101@gmail.com>
Author: Thomas Munro <thomas.munro@gmail.com>
Discussion: https://postgr.es/m/CAL1p7e8LzB2LSeAXo2pXCW4%2BRya9s0sJ3G_ReKOU%3DAjSUWjHWQ%40mail.gmail.com
Discussion: https://postgr.es/m/CAEepm%3D3U7%2BRo7%3DECeQuAZoeFXs8iDVX56NXGCV7z3%3D%2BH%2BWd0Sw%40mail.gmail.com
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/test_dsa/.gitignore | 4 +
src/test/modules/test_dsa/Makefile | 19 +
.../modules/test_dsa/expected/test_dsa.out | 55 +++
src/test/modules/test_dsa/meson.build | 36 ++
src/test/modules/test_dsa/sql/test_dsa.sql | 13 +
src/test/modules/test_dsa/test_dsa--1.0.sql | 21 +
src/test/modules/test_dsa/test_dsa.c | 407 ++++++++++++++++++
src/test/modules/test_dsa/test_dsa.control | 5 +
src/tools/pgindent/typedefs.list | 4 +
11 files changed, 566 insertions(+)
create mode 100644 src/test/modules/test_dsa/.gitignore
create mode 100644 src/test/modules/test_dsa/Makefile
create mode 100644 src/test/modules/test_dsa/expected/test_dsa.out
create mode 100644 src/test/modules/test_dsa/meson.build
create mode 100644 src/test/modules/test_dsa/sql/test_dsa.sql
create mode 100644 src/test/modules/test_dsa/test_dsa--1.0.sql
create mode 100644 src/test/modules/test_dsa/test_dsa.c
create mode 100644 src/test/modules/test_dsa/test_dsa.control
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index c629cbe383..9926c18705 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -18,6 +18,7 @@ SUBDIRS = \
test_copy_callbacks \
test_custom_rmgrs \
test_ddl_deparse \
+ test_dsa \
test_extensions \
test_ginpostinglist \
test_integerset \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 1baa6b558d..17151bfdaa 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -14,6 +14,7 @@ subdir('test_bloomfilter')
subdir('test_copy_callbacks')
subdir('test_custom_rmgrs')
subdir('test_ddl_deparse')
+subdir('test_dsa')
subdir('test_extensions')
subdir('test_ginpostinglist')
subdir('test_integerset')
diff --git a/src/test/modules/test_dsa/.gitignore b/src/test/modules/test_dsa/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/src/test/modules/test_dsa/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_dsa/Makefile b/src/test/modules/test_dsa/Makefile
new file mode 100644
index 0000000000..f45642a32a
--- /dev/null
+++ b/src/test/modules/test_dsa/Makefile
@@ -0,0 +1,19 @@
+# src/test/modules/test_dsa/Makefile
+
+MODULES = test_dsa
+
+EXTENSION = test_dsa
+DATA = test_dsa--1.0.sql
+PGFILEDESC = "test_dsa -- tests for DSA areas"
+REGRESS = test_dsa
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_dsa
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_dsa/expected/test_dsa.out b/src/test/modules/test_dsa/expected/test_dsa.out
new file mode 100644
index 0000000000..4d594130ca
--- /dev/null
+++ b/src/test/modules/test_dsa/expected/test_dsa.out
@@ -0,0 +1,55 @@
+CREATE EXTENSION test_dsa;
+SELECT test_dsa_random(3, 5, 16, 10240, 'random');
+ test_dsa_random
+-----------------
+
+(1 row)
+
+SELECT test_dsa_random(3, 5, 16, 10240, 'forwards');
+ test_dsa_random
+-----------------
+
+(1 row)
+
+SELECT test_dsa_random(3, 5, 16, 10240, 'backwards');
+ test_dsa_random
+-----------------
+
+(1 row)
+
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'random', 5);
+ count
+-------
+ 5
+(1 row)
+
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'forwards', 5);
+ count
+-------
+ 5
+(1 row)
+
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'backwards', 5);
+ count
+-------
+ 5
+(1 row)
+
+SELECT test_dsa_oom(1024);
+ test_dsa_oom
+--------------
+
+(1 row)
+
+SELECT test_dsa_oom(8192);
+ test_dsa_oom
+--------------
+
+(1 row)
+
+SELECT test_dsa_oom(10240);
+ test_dsa_oom
+--------------
+
+(1 row)
+
diff --git a/src/test/modules/test_dsa/meson.build b/src/test/modules/test_dsa/meson.build
new file mode 100644
index 0000000000..4578756ecf
--- /dev/null
+++ b/src/test/modules/test_dsa/meson.build
@@ -0,0 +1,36 @@
+# Copyright (c) 2022-2023, PostgreSQL Global Development Group
+
+# FIXME: prevent install during main install, but not during test :/
+
+test_dsa_sources = files(
+ 'test_dsa.c',
+)
+
+if host_system == 'windows'
+ test_dsa_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_dsa',
+ '--FILEDESC', 'test_dsa - test code for dsa.c',])
+endif
+
+test_dsa = shared_module('test_dsa',
+ test_dsa_sources,
+ kwargs: pg_mod_args,
+)
+testprep_targets += test_dsa
+
+install_data(
+ 'test_dsa.control',
+ 'test_dsa--1.0.sql',
+ kwargs: contrib_data_args,
+)
+
+tests += {
+ 'name': 'test_dsa',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'regress': {
+ 'sql': [
+ 'test_dsa',
+ ],
+ },
+}
diff --git a/src/test/modules/test_dsa/sql/test_dsa.sql b/src/test/modules/test_dsa/sql/test_dsa.sql
new file mode 100644
index 0000000000..3ce5abbeb5
--- /dev/null
+++ b/src/test/modules/test_dsa/sql/test_dsa.sql
@@ -0,0 +1,13 @@
+CREATE EXTENSION test_dsa;
+
+SELECT test_dsa_random(3, 5, 16, 10240, 'random');
+SELECT test_dsa_random(3, 5, 16, 10240, 'forwards');
+SELECT test_dsa_random(3, 5, 16, 10240, 'backwards');
+
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'random', 5);
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'forwards', 5);
+SELECT count(*) from test_dsa_random_parallel(3, 5, 16, 10240, 'backwards', 5);
+
+SELECT test_dsa_oom(1024);
+SELECT test_dsa_oom(8192);
+SELECT test_dsa_oom(10240);
\ No newline at end of file
diff --git a/src/test/modules/test_dsa/test_dsa--1.0.sql b/src/test/modules/test_dsa/test_dsa--1.0.sql
new file mode 100644
index 0000000000..ab575ff66e
--- /dev/null
+++ b/src/test/modules/test_dsa/test_dsa--1.0.sql
@@ -0,0 +1,21 @@
+/* src/test/modules/test_dsa/test_dsa--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_dsa" to load this file. \quit
+
+CREATE FUNCTION test_dsa_random(loops int, num_allocs int, min_alloc int, max_alloc int, mode text)
+RETURNS VOID
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE TYPE test_dsa_row AS (pid int, allocations bigint, elapsed interval);
+
+CREATE FUNCTION test_dsa_random_parallel(loops int, num_allocs int, min_alloc int, max_alloc int, mode text, workers int)
+RETURNS SETOF test_dsa_row
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE FUNCTION test_dsa_oom(alloc_size int)
+RETURNS VOID
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
\ No newline at end of file
diff --git a/src/test/modules/test_dsa/test_dsa.c b/src/test/modules/test_dsa/test_dsa.c
new file mode 100644
index 0000000000..071243a4db
--- /dev/null
+++ b/src/test/modules/test_dsa/test_dsa.c
@@ -0,0 +1,407 @@
+/* -------------------------------------------------------------------------
+ *
+ * test_dsa.c
+ * Simple exercises for dsa.c.
+ *
+ * Copyright (C) 2016-2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_dsa/test_dsa.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/proc.h"
+#include "utils/builtins.h"
+#include "utils/dsa.h"
+#include "utils/resowner.h"
+#include "utils/timestamp.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_dsa_random);
+PG_FUNCTION_INFO_V1(test_dsa_random_parallel);
+PG_FUNCTION_INFO_V1(test_dsa_oom);
+
+PGDLLEXPORT void test_dsa_random_worker_main(Datum arg);
+
+/* Which order to free objects in, within each loop. */
+typedef enum
+{
+ /* Free in random order. */
+ MODE_RANDOM,
+ /* Free in the same order we allocated (FIFO). */
+ MODE_FORWARDS,
+ /* Free in reverse order of allocation (LIFO). */
+ MODE_BACKWARDS
+} test_mode;
+
+/* Per-worker results. */
+typedef struct
+{
+ pid_t pid;
+ int64 count;
+ TimeOffset elapsed_time;
+} test_result;
+
+/* Parameters for a test run, passed to workers. */
+typedef struct
+{
+ int loops;
+ int num_allocs;
+ int min_alloc;
+ int max_alloc;
+ test_mode mode;
+ test_result results[FLEXIBLE_ARRAY_MEMBER];
+} test_parameters;
+
+/* The startup message given to each worker. */
+typedef struct
+{
+ /* How to connect to the shmem area. */
+ dsa_handle area_handle;
+ /* Where to find the parameters. */
+ dsa_pointer parameters;
+ /* What index this worker should write results to. */
+ size_t output_index;
+} test_hello;
+
+static test_mode
+parse_test_mode(text *mode)
+{
+ test_mode result = MODE_RANDOM;
+ char *cstr = text_to_cstring(mode);
+
+ if (strcmp(cstr, "random") == 0)
+ result = MODE_RANDOM;
+ else if (strcmp(cstr, "forwards") == 0)
+ result = MODE_FORWARDS;
+ else if (strcmp(cstr, "backwards") == 0)
+ result = MODE_BACKWARDS;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unknown mode")));
+ return result;
+}
+
+static void
+check_parameters(const test_parameters *parameters)
+{
+ if (parameters->min_alloc < 1 || parameters->min_alloc > parameters->max_alloc)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("min_alloc must be >= 1, and min_alloc must be <= max_alloc")));
+}
+
+static int
+my_tranche_id(void)
+{
+ static int tranche_id = 0;
+
+ if (tranche_id == 0)
+ tranche_id = LWLockNewTrancheId();
+
+ return tranche_id;
+}
+
+static void
+do_random_test(dsa_area *area, size_t output_index, test_parameters *parameters)
+{
+ dsa_pointer *objects;
+ int min_alloc;
+ int extra_alloc;
+ int32 i;
+ int32 loop;
+ TimestampTz start_time = GetCurrentTimestamp();
+ int64 total_allocations = 0;
+
+ /*
+ * Make tests reproducible (on the same computer at least) by using the
+ * same random sequence every time.
+ */
+ srand(42);
+
+ min_alloc = parameters->min_alloc;
+ extra_alloc = parameters->max_alloc - parameters->min_alloc;
+
+ objects = palloc(sizeof(dsa_pointer) * parameters->num_allocs);
+ Assert(objects != NULL);
+ for (loop = 0; loop < parameters->loops; ++loop)
+ {
+ int num_actually_allocated = 0;
+
+ for (i = 0; i < parameters->num_allocs; ++i)
+ {
+ size_t size;
+ void *memory;
+
+ /* Adjust size randomly if needed. */
+ size = min_alloc;
+ if (extra_alloc > 0)
+ size += rand() % extra_alloc;
+
+ /* Allocate! */
+ objects[i] = dsa_allocate_extended(area, size, DSA_ALLOC_NO_OOM);
+ if (!DsaPointerIsValid(objects[i]))
+ {
+ elog(LOG, "dsa: loop %d: out of memory after allocating %d objects", loop, i + 1);
+ break;
+ }
+ ++num_actually_allocated;
+ /* Pay the cost of accessing that memory */
+ memory = dsa_get_address(area, objects[i]);
+ memset(memory, 42, size);
+ }
+ if (parameters->mode == MODE_RANDOM)
+ {
+ for (i = 0; i < num_actually_allocated; ++i)
+ {
+ size_t x = rand() % num_actually_allocated;
+ size_t y = rand() % num_actually_allocated;
+ dsa_pointer temp = objects[x];
+
+ objects[x] = objects[y];
+ objects[y] = temp;
+ }
+ }
+ if (parameters->mode == MODE_BACKWARDS)
+ {
+ for (i = num_actually_allocated - 1; i >= 0; --i)
+ dsa_free(area, objects[i]);
+ }
+ else
+ {
+ for (i = 0; i < num_actually_allocated; ++i)
+ dsa_free(area, objects[i]);
+ }
+ total_allocations += num_actually_allocated;
+ }
+ pfree(objects);
+
+ parameters->results[output_index].elapsed_time = GetCurrentTimestamp() - start_time;
+ parameters->results[output_index].pid = MyProcPid;
+ parameters->results[output_index].count = total_allocations;
+}
+
+/* Non-parallel version: just do it. */
+Datum
+test_dsa_random(PG_FUNCTION_ARGS)
+{
+ test_parameters *parameters;
+ dsa_area *area;
+
+ parameters =
+ palloc(offsetof(test_parameters, results) + sizeof(test_result));
+ parameters->loops = PG_GETARG_INT32(0);
+ parameters->num_allocs = PG_GETARG_INT32(1);
+ parameters->min_alloc = PG_GETARG_INT32(2);
+ parameters->max_alloc = PG_GETARG_INT32(3);
+ parameters->mode = parse_test_mode(PG_GETARG_TEXT_PP(4));
+ check_parameters(parameters);
+
+ area = dsa_create(my_tranche_id());
+ do_random_test(area, 0, parameters);
+ dsa_dump(area);
+ dsa_detach(area);
+
+ pfree(parameters);
+
+ PG_RETURN_NULL();
+}
+
+void
+test_dsa_random_worker_main(Datum arg)
+{
+ test_hello hello;
+ dsa_area *area;
+ test_parameters *parameters;
+
+ CurrentResourceOwner = ResourceOwnerCreate(NULL, "test_dsa toplevel");
+
+ /* Receive hello message and attach to shmem area. */
+ memcpy(&hello, MyBgworkerEntry->bgw_extra, sizeof(hello));
+ area = dsa_attach(hello.area_handle);
+ Assert(area != NULL);
+ parameters = dsa_get_address(area, hello.parameters);
+ Assert(parameters != NULL);
+
+ do_random_test(area, hello.output_index, parameters);
+
+ dsa_detach(area);
+}
+
+/* Parallel version: fork a bunch of background workers to do it. */
+Datum
+test_dsa_random_parallel(PG_FUNCTION_ARGS)
+{
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+
+ test_hello hello;
+ test_parameters *parameters;
+ dsa_area *area;
+ int workers;
+ int i;
+ BackgroundWorkerHandle **handles;
+
+ /* tuplestore boilerplate stuff... */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Prepare to work! */
+ workers = PG_GETARG_INT32(5);
+ handles = palloc(sizeof(BackgroundWorkerHandle *) * workers);
+
+ /* Set up the shared memory area. */
+ area = dsa_create(my_tranche_id());
+
+ /* The workers then will attach to it. */
+ hello.area_handle = dsa_get_handle(area);
+
+ /* Allocate space for the parameters object. */
+ hello.parameters = dsa_allocate(area,
+ offsetof(test_parameters, results) +
+ sizeof(test_result) * workers);
+ Assert(DsaPointerIsValid(hello.parameters));
+
+ /* Set up the parameters object. */
+ parameters = dsa_get_address(area, hello.parameters);
+ parameters->loops = PG_GETARG_INT32(0);
+ parameters->num_allocs = PG_GETARG_INT32(1);
+ parameters->min_alloc = PG_GETARG_INT32(2);
+ parameters->max_alloc = PG_GETARG_INT32(3);
+ parameters->mode = parse_test_mode(PG_GETARG_TEXT_PP(4));
+ check_parameters(parameters);
+
+ /* Start the workers. */
+ for (i = 0; i < workers; ++i)
+ {
+ BackgroundWorker bgw;
+
+ memset(&bgw, 0, sizeof(bgw));
+ snprintf(bgw.bgw_name, sizeof(bgw.bgw_name), "worker%d", i);
+ bgw.bgw_flags = BGWORKER_SHMEM_ACCESS;
+ bgw.bgw_start_time = BgWorkerStart_PostmasterStart;
+ bgw.bgw_restart_time = BGW_NEVER_RESTART;
+ snprintf(bgw.bgw_library_name, sizeof(bgw.bgw_library_name),
+ "test_dsa");
+ snprintf(bgw.bgw_function_name, sizeof(bgw.bgw_function_name),
+ "test_dsa_random_worker_main");
+ Assert(sizeof(parameters) <= BGW_EXTRALEN);
+ /* Each worker will write its output to a different slot. */
+ hello.output_index = i;
+ memcpy(bgw.bgw_extra, &hello, sizeof(hello));
+ bgw.bgw_notify_pid = MyProcPid;
+
+ if (!RegisterDynamicBackgroundWorker(&bgw, &handles[i]))
+ elog(ERROR, "can't start worker");
+ }
+
+ /* Wait for the workers to complete. */
+ for (i = 0; i < workers; ++i)
+ {
+ BgwHandleStatus status;
+
+ status = WaitForBackgroundWorkerShutdown(handles[i]);
+ if (status == BGWH_POSTMASTER_DIED)
+ proc_exit(1);
+ Assert(status == BGWH_STOPPED);
+ }
+
+ /* Generate result tuples. */
+ for (i = 0; i < workers; ++i)
+ {
+ Datum values[3];
+ bool nulls[] = {false, false, false};
+ Interval *interval = palloc(sizeof(Interval));
+
+ interval->month = 0;
+ interval->day = 0;
+ interval->time = parameters->results[i].elapsed_time;
+
+ values[0] = Int32GetDatum(parameters->results[i].pid);
+ values[1] = Int64GetDatum(parameters->results[i].count);
+ values[2] = PointerGetDatum(interval);
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ }
+ tuplestore_donestoring(tupstore);
+
+ pfree(handles);
+ dsa_detach(area);
+
+ return (Datum) 0;
+}
+
+/* Allocate memory until OOM, than free and try allocate again. */
+Datum
+test_dsa_oom(PG_FUNCTION_ARGS)
+{
+ test_parameters *parameters;
+ dsa_area *area;
+ int64 cnt1,
+ cnt2;
+
+ parameters =
+ palloc(offsetof(test_parameters, results) + sizeof(test_result));
+
+ parameters->loops = 1;
+ parameters->min_alloc = PG_GETARG_INT32(0);
+ parameters->max_alloc = parameters->min_alloc;
+ check_parameters(parameters);
+
+ parameters->num_allocs = 1024 * 1024 / parameters->min_alloc;
+ parameters->mode = MODE_RANDOM;
+
+ /* Cap available memory at 1MB. */
+ area = dsa_create(my_tranche_id());
+ dsa_set_size_limit(area, 1024 * 1024);
+ dsa_dump(area);
+
+ do_random_test(area, 0, parameters);
+ dsa_dump(area);
+ cnt1 = parameters->results[0].count;
+
+ /* And again... */
+ do_random_test(area, 0, parameters);
+ dsa_dump(area);
+ cnt2 = parameters->results[0].count;
+
+ dsa_detach(area);
+ pfree(parameters);
+
+ /* We should have allocated the same amount both times. */
+ Assert(cnt1 == cnt2);
+
+ PG_RETURN_NULL();
+}
diff --git a/src/test/modules/test_dsa/test_dsa.control b/src/test/modules/test_dsa/test_dsa.control
new file mode 100644
index 0000000000..2655c3fccd
--- /dev/null
+++ b/src/test/modules/test_dsa/test_dsa.control
@@ -0,0 +1,5 @@
+# dsa_test extension
+comment = 'Tests for DSA'
+default_version = '1.0'
+module_pathname = '$libdir/test_dsa'
+relocatable = true
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 22ea42c16b..c3d567e0e2 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3727,8 +3727,12 @@ symbol
tablespaceinfo
teSection
temp_tablespaces_extra
+test_hello
+test_mode
+test_parameters
test_re_flags
test_regex_ctx
+test_result
test_shm_mq_header
test_spec
test_start_function
--
2.39.1