445 lines
13 KiB
C
445 lines
13 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 <geos_c.h>
|
|
#include "geosWrapper.h"
|
|
#include "geomFunc.h"
|
|
#include "querynodes.h"
|
|
#include "tdatablock.h"
|
|
#include "sclInt.h"
|
|
#include "sclvector.h"
|
|
|
|
typedef int32_t (*_geomDoRelationFunc_t)(const GEOSGeometry *geom1, const GEOSPreparedGeometry *preparedGeom1, const GEOSGeometry *geom2,
|
|
bool swapped, char *res);
|
|
|
|
typedef int32_t (*_geomInitCtxFunc_t)();
|
|
typedef int32_t (*_geomExecuteOneParamFunc_t)(SColumnInfoData *pInputData, int32_t i, SColumnInfoData *pOutputData);
|
|
typedef int32_t (*_geomExecuteTwoParamsFunc_t)(SColumnInfoData *pInputData[], int32_t iLeft, int32_t iRight,
|
|
SColumnInfoData *pOutputData);
|
|
|
|
// output is with VARSTR format
|
|
// need to call taosMemoryFree(*output) later
|
|
int32_t doMakePointFunc(double x, double y, unsigned char **output) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
unsigned char *outputGeom = NULL;
|
|
size_t size = 0;
|
|
code = doMakePoint(x, y, &outputGeom, &size);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
*output = taosMemoryCalloc(1, size + VARSTR_HEADER_SIZE);
|
|
if (*output == NULL) {
|
|
code = TSDB_CODE_OUT_OF_MEMORY;
|
|
goto _exit;
|
|
}
|
|
|
|
memcpy(varDataVal(*output), outputGeom, size);
|
|
varDataSetLen(*output, size);
|
|
code = TSDB_CODE_SUCCESS;
|
|
|
|
_exit:
|
|
geosFreeBuffer(outputGeom);
|
|
|
|
return code;
|
|
}
|
|
|
|
// both input and output are with VARSTR format
|
|
// need to call taosMemoryFree(*output) later
|
|
int32_t doGeomFromTextFunc(const char *input, unsigned char **output) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
if ((varDataLen(input)) == 0) { //empty value
|
|
*output = NULL;
|
|
return TSDB_CODE_SUCCESS;
|
|
}
|
|
|
|
// make input as a zero ending string
|
|
char *end = varDataVal(input) + varDataLen(input);
|
|
char endValue = *end;
|
|
*end = 0;
|
|
|
|
unsigned char *outputGeom = NULL;
|
|
size_t size = 0;
|
|
|
|
code = doGeomFromText(varDataVal(input), &outputGeom, &size);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
*output = taosMemoryCalloc(1, size + VARSTR_HEADER_SIZE);
|
|
if (*output == NULL) {
|
|
code = TSDB_CODE_OUT_OF_MEMORY;
|
|
goto _exit;
|
|
}
|
|
|
|
memcpy(varDataVal(*output), outputGeom, size);
|
|
varDataSetLen(*output, size);
|
|
code = TSDB_CODE_SUCCESS;
|
|
|
|
_exit:
|
|
geosFreeBuffer(outputGeom);
|
|
|
|
*end = endValue; //recover the input string
|
|
|
|
return code;
|
|
}
|
|
|
|
// both input and output are with VARSTR format
|
|
// need to call taosMemoryFree(*output) later
|
|
int32_t doAsTextFunc(unsigned char *input, char **output) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
if ((varDataLen(input)) == 0) { //empty value
|
|
*output = NULL;
|
|
return TSDB_CODE_SUCCESS;
|
|
}
|
|
|
|
char *outputWKT = NULL;
|
|
code = doAsText(varDataVal(input), varDataLen(input), &outputWKT);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
size_t size = strlen(outputWKT);
|
|
*output = taosMemoryCalloc(1, size + VARSTR_HEADER_SIZE);
|
|
if (*output == NULL) {
|
|
code = TSDB_CODE_OUT_OF_MEMORY;
|
|
goto _exit;
|
|
}
|
|
|
|
memcpy(varDataVal(*output), outputWKT, size);
|
|
varDataSetLen(*output, size);
|
|
code = TSDB_CODE_SUCCESS;
|
|
|
|
_exit:
|
|
geosFreeBuffer(outputWKT);
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t executeMakePointFunc(SColumnInfoData *pInputData[], int32_t iLeft, int32_t iRight,
|
|
SColumnInfoData *pOutputData) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
_getDoubleValue_fn_t getDoubleValueFn[2];
|
|
getDoubleValueFn[0]= getVectorDoubleValueFn(pInputData[0]->info.type);
|
|
getDoubleValueFn[1]= getVectorDoubleValueFn(pInputData[1]->info.type);
|
|
|
|
unsigned char *output = NULL;
|
|
code = doMakePointFunc(getDoubleValueFn[0](pInputData[0]->pData, iLeft), getDoubleValueFn[1](pInputData[1]->pData, iRight), &output);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
colDataSetVal(pOutputData, TMAX(iLeft, iRight), output, (output == NULL));
|
|
|
|
_exit:
|
|
if (output) {
|
|
taosMemoryFree(output);
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t executeGeomFromTextFunc(SColumnInfoData *pInputData, int32_t i, SColumnInfoData *pOutputData) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
char *input = colDataGetData(pInputData, i);
|
|
unsigned char *output = NULL;
|
|
code = doGeomFromTextFunc(input, &output);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
colDataSetVal(pOutputData, i, output, (output == NULL));
|
|
|
|
_exit:
|
|
if (output) {
|
|
taosMemoryFree(output);
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t executeAsTextFunc(SColumnInfoData *pInputData, int32_t i, SColumnInfoData *pOutputData) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
unsigned char *input = colDataGetData(pInputData, i);
|
|
char *output = NULL;
|
|
code = doAsTextFunc(input, &output);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
colDataSetVal(pOutputData, i, output, (output == NULL));
|
|
|
|
_exit:
|
|
if (output) {
|
|
taosMemoryFree(output);
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t executeRelationFunc(const GEOSGeometry *geom1, const GEOSPreparedGeometry *preparedGeom1,
|
|
const GEOSGeometry *geom2, int32_t i,
|
|
bool swapped, SColumnInfoData *pOutputData,
|
|
_geomDoRelationFunc_t doRelationFn) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
char res = 0;
|
|
|
|
if (!geom1 || !geom2) { //if empty input value
|
|
res = -1;
|
|
code = TSDB_CODE_SUCCESS;
|
|
}
|
|
else {
|
|
code = doRelationFn(geom1, preparedGeom1, geom2, swapped, &res);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
}
|
|
|
|
colDataSetVal(pOutputData, i, &res, (res==-1));
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t geomOneParamFunction(SScalarParam *pInput, SScalarParam *pOutput,
|
|
_geomInitCtxFunc_t initCtxFn, _geomExecuteOneParamFunc_t executeOneParamFn) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
code = initCtxFn();
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
|
|
SColumnInfoData *pInputData = pInput->columnData;
|
|
SColumnInfoData *pOutputData = pOutput->columnData;
|
|
pOutput->numOfRows = pInput->numOfRows;
|
|
|
|
if (IS_NULL_TYPE(GET_PARAM_TYPE(pInput))) {
|
|
colDataSetNNULL(pOutputData, 0, pInput->numOfRows);
|
|
code = TSDB_CODE_SUCCESS;
|
|
}
|
|
else {
|
|
for (int32_t i = 0; i < pInput->numOfRows; ++i) {
|
|
if (colDataIsNull_s(pInputData, i)) {
|
|
colDataSetNULL(pOutputData, i);
|
|
code = TSDB_CODE_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
code = executeOneParamFn(pInputData, i, pOutputData);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
}
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t geomTwoParamsFunction(SScalarParam *pInput, SScalarParam *pOutput,
|
|
_geomInitCtxFunc_t initCtxFn, _geomExecuteTwoParamsFunc_t executeTwoParamsFn) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
code = initCtxFn();
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
|
|
SColumnInfoData *pInputData[2];
|
|
SColumnInfoData *pOutputData = pOutput->columnData;
|
|
pInputData[0] = pInput[0].columnData;
|
|
pInputData[1] = pInput[1].columnData;
|
|
|
|
bool hasNullType = (IS_NULL_TYPE(GET_PARAM_TYPE(&pInput[0])) ||
|
|
IS_NULL_TYPE(GET_PARAM_TYPE(&pInput[1])));
|
|
bool isConstantLeft = (pInput[0].numOfRows == 1);
|
|
bool isConstantRight = (pInput[1].numOfRows == 1);
|
|
int32_t numOfRows = TMAX(pInput[0].numOfRows, pInput[1].numOfRows);
|
|
pOutput->numOfRows = numOfRows;
|
|
|
|
if (hasNullType || // one of operant is NULL type
|
|
(isConstantLeft && colDataIsNull_s(pInputData[0], 0)) || // left operand is constant NULL
|
|
(isConstantRight && colDataIsNull_s(pInputData[1], 0))) { // right operand is constant NULL
|
|
colDataSetNNULL(pOutputData, 0, numOfRows);
|
|
code = TSDB_CODE_SUCCESS;
|
|
}
|
|
else {
|
|
int32_t iLeft = 0;
|
|
int32_t iRight = 0;
|
|
for (int32_t i = 0; i < numOfRows; ++i) {
|
|
iLeft = isConstantLeft ? 0 : i;
|
|
iRight = isConstantRight ? 0 : i;
|
|
|
|
if ((!isConstantLeft && colDataIsNull_s(pInputData[0], iLeft)) ||
|
|
(!isConstantRight && colDataIsNull_s(pInputData[1], iRight))) {
|
|
colDataSetNULL(pOutputData, i);
|
|
code = TSDB_CODE_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
code = executeTwoParamsFn(pInputData, iLeft, iRight, pOutputData);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
}
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t geomRelationFunction(SScalarParam *pInput, SScalarParam *pOutput,
|
|
bool swapAllowed, _geomDoRelationFunc_t doRelationFn) {
|
|
int32_t code = TSDB_CODE_FAILED;
|
|
|
|
code = initCtxRelationFunc();
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
return code;
|
|
}
|
|
|
|
// handle with all NULL output
|
|
bool hasNullType = (IS_NULL_TYPE(GET_PARAM_TYPE(&pInput[0])) ||
|
|
IS_NULL_TYPE(GET_PARAM_TYPE(&pInput[1])));
|
|
bool isConstant1 = (pInput[0].numOfRows == 1);
|
|
bool isConstant2 = (pInput[1].numOfRows == 1);
|
|
int32_t numOfRows = TMAX(pInput[0].numOfRows, pInput[1].numOfRows);
|
|
pOutput->numOfRows = numOfRows;
|
|
SColumnInfoData *pOutputData = pOutput->columnData;
|
|
|
|
if (hasNullType || // at least one of operant is NULL type
|
|
(isConstant1 && colDataIsNull_s(pInput[0].columnData, 0)) || // left operand is constant NULL
|
|
(isConstant2 && colDataIsNull_s(pInput[1].columnData, 0))) { // right operand is constant NULL
|
|
colDataSetNNULL(pOutputData, 0, numOfRows);
|
|
code = TSDB_CODE_SUCCESS;
|
|
return code;
|
|
}
|
|
|
|
bool swapped = false;
|
|
SColumnInfoData *pInputData[2];
|
|
|
|
// swap two input data to make sure input data 0 is constant if swapAllowed and only isConstant2 is true
|
|
if (swapAllowed &&
|
|
!isConstant1 && isConstant2) {
|
|
pInputData[0] = pInput[1].columnData;
|
|
pInputData[1] = pInput[0].columnData;
|
|
|
|
isConstant1 = true;
|
|
isConstant2 = false;
|
|
swapped = true;
|
|
}
|
|
else {
|
|
pInputData[0] = pInput[0].columnData;
|
|
pInputData[1] = pInput[1].columnData;
|
|
}
|
|
|
|
GEOSGeometry *geom1 = NULL;
|
|
GEOSGeometry *geom2 = NULL;
|
|
const GEOSPreparedGeometry *preparedGeom1 = NULL;
|
|
|
|
// if there is constant, make PreparedGeometry from pInputData 0
|
|
if (isConstant1) {
|
|
code = readGeometry(colDataGetData(pInputData[0], 0), &geom1, &preparedGeom1);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
}
|
|
if (isConstant2) {
|
|
code = readGeometry(colDataGetData(pInputData[1], 0), &geom2, NULL);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < numOfRows; ++i) {
|
|
if ((!isConstant1 && colDataIsNull_s(pInputData[0], i)) ||
|
|
(!isConstant2 && colDataIsNull_s(pInputData[1], i))) {
|
|
colDataSetNULL(pOutputData, i);
|
|
code = TSDB_CODE_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
if (!isConstant1) {
|
|
code = readGeometry(colDataGetData(pInputData[0], i), &geom1, &preparedGeom1);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
}
|
|
if (!isConstant2) {
|
|
code = readGeometry(colDataGetData(pInputData[1], i), &geom2, NULL);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
}
|
|
|
|
code = executeRelationFunc(geom1, preparedGeom1, geom2, i, swapped, pOutputData, doRelationFn);
|
|
if (code != TSDB_CODE_SUCCESS) {
|
|
goto _exit;
|
|
}
|
|
|
|
if (!isConstant1) {
|
|
destroyGeometry(&geom1, &preparedGeom1);
|
|
}
|
|
if (!isConstant2) {
|
|
destroyGeometry(&geom2, NULL);
|
|
}
|
|
}
|
|
|
|
_exit:
|
|
destroyGeometry(&geom1, &preparedGeom1);
|
|
destroyGeometry(&geom2, NULL);
|
|
|
|
return code;
|
|
}
|
|
|
|
int32_t makePointFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomTwoParamsFunction(pInput, pOutput, initCtxMakePoint, executeMakePointFunc);
|
|
}
|
|
|
|
int32_t geomFromTextFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomOneParamFunction(pInput, pOutput, initCtxGeomFromText, executeGeomFromTextFunc);
|
|
}
|
|
|
|
int32_t asTextFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomOneParamFunction(pInput, pOutput, initCtxAsText, executeAsTextFunc);
|
|
}
|
|
|
|
int32_t intersectsFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, true, doIntersects);
|
|
}
|
|
|
|
int32_t equalsFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, true, doEquals);
|
|
}
|
|
|
|
int32_t touchesFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, true, doTouches);
|
|
}
|
|
|
|
int32_t coversFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, true, doCovers);
|
|
}
|
|
|
|
int32_t containsFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, true, doContains);
|
|
}
|
|
|
|
int32_t containsProperlyFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
|
return geomRelationFunction(pInput, pOutput, false, doContainsProperly);
|
|
}
|