Header And Logo

PostgreSQL
| The world's most advanced open source database.

spgquadtreeproc.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * spgquadtreeproc.c
00004  *    implementation of quad tree over points for SP-GiST
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * IDENTIFICATION
00011  *          src/backend/access/spgist/spgquadtreeproc.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 
00016 #include "postgres.h"
00017 
00018 #include "access/gist.h"        /* for RTree strategy numbers */
00019 #include "access/spgist.h"
00020 #include "catalog/pg_type.h"
00021 #include "utils/builtins.h"
00022 #include "utils/geo_decls.h"
00023 
00024 
00025 Datum
00026 spg_quad_config(PG_FUNCTION_ARGS)
00027 {
00028     /* spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0); */
00029     spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
00030 
00031     cfg->prefixType = POINTOID;
00032     cfg->labelType = VOIDOID;   /* we don't need node labels */
00033     cfg->canReturnData = true;
00034     cfg->longValuesOK = false;
00035     PG_RETURN_VOID();
00036 }
00037 
00038 #define SPTEST(f, x, y) \
00039     DatumGetBool(DirectFunctionCall2(f, PointPGetDatum(x), PointPGetDatum(y)))
00040 
00041 /*
00042  * Determine which quadrant a point falls into, relative to the centroid.
00043  *
00044  * Quadrants are identified like this:
00045  *
00046  *   4  |  1
00047  *  ----+-----
00048  *   3  |  2
00049  *
00050  * Points on one of the axes are taken to lie in the lowest-numbered
00051  * adjacent quadrant.
00052  */
00053 static int16
00054 getQuadrant(Point *centroid, Point *tst)
00055 {
00056     if ((SPTEST(point_above, tst, centroid) ||
00057          SPTEST(point_horiz, tst, centroid)) &&
00058         (SPTEST(point_right, tst, centroid) ||
00059          SPTEST(point_vert, tst, centroid)))
00060         return 1;
00061 
00062     if (SPTEST(point_below, tst, centroid) &&
00063         (SPTEST(point_right, tst, centroid) ||
00064          SPTEST(point_vert, tst, centroid)))
00065         return 2;
00066 
00067     if ((SPTEST(point_below, tst, centroid) ||
00068          SPTEST(point_horiz, tst, centroid)) &&
00069         SPTEST(point_left, tst, centroid))
00070         return 3;
00071 
00072     if (SPTEST(point_above, tst, centroid) &&
00073         SPTEST(point_left, tst, centroid))
00074         return 4;
00075 
00076     elog(ERROR, "getQuadrant: impossible case");
00077     return 0;
00078 }
00079 
00080 
00081 Datum
00082 spg_quad_choose(PG_FUNCTION_ARGS)
00083 {
00084     spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
00085     spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
00086     Point      *inPoint = DatumGetPointP(in->datum),
00087                *centroid;
00088 
00089     if (in->allTheSame)
00090     {
00091         out->resultType = spgMatchNode;
00092         /* nodeN will be set by core */
00093         out->result.matchNode.levelAdd = 0;
00094         out->result.matchNode.restDatum = PointPGetDatum(inPoint);
00095         PG_RETURN_VOID();
00096     }
00097 
00098     Assert(in->hasPrefix);
00099     centroid = DatumGetPointP(in->prefixDatum);
00100 
00101     Assert(in->nNodes == 4);
00102 
00103     out->resultType = spgMatchNode;
00104     out->result.matchNode.nodeN = getQuadrant(centroid, inPoint) - 1;
00105     out->result.matchNode.levelAdd = 0;
00106     out->result.matchNode.restDatum = PointPGetDatum(inPoint);
00107 
00108     PG_RETURN_VOID();
00109 }
00110 
00111 #ifdef USE_MEDIAN
00112 static int
00113 x_cmp(const void *a, const void *b, void *arg)
00114 {
00115     Point      *pa = *(Point **) a;
00116     Point      *pb = *(Point **) b;
00117 
00118     if (pa->x == pb->x)
00119         return 0;
00120     return (pa->x > pb->x) ? 1 : -1;
00121 }
00122 
00123 static int
00124 y_cmp(const void *a, const void *b, void *arg)
00125 {
00126     Point      *pa = *(Point **) a;
00127     Point      *pb = *(Point **) b;
00128 
00129     if (pa->y == pb->y)
00130         return 0;
00131     return (pa->y > pb->y) ? 1 : -1;
00132 }
00133 #endif
00134 
00135 Datum
00136 spg_quad_picksplit(PG_FUNCTION_ARGS)
00137 {
00138     spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
00139     spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
00140     int         i;
00141     Point      *centroid;
00142 
00143 #ifdef USE_MEDIAN
00144     /* Use the median values of x and y as the centroid point */
00145     Point     **sorted;
00146 
00147     sorted = palloc(sizeof(*sorted) * in->nTuples);
00148     for (i = 0; i < in->nTuples; i++)
00149         sorted[i] = DatumGetPointP(in->datums[i]);
00150 
00151     centroid = palloc(sizeof(*centroid));
00152 
00153     qsort(sorted, in->nTuples, sizeof(*sorted), x_cmp);
00154     centroid->x = sorted[in->nTuples >> 1]->x;
00155     qsort(sorted, in->nTuples, sizeof(*sorted), y_cmp);
00156     centroid->y = sorted[in->nTuples >> 1]->y;
00157 #else
00158     /* Use the average values of x and y as the centroid point */
00159     centroid = palloc0(sizeof(*centroid));
00160 
00161     for (i = 0; i < in->nTuples; i++)
00162     {
00163         centroid->x += DatumGetPointP(in->datums[i])->x;
00164         centroid->y += DatumGetPointP(in->datums[i])->y;
00165     }
00166 
00167     centroid->x /= in->nTuples;
00168     centroid->y /= in->nTuples;
00169 #endif
00170 
00171     out->hasPrefix = true;
00172     out->prefixDatum = PointPGetDatum(centroid);
00173 
00174     out->nNodes = 4;
00175     out->nodeLabels = NULL;     /* we don't need node labels */
00176 
00177     out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
00178     out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
00179 
00180     for (i = 0; i < in->nTuples; i++)
00181     {
00182         Point      *p = DatumGetPointP(in->datums[i]);
00183         int         quadrant = getQuadrant(centroid, p) - 1;
00184 
00185         out->leafTupleDatums[i] = PointPGetDatum(p);
00186         out->mapTuplesToNodes[i] = quadrant;
00187     }
00188 
00189     PG_RETURN_VOID();
00190 }
00191 
00192 
00193 Datum
00194 spg_quad_inner_consistent(PG_FUNCTION_ARGS)
00195 {
00196     spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
00197     spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
00198     Point      *centroid;
00199     int         which;
00200     int         i;
00201 
00202     Assert(in->hasPrefix);
00203     centroid = DatumGetPointP(in->prefixDatum);
00204 
00205     if (in->allTheSame)
00206     {
00207         /* Report that all nodes should be visited */
00208         out->nNodes = in->nNodes;
00209         out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
00210         for (i = 0; i < in->nNodes; i++)
00211             out->nodeNumbers[i] = i;
00212         PG_RETURN_VOID();
00213     }
00214 
00215     Assert(in->nNodes == 4);
00216 
00217     /* "which" is a bitmask of quadrants that satisfy all constraints */
00218     which = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
00219 
00220     for (i = 0; i < in->nkeys; i++)
00221     {
00222         Point      *query = DatumGetPointP(in->scankeys[i].sk_argument);
00223         BOX        *boxQuery;
00224 
00225         switch (in->scankeys[i].sk_strategy)
00226         {
00227             case RTLeftStrategyNumber:
00228                 if (SPTEST(point_right, centroid, query))
00229                     which &= (1 << 3) | (1 << 4);
00230                 break;
00231             case RTRightStrategyNumber:
00232                 if (SPTEST(point_left, centroid, query))
00233                     which &= (1 << 1) | (1 << 2);
00234                 break;
00235             case RTSameStrategyNumber:
00236                 which &= (1 << getQuadrant(centroid, query));
00237                 break;
00238             case RTBelowStrategyNumber:
00239                 if (SPTEST(point_above, centroid, query))
00240                     which &= (1 << 2) | (1 << 3);
00241                 break;
00242             case RTAboveStrategyNumber:
00243                 if (SPTEST(point_below, centroid, query))
00244                     which &= (1 << 1) | (1 << 4);
00245                 break;
00246             case RTContainedByStrategyNumber:
00247 
00248                 /*
00249                  * For this operator, the query is a box not a point.  We
00250                  * cheat to the extent of assuming that DatumGetPointP won't
00251                  * do anything that would be bad for a pointer-to-box.
00252                  */
00253                 boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
00254 
00255                 if (DatumGetBool(DirectFunctionCall2(box_contain_pt,
00256                                                    PointerGetDatum(boxQuery),
00257                                                  PointerGetDatum(centroid))))
00258                 {
00259                     /* centroid is in box, so all quadrants are OK */
00260                 }
00261                 else
00262                 {
00263                     /* identify quadrant(s) containing all corners of box */
00264                     Point       p;
00265                     int         r = 0;
00266 
00267                     p = boxQuery->low;
00268                     r |= 1 << getQuadrant(centroid, &p);
00269                     p.y = boxQuery->high.y;
00270                     r |= 1 << getQuadrant(centroid, &p);
00271                     p = boxQuery->high;
00272                     r |= 1 << getQuadrant(centroid, &p);
00273                     p.x = boxQuery->low.x;
00274                     r |= 1 << getQuadrant(centroid, &p);
00275 
00276                     which &= r;
00277                 }
00278                 break;
00279             default:
00280                 elog(ERROR, "unrecognized strategy number: %d",
00281                      in->scankeys[i].sk_strategy);
00282                 break;
00283         }
00284 
00285         if (which == 0)
00286             break;              /* no need to consider remaining conditions */
00287     }
00288 
00289     /* We must descend into the quadrant(s) identified by which */
00290     out->nodeNumbers = (int *) palloc(sizeof(int) * 4);
00291     out->nNodes = 0;
00292     for (i = 1; i <= 4; i++)
00293     {
00294         if (which & (1 << i))
00295             out->nodeNumbers[out->nNodes++] = i - 1;
00296     }
00297 
00298     PG_RETURN_VOID();
00299 }
00300 
00301 
00302 Datum
00303 spg_quad_leaf_consistent(PG_FUNCTION_ARGS)
00304 {
00305     spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
00306     spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
00307     Point      *datum = DatumGetPointP(in->leafDatum);
00308     bool        res;
00309     int         i;
00310 
00311     /* all tests are exact */
00312     out->recheck = false;
00313 
00314     /* leafDatum is what it is... */
00315     out->leafValue = in->leafDatum;
00316 
00317     /* Perform the required comparison(s) */
00318     res = true;
00319     for (i = 0; i < in->nkeys; i++)
00320     {
00321         Point      *query = DatumGetPointP(in->scankeys[i].sk_argument);
00322 
00323         switch (in->scankeys[i].sk_strategy)
00324         {
00325             case RTLeftStrategyNumber:
00326                 res = SPTEST(point_left, datum, query);
00327                 break;
00328             case RTRightStrategyNumber:
00329                 res = SPTEST(point_right, datum, query);
00330                 break;
00331             case RTSameStrategyNumber:
00332                 res = SPTEST(point_eq, datum, query);
00333                 break;
00334             case RTBelowStrategyNumber:
00335                 res = SPTEST(point_below, datum, query);
00336                 break;
00337             case RTAboveStrategyNumber:
00338                 res = SPTEST(point_above, datum, query);
00339                 break;
00340             case RTContainedByStrategyNumber:
00341 
00342                 /*
00343                  * For this operator, the query is a box not a point.  We
00344                  * cheat to the extent of assuming that DatumGetPointP won't
00345                  * do anything that would be bad for a pointer-to-box.
00346                  */
00347                 res = SPTEST(box_contain_pt, query, datum);
00348                 break;
00349             default:
00350                 elog(ERROR, "unrecognized strategy number: %d",
00351                      in->scankeys[i].sk_strategy);
00352                 break;
00353         }
00354 
00355         if (!res)
00356             break;
00357     }
00358 
00359     PG_RETURN_BOOL(res);
00360 }