diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml new file mode 100644 index dcdc297..bac9979 *** a/doc/src/sgml/spgist.sgml --- b/doc/src/sgml/spgist.sgml *************** *** 131,136 **** --- 131,172 ---- + circle_ops + circle + + << + &< + && + &> + >> + ~= + @> + <@ + &<| + <<| + |>> + |&> + + + + poly_ops + polygon + + << + &< + && + &> + >> + ~= + @> + <@ + &<| + <<| + |>> + |&> + + + text_ops text diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c new file mode 100644 index d1919fc..1c11f1f *** a/src/backend/access/gist/gistproc.c --- b/src/backend/access/gist/gistproc.c *************** gist_circle_compress(PG_FUNCTION_ARGS) *** 1115,1126 **** CIRCLE *in = DatumGetCircleP(entry->key); BOX *r; ! r = (BOX *) palloc(sizeof(BOX)); ! r->high.x = in->center.x + in->radius; ! r->low.x = in->center.x - in->radius; ! r->high.y = in->center.y + in->radius; ! r->low.y = in->center.y - in->radius; ! retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, --- 1115,1121 ---- CIRCLE *in = DatumGetCircleP(entry->key); BOX *r; ! r = circle_bbox(in); retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c new file mode 100644 index 0348855..fce9f73 *** a/src/backend/utils/adt/geo_ops.c --- b/src/backend/utils/adt/geo_ops.c *************** enum path_delim *** 41,47 **** static int point_inside(Point *p, int npts, Point *plist); static int lseg_crossing(double x, double y, double px, double py); static BOX *box_construct(double x1, double x2, double y1, double y2); - static BOX *box_copy(BOX *box); static BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2); static bool box_ov(BOX *box1, BOX *box2); static double box_ht(BOX *box); --- 41,46 ---- *************** box_fill(BOX *result, double x1, double *** 482,488 **** /* box_copy - copy a box */ ! static BOX * box_copy(BOX *box) { BOX *result = (BOX *) palloc(sizeof(BOX)); --- 481,487 ---- /* box_copy - copy a box */ ! BOX * box_copy(BOX *box) { BOX *result = (BOX *) palloc(sizeof(BOX)); *************** circle_ar(CIRCLE *circle) *** 5089,5094 **** --- 5088,5122 ---- return M_PI * (circle->radius * circle->radius); } + /* circle_bbox - returns bounding box of the circle. + */ + BOX * + circle_bbox(CIRCLE *circle) + { + BOX *bbox = (BOX *) palloc(sizeof(BOX)); + + bbox->high.x = circle->center.x + circle->radius; + bbox->low.x = circle->center.x - circle->radius; + bbox->high.y = circle->center.y + circle->radius; + bbox->low.y = circle->center.y - circle->radius; + + if (isnan(bbox->low.x)) + { + double tmp = bbox->low.x; + bbox->low.x = bbox->high.x; + bbox->high.x = tmp; + } + + if (isnan(bbox->low.y)) + { + double tmp = bbox->low.y; + bbox->low.y = bbox->high.y; + bbox->high.y = tmp; + } + + return bbox; + } + /*---------------------------------------------------------- * Conversion operators. diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c new file mode 100644 index f6334ba..1237bd2 *** a/src/backend/utils/adt/geo_spgist.c --- b/src/backend/utils/adt/geo_spgist.c *************** spg_box_quad_choose(PG_FUNCTION_ARGS) *** 391,397 **** spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); BOX *centroid = DatumGetBoxP(in->prefixDatum), ! *box = DatumGetBoxP(in->datum); out->resultType = spgMatchNode; out->result.matchNode.restDatum = BoxPGetDatum(box); --- 391,397 ---- spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); BOX *centroid = DatumGetBoxP(in->prefixDatum), ! *box = DatumGetBoxP(in->leafDatum); out->resultType = spgMatchNode; out->result.matchNode.restDatum = BoxPGetDatum(box); *************** spg_box_quad_picksplit(PG_FUNCTION_ARGS) *** 474,479 **** --- 474,529 ---- } /* + * Check if result of consistent method based on bounding box is exact. + */ + static bool + is_bounding_box_test_exact(StrategyNumber strategy) + { + switch (strategy) + { + case RTLeftStrategyNumber: + case RTOverLeftStrategyNumber: + case RTOverRightStrategyNumber: + case RTRightStrategyNumber: + case RTOverBelowStrategyNumber: + case RTBelowStrategyNumber: + case RTAboveStrategyNumber: + case RTOverAboveStrategyNumber: + return true; + + default: + return false; + } + } + + /* + * Get bounding box for ScanKey. + */ + static BOX * + spg_box_quad_get_scankey_bbox(ScanKey sk, bool *recheck) + { + switch (sk->sk_subtype) + { + case BOXOID: + return DatumGetBoxP(sk->sk_argument); + + case CIRCLEOID: + if (recheck && !is_bounding_box_test_exact(sk->sk_strategy)) + *recheck = true; + return circle_bbox(DatumGetCircleP(sk->sk_argument)); + + case POLYGONOID: + if (recheck && !is_bounding_box_test_exact(sk->sk_strategy)) + *recheck = true; + return &DatumGetPolygonP(sk->sk_argument)->boundbox; + + default: + elog(ERROR, "unrecognized scankey subtype: %d", sk->sk_subtype); + return NULL; + } + } + + /* * SP-GiST inner consistent function */ Datum *************** spg_box_quad_inner_consistent(PG_FUNCTIO *** 515,521 **** centroid = getRangeBox(DatumGetBoxP(in->prefixDatum)); queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *)); for (i = 0; i < in->nkeys; i++) ! queries[i] = getRangeBox(DatumGetBoxP(in->scankeys[i].sk_argument)); /* Allocate enough memory for nodes */ out->nNodes = 0; --- 565,575 ---- centroid = getRangeBox(DatumGetBoxP(in->prefixDatum)); queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *)); for (i = 0; i < in->nkeys; i++) ! { ! BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], NULL); ! ! queries[i] = getRangeBox(box); ! } /* Allocate enough memory for nodes */ out->nNodes = 0; *************** spg_box_quad_leaf_consistent(PG_FUNCTION *** 637,644 **** /* Perform the required comparison(s) */ for (i = 0; i < in->nkeys; i++) { ! StrategyNumber strategy = in->scankeys[i].sk_strategy; ! Datum query = in->scankeys[i].sk_argument; switch (strategy) { --- 691,700 ---- /* Perform the required comparison(s) */ for (i = 0; i < in->nkeys; i++) { ! StrategyNumber strategy = in->scankeys[i].sk_strategy; ! BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], ! &out->recheck); ! Datum query = BoxPGetDatum(box); switch (strategy) { *************** spg_box_quad_leaf_consistent(PG_FUNCTION *** 713,715 **** --- 769,818 ---- PG_RETURN_BOOL(flag); } + + + /* + * SP-GiST config function for 2-D types that are lossy represented by their + * bounding boxes + */ + Datum + spg_bbox_quad_config(PG_FUNCTION_ARGS) + { + spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); + + cfg->prefixType = BOXOID; /* A type represented by its bounding box */ + cfg->labelType = VOIDOID; /* We don't need node labels. */ + cfg->leafType = BOXOID; + cfg->canReturnData = false; + cfg->longValuesOK = false; + + PG_RETURN_VOID(); + } + + /* + * SP-GiST compress function for circles + */ + Datum + spg_circle_quad_compress(PG_FUNCTION_ARGS) + { + CIRCLE *circle = PG_GETARG_CIRCLE_P(0); + BOX *box; + + box = circle_bbox(circle); + + PG_RETURN_BOX_P(box); + } + + /* + * SP-GiST compress function for polygons + */ + Datum + spg_poly_quad_compress(PG_FUNCTION_ARGS) + { + POLYGON *polygon = PG_GETARG_POLYGON_P(0); + BOX *box; + + box = box_copy(&polygon->boundbox); + + PG_RETURN_BOX_P(box); + } diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h new file mode 100644 index f850be4..d1527db *** a/src/include/catalog/pg_amop.h --- b/src/include/catalog/pg_amop.h *************** DATA(insert ( 5000 603 603 11 s 2573 40 *** 858,863 **** --- 858,895 ---- DATA(insert ( 5000 603 603 12 s 2572 4000 0 )); /* + * SP-GiST circle_ops + */ + DATA(insert ( 5007 718 718 1 s 1506 4000 0 )); + DATA(insert ( 5007 718 718 2 s 1507 4000 0 )); + DATA(insert ( 5007 718 718 3 s 1513 4000 0 )); + DATA(insert ( 5007 718 718 4 s 1508 4000 0 )); + DATA(insert ( 5007 718 718 5 s 1509 4000 0 )); + DATA(insert ( 5007 718 718 6 s 1512 4000 0 )); + DATA(insert ( 5007 718 718 7 s 1511 4000 0 )); + DATA(insert ( 5007 718 718 8 s 1510 4000 0 )); + DATA(insert ( 5007 718 718 9 s 2589 4000 0 )); + DATA(insert ( 5007 718 718 10 s 1515 4000 0 )); + DATA(insert ( 5007 718 718 11 s 1514 4000 0 )); + DATA(insert ( 5007 718 718 12 s 2590 4000 0 )); + + /* + * SP-GiST poly_ops (supports polygons) + */ + DATA(insert ( 5008 604 604 1 s 485 4000 0 )); + DATA(insert ( 5008 604 604 2 s 486 4000 0 )); + DATA(insert ( 5008 604 604 3 s 492 4000 0 )); + DATA(insert ( 5008 604 604 4 s 487 4000 0 )); + DATA(insert ( 5008 604 604 5 s 488 4000 0 )); + DATA(insert ( 5008 604 604 6 s 491 4000 0 )); + DATA(insert ( 5008 604 604 7 s 490 4000 0 )); + DATA(insert ( 5008 604 604 8 s 489 4000 0 )); + DATA(insert ( 5008 604 604 9 s 2575 4000 0 )); + DATA(insert ( 5008 604 604 10 s 2574 4000 0 )); + DATA(insert ( 5008 604 604 11 s 2577 4000 0 )); + DATA(insert ( 5008 604 604 12 s 2576 4000 0 )); + + /* * GiST inet_ops */ DATA(insert ( 3550 869 869 3 s 3552 783 0 )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h new file mode 100644 index 1c95846..e21bc8a *** a/src/include/catalog/pg_amproc.h --- b/src/include/catalog/pg_amproc.h *************** DATA(insert ( 5000 603 603 2 5013 )); *** 334,339 **** --- 334,351 ---- DATA(insert ( 5000 603 603 3 5014 )); DATA(insert ( 5000 603 603 4 5015 )); DATA(insert ( 5000 603 603 5 5016 )); + DATA(insert ( 5007 718 718 1 5009 )); + DATA(insert ( 5007 718 718 2 5013 )); + DATA(insert ( 5007 718 718 3 5014 )); + DATA(insert ( 5007 718 718 4 5015 )); + DATA(insert ( 5007 718 718 5 5016 )); + DATA(insert ( 5007 718 718 6 5010 )); + DATA(insert ( 5008 604 604 1 5009 )); + DATA(insert ( 5008 604 604 2 5013 )); + DATA(insert ( 5008 604 604 3 5014 )); + DATA(insert ( 5008 604 604 4 5015 )); + DATA(insert ( 5008 604 604 5 5016 )); + DATA(insert ( 5008 604 604 6 5011 )); /* BRIN opclasses */ /* minmax bytea */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h new file mode 100644 index 28dbc74..ec803d1 *** a/src/include/catalog/pg_opclass.h --- b/src/include/catalog/pg_opclass.h *************** DATA(insert ( 4000 box_ops PGNSP PGUI *** 205,210 **** --- 205,212 ---- DATA(insert ( 4000 quad_point_ops PGNSP PGUID 4015 600 t 0 )); DATA(insert ( 4000 kd_point_ops PGNSP PGUID 4016 600 f 0 )); DATA(insert ( 4000 text_ops PGNSP PGUID 4017 25 t 0 )); + DATA(insert ( 4000 circle_ops PGNSP PGUID 5007 718 t 603 )); + DATA(insert ( 4000 poly_ops PGNSP PGUID 5008 604 t 603 )); DATA(insert ( 403 jsonb_ops PGNSP PGUID 4033 3802 t 0 )); DATA(insert ( 405 jsonb_ops PGNSP PGUID 4034 3802 t 0 )); DATA(insert ( 2742 jsonb_ops PGNSP PGUID 4036 3802 t 25 )); diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h new file mode 100644 index 0d0ba7c..5a6bc1d *** a/src/include/catalog/pg_opfamily.h --- b/src/include/catalog/pg_opfamily.h *************** DATA(insert OID = 4103 ( 3580 range_incl *** 186,190 **** --- 186,192 ---- DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID )); DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID )); DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID )); + DATA(insert OID = 5007 ( 4000 circle_ops PGNSP PGUID )); + DATA(insert OID = 5008 ( 4000 poly_ops PGNSP PGUID )); #endif /* PG_OPFAMILY_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index 93c031a..95e2f34 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DESCR("SP-GiST support for quad tree ove *** 5335,5340 **** --- 5335,5347 ---- DATA(insert OID = 5016 ( spg_box_quad_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_box_quad_leaf_consistent _null_ _null_ _null_ )); DESCR("SP-GiST support for quad tree over box"); + DATA(insert OID = 5009 ( spg_bbox_quad_config PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_bbox_quad_config _null_ _null_ _null_ )); + DESCR("SP-GiST support for quad tree over 2-D types represented by their bounding boxes"); + DATA(insert OID = 5010 ( spg_circle_quad_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 603 "718" _null_ _null_ _null_ _null_ _null_ spg_circle_quad_compress _null_ _null_ _null_ )); + DESCR("SP-GiST support for quad tree over circle"); + DATA(insert OID = 5011 ( spg_poly_quad_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 603 "604" _null_ _null_ _null_ _null_ _null_ spg_poly_quad_compress _null_ _null_ _null_ )); + DESCR("SP-GiST support for quad tree over polygons"); + /* replication slots */ DATA(insert OID = 3779 ( pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 2249 "19 16 16" "{19,16,16,19,3220}" "{i,i,i,o,o}" "{slot_name,immediately_reserve,temporary,slot_name,lsn}" _null_ _null_ pg_create_physical_replication_slot _null_ _null_ _null_ )); DESCR("create a physical replication slot"); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h new file mode 100644 index 44c6381..1804643 *** a/src/include/utils/geo_decls.h --- b/src/include/utils/geo_decls.h *************** typedef struct *** 178,186 **** * in geo_ops.c */ ! /* private point routines */ extern double point_dt(Point *pt1, Point *pt2); extern double point_sl(Point *pt1, Point *pt2); extern double pg_hypot(double x, double y); #endif /* GEO_DECLS_H */ --- 178,188 ---- * in geo_ops.c */ ! /* private routines */ extern double point_dt(Point *pt1, Point *pt2); extern double point_sl(Point *pt1, Point *pt2); extern double pg_hypot(double x, double y); + extern BOX *box_copy(BOX *box); + extern BOX *circle_bbox(CIRCLE *circle); #endif /* GEO_DECLS_H */ diff --git a/src/test/regress/expected/circle.out b/src/test/regress/expected/circle.out new file mode 100644 index 9ba4a04..7e87b56 *** a/src/test/regress/expected/circle.out --- b/src/test/regress/expected/circle.out *************** SELECT '' as five, c1.f1 AS one, c2.f1 A *** 97,99 **** --- 97,339 ---- | <(1,2),3> | <(100,200),10> | 208.370729772479 (5 rows) + -- + -- Test the SP-GiST index + -- + CREATE TEMPORARY TABLE quad_circle_tbl (id int, c circle); + INSERT INTO quad_circle_tbl + SELECT (x - 1) * 100 + y, circle(point(x * 10, y * 10), 1 + (x + y) % 10) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; + INSERT INTO quad_circle_tbl + SELECT i, '<(200, 300), 5>' + FROM generate_series(10001, 11000) AS i; + INSERT INTO quad_circle_tbl + VALUES + (11001, NULL), + (11002, NULL), + (11003, '<(0,100), infinity>'), + (11004, '<(-infinity,0),1000>'), + (11005, '<(infinity,-infinity),infinity>'); + CREATE INDEX quad_circle_tbl_idx ON quad_circle_tbl USING spgist(c); + -- get reference results for ORDER BY distance from seq scan + SET enable_seqscan = ON; + SET enable_indexscan = OFF; + SET enable_bitmapscan = OFF; + CREATE TEMP TABLE quad_circle_tbl_ord_seq1 AS + SELECT rank() OVER (ORDER BY c <-> point '123,456') n, c <-> point '123,456' dist, id + FROM quad_circle_tbl; + CREATE TEMP TABLE quad_circle_tbl_ord_seq2 AS + SELECT rank() OVER (ORDER BY c <-> point '123,456') n, c <-> point '123,456' dist, id + FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + -- check results results from index scan + SET enable_seqscan = OFF; + SET enable_indexscan = OFF; + SET enable_bitmapscan = ON; + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c << circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c << '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c << '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c << circle '<(300,400),200>'; + count + ------- + 891 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &< circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c &< '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c &< '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c &< circle '<(300,400),200>'; + count + ------- + 5901 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c && circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c && '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c && '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c && circle '<(300,400),200>'; + count + ------- + 2334 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &> circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c &> '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c &> '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c &> circle '<(300,400),200>'; + count + ------- + 10000 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c >> circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c >> '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c >> '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c >> circle '<(300,400),200>'; + count + ------- + 4990 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c <<| circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c <<| '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c <<| '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c <<| circle '<(300,400),200>'; + count + ------- + 1890 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &<| circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c &<| '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c &<| '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c &<| circle '<(300,400),200>'; + count + ------- + 6900 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c |&> circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c |&> '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c |&> '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c |&> circle '<(300,400),200>'; + count + ------- + 9000 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c |>> circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c |>> '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c |>> '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c |>> circle '<(300,400),200>'; + count + ------- + 3990 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c @> circle '<(300,400),1>'; + QUERY PLAN + ---------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c @> '<(300,400),1>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c @> '<(300,400),1>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c @> circle '<(300,400),1>'; + count + ------- + 2 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + QUERY PLAN + ------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c <@ '<(300,400),200>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c <@ '<(300,400),200>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + count + ------- + 2181 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c ~= circle '<(300,400),1>'; + QUERY PLAN + ---------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_circle_tbl + Recheck Cond: (c ~= '<(300,400),1>'::circle) + -> Bitmap Index Scan on quad_circle_tbl_idx + Index Cond: (c ~= '<(300,400),1>'::circle) + (5 rows) + + SELECT count(*) FROM quad_circle_tbl WHERE c ~= circle '<(300,400),1>'; + count + ------- + 1 + (1 row) + + RESET enable_seqscan; + RESET enable_indexscan; + RESET enable_bitmapscan; diff --git a/src/test/regress/expected/polygon.out b/src/test/regress/expected/polygon.out new file mode 100644 index 2361274..a9e7752 *** a/src/test/regress/expected/polygon.out --- b/src/test/regress/expected/polygon.out *************** SELECT '(0,0)'::point <-> '((0,0),(1,2), *** 227,229 **** --- 227,467 ---- 0 | 0 | 0 | 1.4142135623731 | 3.2 (1 row) + -- + -- Test the SP-GiST index + -- + CREATE TEMPORARY TABLE quad_poly_tbl (id int, p polygon); + INSERT INTO quad_poly_tbl + SELECT (x - 1) * 100 + y, polygon(circle(point(x * 10, y * 10), 1 + (x + y) % 10)) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; + INSERT INTO quad_poly_tbl + SELECT i, polygon '((200, 300),(210, 310),(230, 290))' + FROM generate_series(10001, 11000) AS i; + INSERT INTO quad_poly_tbl + VALUES + (11001, NULL), + (11002, NULL), + (11003, NULL); + CREATE INDEX quad_poly_tbl_idx ON quad_poly_tbl USING spgist(p); + -- get reference results for ORDER BY distance from seq scan + SET enable_seqscan = ON; + SET enable_indexscan = OFF; + SET enable_bitmapscan = OFF; + CREATE TEMP TABLE quad_poly_tbl_ord_seq1 AS + SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id + FROM quad_poly_tbl; + CREATE TEMP TABLE quad_poly_tbl_ord_seq2 AS + SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id + FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + -- check results results from index scan + SET enable_seqscan = OFF; + SET enable_indexscan = OFF; + SET enable_bitmapscan = ON; + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p << '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p << '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 3890 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p &< '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p &< '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 7900 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p && '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p && '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 977 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p &> '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p &> '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 7000 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p >> '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p >> '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 2990 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + ---------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p <<| '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p <<| '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 1890 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + ---------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p &<| '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p &<| '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 6900 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + ---------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p |&> '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p |&> '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 9000 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + ---------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p |>> '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p |>> '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 3990 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + QUERY PLAN + --------------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p <@ '((300,300),(400,600),(600,500),(700,200))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p <@ '((300,300),(400,600),(600,500),(700,200))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + count + ------- + 831 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; + QUERY PLAN + ----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p @> '((340,550),(343,552),(341,553))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p @> '((340,550),(343,552),(341,553))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; + count + ------- + 1 + (1 row) + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; + QUERY PLAN + ----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_poly_tbl + Recheck Cond: (p ~= '((200,300),(210,310),(230,290))'::polygon) + -> Bitmap Index Scan on quad_poly_tbl_idx + Index Cond: (p ~= '((200,300),(210,310),(230,290))'::polygon) + (5 rows) + + SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; + count + ------- + 1000 + (1 row) + + RESET enable_seqscan; + RESET enable_indexscan; + RESET enable_bitmapscan; diff --git a/src/test/regress/sql/circle.sql b/src/test/regress/sql/circle.sql new file mode 100644 index c0284b2..63da10c *** a/src/test/regress/sql/circle.sql --- b/src/test/regress/sql/circle.sql *************** SELECT '' as five, c1.f1 AS one, c2.f1 A *** 43,45 **** --- 43,140 ---- FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0) ORDER BY distance, area(c1.f1), area(c2.f1); + + -- + -- Test the SP-GiST index + -- + + CREATE TEMPORARY TABLE quad_circle_tbl (id int, c circle); + + INSERT INTO quad_circle_tbl + SELECT (x - 1) * 100 + y, circle(point(x * 10, y * 10), 1 + (x + y) % 10) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; + + INSERT INTO quad_circle_tbl + SELECT i, '<(200, 300), 5>' + FROM generate_series(10001, 11000) AS i; + + INSERT INTO quad_circle_tbl + VALUES + (11001, NULL), + (11002, NULL), + (11003, '<(0,100), infinity>'), + (11004, '<(-infinity,0),1000>'), + (11005, '<(infinity,-infinity),infinity>'); + + CREATE INDEX quad_circle_tbl_idx ON quad_circle_tbl USING spgist(c); + + -- get reference results for ORDER BY distance from seq scan + SET enable_seqscan = ON; + SET enable_indexscan = OFF; + SET enable_bitmapscan = OFF; + + CREATE TEMP TABLE quad_circle_tbl_ord_seq1 AS + SELECT rank() OVER (ORDER BY c <-> point '123,456') n, c <-> point '123,456' dist, id + FROM quad_circle_tbl; + + CREATE TEMP TABLE quad_circle_tbl_ord_seq2 AS + SELECT rank() OVER (ORDER BY c <-> point '123,456') n, c <-> point '123,456' dist, id + FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + + -- check results results from index scan + SET enable_seqscan = OFF; + SET enable_indexscan = OFF; + SET enable_bitmapscan = ON; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c << circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c << circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &< circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c &< circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c && circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c && circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &> circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c &> circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c >> circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c >> circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c <<| circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c <<| circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c &<| circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c &<| circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c |&> circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c |&> circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c |>> circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c |>> circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c @> circle '<(300,400),1>'; + SELECT count(*) FROM quad_circle_tbl WHERE c @> circle '<(300,400),1>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + SELECT count(*) FROM quad_circle_tbl WHERE c <@ circle '<(300,400),200>'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_circle_tbl WHERE c ~= circle '<(300,400),1>'; + SELECT count(*) FROM quad_circle_tbl WHERE c ~= circle '<(300,400),1>'; + + RESET enable_seqscan; + RESET enable_indexscan; + RESET enable_bitmapscan; diff --git a/src/test/regress/sql/polygon.sql b/src/test/regress/sql/polygon.sql new file mode 100644 index 7ac8079..c58277b *** a/src/test/regress/sql/polygon.sql --- b/src/test/regress/sql/polygon.sql *************** SELECT '(0,0)'::point <-> '((0,0),(1,2), *** 116,118 **** --- 116,211 ---- '(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; + + -- + -- Test the SP-GiST index + -- + + CREATE TEMPORARY TABLE quad_poly_tbl (id int, p polygon); + + INSERT INTO quad_poly_tbl + SELECT (x - 1) * 100 + y, polygon(circle(point(x * 10, y * 10), 1 + (x + y) % 10)) + FROM generate_series(1, 100) x, + generate_series(1, 100) y; + + INSERT INTO quad_poly_tbl + SELECT i, polygon '((200, 300),(210, 310),(230, 290))' + FROM generate_series(10001, 11000) AS i; + + INSERT INTO quad_poly_tbl + VALUES + (11001, NULL), + (11002, NULL), + (11003, NULL); + + CREATE INDEX quad_poly_tbl_idx ON quad_poly_tbl USING spgist(p); + + -- get reference results for ORDER BY distance from seq scan + SET enable_seqscan = ON; + SET enable_indexscan = OFF; + SET enable_bitmapscan = OFF; + + CREATE TEMP TABLE quad_poly_tbl_ord_seq1 AS + SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id + FROM quad_poly_tbl; + + CREATE TEMP TABLE quad_poly_tbl_ord_seq2 AS + SELECT rank() OVER (ORDER BY p <-> point '123,456') n, p <-> point '123,456' dist, id + FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + + -- check results results from index scan + SET enable_seqscan = OFF; + SET enable_indexscan = OFF; + SET enable_bitmapscan = ON; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p << polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p &< polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p && polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p &> polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p >> polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p <<| polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p &<| polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p |&> polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p |>> polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + SELECT count(*) FROM quad_poly_tbl WHERE p <@ polygon '((300,300),(400,600),(600,500),(700,200))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; + SELECT count(*) FROM quad_poly_tbl WHERE p @> polygon '((340,550),(343,552),(341,553))'; + + EXPLAIN (COSTS OFF) + SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; + SELECT count(*) FROM quad_poly_tbl WHERE p ~= polygon '((200, 300),(210, 310),(230, 290))'; + + RESET enable_seqscan; + RESET enable_indexscan; + RESET enable_bitmapscan;