decimal operator test
This commit is contained in:
parent
1fad80e631
commit
e42d0e118c
|
@ -122,7 +122,7 @@ SSdbRaw *mndStbActionEncode(SStbObj *pStb) {
|
||||||
|
|
||||||
int32_t size = sizeof(SStbObj) + (pStb->numOfColumns + pStb->numOfTags) * sizeof(SSchema) + pStb->commentLen +
|
int32_t size = sizeof(SStbObj) + (pStb->numOfColumns + pStb->numOfTags) * sizeof(SSchema) + pStb->commentLen +
|
||||||
pStb->ast1Len + pStb->ast2Len + pStb->numOfColumns * sizeof(SColCmpr) + STB_RESERVE_SIZE +
|
pStb->ast1Len + pStb->ast2Len + pStb->numOfColumns * sizeof(SColCmpr) + STB_RESERVE_SIZE +
|
||||||
taosArrayGetSize(pStb->pFuncs) * TSDB_FUNC_NAME_LEN;
|
taosArrayGetSize(pStb->pFuncs) * TSDB_FUNC_NAME_LEN + sizeof(int32_t) * pStb->numOfColumns;
|
||||||
SSdbRaw *pRaw = sdbAllocRaw(SDB_STB, STB_VER_NUMBER, size);
|
SSdbRaw *pRaw = sdbAllocRaw(SDB_STB, STB_VER_NUMBER, size);
|
||||||
if (pRaw == NULL) goto _OVER;
|
if (pRaw == NULL) goto _OVER;
|
||||||
|
|
||||||
|
|
|
@ -1755,7 +1755,7 @@ static int32_t sclGetMathOperatorResType(SOperatorNode *pOp) {
|
||||||
pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_TIMESTAMP].bytes;
|
pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_TIMESTAMP].bytes;
|
||||||
} else {
|
} else {
|
||||||
if (hasDecimalType) {
|
if (hasDecimalType) {
|
||||||
decimalGetRetType(&ldt, &rdt, pOp->opType, &pOp->node.resType);
|
return decimalGetRetType(&ldt, &rdt, pOp->opType, &pOp->node.resType);
|
||||||
} else {
|
} else {
|
||||||
pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE;
|
pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE;
|
||||||
pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes;
|
pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes;
|
||||||
|
|
|
@ -1048,7 +1048,7 @@ int8_t gConvertTypes[TSDB_DATA_TYPE_MAX][TSDB_DATA_TYPE_MAX] = {
|
||||||
/*UINT*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, -1, 17, 0, 0, -1, 17,
|
/*UINT*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, -1, 17, 0, 0, -1, 17,
|
||||||
/*UBIG*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 17, 0, 0, -1, 17,
|
/*UBIG*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 17, 0, 0, -1, 17,
|
||||||
/*JSON*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, -1, -1,
|
/*JSON*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, -1, -1,
|
||||||
/*VARB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 7,
|
/*VARB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1,
|
||||||
/*DECI*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, -1, -1, 17,
|
/*DECI*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, -1, -1, 17,
|
||||||
/*BLOB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1,
|
/*BLOB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1,
|
||||||
/*MEDB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1,
|
/*MEDB*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1,
|
||||||
|
@ -1251,7 +1251,8 @@ static int32_t vectorConvertVarToDouble(SScalarParam *pInput, int32_t *converted
|
||||||
SColumnInfoData *pCol = pInput->columnData;
|
SColumnInfoData *pCol = pInput->columnData;
|
||||||
int32_t code = TSDB_CODE_SUCCESS;
|
int32_t code = TSDB_CODE_SUCCESS;
|
||||||
*pOutputCol = NULL;
|
*pOutputCol = NULL;
|
||||||
if (IS_VAR_DATA_TYPE(pCol->info.type) && pCol->info.type != TSDB_DATA_TYPE_JSON && pCol->info.type != TSDB_DATA_TYPE_VARBINARY) {
|
bool isVarChar = IS_VAR_DATA_TYPE(pCol->info.type) && pCol->info.type != TSDB_DATA_TYPE_JSON && pCol->info.type != TSDB_DATA_TYPE_VARBINARY;
|
||||||
|
if (isVarChar || IS_DECIMAL_TYPE(pCol->info.type)) {
|
||||||
SCL_ERR_RET(vectorConvertSingleCol(pInput, &output, TSDB_DATA_TYPE_DOUBLE, 0, -1, -1));
|
SCL_ERR_RET(vectorConvertSingleCol(pInput, &output, TSDB_DATA_TYPE_DOUBLE, 0, -1, -1));
|
||||||
*converted = VECTOR_DO_CONVERT;
|
*converted = VECTOR_DO_CONVERT;
|
||||||
*pOutputCol = output.columnData;
|
*pOutputCol = output.columnData;
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
from ast import Tuple
|
||||||
from pydoc import doc
|
from pydoc import doc
|
||||||
from random import randrange
|
from random import randrange
|
||||||
from re import A
|
from re import A
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import secrets
|
import secrets
|
||||||
from tkinter.tix import COLUMN
|
|
||||||
|
|
||||||
from regex import D
|
from regex import D, F
|
||||||
from sympy import true
|
from sympy import Dict, true
|
||||||
|
from torch import is_conj
|
||||||
import query
|
import query
|
||||||
from tag_lite import column
|
from tag_lite import column
|
||||||
from util.log import *
|
from util.log import *
|
||||||
|
@ -23,7 +24,8 @@ syntax_error = -2147473920
|
||||||
invalid_column = -2147473918
|
invalid_column = -2147473918
|
||||||
invalid_compress_level = -2147483084
|
invalid_compress_level = -2147483084
|
||||||
invalid_encode_param = -2147483087
|
invalid_encode_param = -2147483087
|
||||||
|
invalid_operation = -2147483136
|
||||||
|
scalar_convert_err = -2147470768
|
||||||
|
|
||||||
class DecimalTypeGeneratorConfig:
|
class DecimalTypeGeneratorConfig:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -71,7 +73,7 @@ class DecimalStringRandomGenerator:
|
||||||
|
|
||||||
## 写入大整数的例子, 如10000000000, scale解析时可能为负数
|
## 写入大整数的例子, 如10000000000, scale解析时可能为负数
|
||||||
## Generate decimal with E/e
|
## Generate decimal with E/e
|
||||||
def generate_(self, config: DecimalTypeGeneratorConfig) -> str:
|
def generate(self, config: DecimalTypeGeneratorConfig) -> str:
|
||||||
ret: str = ""
|
ret: str = ""
|
||||||
sign = self.generate_sign(config.positive_ratio)
|
sign = self.generate_sign(config.positive_ratio)
|
||||||
if config.with_corner_case and self.current_should_generate_corner_case(
|
if config.with_corner_case and self.current_should_generate_corner_case(
|
||||||
|
@ -161,21 +163,76 @@ class TaosShell:
|
||||||
if len(self.queryResult) == 0:
|
if len(self.queryResult) == 0:
|
||||||
self.queryResult = [[] for i in range(len(vals))]
|
self.queryResult = [[] for i in range(len(vals))]
|
||||||
for val in vals:
|
for val in vals:
|
||||||
self.queryResult[col].append(val)
|
self.queryResult[col].append(val.strip())
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
def query(self, sql: str):
|
def query(self, sql: str):
|
||||||
|
with open(self.tmp_file_path, "r+") as f:
|
||||||
|
f.truncate(0)
|
||||||
try:
|
try:
|
||||||
command = f'taos -s "{sql} >> {self.tmp_file_path}"'
|
command = f'taos -s "{sql} >> {self.tmp_file_path}"'
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
command, shell=True, check=True, stderr=subprocess.PIPE
|
command, shell=True, check=True, stderr=subprocess.PIPE
|
||||||
)
|
)
|
||||||
self.read_result()
|
self.read_result()
|
||||||
except subprocess.CalledProcessError as e:
|
except Exception as e:
|
||||||
tdLog.exit(f"Command '{sql}' failed with error: {e.stderr.decode('utf-8')}")
|
tdLog.exit(f"Command '{sql}' failed with error: {e.stderr.decode('utf-8')}")
|
||||||
self.queryResult = []
|
self.queryResult = []
|
||||||
return self.queryResult
|
return self.queryResult
|
||||||
|
|
||||||
|
class DecimalColumnExpr:
|
||||||
|
def __init__(self, format: str, executor):
|
||||||
|
self.format_: str = format
|
||||||
|
self.executor_ = executor
|
||||||
|
self.params_: Tuple = List
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.format_ % (self.params_)
|
||||||
|
|
||||||
|
def execute(self, params):
|
||||||
|
return self.executor_(params)
|
||||||
|
|
||||||
|
def get_val(self, tbname: str, idx: int):
|
||||||
|
params: Tuple = ()
|
||||||
|
for p in self.params_:
|
||||||
|
params = params + (p.get_val(tbname, idx),)
|
||||||
|
return self.execute(params)
|
||||||
|
|
||||||
|
def check(self, query_col_res: List, tbname: str):
|
||||||
|
for i in range(len(query_col_res)):
|
||||||
|
v_from_query = query_col_res[i]
|
||||||
|
params: Tuple = ()
|
||||||
|
for p in self.params_:
|
||||||
|
if isinstance(p, Column) or isinstance(p, DecimalColumnExpr):
|
||||||
|
p = p.get_val(tbname, i)
|
||||||
|
params = params + (p,)
|
||||||
|
v_from_calc_in_py = self.execute(params)
|
||||||
|
|
||||||
|
if v_from_calc_in_py == 'NULL' or v_from_query == 'NULL':
|
||||||
|
if v_from_calc_in_py != v_from_query:
|
||||||
|
tdLog.exit(f"query with expr: {self} calc in py got: {v_from_calc_in_py}, query got: {v_from_query}")
|
||||||
|
tdLog.debug(f"query with expr: {self} calc got same result: NULL")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(v_from_calc_in_py, float):
|
||||||
|
dec_from_query = float(v_from_query)
|
||||||
|
dec_from_insert = float(v_from_calc_in_py)
|
||||||
|
else:
|
||||||
|
dec_from_query = Decimal(v_from_query)
|
||||||
|
dec_from_insert = Decimal(v_from_calc_in_py)
|
||||||
|
if dec_from_query != dec_from_insert:
|
||||||
|
tdLog.exit(
|
||||||
|
f"check decimal column failed for expr: {self} params: {params}, query: {v_from_query}, expect {dec_from_insert}, but get {dec_from_query}")
|
||||||
|
else:
|
||||||
|
tdLog.debug(
|
||||||
|
f"check decimal succ for expr: {self}, params: {params}, insert:{v_from_calc_in_py} query:{v_from_query}, py dec: {dec_from_insert}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def generate(self, format_params) -> str:
|
||||||
|
self.params_ = format_params
|
||||||
|
return f"({self.format_})".format(*format_params)
|
||||||
|
|
||||||
|
|
||||||
class TypeEnum:
|
class TypeEnum:
|
||||||
BOOL = 1
|
BOOL = 1
|
||||||
|
@ -222,13 +279,13 @@ class TypeEnum:
|
||||||
elif type == TypeEnum.NCHAR:
|
elif type == TypeEnum.NCHAR:
|
||||||
return "NCHAR"
|
return "NCHAR"
|
||||||
elif type == TypeEnum.UTINYINT:
|
elif type == TypeEnum.UTINYINT:
|
||||||
return "UTINYINT"
|
return "TINYINT UNSIGNED"
|
||||||
elif type == TypeEnum.USMALLINT:
|
elif type == TypeEnum.USMALLINT:
|
||||||
return "USMALLINT"
|
return "SMALLINT UNSIGNED"
|
||||||
elif type == TypeEnum.UINT:
|
elif type == TypeEnum.UINT:
|
||||||
return "UINT"
|
return "INT UNSIGNED"
|
||||||
elif type == TypeEnum.UBIGINT:
|
elif type == TypeEnum.UBIGINT:
|
||||||
return "UBIGINT"
|
return "BIGINT UNSIGNED"
|
||||||
elif type == TypeEnum.JSON:
|
elif type == TypeEnum.JSON:
|
||||||
return "JSON"
|
return "JSON"
|
||||||
elif type == TypeEnum.VARBINARY:
|
elif type == TypeEnum.VARBINARY:
|
||||||
|
@ -239,6 +296,8 @@ class TypeEnum:
|
||||||
return "BINARY"
|
return "BINARY"
|
||||||
elif type == TypeEnum.GEOMETRY:
|
elif type == TypeEnum.GEOMETRY:
|
||||||
return "GEOMETRY"
|
return "GEOMETRY"
|
||||||
|
elif type == TypeEnum.DECIMAL64:
|
||||||
|
return "DECIMAL"
|
||||||
else:
|
else:
|
||||||
raise Exception("unknow type")
|
raise Exception("unknow type")
|
||||||
|
|
||||||
|
@ -309,11 +368,17 @@ class DataType:
|
||||||
return str(secrets.randbelow(9223372036854775808))
|
return str(secrets.randbelow(9223372036854775808))
|
||||||
if self.type == TypeEnum.JSON:
|
if self.type == TypeEnum.JSON:
|
||||||
return f'{{"key": "{secrets.token_urlsafe(10)}"}}'
|
return f'{{"key": "{secrets.token_urlsafe(10)}"}}'
|
||||||
|
if self.type == TypeEnum.GEOMETRY:
|
||||||
|
return "'POINT(1.0 1.0)'"
|
||||||
raise Exception(f"unsupport type {self.type}")
|
raise Exception(f"unsupport type {self.type}")
|
||||||
|
|
||||||
def check(self, values, offset: int):
|
def check(self, values, offset: int):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_typed_val(self, val):
|
||||||
|
if self.type == TypeEnum.FLOAT or self.type == TypeEnum.DOUBLE:
|
||||||
|
return float(val)
|
||||||
|
return val
|
||||||
|
|
||||||
class DecimalType(DataType):
|
class DecimalType(DataType):
|
||||||
def __init__(self, type, precision: int, scale: int):
|
def __init__(self, type, precision: int, scale: int):
|
||||||
|
@ -324,7 +389,7 @@ class DecimalType(DataType):
|
||||||
else:
|
else:
|
||||||
bytes = 16
|
bytes = 16
|
||||||
super().__init__(type, bytes, self.get_decimal_type_mod())
|
super().__init__(type, bytes, self.get_decimal_type_mod())
|
||||||
self.generator: DecimalStringRandomGenerator = DecimalStringRandomGenerator()
|
self.decimal_generator: DecimalStringRandomGenerator = DecimalStringRandomGenerator()
|
||||||
self.generator_config: DecimalTypeGeneratorConfig = DecimalTypeGeneratorConfig()
|
self.generator_config: DecimalTypeGeneratorConfig = DecimalTypeGeneratorConfig()
|
||||||
self.generator_config.prec = precision
|
self.generator_config.prec = precision
|
||||||
self.generator_config.scale = scale
|
self.generator_config.scale = scale
|
||||||
|
@ -356,11 +421,16 @@ class DecimalType(DataType):
|
||||||
return f"DecimalType({self.precision_}, {self.scale()})"
|
return f"DecimalType({self.precision_}, {self.scale()})"
|
||||||
|
|
||||||
def generate_value(self) -> str:
|
def generate_value(self) -> str:
|
||||||
val = self.generator.generate_(self.generator_config)
|
val = self.decimal_generator.generate(self.generator_config)
|
||||||
self.aggregator.add_value(val)
|
self.aggregator.add_value(val) ## convert to Decimal first
|
||||||
self.values.append(val) ## save it into files maybe
|
# self.values.append(val) ## save it into files maybe
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
def get_typed_val(self, val):
|
||||||
|
if val == "NULL":
|
||||||
|
return None
|
||||||
|
return Decimal(val).quantize(Decimal("1." + "0" * self.scale()), ROUND_HALF_UP)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_compression() -> str:
|
def default_compression() -> str:
|
||||||
return "zstd"
|
return "zstd"
|
||||||
|
@ -372,30 +442,30 @@ class DecimalType(DataType):
|
||||||
def check(self, values, offset: int):
|
def check(self, values, offset: int):
|
||||||
val_from_query = values
|
val_from_query = values
|
||||||
val_insert = self.values[offset:]
|
val_insert = self.values[offset:]
|
||||||
for v1, v2 in zip(val_from_query, val_insert):
|
for v_from_query, v_from_insert in zip(val_from_query, val_insert):
|
||||||
if v2 == "NULL":
|
if v_from_insert == "NULL":
|
||||||
if v1.strip() != "NULL":
|
if v_from_query.strip() != "NULL":
|
||||||
tdLog.debug(
|
tdLog.debug(
|
||||||
f"val_insert: {val_insert} val_from_query: {val_from_query}"
|
f"val_insert: {val_insert} val_from_query: {val_from_query}"
|
||||||
)
|
)
|
||||||
tdLog.exit(f"insert NULL, query not NULL: {v1}")
|
tdLog.exit(f"insert NULL, query not NULL: {v_from_query}")
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
dec1: Decimal = Decimal(v1)
|
dec_query: Decimal = Decimal(v_from_query)
|
||||||
dec2: Decimal = Decimal(v2)
|
dec_insert: Decimal = Decimal(v_from_insert)
|
||||||
dec2 = dec2.quantize(Decimal("1." + "0" * self.scale()), ROUND_HALF_UP)
|
dec_insert = dec_insert.quantize(Decimal("1." + "0" * self.scale()), ROUND_HALF_UP)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tdLog.exit(f"failed to convert {v1} or {v2} to decimal, {e}")
|
tdLog.exit(f"failed to convert {v_from_query} or {v_from_insert} to decimal, {e}")
|
||||||
return False
|
return False
|
||||||
if dec1 != dec2:
|
if dec_query != dec_insert:
|
||||||
tdLog.exit(
|
tdLog.exit(
|
||||||
f"check decimal column failed for insert: {v2}, query: {v1}, expect {dec2}, but get {dec1}"
|
f"check decimal column failed for insert: {v_from_insert}, query: {v_from_query}, expect {dec_insert}, but get {dec_query}"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
tdLog.debug(
|
tdLog.debug(
|
||||||
f"check decimal succ, insert:{v2} query:{v1}, py dec: {dec2}"
|
f"check decimal succ, insert:{v_from_insert} query:{v_from_query}, py dec: {dec_insert}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -403,9 +473,35 @@ class Column:
|
||||||
def __init__(self, type: DataType):
|
def __init__(self, type: DataType):
|
||||||
self.type_: DataType = type
|
self.type_: DataType = type
|
||||||
self.name_: str = ""
|
self.name_: str = ""
|
||||||
|
self.saved_vals:dict[str:[]] = {}
|
||||||
|
|
||||||
def generate_value(self):
|
def is_constant_col(self):
|
||||||
return self.type_.generate_value()
|
return '' in self.saved_vals.keys()
|
||||||
|
|
||||||
|
def get_typed_val(self, val):
|
||||||
|
return self.type_.get_typed_val(val)
|
||||||
|
|
||||||
|
def get_constant_val(self):
|
||||||
|
return self.get_typed_val(self.saved_vals[''][0])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.is_constant_col():
|
||||||
|
return self.get_constant_val()
|
||||||
|
return self.name_
|
||||||
|
|
||||||
|
def get_val(self, tbname: str, idx: int):
|
||||||
|
if self.is_constant_col():
|
||||||
|
return self.get_constant_val()
|
||||||
|
return self.get_typed_val(self.saved_vals[tbname][idx])
|
||||||
|
|
||||||
|
## tbName: for normal table, pass the tbname, for child table, pass the child table name
|
||||||
|
def generate_value(self, tbName: str = '', save: bool = True):
|
||||||
|
val = self.type_.generate_value()
|
||||||
|
if save:
|
||||||
|
if tbName not in self.saved_vals:
|
||||||
|
self.saved_vals[tbName] = []
|
||||||
|
self.saved_vals[tbName].append(val)
|
||||||
|
return val
|
||||||
|
|
||||||
def get_type_str(self) -> str:
|
def get_type_str(self) -> str:
|
||||||
return str(self.type_)
|
return str(self.type_)
|
||||||
|
@ -415,21 +511,22 @@ class Column:
|
||||||
|
|
||||||
def check(self, values, offset: int):
|
def check(self, values, offset: int):
|
||||||
return self.type_.check(values, offset)
|
return self.type_.check(values, offset)
|
||||||
|
|
||||||
def construct_type_value(self, val: str):
|
def construct_type_value(self, val: str):
|
||||||
if (
|
if (
|
||||||
self.type_.type == TypeEnum.BINARY
|
self.type_.type == TypeEnum.BINARY
|
||||||
or self.type_.type == TypeEnum.VARCHAR
|
or self.type_.type == TypeEnum.VARCHAR
|
||||||
or self.type_.type == TypeEnum.NCHAR
|
or self.type_.type == TypeEnum.NCHAR
|
||||||
or self.type_.type == TypeEnum.VARBINARY
|
or self.type_.type == TypeEnum.VARBINARY
|
||||||
|
or self.type_.type == TypeEnum.JSON
|
||||||
):
|
):
|
||||||
return f"'{val}'"
|
return f"'{val}'"
|
||||||
else:
|
else:
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_type_columns() -> List:
|
def get_all_type_columns(types_to_exclude: List[TypeEnum] = []) -> List:
|
||||||
other_types = [
|
all_types = [
|
||||||
Column(DataType(TypeEnum.BOOL)),
|
Column(DataType(TypeEnum.BOOL)),
|
||||||
Column(DataType(TypeEnum.TINYINT)),
|
Column(DataType(TypeEnum.TINYINT)),
|
||||||
Column(DataType(TypeEnum.SMALLINT)),
|
Column(DataType(TypeEnum.SMALLINT)),
|
||||||
|
@ -439,19 +536,23 @@ class Column:
|
||||||
Column(DataType(TypeEnum.DOUBLE)),
|
Column(DataType(TypeEnum.DOUBLE)),
|
||||||
Column(DataType(TypeEnum.VARCHAR, 255)),
|
Column(DataType(TypeEnum.VARCHAR, 255)),
|
||||||
Column(DataType(TypeEnum.TIMESTAMP)),
|
Column(DataType(TypeEnum.TIMESTAMP)),
|
||||||
Column(DataType(TypeEnum.NCHAR)),
|
Column(DataType(TypeEnum.NCHAR, 255)),
|
||||||
Column(DataType(TypeEnum.UTINYINT)),
|
Column(DataType(TypeEnum.UTINYINT)),
|
||||||
Column(DataType(TypeEnum.USMALLINT)),
|
Column(DataType(TypeEnum.USMALLINT)),
|
||||||
Column(DataType(TypeEnum.UINT)),
|
Column(DataType(TypeEnum.UINT)),
|
||||||
Column(DataType(TypeEnum.UBIGINT)),
|
Column(DataType(TypeEnum.UBIGINT)),
|
||||||
Column(DataType(TypeEnum.JSON)),
|
Column(DataType(TypeEnum.JSON)),
|
||||||
Column(DataType(TypeEnum.VARBINARY)),
|
Column(DataType(TypeEnum.VARBINARY, 255)),
|
||||||
Column(DataType(TypeEnum.DECIMAL)),
|
Column(DecimalType(TypeEnum.DECIMAL, 38, 10)),
|
||||||
Column(DataType(TypeEnum.BINARY)),
|
Column(DataType(TypeEnum.BINARY, 255)),
|
||||||
Column(DataType(TypeEnum.GEOMETRY)),
|
Column(DataType(TypeEnum.GEOMETRY, 10240)),
|
||||||
Column(DataType(TypeEnum.DECIMAL64)),
|
Column(DecimalType(TypeEnum.DECIMAL64, 18, 4)),
|
||||||
]
|
]
|
||||||
return other_types
|
for c in all_types:
|
||||||
|
for type in types_to_exclude:
|
||||||
|
if c.type_.type == type:
|
||||||
|
all_types.remove(c)
|
||||||
|
return all_types
|
||||||
|
|
||||||
|
|
||||||
class DecimalColumnTableCreater:
|
class DecimalColumnTableCreater:
|
||||||
|
@ -521,7 +622,7 @@ class TableInserter:
|
||||||
columns: List[Column],
|
columns: List[Column],
|
||||||
tags_cols: List[Column] = [],
|
tags_cols: List[Column] = [],
|
||||||
):
|
):
|
||||||
self.conn = conn
|
self.conn: TDSql = conn
|
||||||
self.dbName = dbName
|
self.dbName = dbName
|
||||||
self.tbName = tbName
|
self.tbName = tbName
|
||||||
self.tag_cols = tags_cols
|
self.tag_cols = tags_cols
|
||||||
|
@ -533,7 +634,7 @@ class TableInserter:
|
||||||
for i in range(rows):
|
for i in range(rows):
|
||||||
sql += f"({start_ts + i * step}"
|
sql += f"({start_ts + i * step}"
|
||||||
for column in self.columns:
|
for column in self.columns:
|
||||||
sql += f", {column.generate_value()}"
|
sql += f", {column.generate_value(self.tbName)}"
|
||||||
sql += ")"
|
sql += ")"
|
||||||
if i != rows - 1:
|
if i != rows - 1:
|
||||||
sql += ", "
|
sql += ", "
|
||||||
|
@ -572,18 +673,6 @@ class TableDataValidator:
|
||||||
colIdx += 1
|
colIdx += 1
|
||||||
|
|
||||||
|
|
||||||
class DecimalColumnExpr:
|
|
||||||
def __init__(self, format: str, executor):
|
|
||||||
self.format_: str = ""
|
|
||||||
self.executor_ = None
|
|
||||||
|
|
||||||
def execute(self, params: List):
|
|
||||||
return self.executor_(params)
|
|
||||||
|
|
||||||
def generate(self, format_params) -> str:
|
|
||||||
return f"({self.format_ % format_params})"
|
|
||||||
|
|
||||||
|
|
||||||
class DecimalBinaryOperator(DecimalColumnExpr):
|
class DecimalBinaryOperator(DecimalColumnExpr):
|
||||||
def __init__(self, op: str):
|
def __init__(self, op: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -597,7 +686,67 @@ class DecimalBinaryOperator(DecimalColumnExpr):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def execute_plus(params):
|
def execute_plus(params):
|
||||||
return params[0] + params[1]
|
if params[0] is None or params[1] is None:
|
||||||
|
return 'NULL'
|
||||||
|
if isinstance(params[0], float) or isinstance(params[1], float):
|
||||||
|
return float(params[0]) + float(params[1])
|
||||||
|
return Decimal(params[0]) + Decimal(params[1])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_minus(params):
|
||||||
|
return params[0] - params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_mul(params):
|
||||||
|
return params[0] * params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_div(params):
|
||||||
|
return params[0] / params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_mod(params):
|
||||||
|
return params[0] % params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_eq(params):
|
||||||
|
return params[0] == params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_ne(params):
|
||||||
|
return params[0] != params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_gt(params):
|
||||||
|
return params[0] > params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_lt(params):
|
||||||
|
return params[0] < params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_ge(params):
|
||||||
|
return params[0] >= params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def execute_le(params):
|
||||||
|
return params[0] <= params[1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_all_binary_ops() -> List[DecimalColumnExpr]:
|
||||||
|
return [
|
||||||
|
DecimalColumnExpr(" {0} + {1} ", DecimalBinaryOperator.execute_plus),
|
||||||
|
DecimalColumnExpr(" {0} - {1} ", DecimalBinaryOperator.execute_minus),
|
||||||
|
DecimalColumnExpr(" {0} * {1} ", DecimalBinaryOperator.execute_mul),
|
||||||
|
DecimalColumnExpr(" {0} / {1} ", DecimalBinaryOperator.execute_div),
|
||||||
|
DecimalColumnExpr(" {0} % {1} ", DecimalBinaryOperator.execute_mod),
|
||||||
|
DecimalColumnExpr(" {0} == {1} ", DecimalBinaryOperator.execute_eq),
|
||||||
|
DecimalColumnExpr(" {0} != {1} ", DecimalBinaryOperator.execute_ne),
|
||||||
|
DecimalColumnExpr(" {0} > {1} ", DecimalBinaryOperator.execute_gt),
|
||||||
|
DecimalColumnExpr(" {0} < {1} ", DecimalBinaryOperator.execute_lt),
|
||||||
|
DecimalColumnExpr(" {0} >= {1} ", DecimalBinaryOperator.execute_ge),
|
||||||
|
DecimalColumnExpr(" {0} <= {1} ", DecimalBinaryOperator.execute_le),
|
||||||
|
]
|
||||||
|
|
||||||
def execute(self, left, right):
|
def execute(self, left, right):
|
||||||
if self.op_ == "+":
|
if self.op_ == "+":
|
||||||
|
@ -812,12 +961,12 @@ class TDTestCase:
|
||||||
## TODO add more values for all rows
|
## TODO add more values for all rows
|
||||||
tag_values = ["1", "t1"]
|
tag_values = ["1", "t1"]
|
||||||
DecimalColumnTableCreater(
|
DecimalColumnTableCreater(
|
||||||
tdSql, self.db_name, self.stable_name, self.norm_tb_columns
|
tdSql, self.db_name, self.stable_name, self.stb_columns
|
||||||
).create_child_table(
|
).create_child_table(
|
||||||
self.c_table_prefix, self.c_table_num, self.tags, tag_values
|
self.c_table_prefix, self.c_table_num, self.tags, tag_values
|
||||||
)
|
)
|
||||||
self.check_desc("meters", self.stb_columns, self.tags)
|
self.check_desc("meters", self.stb_columns, self.tags)
|
||||||
self.check_desc("t1", self.norm_tb_columns, self.tags)
|
self.check_desc("t1", self.stb_columns, self.tags)
|
||||||
|
|
||||||
## invalid precision/scale
|
## invalid precision/scale
|
||||||
invalid_precision_scale = [
|
invalid_precision_scale = [
|
||||||
|
@ -872,7 +1021,7 @@ class TDTestCase:
|
||||||
f"{self.c_table_prefix}{i}",
|
f"{self.c_table_prefix}{i}",
|
||||||
self.stb_columns,
|
self.stb_columns,
|
||||||
self.tags,
|
self.tags,
|
||||||
).insert(10000, 1537146000000, 500)
|
).insert(1000, 1537146000000, 500)
|
||||||
|
|
||||||
for i in range(self.c_table_num):
|
for i in range(self.c_table_num):
|
||||||
TableDataValidator(
|
TableDataValidator(
|
||||||
|
@ -881,7 +1030,7 @@ class TDTestCase:
|
||||||
|
|
||||||
TableInserter(
|
TableInserter(
|
||||||
tdSql, self.db_name, self.norm_table_name, self.norm_tb_columns
|
tdSql, self.db_name, self.norm_table_name, self.norm_tb_columns
|
||||||
).insert(100000, 1537146000000, 500, flush_database=True)
|
).insert(1000, 1537146000000, 500, flush_database=True)
|
||||||
TableDataValidator(
|
TableDataValidator(
|
||||||
self.norm_tb_columns, self.norm_table_name, self.db_name
|
self.norm_tb_columns, self.norm_table_name, self.db_name
|
||||||
).validate()
|
).validate()
|
||||||
|
@ -995,13 +1144,15 @@ class TDTestCase:
|
||||||
for expr in exprs:
|
for expr in exprs:
|
||||||
for col in tb_cols:
|
for col in tb_cols:
|
||||||
left_is_decimal = col.type_.is_decimal_type()
|
left_is_decimal = col.type_.is_decimal_type()
|
||||||
for const_val in constant_cols:
|
for const_col in constant_cols:
|
||||||
right_is_decimal = const_val.type_.is_decimal_type()
|
right_is_decimal = const_col.type_.is_decimal_type()
|
||||||
if not left_is_decimal and not right_is_decimal:
|
if not left_is_decimal and not right_is_decimal:
|
||||||
continue
|
continue
|
||||||
select_expr = expr.generate((col.name_, const_val.generate_value()))
|
const_col.generate_value()
|
||||||
sql = f"select {select_expr} from {dbname}.{tbname}"
|
select_expr = expr.generate((col, const_col))
|
||||||
res = TaosShell().query(sql)
|
sql = f"select {select_expr} from {dbname}.{tbname}"
|
||||||
|
res = TaosShell().query(sql)
|
||||||
|
expr.check(res[0], tbname)
|
||||||
## query
|
## query
|
||||||
## build expr, expr.generate(column) to generate sql expr
|
## build expr, expr.generate(column) to generate sql expr
|
||||||
## pass this expr into DataValidator.
|
## pass this expr into DataValidator.
|
||||||
|
@ -1011,38 +1162,69 @@ class TDTestCase:
|
||||||
|
|
||||||
## test others unsupported types operator with decimal
|
## test others unsupported types operator with decimal
|
||||||
def test_decimal_unsupported_types(self):
|
def test_decimal_unsupported_types(self):
|
||||||
unsupported_type_cols = [
|
unsupported_type = [
|
||||||
Column(DataType(TypeEnum.JSON)),
|
TypeEnum.JSON,
|
||||||
Column(DataType(TypeEnum.GEOMETRY)),
|
TypeEnum.GEOMETRY,
|
||||||
Column(DataType(TypeEnum.VARBINARY)),
|
TypeEnum.VARBINARY,
|
||||||
]
|
]
|
||||||
all_type_columns = Column.get_all_type_columns()
|
all_type_columns: List[Column] = Column.get_all_type_columns([TypeEnum.JSON])
|
||||||
tbname = "test_decimal_unsupported_types"
|
tbname = "test_decimal_unsupported_types"
|
||||||
DecimalColumnTableCreater(
|
tag_cols = [Column(DataType(TypeEnum.JSON))]
|
||||||
tdSql, self.db_name, tbname, all_type_columns
|
tb_creater = DecimalColumnTableCreater( tdSql, self.db_name, tbname, all_type_columns, tag_cols)
|
||||||
).create()
|
tb_creater.create()
|
||||||
tdSql.error(
|
tb_creater.create_child_table(tbname, 10, tag_cols, ['{"k1": "v1"}'])
|
||||||
f"select c17 + c15 from {self.db_name}{tbname}", queryTimes=1, show=True
|
for i in range(10):
|
||||||
)
|
TableInserter(tdSql, self.db_name, f'{tbname}{i}', all_type_columns, tag_cols).insert(100, 1537146000000, 500, flush_database=True)
|
||||||
|
for col in all_type_columns:
|
||||||
|
## only test decimal cols
|
||||||
|
if not col.type_.is_decimal_type():
|
||||||
|
continue
|
||||||
|
for unsupported_col in all_type_columns:
|
||||||
|
## only test unsupported cols
|
||||||
|
if not unsupported_col.type_.type in unsupported_type:
|
||||||
|
continue
|
||||||
|
for binary_op in DecimalBinaryOperator.get_all_binary_ops():
|
||||||
|
select_expr = binary_op.generate((col.name_, unsupported_col.name_))
|
||||||
|
sql = f"select {select_expr} from {self.db_name}.{tbname}"
|
||||||
|
select_expr_reverse = binary_op.generate(
|
||||||
|
(unsupported_col.name_, col.name_)
|
||||||
|
)
|
||||||
|
sql_reverse = (
|
||||||
|
f"select {select_expr_reverse} from {self.db_name}.{tbname}"
|
||||||
|
)
|
||||||
|
tdLog.info(
|
||||||
|
f"select expr: {select_expr} with type: {col.type_} and {unsupported_col.type_} should err"
|
||||||
|
)
|
||||||
|
err = tdSql.error(sql)
|
||||||
|
if tdSql.errno != invalid_operation and tdSql.errno != scalar_convert_err:
|
||||||
|
tdLog.exit(f"expected err not occured for sql: {sql}, expect: {invalid_operation} or {scalar_convert_err}, but got {tdSql.errno}")
|
||||||
|
|
||||||
|
tdLog.info(
|
||||||
|
f"select expr: {select_expr} with type: {unsupported_col.type_} and {col.type_} should err"
|
||||||
|
)
|
||||||
|
err = tdSql.error(sql_reverse)
|
||||||
|
if tdSql.errno != invalid_operation and tdSql.errno != scalar_convert_err:
|
||||||
|
tdLog.exit(f"expected err not occured for sql: {sql}, expect: {invalid_operation} or {scalar_convert_err}, but got {tdSql.errno}")
|
||||||
|
|
||||||
def test_decimal_operators(self):
|
def test_decimal_operators(self):
|
||||||
tdLog.debug("start to test decimal operators")
|
tdLog.debug("start to test decimal operators")
|
||||||
|
self.test_decimal_unsupported_types()
|
||||||
## tables: meters, nt
|
## tables: meters, nt
|
||||||
## columns: c1, c2, c3, c4, c5, c7, c8, c9, c10, c99, c100
|
## columns: c1, c2, c3, c4, c5, c7, c8, c9, c10, c99, c100
|
||||||
binary_operators = [
|
binary_operators = [
|
||||||
DecimalColumnExpr("%s + %s", DecimalBinaryOperator.execute_plus),
|
DecimalColumnExpr("%s + %s", DecimalBinaryOperator.execute_plus),
|
||||||
DecimalColumnExpr("-"),
|
# DecimalColumnExpr("-"),
|
||||||
DecimalColumnExpr("*"),
|
# DecimalColumnExpr("*"),
|
||||||
DecimalColumnExpr("/"),
|
# DecimalColumnExpr("/"),
|
||||||
DecimalColumnExpr("%"),
|
# DecimalColumnExpr("%"),
|
||||||
DecimalColumnExpr(">"),
|
# DecimalColumnExpr(">"),
|
||||||
DecimalColumnExpr("<"),
|
# DecimalColumnExpr("<"),
|
||||||
DecimalColumnExpr(">="),
|
# DecimalColumnExpr(">="),
|
||||||
DecimalColumnExpr("<="),
|
# DecimalColumnExpr("<="),
|
||||||
DecimalColumnExpr("=="),
|
# DecimalColumnExpr("=="),
|
||||||
DecimalColumnExpr("!="),
|
# DecimalColumnExpr("!="),
|
||||||
DecimalBinaryOperatorIn("in"),
|
# DecimalBinaryOperatorIn("in"),
|
||||||
DecimalBinaryOperatorIn("not in"),
|
# DecimalBinaryOperatorIn("not in"),
|
||||||
]
|
]
|
||||||
all_type_columns = Column.get_all_type_columns()
|
all_type_columns = Column.get_all_type_columns()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue