mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-06 12:59:20 +02:00
Handle arrays and ranges in pg_upgrade's test for non-upgradable types.
pg_upgrade needs to check whether certain non-upgradable data types appear anywhere on-disk in the source cluster. It knew that it has to check for these types being contained inside domains and composite types; but it somehow overlooked that they could be contained in arrays and ranges, too. Extend the existing recursive-containment query to handle those cases. We probably should have noticed this oversight while working on commit0ccfc2822
and follow-ups, but we failed to :-(. The whole thing's possibly a bit overdesigned, since we don't really expect that any of these types will appear on disk; but if we're going to the effort of doing a recursive search then it's silly not to cover all the possibilities. While at it, refactor so that we have only one copy of the search logic, not three-and-counting. Also, to keep the branches looking more alike, back-patch the output wording change of commit1634d3615
. Back-patch to all supported branches. Discussion: https://postgr.es/m/31473.1573412838@sss.pgh.pa.us
This commit is contained in:
parent
5f2cfe7f5f
commit
1cd57b05ef
@ -100,27 +100,27 @@ new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* old_9_3_check_for_line_data_type_usage()
|
* check_for_data_type_usage
|
||||||
* 9.3 -> 9.4
|
* Detect whether there are any stored columns depending on the given type
|
||||||
* Fully implement the 'line' data type in 9.4, which previously returned
|
*
|
||||||
* "not enabled" by default and was only functionally enabled with a
|
* If so, write a report to the given file name, and return true.
|
||||||
* compile-time switch; 9.4 "line" has different binary and text
|
*
|
||||||
* representation formats; checks tables and indexes.
|
* We check for the type in tables, matviews, and indexes, but not views;
|
||||||
|
* there's no storage involved in a view.
|
||||||
*/
|
*/
|
||||||
void
|
static bool
|
||||||
old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
||||||
|
char *output_path)
|
||||||
{
|
{
|
||||||
int dbnum;
|
|
||||||
FILE *script = NULL;
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
char output_path[MAXPGPATH];
|
FILE *script = NULL;
|
||||||
|
int dbnum;
|
||||||
prep_status("Checking for incompatible \"line\" data type");
|
|
||||||
|
|
||||||
snprintf(output_path, sizeof(output_path), "tables_using_line.txt");
|
|
||||||
|
|
||||||
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
|
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
|
||||||
{
|
{
|
||||||
|
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
|
||||||
|
PGconn *conn = connectToServer(cluster, active_db->db_name);
|
||||||
|
PQExpBufferData querybuf;
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
bool db_used = false;
|
bool db_used = false;
|
||||||
int ntups;
|
int ntups;
|
||||||
@ -128,50 +128,68 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
|||||||
int i_nspname,
|
int i_nspname,
|
||||||
i_relname,
|
i_relname,
|
||||||
i_attname;
|
i_attname;
|
||||||
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
|
|
||||||
PGconn *conn = connectToServer(cluster, active_db->db_name);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The pg_catalog.line type may be wrapped in a domain or composite
|
* The type of interest might be wrapped in a domain, array,
|
||||||
* type, or both (9.3 did not allow domains on composite types, but
|
* composite, or range, and these container types can be nested (to
|
||||||
* there may be multi-level composite type). To detect these cases
|
* varying extents depending on server version, but that's not of
|
||||||
* we need a recursive CTE.
|
* concern here). To handle all these cases we need a recursive CTE.
|
||||||
*/
|
*/
|
||||||
res = executeQueryOrDie(conn,
|
initPQExpBuffer(&querybuf);
|
||||||
"WITH RECURSIVE oids AS ( "
|
appendPQExpBuffer(&querybuf,
|
||||||
/* the pg_catalog.line type itself */
|
"WITH RECURSIVE oids AS ( "
|
||||||
" SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid "
|
/* the target type itself */
|
||||||
" UNION ALL "
|
" SELECT '%s'::pg_catalog.regtype AS oid "
|
||||||
" SELECT * FROM ( "
|
" UNION ALL "
|
||||||
/* domains on the type */
|
" SELECT * FROM ( "
|
||||||
" WITH x AS (SELECT oid FROM oids) "
|
/* inner WITH because we can only reference the CTE once */
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
|
" WITH x AS (SELECT oid FROM oids) "
|
||||||
" UNION "
|
/* domains on any type selected so far */
|
||||||
/* composite types containing the type */
|
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
|
" UNION ALL "
|
||||||
" WHERE t.typtype = 'c' AND "
|
/* arrays over any type selected so far */
|
||||||
" t.oid = c.reltype AND "
|
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typelem = x.oid AND typtype = 'b' "
|
||||||
" c.oid = a.attrelid AND "
|
" UNION ALL "
|
||||||
" NOT a.attisdropped AND "
|
/* composite types containing any type selected so far */
|
||||||
" a.atttypid = x.oid "
|
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
|
||||||
" ) foo "
|
" WHERE t.typtype = 'c' AND "
|
||||||
") "
|
" t.oid = c.reltype AND "
|
||||||
"SELECT n.nspname, c.relname, a.attname "
|
" c.oid = a.attrelid AND "
|
||||||
"FROM pg_catalog.pg_class c, "
|
" NOT a.attisdropped AND "
|
||||||
" pg_catalog.pg_namespace n, "
|
" a.atttypid = x.oid ",
|
||||||
" pg_catalog.pg_attribute a "
|
typename);
|
||||||
"WHERE c.oid = a.attrelid AND "
|
|
||||||
" NOT a.attisdropped AND "
|
/* Ranges came in in 9.2 */
|
||||||
" a.atttypid IN (SELECT oid FROM oids) AND "
|
if (GET_MAJOR_VERSION(cluster->major_version) >= 902)
|
||||||
" c.relkind IN ("
|
appendPQExpBuffer(&querybuf,
|
||||||
CppAsString2(RELKIND_RELATION) ", "
|
" UNION ALL "
|
||||||
CppAsString2(RELKIND_MATVIEW) ", "
|
/* ranges containing any type selected so far */
|
||||||
CppAsString2(RELKIND_INDEX) ") AND "
|
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_range r, x "
|
||||||
" c.relnamespace = n.oid AND "
|
" WHERE t.typtype = 'r' AND r.rngtypid = t.oid AND r.rngsubtype = x.oid");
|
||||||
|
|
||||||
|
appendPQExpBuffer(&querybuf,
|
||||||
|
" ) foo "
|
||||||
|
") "
|
||||||
|
/* now look for stored columns of any such type */
|
||||||
|
"SELECT n.nspname, c.relname, a.attname "
|
||||||
|
"FROM pg_catalog.pg_class c, "
|
||||||
|
" pg_catalog.pg_namespace n, "
|
||||||
|
" pg_catalog.pg_attribute a "
|
||||||
|
"WHERE c.oid = a.attrelid AND "
|
||||||
|
" NOT a.attisdropped AND "
|
||||||
|
" a.atttypid IN (SELECT oid FROM oids) AND "
|
||||||
|
" c.relkind IN ("
|
||||||
|
CppAsString2(RELKIND_RELATION) ", "
|
||||||
|
CppAsString2(RELKIND_MATVIEW) ", "
|
||||||
|
CppAsString2(RELKIND_INDEX) ") AND "
|
||||||
|
" c.relnamespace = n.oid AND "
|
||||||
/* exclude possible orphaned temp tables */
|
/* exclude possible orphaned temp tables */
|
||||||
" n.nspname !~ '^pg_temp_' AND "
|
" n.nspname !~ '^pg_temp_' AND "
|
||||||
" n.nspname !~ '^pg_toast_temp_' AND "
|
" n.nspname !~ '^pg_toast_temp_' AND "
|
||||||
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
|
/* exclude system catalogs, too */
|
||||||
|
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
|
||||||
|
|
||||||
|
res = executeQueryOrDie(conn, "%s", querybuf.data);
|
||||||
|
|
||||||
ntups = PQntuples(res);
|
ntups = PQntuples(res);
|
||||||
i_nspname = PQfnumber(res, "nspname");
|
i_nspname = PQfnumber(res, "nspname");
|
||||||
@ -185,7 +203,7 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
|||||||
strerror(errno));
|
strerror(errno));
|
||||||
if (!db_used)
|
if (!db_used)
|
||||||
{
|
{
|
||||||
fprintf(script, "Database: %s\n", active_db->db_name);
|
fprintf(script, "In database: %s\n", active_db->db_name);
|
||||||
db_used = true;
|
db_used = true;
|
||||||
}
|
}
|
||||||
fprintf(script, " %s.%s.%s\n",
|
fprintf(script, " %s.%s.%s\n",
|
||||||
@ -196,13 +214,36 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
|||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
|
termPQExpBuffer(&querybuf);
|
||||||
|
|
||||||
PQfinish(conn);
|
PQfinish(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script)
|
if (script)
|
||||||
fclose(script);
|
fclose(script);
|
||||||
|
|
||||||
if (found)
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* old_9_3_check_for_line_data_type_usage()
|
||||||
|
* 9.3 -> 9.4
|
||||||
|
* Fully implement the 'line' data type in 9.4, which previously returned
|
||||||
|
* "not enabled" by default and was only functionally enabled with a
|
||||||
|
* compile-time switch; as of 9.4 "line" has a different on-disk
|
||||||
|
* representation format.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
||||||
|
{
|
||||||
|
char output_path[MAXPGPATH];
|
||||||
|
|
||||||
|
prep_status("Checking for incompatible \"line\" data type");
|
||||||
|
|
||||||
|
snprintf(output_path, sizeof(output_path), "tables_using_line.txt");
|
||||||
|
|
||||||
|
if (check_for_data_type_usage(cluster, "pg_catalog.line", output_path))
|
||||||
{
|
{
|
||||||
pg_log(PG_REPORT, "fatal\n");
|
pg_log(PG_REPORT, "fatal\n");
|
||||||
pg_fatal("Your installation contains the \"line\" data type in user tables. This\n"
|
pg_fatal("Your installation contains the \"line\" data type in user tables. This\n"
|
||||||
@ -229,105 +270,17 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
|
|||||||
* mid-upgrade. Worse, if there's a matview with such a column, the
|
* mid-upgrade. Worse, if there's a matview with such a column, the
|
||||||
* DDL reload will silently change it to "text" which won't match the
|
* DDL reload will silently change it to "text" which won't match the
|
||||||
* on-disk storage (which is like "cstring"). So we *must* reject that.
|
* on-disk storage (which is like "cstring"). So we *must* reject that.
|
||||||
* Also check composite types and domains on the "unknwown" type (even
|
|
||||||
* combinations of both), in case they are used for table columns.
|
|
||||||
* We needn't check indexes, because "unknown" has no opclasses.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
|
old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
|
||||||
{
|
{
|
||||||
int dbnum;
|
|
||||||
FILE *script = NULL;
|
|
||||||
bool found = false;
|
|
||||||
char output_path[MAXPGPATH];
|
char output_path[MAXPGPATH];
|
||||||
|
|
||||||
prep_status("Checking for invalid \"unknown\" user columns");
|
prep_status("Checking for invalid \"unknown\" user columns");
|
||||||
|
|
||||||
snprintf(output_path, sizeof(output_path), "tables_using_unknown.txt");
|
snprintf(output_path, sizeof(output_path), "tables_using_unknown.txt");
|
||||||
|
|
||||||
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
|
if (check_for_data_type_usage(cluster, "pg_catalog.unknown", output_path))
|
||||||
{
|
|
||||||
PGresult *res;
|
|
||||||
bool db_used = false;
|
|
||||||
int ntups;
|
|
||||||
int rowno;
|
|
||||||
int i_nspname,
|
|
||||||
i_relname,
|
|
||||||
i_attname;
|
|
||||||
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
|
|
||||||
PGconn *conn = connectToServer(cluster, active_db->db_name);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The pg_catalog.unknown type may be wrapped in a domain or composite
|
|
||||||
* type, or both (9.3 did not allow domains on composite types, but
|
|
||||||
* there may be multi-level composite type). To detect these cases
|
|
||||||
* we need a recursive CTE.
|
|
||||||
*/
|
|
||||||
res = executeQueryOrDie(conn,
|
|
||||||
"WITH RECURSIVE oids AS ( "
|
|
||||||
/* the pg_catalog.unknown type itself */
|
|
||||||
" SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid "
|
|
||||||
" UNION ALL "
|
|
||||||
" SELECT * FROM ( "
|
|
||||||
/* domains on the type */
|
|
||||||
" WITH x AS (SELECT oid FROM oids) "
|
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
|
|
||||||
" UNION "
|
|
||||||
/* composite types containing the type */
|
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
|
|
||||||
" WHERE t.typtype = 'c' AND "
|
|
||||||
" t.oid = c.reltype AND "
|
|
||||||
" c.oid = a.attrelid AND "
|
|
||||||
" NOT a.attisdropped AND "
|
|
||||||
" a.atttypid = x.oid "
|
|
||||||
" ) foo "
|
|
||||||
") "
|
|
||||||
"SELECT n.nspname, c.relname, a.attname "
|
|
||||||
"FROM pg_catalog.pg_class c, "
|
|
||||||
" pg_catalog.pg_namespace n, "
|
|
||||||
" pg_catalog.pg_attribute a "
|
|
||||||
"WHERE c.oid = a.attrelid AND "
|
|
||||||
" NOT a.attisdropped AND "
|
|
||||||
" a.atttypid IN (SELECT oid FROM oids) AND "
|
|
||||||
" c.relkind IN ("
|
|
||||||
CppAsString2(RELKIND_RELATION) ", "
|
|
||||||
CppAsString2(RELKIND_MATVIEW) ") AND "
|
|
||||||
" c.relnamespace = n.oid AND "
|
|
||||||
/* exclude possible orphaned temp tables */
|
|
||||||
" n.nspname !~ '^pg_temp_' AND "
|
|
||||||
" n.nspname !~ '^pg_toast_temp_' AND "
|
|
||||||
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
|
|
||||||
|
|
||||||
ntups = PQntuples(res);
|
|
||||||
i_nspname = PQfnumber(res, "nspname");
|
|
||||||
i_relname = PQfnumber(res, "relname");
|
|
||||||
i_attname = PQfnumber(res, "attname");
|
|
||||||
for (rowno = 0; rowno < ntups; rowno++)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
|
|
||||||
pg_fatal("could not open file \"%s\": %s\n", output_path,
|
|
||||||
strerror(errno));
|
|
||||||
if (!db_used)
|
|
||||||
{
|
|
||||||
fprintf(script, "Database: %s\n", active_db->db_name);
|
|
||||||
db_used = true;
|
|
||||||
}
|
|
||||||
fprintf(script, " %s.%s.%s\n",
|
|
||||||
PQgetvalue(res, rowno, i_nspname),
|
|
||||||
PQgetvalue(res, rowno, i_relname),
|
|
||||||
PQgetvalue(res, rowno, i_attname));
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
PQfinish(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script)
|
|
||||||
fclose(script);
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
{
|
{
|
||||||
pg_log(PG_REPORT, "fatal\n");
|
pg_log(PG_REPORT, "fatal\n");
|
||||||
pg_fatal("Your installation contains the \"unknown\" data type in user tables. This\n"
|
pg_fatal("Your installation contains the \"unknown\" data type in user tables. This\n"
|
||||||
@ -459,105 +412,18 @@ old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
|
|||||||
* which does affect the storage (name is by-ref, but not varlena). This
|
* which does affect the storage (name is by-ref, but not varlena). This
|
||||||
* means user tables using sql_identifier for columns are broken because
|
* means user tables using sql_identifier for columns are broken because
|
||||||
* the on-disk format is different.
|
* the on-disk format is different.
|
||||||
*
|
|
||||||
* We need to check all objects that might store sql_identifier on disk,
|
|
||||||
* i.e. tables, matviews and indexes. Also check composite types in case
|
|
||||||
* they are used in this context.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
old_11_check_for_sql_identifier_data_type_usage(ClusterInfo *cluster)
|
old_11_check_for_sql_identifier_data_type_usage(ClusterInfo *cluster)
|
||||||
{
|
{
|
||||||
int dbnum;
|
|
||||||
FILE *script = NULL;
|
|
||||||
bool found = false;
|
|
||||||
char output_path[MAXPGPATH];
|
char output_path[MAXPGPATH];
|
||||||
|
|
||||||
prep_status("Checking for invalid \"sql_identifier\" user columns");
|
prep_status("Checking for invalid \"sql_identifier\" user columns");
|
||||||
|
|
||||||
snprintf(output_path, sizeof(output_path), "tables_using_sql_identifier.txt");
|
snprintf(output_path, sizeof(output_path), "tables_using_sql_identifier.txt");
|
||||||
|
|
||||||
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
|
if (check_for_data_type_usage(cluster, "information_schema.sql_identifier",
|
||||||
{
|
output_path))
|
||||||
PGresult *res;
|
|
||||||
bool db_used = false;
|
|
||||||
int ntups;
|
|
||||||
int rowno;
|
|
||||||
int i_nspname,
|
|
||||||
i_relname,
|
|
||||||
i_attname;
|
|
||||||
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
|
|
||||||
PGconn *conn = connectToServer(cluster, active_db->db_name);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need the recursive CTE because the sql_identifier may be wrapped
|
|
||||||
* either in a domain or composite type, or both (in arbitrary order).
|
|
||||||
*/
|
|
||||||
res = executeQueryOrDie(conn,
|
|
||||||
"WITH RECURSIVE oids AS ( "
|
|
||||||
/* the sql_identifier type itself */
|
|
||||||
" SELECT 'information_schema.sql_identifier'::pg_catalog.regtype AS oid "
|
|
||||||
" UNION ALL "
|
|
||||||
" SELECT * FROM ( "
|
|
||||||
/* domains on the type */
|
|
||||||
" WITH x AS (SELECT oid FROM oids) "
|
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
|
|
||||||
" UNION "
|
|
||||||
/* composite types containing the type */
|
|
||||||
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
|
|
||||||
" WHERE t.typtype = 'c' AND "
|
|
||||||
" t.oid = c.reltype AND "
|
|
||||||
" c.oid = a.attrelid AND "
|
|
||||||
" NOT a.attisdropped AND "
|
|
||||||
" a.atttypid = x.oid "
|
|
||||||
" ) foo "
|
|
||||||
") "
|
|
||||||
"SELECT n.nspname, c.relname, a.attname "
|
|
||||||
"FROM pg_catalog.pg_class c, "
|
|
||||||
" pg_catalog.pg_namespace n, "
|
|
||||||
" pg_catalog.pg_attribute a "
|
|
||||||
"WHERE c.oid = a.attrelid AND "
|
|
||||||
" NOT a.attisdropped AND "
|
|
||||||
" a.atttypid IN (SELECT oid FROM oids) AND "
|
|
||||||
" c.relkind IN ("
|
|
||||||
CppAsString2(RELKIND_RELATION) ", "
|
|
||||||
CppAsString2(RELKIND_MATVIEW) ", "
|
|
||||||
CppAsString2(RELKIND_INDEX) ") AND "
|
|
||||||
" c.relnamespace = n.oid AND "
|
|
||||||
/* exclude possible orphaned temp tables */
|
|
||||||
" n.nspname !~ '^pg_temp_' AND "
|
|
||||||
" n.nspname !~ '^pg_toast_temp_' AND "
|
|
||||||
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
|
|
||||||
|
|
||||||
ntups = PQntuples(res);
|
|
||||||
i_nspname = PQfnumber(res, "nspname");
|
|
||||||
i_relname = PQfnumber(res, "relname");
|
|
||||||
i_attname = PQfnumber(res, "attname");
|
|
||||||
for (rowno = 0; rowno < ntups; rowno++)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
|
|
||||||
pg_fatal("could not open file \"%s\": %s\n", output_path,
|
|
||||||
strerror(errno));
|
|
||||||
if (!db_used)
|
|
||||||
{
|
|
||||||
fprintf(script, "Database: %s\n", active_db->db_name);
|
|
||||||
db_used = true;
|
|
||||||
}
|
|
||||||
fprintf(script, " %s.%s.%s\n",
|
|
||||||
PQgetvalue(res, rowno, i_nspname),
|
|
||||||
PQgetvalue(res, rowno, i_relname),
|
|
||||||
PQgetvalue(res, rowno, i_attname));
|
|
||||||
}
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
PQfinish(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script)
|
|
||||||
fclose(script);
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
{
|
{
|
||||||
pg_log(PG_REPORT, "fatal\n");
|
pg_log(PG_REPORT, "fatal\n");
|
||||||
pg_fatal("Your installation contains the \"sql_identifier\" data type in user tables\n"
|
pg_fatal("Your installation contains the \"sql_identifier\" data type in user tables\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user