LCOV - code coverage report
Current view: top level - src/backend/utils/adt - ddlutils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 88.0 % 342 301
Test Date: 2026-07-22 12:15:41 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 61.7 % 196 121

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

Generated by: LCOV version 2.0-1