1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Add the sqlite3_rtree_query_callback() API to the RTree virtual table.

(Cherrypick from the sessions branch.)

FossilOrigin-Name: af2cbe64adab5f9e3b0f3da00d06428088589d7f
This commit is contained in:
drh
2014-04-28 17:56:19 +00:00
parent 1f8bb4b0a8
commit 65e6b0dd12
14 changed files with 1425 additions and 801 deletions

File diff suppressed because it is too large Load Diff

View File

@ -120,12 +120,13 @@ proc execsql_intout {sql} {
# Test that it is possible to open an existing database that contains
# r-tree tables.
#
do_test rtree-1.4.1 {
execsql {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
INSERT INTO t1 VALUES(1, 5.0, 10.0);
INSERT INTO t1 VALUES(2, 15.0, 20.0);
}
do_execsql_test rtree-1.4.1a {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
INSERT INTO t1 VALUES(1, 5.0, 10.0);
SELECT substr(hex(data),1,40) FROM t1_node;
} {00000001000000000000000140A0000041200000}
do_execsql_test rtree-1.4.1b {
INSERT INTO t1 VALUES(2, 15.0, 20.0);
} {}
do_test rtree-1.4.2 {
db close
@ -435,16 +436,18 @@ do_test rtree-11.2 {
# Test on-conflict clause handling.
#
db_delete_and_reopen
do_execsql_test 12.0 {
do_execsql_test 12.0.1 {
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
SELECT substr(hex(data),1,56) FROM t1_node;
} {00000001000000000000000100000001000000020000000300000004}
do_execsql_test 12.0.2 {
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
CREATE TABLE source(idx, x1, x2, y1, y2);
INSERT INTO source VALUES(5, 8, 8, 8, 8);
INSERT INTO source VALUES(2, 7, 7, 7, 7);
}
db_save_and_close
foreach {tn sql_template testdata} {

View File

@ -57,31 +57,31 @@ do_test rtree6-1.1 {
do_test rtree6-1.2 {
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
} {Ea}
} {E0}
do_test rtree6-1.3 {
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
} {Ca}
} {C0}
do_test rtree6-1.4 {
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
} {Ca}
} {C0}
do_test rtree6-1.5 {
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
} {Ca}
} {C0}
do_eqp_test rtree6.2.1 {
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_eqp_test rtree6.2.2 {
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
@ -95,7 +95,7 @@ do_eqp_test rtree6.2.3 {
do_eqp_test rtree6.2.4 {
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb}
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
}
@ -126,7 +126,7 @@ do_test rtree6.3.2 {
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
do_test rtree6.3.3 {
rtree_strategy {
SELECT * FROM t3 WHERE
@ -137,7 +137,7 @@ do_test rtree6.3.3 {
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
do_execsql_test rtree6-3.4 {
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1

View File

@ -41,7 +41,7 @@ ifcapable rtree_int_only {
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
SELECT rtreenode(2, data) FROM t1_node;
}
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
}
finish_test

View File

@ -29,7 +29,7 @@ do_eqp_test 1.1 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
} {
0 0 1 {SCAN TABLE t}
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 1.2 {
@ -37,7 +37,7 @@ do_eqp_test 1.2 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
} {
0 0 0 {SCAN TABLE t}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 1.3 {
@ -45,7 +45,7 @@ do_eqp_test 1.3 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
} {
0 0 0 {SCAN TABLE t}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 1.5 {
@ -82,7 +82,7 @@ do_eqp_test 2.1 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
} {
0 0 1 {SCAN TABLE t}
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 2.2 {
@ -90,7 +90,7 @@ do_eqp_test 2.2 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
} {
0 0 0 {SCAN TABLE t}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 2.3 {
@ -98,7 +98,7 @@ do_eqp_test 2.3 {
WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
} {
0 0 0 {SCAN TABLE t}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:DdBcDbBa}
0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
}
do_eqp_test 2.5 {
@ -271,4 +271,3 @@ ifcapable rtree {
finish_test

129
ext/rtree/rtreeE.test Normal file
View File

@ -0,0 +1,129 @@
# 2010 August 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains tests for the r-tree module. Specifically, it tests
# that new-style custom r-tree queries (geometry callbacks) work.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return }
ifcapable rtree_int_only { finish_test; return }
#-------------------------------------------------------------------------
# Test the example 2d "circle" geometry callback.
#
register_circle_geom db
do_execsql_test rtreeE-1.1 {
PRAGMA page_size=512;
CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1);
/* A tight pattern of small boxes near 0,0 */
WITH RECURSIVE
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
INSERT INTO rt1 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
/* A looser pattern of small boxes near 100, 0 */
WITH RECURSIVE
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
INSERT INTO rt1 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
/* A looser pattern of larger boxes near 0, 200 */
WITH RECURSIVE
x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
INSERT INTO rt1 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
} {}
# Queries against each of the three clusters */
do_execsql_test rtreeE-1.1 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) ORDER BY id;
} {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24}
do_execsql_test rtreeE-1.2 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) ORDER BY id;
} {100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124}
do_execsql_test rtreeE-1.3 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) ORDER BY id;
} {200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224}
# The Qcircle geometry function gives a lower score to larger leaf-nodes.
# This causes the 200s to sort before the 100s and the 0s to sort before
# last.
#
do_execsql_test rtreeE-1.4 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0
} {200 100 0}
# Exclude odd rowids on a depth-first search
do_execsql_test rtreeE-1.5 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,4) ORDER BY +id
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
# Exclude odd rowids on a breadth-first search.
do_execsql_test rtreeE-1.6 {
SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id
} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
# Construct a large 2-D RTree with thousands of random entries.
#
do_test rtreeE-2.1 {
db eval {
CREATE TABLE t2(id,x0,x1,y0,y1);
CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
BEGIN;
}
expr srand(0)
for {set i 1} {$i<=10000} {incr i} {
set dx [expr {int(rand()*40)+1}]
set dy [expr {int(rand()*40)+1}]
set x0 [expr {int(rand()*(10000 - $dx))}]
set x1 [expr {$x0+$dx}]
set y0 [expr {int(rand()*(10000 - $dy))}]
set y1 [expr {$y0+$dy}]
set id [expr {$i+10000}]
db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)}
}
db eval {
INSERT INTO rt2 SELECT * FROM t2;
COMMIT;
}
} {}
for {set i 1} {$i<=200} {incr i} {
set dx [expr {int(rand()*100)}]
set dy [expr {int(rand()*100)}]
set x0 [expr {int(rand()*(10000 - $dx))}]
set x1 [expr {$x0+$dx}]
set y0 [expr {int(rand()*(10000 - $dy))}]
set y1 [expr {$y0+$dy}]
set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}]
do_execsql_test rtreeE-2.2.$i {
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id
} $ans
}
# Run query that have very deep priority queues
#
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}]
do_execsql_test rtreeE-2.3 {
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id
} $ans
set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}]
do_execsql_test rtreeE-2.4 {
SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id
} $ans
finish_test

View File

@ -21,6 +21,16 @@ extern "C" {
#endif
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
/* The double-precision datatype used by RTree depends on the
** SQLITE_RTREE_INT_ONLY compile-time option.
*/
#ifdef SQLITE_RTREE_INT_ONLY
typedef sqlite3_int64 sqlite3_rtree_dbl;
#else
typedef double sqlite3_rtree_dbl;
#endif
/*
** Register a geometry callback named zGeom that can be used as part of an
@ -31,11 +41,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
int sqlite3_rtree_geometry_callback(
sqlite3 *db,
const char *zGeom,
#ifdef SQLITE_RTREE_INT_ONLY
int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
#else
int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
#endif
int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
void *pContext
);
@ -47,11 +53,60 @@ int sqlite3_rtree_geometry_callback(
struct sqlite3_rtree_geometry {
void *pContext; /* Copy of pContext passed to s_r_g_c() */
int nParam; /* Size of array aParam[] */
double *aParam; /* Parameters passed to SQL geom function */
sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
void *pUser; /* Callback implementation user data */
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
};
/*
** Register a 2nd-generation geometry callback named zScore that can be
** used as part of an R-Tree geometry query as follows:
**
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
*/
int sqlite3_rtree_query_callback(
sqlite3 *db,
const char *zQueryFunc,
int (*xQueryFunc)(sqlite3_rtree_query_info*),
void *pContext,
void (*xDestructor)(void*)
);
/*
** A pointer to a structure of the following type is passed as the
** argument to scored geometry callback registered using
** sqlite3_rtree_query_callback().
**
** Note that the first 5 fields of this structure are identical to
** sqlite3_rtree_geometry. This structure is a subclass of
** sqlite3_rtree_geometry.
*/
struct sqlite3_rtree_query_info {
void *pContext; /* pContext from when function registered */
int nParam; /* Number of function parameters */
sqlite3_rtree_dbl *aParam; /* value of function parameters */
void *pUser; /* callback can use this, if desired */
void (*xDelUser)(void*); /* function to free pUser */
sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
unsigned int *anQueue; /* Number of pending entries in the queue */
int nCoord; /* Number of coordinates */
int iLevel; /* Level of current node or entry */
int mxLevel; /* The largest iLevel value in the tree */
sqlite3_int64 iRowid; /* Rowid for current entry */
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
int eParentWithin; /* Visibility of parent node */
int eWithin; /* OUT: Visiblity */
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
};
/*
** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
*/
#define NOT_WITHIN 0 /* Object completely outside of query region */
#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
#define FULLY_WITHIN 2 /* Object fully contained within query region */
#ifdef __cplusplus
} /* end of the 'extern "C"' block */