1616
1717static void check_new_cluster_is_empty (void );
1818static void check_databases_are_compatible (void );
19+ static void check_for_changed_signatures (void );
1920static void check_locale_and_encoding (DbInfo * olddb , DbInfo * newdb );
2021static bool equivalent_locale (int category , const char * loca , const char * locb );
2122static 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