> > 1. This patch introduces a new "polygon <-> point" operator. That seems > > useful on its own, with or without this patch. > > Yeah, but exact-knn cant come with no one implementation. But it would > better come in a separate patch.
I tried to split them. Separated patches are attached. I changed the order of the arguments as point <-> polygon, because point was the first one on all the others. Its commutator was required for the index, so I added it on the second patch. I also added tests for the operator. I think it is ready for committer as a separate patch. We can add it to the open CommitFest. I have made some cosmetic changes on the patches. I hope they are useful. I added support to point <-> circle operator with the same GiST distance function you added for polygon. I can change it, if it is not the right way. > > 2. I wonder how useful it really is to allow mixing exact and non-exact > > return values from the distance function. The distance function included in > > the patch always returns recheck=true. I have a feeling that all other > > distance function will also always return either true or false. > > For geometrical datatypes recheck variations in consistent methods are also > very rare (I can't remember any). But imagine opclass for arrays where keys > have different representation depending on array length. For such opclass > and knn on similarity recheck flag could be useful. I also wonder how useful it is. Your example is convincing, but maybe setting it index-wide will make the decisions on the framework easier. For example, how hard would it be to decide if further sorting is required or not on the planner? > > 4. (as you mentioned in the other thread: ) It's a modularity violation > > that you peek into the heap tuple from gist. I think the proper way to do > > this would be to extend the IndexScan executor node to perform the > > re-shuffling of tuples that come from the index in wrong order, or perhaps > > add a new node type for it. > > > > Of course that's exactly what your partial sort patch does :-). I haven't > > looked at that in detail, but I don't think the approach the partial sort > > patch takes will work here as is. In the KNN-GiST case, the index is > > returning tuples roughly in the right order, but a tuple that it returns > > might in reality belong somewhere later in the ordering. In the partial > > sort patch, the "input stream" of tuples is divided into non-overlapping > > groups, so that the tuples within the group are not sorted, but the groups > > are. I think the partial sort case is a special case of the KNN-GiST case, > > if you consider the lower bound of each tuple to be the leading keys that > > you don't need to sort. > > Yes. But, for instance btree accesses heap for unique checking. Is really > it so crimilal? :-) > This is not only question of a new node or extending existing node. We need > to teach planner/executor access method can return value of some expression > which is lower bound of another expression. AFICS now access method can > return only original indexed datums and TIDs. So, I afraid that enormous > infrastructure changes are required. And I can hardly imagine what they > should look like. Unfortunately, I am not experienced enough to judge your implementation. As far as I understand the problem is partially sorting rows on the index scan node. It can lead the planner to choose non-optimal plans, because of not taking into account the cost of sorting.
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 54391fd..402ea40 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -2408,36 +2408,42 @@ lseg_interpt(PG_FUNCTION_ARGS) ** Routines for position comparisons of differently-typed ** 2D objects. ** ***********************************************************************/ /*--------------------------------------------------------------------- * dist_ * Minimum distance from one object to another. *-------------------------------------------------------------------*/ +/* + * Distance from a point to a line + */ Datum dist_pl(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); LINE *line = PG_GETARG_LINE_P(1); PG_RETURN_FLOAT8(dist_pl_internal(pt, line)); } static double dist_pl_internal(Point *pt, LINE *line) { return fabs((line->A * pt->x + line->B * pt->y + line->C) / HYPOT(line->A, line->B)); } +/* + * Distance from a point to a lseg + */ Datum dist_ps(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); LSEG *lseg = PG_GETARG_LSEG_P(1); PG_RETURN_FLOAT8(dist_ps_internal(pt, lseg)); } static double @@ -2487,21 +2493,21 @@ dist_ps_internal(Point *pt, LSEG *lseg) result = point_dt(pt, &lseg->p[0]); tmpdist = point_dt(pt, &lseg->p[1]); if (tmpdist < result) result = tmpdist; } return result; } /* - ** Distance from a point to a path + * Distance from a point to a path */ Datum dist_ppath(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); PATH *path = PG_GETARG_PATH_P(1); float8 result = 0.0; /* keep compiler quiet */ bool have_min = false; float8 tmp; int i; @@ -2543,37 +2549,42 @@ dist_ppath(PG_FUNCTION_ARGS) { result = tmp; have_min = true; } } break; } PG_RETURN_FLOAT8(result); } +/* + * Distance from a point to a box + */ Datum dist_pb(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); BOX *box = PG_GETARG_BOX_P(1); float8 result; Point *near; near = DatumGetPointP(DirectFunctionCall2(close_pb, PointPGetDatum(pt), BoxPGetDatum(box))); result = point_dt(near, pt); PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a line + */ Datum dist_sl(PG_FUNCTION_ARGS) { LSEG *lseg = PG_GETARG_LSEG_P(0); LINE *line = PG_GETARG_LINE_P(1); float8 result, d2; if (has_interpt_sl(lseg, line)) result = 0.0; @@ -2582,57 +2593,63 @@ dist_sl(PG_FUNCTION_ARGS) result = dist_pl_internal(&lseg->p[0], line); d2 = dist_pl_internal(&lseg->p[1], line); /* XXX shouldn't we take the min not max? */ if (d2 > result) result = d2; } PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a box + */ Datum dist_sb(PG_FUNCTION_ARGS) { LSEG *lseg = PG_GETARG_LSEG_P(0); BOX *box = PG_GETARG_BOX_P(1); Point *tmp; Datum result; tmp = DatumGetPointP(DirectFunctionCall2(close_sb, LsegPGetDatum(lseg), BoxPGetDatum(box))); result = DirectFunctionCall2(dist_pb, PointPGetDatum(tmp), BoxPGetDatum(box)); PG_RETURN_DATUM(result); } - +/* + * Distance from a line to a box + */ Datum dist_lb(PG_FUNCTION_ARGS) { #ifdef NOT_USED LINE *line = PG_GETARG_LINE_P(0); BOX *box = PG_GETARG_BOX_P(1); #endif /* need to think about this one for a while */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function \"dist_lb\" not implemented"))); PG_RETURN_NULL(); } - +/* + * Distance from a circle to a polygon + */ Datum dist_cpoly(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; float8 d; int i; LSEG seg; @@ -2669,20 +2686,69 @@ dist_cpoly(PG_FUNCTION_ARGS) result = d; } result -= circle->radius; if (result < 0) result = 0; PG_RETURN_FLOAT8(result); } +/* + * Distance from a point to a polygon + */ +Datum +dist_ppoly(PG_FUNCTION_ARGS) +{ + Point *point = PG_GETARG_POINT_P(0); + POLYGON *poly = PG_GETARG_POLYGON_P(1); + float8 result; + float8 distance; + int i; + LSEG seg; + + if (point_inside(point, poly->npts, poly->p) != 0) + { +#ifdef GEODEBUG + printf("dist_ppoly- point inside of polygon\n"); +#endif + PG_RETURN_FLOAT8(0.0); + } + + /* initialize distance with segment between first and last points */ + seg.p[0].x = poly->p[0].x; + seg.p[0].y = poly->p[0].y; + seg.p[1].x = poly->p[poly->npts - 1].x; + seg.p[1].y = poly->p[poly->npts - 1].y; + result = dist_ps_internal(point, &seg); +#ifdef GEODEBUG + printf("dist_ppoly- segment 0/n distance is %f\n", result); +#endif + + /* check distances for other segments */ + for (i = 0; i < poly->npts - 1; i++) + { + seg.p[0].x = poly->p[i].x; + seg.p[0].y = poly->p[i].y; + seg.p[1].x = poly->p[i + 1].x; + seg.p[1].y = poly->p[i + 1].y; + distance = dist_ps_internal(point, &seg); +#ifdef GEODEBUG + printf("dist_ppoly- segment %d distance is %f\n", i + 1, distance); +#endif + if (distance < result) + result = distance; + } + + PG_RETURN_FLOAT8(result); +} + /*--------------------------------------------------------------------- * interpt_ * Intersection point of objects. * We choose to ignore the "point" of intersection between * lines and boxes, since there are typically two. *-------------------------------------------------------------------*/ /* Get intersection point of lseg and line; returns NULL if no intersection */ static Point * diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index f8b4a65..c31b8a8 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1009,20 +1009,22 @@ DATA(insert OID = 1518 ( "*" PGNSP PGUID b f f 718 600 718 0 0 circle_ DESCR("multiply"); DATA(insert OID = 1519 ( "/" PGNSP PGUID b f f 718 600 718 0 0 circle_div_pt - - )); DESCR("divide"); DATA(insert OID = 1520 ( "<->" PGNSP PGUID b f f 718 718 701 1520 0 circle_distance - - )); DESCR("distance between"); DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npoints - - )); DESCR("number of points"); DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); DESCR("distance between"); +DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); +DESCR("distance between"); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - )); DESCR("distance between"); /* additional geometric operators - thomas 1997-07-09 */ DATA(insert OID = 1524 ( "<->" PGNSP PGUID b f f 628 603 701 0 0 dist_lb - - )); DESCR("distance between"); DATA(insert OID = 1525 ( "?#" PGNSP PGUID b f f 601 601 16 1525 0 lseg_intersect - - )); DESCR("intersect"); DATA(insert OID = 1526 ( "?||" PGNSP PGUID b f f 601 601 16 1526 0 lseg_parallel - - )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0af1248..95f0b74 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -806,20 +806,21 @@ DESCR("set bit"); DATA(insert OID = 749 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 17 "17 17 23 23" _null_ _null_ _null_ _null_ byteaoverlay _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 752 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_ byteaoverlay_no_len _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 725 ( dist_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_ dist_pl _null_ _null_ _null_ )); DATA(insert OID = 726 ( dist_lb PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "628 603" _null_ _null_ _null_ _null_ dist_lb _null_ _null_ _null_ )); DATA(insert OID = 727 ( dist_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "601 628" _null_ _null_ _null_ _null_ dist_sl _null_ _null_ _null_ )); DATA(insert OID = 728 ( dist_cpoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 604" _null_ _null_ _null_ _null_ dist_cpoly _null_ _null_ _null_ )); DATA(insert OID = 729 ( poly_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 604" _null_ _null_ _null_ _null_ poly_distance _null_ _null_ _null_ )); +DATA(insert OID = 3590 ( dist_ppoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 604" _null_ _null_ _null_ _null_ dist_ppoly _null_ _null_ _null_ )); DATA(insert OID = 740 ( text_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_lt _null_ _null_ _null_ )); DATA(insert OID = 741 ( text_le PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_le _null_ _null_ _null_ )); DATA(insert OID = 742 ( text_gt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_gt _null_ _null_ _null_ )); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_ge _null_ _null_ _null_ )); DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("current user name"); DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); DESCR("session user name"); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 60b5d01..91610d8 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -388,20 +388,21 @@ extern Datum circle_contain_pt(PG_FUNCTION_ARGS); extern Datum pt_contained_circle(PG_FUNCTION_ARGS); extern Datum circle_add_pt(PG_FUNCTION_ARGS); extern Datum circle_sub_pt(PG_FUNCTION_ARGS); extern Datum circle_mul_pt(PG_FUNCTION_ARGS); extern Datum circle_div_pt(PG_FUNCTION_ARGS); extern Datum circle_diameter(PG_FUNCTION_ARGS); extern Datum circle_radius(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS); extern Datum dist_cpoly(PG_FUNCTION_ARGS); +extern Datum dist_ppoly(PG_FUNCTION_ARGS); extern Datum circle_center(PG_FUNCTION_ARGS); extern Datum cr_circle(PG_FUNCTION_ARGS); extern Datum box_circle(PG_FUNCTION_ARGS); extern Datum circle_box(PG_FUNCTION_ARGS); extern Datum poly_circle(PG_FUNCTION_ARGS); extern Datum circle_poly(PG_FUNCTION_ARGS); extern Datum circle_area(PG_FUNCTION_ARGS); /* support routines for the GiST access method (access/gist/gistproc.c) */ extern Datum gist_box_compress(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/polygon.out b/src/test/regress/expected/polygon.out index b252902..b449e32 100644 --- a/src/test/regress/expected/polygon.out +++ b/src/test/regress/expected/polygon.out @@ -271,10 +271,22 @@ SELECT '((1,4),(1,1),(4,1),(4,2),(2,2),(2,4),(1,4))'::polygon && '((3,3),(4,3),( ------- f (1 row) SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygon AS "true"; true ------ t (1 row) +-- distance from a point +SELECT + '(0,0)'::point <-> '((0,0),(1,2),(2,1))'::polygon as on_corner, + '(1,1)'::point <-> '((0,0),(2,2),(1,3))'::polygon as on_segment, + '(2,2)'::point <-> '((0,0),(1,4),(3,1))'::polygon as inside, + '(3,3)'::point <-> '((0,2),(2,0),(2,2))'::polygon as near_corner, + '(4,4)'::point <-> '((0,0),(0,3),(4,0))'::polygon as near_segment; + on_corner | on_segment | inside | near_corner | near_segment +-----------+------------+--------+-----------------+-------------- + 0 | 0 | 0 | 1.4142135623731 | 3.2 +(1 row) + diff --git a/src/test/regress/sql/polygon.sql b/src/test/regress/sql/polygon.sql index 2dad566..20a7c36 100644 --- a/src/test/regress/sql/polygon.sql +++ b/src/test/regress/sql/polygon.sql @@ -164,10 +164,18 @@ SELECT polygon '(2.0,0.0),(2.0,4.0),(0.0,0.0)' && polygon '(3.0,1.0),(3.0,3.0),( SELECT '((0,4),(6,4),(1,2),(6,0),(0,0))'::polygon && '((2,1),(2,3),(3,3),(3,1))'::polygon AS "true"; -- +--+ *--* -- | | | | -- | | *--* -- | +----+ -- | | -- +-------+ SELECT '((1,4),(1,1),(4,1),(4,2),(2,2),(2,4),(1,4))'::polygon && '((3,3),(4,3),(4,4),(3,4),(3,3))'::polygon AS "false"; SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygon AS "true"; + +-- distance from a point +SELECT + '(0,0)'::point <-> '((0,0),(1,2),(2,1))'::polygon as on_corner, + '(1,1)'::point <-> '((0,0),(2,2),(1,3))'::polygon as on_segment, + '(2,2)'::point <-> '((0,0),(1,4),(3,1))'::polygon as inside, + '(3,3)'::point <-> '((0,2),(2,0),(2,2))'::polygon as near_corner, + '(4,4)'::point <-> '((0,0),(0,3),(4,0))'::polygon as near_segment;
diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml index 0158b17..2cfe9e8 100644 --- a/doc/src/sgml/gist.sgml +++ b/doc/src/sgml/gist.sgml @@ -98,20 +98,21 @@ <literal><<|</> <literal><@</> <literal>@></> <literal>@</> <literal>|&></> <literal>|>></> <literal>~</> <literal>~=</> </entry> <entry> + <literal><-></> </entry> </row> <row> <entry><literal>inet_ops</></entry> <entry><type>inet</>, <type>cidr</></entry> <entry> <literal>&&</> <literal>>></> <literal>>>=</> <literal>></> @@ -156,20 +157,21 @@ <literal><<|</> <literal><@</> <literal>@></> <literal>@</> <literal>|&></> <literal>|>></> <literal>~</> <literal>~=</> </entry> <entry> + <literal><-></> </entry> </row> <row> <entry><literal>range_ops</></entry> <entry>any range type</entry> <entry> <literal>&&</> <literal>&></> <literal>&<</> <literal>>></> @@ -200,20 +202,26 @@ <literal>@@</> </entry> <entry> </entry> </row> </tbody> </tgroup> </table> <para> + Currently, ordering by the distance operator <literal><-></> + is supported only with <literal>point</> by the operator classes + of the geometric types. + </para> + + <para> For historical reasons, the <literal>inet_ops</> operator class is not the default class for types <type>inet</> and <type>cidr</>. To use it, mention the class name in <command>CREATE INDEX</>, for example <programlisting> CREATE INDEX ON my_table USING gist (my_inet_column inet_ops); </programlisting> </para> </sect1> @@ -759,56 +767,62 @@ my_same(PG_FUNCTION_ARGS) so the results must be consistent with the operator's semantics. For a leaf index entry the result just represents the distance to the index entry; for an internal tree node, the result must be the smallest distance that any child entry could have. </para> <para> The <acronym>SQL</> declaration of the function must look like this: <programlisting> -CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid) +CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C STRICT; </programlisting> And the matching code in the C module could then follow this skeleton: <programlisting> Datum my_distance(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(my_distance); Datum my_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); data_type *query = PG_GETARG_DATA_TYPE_P(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); data_type *key = DatumGetDataType(entry->key); double retval; /* * determine return value as a function of strategy, key and query. */ PG_RETURN_FLOAT8(retval); } </programlisting> The arguments to the <function>distance</> function are identical to - the arguments of the <function>consistent</> function, except that no - recheck flag is used. The distance to a leaf index entry must always - be determined exactly, since there is no way to re-order the tuples - once they are returned. Some approximation is allowed when determining - the distance to an internal tree node, so long as the result is never + the arguments of the <function>consistent</> function. When + <literal>recheck = true</> then value of distance will + be rechecked from heap tuple before tuple is returned. If + <literal>recheck</> flag isn't set then it's true by default for + compatibility reasons. The <literal>recheck</> flag can be used only + when ordering operator returns <type>float8</> value comparable with + result of <function>distance</> function. Result of distance function + should be never greater than result of ordering operator. + Same approximation is allowed when determining the distance to an + internal tree node, so long as the result is never greater than any child's actual distance. Thus, for example, distance to a bounding box is usually sufficient in geometric applications. The result value can be any finite <type>float8</> value. (Infinity and minus infinity are used internally to handle cases such as nulls, so it is not recommended that <function>distance</> functions return these values.) </para> </listitem> </varlistentry> diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 7a8692b..7be050d 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -9,20 +9,21 @@ * * IDENTIFICATION * src/backend/access/gist/gistget.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/gist_private.h" #include "access/relscan.h" +#include "catalog/index.h" #include "miscadmin.h" #include "pgstat.h" #include "utils/builtins.h" #include "utils/memutils.h" #include "utils/rel.h" /* * gistindex_keytest() -- does this index tuple satisfy the scan key(s)? * @@ -48,38 +49,41 @@ static bool gistindex_keytest(IndexScanDesc scan, IndexTuple tuple, Page page, OffsetNumber offset, bool *recheck_p) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; GISTSTATE *giststate = so->giststate; ScanKey key = scan->keyData; int keySize = scan->numberOfKeys; - double *distance_p; + GISTSearchTreeItemDistance *distance_p; Relation r = scan->indexRelation; *recheck_p = false; /* * If it's a leftover invalid tuple from pre-9.1, treat it as a match with * minimum possible distances. This means we'll always follow it to the * referenced page. */ if (GistTupleIsInvalid(tuple)) { int i; if (GistPageIsLeaf(page)) /* shouldn't happen */ elog(ERROR, "invalid GiST tuple found on leaf page"); for (i = 0; i < scan->numberOfOrderBys; i++) - so->distances[i] = -get_float8_infinity(); + { + so->distances[i].value = -get_float8_infinity(); + so->distances[i].recheck = false; + } return true; } /* Check whether it matches according to the Consistent functions */ while (keySize > 0) { Datum datum; bool isNull; datum = index_getattr(tuple, @@ -163,53 +167,56 @@ gistindex_keytest(IndexScanDesc scan, bool isNull; datum = index_getattr(tuple, key->sk_attno, giststate->tupdesc, &isNull); if ((key->sk_flags & SK_ISNULL) || isNull) { /* Assume distance computes as null and sorts to the end */ - *distance_p = get_float8_infinity(); + distance_p->value = get_float8_infinity(); + distance_p->recheck = false; } else { Datum dist; GISTENTRY de; gistdentryinit(giststate, key->sk_attno - 1, &de, datum, r, page, offset, FALSE, isNull); /* * Call the Distance function to evaluate the distance. The * arguments are the index datum (as a GISTENTRY*), the comparison * datum, and the ordering operator's strategy number and subtype * from pg_amop. * * (Presently there's no need to pass the subtype since it'll * always be zero, but might as well pass it for possible future * use.) * - * Note that Distance functions don't get a recheck argument. We - * can't tolerate lossy distance calculations on leaf tuples; - * there is no opportunity to re-sort the tuples afterwards. + * Distance function gets a recheck argument as well as consistent + * function. Distance will be re-calculated from heap tuple when + * needed. */ - dist = FunctionCall4Coll(&key->sk_func, + distance_p->recheck = false; + dist = FunctionCall5Coll(&key->sk_func, key->sk_collation, PointerGetDatum(&de), key->sk_argument, Int32GetDatum(key->sk_strategy), - ObjectIdGetDatum(key->sk_subtype)); + ObjectIdGetDatum(key->sk_subtype), + PointerGetDatum(&distance_p->recheck)); - *distance_p = DatumGetFloat8(dist); + distance_p->value = DatumGetFloat8(dist); } key++; distance_p++; keySize--; } return true; } @@ -227,21 +234,21 @@ gistindex_keytest(IndexScanDesc scan, * tuples should be reported directly into the bitmap. If they are NULL, * we're doing a plain or ordered indexscan. For a plain indexscan, heap * tuple TIDs are returned into so->pageData[]. For an ordered indexscan, * heap tuple TIDs are pushed into individual search queue items. * * If we detect that the index page has split since we saw its downlink * in the parent, we push its new right sibling onto the queue so the * sibling will be processed next. */ static void -gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, +gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, GISTSearchTreeItemDistance *myDistances, TIDBitmap *tbm, int64 *ntids) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; Buffer buffer; Page page; GISTPageOpaque opaque; OffsetNumber maxoff; OffsetNumber i; GISTSearchTreeItem *tmpItem = so->tmpTreeItem; bool isNew; @@ -277,21 +284,21 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, /* Create new GISTSearchItem for the right sibling index page */ item = palloc(sizeof(GISTSearchItem)); item->next = NULL; item->blkno = opaque->rightlink; item->data.parentlsn = pageItem->data.parentlsn; /* Insert it into the queue using same distances as for this page */ tmpItem->head = item; tmpItem->lastHeap = NULL; memcpy(tmpItem->distances, myDistances, - sizeof(double) * scan->numberOfOrderBys); + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); (void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew); MemoryContextSwitchTo(oldcxt); } so->nPageData = so->curPageData = 0; /* * check all tuples on page @@ -368,63 +375,166 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, * only have a shared lock, so we need to get the LSN * atomically. */ item->data.parentlsn = BufferGetLSNAtomic(buffer); } /* Insert it into the queue using new distance data */ tmpItem->head = item; tmpItem->lastHeap = GISTSearchItemIsHeap(*item) ? item : NULL; memcpy(tmpItem->distances, so->distances, - sizeof(double) * scan->numberOfOrderBys); + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); (void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew); MemoryContextSwitchTo(oldcxt); } } UnlockReleaseBuffer(buffer); } /* + * Do this tree item distance values needs recheck? + */ +static bool +searchTreeItemNeedDistanceRecheck(IndexScanDesc scan, GISTSearchTreeItem *item) +{ + int i; + for (i = 0; i < scan->numberOfOrderBys; i++) + { + if (item->distances[i].recheck) + return true; + } + return false; +} + +/* + * Recheck distance values of item from heap and reinsert it into RB-tree. + */ +static void +searchTreeItemDistanceRecheck(IndexScanDesc scan, GISTSearchTreeItem *treeItem, + GISTSearchItem *item) +{ + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + GISTSearchTreeItem *tmpItem = so->tmpTreeItem; + Buffer buffer; + bool got_heap_tuple, all_dead; + HeapTupleData tup; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + bool isNew; + int i; + + /* Get tuple from heap */ + buffer = ReadBuffer(scan->heapRelation, + ItemPointerGetBlockNumber(&item->data.heap.heapPtr)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + got_heap_tuple = heap_hot_search_buffer(&item->data.heap.heapPtr, + scan->heapRelation, + buffer, + scan->xs_snapshot, + &tup, + &all_dead, + true); + if (!got_heap_tuple) + { + /* + * Tuple not found: it has been deleted from heap. We don't have to + * reinsert it into RB-tree. + */ + UnlockReleaseBuffer(buffer); + pfree(item); + return; + } + + /* Calculate index datums */ + ExecStoreTuple(&tup, so->slot, InvalidBuffer, false); + FormIndexDatum(so->indexInfo, so->slot, so->estate, values, isnull); + + /* Prepare new tree item and reinsert it */ + memcpy(tmpItem, treeItem, GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys); + tmpItem->head = item; + tmpItem->lastHeap = item; + item->next = NULL; + for (i = 0; i < scan->numberOfOrderBys; i++) + { + if (tmpItem->distances[i].recheck) + { + /* Re-calculate lossy distance */ + ScanKey key = scan->orderByData + i; + float8 newDistance; + + tmpItem->distances[i].recheck = false; + if (isnull[key->sk_attno - 1]) + { + tmpItem->distances[i].value = -get_float8_infinity(); + continue; + } + + newDistance = DatumGetFloat8( + FunctionCall2Coll(&so->orderByRechecks[i], + key->sk_collation, + values[key->sk_attno - 1], + key->sk_argument)); + + tmpItem->distances[i].value = newDistance; + + } + } + (void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew); + UnlockReleaseBuffer(buffer); +} + +/* * Extract next item (in order) from search queue * * Returns a GISTSearchItem or NULL. Caller must pfree item when done with it. * * NOTE: on successful return, so->curTreeItem is the GISTSearchTreeItem that * contained the result item. Callers can use so->curTreeItem->distances as * the distances value for the item. */ static GISTSearchItem * -getNextGISTSearchItem(GISTScanOpaque so) +getNextGISTSearchItem(IndexScanDesc scan) { + GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + for (;;) { GISTSearchItem *item; /* Update curTreeItem if we don't have one */ if (so->curTreeItem == NULL) { so->curTreeItem = (GISTSearchTreeItem *) rb_leftmost(so->queue); /* Done when tree is empty */ if (so->curTreeItem == NULL) break; } item = so->curTreeItem->head; if (item != NULL) { /* Delink item from chain */ so->curTreeItem->head = item->next; if (item == so->curTreeItem->lastHeap) so->curTreeItem->lastHeap = NULL; + + /* Recheck distance from heap tuple if needed */ + if (GISTSearchItemIsHeap(*item) && + searchTreeItemNeedDistanceRecheck(scan, so->curTreeItem)) + { + searchTreeItemDistanceRecheck(scan, so->curTreeItem, item); + continue; + } /* Return item; caller is responsible to pfree it */ return item; } /* curTreeItem is exhausted, so remove it from rbtree */ rb_delete(so->queue, (RBNode *) so->curTreeItem); so->curTreeItem = NULL; } return NULL; @@ -434,21 +544,21 @@ getNextGISTSearchItem(GISTScanOpaque so) * Fetch next heap tuple in an ordered search */ static bool getNextNearest(IndexScanDesc scan) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; do { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) break; if (GISTSearchItemIsHeap(*item)) { /* found a heap item at currently minimal distance */ scan->xs_ctup.t_self = item->data.heap.heapPtr; scan->xs_recheck = item->data.heap.recheck; res = true; @@ -514,21 +624,21 @@ gistgettuple(PG_FUNCTION_ARGS) /* continuing to return tuples from a leaf page */ scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr; scan->xs_recheck = so->pageData[so->curPageData].recheck; so->curPageData++; PG_RETURN_BOOL(true); } /* find and process the next index page */ do { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) PG_RETURN_BOOL(false); CHECK_FOR_INTERRUPTS(); /* * While scanning a leaf page, ItemPointers of matching heap * tuples are stored in so->pageData. If there are any on * this page, we fall out of the inner "do" and loop around to @@ -566,21 +676,21 @@ gistgetbitmap(PG_FUNCTION_ARGS) fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); gistScanPage(scan, &fakeItem, NULL, tbm, &ntids); /* * While scanning a leaf page, ItemPointers of matching heap tuples will * be stored directly into tbm, so we don't need to deal with them here. */ for (;;) { - GISTSearchItem *item = getNextGISTSearchItem(so); + GISTSearchItem *item = getNextGISTSearchItem(scan); if (!item) break; CHECK_FOR_INTERRUPTS(); gistScanPage(scan, item, so->curTreeItem->distances, tbm, &ntids); pfree(item); } diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index db0bec6..fd3546a 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -1091,20 +1091,21 @@ gist_poly_consistent(PG_FUNCTION_ARGS) */ result = rtree_internal_consistent(DatumGetBoxP(entry->key), &(query->boundbox), strategy); /* Avoid memory leak if supplied poly is toasted */ PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(result); } + /************************************************** * Circle ops **************************************************/ /* * GiST compress for circles: represent a circle by its bounding box */ Datum gist_circle_compress(PG_FUNCTION_ARGS) { @@ -1452,10 +1453,44 @@ gist_point_distance(PG_FUNCTION_ARGS) PG_GETARG_POINT_P(1)); break; default: elog(ERROR, "unrecognized strategy number: %d", strategy); distance = 0.0; /* keep compiler quiet */ break; } PG_RETURN_FLOAT8(distance); } + +/* + * The inexact GiST distance method for geometric types + * + * Compute lossy distance from point to index entries. The result is inexact + * because index entries are bounding boxes, not the exact shapes of the + * indexed geometric types. We use distance from point to MBR of index entry. + * This is correct lower bound estimate of distance from point to indexed + * geometric type. + */ +Datum +gist_inexact_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + bool *recheck = (bool *) PG_GETARG_POINTER(4); + double distance; + StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset; + *recheck = true; + + switch (strategyGroup) + { + case PointStrategyNumberGroup: + distance = computeDistance(false, + DatumGetBoxP(entry->key), + PG_GETARG_POINT_P(1)); + break; + default: + elog(ERROR, "unknown strategy number: %d", strategy); + distance = 0.0; /* keep compiler quiet */ + } + + PG_RETURN_FLOAT8(distance); +} diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index 8360b16..9bb8294 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -10,41 +10,54 @@ * IDENTIFICATION * src/backend/access/gist/gistscan.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/gist_private.h" #include "access/gistscan.h" #include "access/relscan.h" +#include "catalog/index.h" +#include "executor/executor.h" +#include "executor/tuptable.h" #include "utils/memutils.h" #include "utils/rel.h" /* * RBTree support functions for the GISTSearchTreeItem queue */ static int GISTSearchTreeItemComparator(const RBNode *a, const RBNode *b, void *arg) { const GISTSearchTreeItem *sa = (const GISTSearchTreeItem *) a; const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b; IndexScanDesc scan = (IndexScanDesc) arg; int i; /* Order according to distance comparison */ for (i = 0; i < scan->numberOfOrderBys; i++) { - if (sa->distances[i] != sb->distances[i]) - return (sa->distances[i] > sb->distances[i]) ? 1 : -1; + const GISTSearchTreeItemDistance distance_a = sa->distances[i]; + const GISTSearchTreeItemDistance distance_b = sb->distances[i]; + + if (distance_a.value != distance_b.value) + return (distance_a.value > distance_b.value) ? 1 : -1; + + /* + * Items without recheck can be immediately returned. So they are + * placed first. + */ + if (distance_a.recheck != distance_b.recheck) + return distance_a.recheck ? 1 : -1; } return 0; } static void GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg) { GISTSearchTreeItem *scurrent = (GISTSearchTreeItem *) existing; const GISTSearchTreeItem *snew = (const GISTSearchTreeItem *) newrb; @@ -76,21 +89,21 @@ GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg) newitem->next = scurrent->lastHeap->next; scurrent->lastHeap->next = newitem; } } static RBNode * GISTSearchTreeItemAllocator(void *arg) { IndexScanDesc scan = (IndexScanDesc) arg; - return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys); + return palloc(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * scan->numberOfOrderBys); } static void GISTSearchTreeItemDeleter(RBNode *rb, void *arg) { pfree(rb); } /* @@ -120,24 +133,36 @@ gistbeginscan(PG_FUNCTION_ARGS) oldCxt = MemoryContextSwitchTo(giststate->scanCxt); /* initialize opaque data */ so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData)); so->giststate = giststate; giststate->tempCxt = createTempGistContext(); so->queue = NULL; so->queueCxt = giststate->scanCxt; /* see gistrescan */ /* workspaces with size dependent on numberOfOrderBys: */ - so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys); - so->distances = palloc(sizeof(double) * scan->numberOfOrderBys); + so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys); + so->distances = palloc(sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys); so->qual_ok = true; /* in case there are zero keys */ + if (scan->numberOfOrderBys > 0) + { + /* Prepare data structures for distance recheck from heap tuple */ + + so->orderByRechecks = (FmgrInfo *)palloc(sizeof(FmgrInfo) + * scan->numberOfOrderBys); + so->indexInfo = BuildIndexInfo(scan->indexRelation); + so->estate = CreateExecutorState(); + } + scan->opaque = so; MemoryContextSwitchTo(oldCxt); PG_RETURN_POINTER(scan); } Datum gistrescan(PG_FUNCTION_ARGS) { @@ -179,23 +204,30 @@ gistrescan(PG_FUNCTION_ARGS) ALLOCSET_DEFAULT_MAXSIZE); first_time = false; } else { /* third or later time through */ MemoryContextReset(so->queueCxt); first_time = false; } + if (scan->numberOfOrderBys > 0 && !so->slot) + { + /* Prepare heap tuple slot for distance recheck */ + so->slot = MakeSingleTupleTableSlot(RelationGetDescr(scan->heapRelation)); + } + /* create new, empty RBTree for search queue */ oldCxt = MemoryContextSwitchTo(so->queueCxt); - so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys, + so->queue = rb_create(GSTIHDRSZ + sizeof(GISTSearchTreeItemDistance) * + scan->numberOfOrderBys, GISTSearchTreeItemComparator, GISTSearchTreeItemCombiner, GISTSearchTreeItemAllocator, GISTSearchTreeItemDeleter, scan); MemoryContextSwitchTo(oldCxt); so->curTreeItem = NULL; so->firstCall = true; @@ -282,20 +314,24 @@ gistrescan(PG_FUNCTION_ARGS) { ScanKey skey = scan->orderByData + i; FmgrInfo *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]); /* Check we actually have a distance function ... */ if (!OidIsValid(finfo->fn_oid)) elog(ERROR, "missing support function %d for attribute %d of index \"%s\"", GIST_DISTANCE_PROC, skey->sk_attno, RelationGetRelationName(scan->indexRelation)); + /* Copy original sk_func for distance recheck from heap tuple */ + fmgr_info_copy(&so->orderByRechecks[i], &(skey->sk_func), + so->giststate->scanCxt); + fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt); /* Restore prior fn_extra pointers, if not first time */ if (!first_time) skey->sk_func.fn_extra = fn_extras[i]; } if (!first_time) pfree(fn_extras); } @@ -316,18 +352,21 @@ gistrestrpos(PG_FUNCTION_ARGS) elog(ERROR, "GiST does not support mark/restore"); PG_RETURN_VOID(); } Datum gistendscan(PG_FUNCTION_ARGS) { IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + if (so->slot) + ExecDropSingleTupleTableSlot(so->slot); + /* * freeGISTstate is enough to clean up everything made by gistbeginscan, * as well as the queueCxt if there is a separate context for it. */ freeGISTstate(so->giststate); PG_RETURN_VOID(); } diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 402ea40..c9788e4 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -63,20 +63,21 @@ static int pair_encode(float8 x, float8 y, char *str); static int pair_count(char *s, char delim); static int path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p); static char *path_encode(enum path_delim path_delim, int npts, Point *pt); static void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2); static double box_ar(BOX *box); static void box_cn(Point *center, BOX *box); static Point *interpt_sl(LSEG *lseg, LINE *line); static bool has_interpt_sl(LSEG *lseg, LINE *line); static double dist_pl_internal(Point *pt, LINE *line); static double dist_ps_internal(Point *pt, LSEG *lseg); +static float8 dist_ppoly_internal(Point *point, POLYGON *poly); static Point *line_interpt_internal(LINE *l1, LINE *l2); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2); /* * Delimiters for input and output strings. * LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively. * LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints. */ @@ -2634,20 +2635,52 @@ dist_lb(PG_FUNCTION_ARGS) /* need to think about this one for a while */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function \"dist_lb\" not implemented"))); PG_RETURN_NULL(); } /* + * Distance from a point to a circle + */ +Datum +dist_pc(PG_FUNCTION_ARGS) +{ + Point *point = PG_GETARG_POINT_P(0); + CIRCLE *circle = PG_GETARG_CIRCLE_P(1); + float8 result; + + result = point_dt(point, &circle->center) - circle->radius; + if (result < 0) + result = 0; + PG_RETURN_FLOAT8(result); +} + +/* + * Distance from a circle to a point + */ +Datum +dist_cpoint(PG_FUNCTION_ARGS) +{ + CIRCLE *circle = PG_GETARG_CIRCLE_P(0); + Point *point = PG_GETARG_POINT_P(1); + float8 result; + + result = point_dt(point, &circle->center) - circle->radius; + if (result < 0) + result = 0; + PG_RETURN_FLOAT8(result); +} + +/* * Distance from a circle to a polygon */ Datum dist_cpoly(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; float8 d; int i; @@ -2692,33 +2725,48 @@ dist_cpoly(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } /* * Distance from a point to a polygon */ Datum dist_ppoly(PG_FUNCTION_ARGS) { - Point *point = PG_GETARG_POINT_P(0); - POLYGON *poly = PG_GETARG_POLYGON_P(1); + PG_RETURN_FLOAT8(dist_ppoly_internal(PG_GETARG_POINT_P(0), + PG_GETARG_POLYGON_P(1))); +} + +/* + * Distance from a polygon to a point + */ +Datum +dist_polyp(PG_FUNCTION_ARGS) +{ + PG_RETURN_FLOAT8(dist_ppoly_internal(PG_GETARG_POINT_P(1), + PG_GETARG_POLYGON_P(0))); +} + +static float8 +dist_ppoly_internal(Point *point, POLYGON *poly) +{ float8 result; float8 distance; int i; LSEG seg; if (point_inside(point, poly->npts, poly->p) != 0) { #ifdef GEODEBUG printf("dist_ppoly- point inside of polygon\n"); #endif - PG_RETURN_FLOAT8(0.0); + return 0.0; } /* initialize distance with segment between first and last points */ seg.p[0].x = poly->p[0].x; seg.p[0].y = poly->p[0].y; seg.p[1].x = poly->p[poly->npts - 1].x; seg.p[1].y = poly->p[poly->npts - 1].y; result = dist_ps_internal(point, &seg); #ifdef GEODEBUG printf("dist_ppoly- segment 0/n distance is %f\n", result); @@ -2732,21 +2780,21 @@ dist_ppoly(PG_FUNCTION_ARGS) seg.p[1].x = poly->p[i + 1].x; seg.p[1].y = poly->p[i + 1].y; distance = dist_ps_internal(point, &seg); #ifdef GEODEBUG printf("dist_ppoly- segment %d distance is %f\n", i + 1, distance); #endif if (distance < result) result = distance; } - PG_RETURN_FLOAT8(result); + return result; } /*--------------------------------------------------------------------- * interpt_ * Intersection point of objects. * We choose to ignore the "point" of intersection between * lines and boxes, since there are typically two. *-------------------------------------------------------------------*/ @@ -5091,37 +5139,20 @@ pt_contained_circle(PG_FUNCTION_ARGS) { Point *point = PG_GETARG_POINT_P(0); CIRCLE *circle = PG_GETARG_CIRCLE_P(1); double d; d = point_dt(&circle->center, point); PG_RETURN_BOOL(d <= circle->radius); } -/* dist_pc - returns the distance between - * a point and a circle. - */ -Datum -dist_pc(PG_FUNCTION_ARGS) -{ - Point *point = PG_GETARG_POINT_P(0); - CIRCLE *circle = PG_GETARG_CIRCLE_P(1); - float8 result; - - result = point_dt(point, &circle->center) - circle->radius; - if (result < 0) - result = 0; - PG_RETURN_FLOAT8(result); -} - - /* circle_center - returns the center point of the circle. */ Datum circle_center(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); Point *result; result = (Point *) palloc(sizeof(Point)); result->x = circle->center.x; diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 03e9903..6f98583 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -9,21 +9,23 @@ * * src/include/access/gist_private.h * *------------------------------------------------------------------------- */ #ifndef GIST_PRIVATE_H #define GIST_PRIVATE_H #include "access/gist.h" #include "access/itup.h" +#include "executor/tuptable.h" #include "fmgr.h" +#include "nodes/execnodes.h" #include "storage/bufmgr.h" #include "storage/buffile.h" #include "utils/rbtree.h" #include "utils/hsearch.h" /* * Maximum number of "halves" a page can be split into in one operation. * Typically a split produces 2 halves, but can be more if keys have very * different lengths, or when inserting multiple keys in one operation (as * when inserting downlinks to an internal node). There is no theoretical @@ -128,55 +130,70 @@ typedef struct GISTSearchItem { GistNSN parentlsn; /* parent page's LSN, if index page */ /* we must store parentlsn to detect whether a split occurred */ GISTSearchHeapItem heap; /* heap info, if heap tuple */ } data; } GISTSearchItem; #define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber) /* + * KNN distance item: distance which can be rechecked from heap tuple. + */ +typedef struct GISTSearchTreeItemDistance +{ + double value; + bool recheck; +} GISTSearchTreeItemDistance; + +/* * Within a GISTSearchTreeItem's chain, heap items always appear before * index-page items, since we want to visit heap items first. lastHeap points * to the last heap item in the chain, or is NULL if there are none. */ typedef struct GISTSearchTreeItem { RBNode rbnode; /* this is an RBTree item */ GISTSearchItem *head; /* first chain member */ GISTSearchItem *lastHeap; /* last heap-tuple member, if any */ - double distances[1]; /* array with numberOfOrderBys entries */ + GISTSearchTreeItemDistance distances[1]; /* array with numberOfOrderBys entries */ } GISTSearchTreeItem; #define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances) /* * GISTScanOpaqueData: private state for a scan of a GiST index */ typedef struct GISTScanOpaqueData { GISTSTATE *giststate; /* index information, see above */ RBTree *queue; /* queue of unvisited items */ MemoryContext queueCxt; /* context holding the queue */ bool qual_ok; /* false if qual can never be satisfied */ bool firstCall; /* true until first gistgettuple call */ GISTSearchTreeItem *curTreeItem; /* current queue item, if any */ /* pre-allocated workspace arrays */ GISTSearchTreeItem *tmpTreeItem; /* workspace to pass to rb_insert */ - double *distances; /* output area for gistindex_keytest */ + GISTSearchTreeItemDistance *distances; /* output area for gistindex_keytest */ /* In a non-ordered search, returnable heap items are stored here: */ GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)]; OffsetNumber nPageData; /* number of valid items in array */ OffsetNumber curPageData; /* next item to return */ + + /* Data structures for performing recheck of lossy knn distance */ + FmgrInfo *orderByRechecks; /* functions for lossy knn distance recheck */ + IndexInfo *indexInfo; /* index info for index tuple calculation */ + TupleTableSlot *slot; /* heap tuple slot */ + EState *estate; /* executor state for index tuple calculation */ } GISTScanOpaqueData; typedef GISTScanOpaqueData *GISTScanOpaque; /* XLog stuff */ #define XLOG_GIST_PAGE_UPDATE 0x00 /* #define XLOG_GIST_NEW_ROOT 0x20 */ /* not used anymore */ #define XLOG_GIST_PAGE_SPLIT 0x30 diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 3ef5a49..dd468f6 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -643,39 +643,41 @@ DATA(insert ( 2594 604 604 4 s 487 783 0 )); DATA(insert ( 2594 604 604 5 s 488 783 0 )); DATA(insert ( 2594 604 604 6 s 491 783 0 )); DATA(insert ( 2594 604 604 7 s 490 783 0 )); DATA(insert ( 2594 604 604 8 s 489 783 0 )); DATA(insert ( 2594 604 604 9 s 2575 783 0 )); DATA(insert ( 2594 604 604 10 s 2574 783 0 )); DATA(insert ( 2594 604 604 11 s 2577 783 0 )); DATA(insert ( 2594 604 604 12 s 2576 783 0 )); DATA(insert ( 2594 604 604 13 s 2861 783 0 )); DATA(insert ( 2594 604 604 14 s 2860 783 0 )); +DATA(insert ( 2594 604 600 15 o 3588 783 1970 )); /* * gist circle_ops */ DATA(insert ( 2595 718 718 1 s 1506 783 0 )); DATA(insert ( 2595 718 718 2 s 1507 783 0 )); DATA(insert ( 2595 718 718 3 s 1513 783 0 )); DATA(insert ( 2595 718 718 4 s 1508 783 0 )); DATA(insert ( 2595 718 718 5 s 1509 783 0 )); DATA(insert ( 2595 718 718 6 s 1512 783 0 )); DATA(insert ( 2595 718 718 7 s 1511 783 0 )); DATA(insert ( 2595 718 718 8 s 1510 783 0 )); DATA(insert ( 2595 718 718 9 s 2589 783 0 )); DATA(insert ( 2595 718 718 10 s 1515 783 0 )); DATA(insert ( 2595 718 718 11 s 1514 783 0 )); DATA(insert ( 2595 718 718 12 s 2590 783 0 )); DATA(insert ( 2595 718 718 13 s 2865 783 0 )); DATA(insert ( 2595 718 718 14 s 2864 783 0 )); +DATA(insert ( 2595 718 600 15 o 3586 783 1970 )); /* * gin array_ops (these anyarray operators are used with all the opclasses * of the family) */ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 )); DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 )); DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 )); DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 10a47df..1149923 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -197,27 +197,29 @@ DATA(insert ( 2593 603 603 4 2580 )); DATA(insert ( 2593 603 603 5 2581 )); DATA(insert ( 2593 603 603 6 2582 )); DATA(insert ( 2593 603 603 7 2584 )); DATA(insert ( 2594 604 604 1 2585 )); DATA(insert ( 2594 604 604 2 2583 )); DATA(insert ( 2594 604 604 3 2586 )); DATA(insert ( 2594 604 604 4 2580 )); DATA(insert ( 2594 604 604 5 2581 )); DATA(insert ( 2594 604 604 6 2582 )); DATA(insert ( 2594 604 604 7 2584 )); +DATA(insert ( 2594 604 604 8 3589 )); DATA(insert ( 2595 718 718 1 2591 )); DATA(insert ( 2595 718 718 2 2583 )); DATA(insert ( 2595 718 718 3 2592 )); DATA(insert ( 2595 718 718 4 2580 )); DATA(insert ( 2595 718 718 5 2581 )); DATA(insert ( 2595 718 718 6 2582 )); DATA(insert ( 2595 718 718 7 2584 )); +DATA(insert ( 2595 718 718 8 3589 )); DATA(insert ( 3655 3614 3614 1 3654 )); DATA(insert ( 3655 3614 3614 2 3651 )); DATA(insert ( 3655 3614 3614 3 3648 )); DATA(insert ( 3655 3614 3614 4 3649 )); DATA(insert ( 3655 3614 3614 5 3653 )); DATA(insert ( 3655 3614 3614 6 3650 )); DATA(insert ( 3655 3614 3614 7 3652 )); DATA(insert ( 3702 3615 3615 1 3701 )); DATA(insert ( 3702 3615 3615 2 3698 )); DATA(insert ( 3702 3615 3615 3 3695 )); diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index c31b8a8..b633665 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1007,23 +1007,27 @@ DATA(insert OID = 1517 ( "-" PGNSP PGUID b f f 718 600 718 0 0 circle_ DESCR("subtract"); DATA(insert OID = 1518 ( "*" PGNSP PGUID b f f 718 600 718 0 0 circle_mul_pt - - )); DESCR("multiply"); DATA(insert OID = 1519 ( "/" PGNSP PGUID b f f 718 600 718 0 0 circle_div_pt - - )); DESCR("divide"); DATA(insert OID = 1520 ( "<->" PGNSP PGUID b f f 718 718 701 1520 0 circle_distance - - )); DESCR("distance between"); DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npoints - - )); DESCR("number of points"); -DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); +DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 3586 0 dist_pc - - )); DESCR("distance between"); -DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); +DATA(insert OID = 3586 ( "<->" PGNSP PGUID b f f 718 600 701 1522 0 dist_cpoint - - )); +DESCR("distance between"); +DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 3588 0 dist_ppoly - - )); +DESCR("distance between"); +DATA(insert OID = 3588 ( "<->" PGNSP PGUID b f f 604 600 701 3591 0 dist_polyp - - )); DESCR("distance between"); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - )); DESCR("distance between"); /* additional geometric operators - thomas 1997-07-09 */ DATA(insert OID = 1524 ( "<->" PGNSP PGUID b f f 628 603 701 0 0 dist_lb - - )); DESCR("distance between"); DATA(insert OID = 1525 ( "?#" PGNSP PGUID b f f 601 601 16 1525 0 lseg_intersect - - )); DESCR("intersect"); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 95f0b74..1b7664e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -807,20 +807,22 @@ DATA(insert OID = 749 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 17 DESCR("substitute portion of string"); DATA(insert OID = 752 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_ byteaoverlay_no_len _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 725 ( dist_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_ dist_pl _null_ _null_ _null_ )); DATA(insert OID = 726 ( dist_lb PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "628 603" _null_ _null_ _null_ _null_ dist_lb _null_ _null_ _null_ )); DATA(insert OID = 727 ( dist_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "601 628" _null_ _null_ _null_ _null_ dist_sl _null_ _null_ _null_ )); DATA(insert OID = 728 ( dist_cpoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 604" _null_ _null_ _null_ _null_ dist_cpoly _null_ _null_ _null_ )); DATA(insert OID = 729 ( poly_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 604" _null_ _null_ _null_ _null_ poly_distance _null_ _null_ _null_ )); DATA(insert OID = 3590 ( dist_ppoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 604" _null_ _null_ _null_ _null_ dist_ppoly _null_ _null_ _null_ )); +DATA(insert OID = 3587 ( dist_polyp PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 600" _null_ _null_ _null_ _null_ dist_polyp _null_ _null_ _null_ )); +DATA(insert OID = 3585 ( dist_cpoint PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 600" _null_ _null_ _null_ _null_ dist_cpoint _null_ _null_ _null_ )); DATA(insert OID = 740 ( text_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_lt _null_ _null_ _null_ )); DATA(insert OID = 741 ( text_le PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_le _null_ _null_ _null_ )); DATA(insert OID = 742 ( text_gt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_gt _null_ _null_ _null_ )); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_ge _null_ _null_ _null_ )); DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("current user name"); DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); DESCR("session user name"); @@ -4002,20 +4004,22 @@ DESCR("GiST support"); DATA(insert OID = 2582 ( gist_box_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gist_box_picksplit _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2583 ( gist_box_union PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 603 "2281 2281" _null_ _null_ _null_ _null_ gist_box_union _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2584 ( gist_box_same PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "603 603 2281" _null_ _null_ _null_ _null_ gist_box_same _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2585 ( gist_poly_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 604 23 26 2281" _null_ _null_ _null_ _null_ gist_poly_consistent _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2586 ( gist_poly_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_poly_compress _null_ _null_ _null_ )); DESCR("GiST support"); +DATA(insert OID = 3589 ( gist_inexact_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_inexact_distance _null_ _null_ _null_ )); +DESCR("GiST support"); DATA(insert OID = 2591 ( gist_circle_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 718 23 26 2281" _null_ _null_ _null_ _null_ gist_circle_consistent _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_circle_compress _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 3064 ( gist_point_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_point_distance _null_ _null_ _null_ )); DESCR("GiST support"); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 91610d8..64f63b2 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -387,22 +387,24 @@ extern Datum circle_ge(PG_FUNCTION_ARGS); extern Datum circle_contain_pt(PG_FUNCTION_ARGS); extern Datum pt_contained_circle(PG_FUNCTION_ARGS); extern Datum circle_add_pt(PG_FUNCTION_ARGS); extern Datum circle_sub_pt(PG_FUNCTION_ARGS); extern Datum circle_mul_pt(PG_FUNCTION_ARGS); extern Datum circle_div_pt(PG_FUNCTION_ARGS); extern Datum circle_diameter(PG_FUNCTION_ARGS); extern Datum circle_radius(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS); +extern Datum dist_cpoint(PG_FUNCTION_ARGS); extern Datum dist_cpoly(PG_FUNCTION_ARGS); extern Datum dist_ppoly(PG_FUNCTION_ARGS); +extern Datum dist_polyp(PG_FUNCTION_ARGS); extern Datum circle_center(PG_FUNCTION_ARGS); extern Datum cr_circle(PG_FUNCTION_ARGS); extern Datum box_circle(PG_FUNCTION_ARGS); extern Datum circle_box(PG_FUNCTION_ARGS); extern Datum poly_circle(PG_FUNCTION_ARGS); extern Datum circle_poly(PG_FUNCTION_ARGS); extern Datum circle_area(PG_FUNCTION_ARGS); /* support routines for the GiST access method (access/gist/gistproc.c) */ extern Datum gist_box_compress(PG_FUNCTION_ARGS); @@ -412,20 +414,21 @@ extern Datum gist_box_picksplit(PG_FUNCTION_ARGS); extern Datum gist_box_consistent(PG_FUNCTION_ARGS); extern Datum gist_box_penalty(PG_FUNCTION_ARGS); extern Datum gist_box_same(PG_FUNCTION_ARGS); extern Datum gist_poly_compress(PG_FUNCTION_ARGS); extern Datum gist_poly_consistent(PG_FUNCTION_ARGS); extern Datum gist_circle_compress(PG_FUNCTION_ARGS); extern Datum gist_circle_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_compress(PG_FUNCTION_ARGS); extern Datum gist_point_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_distance(PG_FUNCTION_ARGS); +extern Datum gist_inexact_distance(PG_FUNCTION_ARGS); /* geo_selfuncs.c */ extern Datum areasel(PG_FUNCTION_ARGS); extern Datum areajoinsel(PG_FUNCTION_ARGS); extern Datum positionsel(PG_FUNCTION_ARGS); extern Datum positionjoinsel(PG_FUNCTION_ARGS); extern Datum contsel(PG_FUNCTION_ARGS); extern Datum contjoinsel(PG_FUNCTION_ARGS); #endif /* GEO_DECLS_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers