Skip to content

Commit 6918d90

Browse files
committed
pg_upgrade_ACL_check_v9.patch
1 parent 350f477 commit 6918d90

File tree

3 files changed

+374
-0
lines changed

3 files changed

+374
-0
lines changed

Diff for: src/bin/pg_upgrade/check.c

+221
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
static void check_new_cluster_is_empty(void);
1818
static void check_databases_are_compatible(void);
19+
static void check_for_changed_signatures(void);
1920
static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb);
2021
static bool equivalent_locale(int category, const char *loca, const char *locb);
2122
static void check_is_install_user(ClusterInfo *cluster);
@@ -142,6 +143,8 @@ check_and_dump_old_cluster(bool live_check)
142143
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804)
143144
new_9_0_populate_pg_largeobject_metadata(&old_cluster, true);
144145

146+
get_non_default_acl_infos(&old_cluster);
147+
145148
/*
146149
* While not a check option, we do this now because this is the only time
147150
* the old server is running.
@@ -161,6 +164,7 @@ check_new_cluster(void)
161164

162165
check_new_cluster_is_empty();
163166
check_databases_are_compatible();
167+
check_for_changed_signatures();
164168

165169
check_loadable_libraries();
166170

@@ -443,6 +447,223 @@ check_databases_are_compatible(void)
443447
}
444448
}
445449

450+
/*
451+
* Find the location of the last dot, return NULL if not found.
452+
*/
453+
static char *
454+
last_dot_location(const char *identity)
455+
{
456+
const char *p,
457+
*ret = NULL;
458+
459+
for (p = identity; *p; p++)
460+
if (*p == '.')
461+
ret = p;
462+
return unconstify(char *, ret);
463+
}
464+
465+
/*
466+
* check_for_changed_signatures()
467+
*
468+
* Check that the old cluster doesn't have non-default ACL's for system objects
469+
* (relations, attributes, functions and procedures) which have different
470+
* signatures in the new cluster. Otherwise generate revoke_objects.sql.
471+
*/
472+
static void
473+
check_for_changed_signatures(void)
474+
{
475+
PGconn *conn;
476+
char subquery[QUERY_ALLOC];
477+
PGresult *res;
478+
int ntups;
479+
int i_obj_ident;
480+
int dbnum;
481+
bool need_check = false;
482+
FILE *script = NULL;
483+
bool found_changed = false;
484+
char output_path[MAXPGPATH];
485+
486+
prep_status("Checking for system objects with non-default ACL");
487+
488+
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
489+
if (old_cluster.dbarr.dbs[dbnum].non_def_acl_arr.nacls > 0)
490+
{
491+
need_check = true;
492+
break;
493+
}
494+
/*
495+
* The old cluster doesn't have system objects with non-default ACL so
496+
* quickly exit.
497+
*/
498+
if (!need_check)
499+
{
500+
check_ok();
501+
return;
502+
}
503+
504+
snprintf(output_path, sizeof(output_path), "revoke_objects.sql");
505+
506+
snprintf(subquery, sizeof(subquery),
507+
/* Get system relations which created in pg_catalog */
508+
"SELECT 'pg_class'::regclass classid, oid objid, 0 objsubid "
509+
"FROM pg_catalog.pg_class "
510+
"WHERE relnamespace = 'pg_catalog'::regnamespace "
511+
"UNION ALL "
512+
/* Get system relations attributes which created in pg_catalog */
513+
"SELECT 'pg_class'::regclass, att.attrelid, att.attnum "
514+
"FROM pg_catalog.pg_class rel "
515+
"INNER JOIN pg_catalog.pg_attribute att ON rel.oid = att.attrelid "
516+
"WHERE rel.relnamespace = 'pg_catalog'::regnamespace "
517+
"UNION ALL "
518+
/* Get system functions and procedure which created in pg_catalog */
519+
"SELECT 'pg_proc'::regclass, oid, 0 "
520+
"FROM pg_catalog.pg_proc "
521+
"WHERE pronamespace = 'pg_catalog'::regnamespace ");
522+
523+
conn = connectToServer(&new_cluster, "template1");
524+
res = executeQueryOrDie(conn,
525+
"SELECT ident.type, ident.identity "
526+
"FROM (%s) obj, "
527+
"LATERAL pg_catalog.pg_identify_object("
528+
" obj.classid, obj.objid, obj.objsubid) ident "
529+
/*
530+
* Don't rely on database collation, since we use strcmp
531+
* comparison to find non-default ACLs.
532+
*/
533+
"ORDER BY ident.identity COLLATE \"C\";", subquery);
534+
ntups = PQntuples(res);
535+
536+
i_obj_ident = PQfnumber(res, "identity");
537+
538+
for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
539+
{
540+
DbInfo *dbinfo = &old_cluster.dbarr.dbs[dbnum];
541+
bool db_used = false;
542+
int aclnum = 0,
543+
objnum = 0;
544+
545+
/*
546+
* For every database check system objects with non-default ACL.
547+
*
548+
* AclInfo array is sorted by obj_ident. This allows us to compare
549+
* AclInfo entries with the query result above efficiently.
550+
*/
551+
for (aclnum = 0; aclnum < dbinfo->non_def_acl_arr.nacls; aclnum++)
552+
{
553+
AclInfo *aclinfo = &dbinfo->non_def_acl_arr.aclinfos[aclnum];
554+
bool report = false;
555+
556+
while (objnum < ntups)
557+
{
558+
int ret;
559+
560+
ret = strcmp(aclinfo->obj_ident,
561+
PQgetvalue(res, objnum, i_obj_ident));
562+
563+
/*
564+
* The new cluster doesn't have an object with same identity,
565+
* exit the loop, report below and check next object.
566+
*/
567+
if (ret < 0)
568+
{
569+
report = true;
570+
break;
571+
}
572+
/*
573+
* The new cluster has an object with same identity, just exit
574+
* the loop.
575+
*/
576+
else if (ret == 0)
577+
{
578+
objnum++;
579+
break;
580+
}
581+
else
582+
objnum++;
583+
}
584+
585+
if (report)
586+
{
587+
found_changed = true;
588+
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
589+
pg_fatal("could not open file \"%s\": %s\n",
590+
output_path, strerror(errno));
591+
if (!db_used)
592+
{
593+
PQExpBufferData conn_buf;
594+
595+
initPQExpBuffer(&conn_buf);
596+
appendPsqlMetaConnect(&conn_buf, dbinfo->db_name);
597+
fputs(conn_buf.data, script);
598+
termPQExpBuffer(&conn_buf);
599+
600+
db_used = true;
601+
}
602+
603+
/* Handle columns separately */
604+
if (strstr(aclinfo->obj_type, "column") != NULL)
605+
{
606+
char *pdot = last_dot_location(aclinfo->obj_ident);
607+
PQExpBufferData ident_buf;
608+
609+
if (pdot == NULL || *(pdot + 1) == '\0')
610+
pg_fatal("invalid column identity \"%s\"",
611+
aclinfo->obj_ident);
612+
613+
initPQExpBuffer(&ident_buf);
614+
appendBinaryPQExpBuffer(&ident_buf, aclinfo->obj_ident,
615+
pdot - aclinfo->obj_ident);
616+
617+
fprintf(script, "REVOKE ALL (%s) ON %s FROM %s;\n",
618+
/* pg_identify_object() quotes identity if necessary */
619+
pdot + 1, ident_buf.data,
620+
/* role_names is already quoted */
621+
aclinfo->role_names);
622+
termPQExpBuffer(&ident_buf);
623+
}
624+
/*
625+
* For relations except sequences we don't need to specify
626+
* the object type.
627+
*/
628+
else if (aclinfo->is_relation &&
629+
strcmp(aclinfo->obj_type, "sequence") != 0)
630+
fprintf(script, "REVOKE ALL ON %s FROM %s;\n",
631+
/* pg_identify_object() quotes identity if necessary */
632+
aclinfo->obj_ident,
633+
/* role_names is already quoted */
634+
aclinfo->role_names);
635+
/* Other object types */
636+
else
637+
fprintf(script, "REVOKE ALL ON %s %s FROM %s;\n",
638+
aclinfo->obj_type,
639+
/* pg_identify_object() quotes identity if necessary */
640+
aclinfo->obj_ident,
641+
/* role_names is already quoted */
642+
aclinfo->role_names);
643+
}
644+
}
645+
}
646+
647+
PQclear(res);
648+
PQfinish(conn);
649+
650+
if (script)
651+
fclose(script);
652+
653+
if (found_changed)
654+
{
655+
pg_log(PG_REPORT, "fatal\n");
656+
pg_fatal("Your installation contains non-default privileges for system objects\n"
657+
"for which the API has changed. To perform the upgrade, reset these\n"
658+
"privileges to default. The file\n"
659+
" %s\n"
660+
"when executed by psql will revoke non-default privileges for those objects.\n\n",
661+
output_path);
662+
}
663+
else
664+
check_ok();
665+
}
666+
446667

447668
/*
448669
* create_script_for_cluster_analyze()

0 commit comments

Comments
 (0)