blob: 66324b48049cde0c0c2a85ec1dcd53e366118d74 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*-------------------------------------------------------------------------
*
* cdb_dump_include.c
* cdb_dump_include is collecion of functions used to determine the
* set of tables to be included in a dump. These functions are
* largely extracted from pg_dump.c.
*
* Portions Copyright (c) 2005-2010, Greenplum inc
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "pg_backup.h"
#include "catalog/pg_class.h"
#include "dumputils.h"
#include "cdb_dump_util.h"
#include "cdb_dump_include.h"
static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
static void getDomainConstraints(TypeInfo *tinfo);
static void expand_schema_name_patterns(SimpleStringList *patterns,
SimpleOidList *oids);
static void expand_table_name_patterns(SimpleStringList *patterns,
SimpleOidList *oids);
/*
* Subquery used to convert user ID (eg, datdba) to user name. The default
* value is "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =" but may
* be set to a string of similar form.
*/
const char *username_subquery = "SELECT rolname FROM pg_catalog.pg_roles WHERE oid =";
/*
* String designating the mode used to LOCK the tables to be dumped.
*/
const char *table_lock_mode = "ACCESS SHARE";
/*
* Partitioning is presumed supported by this code level. The caller
* of cdb_dump_include functions may declare otherwise.
*/
bool g_gp_supportsPartitioning = true;
/*
* MPP-6095: Partition templates are presumed supported by this code
* level. The caller of cdb_dump_include functions may declare
* otherwise.
*/
bool g_gp_supportsPartitionTemplates = true;
/*
* Indicates whether or not the GPDB cluster supports column attributes.
*/
bool g_gp_supportsAttributeEncoding = false;
/*
* When true, indicates that only the MPP schema is being dumped.
*/
bool g_gp_catalog_only = false;
const CatalogId nilCatalogId = {0, 0};
/*
* Constants used for message logging.
*/
static const char *logInfo = "INFO";
static const char *logWarn = "WARN";
static const char *logError = "ERROR";
/*
* Object inclusion/exclusion lists
*
* The string lists record the patterns given by command-line switches,
* which we then convert to lists of OIDs of matching objects.
*/
SimpleStringList schema_include_patterns = {NULL, NULL};
SimpleOidList schema_include_oids = {NULL, NULL};
SimpleStringList schema_exclude_patterns = {NULL, NULL};
SimpleOidList schema_exclude_oids = {NULL, NULL};
SimpleStringList table_include_patterns = {NULL, NULL};
SimpleOidList table_include_oids = {NULL, NULL};
SimpleStringList table_exclude_patterns = {NULL, NULL};
SimpleOidList table_exclude_oids = {NULL, NULL};
/* these are to avoid passing around info for findNamespace() */
static NamespaceInfo *g_namespaces;
static int g_numNamespaces;
/*
* fmtQualifiedId - convert a qualified name to the proper format for
* the source database.
*
* Like fmtId, use the result before calling again.
*/
const char *
fmtQualifiedId(const char *schema, const char *id)
{
static PQExpBuffer id_return = NULL;
if (id_return) /* first time through? */
resetPQExpBuffer(id_return);
else
id_return = createPQExpBuffer();
/* Suppress schema name if fetching from pre-7.3 DB */
if (schema && *schema)
{
appendPQExpBuffer(id_return, "%s.",
fmtId(schema));
}
appendPQExpBuffer(id_return, "%s",
fmtId(id));
return id_return->data;
}
/*
* Convenience subroutine to execute a SQL command and check for
* COMMAND_OK status.
*/
void
do_sql_command(PGconn *conn, const char *query)
{
PGresult *res;
res = PQexec(conn, query);
check_sql_result(res, conn, query, PGRES_COMMAND_OK);
PQclear(res);
}
/*
* selectDumpableNamespace: policy-setting subroutine
* Mark a namespace as to be dumped or not
*/
static void
selectDumpableNamespace(NamespaceInfo *nsinfo)
{
/*
* If specific tables are being dumped, do not dump any complete
* namespaces. If specific namespaces are being dumped, dump just those
* namespaces. Otherwise, dump all non-system namespaces.
*/
if (table_include_oids.head != NULL)
nsinfo->dobj.dump = false;
else if (schema_include_oids.head != NULL)
nsinfo->dobj.dump = simple_oid_list_member(&schema_include_oids,
nsinfo->dobj.catId.oid);
else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
strcmp(nsinfo->dobj.name, "information_schema") == 0 ||
strcmp(nsinfo->dobj.name, "gp_toolkit") == 0)
nsinfo->dobj.dump = false;
else
nsinfo->dobj.dump = true;
/*
* In any case, a namespace can be excluded by an exclusion switch
*/
if (nsinfo->dobj.dump &&
simple_oid_list_member(&schema_exclude_oids,
nsinfo->dobj.catId.oid))
nsinfo->dobj.dump = false;
}
/*
* selectDumpableTable: policy-setting subroutine
* Mark a table as to be dumped or not
*/
static void
selectDumpableTable(TableInfo *tbinfo)
{
/*
* If specific tables are being dumped, dump just those tables; else, dump
* according to the parent namespace's dump flag.
*/
if (table_include_oids.head != NULL)
tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
tbinfo->dobj.catId.oid);
else
tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump;
/*
* In any case, a table can be excluded by an exclusion switch
*/
if (tbinfo->dobj.dump &&
simple_oid_list_member(&table_exclude_oids,
tbinfo->dobj.catId.oid))
tbinfo->dobj.dump = false;
/* MPP addition */
/*
* if we are dumping the mpp catalog, mark all mpp catalog tables for dump
* and skip the policy table since it has oids and will be populated
* automatically when creating the tables.
*/
if (g_gp_catalog_only)
{
if (strncmp(tbinfo->dobj.name, "gp_", 3) == 0 &&
strcmp(tbinfo->dobj.namespace->dobj.name, "pg_catalog") == 0 &&
strcmp(tbinfo->dobj.name, "gp_distribution_policy") != 0)
tbinfo->dobj.dump = true;
else
tbinfo->dobj.dump = false;
}
/* MPP addition end */
}
/*
* selectDumpableObject: policy-setting subroutine
* Mark a generic dumpable object as to be dumped or not
*
* Use this only for object types without a special-case routine above.
*/
static void
selectDumpableObject(DumpableObject *dobj)
{
/*
* Default policy is to dump if parent namespace is dumpable, or always
* for non-namespace-associated items.
*/
if (dobj->namespace)
dobj->dump = dobj->namespace->dobj.dump;
else
dobj->dump = true;
}
/*
* selectDumpableType: policy-setting subroutine
* Mark a type as to be dumped or not
*/
static void
selectDumpableType(TypeInfo *tinfo)
{
/* Dump only types in dumpable namespaces */
if (!tinfo->dobj.namespace->dobj.dump)
tinfo->dobj.dump = false;
/* skip complex types, except for standalone composite types */
/* (note: this test should now be unnecessary) */
else if (OidIsValid(tinfo->typrelid) &&
tinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
tinfo->dobj.dump = false;
/* skip undefined placeholder types */
else if (!tinfo->isDefined)
tinfo->dobj.dump = false;
/* skip all array types that start w/ underscore */
else if ((tinfo->dobj.name[0] == '_') &&
OidIsValid(tinfo->typelem))
tinfo->dobj.dump = false;
else
tinfo->dobj.dump = true;
}
/*
* findNamespace:
* given a namespace OID and an object OID, look up the info read by
* getNamespaces
*
* NB: for pre-7.3 source database, we use object OID to guess whether it's
* a system object or not. In 7.3 and later there is no guessing.
*/
static NamespaceInfo *
findNamespace(Oid nsoid, Oid objoid)
{
int i;
for (i = 0; i < g_numNamespaces; i++)
{
NamespaceInfo *nsinfo = &g_namespaces[i];
if (nsoid == nsinfo->dobj.catId.oid)
return nsinfo;
}
mpp_err_msg(logError, progname, "schema with OID %u does not exist\n", nsoid);
exit_nicely();
return NULL; /* keep compiler quiet */
}
/*
* getDomainConstraints
*
* Get info about constraints on a domain.
*/
static void
getDomainConstraints(TypeInfo *tinfo)
{
int i;
ConstraintInfo *constrinfo;
PQExpBuffer query;
PGresult *res;
int i_tableoid,
i_oid,
i_conname,
i_consrc;
int ntups;
/*
* select appropriate schema to ensure names in constraint are properly
* qualified
*/
selectSourceSchema(tinfo->dobj.namespace->dobj.name);
query = createPQExpBuffer();
appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc "
"FROM pg_catalog.pg_constraint "
"WHERE contypid = '%u'::pg_catalog.oid "
"ORDER BY conname",
tinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
tinfo->nDomChecks = ntups;
tinfo->domChecks = constrinfo;
for (i = 0; i < ntups; i++)
{
constrinfo[i].dobj.objType = DO_CONSTRAINT;
constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&constrinfo[i].dobj);
constrinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
constrinfo[i].dobj.namespace = tinfo->dobj.namespace;
constrinfo[i].contable = NULL;
constrinfo[i].condomain = tinfo;
constrinfo[i].contype = 'c';
constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].conindex = 0;
constrinfo[i].coninherited = false;
constrinfo[i].separate = false;
/*
* Make the domain depend on the constraint, ensuring it won't be
* output till any constraint dependencies are OK.
*/
addObjectDependency(&tinfo->dobj,
constrinfo[i].dobj.dumpId);
}
PQclear(res);
destroyPQExpBuffer(query);
}
/*
* Convenience subroutine to verify a SQL command succeeded,
* and exit with a useful error message if not.
*/
void
check_sql_result(PGresult *res, PGconn *conn, const char *query,
ExecStatusType expected)
{
const char *err;
if (res && PQresultStatus(res) == expected)
return; /* A-OK */
mpp_err_msg(logError, progname, "SQL command failed\n");
if (res)
err = PQresultErrorMessage(res);
else
err = PQerrorMessage(conn);
mpp_err_msg(logError, progname, "Error message from server: %s", err);
mpp_err_msg(logError, progname, "The command was: %s\n", query);
exit_nicely();
}
/*
* selectSourceSchema - make the specified schema the active search path
* in the source database.
*
* NB: pg_catalog is explicitly searched after the specified schema;
* so user names are only qualified if they are cross-schema references,
* and system names are only qualified if they conflict with a user name
* in the current schema.
*
* Whenever the selected schema is not pg_catalog, be careful to qualify
* references to system catalogs and types in our emitted commands!
*/
void
selectSourceSchema(const char *schemaName)
{
static char *curSchemaName = NULL;
PQExpBuffer query;
/* Ignore null schema names */
if (schemaName == NULL || *schemaName == '\0')
return;
/* Optimize away repeated selection of same schema */
if (curSchemaName && strcmp(curSchemaName, schemaName) == 0)
return;
query = createPQExpBuffer();
appendPQExpBuffer(query, "SET search_path = %s",
fmtId(schemaName));
if (strcmp(schemaName, "pg_catalog") != 0)
appendPQExpBuffer(query, ", pg_catalog");
do_sql_command(g_conn, query->data);
destroyPQExpBuffer(query);
if (curSchemaName)
free(curSchemaName);
curSchemaName = strdup(schemaName);
}
/*
* Find the OIDs of all schemas matching the given list of patterns,
* and append them to the given OID list.
*/
static void
expand_schema_name_patterns(SimpleStringList *patterns, SimpleOidList *oids)
{
PQExpBuffer query;
PGresult *res;
SimpleStringListCell *cell;
int i;
if (patterns->head == NULL)
return; /* nothing to do */
query = createPQExpBuffer();
/*
* We use UNION ALL rather than UNION; this might sometimes result in
* duplicate entries in the OID list, but we don't care.
*/
for (cell = patterns->head; cell; cell = cell->next)
{
if (cell != patterns->head)
appendPQExpBuffer(query, "UNION ALL\n");
appendPQExpBuffer(query,
"SELECT oid FROM pg_catalog.pg_namespace n\n");
processSQLNamePattern(g_conn, query, cell->val, false, false,
NULL, "n.nspname", NULL,
NULL);
}
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
for (i = 0; i < PQntuples(res); i++)
{
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
}
PQclear(res);
destroyPQExpBuffer(query);
}
/*
* Find the OIDs of all tables matching the given list of patterns,
* and append them to the given OID list.
*/
static void
expand_table_name_patterns(SimpleStringList *patterns, SimpleOidList *oids)
{
PQExpBuffer query;
PGresult *res;
SimpleStringListCell *cell;
int i;
if (patterns->head == NULL)
return; /* nothing to do */
query = createPQExpBuffer();
/*
* We use UNION ALL rather than UNION; this might sometimes result in
* duplicate entries in the OID list, but we don't care.
*/
for (cell = patterns->head; cell; cell = cell->next)
{
if (cell != patterns->head)
appendPQExpBuffer(query, "UNION ALL\n");
appendPQExpBuffer(query,
"SELECT c.oid"
"\nFROM pg_catalog.pg_class c"
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
"\nWHERE c.relkind in ('%c', '%c', '%c')\n",
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
processSQLNamePattern(g_conn, query, cell->val, true, false,
"n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
}
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
for (i = 0; i < PQntuples(res); i++)
{
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
}
PQclear(res);
destroyPQExpBuffer(query);
}
/*
* Scan the schema include/exclude and table include/exclude patterns
* and develop a set of OIDs to be processed.
*/
void
convert_patterns_to_oids(void)
{
/* Expand schema selection patterns into OID lists */
if (schema_include_patterns.head != NULL)
{
expand_schema_name_patterns(&schema_include_patterns,
&schema_include_oids);
if (schema_include_oids.head == NULL)
{
mpp_err_msg(logError, progname, "No matching schemas were found\n");
exit_nicely();
}
}
expand_schema_name_patterns(&schema_exclude_patterns,
&schema_exclude_oids);
/* non-matching exclusion patterns aren't an error */
/* Expand table selection patterns into OID lists */
if (table_include_patterns.head != NULL)
{
expand_table_name_patterns(&table_include_patterns,
&table_include_oids);
if (table_include_oids.head == NULL)
{
mpp_err_msg(logError, progname, "No matching tables were found\n");
exit_nicely();
}
}
expand_table_name_patterns(&table_exclude_patterns,
&table_exclude_oids);
/* non-matching exclusion patterns aren't an error */
}
/*
* getNamespaces:
* read all namespaces in the system catalogs and return them in the
* NamespaceInfo* structure
*
* numNamespaces is set to the number of namespaces read in
*/
/* Declared in pg_dump.h */
NamespaceInfo *
getNamespaces(int *numNamespaces)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query;
NamespaceInfo *nsinfo;
int i_tableoid;
int i_oid;
int i_nspname;
int i_rolname;
int i_nspacl;
query = createPQExpBuffer();
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/*
* we fetch all namespaces including system ones, so that every object we
* read in can be linked to a containing namespace.
*/
appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
"(%s nspowner) as rolname, "
"nspacl FROM pg_namespace",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
nsinfo = (NamespaceInfo *) malloc(ntups * sizeof(NamespaceInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_nspname = PQfnumber(res, "nspname");
i_rolname = PQfnumber(res, "rolname");
i_nspacl = PQfnumber(res, "nspacl");
for (i = 0; i < ntups; i++)
{
nsinfo[i].dobj.objType = DO_NAMESPACE;
nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&nsinfo[i].dobj);
nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
/* Decide whether to dump this namespace */
selectDumpableNamespace(&nsinfo[i]);
if (strlen(nsinfo[i].rolname) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of schema \"%s\" appears to be invalid\n",
nsinfo[i].dobj.name);
}
PQclear(res);
destroyPQExpBuffer(query);
g_namespaces = nsinfo;
g_numNamespaces = *numNamespaces = ntups;
return nsinfo;
}
/*
* getFuncs:
* read all the user-defined functions in the system catalogs and
* return them in the FuncInfo* structure
*
* numFuncs is set to the number of functions read in
*/
/* Declared in pg_dump.h */
FuncInfo *
getFuncs(int *numFuncs)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
FuncInfo *finfo;
int i_tableoid;
int i_oid;
int i_proname;
int i_pronamespace;
int i_rolname;
int i_prolang;
int i_pronargs;
int i_proargtypes;
int i_prorettype;
int i_proacl;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* find all user-defined funcs */
appendPQExpBuffer(query,
"SELECT tableoid, oid, proname, prolang, "
"pronargs, proargtypes, prorettype, proacl, "
"pronamespace, "
"(%s proowner) as rolname "
"FROM pg_proc "
"WHERE NOT proisagg "
"AND pronamespace != "
"(select oid from pg_namespace"
" where nspname = 'pg_catalog')",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numFuncs = ntups;
finfo = (FuncInfo *) calloc(ntups, sizeof(FuncInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_proname = PQfnumber(res, "proname");
i_pronamespace = PQfnumber(res, "pronamespace");
i_rolname = PQfnumber(res, "rolname");
i_prolang = PQfnumber(res, "prolang");
i_pronargs = PQfnumber(res, "pronargs");
i_proargtypes = PQfnumber(res, "proargtypes");
i_prorettype = PQfnumber(res, "prorettype");
i_proacl = PQfnumber(res, "proacl");
for (i = 0; i < ntups; i++)
{
finfo[i].dobj.objType = DO_FUNC;
finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&finfo[i].dobj);
finfo[i].dobj.name = strdup(PQgetvalue(res, i, i_proname));
finfo[i].dobj.namespace =
findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)),
finfo[i].dobj.catId.oid);
finfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
if (finfo[i].nargs == 0)
finfo[i].argtypes = NULL;
else
{
finfo[i].argtypes = (Oid *) malloc(finfo[i].nargs * sizeof(Oid));
parseOidArray(PQgetvalue(res, i, i_proargtypes),
finfo[i].argtypes, finfo[i].nargs);
}
/* Decide whether we want to dump it */
selectDumpableObject(&(finfo[i].dobj));
if (strlen(finfo[i].rolname) == 0)
mpp_err_msg(logWarn, progname,
"WARNING: owner of function \"%s\" appears to be invalid\n",
finfo[i].dobj.name);
}
PQclear(res);
destroyPQExpBuffer(query);
return finfo;
}
/*
* getTypes:
* read all types in the system catalogs and return them in the
* TypeInfo* structure
*
* numTypes is set to the number of types read in
*
* NB: this must run after getFuncs() because we assume we can do
* findFuncByOid().
*/
/* Declared in pg_dump.h */
TypeInfo *
getTypes(int *numTypes)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
TypeInfo *tinfo;
ShellTypeInfo *stinfo;
int i_tableoid;
int i_oid;
int i_typname;
int i_typnamespace;
int i_rolname;
int i_typinput;
int i_typoutput;
int i_typelem;
int i_typrelid;
int i_typrelkind;
int i_typtype;
int i_typisdefined;
/*
* we include even the built-in types because those may be used as array
* elements by user-defined types
*
* we filter out the built-in types when we dump out the types
*
* same approach for undefined (shell) types
*/
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
"typnamespace, "
"(%s typowner) as rolname, "
"typinput::oid as typinput, "
"typoutput::oid as typoutput, typelem, typrelid, "
"CASE WHEN typrelid = 0 THEN ' '::\"char\" "
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
"typtype, typisdefined "
"FROM pg_type",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
tinfo = (TypeInfo *) malloc(ntups * sizeof(TypeInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_typname = PQfnumber(res, "typname");
i_typnamespace = PQfnumber(res, "typnamespace");
i_rolname = PQfnumber(res, "rolname");
i_typinput = PQfnumber(res, "typinput");
i_typoutput = PQfnumber(res, "typoutput");
i_typelem = PQfnumber(res, "typelem");
i_typrelid = PQfnumber(res, "typrelid");
i_typrelkind = PQfnumber(res, "typrelkind");
i_typtype = PQfnumber(res, "typtype");
i_typisdefined = PQfnumber(res, "typisdefined");
for (i = 0; i < ntups; i++)
{
tinfo[i].dobj.objType = DO_TYPE;
tinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
tinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&tinfo[i].dobj);
tinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_typname));
tinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)),
tinfo[i].dobj.catId.oid);
tinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
tinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
tinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
tinfo[i].shellType = NULL;
/*
* If it's a table's rowtype, use special type code to facilitate
* sorting into the desired order. (We don't want to consider it an
* ordinary type because that would bring the table up into the
* datatype part of the dump order.)
*/
if (OidIsValid(tinfo[i].typrelid) &&
tinfo[i].typrelkind != RELKIND_COMPOSITE_TYPE)
tinfo[i].dobj.objType = DO_TABLE_TYPE;
/*
* check for user-defined array types, omit system generated ones
*/
if (OidIsValid(tinfo[i].typelem) &&
tinfo[i].dobj.name[0] != '_')
tinfo[i].isArray = true;
else
tinfo[i].isArray = false;
if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
tinfo[i].isDefined = true;
else
tinfo[i].isDefined = false;
/* Decide whether we want to dump it */
selectDumpableType(&tinfo[i]);
/*
* If it's a domain, fetch info about its constraints, if any
*/
tinfo[i].nDomChecks = 0;
tinfo[i].domChecks = NULL;
if (tinfo[i].dobj.dump && tinfo[i].typtype == 'd')
getDomainConstraints(&(tinfo[i]));
/*
* If it's a base type, make a DumpableObject representing a shell
* definition of the type. We will need to dump that ahead of the I/O
* functions for the type.
*
* Note: the shell type doesn't have a catId. You might think it
* should copy the base type's catId, but then it might capture the
* pg_depend entries for the type, which we don't want.
*/
if (tinfo[i].dobj.dump && tinfo[i].typtype == 'b')
{
stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
stinfo->dobj.objType = DO_SHELL_TYPE;
stinfo->dobj.catId = nilCatalogId;
AssignDumpId(&stinfo->dobj);
stinfo->dobj.name = strdup(tinfo[i].dobj.name);
stinfo->dobj.namespace = tinfo[i].dobj.namespace;
stinfo->baseType = &(tinfo[i]);
tinfo[i].shellType = stinfo;
/*
* Initially mark the shell type as not to be dumped. We'll only
* dump it if the I/O functions need to be dumped; this is taken
* care of while sorting dependencies.
*/
stinfo->dobj.dump = false;
}
if (strlen(tinfo[i].rolname) == 0 && tinfo[i].isDefined)
mpp_err_msg(logWarn, progname, "WARNING: owner of data type \"%s\" appears to be invalid\n",
tinfo[i].dobj.name);
}
*numTypes = ntups;
PQclear(res);
destroyPQExpBuffer(query);
return tinfo;
}
/*
* getTypeStorageOptions:
* read all types with storage options in the system catalogs and return them in the
* TypeStorageOptions* structure
*
* numTypes is set to the number of types with storage options read in
*
*/
TypeStorageOptions *
getTypeStorageOptions(int *numTypes)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
TypeStorageOptions *tstorageoptions;
int i_oid;
int i_typname;
int i_typnamespace;
int i_typoptions;
int i_rolname;
if (g_gp_supportsAttributeEncoding == false)
{
numTypes = 0;
tstorageoptions = (TypeStorageOptions *) malloc(0);
return tstorageoptions;
}
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/*
* The following statement used format_type to resolve an internal name to its equivalent sql name.
* The format_type seems to do two things, it translates an internal type name (e.g. bpchar) into its
* sql equivalent (e.g. character), and it puts trailing "[]" on a type if it is an array.
* For any user defined type (ie. oid > 10000) or any type that might be an array (ie. starts with '_'),
* then we will call quote_ident. If the type is a system defined type (i.e. oid <= 10000)
* and can not possibly be an array (i.e. does not start with '_'), then call format_type to get the name. The
* reason we do not call format_type for arrays is that it will return a '[]' on the end, which can not be used
* when dumping the type.
*/
appendPQExpBuffer(query, "SELECT "
" CASE WHEN t.oid > 10000 OR substring(t.typname from 1 for 1) = '_' "
" THEN quote_ident(t.typname) "
" ELSE pg_catalog.format_type(t.oid, NULL) "
" END as typname "
", t.oid AS oid"
", t.typnamespace AS typnamespace"
", (%s typowner) as rolname"
", array_to_string(a.typoptions, ', ') AS typoptions "
" FROM pg_type AS t "
" INNER JOIN pg_catalog.pg_type_encoding a ON a.typid = t.oid"
" WHERE t.typisdefined = 't'", username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
tstorageoptions = (TypeStorageOptions *) malloc(ntups * sizeof(TypeStorageOptions));
i_typname = PQfnumber(res, "typname");
i_oid = PQfnumber(res, "oid");
i_typnamespace = PQfnumber(res, "typnamespace");
i_typoptions = PQfnumber(res, "typoptions");
i_rolname = PQfnumber(res, "rolname");
for (i = 0; i < ntups; i++)
{
tstorageoptions[i].dobj.objType = DO_TYPE_STORAGE_OPTIONS;
AssignDumpId(&tstorageoptions[i].dobj);
tstorageoptions[i].dobj.name = strdup(PQgetvalue(res, i, i_typname));
tstorageoptions[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
tstorageoptions[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)),tstorageoptions[i].dobj.catId.oid);
tstorageoptions[i].typoptions = strdup(PQgetvalue(res, i, i_typoptions));
tstorageoptions[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
}
*numTypes = ntups;
PQclear(res);
destroyPQExpBuffer(query);
return tstorageoptions;
}
/*
* getOperators:
* read all operators in the system catalogs and return them in the
* OprInfo* structure
*
* numOprs is set to the number of operators read in
*/
/* Declared in pg_dump.h */
OprInfo *
getOperators(int *numOprs)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
OprInfo *oprinfo;
int i_tableoid;
int i_oid;
int i_oprname;
int i_oprnamespace;
int i_rolname;
int i_oprcode;
/*
* find all operators, including builtin operators; we filter out
* system-defined operators at dump-out time.
*/
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT tableoid, oid, oprname, "
"oprnamespace, "
"(%s oprowner) as rolname, "
"oprcode::oid as oprcode "
"FROM pg_operator",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOprs = ntups;
oprinfo = (OprInfo *) malloc(ntups * sizeof(OprInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_oprname = PQfnumber(res, "oprname");
i_oprnamespace = PQfnumber(res, "oprnamespace");
i_rolname = PQfnumber(res, "rolname");
i_oprcode = PQfnumber(res, "oprcode");
for (i = 0; i < ntups; i++)
{
oprinfo[i].dobj.objType = DO_OPERATOR;
oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&oprinfo[i].dobj);
oprinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_oprname));
oprinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)),
oprinfo[i].dobj.catId.oid);
oprinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
/* Decide whether we want to dump it */
selectDumpableObject(&(oprinfo[i].dobj));
if (strlen(oprinfo[i].rolname) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of operator \"%s\" appears to be invalid\n",
oprinfo[i].dobj.name);
}
PQclear(res);
destroyPQExpBuffer(query);
return oprinfo;
}
/*
* getConversions:
* read all conversions in the system catalogs and return them in the
* ConvInfo* structure
*
* numConversions is set to the number of conversions read in
*/
/* Declared in pg_dump.h */
ConvInfo *
getConversions(int *numConversions)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
ConvInfo *convinfo;
int i_tableoid;
int i_oid;
int i_conname;
int i_connamespace;
int i_rolname;
/*
* find all conversions, including builtin conversions; we filter out
* system-defined conversions at dump-out time.
*/
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
"connamespace, "
"(%s conowner) as rolname "
"FROM pg_conversion",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numConversions = ntups;
convinfo = (ConvInfo *) malloc(ntups * sizeof(ConvInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_connamespace = PQfnumber(res, "connamespace");
i_rolname = PQfnumber(res, "rolname");
for (i = 0; i < ntups; i++)
{
convinfo[i].dobj.objType = DO_CONVERSION;
convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&convinfo[i].dobj);
convinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_conname));
convinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_connamespace)),
convinfo[i].dobj.catId.oid);
convinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
selectDumpableObject(&(convinfo[i].dobj));
}
PQclear(res);
destroyPQExpBuffer(query);
return convinfo;
}
/*
* getOpclasses:
* read all opclasses in the system catalogs and return them in the
* OpclassInfo* structure
*
* numOpclasses is set to the number of opclasses read in
*/
/* Declared in pg_dump.h */
OpclassInfo *
getOpclasses(int *numOpclasses)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
OpclassInfo *opcinfo;
int i_tableoid;
int i_oid;
int i_opcname;
int i_opcnamespace;
int i_rolname;
/*
* find all opclasses, including builtin opclasses; we filter out
* system-defined opclasses at dump-out time.
*/
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT tableoid, oid, opcname, "
"opcnamespace, "
"(%s opcowner) as rolname "
"FROM pg_opclass",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numOpclasses = ntups;
opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_opcname = PQfnumber(res, "opcname");
i_opcnamespace = PQfnumber(res, "opcnamespace");
i_rolname = PQfnumber(res, "rolname");
for (i = 0; i < ntups; i++)
{
opcinfo[i].dobj.objType = DO_OPCLASS;
opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&opcinfo[i].dobj);
opcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_opcname));
opcinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)),
opcinfo[i].dobj.catId.oid);
opcinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
selectDumpableObject(&(opcinfo[i].dobj));
if (strlen(opcinfo[i].rolname) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of operator class \"%s\" appears to be invalid\n",
opcinfo[i].dobj.name);
}
PQclear(res);
destroyPQExpBuffer(query);
return opcinfo;
}
/*
* getAggregates:
* read all the user-defined aggregates in the system catalogs and
* return them in the AggInfo* structure
*
* numAggs is set to the number of aggregates read in
*/
/* Declared in pg_dump.h */
AggInfo *
getAggregates(int *numAggs)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
AggInfo *agginfo;
int i_tableoid;
int i_oid;
int i_aggname;
int i_aggnamespace;
int i_pronargs;
int i_proargtypes;
int i_rolname;
int i_aggacl;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* find all user-defined aggregates */
appendPQExpBuffer(query, "SELECT tableoid, oid, proname as aggname, "
"pronamespace as aggnamespace, "
"pronargs, proargtypes, "
"(%s proowner) as rolname, "
"proacl as aggacl "
"FROM pg_proc "
"WHERE proisagg "
"AND pronamespace != "
"(select oid from pg_namespace where nspname = 'pg_catalog')",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numAggs = ntups;
agginfo = (AggInfo *) malloc(ntups * sizeof(AggInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_aggname = PQfnumber(res, "aggname");
i_aggnamespace = PQfnumber(res, "aggnamespace");
i_pronargs = PQfnumber(res, "pronargs");
i_proargtypes = PQfnumber(res, "proargtypes");
i_rolname = PQfnumber(res, "rolname");
i_aggacl = PQfnumber(res, "aggacl");
for (i = 0; i < ntups; i++)
{
agginfo[i].aggfn.dobj.objType = DO_AGG;
agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&agginfo[i].aggfn.dobj);
agginfo[i].aggfn.dobj.name = strdup(PQgetvalue(res, i, i_aggname));
agginfo[i].aggfn.dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)),
agginfo[i].aggfn.dobj.catId.oid);
agginfo[i].aggfn.rolname = strdup(PQgetvalue(res, i, i_rolname));
if (strlen(agginfo[i].aggfn.rolname) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of aggregate function \"%s\" appears to be invalid\n",
agginfo[i].aggfn.dobj.name);
agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
agginfo[i].aggfn.proacl = strdup(PQgetvalue(res, i, i_aggacl));
agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
if (agginfo[i].aggfn.nargs == 0)
agginfo[i].aggfn.argtypes = NULL;
else
{
agginfo[i].aggfn.argtypes = (Oid *) malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
parseOidArray(PQgetvalue(res, i, i_proargtypes),
agginfo[i].aggfn.argtypes,
agginfo[i].aggfn.nargs);
}
/* Decide whether we want to dump it */
selectDumpableObject(&(agginfo[i].aggfn.dobj));
}
PQclear(res);
destroyPQExpBuffer(query);
return agginfo;
}
/*
* getExtProtocols:
* read all the user-defined protocols in the system catalogs and
* return them in the ExtProtInfo* structure
*
* numExtProtocols is set to the number of protocols read in
*/
/* Declared in pg_dump.h */
ExtProtInfo *
getExtProtocols(int *numExtProtocols)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
ExtProtInfo *ptcinfo;
int i_oid;
int i_tableoid;
int i_ptcname;
int i_rolname;
int i_ptcacl;
int i_ptctrusted;
int i_ptcreadid;
int i_ptcwriteid;
int i_ptcvalidid;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* find all user-defined aggregates */
appendPQExpBuffer(query, "SELECT ptc.tableoid as tableoid, "
" ptc.oid as oid, "
" ptc.ptcname as ptcname, "
" ptcreadfn as ptcreadoid, "
" ptcwritefn as ptcwriteoid, "
" ptcvalidatorfn as ptcvaloid, "
" (%s ptc.ptcowner) as rolname, "
" ptc.ptctrusted as ptctrusted, "
" ptc.ptcacl as ptcacl "
"FROM pg_extprotocol ptc",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numExtProtocols = ntups;
ptcinfo = (ExtProtInfo *) malloc(ntups * sizeof(ExtProtInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_ptcname = PQfnumber(res, "ptcname");
i_rolname = PQfnumber(res, "rolname");
i_ptcacl = PQfnumber(res, "ptcacl");
i_ptctrusted = PQfnumber(res, "ptctrusted");
i_ptcreadid = PQfnumber(res, "ptcreadoid");
i_ptcwriteid = PQfnumber(res, "ptcwriteoid");
i_ptcvalidid = PQfnumber(res, "ptcvaloid");
for (i = 0; i < ntups; i++)
{
ptcinfo[i].dobj.objType = DO_EXTPROTOCOL;
ptcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
ptcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&ptcinfo[i].dobj);
ptcinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_ptcname));
ptcinfo[i].dobj.namespace = NULL;
ptcinfo[i].ptcowner = strdup(PQgetvalue(res, i, i_rolname));
if (strlen(ptcinfo[i].ptcowner) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of external protocol \"%s\" appears to be invalid\n",
ptcinfo[i].dobj.name);
if (PQgetisnull(res, i, i_ptcreadid))
ptcinfo[i].ptcreadid = InvalidOid;
else
ptcinfo[i].ptcreadid = atooid(PQgetvalue(res, i, i_ptcreadid));
if (PQgetisnull(res, i, i_ptcwriteid))
ptcinfo[i].ptcwriteid = InvalidOid;
else
ptcinfo[i].ptcwriteid = atooid(PQgetvalue(res, i, i_ptcwriteid));
if (PQgetisnull(res, i, i_ptcvalidid))
ptcinfo[i].ptcvalidid = InvalidOid;
else
ptcinfo[i].ptcvalidid = atooid(PQgetvalue(res, i, i_ptcvalidid));
ptcinfo[i].ptcacl = strdup(PQgetvalue(res, i, i_ptcacl));
ptcinfo[i].ptctrusted = *(PQgetvalue(res, i, i_ptctrusted)) == 't';
/* Decide whether we want to dump it */
selectDumpableObject(&(ptcinfo[i].dobj));
}
PQclear(res);
destroyPQExpBuffer(query);
return ptcinfo;
}
/*
* getForeignDataWrappers:
* read all foreign-data wrappers in the system catalogs and return
* them in the FdwInfo* structure
*
* numForeignDataWrappers is set to the number of fdws read in
*/
FdwInfo *
getForeignDataWrappers(int *numForeignDataWrappers)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
FdwInfo *fdwinfo;
int i_oid;
int i_fdwname;
int i_rolname;
int i_fdwvalidator;
int i_fdwacl;
int i_fdwoptions;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT oid, fdwname, "
"(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
"array_to_string(ARRAY("
" SELECT option_name || ' ' || quote_literal(option_value) "
" FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
"FROM pg_foreign_data_wrapper",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numForeignDataWrappers = ntups;
fdwinfo = (FdwInfo *) malloc(ntups * sizeof(FdwInfo));
i_oid = PQfnumber(res, "oid");
i_fdwname = PQfnumber(res, "fdwname");
i_rolname = PQfnumber(res, "rolname");
i_fdwvalidator = PQfnumber(res, "fdwvalidator");
i_fdwacl = PQfnumber(res, "fdwacl");
i_fdwoptions = PQfnumber(res, "fdwoptions");
for (i = 0; i < ntups; i++)
{
fdwinfo[i].dobj.objType = DO_FDW;
fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&fdwinfo[i].dobj);
fdwinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_fdwname));
fdwinfo[i].dobj.namespace = NULL;
fdwinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
fdwinfo[i].fdwvalidator = strdup(PQgetvalue(res, i, i_fdwvalidator));
fdwinfo[i].fdwoptions = strdup(PQgetvalue(res, i, i_fdwoptions));
fdwinfo[i].fdwacl = strdup(PQgetvalue(res, i, i_fdwacl));
/* Decide whether we want to dump it */
selectDumpableObject(&(fdwinfo[i].dobj));
}
PQclear(res);
destroyPQExpBuffer(query);
return fdwinfo;
}
/*
* getForeignServers:
* read all foreign servers in the system catalogs and return
* them in the ForeignServerInfo * structure
*
* numForeignServers is set to the number of servers read in
*/
ForeignServerInfo *
getForeignServers(int *numForeignServers)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
ForeignServerInfo *srvinfo;
int i_oid;
int i_srvname;
int i_rolname;
int i_srvfdw;
int i_srvtype;
int i_srvversion;
int i_srvacl;
int i_srvoptions;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT oid, srvname, "
"(%s srvowner) AS rolname, "
"srvfdw, srvtype, srvversion, srvacl,"
"array_to_string(ARRAY("
" SELECT option_name || ' ' || quote_literal(option_value) "
" FROM pg_options_to_table(srvoptions)), ', ') AS srvoptions "
"FROM pg_foreign_server",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numForeignServers = ntups;
srvinfo = (ForeignServerInfo *) malloc(ntups * sizeof(ForeignServerInfo));
i_oid = PQfnumber(res, "oid");
i_srvname = PQfnumber(res, "srvname");
i_rolname = PQfnumber(res, "rolname");
i_srvfdw = PQfnumber(res, "srvfdw");
i_srvtype = PQfnumber(res, "srvtype");
i_srvversion = PQfnumber(res, "srvversion");
i_srvacl = PQfnumber(res, "srvacl");
i_srvoptions = PQfnumber(res, "srvoptions");
for (i = 0; i < ntups; i++)
{
srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&srvinfo[i].dobj);
srvinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_srvname));
srvinfo[i].dobj.namespace = NULL;
srvinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
srvinfo[i].srvtype = strdup(PQgetvalue(res, i, i_srvtype));
srvinfo[i].srvversion = strdup(PQgetvalue(res, i, i_srvversion));
srvinfo[i].srvoptions = strdup(PQgetvalue(res, i, i_srvoptions));
srvinfo[i].srvacl = strdup(PQgetvalue(res, i, i_srvacl));
/* Decide whether we want to dump it */
selectDumpableObject(&(srvinfo[i].dobj));
}
PQclear(res);
destroyPQExpBuffer(query);
return srvinfo;
}
/*
* getTables
* read all the user-defined tables (no indexes, no catalogs)
* in the system catalogs return them in the TableInfo* structure
*
* numTables is set to the number of tables read in
*/
/* Declared in pg_dump.h */
TableInfo *
getTables(int *numTables)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
PQExpBuffer delqry = createPQExpBuffer();
PQExpBuffer lockquery = createPQExpBuffer();
TableInfo *tblinfo;
int i_reltableoid;
int i_reloid;
int i_relname;
int i_relnamespace;
int i_relkind;
int i_relstorage;
int i_relacl;
int i_rolname;
int i_relchecks;
int i_reltriggers;
int i_relhasindex;
int i_relhasrules;
int i_relhasoids;
int i_owning_tab;
int i_owning_col;
int i_reltablespace;
int i_reloptions;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/*
* Find all the tables (including views and sequences).
*
* We include system catalogs, so that we can work if a user table is
* defined to inherit from a system catalog (pretty weird, but...)
*
* We ignore tables that are not type 'r' (ordinary relation), 'S'
* (sequence), 'v' (view), or 'c' (composite type).
*
* Composite-type table entries won't be dumped as such, but we have to
* make a DumpableObject for them so that we can track dependencies of the
* composite type (pg_depend entries for columns of the composite type
* link to the pg_class entry not the pg_type entry).
*
* Note: in this phase we should collect only a minimal amount of
* information about each table, basically just enough to decide if it is
* interesting. We must fetch all tables in this phase because otherwise
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
/*
* Left join to pick up dependency info linking sequences to their owning
* column, if any (note this dependency is AUTO as of 8.2)
*/
appendPQExpBuffer(query,
"SELECT c.tableoid, c.oid, relname, "
"relacl, relkind, relstorage, relnamespace, "
"(%s relowner) as rolname, "
"relchecks, reltriggers, "
"relhasindex, relhasrules, relhasoids, "
"d.refobjid as owning_tab, "
"d.refobjsubid as owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') as reloptions "
"from pg_class c "
"left join pg_depend d on "
"(c.relkind = '%c' and "
"d.classid = c.tableoid and d.objid = c.oid and "
"d.objsubid = 0 and "
"d.refclassid = c.tableoid and d.deptype = 'a') "
"where relkind in ('%c', '%c', '%c', '%c') %s"
"order by c.oid",
username_subquery,
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE,
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
g_gp_supportsPartitioning ?
"AND c.oid NOT IN (select parchildrelid from "
"pg_partition_rule)" : "");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numTables = ntups;
/*
* Extract data from result and lock dumpable tables. We do the locking
* before anything else, to minimize the window wherein a table could
* disappear under us.
*
* Note that we have to save info about all tables here, even when dumping
* only one, because we don't yet know which tables might be inheritance
* ancestors of the target table.
*/
tblinfo = (TableInfo *) calloc(ntups, sizeof(TableInfo));
i_reltableoid = PQfnumber(res, "tableoid");
i_reloid = PQfnumber(res, "oid");
i_relname = PQfnumber(res, "relname");
i_relnamespace = PQfnumber(res, "relnamespace");
i_relacl = PQfnumber(res, "relacl");
i_relkind = PQfnumber(res, "relkind");
i_relstorage = PQfnumber(res, "relstorage");
i_rolname = PQfnumber(res, "rolname");
i_relchecks = PQfnumber(res, "relchecks");
i_reltriggers = PQfnumber(res, "reltriggers");
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules");
i_relhasoids = PQfnumber(res, "relhasoids");
i_owning_tab = PQfnumber(res, "owning_tab");
i_owning_col = PQfnumber(res, "owning_col");
i_reltablespace = PQfnumber(res, "reltablespace");
i_reloptions = PQfnumber(res, "reloptions");
for (i = 0; i < ntups; i++)
{
tblinfo[i].dobj.objType = DO_TABLE;
tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
AssignDumpId(&tblinfo[i].dobj);
tblinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_relname));
tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)),
tblinfo[i].dobj.catId.oid);
tblinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
tblinfo[i].relacl = strdup(PQgetvalue(res, i, i_relacl));
tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
tblinfo[i].relstorage = *(PQgetvalue(res, i, i_relstorage));
tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
tblinfo[i].ntrig = atoi(PQgetvalue(res, i, i_reltriggers));
if (PQgetisnull(res, i, i_owning_tab))
{
tblinfo[i].owning_tab = InvalidOid;
tblinfo[i].owning_col = 0;
}
else
{
tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
}
tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions));
/* other fields were zeroed above */
/*
* Decide whether we want to dump this table.
*/
if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
tblinfo[i].dobj.dump = false;
else
selectDumpableTable(&tblinfo[i]);
tblinfo[i].interesting = tblinfo[i].dobj.dump;
/*
* Lock target tables to make sure they aren't DROPPED or altered in
* schema before we get around to dumping them.
*
* Note that we don't explicitly lock parents of the target tables; we
* assume our lock on the child is enough to prevent schema
* alterations to parent tables.
*
* NOTE: it'd be kinda nice to lock views and sequences too, not only
* plain tables, but the backend doesn't presently allow that.
*/
if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
{
resetPQExpBuffer(lockquery);
appendPQExpBuffer(lockquery, "LOCK TABLE %s IN %s MODE",
fmtQualifiedId(tblinfo[i].dobj.namespace->dobj.name,
tblinfo[i].dobj.name), table_lock_mode);
do_sql_command(g_conn, lockquery->data);
}
/* Emit notice if join for owner failed */
if (strlen(tblinfo[i].rolname) == 0)
mpp_err_msg(logWarn, progname, "WARNING: owner of table \"%s\" appears to be invalid\n",
tblinfo[i].dobj.name);
}
PQclear(res);
/*
* Force sequences that are "owned" by table columns to be dumped whenever
* their owning table is being dumped.
*/
for (i = 0; i < ntups; i++)
{
TableInfo *seqinfo = &tblinfo[i];
int j;
if (!OidIsValid(seqinfo->owning_tab))
continue; /* not an owned sequence */
if (seqinfo->dobj.dump)
continue; /* no need to search */
/* can't use findTableByOid yet, unfortunately */
for (j = 0; j < ntups; j++)
{
if (tblinfo[j].dobj.catId.oid == seqinfo->owning_tab)
{
if (tblinfo[j].dobj.dump)
{
seqinfo->interesting = true;
seqinfo->dobj.dump = true;
}
break;
}
}
}
destroyPQExpBuffer(query);
destroyPQExpBuffer(delqry);
destroyPQExpBuffer(lockquery);
return tblinfo;
}
/*
* getInherits
* read all the inheritance information
* from the system catalogs return them in the InhInfo* structure
*
* numInherits is set to the number of pairs read in
*/
/* Declared in pg_dump.h */
InhInfo *
getInherits(int *numInherits)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
InhInfo *inhinfo;
int i_inhrelid;
int i_inhparent;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* find all the inheritance information */
appendPQExpBuffer(query, "SELECT inhrelid, inhparent from pg_inherits");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numInherits = ntups;
inhinfo = (InhInfo *) malloc(ntups * sizeof(InhInfo));
i_inhrelid = PQfnumber(res, "inhrelid");
i_inhparent = PQfnumber(res, "inhparent");
for (i = 0; i < ntups; i++)
{
inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
}
PQclear(res);
destroyPQExpBuffer(query);
return inhinfo;
}
/*
* getIndexes
* get information about every index on a dumpable table
*
* Note: index data is not returned directly to the caller, but it
* does get entered into the DumpableObject tables.
*/
/* Declared in pg_dump.h */
void
getIndexes(TableInfo tblinfo[], int numTables)
{
int i,
j;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
IndxInfo *indxinfo;
ConstraintInfo *constrinfo;
int i_tableoid,
i_oid,
i_indexname,
i_indexdef,
i_indnkeys,
i_indkey,
i_indisclustered,
i_contype,
i_conname,
i_contableoid,
i_conoid,
i_tablespace,
i_options;
int ntups;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
/* Only plain tables have indexes */
if (tbinfo->relkind != RELKIND_RELATION || !tbinfo->hasindex)
continue;
/* Ignore indexes of tables not to be dumped */
if (!tbinfo->dobj.dump)
continue;
if (g_verbose)
mpp_err_msg(logInfo, progname, "reading indexes for table \"%s\"\n",
tbinfo->dobj.name);
/* Make sure we are in proper schema so indexdef is right */
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
/*
* The point of the messy-looking outer join is to find a constraint
* that is related by an internal dependency link to the index. If we
* find one, create a CONSTRAINT entry linked to the INDEX entry. We
* assume an index won't have more than one internal dependency.
*/
resetPQExpBuffer(query);
appendPQExpBuffer(query,
"SELECT t.tableoid, t.oid, "
"t.relname as indexname, "
"pg_catalog.pg_get_indexdef(i.indexrelid) as indexdef, "
"t.relnatts as indnkeys, "
"i.indkey, i.indisclustered, "
"c.contype, c.conname, "
"c.tableoid as contableoid, "
"c.oid as conoid, "
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) as tablespace, "
"array_to_string(t.reloptions, ', ') as options "
"FROM pg_catalog.pg_index i "
"JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
"LEFT JOIN pg_catalog.pg_depend d "
"ON (d.classid = t.tableoid "
"AND d.objid = t.oid "
"AND d.deptype = 'i') "
"LEFT JOIN pg_catalog.pg_constraint c "
"ON (d.refclassid = c.tableoid "
"AND d.refobjid = c.oid) "
"WHERE i.indrelid = '%u'::pg_catalog.oid "
"ORDER BY indexname",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_indexname = PQfnumber(res, "indexname");
i_indexdef = PQfnumber(res, "indexdef");
i_indnkeys = PQfnumber(res, "indnkeys");
i_indkey = PQfnumber(res, "indkey");
i_indisclustered = PQfnumber(res, "indisclustered");
i_contype = PQfnumber(res, "contype");
i_conname = PQfnumber(res, "conname");
i_contableoid = PQfnumber(res, "contableoid");
i_conoid = PQfnumber(res, "conoid");
i_tablespace = PQfnumber(res, "tablespace");
i_options = PQfnumber(res, "options");
indxinfo = (IndxInfo *) malloc(ntups * sizeof(IndxInfo));
constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
for (j = 0; j < ntups; j++)
{
char contype;
indxinfo[j].dobj.objType = DO_INDEX;
indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
AssignDumpId(&indxinfo[j].dobj);
indxinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_indexname));
indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
indxinfo[j].indextable = tbinfo;
indxinfo[j].indexdef = strdup(PQgetvalue(res, j, i_indexdef));
indxinfo[j].indnkeys = atoi(PQgetvalue(res, j, i_indnkeys));
indxinfo[j].tablespace = strdup(PQgetvalue(res, j, i_tablespace));
indxinfo[j].options = strdup(PQgetvalue(res, j, i_options));
/*
* In pre-7.4 releases, indkeys may contain more entries than
* indnkeys says (since indnkeys will be 1 for a functional
* index). We don't actually care about this case since we don't
* examine indkeys except for indexes associated with PRIMARY and
* UNIQUE constraints, which are never functional indexes. But we
* have to allocate enough space to keep parseOidArray from
* complaining.
*/
indxinfo[j].indkeys = (Oid *) malloc(INDEX_MAX_KEYS * sizeof(Oid));
parseOidArray(PQgetvalue(res, j, i_indkey),
indxinfo[j].indkeys, INDEX_MAX_KEYS);
indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
contype = *(PQgetvalue(res, j, i_contype));
if (contype == 'p' || contype == 'u')
{
/*
* If we found a constraint matching the index, create an
* entry for it.
*
* In a pre-7.3 database, we take this path iff the index was
* marked indisprimary.
*/
constrinfo[j].dobj.objType = DO_CONSTRAINT;
constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
AssignDumpId(&constrinfo[j].dobj);
constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
constrinfo[j].contable = tbinfo;
constrinfo[j].condomain = NULL;
constrinfo[j].contype = contype;
constrinfo[j].condef = NULL;
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
constrinfo[j].coninherited = false;
constrinfo[j].separate = true;
indxinfo[j].indexconstraint = constrinfo[j].dobj.dumpId;
/* If pre-7.3 DB, better make sure table comes first */
addObjectDependency(&constrinfo[j].dobj,
tbinfo->dobj.dumpId);
}
else
{
/* Plain secondary index */
indxinfo[j].indexconstraint = 0;
}
}
PQclear(res);
}
destroyPQExpBuffer(query);
}
/*
* getConstraints
*
* Get info about constraints on dumpable tables.
*
* Currently handles foreign keys only.
* Unique and primary key constraints are handled with indexes,
* while check constraints are processed in getTableAttrs().
*/
/* Declared in pg_dump.h */
void
getConstraints(TableInfo tblinfo[], int numTables)
{
int i,
j;
ConstraintInfo *constrinfo;
PQExpBuffer query;
PGresult *res;
int i_condef,
i_contableoid,
i_conoid,
i_conname;
int ntups;
query = createPQExpBuffer();
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
if (tbinfo->ntrig == 0 || !tbinfo->dobj.dump)
continue;
if (g_verbose)
mpp_err_msg(logInfo, progname, "reading foreign key constraints for table \"%s\"\n",
tbinfo->dobj.name);
/*
* select table schema to ensure constraint expr is qualified if
* needed
*/
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
appendPQExpBuffer(query,
"SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) as condef "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
"AND contype = 'f'",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_contableoid = PQfnumber(res, "tableoid");
i_conoid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_condef = PQfnumber(res, "condef");
constrinfo = (ConstraintInfo *) malloc(ntups * sizeof(ConstraintInfo));
for (j = 0; j < ntups; j++)
{
constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
AssignDumpId(&constrinfo[j].dobj);
constrinfo[j].dobj.name = strdup(PQgetvalue(res, j, i_conname));
constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
constrinfo[j].contable = tbinfo;
constrinfo[j].condomain = NULL;
constrinfo[j].contype = 'f';
constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
constrinfo[j].conindex = 0;
constrinfo[j].coninherited = false;
constrinfo[j].separate = true;
}
PQclear(res);
}
destroyPQExpBuffer(query);
}
/*
* getRules
* get basic information about every rule in the system
*
* numRules is set to the number of rules read in
*/
RuleInfo *
getRules(int *numRules)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
RuleInfo *ruleinfo;
int i_tableoid;
int i_oid;
int i_rulename;
int i_ruletable;
int i_ev_type;
int i_is_instead;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT "
"tableoid, oid, rulename, "
"ev_class as ruletable, ev_type, is_instead "
"FROM pg_rewrite "
"ORDER BY oid");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numRules = ntups;
ruleinfo = (RuleInfo *) malloc(ntups * sizeof(RuleInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_rulename = PQfnumber(res, "rulename");
i_ruletable = PQfnumber(res, "ruletable");
i_ev_type = PQfnumber(res, "ev_type");
i_is_instead = PQfnumber(res, "is_instead");
for (i = 0; i < ntups; i++)
{
Oid ruletableoid;
ruleinfo[i].dobj.objType = DO_RULE;
ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&ruleinfo[i].dobj);
ruleinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_rulename));
ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
ruleinfo[i].ruletable = findTableByOid(ruletableoid);
if (ruleinfo[i].ruletable == NULL)
{
mpp_err_msg(logError, progname, "failed sanity check, parent table OID %u of pg_rewrite entry OID %u not found\n",
ruletableoid,
ruleinfo[i].dobj.catId.oid);
exit_nicely();
}
ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
if (ruleinfo[i].ruletable)
{
/*
* If the table is a view, force its ON SELECT rule to be sorted
* before the view itself --- this ensures that any dependencies
* for the rule affect the table's positioning. Other rules are
* forced to appear after their table.
*/
if (ruleinfo[i].ruletable->relkind == RELKIND_VIEW &&
ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
{
addObjectDependency(&ruleinfo[i].ruletable->dobj,
ruleinfo[i].dobj.dumpId);
/* We'll merge the rule into CREATE VIEW, if possible */
ruleinfo[i].separate = false;
}
else
{
addObjectDependency(&ruleinfo[i].dobj,
ruleinfo[i].ruletable->dobj.dumpId);
ruleinfo[i].separate = true;
}
}
else
ruleinfo[i].separate = true;
}
PQclear(res);
destroyPQExpBuffer(query);
return ruleinfo;
}
/*
* getTriggers
* get information about every trigger on a dumpable table
*
* Note: trigger data is not returned directly to the caller, but it
* does get entered into the DumpableObject tables.
*/
void
getTriggers(TableInfo tblinfo[], int numTables)
{
int i,
j;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
TriggerInfo *tginfo;
int i_tableoid,
i_oid,
i_tgname,
i_tgfname,
i_tgtype,
i_tgnargs,
i_tgargs,
i_tgisconstraint,
i_tgconstrname,
i_tgconstrrelid,
i_tgconstrrelname,
i_tgenabled,
i_tgdeferrable,
i_tginitdeferred;
int ntups;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
if (tbinfo->ntrig == 0 || !tbinfo->dobj.dump)
continue;
if (g_verbose)
mpp_err_msg(logInfo, progname, "reading triggers for table \"%s\"\n",
tbinfo->dobj.name);
/*
* select table schema to ensure regproc name is qualified if needed
*/
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
/*
* We ignore triggers that are tied to a foreign-key constraint
*/
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "
"tgtype, tgnargs, tgargs, tgenabled, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, tableoid, oid, "
"tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
"from pg_catalog.pg_trigger t "
"where tgrelid = '%u'::pg_catalog.oid "
"and (not tgisconstraint "
" OR NOT EXISTS"
" (SELECT 1 FROM pg_catalog.pg_depend d "
" JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
" WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
/*
* We may have less triggers than recorded due to having ignored
* foreign-key triggers
*/
if (ntups > tbinfo->ntrig)
{
mpp_err_msg(logError, progname, "expected %d triggers on table \"%s\" but found %d\n",
tbinfo->ntrig, tbinfo->dobj.name, ntups);
exit_nicely();
}
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_tgname = PQfnumber(res, "tgname");
i_tgfname = PQfnumber(res, "tgfname");
i_tgtype = PQfnumber(res, "tgtype");
i_tgnargs = PQfnumber(res, "tgnargs");
i_tgargs = PQfnumber(res, "tgargs");
i_tgisconstraint = PQfnumber(res, "tgisconstraint");
i_tgconstrname = PQfnumber(res, "tgconstrname");
i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
i_tgenabled = PQfnumber(res, "tgenabled");
i_tgdeferrable = PQfnumber(res, "tgdeferrable");
i_tginitdeferred = PQfnumber(res, "tginitdeferred");
tginfo = (TriggerInfo *) malloc(ntups * sizeof(TriggerInfo));
for (j = 0; j < ntups; j++)
{
tginfo[j].dobj.objType = DO_TRIGGER;
tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
AssignDumpId(&tginfo[j].dobj);
tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
tginfo[j].tgtable = tbinfo;
tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled)) == 't';
tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
if (tginfo[j].tgisconstraint)
{
tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
if (OidIsValid(tginfo[j].tgconstrrelid))
{
if (PQgetisnull(res, j, i_tgconstrrelname))
{
mpp_err_msg(logError, progname, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
tginfo[j].dobj.name, tbinfo->dobj.name,
tginfo[j].tgconstrrelid);
exit_nicely();
}
tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
}
else
tginfo[j].tgconstrrelname = NULL;
}
else
{
tginfo[j].tgconstrname = NULL;
tginfo[j].tgconstrrelid = InvalidOid;
tginfo[j].tgconstrrelname = NULL;
}
}
PQclear(res);
}
destroyPQExpBuffer(query);
}
/*
* getProcLangs
* get basic information about every procedural language in the system
*
* numProcLangs is set to the number of langs read in
*
* NB: this must run after getFuncs() because we assume we can do
* findFuncByOid().
*/
ProcLangInfo *
getProcLangs(int *numProcLangs)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
ProcLangInfo *planginfo;
int i_tableoid;
int i_oid;
int i_lanname;
int i_lanpltrusted;
int i_lanplcallfoid;
int i_lanvalidator;
int i_lanacl;
int i_lanowner;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* Languages are owned by the bootstrap superuser, OID 10 */
appendPQExpBuffer(query, "SELECT tableoid, oid, *, "
"(%s '10') as lanowner "
"FROM pg_language "
"WHERE lanispl "
"ORDER BY oid",
username_subquery);
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numProcLangs = ntups;
planginfo = (ProcLangInfo *) malloc(ntups * sizeof(ProcLangInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_lanname = PQfnumber(res, "lanname");
i_lanpltrusted = PQfnumber(res, "lanpltrusted");
i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
/* these may fail and return -1: */
i_lanvalidator = PQfnumber(res, "lanvalidator");
i_lanacl = PQfnumber(res, "lanacl");
i_lanowner = PQfnumber(res, "lanowner");
for (i = 0; i < ntups; i++)
{
planginfo[i].dobj.objType = DO_PROCLANG;
planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&planginfo[i].dobj);
planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
if (i_lanvalidator >= 0)
planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
else
planginfo[i].lanvalidator = InvalidOid;
if (i_lanacl >= 0)
planginfo[i].lanacl = strdup(PQgetvalue(res, i, i_lanacl));
else
planginfo[i].lanacl = strdup("{=U}");
if (i_lanowner >= 0)
planginfo[i].lanowner = strdup(PQgetvalue(res, i, i_lanowner));
else
planginfo[i].lanowner = strdup("");
}
PQclear(res);
destroyPQExpBuffer(query);
return planginfo;
}
/*
* getCasts
* get basic information about every cast in the system
*
* numCasts is set to the number of casts read in
*/
CastInfo *
getCasts(int *numCasts)
{
PGresult *res;
int ntups;
int i;
PQExpBuffer query = createPQExpBuffer();
CastInfo *castinfo;
int i_tableoid;
int i_oid;
int i_castsource;
int i_casttarget;
int i_castfunc;
int i_castcontext;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
appendPQExpBuffer(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext "
"FROM pg_cast ORDER BY 3,4");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
*numCasts = ntups;
castinfo = (CastInfo *) malloc(ntups * sizeof(CastInfo));
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_castsource = PQfnumber(res, "castsource");
i_casttarget = PQfnumber(res, "casttarget");
i_castfunc = PQfnumber(res, "castfunc");
i_castcontext = PQfnumber(res, "castcontext");
for (i = 0; i < ntups; i++)
{
PQExpBufferData namebuf;
TypeInfo *sTypeInfo;
TypeInfo *tTypeInfo;
castinfo[i].dobj.objType = DO_CAST;
castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&castinfo[i].dobj);
castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
/*
* Try to name cast as concatenation of typnames. This is only used
* for purposes of sorting. If we fail to find either type, the name
* will be an empty string.
*/
initPQExpBuffer(&namebuf);
sTypeInfo = findTypeByOid(castinfo[i].castsource);
tTypeInfo = findTypeByOid(castinfo[i].casttarget);
if (sTypeInfo && tTypeInfo)
appendPQExpBuffer(&namebuf, "%s %s",
sTypeInfo->dobj.name, tTypeInfo->dobj.name);
castinfo[i].dobj.name = namebuf.data;
if (OidIsValid(castinfo[i].castfunc))
{
/*
* We need to make a dependency to ensure the function will be
* dumped first. (In 7.3 and later the regular dependency
* mechanism will handle this for us.)
*/
FuncInfo *funcInfo;
funcInfo = findFuncByOid(castinfo[i].castfunc);
if (funcInfo)
addObjectDependency(&castinfo[i].dobj,
funcInfo->dobj.dumpId);
}
}
PQclear(res);
destroyPQExpBuffer(query);
return castinfo;
}
/*
* getTableAttrs -
* for each interesting table, read info about its attributes
* (names, types, default values, CHECK constraints, etc)
*
* This is implemented in a very inefficient way right now, looping
* through the tblinfo and doing a join per table to find the attrs and their
* types. However, because we want type names and so forth to be named
* relative to the schema of each table, we couldn't do it in just one
* query. (Maybe one query per schema?)
*
* modifies tblinfo
*/
void
getTableAttrs(TableInfo *tblinfo, int numTables)
{
int i,
j;
PQExpBuffer q = createPQExpBuffer();
int i_attnum;
int i_attname;
int i_atttypname;
int i_atttypmod;
int i_attstattarget;
int i_attstorage;
int i_typstorage;
int i_attnotnull;
int i_atthasdef;
int i_attisdropped;
int i_attislocal;
int i_attencoding;
PGresult *res;
int ntups;
bool hasdefaults;
for (i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
/* Don't bother to collect info for sequences */
if (tbinfo->relkind == RELKIND_SEQUENCE)
continue;
/* Don't bother with uninteresting tables, either */
if (!tbinfo->interesting)
continue;
/*
* Make sure we are in proper schema for this table; this allows
* correct retrieval of formatted type names and default exprs
*/
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
/* find all the user attributes and their types */
/*
* we must read the attribute names in attribute number order! because
* we will use the attnum to index into the attnames array later. We
* actually ask to order by "attrelid, attnum" because (at least up to
* 7.3) the planner is not smart enough to realize it needn't re-sort
* the output of an indexscan on pg_attribute_relid_attnum_index.
*/
if (g_verbose)
mpp_err_msg(logInfo, progname, "finding the columns and types of table \"%s\"\n",
tbinfo->dobj.name);
resetPQExpBuffer(q);
/* need left join here to not fail on dropped columns ... */
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) as atttypname ");
if (g_gp_supportsAttributeEncoding)
appendPQExpBuffer(q, ", pg_catalog.array_to_string(e.attoptions, ',') as attencoding ");
appendPQExpBuffer(q, "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t "
"on a.atttypid = t.oid ");
if (g_gp_supportsAttributeEncoding)
appendPQExpBuffer(q, " LEFT OUTER JOIN pg_catalog.pg_attribute_encoding e ON e.attrelid = a.attrelid AND e.attnum = a.attnum ");
appendPQExpBuffer(q, "where a.attrelid = '%u'::pg_catalog.oid "
"and a.attnum > 0::pg_catalog.int2 "
"order by a.attrelid, a.attnum",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, q->data);
check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
i_attnum = PQfnumber(res, "attnum");
i_attname = PQfnumber(res, "attname");
i_atttypname = PQfnumber(res, "atttypname");
i_atttypmod = PQfnumber(res, "atttypmod");
i_attstattarget = PQfnumber(res, "attstattarget");
i_attstorage = PQfnumber(res, "attstorage");
i_typstorage = PQfnumber(res, "typstorage");
i_attnotnull = PQfnumber(res, "attnotnull");
i_atthasdef = PQfnumber(res, "atthasdef");
i_attisdropped = PQfnumber(res, "attisdropped");
i_attislocal = PQfnumber(res, "attislocal");
i_attencoding = PQfnumber(res, "attencoding");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attencoding = (char **)malloc(ntups * sizeof(char *));
hasdefaults = false;
for (j = 0; j < ntups; j++)
{
if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
{
mpp_err_msg(logError, progname, "invalid column numbering in table \"%s\"\n",
tbinfo->dobj.name);
exit_nicely();
}
tbinfo->attnames[j] = strdup(PQgetvalue(res, j, i_attname));
tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
/* these flags will be set in flagInhAttrs() */
tbinfo->inhAttrs[j] = false;
tbinfo->inhAttrDef[j] = false;
tbinfo->inhNotNull[j] = false;
/* column storage attributes */
if (g_gp_supportsAttributeEncoding && !PQgetisnull(res, j, i_attencoding))
tbinfo->attencoding[j] = strdup(PQgetvalue(res, j, i_attencoding));
else
tbinfo->attencoding[j] = NULL;
}
PQclear(res);
/*
* Get info about column defaults
*/
if (hasdefaults)
{
AttrDefInfo *attrdefs;
int numDefaults;
if (g_verbose)
mpp_err_msg(logInfo, progname, "finding default expressions of table \"%s\"\n",
tbinfo->dobj.name);
resetPQExpBuffer(q);
appendPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
"pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
"FROM pg_catalog.pg_attrdef "
"WHERE adrelid = '%u'::pg_catalog.oid",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, q->data);
check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
numDefaults = PQntuples(res);
attrdefs = (AttrDefInfo *) malloc(numDefaults * sizeof(AttrDefInfo));
for (j = 0; j < numDefaults; j++)
{
int adnum;
attrdefs[j].dobj.objType = DO_ATTRDEF;
attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
AssignDumpId(&attrdefs[j].dobj);
attrdefs[j].adtable = tbinfo;
attrdefs[j].adnum = adnum = atoi(PQgetvalue(res, j, 2));
attrdefs[j].adef_expr = strdup(PQgetvalue(res, j, 3));
attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
attrdefs[j].dobj.dump = tbinfo->dobj.dump;
/*
* Defaults on a VIEW must always be dumped as separate ALTER
* TABLE commands. Defaults on regular tables are dumped as
* part of the CREATE TABLE if possible. To check if it's
* safe, we mark the default as needing to appear before the
* CREATE.
*/
if (tbinfo->relkind == RELKIND_VIEW)
{
attrdefs[j].separate = true;
/* needed in case pre-7.3 DB: */
addObjectDependency(&attrdefs[j].dobj,
tbinfo->dobj.dumpId);
}
else
{
attrdefs[j].separate = false;
addObjectDependency(&tbinfo->dobj,
attrdefs[j].dobj.dumpId);
}
if (adnum <= 0 || adnum > ntups)
{
mpp_err_msg(logError, progname, "invalid adnum value %d for table \"%s\"\n",
adnum, tbinfo->dobj.name);
exit_nicely();
}
tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
}
PQclear(res);
}
/*
* Get info about table CHECK constraints
*/
if (tbinfo->ncheck > 0)
{
ConstraintInfo *constrs;
int numConstrs;
if (g_verbose)
mpp_err_msg(logInfo, progname, "finding check constraints for table \"%s\"\n",
tbinfo->dobj.name);
resetPQExpBuffer(q);
appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc "
"FROM pg_catalog.pg_constraint "
"WHERE conrelid = '%u'::pg_catalog.oid "
" AND contype = 'c' "
"ORDER BY conname",
tbinfo->dobj.catId.oid);
res = PQexec(g_conn, q->data);
check_sql_result(res, g_conn, q->data, PGRES_TUPLES_OK);
numConstrs = PQntuples(res);
if (numConstrs != tbinfo->ncheck)
{
mpp_err_msg(logError, progname, "expected %d check constraints on table \"%s\" but found %d\n",
tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
mpp_err_msg(logError, progname, "(The system catalogs might be corrupted.)\n");
exit_nicely();
}
constrs = (ConstraintInfo *) malloc(numConstrs * sizeof(ConstraintInfo));
tbinfo->checkexprs = constrs;
for (j = 0; j < numConstrs; j++)
{
constrs[j].dobj.objType = DO_CONSTRAINT;
constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
AssignDumpId(&constrs[j].dobj);
constrs[j].dobj.name = strdup(PQgetvalue(res, j, 2));
constrs[j].dobj.namespace = tbinfo->dobj.namespace;
constrs[j].contable = tbinfo;
constrs[j].condomain = NULL;
constrs[j].contype = 'c';
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
constrs[j].conindex = 0;
constrs[j].coninherited = false;
constrs[j].separate = false;
constrs[j].dobj.dump = tbinfo->dobj.dump;
/*
* Mark the constraint as needing to appear before the table
* --- this is so that any other dependencies of the
* constraint will be emitted before we try to create the
* table.
*/
addObjectDependency(&tbinfo->dobj,
constrs[j].dobj.dumpId);
/*
* If the constraint is inherited, this will be detected
* later. We also detect later if the constraint must be
* split out from the table definition.
*/
}
PQclear(res);
}
}
destroyPQExpBuffer(q);
}
/*
* testSqlMedSupport - tests whether or not the current GP database includes
* support for SQL/MED and foreign tables.
*/
bool
testSqlMedSupport(void)
{
PQExpBuffer query;
PGresult *res;
bool isSupported;
query = createPQExpBuffer();
appendPQExpBuffer(query, "SELECT 1 FROM pg_class WHERE relname = 'pg_foreign_server' and relnamespace = 11;");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
isSupported = (PQntuples(res) == 1);
PQclear(res);
destroyPQExpBuffer(query);
return isSupported;
}
bool
testExtProtocolSupport(void)
{
PQExpBuffer query;
PGresult *res;
bool isSupported;
query = createPQExpBuffer();
appendPQExpBuffer(query, "SELECT 1 FROM pg_class WHERE relname = 'pg_extprotocol' and relnamespace = 11;");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
isSupported = (PQntuples(res) == 1);
PQclear(res);
destroyPQExpBuffer(query);
return isSupported;
}