Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5433b48

Browse files
committed
Tweak sorting so that nulls appear at the front of a descending sort
(vs. at the end of a normal sort). This ensures that explicit sorts yield the same ordering as a btree index scan. To be really sure that that equivalence holds, we use the btree entries in pg_amop to decide whether we are looking at a '<' or '>' operator. For a sort operator that has no btree association, we put the nulls at the front if the operator is named '>' ... pretty grotty, but it does the right thing in simple ASC and DESC cases, and at least there's no possibility of getting a different answer depending on the plan type chosen.
1 parent e542036 commit 5433b48

File tree

3 files changed

+144
-127
lines changed

3 files changed

+144
-127
lines changed

src/backend/commands/analyze.c

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.17 2001/05/07 00:43:17 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.18 2001/06/02 19:01:53 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1439,27 +1439,12 @@ compare_scalars(const void *a, const void *b)
14391439
int ta = ((ScalarItem *) a)->tupno;
14401440
Datum db = ((ScalarItem *) b)->value;
14411441
int tb = ((ScalarItem *) b)->tupno;
1442+
int32 compare;
14421443

1443-
if (datumCmpFnKind == SORTFUNC_LT)
1444-
{
1445-
if (DatumGetBool(FunctionCall2(datumCmpFn, da, db)))
1446-
return -1; /* a < b */
1447-
if (DatumGetBool(FunctionCall2(datumCmpFn, db, da)))
1448-
return 1; /* a > b */
1449-
}
1450-
else
1451-
{
1452-
/* sort function is CMP or REVCMP */
1453-
int32 compare;
1454-
1455-
compare = DatumGetInt32(FunctionCall2(datumCmpFn, da, db));
1456-
if (compare != 0)
1457-
{
1458-
if (datumCmpFnKind == SORTFUNC_REVCMP)
1459-
compare = -compare;
1460-
return compare;
1461-
}
1462-
}
1444+
compare = ApplySortFunction(datumCmpFn, datumCmpFnKind,
1445+
da, false, db, false);
1446+
if (compare != 0)
1447+
return compare;
14631448

14641449
/*
14651450
* The two datums are equal, so update datumCmpTupnoLink[].

src/backend/utils/sort/tuplesort.c

Lines changed: 126 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
* Portions Copyright (c) 1994, Regents of the University of California
7979
*
8080
* IDENTIFICATION
81-
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.16 2001/05/07 00:43:24 tgl Exp $
81+
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.17 2001/06/02 19:01:52 tgl Exp $
8282
*
8383
*-------------------------------------------------------------------------
8484
*/
@@ -90,10 +90,12 @@
9090
#include "catalog/catname.h"
9191
#include "catalog/pg_amop.h"
9292
#include "catalog/pg_amproc.h"
93+
#include "catalog/pg_operator.h"
9394
#include "miscadmin.h"
9495
#include "utils/fmgroids.h"
9596
#include "utils/logtape.h"
9697
#include "utils/lsyscache.h"
98+
#include "utils/syscache.h"
9799
#include "utils/tuplesort.h"
98100

99101
/*
@@ -1732,53 +1734,25 @@ comparetup_heap(Tuplesortstate *state, const void *a, const void *b)
17321734
for (nkey = 0; nkey < state->nKeys; nkey++)
17331735
{
17341736
ScanKey scanKey = state->scanKeys + nkey;
1735-
SortFunctionKind fnKind = state->sortFnKinds[nkey];
17361737
AttrNumber attno = scanKey->sk_attno;
1737-
Datum lattr,
1738-
rattr;
1738+
Datum datum1,
1739+
datum2;
17391740
bool isnull1,
17401741
isnull2;
1742+
int32 compare;
17411743

1742-
lattr = heap_getattr(ltup, attno, tupDesc, &isnull1);
1743-
rattr = heap_getattr(rtup, attno, tupDesc, &isnull2);
1744+
datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
1745+
datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);
17441746

1745-
if (isnull1)
1746-
{
1747-
if (!isnull2)
1748-
return 1; /* NULL sorts after non-NULL */
1749-
}
1750-
else if (isnull2)
1751-
return -1;
1752-
else
1747+
compare = ApplySortFunction(&scanKey->sk_func,
1748+
state->sortFnKinds[nkey],
1749+
datum1, isnull1, datum2, isnull2);
1750+
if (compare != 0)
17531751
{
1754-
int32 compare;
1755-
1756-
if (fnKind == SORTFUNC_LT)
1757-
{
1758-
if (DatumGetBool(FunctionCall2(&scanKey->sk_func,
1759-
lattr, rattr)))
1760-
compare = -1; /* a < b */
1761-
else if (DatumGetBool(FunctionCall2(&scanKey->sk_func,
1762-
rattr, lattr)))
1763-
compare = 1; /* a > b */
1764-
else
1765-
compare = 0;
1766-
}
1767-
else
1768-
{
1769-
/* sort function is CMP or REVCMP */
1770-
compare = DatumGetInt32(FunctionCall2(&scanKey->sk_func,
1771-
lattr, rattr));
1772-
if (fnKind == SORTFUNC_REVCMP)
1773-
compare = -compare;
1774-
}
1775-
1776-
if (compare != 0)
1777-
{
1778-
if (scanKey->sk_flags & SK_COMMUTE)
1779-
compare = -compare;
1780-
return compare;
1781-
}
1752+
/* dead code? SK_COMMUTE can't actually be set here, can it? */
1753+
if (scanKey->sk_flags & SK_COMMUTE)
1754+
compare = -compare;
1755+
return compare;
17821756
}
17831757
}
17841758

@@ -1861,7 +1835,6 @@ tuplesize_heap(Tuplesortstate *state, void *tup)
18611835
static int
18621836
comparetup_index(Tuplesortstate *state, const void *a, const void *b)
18631837
{
1864-
18651838
/*
18661839
* This is almost the same as _bt_tuplecompare(), but we need to keep
18671840
* track of whether any null fields are present.
@@ -1880,41 +1853,28 @@ comparetup_index(Tuplesortstate *state, const void *a, const void *b)
18801853
for (i = 1; i <= keysz; i++)
18811854
{
18821855
ScanKey entry = &scankey[i - 1];
1883-
Datum attrDatum1,
1884-
attrDatum2;
1885-
bool isFirstNull,
1886-
isSecondNull;
1856+
Datum datum1,
1857+
datum2;
1858+
bool isnull1,
1859+
isnull2;
18871860
int32 compare;
18881861

1889-
attrDatum1 = index_getattr(tuple1, i, tupDes, &isFirstNull);
1890-
attrDatum2 = index_getattr(tuple2, i, tupDes, &isSecondNull);
1862+
datum1 = index_getattr(tuple1, i, tupDes, &isnull1);
1863+
datum2 = index_getattr(tuple2, i, tupDes, &isnull2);
18911864

18921865
/* see comments about NULLs handling in btbuild */
1893-
if (isFirstNull) /* attr in tuple1 is NULL */
1894-
{
1895-
if (isSecondNull) /* attr in tuple2 is NULL too */
1896-
{
1897-
compare = 0;
1898-
equal_hasnull = true;
1899-
}
1900-
else
1901-
compare = 1; /* NULL ">" not-NULL */
1902-
}
1903-
else if (isSecondNull) /* attr in tuple1 is NOT_NULL and */
1904-
{ /* attr in tuple2 is NULL */
1905-
compare = -1; /* not-NULL "<" NULL */
1906-
}
1907-
else
1908-
{
1909-
/* the comparison function is always of CMP type */
1910-
compare = DatumGetInt32(FunctionCall2(&entry->sk_func,
1911-
attrDatum1,
1912-
attrDatum2));
1913-
}
1866+
1867+
/* the comparison function is always of CMP type */
1868+
compare = ApplySortFunction(&entry->sk_func, SORTFUNC_CMP,
1869+
datum1, isnull1, datum2, isnull2);
19141870

19151871
if (compare != 0)
19161872
return (int) compare; /* done when we find unequal
19171873
* attributes */
1874+
1875+
/* they are equal, so we only need to examine one null flag */
1876+
if (isnull1)
1877+
equal_hasnull = true;
19181878
}
19191879

19201880
/*
@@ -2002,35 +1962,9 @@ comparetup_datum(Tuplesortstate *state, const void *a, const void *b)
20021962
DatumTuple *ltup = (DatumTuple *) a;
20031963
DatumTuple *rtup = (DatumTuple *) b;
20041964

2005-
if (ltup->isNull)
2006-
{
2007-
if (!rtup->isNull)
2008-
return 1; /* NULL sorts after non-NULL */
2009-
return 0;
2010-
}
2011-
else if (rtup->isNull)
2012-
return -1;
2013-
else if (state->sortFnKind == SORTFUNC_LT)
2014-
{
2015-
if (DatumGetBool(FunctionCall2(&state->sortOpFn,
2016-
ltup->val, rtup->val)))
2017-
return -1; /* a < b */
2018-
if (DatumGetBool(FunctionCall2(&state->sortOpFn,
2019-
rtup->val, ltup->val)))
2020-
return 1; /* a > b */
2021-
return 0;
2022-
}
2023-
else
2024-
{
2025-
/* sort function is CMP or REVCMP */
2026-
int32 compare;
2027-
2028-
compare = DatumGetInt32(FunctionCall2(&state->sortOpFn,
2029-
ltup->val, rtup->val));
2030-
if (state->sortFnKind == SORTFUNC_REVCMP)
2031-
compare = -compare;
2032-
return compare;
2033-
}
1965+
return ApplySortFunction(&state->sortOpFn, state->sortFnKind,
1966+
ltup->val, ltup->isNull,
1967+
rtup->val, rtup->isNull);
20341968
}
20351969

20361970
static void *
@@ -2123,6 +2057,7 @@ SelectSortFunction(Oid sortOperator,
21232057
HeapScanDesc scan;
21242058
ScanKeyData skey[3];
21252059
HeapTuple tuple;
2060+
Form_pg_operator optup;
21262061
Oid opclass = InvalidOid;
21272062

21282063
/*
@@ -2207,11 +2142,97 @@ SelectSortFunction(Oid sortOperator,
22072142
return;
22082143
}
22092144

2210-
/* Can't find a comparator, so use the operator as-is */
2211-
2212-
*kind = SORTFUNC_LT;
2213-
*sortFunction = get_opcode(sortOperator);
2214-
if (!RegProcedureIsValid(*sortFunction))
2215-
elog(ERROR, "SelectSortFunction: operator %u has no implementation",
2145+
/*
2146+
* Can't find a comparator, so use the operator as-is. Decide whether
2147+
* it is forward or reverse sort by looking at its name (grotty, but
2148+
* this only matters for deciding which end NULLs should get sorted to).
2149+
*/
2150+
tuple = SearchSysCache(OPEROID,
2151+
ObjectIdGetDatum(sortOperator),
2152+
0, 0, 0);
2153+
if (!HeapTupleIsValid(tuple))
2154+
elog(ERROR, "SelectSortFunction: cache lookup failed for operator %u",
22162155
sortOperator);
2156+
optup = (Form_pg_operator) GETSTRUCT(tuple);
2157+
if (strcmp(NameStr(optup->oprname), ">") == 0)
2158+
*kind = SORTFUNC_REVLT;
2159+
else
2160+
*kind = SORTFUNC_LT;
2161+
*sortFunction = optup->oprcode;
2162+
ReleaseSysCache(tuple);
2163+
2164+
Assert(RegProcedureIsValid(*sortFunction));
2165+
}
2166+
2167+
/*
2168+
* Apply a sort function (by now converted to fmgr lookup form)
2169+
* and return a 3-way comparison result. This takes care of handling
2170+
* NULLs and sort ordering direction properly.
2171+
*/
2172+
int32
2173+
ApplySortFunction(FmgrInfo *sortFunction, SortFunctionKind kind,
2174+
Datum datum1, bool isNull1,
2175+
Datum datum2, bool isNull2)
2176+
{
2177+
switch (kind)
2178+
{
2179+
case SORTFUNC_LT:
2180+
if (isNull1)
2181+
{
2182+
if (isNull2)
2183+
return 0;
2184+
return 1; /* NULL sorts after non-NULL */
2185+
}
2186+
if (isNull2)
2187+
return -1;
2188+
if (DatumGetBool(FunctionCall2(sortFunction, datum1, datum2)))
2189+
return -1; /* a < b */
2190+
if (DatumGetBool(FunctionCall2(sortFunction, datum2, datum1)))
2191+
return 1; /* a > b */
2192+
return 0;
2193+
2194+
case SORTFUNC_REVLT:
2195+
/* We reverse the ordering of NULLs, but not the operator */
2196+
if (isNull1)
2197+
{
2198+
if (isNull2)
2199+
return 0;
2200+
return -1; /* NULL sorts before non-NULL */
2201+
}
2202+
if (isNull2)
2203+
return 1;
2204+
if (DatumGetBool(FunctionCall2(sortFunction, datum1, datum2)))
2205+
return -1; /* a < b */
2206+
if (DatumGetBool(FunctionCall2(sortFunction, datum2, datum1)))
2207+
return 1; /* a > b */
2208+
return 0;
2209+
2210+
case SORTFUNC_CMP:
2211+
if (isNull1)
2212+
{
2213+
if (isNull2)
2214+
return 0;
2215+
return 1; /* NULL sorts after non-NULL */
2216+
}
2217+
if (isNull2)
2218+
return -1;
2219+
return DatumGetInt32(FunctionCall2(sortFunction,
2220+
datum1, datum2));
2221+
2222+
case SORTFUNC_REVCMP:
2223+
if (isNull1)
2224+
{
2225+
if (isNull2)
2226+
return 0;
2227+
return -1; /* NULL sorts before non-NULL */
2228+
}
2229+
if (isNull2)
2230+
return 1;
2231+
return - DatumGetInt32(FunctionCall2(sortFunction,
2232+
datum1, datum2));
2233+
2234+
default:
2235+
elog(ERROR, "Invalid SortFunctionKind %d", (int) kind);
2236+
return 0; /* can't get here, but keep compiler quiet */
2237+
}
22172238
}

src/include/utils/tuplesort.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
1414
* Portions Copyright (c) 1994, Regents of the University of California
1515
*
16-
* $Id: tuplesort.h,v 1.7 2001/05/07 00:43:26 tgl Exp $
16+
* $Id: tuplesort.h,v 1.8 2001/06/02 19:01:52 tgl Exp $
1717
*
1818
*-------------------------------------------------------------------------
1919
*/
@@ -22,6 +22,7 @@
2222

2323
#include "access/htup.h"
2424
#include "access/itup.h"
25+
#include "fmgr.h"
2526

2627
/* Tuplesortstate is an opaque type whose details are not known outside tuplesort.c. */
2728

@@ -83,6 +84,7 @@ extern void tuplesort_restorepos(Tuplesortstate *state);
8384
typedef enum
8485
{
8586
SORTFUNC_LT, /* raw "<" operator */
87+
SORTFUNC_REVLT, /* raw "<" operator, but reverse NULLs */
8688
SORTFUNC_CMP, /* -1 / 0 / 1 three-way comparator */
8789
SORTFUNC_REVCMP /* 1 / 0 / -1 (reversed) 3-way comparator */
8890
} SortFunctionKind;
@@ -91,4 +93,13 @@ extern void SelectSortFunction(Oid sortOperator,
9193
RegProcedure *sortFunction,
9294
SortFunctionKind *kind);
9395

96+
/*
97+
* Apply a sort function (by now converted to fmgr lookup form)
98+
* and return a 3-way comparison result. This takes care of handling
99+
* NULLs and sort ordering direction properly.
100+
*/
101+
extern int32 ApplySortFunction(FmgrInfo *sortFunction, SortFunctionKind kind,
102+
Datum datum1, bool isNull1,
103+
Datum datum2, bool isNull2);
104+
94105
#endif /* TUPLESORT_H */

0 commit comments

Comments
 (0)