TDengine/source/libs/geometry/test/geomRelationFuncTest.cpp
Dingle Zhang 984211f3b4
Feature/3.0 geometry (#21037)
* Add GEOMETRY data type and make sql.c able to parse it. The GEMETRY works like BINARY so far.

* add GEOMETRY type into gConvertTypes to fix some issues like DELETE calling

* change some test cases to make sure no same timestamp is inserted, and add my smoketest.sh

* Add a function MakePoint() and introduce a lib geometry

* implement sql functions GeomFromText() and AsText()

* Use GEOS *_r funcions instead for thread safety

* Handle with TSDB_DATA_TYPE_GEOMETRY when INSERT geometry data by converting WKT.
Add geosWrapper to wrap the basic GEOS functions for TDEngine.

* refactor AsText and MakePoint functions to be like GeomFromText

* Show WKT when print geometry data in screen
Dump hex data when dump geometry data in a file

* define TYPE_BYTES item for TSDB_DATA_TYPE_GEOMETRY, which casued some strange issues.

* set number of decimals of WKT to 6

* Implement SQL function Intersects()

* refactor geometry sql functions

* Add geosErrMsgeHandler() to get the GEOS error detail

* use threadlocal to instantiate SGeosContext
call destroyGeosContext() only if the thread exists

* remove SGeosContext *context param for all geometry functions since we use thread local one,
so that all caller do not need to know the context.

* Modify Intersects() to call PreparedIntersects() when one of param is a constant, which has higher performance.

* rename prepareFn() to initCtxFn() to avoid confusion with PreparedFn

* Add prefix "ST_" for all geometry functions

* move getThreadLocalGeosCtx() and destroyThreadLocalGeosCtx() into util,
so that all unit test tools can compile

* Add unit test for geometry lib, only test MakePoint so far

* refactor and enhance existing cases in geomFuncTest

* implement NULL type and NULL value test for geomFuncTest

* add test on geomFromText()

* add unit test on AsText() in geomFuncTest

* combine some makePointFunction test items

* add intersectsFunctionTwoColumns test
refactor on callGeomFromTextWrapper functions

* enhance intersectsFunction test to add cases like input constant , NULL type, NULL value, or wrong content

* add more cases into intersectsFunction test

* Add basic test on geometry in system test

* Add ST_GeomFromText and ST_AsText function test in system test on geometry

* add ST_Intersects function test in system test on geometry

* support to check expectedErrno in system test on geometry

* adjust geomTest unit test and geometry system test

* add geometry data type and functions in doc english version

* implement touchesFunction() in geometry lib
refactor geometry relation functions model

* separate gemFuncTest into several src files

* add unit test on touchesFunction

* support sql function ST_Touches()
add system test on ST_Touches

* add docs for ST_Touches()

* Add ST_Contains()

* Add ST_Covers()

* Add ST_Equals()

* add swapAllowed param for geomRelationFunction()
read geom2 earlier intead of at doGeosRelation()

* Add ST_ContainsProperly()

* build on windows

* Merge from 3.0 to 3.0_geometry

* change macro definition TSDB_DATA_TYPE_GEOMETRY as the last one for compatibility

* change '\\NULL' to 'NULL' back in shellDumpFieldToFile()

* add /usr/local/include into include directory

* add /usr/local/inlcude and /usr/local/lib in cmake.platform for DARWIN
2023-05-24 15:36:46 +08:00

239 lines
10 KiB
C++

/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "geomFuncTestUtil.h"
void callGeomRelationFuncAndCompareResult(FScalarExecProcess geomRelationFunc,
SScalarParam *pInput, int32_t rowNum,
int32_t expectedCode, int8_t expectedResult[]) {
SScalarParam *pOutput;
makeOneScalarParam(&pOutput, TSDB_DATA_TYPE_BOOL, 0, 0, rowNum);
int32_t code = geomRelationFunc(pInput, 2, pOutput);
ASSERT_EQ(code, expectedCode);
if (code == TSDB_CODE_SUCCESS) {
int8_t res = -1;
for (int32_t i = 0; i < rowNum; ++i) {
bool isNull1 = colDataIsNull_s(pOutput->columnData, i);
if (isNull1) {
res = -1;
}
else {
res = *(bool*)colDataGetData(pOutput->columnData, i);
}
ASSERT_EQ(res, expectedResult[i]);
}
}
destroyScalarParam(pOutput, 1);
destroyScalarParam(pInput, 2);
}
/*
-- Use the following SQL to get expected results for all relation functions in PostgreSQL with PostGIS
WITH geom_str AS
(SELECT 'POINT(3.5 7.0)' AS g1, 'POINT(3.5 7.0)' AS g2
UNION ALL
SELECT 'POINT(3.0 3.0)' AS g1, 'LINESTRING(1.0 1.0, 2.0 2.0, 5.0 6.0)' AS g2
UNION ALL
SELECT 'POINT(3.0 6.0)' AS g1, 'POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))' AS g2
UNION ALL
SELECT 'LINESTRING(1.0 1.0, 2.0 2.0, 5.0 5.0)' AS g1, 'LINESTRING(1.0 4.0, 2.0 3.0, 5.0 0.0)' AS g2
UNION ALL
SELECT 'LINESTRING(3.0 7.0, 4.0 7.0, 5.0 7.0)' AS g1, 'POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))' AS g2
UNION ALL
SELECT 'POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))' AS g1, 'POLYGON((5.0 6.0, 7.0 6.0, 7.0 8.0, 5.0 8.0, 5.0 6.0))' AS g2
)
SELECT ST_Intersects(g1, g2), ST_Equals(g1, g2), ST_Touches(g1, g2), ST_Covers(g1, g2), ST_Contains(g1, g2), ST_ContainsProperly(g1, g2) FROM geom_str
*/
void geomRelationFuncTest(FScalarExecProcess geomRelationFunc, int8_t expectedResults[6][6]) {
const int32_t rowNum = 6;
char strArray1[rowNum][TSDB_MAX_BINARY_LEN];
STR_TO_VARSTR(strArray1[0], "POINT(3.5 7.0)");
STR_TO_VARSTR(strArray1[1], "POINT(3.0 3.0)");
STR_TO_VARSTR(strArray1[2], "POINT(3.0 6.0)");
STR_TO_VARSTR(strArray1[3], "LINESTRING(1.0 1.0, 2.0 2.0, 5.0 5.0)");
STR_TO_VARSTR(strArray1[4], "LINESTRING(3.0 7.0, 4.0 7.0, 5.0 7.0)");
STR_TO_VARSTR(strArray1[5], "POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))");
TDRowValT valTypeArray1[rowNum] = {TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM};
char strArray2[rowNum][TSDB_MAX_BINARY_LEN];
STR_TO_VARSTR(strArray2[0], "POINT(3.5 7.0)");
STR_TO_VARSTR(strArray2[1], "LINESTRING(1.0 1.0, 2.0 2.0, 5.0 6.0)");
STR_TO_VARSTR(strArray2[2], "POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))");
STR_TO_VARSTR(strArray2[3], "LINESTRING(1.0 4.0, 2.0 3.0, 5.0 0.0)");
STR_TO_VARSTR(strArray2[4], "POLYGON((3.0 6.0, 5.0 6.0, 5.0 8.0, 3.0 8.0, 3.0 6.0))");
STR_TO_VARSTR(strArray2[5], "POLYGON((5.0 6.0, 7.0 6.0, 7.0 8.0, 5.0 8.0, 5.0 6.0))");
TDRowValT valTypeArray2[rowNum] = {TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM, TD_VTYPE_NORM};
// two columns input
SScalarParam *pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput); //pInput come from GeomFromText()
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResults[0]);
// swap two columns
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput);
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResults[1]);
// constant and column input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray1, valTypeArray1, 1, pInput);
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResults[2]);
// column and constant input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput);
callGeomFromTextWrapper5(strArray2, valTypeArray2, 1, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResults[3]);
// two constants input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray1, valTypeArray1, 1, pInput);
callGeomFromTextWrapper5(strArray2, valTypeArray2, 1, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, 1, TSDB_CODE_SUCCESS, expectedResults[4]);
// two columns with NULL value input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
valTypeArray1[2] = TD_VTYPE_NULL;
valTypeArray2[4] = TD_VTYPE_NULL;
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput);
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResults[5]);
// first NULL type input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
setScalarParam(pInput, TSDB_DATA_TYPE_NULL, 0, 0, 1);
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
int8_t expectedResultNullType[rowNum] = {-1, -1, -1, -1, -1, -1};
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResultNullType);
// second NULL type input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput);
setScalarParam(pInput + 1, TSDB_DATA_TYPE_NULL, 0, 0, 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResultNullType);
// first empty content input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
char strInput[TSDB_MAX_BINARY_LEN];
STR_TO_VARSTR(strInput, "");
setScalarParam(pInput, TSDB_DATA_TYPE_GEOMETRY, strInput, valTypeArray1, 1);
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_SUCCESS, expectedResultNullType);
// first wrong type input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
int32_t intInput = 3;
setScalarParam(pInput, TSDB_DATA_TYPE_INT, &intInput, valTypeArray1, 1);
callGeomFromTextWrapper5(strArray2, valTypeArray2, rowNum, pInput + 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_FUNC_FUNTION_PARA_VALUE, 0);
// second wrong content input
pInput = (SScalarParam *)taosMemoryCalloc(2, sizeof(SScalarParam));
STR_TO_VARSTR(strInput, "XXX");
callGeomFromTextWrapper5(strArray1, valTypeArray1, rowNum, pInput);
setScalarParam(pInput + 1, TSDB_DATA_TYPE_GEOMETRY, strInput, valTypeArray2, 1);
callGeomRelationFuncAndCompareResult(geomRelationFunc, pInput, rowNum, TSDB_CODE_FUNC_FUNTION_PARA_VALUE, 0);
}
TEST(GeomRelationFuncTest, intersectsFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{1, 0, 1, 1, 1, 1}, // two columns
{1, 0, 1, 1, 1, 1}, // two columns swpped
{1, 0, 1, 0, 1, 0}, // first constant
{1, 0, 0, 0, 1, 1}, // second constant
{1}, // two constant
{1, 0, -1, 1, -1, 1} // with Null value
};
geomRelationFuncTest(intersectsFunction, expectedResults);
}
TEST(GeomRelationFuncTest, equalsFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{1, 0, 0, 0, 0, 0}, // two columns
{1, 0, 0, 0, 0, 0}, // two columns swapped
{1, 0, 0, 0, 0, 0}, // first constant
{1, 0, 0, 0, 0, 0}, // second constant
{1}, // two constant
{1, 0, -1, 0, -1, 0} // with Null value
};
geomRelationFuncTest(equalsFunction, expectedResults);
}
TEST(GeomRelationFuncTest, touchesFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{0, 0, 1, 0, 0, 1}, // two columns
{0, 0, 1, 0, 0, 1}, // two columns swapped
{0, 0, 0, 0, 0, 0}, // first constant
{0, 0, 0, 0, 0, 0}, // second constant
{0}, // two constant
{0, 0, -1, 0, -1, 1} // with Null value
};
geomRelationFuncTest(touchesFunction, expectedResults);
}
TEST(GeomRelationFuncTest, coversFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{1, 0, 0, 0, 0, 0}, // two columns
{1, 0, 1, 0, 1, 0}, // two columns swapped
{1, 0, 0, 0, 0, 0}, // first constant
{1, 0, 0, 0, 1, 1}, // second constant
{1}, // two constant
{1, 0, -1, 0, -1, 0} // with Null value
};
geomRelationFuncTest(coversFunction, expectedResults);
}
TEST(GeomRelationFuncTest, containsFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{1, 0, 0, 0, 0, 0}, // two columns
{1, 0, 0, 0, 1, 0}, // two columns swapped
{1, 0, 0, 0, 0, 0}, // first constant
{1, 0, 0, 0, 1, 1}, // second constant
{1}, // two constant
{1, 0, -1, 0, -1, 0} // with Null value
};
geomRelationFuncTest(containsFunction, expectedResults);
}
TEST(GeomRelationFuncTest, containsProperlyFunction) {
// 1: true, 0: false, -1: null
int8_t expectedResults[6][6] = {
{1, 0, 0, 0, 0, 0}, // two columns
{1, 0, 0, 0, 0, 0}, // two columns swapped
{1, 0, 0, 0, 0, 0}, // first constant
{1, 0, 0, 0, 1, 1}, // second constant
{1}, // two constant
{1, 0, -1, 0, -1, 0} // with Null value
};
geomRelationFuncTest(containsProperlyFunction, expectedResults);
}