LCOV - code coverage report
Current view: top level - src/backend/utils/adt - ddlutils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 86.1 % 416 358
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * ddlutils.c
       4              :  *      Utility functions for generating DDL statements
       5              :  *
       6              :  * This file contains the pg_get_*_ddl family of functions that generate
       7              :  * DDL statements to recreate database objects such as roles, tablespaces,
       8              :  * and databases, along with common infrastructure for option parsing and
       9              :  * pretty-printing.
      10              :  *
      11              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      12              :  * Portions Copyright (c) 1994, Regents of the University of California
      13              :  *
      14              :  * IDENTIFICATION
      15              :  *    src/backend/utils/adt/ddlutils.c
      16              :  *
      17              :  *-------------------------------------------------------------------------
      18              :  */
      19              : #include "postgres.h"
      20              : 
      21              : #include "access/genam.h"
      22              : #include "access/htup_details.h"
      23              : #include "access/table.h"
      24              : #include "catalog/pg_auth_members.h"
      25              : #include "catalog/pg_authid.h"
      26              : #include "catalog/pg_collation.h"
      27              : #include "catalog/pg_database.h"
      28              : #include "catalog/pg_db_role_setting.h"
      29              : #include "catalog/pg_tablespace.h"
      30              : #include "commands/tablespace.h"
      31              : #include "common/relpath.h"
      32              : #include "funcapi.h"
      33              : #include "mb/pg_wchar.h"
      34              : #include "miscadmin.h"
      35              : #include "utils/acl.h"
      36              : #include "utils/array.h"
      37              : #include "utils/builtins.h"
      38              : #include "utils/datetime.h"
      39              : #include "utils/fmgroids.h"
      40              : #include "utils/guc.h"
      41              : #include "utils/lsyscache.h"
      42              : #include "utils/pg_locale.h"
      43              : #include "utils/rel.h"
      44              : #include "utils/ruleutils.h"
      45              : #include "utils/syscache.h"
      46              : #include "utils/timestamp.h"
      47              : #include "utils/varlena.h"
      48              : 
      49              : /* Option value types for DDL option parsing */
      50              : typedef enum
      51              : {
      52              :     DDL_OPT_BOOL,
      53              :     DDL_OPT_TEXT,
      54              :     DDL_OPT_INT,
      55              : } DdlOptType;
      56              : 
      57              : /*
      58              :  * A single DDL option descriptor: caller fills in name and type,
      59              :  * parse_ddl_options fills in isset + the appropriate value field.
      60              :  */
      61              : typedef struct DdlOption
      62              : {
      63              :     const char *name;           /* option name (case-insensitive match) */
      64              :     DdlOptType  type;           /* expected value type */
      65              :     bool        isset;          /* true if caller supplied this option */
      66              :     /* fields for specific option types */
      67              :     union
      68              :     {
      69              :         bool        boolval;    /* filled in for DDL_OPT_BOOL */
      70              :         char       *textval;    /* filled in for DDL_OPT_TEXT (palloc'd) */
      71              :         int         intval;     /* filled in for DDL_OPT_INT */
      72              :     };
      73              : } DdlOption;
      74              : 
      75              : 
      76              : static void parse_ddl_options(FunctionCallInfo fcinfo, int variadic_start,
      77              :                               DdlOption *opts, int nopts);
      78              : static void append_ddl_option(StringInfo buf, bool pretty, int indent,
      79              :                               const char *fmt,...)
      80              :             pg_attribute_printf(4, 5);
      81              : static void append_guc_value(StringInfo buf, const char *name,
      82              :                              const char *value);
      83              : static List *pg_get_role_ddl_internal(Oid roleid, bool pretty,
      84              :                                       bool memberships);
      85              : static List *pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner);
      86              : static Datum pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull);
      87              : static List *pg_get_database_ddl_internal(Oid dbid, bool pretty,
      88              :                                           bool no_owner, bool no_tablespace);
      89              : 
      90              : 
      91              : /*
      92              :  * parse_ddl_options
      93              :  *      Parse variadic name/value option pairs
      94              :  *
      95              :  * Options are passed as alternating key/value text pairs.  The caller
      96              :  * provides an array of DdlOption descriptors specifying the accepted
      97              :  * option names and their types; this function matches each supplied
      98              :  * pair against the array, validates the value, and fills in the
      99              :  * result fields.
     100              :  */
     101              : static void
     102           96 : parse_ddl_options(FunctionCallInfo fcinfo, int variadic_start,
     103              :                   DdlOption *opts, int nopts)
     104              : {
     105              :     Datum      *args;
     106              :     bool       *nulls;
     107              :     Oid        *types;
     108              :     int         nargs;
     109              : 
     110              :     /* Clear all output fields */
     111          312 :     for (int i = 0; i < nopts; i++)
     112              :     {
     113          216 :         opts[i].isset = false;
     114          216 :         switch (opts[i].type)
     115              :         {
     116          216 :             case DDL_OPT_BOOL:
     117          216 :                 opts[i].boolval = false;
     118          216 :                 break;
     119            0 :             case DDL_OPT_TEXT:
     120            0 :                 opts[i].textval = NULL;
     121            0 :                 break;
     122            0 :             case DDL_OPT_INT:
     123            0 :                 opts[i].intval = 0;
     124            0 :                 break;
     125              :         }
     126              :     }
     127              : 
     128           96 :     nargs = extract_variadic_args(fcinfo, variadic_start, true,
     129              :                                   &args, &types, &nulls);
     130              : 
     131           96 :     if (nargs <= 0)
     132           64 :         return;
     133              : 
     134              :     /* Handle DEFAULT NULL case */
     135           96 :     if (nargs == 1 && nulls[0])
     136           64 :         return;
     137              : 
     138           32 :     if (nargs % 2 != 0)
     139            0 :         ereport(ERROR,
     140              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     141              :                  errmsg("variadic arguments must be name/value pairs"),
     142              :                  errhint("Provide an even number of variadic arguments that can be divided into pairs.")));
     143              : 
     144              :     /*
     145              :      * For each option name/value pair, find corresponding positional option
     146              :      * for the option name, and assign the option value.
     147              :      */
     148           64 :     for (int i = 0; i < nargs; i += 2)
     149              :     {
     150              :         char       *name;
     151              :         char       *valstr;
     152           40 :         DdlOption  *opt = NULL;
     153              : 
     154           40 :         if (nulls[i])
     155            0 :             ereport(ERROR,
     156              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     157              :                      errmsg("option name at variadic position %d is null", i + 1)));
     158              : 
     159           40 :         name = TextDatumGetCString(args[i]);
     160              : 
     161           40 :         if (nulls[i + 1])
     162            0 :             ereport(ERROR,
     163              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     164              :                      errmsg("value for option \"%s\" must not be null", name)));
     165              : 
     166              :         /* Find matching option descriptor */
     167           72 :         for (int j = 0; j < nopts; j++)
     168              :         {
     169           72 :             if (pg_strcasecmp(name, opts[j].name) == 0)
     170              :             {
     171           40 :                 opt = &opts[j];
     172           40 :                 break;
     173              :             }
     174              :         }
     175              : 
     176           40 :         if (opt == NULL)
     177            0 :             ereport(ERROR,
     178              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     179              :                      errmsg("unrecognized option: \"%s\"", name)));
     180              : 
     181           40 :         if (opt->isset)
     182            4 :             ereport(ERROR,
     183              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     184              :                      errmsg("option \"%s\" is specified more than once",
     185              :                             name)));
     186              : 
     187           36 :         valstr = TextDatumGetCString(args[i + 1]);
     188              : 
     189           36 :         switch (opt->type)
     190              :         {
     191           36 :             case DDL_OPT_BOOL:
     192           36 :                 if (!parse_bool(valstr, &opt->boolval))
     193            4 :                     ereport(ERROR,
     194              :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     195              :                              errmsg("invalid value for boolean option \"%s\": %s",
     196              :                                     name, valstr)));
     197           32 :                 break;
     198              : 
     199            0 :             case DDL_OPT_TEXT:
     200            0 :                 opt->textval = valstr;
     201            0 :                 valstr = NULL;  /* don't pfree below */
     202            0 :                 break;
     203              : 
     204            0 :             case DDL_OPT_INT:
     205              :                 {
     206              :                     char       *endp;
     207              :                     long        val;
     208              : 
     209            0 :                     errno = 0;
     210            0 :                     val = strtol(valstr, &endp, 10);
     211            0 :                     if (*endp != '\0' || errno == ERANGE ||
     212            0 :                         val < PG_INT32_MIN || val > PG_INT32_MAX)
     213            0 :                         ereport(ERROR,
     214              :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     215              :                                  errmsg("invalid value for integer option \"%s\": %s",
     216              :                                         name, valstr)));
     217            0 :                     opt->intval = (int) val;
     218              :                 }
     219            0 :                 break;
     220              :         }
     221              : 
     222           32 :         opt->isset = true;
     223              : 
     224           32 :         if (valstr)
     225           32 :             pfree(valstr);
     226           32 :         pfree(name);
     227              :     }
     228              : }
     229              : 
     230              : /*
     231              :  * Helper to append a formatted string with optional pretty-printing.
     232              :  */
     233              : static void
     234          355 : append_ddl_option(StringInfo buf, bool pretty, int indent,
     235              :                   const char *fmt,...)
     236              : {
     237          355 :     if (pretty)
     238              :     {
     239           61 :         appendStringInfoChar(buf, '\n');
     240           61 :         appendStringInfoSpaces(buf, indent);
     241              :     }
     242              :     else
     243          294 :         appendStringInfoChar(buf, ' ');
     244              : 
     245              :     for (;;)
     246            0 :     {
     247              :         va_list     args;
     248              :         int         needed;
     249              : 
     250          355 :         va_start(args, fmt);
     251          355 :         needed = appendStringInfoVA(buf, fmt, args);
     252          355 :         va_end(args);
     253          355 :         if (needed == 0)
     254          355 :             break;
     255            0 :         enlargeStringInfo(buf, needed);
     256              :     }
     257          355 : }
     258              : 
     259              : /*
     260              :  * append_guc_value
     261              :  *      Append a GUC setting value to buf, handling GUC_LIST_QUOTE properly.
     262              :  *
     263              :  * Variables marked GUC_LIST_QUOTE were already fully quoted before they
     264              :  * were stored in the setconfig array.  We break the list value apart
     265              :  * and re-quote the elements as string literals.  For all other variables
     266              :  * we simply quote the value as a single string literal.
     267              :  *
     268              :  * The caller has already appended "SET <name> TO " to buf.
     269              :  */
     270              : static void
     271           24 : append_guc_value(StringInfo buf, const char *name, const char *value)
     272              : {
     273              :     char       *rawval;
     274              : 
     275           24 :     rawval = pstrdup(value);
     276              : 
     277           24 :     if (GetConfigOptionFlags(name, true) & GUC_LIST_QUOTE)
     278              :     {
     279              :         List       *namelist;
     280            4 :         bool        first = true;
     281              : 
     282              :         /* Parse string into list of identifiers */
     283            4 :         if (!SplitGUCList(rawval, ',', &namelist))
     284              :         {
     285              :             /* this shouldn't fail really */
     286            0 :             elog(ERROR, "invalid list syntax in setconfig item");
     287              :         }
     288              :         /* Special case: represent an empty list as NULL */
     289            4 :         if (namelist == NIL)
     290            0 :             appendStringInfoString(buf, "NULL");
     291           16 :         foreach_ptr(char, curname, namelist)
     292              :         {
     293            8 :             if (first)
     294            4 :                 first = false;
     295              :             else
     296            4 :                 appendStringInfoString(buf, ", ");
     297            8 :             appendStringInfoString(buf, quote_literal_cstr(curname));
     298              :         }
     299            4 :         list_free(namelist);
     300              :     }
     301              :     else
     302           20 :         appendStringInfoString(buf, quote_literal_cstr(rawval));
     303              : 
     304           24 :     pfree(rawval);
     305           24 : }
     306              : 
     307              : /*
     308              :  * pg_get_role_ddl_internal
     309              :  *      Generate DDL statements to recreate a role
     310              :  *
     311              :  * Returns a List of palloc'd strings, each being a complete SQL statement.
     312              :  * The first list element is always the CREATE ROLE statement; subsequent
     313              :  * elements are ALTER ROLE SET statements for any role-specific or
     314              :  * role-in-database configuration settings.  If memberships is true,
     315              :  * GRANT statements for role memberships are appended.
     316              :  */
     317              : static List *
     318           44 : pg_get_role_ddl_internal(Oid roleid, bool pretty, bool memberships)
     319              : {
     320              :     HeapTuple   tuple;
     321              :     Form_pg_authid roleform;
     322              :     StringInfoData buf;
     323              :     char       *rolname;
     324              :     Datum       rolevaliduntil;
     325              :     bool        isnull;
     326              :     Relation    rel;
     327              :     ScanKeyData scankey;
     328              :     SysScanDesc scan;
     329           44 :     List       *statements = NIL;
     330              : 
     331           44 :     tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
     332           44 :     if (!HeapTupleIsValid(tuple))
     333            4 :         ereport(ERROR,
     334              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     335              :                  errmsg("role with OID %u does not exist", roleid)));
     336              : 
     337           40 :     roleform = (Form_pg_authid) GETSTRUCT(tuple);
     338           40 :     rolname = pstrdup(NameStr(roleform->rolname));
     339              : 
     340              :     /* User must have SELECT privilege on pg_authid. */
     341           40 :     if (pg_class_aclcheck(AuthIdRelationId, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
     342              :     {
     343            4 :         ReleaseSysCache(tuple);
     344            4 :         ereport(ERROR,
     345              :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     346              :                  errmsg("permission denied for role %s", rolname)));
     347              :     }
     348              : 
     349              :     /*
     350              :      * We don't support generating DDL for system roles.  The primary reason
     351              :      * for this is that users shouldn't be recreating them.
     352              :      */
     353           36 :     if (IsReservedName(rolname))
     354            0 :         ereport(ERROR,
     355              :                 (errcode(ERRCODE_RESERVED_NAME),
     356              :                  errmsg("role name \"%s\" is reserved", rolname),
     357              :                  errdetail("Role names starting with \"pg_\" are reserved for system roles.")));
     358              : 
     359           36 :     initStringInfo(&buf);
     360           36 :     appendStringInfo(&buf, "CREATE ROLE %s", quote_identifier(rolname));
     361              : 
     362              :     /*
     363              :      * Append role attributes.  The order here follows the same sequence as
     364              :      * you'd typically write them in a CREATE ROLE command, though any order
     365              :      * is actually acceptable to the parser.
     366              :      */
     367           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     368           36 :                       roleform->rolsuper ? "SUPERUSER" : "NOSUPERUSER");
     369              : 
     370           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     371           36 :                       roleform->rolinherit ? "INHERIT" : "NOINHERIT");
     372              : 
     373           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     374           36 :                       roleform->rolcreaterole ? "CREATEROLE" : "NOCREATEROLE");
     375              : 
     376           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     377           36 :                       roleform->rolcreatedb ? "CREATEDB" : "NOCREATEDB");
     378              : 
     379           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     380           36 :                       roleform->rolcanlogin ? "LOGIN" : "NOLOGIN");
     381              : 
     382           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     383           36 :                       roleform->rolreplication ? "REPLICATION" : "NOREPLICATION");
     384              : 
     385           36 :     append_ddl_option(&buf, pretty, 4, "%s",
     386           36 :                       roleform->rolbypassrls ? "BYPASSRLS" : "NOBYPASSRLS");
     387              : 
     388              :     /*
     389              :      * CONNECTION LIMIT is only interesting if it's not -1 (the default,
     390              :      * meaning no limit).
     391              :      */
     392           36 :     if (roleform->rolconnlimit >= 0)
     393            8 :         append_ddl_option(&buf, pretty, 4, "CONNECTION LIMIT %d",
     394              :                           roleform->rolconnlimit);
     395              : 
     396           36 :     rolevaliduntil = SysCacheGetAttr(AUTHOID, tuple,
     397              :                                      Anum_pg_authid_rolvaliduntil,
     398              :                                      &isnull);
     399           36 :     if (!isnull)
     400              :     {
     401              :         TimestampTz ts;
     402              :         int         tz;
     403              :         struct pg_tm tm;
     404              :         fsec_t      fsec;
     405              :         const char *tzn;
     406              :         char        ts_str[MAXDATELEN + 1];
     407              : 
     408            8 :         ts = DatumGetTimestampTz(rolevaliduntil);
     409            8 :         if (TIMESTAMP_NOT_FINITE(ts))
     410            0 :             EncodeSpecialTimestamp(ts, ts_str);
     411            8 :         else if (timestamp2tm(ts, &tz, &tm, &fsec, &tzn, NULL) == 0)
     412            8 :             EncodeDateTime(&tm, fsec, true, tz, tzn, USE_ISO_DATES, ts_str);
     413              :         else
     414            0 :             ereport(ERROR,
     415              :                     (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     416              :                      errmsg("timestamp out of range")));
     417              : 
     418            8 :         append_ddl_option(&buf, pretty, 4, "VALID UNTIL %s",
     419              :                           quote_literal_cstr(ts_str));
     420              :     }
     421              : 
     422           36 :     ReleaseSysCache(tuple);
     423              : 
     424              :     /*
     425              :      * We intentionally omit PASSWORD.  There's no way to retrieve the
     426              :      * original password text from the stored hash, and even if we could,
     427              :      * exposing passwords through a SQL function would be a security issue.
     428              :      * Users must set passwords separately after recreating roles.
     429              :      */
     430              : 
     431           36 :     appendStringInfoChar(&buf, ';');
     432              : 
     433           36 :     statements = lappend(statements, pstrdup(buf.data));
     434              : 
     435              :     /*
     436              :      * Now scan pg_db_role_setting for ALTER ROLE SET configurations.
     437              :      *
     438              :      * These can be role-wide (setdatabase = 0) or specific to a particular
     439              :      * database (setdatabase = a valid DB OID).  It generates one ALTER
     440              :      * statement per setting.
     441              :      */
     442           36 :     rel = table_open(DbRoleSettingRelationId, AccessShareLock);
     443           36 :     ScanKeyInit(&scankey,
     444              :                 Anum_pg_db_role_setting_setrole,
     445              :                 BTEqualStrategyNumber, F_OIDEQ,
     446              :                 ObjectIdGetDatum(roleid));
     447           36 :     scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true,
     448              :                               NULL, 1, &scankey);
     449              : 
     450           44 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     451              :     {
     452            8 :         Form_pg_db_role_setting setting = (Form_pg_db_role_setting) GETSTRUCT(tuple);
     453            8 :         Oid         datid = setting->setdatabase;
     454              :         Datum       datum;
     455              :         ArrayType  *role_settings;
     456              :         Datum      *settings;
     457              :         bool       *nulls;
     458              :         int         nsettings;
     459            8 :         char       *datname = NULL;
     460              : 
     461              :         /*
     462              :          * If setdatabase is valid, this is a role-in-database setting;
     463              :          * otherwise it's a role-wide setting.  Look up the database name once
     464              :          * for all settings in this row.
     465              :          */
     466            8 :         if (OidIsValid(datid))
     467              :         {
     468            4 :             datname = get_database_name(datid);
     469              :             /* Database has been dropped; skip all settings in this row. */
     470            4 :             if (datname == NULL)
     471            0 :                 continue;
     472              :         }
     473              : 
     474              :         /*
     475              :          * The setconfig column is a text array in "name=value" format. It
     476              :          * should never be null for a valid row, but be defensive.
     477              :          */
     478            8 :         datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
     479              :                              RelationGetDescr(rel), &isnull);
     480            8 :         if (isnull)
     481            0 :             continue;
     482              : 
     483            8 :         role_settings = DatumGetArrayTypeP(datum);
     484              : 
     485            8 :         deconstruct_array_builtin(role_settings, TEXTOID, &settings, &nulls, &nsettings);
     486              : 
     487           20 :         for (int i = 0; i < nsettings; i++)
     488              :         {
     489              :             char       *s,
     490              :                        *p;
     491              : 
     492           12 :             if (nulls[i])
     493            0 :                 continue;
     494              : 
     495           12 :             s = TextDatumGetCString(settings[i]);
     496           12 :             p = strchr(s, '=');
     497           12 :             if (p == NULL)
     498              :             {
     499            0 :                 pfree(s);
     500            0 :                 continue;
     501              :             }
     502           12 :             *p++ = '\0';
     503              : 
     504              :             /* Build a fresh ALTER ROLE statement for this setting */
     505           12 :             resetStringInfo(&buf);
     506           12 :             appendStringInfo(&buf, "ALTER ROLE %s", quote_identifier(rolname));
     507              : 
     508           12 :             if (datname != NULL)
     509            4 :                 appendStringInfo(&buf, " IN DATABASE %s",
     510              :                                  quote_identifier(datname));
     511              : 
     512           12 :             appendStringInfo(&buf, " SET %s TO ",
     513              :                              quote_identifier(s));
     514              : 
     515           12 :             append_guc_value(&buf, s, p);
     516              : 
     517           12 :             appendStringInfoChar(&buf, ';');
     518              : 
     519           12 :             statements = lappend(statements, pstrdup(buf.data));
     520              : 
     521           12 :             pfree(s);
     522              :         }
     523              : 
     524            8 :         pfree(settings);
     525            8 :         pfree(nulls);
     526            8 :         pfree(role_settings);
     527              : 
     528            8 :         if (datname != NULL)
     529            4 :             pfree(datname);
     530              :     }
     531              : 
     532           36 :     systable_endscan(scan);
     533           36 :     table_close(rel, AccessShareLock);
     534              : 
     535              :     /*
     536              :      * Scan pg_auth_members for role memberships.  We look for rows where
     537              :      * member = roleid, meaning this role has been granted membership in other
     538              :      * roles.
     539              :      */
     540           36 :     if (memberships)
     541              :     {
     542           32 :         rel = table_open(AuthMemRelationId, AccessShareLock);
     543           32 :         ScanKeyInit(&scankey,
     544              :                     Anum_pg_auth_members_member,
     545              :                     BTEqualStrategyNumber, F_OIDEQ,
     546              :                     ObjectIdGetDatum(roleid));
     547           32 :         scan = systable_beginscan(rel, AuthMemMemRoleIndexId, true,
     548              :                                   NULL, 1, &scankey);
     549              : 
     550           40 :         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
     551              :         {
     552            8 :             Form_pg_auth_members memform = (Form_pg_auth_members) GETSTRUCT(tuple);
     553              :             char       *granted_role;
     554              :             char       *grantor;
     555              : 
     556            8 :             granted_role = GetUserNameFromId(memform->roleid, false);
     557            8 :             grantor = GetUserNameFromId(memform->grantor, false);
     558              : 
     559            8 :             resetStringInfo(&buf);
     560            8 :             appendStringInfo(&buf, "GRANT %s TO %s",
     561              :                              quote_identifier(granted_role),
     562              :                              quote_identifier(rolname));
     563           24 :             appendStringInfo(&buf, " WITH ADMIN %s, INHERIT %s, SET %s",
     564            8 :                              memform->admin_option ? "TRUE" : "FALSE",
     565            8 :                              memform->inherit_option ? "TRUE" : "FALSE",
     566            8 :                              memform->set_option ? "TRUE" : "FALSE");
     567            8 :             appendStringInfo(&buf, " GRANTED BY %s;",
     568              :                              quote_identifier(grantor));
     569              : 
     570            8 :             statements = lappend(statements, pstrdup(buf.data));
     571              : 
     572            8 :             pfree(granted_role);
     573            8 :             pfree(grantor);
     574              :         }
     575              : 
     576           32 :         systable_endscan(scan);
     577           32 :         table_close(rel, AccessShareLock);
     578              :     }
     579              : 
     580           36 :     pfree(buf.data);
     581           36 :     pfree(rolname);
     582              : 
     583           36 :     return statements;
     584              : }
     585              : 
     586              : /*
     587              :  * pg_get_role_ddl
     588              :  *      Return DDL to recreate a role as a set of text rows.
     589              :  *
     590              :  * Each row is a complete SQL statement.  The first row is always the
     591              :  * CREATE ROLE statement; subsequent rows are ALTER ROLE SET statements
     592              :  * and optionally GRANT statements for role memberships.
     593              :  * Returns no rows if the role argument is NULL.
     594              :  */
     595              : Datum
     596          104 : pg_get_role_ddl(PG_FUNCTION_ARGS)
     597              : {
     598              :     FuncCallContext *funcctx;
     599              :     List       *statements;
     600              : 
     601          104 :     if (SRF_IS_FIRSTCALL())
     602              :     {
     603              :         MemoryContext oldcontext;
     604              :         Oid         roleid;
     605           48 :         DdlOption   opts[] = {
     606              :             {"pretty", DDL_OPT_BOOL},
     607              :             {"memberships", DDL_OPT_BOOL},
     608              :         };
     609              : 
     610           48 :         funcctx = SRF_FIRSTCALL_INIT();
     611           48 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     612              : 
     613           48 :         if (PG_ARGISNULL(0))
     614              :         {
     615            4 :             MemoryContextSwitchTo(oldcontext);
     616            4 :             SRF_RETURN_DONE(funcctx);
     617              :         }
     618              : 
     619           44 :         roleid = PG_GETARG_OID(0);
     620           44 :         parse_ddl_options(fcinfo, 1, opts, lengthof(opts));
     621              : 
     622           88 :         statements = pg_get_role_ddl_internal(roleid,
     623           44 :                                               opts[0].isset && opts[0].boolval,
     624           88 :                                               !opts[1].isset || opts[1].boolval);
     625           36 :         funcctx->user_fctx = statements;
     626           36 :         funcctx->max_calls = list_length(statements);
     627              : 
     628           36 :         MemoryContextSwitchTo(oldcontext);
     629              :     }
     630              : 
     631           92 :     funcctx = SRF_PERCALL_SETUP();
     632           92 :     statements = (List *) funcctx->user_fctx;
     633              : 
     634           92 :     if (funcctx->call_cntr < funcctx->max_calls)
     635              :     {
     636              :         char       *stmt;
     637              : 
     638           56 :         stmt = list_nth(statements, funcctx->call_cntr);
     639              : 
     640           56 :         SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(stmt));
     641              :     }
     642              :     else
     643              :     {
     644           36 :         list_free_deep(statements);
     645           36 :         SRF_RETURN_DONE(funcctx);
     646              :     }
     647              : }
     648              : 
     649              : /*
     650              :  * pg_get_tablespace_ddl_internal
     651              :  *      Generate DDL statements to recreate a tablespace.
     652              :  *
     653              :  * Returns a List of palloc'd strings.  The first element is the
     654              :  * CREATE TABLESPACE statement; if the tablespace has reloptions,
     655              :  * a second element with ALTER TABLESPACE SET (...) is appended.
     656              :  */
     657              : static List *
     658           28 : pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner)
     659              : {
     660              :     HeapTuple   tuple;
     661              :     Form_pg_tablespace tspForm;
     662              :     StringInfoData buf;
     663              :     char       *spcname;
     664              :     char       *spcowner;
     665              :     char       *path;
     666              :     bool        isNull;
     667              :     Datum       datum;
     668           28 :     List       *statements = NIL;
     669              : 
     670           28 :     tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tsid));
     671           28 :     if (!HeapTupleIsValid(tuple))
     672            4 :         ereport(ERROR,
     673              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     674              :                  errmsg("tablespace with OID %u does not exist",
     675              :                         tsid)));
     676              : 
     677           24 :     tspForm = (Form_pg_tablespace) GETSTRUCT(tuple);
     678           24 :     spcname = pstrdup(NameStr(tspForm->spcname));
     679              : 
     680              :     /* User must have SELECT privilege on pg_tablespace. */
     681           24 :     if (pg_class_aclcheck(TableSpaceRelationId, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
     682              :     {
     683            4 :         ReleaseSysCache(tuple);
     684            4 :         aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_TABLESPACE, spcname);
     685              :     }
     686              : 
     687              :     /*
     688              :      * We don't support generating DDL for system tablespaces.  The primary
     689              :      * reason for this is that users shouldn't be recreating them.
     690              :      */
     691           20 :     if (IsReservedName(spcname))
     692            0 :         ereport(ERROR,
     693              :                 (errcode(ERRCODE_RESERVED_NAME),
     694              :                  errmsg("tablespace name \"%s\" is reserved", spcname),
     695              :                  errdetail("Tablespace names starting with \"pg_\" are reserved for system tablespaces.")));
     696              : 
     697           20 :     initStringInfo(&buf);
     698              : 
     699              :     /* Start building the CREATE TABLESPACE statement */
     700           20 :     appendStringInfo(&buf, "CREATE TABLESPACE %s", quote_identifier(spcname));
     701              : 
     702              :     /* Add OWNER clause */
     703           20 :     if (!no_owner)
     704              :     {
     705           16 :         spcowner = GetUserNameFromId(tspForm->spcowner, false);
     706           16 :         append_ddl_option(&buf, pretty, 4, "OWNER %s",
     707              :                           quote_identifier(spcowner));
     708           16 :         pfree(spcowner);
     709              :     }
     710              : 
     711              :     /* Find tablespace directory path */
     712           20 :     path = get_tablespace_location(tsid);
     713              : 
     714              :     /* Add directory LOCATION (path), if it exists */
     715           20 :     if (path[0] != '\0')
     716              :     {
     717              :         /*
     718              :          * Special case: if the tablespace was created with GUC
     719              :          * "allow_in_place_tablespaces = true" and "LOCATION ''", path will
     720              :          * begin with "pg_tblspc/". In that case, show "LOCATION ''" as the
     721              :          * user originally specified.
     722              :          */
     723           20 :         if (strncmp(PG_TBLSPC_DIR_SLASH, path, strlen(PG_TBLSPC_DIR_SLASH)) == 0)
     724           20 :             append_ddl_option(&buf, pretty, 4, "LOCATION ''");
     725              :         else
     726            0 :             append_ddl_option(&buf, pretty, 4, "LOCATION %s",
     727              :                               quote_literal_cstr(path));
     728              :     }
     729           20 :     pfree(path);
     730              : 
     731           20 :     appendStringInfoChar(&buf, ';');
     732           20 :     statements = lappend(statements, pstrdup(buf.data));
     733              : 
     734              :     /* Check for tablespace options */
     735           20 :     datum = SysCacheGetAttr(TABLESPACEOID, tuple,
     736              :                             Anum_pg_tablespace_spcoptions, &isNull);
     737           20 :     if (!isNull)
     738              :     {
     739           12 :         resetStringInfo(&buf);
     740           12 :         appendStringInfo(&buf, "ALTER TABLESPACE %s SET (",
     741              :                          quote_identifier(spcname));
     742           12 :         get_reloptions(&buf, datum);
     743           12 :         appendStringInfoString(&buf, ");");
     744           12 :         statements = lappend(statements, pstrdup(buf.data));
     745              :     }
     746              : 
     747           20 :     ReleaseSysCache(tuple);
     748           20 :     pfree(spcname);
     749           20 :     pfree(buf.data);
     750              : 
     751           20 :     return statements;
     752              : }
     753              : 
     754              : /*
     755              :  * pg_get_tablespace_ddl_srf - common SRF logic for tablespace DDL
     756              :  */
     757              : static Datum
     758           68 : pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid, bool isnull)
     759              : {
     760              :     FuncCallContext *funcctx;
     761              :     List       *statements;
     762              : 
     763           68 :     if (SRF_IS_FIRSTCALL())
     764              :     {
     765              :         MemoryContext oldcontext;
     766           36 :         DdlOption   opts[] = {
     767              :             {"pretty", DDL_OPT_BOOL},
     768              :             {"owner", DDL_OPT_BOOL},
     769              :         };
     770              : 
     771           36 :         funcctx = SRF_FIRSTCALL_INIT();
     772           36 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     773              : 
     774           36 :         if (isnull)
     775              :         {
     776            8 :             MemoryContextSwitchTo(oldcontext);
     777            8 :             SRF_RETURN_DONE(funcctx);
     778              :         }
     779              : 
     780           28 :         parse_ddl_options(fcinfo, 1, opts, lengthof(opts));
     781              : 
     782           56 :         statements = pg_get_tablespace_ddl_internal(tsid,
     783           28 :                                                     opts[0].isset && opts[0].boolval,
     784           56 :                                                     opts[1].isset && !opts[1].boolval);
     785           20 :         funcctx->user_fctx = statements;
     786           20 :         funcctx->max_calls = list_length(statements);
     787              : 
     788           20 :         MemoryContextSwitchTo(oldcontext);
     789              :     }
     790              : 
     791           52 :     funcctx = SRF_PERCALL_SETUP();
     792           52 :     statements = (List *) funcctx->user_fctx;
     793              : 
     794           52 :     if (funcctx->call_cntr < funcctx->max_calls)
     795              :     {
     796              :         char       *stmt;
     797              : 
     798           32 :         stmt = (char *) list_nth(statements, funcctx->call_cntr);
     799              : 
     800           32 :         SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(stmt));
     801              :     }
     802              :     else
     803              :     {
     804           20 :         list_free_deep(statements);
     805           20 :         SRF_RETURN_DONE(funcctx);
     806              :     }
     807              : }
     808              : 
     809              : /*
     810              :  * pg_get_tablespace_ddl_oid
     811              :  *      Return DDL to recreate a tablespace, taking OID.
     812              :  */
     813              : Datum
     814           16 : pg_get_tablespace_ddl_oid(PG_FUNCTION_ARGS)
     815              : {
     816           16 :     Oid         tsid = InvalidOid;
     817              :     bool        isnull;
     818              : 
     819           16 :     isnull = PG_ARGISNULL(0);
     820           16 :     if (!isnull)
     821           12 :         tsid = PG_GETARG_OID(0);
     822              : 
     823           16 :     return pg_get_tablespace_ddl_srf(fcinfo, tsid, isnull);
     824              : }
     825              : 
     826              : /*
     827              :  * pg_get_tablespace_ddl_name
     828              :  *      Return DDL to recreate a tablespace, taking name.
     829              :  */
     830              : Datum
     831           56 : pg_get_tablespace_ddl_name(PG_FUNCTION_ARGS)
     832              : {
     833           56 :     Oid         tsid = InvalidOid;
     834              :     Name        tspname;
     835              :     bool        isnull;
     836              : 
     837           56 :     isnull = PG_ARGISNULL(0);
     838              : 
     839           56 :     if (!isnull)
     840              :     {
     841           52 :         tspname = PG_GETARG_NAME(0);
     842           52 :         tsid = get_tablespace_oid(NameStr(*tspname), false);
     843              :     }
     844              : 
     845           52 :     return pg_get_tablespace_ddl_srf(fcinfo, tsid, isnull);
     846              : }
     847              : 
     848              : /*
     849              :  * pg_get_database_ddl_internal
     850              :  *      Generate DDL statements to recreate a database.
     851              :  *
     852              :  * Returns a List of palloc'd strings.  The first element is the
     853              :  * CREATE DATABASE statement; subsequent elements are ALTER DATABASE
     854              :  * statements for properties and configuration settings.
     855              :  */
     856              : static List *
     857           16 : pg_get_database_ddl_internal(Oid dbid, bool pretty,
     858              :                              bool no_owner, bool no_tablespace)
     859              : {
     860              :     HeapTuple   tuple;
     861              :     Form_pg_database dbform;
     862              :     StringInfoData buf;
     863              :     bool        isnull;
     864              :     Datum       datum;
     865              :     const char *encoding;
     866              :     char       *dbname;
     867              :     char       *collate;
     868              :     char       *ctype;
     869              :     Relation    rel;
     870              :     ScanKeyData scankey[2];
     871              :     SysScanDesc scan;
     872           16 :     List       *statements = NIL;
     873              :     AclResult   aclresult;
     874              : 
     875           16 :     tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbid));
     876           16 :     if (!HeapTupleIsValid(tuple))
     877            0 :         ereport(ERROR,
     878              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     879              :                  errmsg("database with OID %u does not exist", dbid)));
     880              : 
     881              :     /* User must have connect privilege for target database. */
     882           16 :     aclresult = object_aclcheck(DatabaseRelationId, dbid, GetUserId(), ACL_CONNECT);
     883           16 :     if (aclresult != ACLCHECK_OK)
     884            4 :         aclcheck_error(aclresult, OBJECT_DATABASE,
     885            4 :                        get_database_name(dbid));
     886              : 
     887           12 :     dbform = (Form_pg_database) GETSTRUCT(tuple);
     888           12 :     dbname = pstrdup(NameStr(dbform->datname));
     889              : 
     890              :     /*
     891              :      * We don't support generating DDL for system databases.  The primary
     892              :      * reason for this is that users shouldn't be recreating them.
     893              :      */
     894           12 :     if (strcmp(dbname, "template0") == 0 || strcmp(dbname, "template1") == 0)
     895            0 :         ereport(ERROR,
     896              :                 (errcode(ERRCODE_RESERVED_NAME),
     897              :                  errmsg("database \"%s\" is a system database", dbname),
     898              :                  errdetail("DDL generation is not supported for template0 and template1.")));
     899              : 
     900           12 :     initStringInfo(&buf);
     901              : 
     902              :     /* --- Build CREATE DATABASE statement --- */
     903           12 :     appendStringInfo(&buf, "CREATE DATABASE %s", quote_identifier(dbname));
     904              : 
     905              :     /*
     906              :      * Always use template0: the target database already contains the catalog
     907              :      * data from whatever template was used originally, so we must start from
     908              :      * the pristine template to avoid duplication.
     909              :      */
     910           12 :     append_ddl_option(&buf, pretty, 4, "WITH TEMPLATE = template0");
     911              : 
     912              :     /* ENCODING */
     913           12 :     encoding = pg_encoding_to_char(dbform->encoding);
     914           12 :     if (strlen(encoding) > 0)
     915           12 :         append_ddl_option(&buf, pretty, 4, "ENCODING = %s",
     916              :                           quote_literal_cstr(encoding));
     917              : 
     918              :     /* LOCALE_PROVIDER */
     919           12 :     if (dbform->datlocprovider == COLLPROVIDER_BUILTIN ||
     920            9 :         dbform->datlocprovider == COLLPROVIDER_ICU ||
     921            9 :         dbform->datlocprovider == COLLPROVIDER_LIBC)
     922           12 :         append_ddl_option(&buf, pretty, 4, "LOCALE_PROVIDER = %s",
     923           12 :                           collprovider_name(dbform->datlocprovider));
     924              :     else
     925            0 :         ereport(ERROR,
     926              :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     927              :                  errmsg("unrecognized locale provider: %c",
     928              :                         dbform->datlocprovider)));
     929              : 
     930              :     /* LOCALE, LC_COLLATE, LC_CTYPE */
     931           12 :     datum = SysCacheGetAttr(DATABASEOID, tuple,
     932              :                             Anum_pg_database_datcollate, &isnull);
     933           12 :     collate = isnull ? NULL : TextDatumGetCString(datum);
     934           12 :     datum = SysCacheGetAttr(DATABASEOID, tuple,
     935              :                             Anum_pg_database_datctype, &isnull);
     936           12 :     ctype = isnull ? NULL : TextDatumGetCString(datum);
     937           12 :     if (collate != NULL && ctype != NULL && strcmp(collate, ctype) == 0)
     938              :     {
     939           12 :         append_ddl_option(&buf, pretty, 4, "LOCALE = %s",
     940              :                           quote_literal_cstr(collate));
     941              :     }
     942              :     else
     943              :     {
     944            0 :         if (collate != NULL)
     945            0 :             append_ddl_option(&buf, pretty, 4, "LC_COLLATE = %s",
     946              :                               quote_literal_cstr(collate));
     947            0 :         if (ctype != NULL)
     948            0 :             append_ddl_option(&buf, pretty, 4, "LC_CTYPE = %s",
     949              :                               quote_literal_cstr(ctype));
     950              :     }
     951              : 
     952              :     /* LOCALE (provider-specific) */
     953           12 :     datum = SysCacheGetAttr(DATABASEOID, tuple,
     954              :                             Anum_pg_database_datlocale, &isnull);
     955           12 :     if (!isnull)
     956              :     {
     957            3 :         const char *locale = TextDatumGetCString(datum);
     958              : 
     959            3 :         if (dbform->datlocprovider == COLLPROVIDER_BUILTIN)
     960            3 :             append_ddl_option(&buf, pretty, 4, "BUILTIN_LOCALE = %s",
     961              :                               quote_literal_cstr(locale));
     962            0 :         else if (dbform->datlocprovider == COLLPROVIDER_ICU)
     963            0 :             append_ddl_option(&buf, pretty, 4, "ICU_LOCALE = %s",
     964              :                               quote_literal_cstr(locale));
     965              :     }
     966              : 
     967              :     /* ICU_RULES */
     968           12 :     datum = SysCacheGetAttr(DATABASEOID, tuple,
     969              :                             Anum_pg_database_daticurules, &isnull);
     970           12 :     if (!isnull && dbform->datlocprovider == COLLPROVIDER_ICU)
     971            0 :         append_ddl_option(&buf, pretty, 4, "ICU_RULES = %s",
     972            0 :                           quote_literal_cstr(TextDatumGetCString(datum)));
     973              : 
     974              :     /* TABLESPACE */
     975           12 :     if (!no_tablespace && OidIsValid(dbform->dattablespace))
     976              :     {
     977            8 :         char       *spcname = get_tablespace_name(dbform->dattablespace);
     978              : 
     979            8 :         if (pg_strcasecmp(spcname, "pg_default") != 0)
     980            0 :             append_ddl_option(&buf, pretty, 4, "TABLESPACE = %s",
     981              :                               quote_identifier(spcname));
     982              :     }
     983              : 
     984           12 :     appendStringInfoChar(&buf, ';');
     985           12 :     statements = lappend(statements, pstrdup(buf.data));
     986              : 
     987              :     /* OWNER */
     988           12 :     if (!no_owner && OidIsValid(dbform->datdba))
     989              :     {
     990           12 :         char       *owner = GetUserNameFromId(dbform->datdba, false);
     991              : 
     992           12 :         resetStringInfo(&buf);
     993           12 :         appendStringInfo(&buf, "ALTER DATABASE %s OWNER TO %s;",
     994              :                          quote_identifier(dbname), quote_identifier(owner));
     995           12 :         pfree(owner);
     996           12 :         statements = lappend(statements, pstrdup(buf.data));
     997              :     }
     998              : 
     999              :     /* CONNECTION LIMIT */
    1000           12 :     if (dbform->datconnlimit != -1)
    1001              :     {
    1002           12 :         resetStringInfo(&buf);
    1003           12 :         appendStringInfo(&buf, "ALTER DATABASE %s CONNECTION LIMIT = %d;",
    1004              :                          quote_identifier(dbname), dbform->datconnlimit);
    1005           12 :         statements = lappend(statements, pstrdup(buf.data));
    1006              :     }
    1007              : 
    1008              :     /* IS_TEMPLATE */
    1009           12 :     if (dbform->datistemplate)
    1010              :     {
    1011            0 :         resetStringInfo(&buf);
    1012            0 :         appendStringInfo(&buf, "ALTER DATABASE %s IS_TEMPLATE = true;",
    1013              :                          quote_identifier(dbname));
    1014            0 :         statements = lappend(statements, pstrdup(buf.data));
    1015              :     }
    1016              : 
    1017              :     /* ALLOW_CONNECTIONS */
    1018           12 :     if (!dbform->datallowconn)
    1019              :     {
    1020            0 :         resetStringInfo(&buf);
    1021            0 :         appendStringInfo(&buf, "ALTER DATABASE %s ALLOW_CONNECTIONS = false;",
    1022              :                          quote_identifier(dbname));
    1023            0 :         statements = lappend(statements, pstrdup(buf.data));
    1024              :     }
    1025              : 
    1026           12 :     ReleaseSysCache(tuple);
    1027              : 
    1028              :     /*
    1029              :      * Now scan pg_db_role_setting for ALTER DATABASE SET configurations.
    1030              :      *
    1031              :      * It is only database-wide (setrole = 0). It generates one ALTER
    1032              :      * statement per setting.
    1033              :      */
    1034           12 :     rel = table_open(DbRoleSettingRelationId, AccessShareLock);
    1035           12 :     ScanKeyInit(&scankey[0],
    1036              :                 Anum_pg_db_role_setting_setdatabase,
    1037              :                 BTEqualStrategyNumber, F_OIDEQ,
    1038              :                 ObjectIdGetDatum(dbid));
    1039           12 :     ScanKeyInit(&scankey[1],
    1040              :                 Anum_pg_db_role_setting_setrole,
    1041              :                 BTEqualStrategyNumber, F_OIDEQ,
    1042              :                 ObjectIdGetDatum(InvalidOid));
    1043              : 
    1044           12 :     scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true,
    1045              :                               NULL, 2, scankey);
    1046              : 
    1047           24 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    1048              :     {
    1049              :         ArrayType  *dbconfig;
    1050              :         Datum      *settings;
    1051              :         bool       *nulls;
    1052              :         int         nsettings;
    1053              : 
    1054              :         /*
    1055              :          * The setconfig column is a text array in "name=value" format. It
    1056              :          * should never be null for a valid row, but be defensive.
    1057              :          */
    1058           12 :         datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
    1059              :                              RelationGetDescr(rel), &isnull);
    1060           12 :         if (isnull)
    1061            0 :             continue;
    1062              : 
    1063           12 :         dbconfig = DatumGetArrayTypeP(datum);
    1064              : 
    1065           12 :         deconstruct_array_builtin(dbconfig, TEXTOID, &settings, &nulls, &nsettings);
    1066              : 
    1067           24 :         for (int i = 0; i < nsettings; i++)
    1068              :         {
    1069              :             char       *s,
    1070              :                        *p;
    1071              : 
    1072           12 :             if (nulls[i])
    1073            0 :                 continue;
    1074              : 
    1075           12 :             s = TextDatumGetCString(settings[i]);
    1076           12 :             p = strchr(s, '=');
    1077           12 :             if (p == NULL)
    1078              :             {
    1079            0 :                 pfree(s);
    1080            0 :                 continue;
    1081              :             }
    1082           12 :             *p++ = '\0';
    1083              : 
    1084           12 :             resetStringInfo(&buf);
    1085           12 :             appendStringInfo(&buf, "ALTER DATABASE %s SET %s TO ",
    1086              :                              quote_identifier(dbname),
    1087              :                              quote_identifier(s));
    1088              : 
    1089           12 :             append_guc_value(&buf, s, p);
    1090              : 
    1091           12 :             appendStringInfoChar(&buf, ';');
    1092              : 
    1093           12 :             statements = lappend(statements, pstrdup(buf.data));
    1094              : 
    1095           12 :             pfree(s);
    1096              :         }
    1097              : 
    1098           12 :         pfree(settings);
    1099           12 :         pfree(nulls);
    1100           12 :         pfree(dbconfig);
    1101              :     }
    1102              : 
    1103           12 :     systable_endscan(scan);
    1104           12 :     table_close(rel, AccessShareLock);
    1105              : 
    1106           12 :     pfree(buf.data);
    1107           12 :     pfree(dbname);
    1108              : 
    1109           12 :     return statements;
    1110              : }
    1111              : 
    1112              : /*
    1113              :  * pg_get_database_ddl
    1114              :  *      Return DDL to recreate a database as a set of text rows.
    1115              :  */
    1116              : Datum
    1117           76 : pg_get_database_ddl(PG_FUNCTION_ARGS)
    1118              : {
    1119              :     FuncCallContext *funcctx;
    1120              :     List       *statements;
    1121              : 
    1122           76 :     if (SRF_IS_FIRSTCALL())
    1123              :     {
    1124              :         MemoryContext oldcontext;
    1125              :         Oid         dbid;
    1126           28 :         DdlOption   opts[] = {
    1127              :             {"pretty", DDL_OPT_BOOL},
    1128              :             {"owner", DDL_OPT_BOOL},
    1129              :             {"tablespace", DDL_OPT_BOOL},
    1130              :         };
    1131              : 
    1132           28 :         funcctx = SRF_FIRSTCALL_INIT();
    1133           28 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1134              : 
    1135           28 :         if (PG_ARGISNULL(0))
    1136              :         {
    1137            4 :             MemoryContextSwitchTo(oldcontext);
    1138            4 :             SRF_RETURN_DONE(funcctx);
    1139              :         }
    1140              : 
    1141           24 :         dbid = PG_GETARG_OID(0);
    1142           24 :         parse_ddl_options(fcinfo, 1, opts, lengthof(opts));
    1143              : 
    1144           48 :         statements = pg_get_database_ddl_internal(dbid,
    1145           16 :                                                   opts[0].isset && opts[0].boolval,
    1146           16 :                                                   opts[1].isset && !opts[1].boolval,
    1147           48 :                                                   opts[2].isset && !opts[2].boolval);
    1148           12 :         funcctx->user_fctx = statements;
    1149           12 :         funcctx->max_calls = list_length(statements);
    1150              : 
    1151           12 :         MemoryContextSwitchTo(oldcontext);
    1152              :     }
    1153              : 
    1154           60 :     funcctx = SRF_PERCALL_SETUP();
    1155           60 :     statements = (List *) funcctx->user_fctx;
    1156              : 
    1157           60 :     if (funcctx->call_cntr < funcctx->max_calls)
    1158              :     {
    1159              :         char       *stmt;
    1160              : 
    1161           48 :         stmt = list_nth(statements, funcctx->call_cntr);
    1162              : 
    1163           48 :         SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(stmt));
    1164              :     }
    1165              :     else
    1166              :     {
    1167           12 :         list_free_deep(statements);
    1168           12 :         SRF_RETURN_DONE(funcctx);
    1169              :     }
    1170              : }
        

Generated by: LCOV version 2.0-1