Ensure pg_dump_sort.c sorts null vs non-null namespace consistently.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 7 Aug 2018 17:13:42 +0000 (13:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 7 Aug 2018 17:13:42 +0000 (13:13 -0400)
The original coding here (which is, I believe, my fault) supposed that
it didn't need to concern itself with the possibility that one object
of a given type-priority has a namespace while another doesn't.  But
that's not reliably true anymore, if it ever was; and if it does happen
then it's possible that DOTypeNameCompare returns self-inconsistent
comparison results.  That leads to unspecified behavior in qsort()
and a resultant weird output order from pg_dump.

This should end up being only a cosmetic problem, because any ordering
constraints that actually matter should be enforced by the later
dependency-based sort.  Still, it's a bug, so back-patch.

Report and fix by Jacob Champion, though I editorialized on his
patch to the extent of making NULL sort after non-NULL, for consistency
with our usual sorting definitions.

Discussion: https://postgr.es/m/CABAq_6Hw+V-Kj7PNfD5tgOaWT_-qaYkc+SRmJkPLeUjYXLdxwQ@mail.gmail.com

src/bin/pg_dump/pg_dump_sort.c

index d2b0949d6b3eb2083b018eef75b38565c8788ec9..6227a8fd26843452f36c62733bbf6026b95581d3 100644 (file)
@@ -223,7 +223,7 @@ DOTypeNameCompare(const void *p1, const void *p2)
    DumpableObject *obj2 = *(DumpableObject *const *) p2;
    int         cmpval;
 
-   /* Sort by type */
+   /* Sort by type's priority */
    cmpval = dbObjectTypePriority[obj1->objType] -
        dbObjectTypePriority[obj2->objType];
 
@@ -231,17 +231,24 @@ DOTypeNameCompare(const void *p1, const void *p2)
        return cmpval;
 
    /*
-    * Sort by namespace.  Note that all objects of the same type should
-    * either have or not have a namespace link, so we needn't be fancy about
-    * cases where one link is null and the other not.
+    * Sort by namespace.  Typically, all objects of the same priority would
+    * either have or not have a namespace link, but there are exceptions.
+    * Sort NULL namespace after non-NULL in such cases.
     */
-   if (obj1->namespace && obj2->namespace)
+   if (obj1->namespace)
    {
-       cmpval = strcmp(obj1->namespace->dobj.name,
-                       obj2->namespace->dobj.name);
-       if (cmpval != 0)
-           return cmpval;
+       if (obj2->namespace)
+       {
+           cmpval = strcmp(obj1->namespace->dobj.name,
+                           obj2->namespace->dobj.name);
+           if (cmpval != 0)
+               return cmpval;
+       }
+       else
+           return -1;
    }
+   else if (obj2->namespace)
+       return 1;
 
    /* Sort by name */
    cmpval = strcmp(obj1->name, obj2->name);