> > 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers