/* * Copyright (c) 2019 TAOS Data, Inc. * * 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 . */ #define _DEFAULT_SOURCE #include "tdataformat.h" #include "tRealloc.h" #include "tdatablock.h" #include "tlog.h" static int32_t (*tColDataAppendValueImpl[8][3])(SColData *pColData, uint8_t *pData, uint32_t nData); static int32_t (*tColDataUpdateValueImpl[8][3])(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward); // ================================ static int32_t tGetTagVal(uint8_t *p, STagVal *pTagVal, int8_t isJson); // SRow ======================================================================== #define KV_FLG_LIT ((uint8_t)0x10) #define KV_FLG_MID ((uint8_t)0x20) #define KV_FLG_BIG ((uint8_t)0x40) #define BIT_FLG_NONE ((uint8_t)0x0) #define BIT_FLG_NULL ((uint8_t)0x1) #define BIT_FLG_VALUE ((uint8_t)0x2) #pragma pack(push, 1) typedef struct { int16_t nCol; uint8_t idx[]; // uint8_t * | uint16_t * | uint32_t * } SKVIdx; #pragma pack(pop) #define ROW_SET_BITMAP(PB, FLAG, IDX, VAL) \ do { \ switch (FLAG) { \ case (HAS_NULL | HAS_NONE): \ SET_BIT1(PB, IDX, VAL); \ break; \ case (HAS_VALUE | HAS_NONE): \ SET_BIT1(PB, IDX, (VAL) ? (VAL)-1 : 0); \ break; \ case (HAS_VALUE | HAS_NULL): \ SET_BIT1(PB, IDX, (VAL)-1); \ break; \ case (HAS_VALUE | HAS_NULL | HAS_NONE): \ SET_BIT2(PB, IDX, VAL); \ break; \ default: \ break; \ } \ } while (0) static int32_t tPutPrimaryKeyIndex(uint8_t *p, const SPrimaryKeyIndex *index) { int32_t n = 0; n += tPutI8(p ? p + n : p, index->type); n += tPutU32v(p ? p + n : p, index->offset); return n; } static int32_t tGetPrimaryKeyIndex(uint8_t *p, SPrimaryKeyIndex *index) { int32_t n = 0; n += tGetI8(p + n, &index->type); n += tGetU32v(p + n, &index->offset); return n; } typedef struct { int32_t numOfNone; int32_t numOfNull; int32_t numOfValue; int32_t numOfPKs; int8_t flag; // tuple int8_t tupleFlag; SPrimaryKeyIndex tupleIndices[TD_MAX_PK_COLS]; int32_t tuplePKSize; // primary key size int32_t tupleBitmapSize; // bitmap size int32_t tupleFixedSize; // fixed part size int32_t tupleVarSize; // var part size int32_t tupleRowSize; // key-value int8_t kvFlag; SPrimaryKeyIndex kvIndices[TD_MAX_PK_COLS]; int32_t kvMaxOffset; int32_t kvPKSize; // primary key size int32_t kvIndexSize; // offset array size int32_t kvPayloadSize; // payload size int32_t kvRowSize; } SRowBuildScanInfo; static FORCE_INLINE int32_t tRowBuildScanAddNone(SRowBuildScanInfo *sinfo, const STColumn *pTColumn) { if ((pTColumn->flags & COL_IS_KEY)) return TSDB_CODE_PAR_PRIMARY_KEY_IS_NONE; sinfo->numOfNone++; return 0; } static FORCE_INLINE int32_t tRowBuildScanAddNull(SRowBuildScanInfo *sinfo, const STColumn *pTColumn) { if ((pTColumn->flags & COL_IS_KEY)) return TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; sinfo->numOfNull++; sinfo->kvMaxOffset = sinfo->kvPayloadSize; sinfo->kvPayloadSize += tPutI16v(NULL, -pTColumn->colId); return 0; } static FORCE_INLINE void tRowBuildScanAddValue(SRowBuildScanInfo *sinfo, SColVal *colVal, const STColumn *pTColumn) { bool isPK = ((pTColumn->flags & COL_IS_KEY) != 0); if (isPK) { ASSERTS(sinfo->numOfPKs < TD_MAX_PK_COLS, "too many primary keys"); sinfo->tupleIndices[sinfo->numOfPKs].type = colVal->value.type; sinfo->tupleIndices[sinfo->numOfPKs].offset = IS_VAR_DATA_TYPE(pTColumn->type) ? sinfo->tupleVarSize + sinfo->tupleFixedSize : pTColumn->offset; sinfo->kvIndices[sinfo->numOfPKs].type = colVal->value.type; sinfo->kvIndices[sinfo->numOfPKs].offset = sinfo->kvPayloadSize; sinfo->numOfPKs++; } sinfo->kvMaxOffset = sinfo->kvPayloadSize; if (IS_VAR_DATA_TYPE(colVal->value.type)) { sinfo->tupleVarSize += tPutU32v(NULL, colVal->value.nData) // size + colVal->value.nData; // value sinfo->kvPayloadSize += tPutI16v(NULL, colVal->cid) // colId + tPutU32v(NULL, colVal->value.nData) // size + colVal->value.nData; // value } else { sinfo->kvPayloadSize += tPutI16v(NULL, colVal->cid) // colId + tDataTypes[colVal->value.type].bytes; // value } sinfo->numOfValue++; } static int32_t tRowBuildScan(SArray *colVals, const STSchema *schema, SRowBuildScanInfo *sinfo) { int32_t code = 0; int32_t colValIndex = 1; int32_t numOfColVals = TARRAY_SIZE(colVals); SColVal *colValArray = (SColVal *)TARRAY_DATA(colVals); ASSERT(numOfColVals > 0); ASSERT(colValArray[0].cid == PRIMARYKEY_TIMESTAMP_COL_ID); ASSERT(colValArray[0].value.type == TSDB_DATA_TYPE_TIMESTAMP); *sinfo = (SRowBuildScanInfo){ .tupleFixedSize = schema->flen, }; // loop scan for (int32_t i = 1; i < schema->numOfCols; i++) { for (;;) { if (colValIndex >= numOfColVals) { if ((code = tRowBuildScanAddNone(sinfo, schema->columns + i))) goto _exit; break; } if (colValArray[colValIndex].cid == schema->columns[i].colId) { ASSERT(colValArray[colValIndex].value.type == schema->columns[i].type); if (COL_VAL_IS_VALUE(&colValArray[colValIndex])) { tRowBuildScanAddValue(sinfo, &colValArray[colValIndex], schema->columns + i); } else if (COL_VAL_IS_NULL(&colValArray[colValIndex])) { if ((code = tRowBuildScanAddNull(sinfo, schema->columns + i))) goto _exit; } else if (COL_VAL_IS_NONE(&colValArray[colValIndex])) { if ((code = tRowBuildScanAddNone(sinfo, schema->columns + i))) goto _exit; } colValIndex++; break; } else if (colValArray[colValIndex].cid > schema->columns[i].colId) { if ((code = tRowBuildScanAddNone(sinfo, schema->columns + i))) goto _exit; break; } else { // skip useless value colValIndex++; } } } if (sinfo->numOfNone) { sinfo->flag |= HAS_NONE; } if (sinfo->numOfNull) { sinfo->flag |= HAS_NULL; } if (sinfo->numOfValue) { sinfo->flag |= HAS_VALUE; } // Tuple sinfo->tupleFlag = sinfo->flag; switch (sinfo->flag) { case HAS_NONE: case HAS_NULL: sinfo->tupleBitmapSize = 0; sinfo->tupleFixedSize = 0; break; case HAS_VALUE: sinfo->tupleBitmapSize = 0; sinfo->tupleFixedSize = schema->flen; break; case (HAS_NONE | HAS_NULL): sinfo->tupleBitmapSize = BIT1_SIZE(schema->numOfCols - 1); sinfo->tupleFixedSize = 0; break; case (HAS_NONE | HAS_VALUE): case (HAS_NULL | HAS_VALUE): sinfo->tupleBitmapSize = BIT1_SIZE(schema->numOfCols - 1); sinfo->tupleFixedSize = schema->flen; break; case (HAS_NONE | HAS_NULL | HAS_VALUE): sinfo->tupleBitmapSize = BIT2_SIZE(schema->numOfCols - 1); sinfo->tupleFixedSize = schema->flen; break; } for (int32_t i = 0; i < sinfo->numOfPKs; i++) { sinfo->tupleIndices[i].offset += sinfo->tupleBitmapSize; sinfo->tuplePKSize += tPutPrimaryKeyIndex(NULL, sinfo->tupleIndices + i); } sinfo->tupleRowSize = sizeof(SRow) // SRow + sinfo->tuplePKSize // primary keys + sinfo->tupleBitmapSize // bitmap + sinfo->tupleFixedSize // fixed part + sinfo->tupleVarSize; // var part // Key-Value if (sinfo->kvMaxOffset <= UINT8_MAX) { sinfo->kvFlag = (KV_FLG_LIT | sinfo->flag); sinfo->kvIndexSize = sizeof(SKVIdx) + (sinfo->numOfNull + sinfo->numOfValue) * sizeof(uint8_t); } else if (sinfo->kvMaxOffset <= UINT16_MAX) { sinfo->kvFlag = (KV_FLG_MID | sinfo->flag); sinfo->kvIndexSize = sizeof(SKVIdx) + (sinfo->numOfNull + sinfo->numOfValue) * sizeof(uint16_t); } else { sinfo->kvFlag = (KV_FLG_BIG | sinfo->flag); sinfo->kvIndexSize = sizeof(SKVIdx) + (sinfo->numOfNull + sinfo->numOfValue) * sizeof(uint32_t); } for (int32_t i = 0; i < sinfo->numOfPKs; i++) { sinfo->kvIndices[i].offset += sinfo->kvIndexSize; sinfo->kvPKSize += tPutPrimaryKeyIndex(NULL, sinfo->kvIndices + i); } sinfo->kvRowSize = sizeof(SRow) // SRow + sinfo->kvPKSize // primary keys + sinfo->kvIndexSize // index array + sinfo->kvPayloadSize; // payload _exit: return code; } static int32_t tRowBuildTupleRow(SArray *aColVal, const SRowBuildScanInfo *sinfo, const STSchema *schema, SRow **ppRow) { SColVal *colValArray = (SColVal *)TARRAY_DATA(aColVal); *ppRow = (SRow *)taosMemoryCalloc(1, sinfo->tupleRowSize); if (*ppRow == NULL) { return TSDB_CODE_OUT_OF_MEMORY; } (*ppRow)->flag = sinfo->tupleFlag; (*ppRow)->numOfPKs = sinfo->numOfPKs; (*ppRow)->sver = schema->version; (*ppRow)->len = sinfo->tupleRowSize; (*ppRow)->ts = colValArray[0].value.val; if (sinfo->tupleFlag == HAS_NONE || sinfo->tupleFlag == HAS_NULL) { ASSERT(sinfo->tupleRowSize == sizeof(SRow)); return 0; } uint8_t *primaryKeys = (*ppRow)->data; uint8_t *bitmap = primaryKeys + sinfo->tuplePKSize; uint8_t *fixed = bitmap + sinfo->tupleBitmapSize; uint8_t *varlen = fixed + sinfo->tupleFixedSize; // primary keys for (int32_t i = 0; i < sinfo->numOfPKs; i++) { primaryKeys += tPutPrimaryKeyIndex(primaryKeys, sinfo->tupleIndices + i); } ASSERT(primaryKeys == bitmap); // bitmap + fixed + varlen int32_t numOfColVals = TARRAY_SIZE(aColVal); int32_t colValIndex = 1; for (int32_t i = 1; i < schema->numOfCols; i++) { for (;;) { if (colValIndex >= numOfColVals) { // NONE ROW_SET_BITMAP(bitmap, sinfo->tupleFlag, i - 1, BIT_FLG_NONE); break; } if (colValArray[colValIndex].cid == schema->columns[i].colId) { if (COL_VAL_IS_VALUE(&colValArray[colValIndex])) { // value ROW_SET_BITMAP(bitmap, sinfo->tupleFlag, i - 1, BIT_FLG_VALUE); if (IS_VAR_DATA_TYPE(schema->columns[i].type)) { *(int32_t *)(fixed + schema->columns[i].offset) = varlen - fixed - sinfo->tupleFixedSize; varlen += tPutU32v(varlen, colValArray[colValIndex].value.nData); if (colValArray[colValIndex].value.nData) { (void)memcpy(varlen, colValArray[colValIndex].value.pData, colValArray[colValIndex].value.nData); varlen += colValArray[colValIndex].value.nData; } } else { (void)memcpy(fixed + schema->columns[i].offset, &colValArray[colValIndex].value.val, tDataTypes[schema->columns[i].type].bytes); } } else if (COL_VAL_IS_NULL(&colValArray[colValIndex])) { // NULL ROW_SET_BITMAP(bitmap, sinfo->tupleFlag, i - 1, BIT_FLG_NULL); } else if (COL_VAL_IS_NONE(&colValArray[colValIndex])) { // NONE ROW_SET_BITMAP(bitmap, sinfo->tupleFlag, i - 1, BIT_FLG_NONE); } colValIndex++; break; } else if (colValArray[colValIndex].cid > schema->columns[i].colId) { // NONE ROW_SET_BITMAP(bitmap, sinfo->tupleFlag, i - 1, BIT_FLG_NONE); break; } else { colValIndex++; } } } return 0; } static FORCE_INLINE void tRowBuildKVRowSetIndex(uint8_t flag, SKVIdx *indices, uint32_t offset) { if (flag & KV_FLG_LIT) { ((uint8_t *)indices->idx)[indices->nCol] = (uint8_t)offset; } else if (flag & KV_FLG_MID) { ((uint16_t *)indices->idx)[indices->nCol] = (uint16_t)offset; } else { ((uint32_t *)indices->idx)[indices->nCol] = (uint32_t)offset; } indices->nCol++; } static int32_t tRowBuildKVRow(SArray *aColVal, const SRowBuildScanInfo *sinfo, const STSchema *schema, SRow **ppRow) { SColVal *colValArray = (SColVal *)TARRAY_DATA(aColVal); *ppRow = (SRow *)taosMemoryCalloc(1, sinfo->kvRowSize); if (*ppRow == NULL) { return TSDB_CODE_OUT_OF_MEMORY; } (*ppRow)->flag = sinfo->kvFlag; (*ppRow)->numOfPKs = sinfo->numOfPKs; (*ppRow)->sver = schema->version; (*ppRow)->len = sinfo->kvRowSize; (*ppRow)->ts = colValArray[0].value.val; ASSERT(sinfo->flag != HAS_NONE && sinfo->flag != HAS_NULL); uint8_t *primaryKeys = (*ppRow)->data; SKVIdx *indices = (SKVIdx *)(primaryKeys + sinfo->kvPKSize); uint8_t *payload = primaryKeys + sinfo->kvPKSize + sinfo->kvIndexSize; uint32_t payloadSize = 0; // primary keys for (int32_t i = 0; i < sinfo->numOfPKs; i++) { primaryKeys += tPutPrimaryKeyIndex(primaryKeys, sinfo->kvIndices + i); } ASSERT(primaryKeys == (uint8_t *)indices); int32_t numOfColVals = TARRAY_SIZE(aColVal); int32_t colValIndex = 1; for (int32_t i = 1; i < schema->numOfCols; i++) { for (;;) { if (colValIndex >= numOfColVals) { // NONE break; } if (colValArray[colValIndex].cid == schema->columns[i].colId) { if (COL_VAL_IS_VALUE(&colValArray[colValIndex])) { // value tRowBuildKVRowSetIndex(sinfo->kvFlag, indices, payloadSize); if (IS_VAR_DATA_TYPE(schema->columns[i].type)) { payloadSize += tPutI16v(payload + payloadSize, colValArray[colValIndex].cid); payloadSize += tPutU32v(payload + payloadSize, colValArray[colValIndex].value.nData); if (colValArray[colValIndex].value.nData > 0) { (void)memcpy(payload + payloadSize, colValArray[colValIndex].value.pData, colValArray[colValIndex].value.nData); } payloadSize += colValArray[colValIndex].value.nData; } else { payloadSize += tPutI16v(payload + payloadSize, colValArray[colValIndex].cid); (void)memcpy(payload + payloadSize, &colValArray[colValIndex].value.val, tDataTypes[schema->columns[i].type].bytes); payloadSize += tDataTypes[schema->columns[i].type].bytes; } } else if (COL_VAL_IS_NULL(&colValArray[colValIndex])) { // NULL tRowBuildKVRowSetIndex(sinfo->kvFlag, indices, payloadSize); payloadSize += tPutI16v(payload + payloadSize, -schema->columns[i].colId); } colValIndex++; break; } else if (colValArray[colValIndex].cid > schema->columns[i].colId) { // NONE break; } else { colValIndex++; } } } return 0; } int32_t tRowBuild(SArray *aColVal, const STSchema *pTSchema, SRow **ppRow) { int32_t code; SRowBuildScanInfo sinfo; code = tRowBuildScan(aColVal, pTSchema, &sinfo); if (code) return code; if (sinfo.tupleRowSize <= sinfo.kvRowSize) { code = tRowBuildTupleRow(aColVal, &sinfo, pTSchema, ppRow); } else { code = tRowBuildKVRow(aColVal, &sinfo, pTSchema, ppRow); } return code; } static int32_t tBindInfoCompare(const void *p1, const void *p2, const void *param) { if (((SBindInfo *)p1)->columnId < ((SBindInfo *)p2)->columnId) { return -1; } else if (((SBindInfo *)p1)->columnId > ((SBindInfo *)p2)->columnId) { return 1; } return 0; } /* build rows to `rowArray` from bind * `infos` is the bind information array * `numOfInfos` is the number of bind information * `infoSorted` is whether the bind information is sorted by column id * `pTSchema` is the schema of the table * `rowArray` is the array to store the rows */ int32_t tRowBuildFromBind(SBindInfo *infos, int32_t numOfInfos, bool infoSorted, const STSchema *pTSchema, SArray *rowArray) { if (infos == NULL || numOfInfos <= 0 || numOfInfos > pTSchema->numOfCols || pTSchema == NULL || rowArray == NULL) { return TSDB_CODE_INVALID_PARA; } if (!infoSorted) { taosqsort_r(infos, numOfInfos, sizeof(SBindInfo), NULL, tBindInfoCompare); } int32_t code = 0; int32_t numOfRows = infos[0].bind->num; SArray *colValArray; SColVal colVal; if ((colValArray = taosArrayInit(numOfInfos, sizeof(SColVal))) == NULL) { return terrno; } for (int32_t iRow = 0; iRow < numOfRows; iRow++) { taosArrayClear(colValArray); for (int32_t iInfo = 0; iInfo < numOfInfos; iInfo++) { if (infos[iInfo].bind->is_null && infos[iInfo].bind->is_null[iRow]) { colVal = COL_VAL_NULL(infos[iInfo].columnId, infos[iInfo].type); } else { SValue value = { .type = infos[iInfo].type, }; if (IS_VAR_DATA_TYPE(infos[iInfo].type)) { value.nData = infos[iInfo].bind->length[iRow]; value.pData = (uint8_t *)infos[iInfo].bind->buffer + infos[iInfo].bind->buffer_length * iRow; } else { (void)memcpy(&value.val, (uint8_t *)infos[iInfo].bind->buffer + infos[iInfo].bind->buffer_length * iRow, infos[iInfo].bind->buffer_length); } colVal = COL_VAL_VALUE(infos[iInfo].columnId, value); } if (taosArrayPush(colValArray, &colVal) == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } } SRow *row; if ((code = tRowBuild(colValArray, pTSchema, &row))) { goto _exit; } if ((taosArrayPush(rowArray, &row)) == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } } _exit: taosArrayDestroy(colValArray); return code; } int32_t tRowGet(SRow *pRow, STSchema *pTSchema, int32_t iCol, SColVal *pColVal) { ASSERT(iCol < pTSchema->numOfCols); ASSERT(pRow->sver == pTSchema->version); STColumn *pTColumn = pTSchema->columns + iCol; if (iCol == 0) { pColVal->cid = pTColumn->colId; pColVal->value.type = pTColumn->type; pColVal->flag = CV_FLAG_VALUE; (void)memcpy(&pColVal->value.val, &pRow->ts, sizeof(TSKEY)); return 0; } if (pRow->flag == HAS_NONE) { *pColVal = COL_VAL_NONE(pTColumn->colId, pTColumn->type); return 0; } if (pRow->flag == HAS_NULL) { *pColVal = COL_VAL_NULL(pTColumn->colId, pTColumn->type); return 0; } SPrimaryKeyIndex index; uint8_t *data = pRow->data; for (int32_t i = 0; i < pRow->numOfPKs; i++) { data += tGetPrimaryKeyIndex(data, &index); } if (pRow->flag >> 4) { // KV Row SKVIdx *pIdx = (SKVIdx *)data; uint8_t *pv = NULL; if (pRow->flag & KV_FLG_LIT) { pv = pIdx->idx + pIdx->nCol; } else if (pRow->flag & KV_FLG_MID) { pv = pIdx->idx + (pIdx->nCol << 1); } else { pv = pIdx->idx + (pIdx->nCol << 2); } int16_t lidx = 0; int16_t ridx = pIdx->nCol - 1; while (lidx <= ridx) { int16_t mid = (lidx + ridx) >> 1; uint8_t *pData = NULL; if (pRow->flag & KV_FLG_LIT) { pData = pv + ((uint8_t *)pIdx->idx)[mid]; } else if (pRow->flag & KV_FLG_MID) { pData = pv + ((uint16_t *)pIdx->idx)[mid]; } else { pData = pv + ((uint32_t *)pIdx->idx)[mid]; } int16_t cid; pData += tGetI16v(pData, &cid); if (TABS(cid) == pTColumn->colId) { if (cid < 0) { *pColVal = COL_VAL_NULL(pTColumn->colId, pTColumn->type); } else { pColVal->cid = pTColumn->colId; pColVal->value.type = pTColumn->type; pColVal->flag = CV_FLAG_VALUE; if (IS_VAR_DATA_TYPE(pTColumn->type)) { pData += tGetU32v(pData, &pColVal->value.nData); if (pColVal->value.nData > 0) { pColVal->value.pData = pData; } else { pColVal->value.pData = NULL; } } else { (void)memcpy(&pColVal->value.val, pData, pTColumn->bytes); } } return 0; } else if (TABS(cid) < pTColumn->colId) { lidx = mid + 1; } else { ridx = mid - 1; } } *pColVal = COL_VAL_NONE(pTColumn->colId, pTColumn->type); } else { // Tuple Row uint8_t *bitmap = data; uint8_t *fixed; uint8_t *varlen; uint8_t bit; if (pRow->flag == HAS_VALUE) { fixed = bitmap; bit = BIT_FLG_VALUE; } else if (pRow->flag == (HAS_NONE | HAS_NULL | HAS_VALUE)) { fixed = BIT2_SIZE(pTSchema->numOfCols - 1) + bitmap; bit = GET_BIT2(bitmap, iCol - 1); } else { fixed = BIT1_SIZE(pTSchema->numOfCols - 1) + bitmap; bit = GET_BIT1(bitmap, iCol - 1); if (pRow->flag == (HAS_NONE | HAS_VALUE)) { if (bit) bit++; } else if (pRow->flag == (HAS_NULL | HAS_VALUE)) { bit++; } } varlen = fixed + pTSchema->flen; if (bit == BIT_FLG_NONE) { *pColVal = COL_VAL_NONE(pTColumn->colId, pTColumn->type); return 0; } else if (bit == BIT_FLG_NULL) { *pColVal = COL_VAL_NULL(pTColumn->colId, pTColumn->type); return 0; } pColVal->cid = pTColumn->colId; pColVal->value.type = pTColumn->type; pColVal->flag = CV_FLAG_VALUE; if (IS_VAR_DATA_TYPE(pTColumn->type)) { pColVal->value.pData = varlen + *(int32_t *)(fixed + pTColumn->offset); pColVal->value.pData += tGetU32v(pColVal->value.pData, &pColVal->value.nData); } else { (void)memcpy(&pColVal->value.val, fixed + pTColumn->offset, TYPE_BYTES[pTColumn->type]); } } return 0; } void tRowDestroy(SRow *pRow) { if (pRow) taosMemoryFree(pRow); } static int32_t tRowPCmprFn(const void *p1, const void *p2) { SRowKey key1, key2; tRowGetKey(*(SRow **)p1, &key1); tRowGetKey(*(SRow **)p2, &key2); return tRowKeyCompare(&key1, &key2); } static void tRowPDestroy(SRow **ppRow) { tRowDestroy(*ppRow); } static int32_t tRowMergeImpl(SArray *aRowP, STSchema *pTSchema, int32_t iStart, int32_t iEnd, int8_t flag) { int32_t code = 0; int32_t nRow = iEnd - iStart; SRowIter **aIter = NULL; SArray *aColVal = NULL; SRow *pRow = NULL; aIter = taosMemoryCalloc(nRow, sizeof(SRowIter *)); if (aIter == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } for (int32_t i = 0; i < nRow; i++) { SRow *pRowT = taosArrayGetP(aRowP, iStart + i); code = tRowIterOpen(pRowT, pTSchema, &aIter[i]); if (code) goto _exit; } // merge aColVal = taosArrayInit(pTSchema->numOfCols, sizeof(SColVal)); if (aColVal == NULL) { code = terrno; goto _exit; } for (int32_t iCol = 0; iCol < pTSchema->numOfCols; iCol++) { SColVal *pColVal = NULL; for (int32_t iRow = nRow - 1; iRow >= 0; --iRow) { SColVal *pColValT = tRowIterNext(aIter[iRow]); while (pColValT->cid < pTSchema->columns[iCol].colId) { pColValT = tRowIterNext(aIter[iRow]); } // todo: take strategy according to the flag if (COL_VAL_IS_VALUE(pColValT)) { pColVal = pColValT; break; } else if (COL_VAL_IS_NULL(pColValT)) { if (pColVal == NULL) { pColVal = pColValT; } } } if (pColVal) { if (taosArrayPush(aColVal, pColVal) == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } } } // build code = tRowBuild(aColVal, pTSchema, &pRow); if (code) goto _exit; taosArrayRemoveBatch(aRowP, iStart, nRow, (FDelete)tRowPDestroy); if (taosArrayInsert(aRowP, iStart, &pRow) == NULL) { code = terrno; goto _exit; } _exit: if (aIter) { for (int32_t i = 0; i < nRow; i++) { tRowIterClose(&aIter[i]); } taosMemoryFree(aIter); } if (aColVal) taosArrayDestroy(aColVal); if (code) tRowDestroy(pRow); return code; } int32_t tRowSort(SArray *aRowP) { if (TARRAY_SIZE(aRowP) <= 1) return 0; int32_t code = taosArrayMSort(aRowP, tRowPCmprFn); if (code != TSDB_CODE_SUCCESS) { uError("taosArrayMSort failed caused by %d", code); } return code; } int32_t tRowMerge(SArray *aRowP, STSchema *pTSchema, int8_t flag) { int32_t code = 0; int32_t iStart = 0; while (iStart < aRowP->size) { SRowKey key1; SRow *row1 = (SRow *)taosArrayGetP(aRowP, iStart); tRowGetKey(row1, &key1); int32_t iEnd = iStart + 1; while (iEnd < aRowP->size) { SRowKey key2; SRow *row2 = (SRow *)taosArrayGetP(aRowP, iEnd); tRowGetKey(row2, &key2); if (tRowKeyCompare(&key1, &key2) != 0) break; iEnd++; } if (iEnd - iStart > 1) { code = tRowMergeImpl(aRowP, pTSchema, iStart, iEnd, flag); if (code) return code; } // the array is also changing, so the iStart just ++ instead of iEnd iStart++; } return code; } // SRowIter ======================================== struct SRowIter { SRow *pRow; STSchema *pTSchema; int32_t iTColumn; union { struct { // kv int32_t iCol; SKVIdx *pIdx; }; struct { // tuple uint8_t *pb; uint8_t *pf; }; }; uint8_t *pv; SColVal cv; }; int32_t tRowIterOpen(SRow *pRow, STSchema *pTSchema, SRowIter **ppIter) { ASSERT(pRow->sver == pTSchema->version); int32_t code = 0; SRowIter *pIter = taosMemoryCalloc(1, sizeof(*pIter)); if (pIter == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } pIter->pRow = pRow; pIter->pTSchema = pTSchema; pIter->iTColumn = 0; if (pRow->flag == HAS_NONE || pRow->flag == HAS_NULL) goto _exit; uint8_t *data = pRow->data; SPrimaryKeyIndex index; for (int32_t i = 0; i < pRow->numOfPKs; i++) { data += tGetPrimaryKeyIndex(data, &index); } if (pRow->flag >> 4) { pIter->iCol = 0; pIter->pIdx = (SKVIdx *)data; if (pRow->flag & KV_FLG_LIT) { pIter->pv = pIter->pIdx->idx + pIter->pIdx->nCol; } else if (pRow->flag & KV_FLG_MID) { pIter->pv = pIter->pIdx->idx + (pIter->pIdx->nCol << 1); // * sizeof(uint16_t) } else { pIter->pv = pIter->pIdx->idx + (pIter->pIdx->nCol << 2); // * sizeof(uint32_t) } } else { switch (pRow->flag) { case (HAS_NULL | HAS_NONE): pIter->pb = data; break; case HAS_VALUE: pIter->pf = data; pIter->pv = pIter->pf + pTSchema->flen; break; case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): pIter->pb = data; pIter->pf = data + BIT1_SIZE(pTSchema->numOfCols - 1); pIter->pv = pIter->pf + pTSchema->flen; break; case (HAS_VALUE | HAS_NULL | HAS_NONE): pIter->pb = data; pIter->pf = data + BIT2_SIZE(pTSchema->numOfCols - 1); pIter->pv = pIter->pf + pTSchema->flen; break; default: ASSERT(0); break; } } _exit: if (code) { *ppIter = NULL; } else { *ppIter = pIter; } return code; } void tRowIterClose(SRowIter **ppIter) { SRowIter *pIter = *ppIter; if (pIter) { taosMemoryFree(pIter); } *ppIter = NULL; } SColVal *tRowIterNext(SRowIter *pIter) { if (pIter->iTColumn >= pIter->pTSchema->numOfCols) { return NULL; } STColumn *pTColumn = pIter->pTSchema->columns + pIter->iTColumn; // timestamp if (0 == pIter->iTColumn) { pIter->cv.cid = pTColumn->colId; pIter->cv.value.type = pTColumn->type; pIter->cv.flag = CV_FLAG_VALUE; (void)memcpy(&pIter->cv.value.val, &pIter->pRow->ts, sizeof(TSKEY)); goto _exit; } if (pIter->pRow->flag == HAS_NONE) { pIter->cv = COL_VAL_NONE(pTColumn->colId, pTColumn->type); goto _exit; } if (pIter->pRow->flag == HAS_NULL) { pIter->cv = COL_VAL_NULL(pTColumn->colId, pTColumn->type); goto _exit; } if (pIter->pRow->flag >> 4) { // KV if (pIter->iCol < pIter->pIdx->nCol) { uint8_t *pData; if (pIter->pRow->flag & KV_FLG_LIT) { pData = pIter->pv + ((uint8_t *)pIter->pIdx->idx)[pIter->iCol]; } else if (pIter->pRow->flag & KV_FLG_MID) { pData = pIter->pv + ((uint16_t *)pIter->pIdx->idx)[pIter->iCol]; } else { pData = pIter->pv + ((uint32_t *)pIter->pIdx->idx)[pIter->iCol]; } int16_t cid; pData += tGetI16v(pData, &cid); if (TABS(cid) == pTColumn->colId) { if (cid < 0) { pIter->cv = COL_VAL_NULL(pTColumn->colId, pTColumn->type); } else { pIter->cv.cid = pTColumn->colId; pIter->cv.value.type = pTColumn->type; pIter->cv.flag = CV_FLAG_VALUE; if (IS_VAR_DATA_TYPE(pTColumn->type)) { pData += tGetU32v(pData, &pIter->cv.value.nData); if (pIter->cv.value.nData > 0) { pIter->cv.value.pData = pData; } else { pIter->cv.value.pData = NULL; } } else { (void)memcpy(&pIter->cv.value.val, pData, pTColumn->bytes); } } pIter->iCol++; goto _exit; } else if (TABS(cid) > pTColumn->colId) { pIter->cv = COL_VAL_NONE(pTColumn->colId, pTColumn->type); goto _exit; } else { ASSERT(0); } } else { pIter->cv = COL_VAL_NONE(pTColumn->colId, pTColumn->type); goto _exit; } } else { // Tuple uint8_t bv = BIT_FLG_VALUE; if (pIter->pb) { switch (pIter->pRow->flag) { case (HAS_NULL | HAS_NONE): bv = GET_BIT1(pIter->pb, pIter->iTColumn - 1); break; case (HAS_VALUE | HAS_NONE): bv = GET_BIT1(pIter->pb, pIter->iTColumn - 1); if (bv) bv++; break; case (HAS_VALUE | HAS_NULL): bv = GET_BIT1(pIter->pb, pIter->iTColumn - 1) + 1; break; case (HAS_VALUE | HAS_NULL | HAS_NONE): bv = GET_BIT2(pIter->pb, pIter->iTColumn - 1); break; default: ASSERT(0); break; } if (bv == BIT_FLG_NONE) { pIter->cv = COL_VAL_NONE(pTColumn->colId, pTColumn->type); goto _exit; } else if (bv == BIT_FLG_NULL) { pIter->cv = COL_VAL_NULL(pTColumn->colId, pTColumn->type); goto _exit; } } pIter->cv.cid = pTColumn->colId; pIter->cv.value.type = pTColumn->type; pIter->cv.flag = CV_FLAG_VALUE; if (IS_VAR_DATA_TYPE(pTColumn->type)) { uint8_t *pData = pIter->pv + *(int32_t *)(pIter->pf + pTColumn->offset); pData += tGetU32v(pData, &pIter->cv.value.nData); if (pIter->cv.value.nData > 0) { pIter->cv.value.pData = pData; } else { pIter->cv.value.pData = NULL; } } else { (void)memcpy(&pIter->cv.value.val, pIter->pf + pTColumn->offset, TYPE_BYTES[pTColumn->type]); } goto _exit; } _exit: pIter->iTColumn++; return &pIter->cv; } static int32_t tRowNoneUpsertColData(SColData *aColData, int32_t nColData, int32_t flag) { int32_t code = 0; if (flag) return code; for (int32_t iColData = 0; iColData < nColData; iColData++) { code = tColDataAppendValueImpl[aColData[iColData].flag][CV_FLAG_NONE](&aColData[iColData], NULL, 0); if (code) return code; } return code; } static int32_t tRowNullUpsertColData(SColData *aColData, int32_t nColData, STSchema *pSchema, int32_t flag) { int32_t code = 0; int32_t iColData = 0; SColData *pColData = &aColData[iColData]; int32_t iTColumn = 1; STColumn *pTColumn = &pSchema->columns[iTColumn]; while (pColData) { if (pTColumn) { if (pTColumn->colId == pColData->cid) { // NULL if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0, flag > 0); } if (code) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; pTColumn = (++iTColumn < pSchema->numOfCols) ? &pSchema->columns[iTColumn] : NULL; } else if (pTColumn->colId > pColData->cid) { // NONE if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } else { pTColumn = (++iTColumn < pSchema->numOfCols) ? &pSchema->columns[iTColumn] : NULL; } } else { // NONE if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } } _exit: return code; } static int32_t tRowTupleUpsertColData(SRow *pRow, STSchema *pTSchema, SColData *aColData, int32_t nColData, int32_t flag) { int32_t code = 0; int32_t iColData = 0; SColData *pColData = &aColData[iColData]; int32_t iTColumn = 1; STColumn *pTColumn = &pTSchema->columns[iTColumn]; uint8_t *pb = NULL, *pf = NULL, *pv = NULL; SPrimaryKeyIndex index; uint8_t *data = pRow->data; for (int32_t i = 0; i < pRow->numOfPKs; i++) { data += tGetPrimaryKeyIndex(data, &index); } switch (pRow->flag) { case HAS_VALUE: pf = data; // TODO: fix here pv = pf + pTSchema->flen; break; case (HAS_NULL | HAS_NONE): pb = data; break; case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): pb = data; pf = pb + BIT1_SIZE(pTSchema->numOfCols - 1); pv = pf + pTSchema->flen; break; case (HAS_VALUE | HAS_NULL | HAS_NONE): pb = data; pf = pb + BIT2_SIZE(pTSchema->numOfCols - 1); pv = pf + pTSchema->flen; break; default: ASSERTS(0, "Invalid row flag"); return TSDB_CODE_INVALID_DATA_FMT; } while (pColData) { if (pTColumn) { if (pTColumn->colId == pColData->cid) { ASSERT(pTColumn->type == pColData->type); if (pb) { uint8_t bv; switch (pRow->flag) { case (HAS_NULL | HAS_NONE): bv = GET_BIT1(pb, iTColumn - 1); break; case (HAS_VALUE | HAS_NONE): bv = GET_BIT1(pb, iTColumn - 1); if (bv) bv++; break; case (HAS_VALUE | HAS_NULL): bv = GET_BIT1(pb, iTColumn - 1) + 1; break; case (HAS_VALUE | HAS_NULL | HAS_NONE): bv = GET_BIT2(pb, iTColumn - 1); break; default: ASSERTS(0, "Invalid row flag"); return TSDB_CODE_INVALID_DATA_FMT; } if (bv == BIT_FLG_NONE) { if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; goto _continue; } else if (bv == BIT_FLG_NULL) { if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0, flag > 0); } if (code) goto _exit; goto _continue; } } if (IS_VAR_DATA_TYPE(pColData->type)) { uint8_t *pData = pv + *(int32_t *)(pf + pTColumn->offset); uint32_t nData; pData += tGetU32v(pData, &nData); if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pData, nData); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pData, nData, flag > 0); } if (code) goto _exit; } else { if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pf + pTColumn->offset, TYPE_BYTES[pColData->type]); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pf + pTColumn->offset, TYPE_BYTES[pColData->type], flag > 0); } if (code) goto _exit; } _continue: pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; pTColumn = (++iTColumn < pTSchema->numOfCols) ? &pTSchema->columns[iTColumn] : NULL; } else if (pTColumn->colId > pColData->cid) { // NONE if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } else { pTColumn = (++iTColumn < pTSchema->numOfCols) ? &pTSchema->columns[iTColumn] : NULL; } } else { if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } } _exit: return code; } static int32_t tRowKVUpsertColData(SRow *pRow, STSchema *pTSchema, SColData *aColData, int32_t nColData, int32_t flag) { int32_t code = 0; uint8_t *pv = NULL; int32_t iColData = 0; SColData *pColData = &aColData[iColData]; int32_t iTColumn = 1; STColumn *pTColumn = &pTSchema->columns[iTColumn]; int32_t iCol = 0; // primary keys uint8_t *data = pRow->data; SPrimaryKeyIndex index; for (int32_t i = 0; i < pRow->numOfPKs; i++) { data += tGetPrimaryKeyIndex(data, &index); } SKVIdx *pKVIdx = (SKVIdx *)data; if (pRow->flag & KV_FLG_LIT) { pv = pKVIdx->idx + pKVIdx->nCol; } else if (pRow->flag & KV_FLG_MID) { pv = pKVIdx->idx + (pKVIdx->nCol << 1); } else if (pRow->flag & KV_FLG_BIG) { pv = pKVIdx->idx + (pKVIdx->nCol << 2); } else { ASSERT(0); } while (pColData) { if (pTColumn) { if (pTColumn->colId == pColData->cid) { while (iCol < pKVIdx->nCol) { uint8_t *pData; if (pRow->flag & KV_FLG_LIT) { pData = pv + ((uint8_t *)pKVIdx->idx)[iCol]; } else if (pRow->flag & KV_FLG_MID) { pData = pv + ((uint16_t *)pKVIdx->idx)[iCol]; } else if (pRow->flag & KV_FLG_BIG) { pData = pv + ((uint32_t *)pKVIdx->idx)[iCol]; } else { ASSERTS(0, "Invalid KV row format"); return TSDB_CODE_INVALID_DATA_FMT; } int16_t cid; pData += tGetI16v(pData, &cid); if (TABS(cid) == pTColumn->colId) { if (cid < 0) { if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0, flag > 0); } if (code) goto _exit; } else { uint32_t nData; if (IS_VAR_DATA_TYPE(pTColumn->type)) { pData += tGetU32v(pData, &nData); } else { nData = 0; } if (flag == 0) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pData, nData); } else { code = tColDataUpdateValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, pData, nData, flag > 0); } if (code) goto _exit; } iCol++; goto _continue; } else if (TABS(cid) > pTColumn->colId) { // NONE break; } else { iCol++; } } if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; _continue: pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; pTColumn = (++iTColumn < pTSchema->numOfCols) ? &pTSchema->columns[iTColumn] : NULL; } else if (pTColumn->colId > pColData->cid) { if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } else { pTColumn = (++iTColumn < pTSchema->numOfCols) ? &pTSchema->columns[iTColumn] : NULL; } } else { if (flag == 0 && (code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0))) goto _exit; pColData = (++iColData < nColData) ? &aColData[iColData] : NULL; } } _exit: return code; } /* flag > 0: forward update * flag == 0: append * flag < 0: backward update */ int32_t tRowUpsertColData(SRow *pRow, STSchema *pTSchema, SColData *aColData, int32_t nColData, int32_t flag) { ASSERT(pRow->sver == pTSchema->version); ASSERT(nColData > 0); if (pRow->flag == HAS_NONE) { return tRowNoneUpsertColData(aColData, nColData, flag); } else if (pRow->flag == HAS_NULL) { return tRowNullUpsertColData(aColData, nColData, pTSchema, flag); } else if (pRow->flag >> 4) { // KV row return tRowKVUpsertColData(pRow, pTSchema, aColData, nColData, flag); } else { // TUPLE row return tRowTupleUpsertColData(pRow, pTSchema, aColData, nColData, flag); } } void tRowGetPrimaryKey(SRow *row, SRowKey *key) { key->numOfPKs = row->numOfPKs; if (key->numOfPKs == 0) { return; } ASSERT(row->numOfPKs <= TD_MAX_PK_COLS); SPrimaryKeyIndex indices[TD_MAX_PK_COLS]; uint8_t *data = row->data; for (int32_t i = 0; i < row->numOfPKs; i++) { data += tGetPrimaryKeyIndex(data, &indices[i]); } // primary keys for (int32_t i = 0; i < row->numOfPKs; i++) { key->pks[i].type = indices[i].type; uint8_t *tdata = data + indices[i].offset; if (row->flag >> 4) { tdata += tGetI16v(tdata, NULL); } if (IS_VAR_DATA_TYPE(indices[i].type)) { key->pks[i].pData = tdata; key->pks[i].pData += tGetU32v(key->pks[i].pData, &key->pks[i].nData); } else { (void)memcpy(&key->pks[i].val, tdata, tDataTypes[indices[i].type].bytes); } } } #define T_COMPARE_SCALAR_VALUE(TYPE, V1, V2) \ do { \ if (*(TYPE *)(V1) < *(TYPE *)(V2)) { \ return -1; \ } else if (*(TYPE *)(V1) > *(TYPE *)(V2)) { \ return 1; \ } else { \ return 0; \ } \ } while (0) int32_t tValueCompare(const SValue *tv1, const SValue *tv2) { ASSERT(tv1->type == tv2->type); switch (tv1->type) { case TSDB_DATA_TYPE_BOOL: case TSDB_DATA_TYPE_TINYINT: T_COMPARE_SCALAR_VALUE(int8_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_SMALLINT: T_COMPARE_SCALAR_VALUE(int16_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_INT: T_COMPARE_SCALAR_VALUE(int32_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_BIGINT: case TSDB_DATA_TYPE_TIMESTAMP: T_COMPARE_SCALAR_VALUE(int64_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_FLOAT: T_COMPARE_SCALAR_VALUE(float, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_DOUBLE: T_COMPARE_SCALAR_VALUE(double, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_UTINYINT: T_COMPARE_SCALAR_VALUE(uint8_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_USMALLINT: T_COMPARE_SCALAR_VALUE(uint16_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_UINT: T_COMPARE_SCALAR_VALUE(uint32_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_UBIGINT: T_COMPARE_SCALAR_VALUE(uint64_t, &tv1->val, &tv2->val); case TSDB_DATA_TYPE_GEOMETRY: case TSDB_DATA_TYPE_BINARY: { int32_t ret = strncmp((const char *)tv1->pData, (const char *)tv2->pData, TMIN(tv1->nData, tv2->nData)); return ret ? ret : (tv1->nData < tv2->nData ? -1 : (tv1->nData > tv2->nData ? 1 : 0)); } case TSDB_DATA_TYPE_NCHAR: { int32_t ret = tasoUcs4Compare((TdUcs4 *)tv1->pData, (TdUcs4 *)tv2->pData, tv1->nData < tv2->nData ? tv1->nData : tv2->nData); return ret ? ret : (tv1->nData < tv2->nData ? -1 : (tv1->nData > tv2->nData ? 1 : 0)); } case TSDB_DATA_TYPE_VARBINARY: { int32_t ret = memcmp(tv1->pData, tv2->pData, tv1->nData < tv2->nData ? tv1->nData : tv2->nData); return ret ? ret : (tv1->nData < tv2->nData ? -1 : (tv1->nData > tv2->nData ? 1 : 0)); } case TSDB_DATA_TYPE_DECIMAL: ASSERT(0); break; default: ASSERT(0); } return 0; } // NOTE: // set key->numOfPKs to 0 as the smallest key with ts // set key->numOfPKs to (TD_MAX_PK_COLS + 1) as the largest key with ts FORCE_INLINE int32_t tRowKeyCompare(const SRowKey *key1, const SRowKey *key2) { if (key1->ts < key2->ts) { return -1; } else if (key1->ts > key2->ts) { return 1; } if (key1->numOfPKs == key2->numOfPKs) { for (uint8_t iKey = 0; iKey < key1->numOfPKs; iKey++) { int32_t ret = tValueCompare(&key1->pks[iKey], &key2->pks[iKey]); if (ret) return ret; } } else if (key1->numOfPKs < key2->numOfPKs) { return -1; } else { return 1; } return 0; } void tRowKeyAssign(SRowKey *pDst, SRowKey *pSrc) { pDst->ts = pSrc->ts; pDst->numOfPKs = pSrc->numOfPKs; if (pSrc->numOfPKs > 0) { for (int32_t i = 0; i < pSrc->numOfPKs; ++i) { SValue *pVal = &pDst->pks[i]; pVal->type = pSrc->pks[i].type; if (IS_NUMERIC_TYPE(pVal->type)) { pVal->val = pSrc->pks[i].val; } else { pVal->nData = pSrc->pks[i].nData; ASSERT(pSrc->pks[i].pData != NULL); (void)memcpy(pVal->pData, pSrc->pks[i].pData, pVal->nData); } } } } // STag ======================================== static int tTagValCmprFn(const void *p1, const void *p2) { if (((STagVal *)p1)->cid < ((STagVal *)p2)->cid) { return -1; } else if (((STagVal *)p1)->cid > ((STagVal *)p2)->cid) { return 1; } return 0; } static int tTagValJsonCmprFn(const void *p1, const void *p2) { return strcmp(((STagVal *)p1)[0].pKey, ((STagVal *)p2)[0].pKey); } #ifdef TD_DEBUG_PRINT_TAG static void debugPrintTagVal(int8_t type, const void *val, int32_t vlen, const char *tag, int32_t ln) { switch (type) { case TSDB_DATA_TYPE_VARBINARY: case TSDB_DATA_TYPE_JSON: case TSDB_DATA_TYPE_VARCHAR: case TSDB_DATA_TYPE_NCHAR: case TSDB_DATA_TYPE_GEOMETRY: { char tmpVal[32] = {0}; strncpy(tmpVal, val, vlen > 31 ? 31 : vlen); printf("%s:%d type:%d vlen:%d, val:\"%s\"\n", tag, ln, (int32_t)type, vlen, tmpVal); } break; case TSDB_DATA_TYPE_FLOAT: printf("%s:%d type:%d vlen:%d, val:%f\n", tag, ln, (int32_t)type, vlen, *(float *)val); break; case TSDB_DATA_TYPE_DOUBLE: printf("%s:%d type:%d vlen:%d, val:%lf\n", tag, ln, (int32_t)type, vlen, *(double *)val); break; case TSDB_DATA_TYPE_BOOL: printf("%s:%d type:%d vlen:%d, val:%" PRIu8 "\n", tag, ln, (int32_t)type, vlen, *(uint8_t *)val); break; case TSDB_DATA_TYPE_TINYINT: printf("%s:%d type:%d vlen:%d, val:%" PRIi8 "\n", tag, ln, (int32_t)type, vlen, *(int8_t *)val); break; case TSDB_DATA_TYPE_SMALLINT: printf("%s:%d type:%d vlen:%d, val:%" PRIi16 "\n", tag, ln, (int32_t)type, vlen, *(int16_t *)val); break; case TSDB_DATA_TYPE_INT: printf("%s:%d type:%d vlen:%d, val:%" PRIi32 "\n", tag, ln, (int32_t)type, vlen, *(int32_t *)val); break; case TSDB_DATA_TYPE_BIGINT: printf("%s:%d type:%d vlen:%d, val:%" PRIi64 "\n", tag, ln, (int32_t)type, vlen, *(int64_t *)val); break; case TSDB_DATA_TYPE_TIMESTAMP: printf("%s:%d type:%d vlen:%d, val:%" PRIi64 "\n", tag, ln, (int32_t)type, vlen, *(int64_t *)val); break; case TSDB_DATA_TYPE_UTINYINT: printf("%s:%d type:%d vlen:%d, val:%" PRIu8 "\n", tag, ln, (int32_t)type, vlen, *(uint8_t *)val); break; case TSDB_DATA_TYPE_USMALLINT: printf("%s:%d type:%d vlen:%d, val:%" PRIu16 "\n", tag, ln, (int32_t)type, vlen, *(uint16_t *)val); break; case TSDB_DATA_TYPE_UINT: printf("%s:%d type:%d vlen:%d, val:%" PRIu32 "\n", tag, ln, (int32_t)type, vlen, *(uint32_t *)val); break; case TSDB_DATA_TYPE_UBIGINT: printf("%s:%d type:%d vlen:%d, val:%" PRIu64 "\n", tag, ln, (int32_t)type, vlen, *(uint64_t *)val); break; case TSDB_DATA_TYPE_NULL: printf("%s:%d type:%d vlen:%d, val:%" PRIi8 "\n", tag, ln, (int32_t)type, vlen, *(int8_t *)val); break; default: ASSERT(0); break; } } void debugPrintSTag(STag *pTag, const char *tag, int32_t ln) { int8_t isJson = pTag->flags & TD_TAG_JSON; int8_t isLarge = pTag->flags & TD_TAG_LARGE; uint8_t *p = NULL; int16_t offset = 0; if (isLarge) { p = (uint8_t *)&((int16_t *)pTag->idx)[pTag->nTag]; } else { p = (uint8_t *)&pTag->idx[pTag->nTag]; } printf("%s:%d >>> STAG === %s:%s, len: %d, nTag: %d, sver:%d\n", tag, ln, isJson ? "json" : "normal", isLarge ? "large" : "small", (int32_t)pTag->len, (int32_t)pTag->nTag, pTag->ver); for (uint16_t n = 0; n < pTag->nTag; ++n) { if (isLarge) { offset = ((int16_t *)pTag->idx)[n]; } else { offset = pTag->idx[n]; } STagVal tagVal = {0}; if (isJson) { tagVal.pKey = (char *)POINTER_SHIFT(p, offset); } else { tagVal.cid = *(int16_t *)POINTER_SHIFT(p, offset); } printf("%s:%d loop[%d-%d] offset=%d\n", __func__, __LINE__, (int32_t)pTag->nTag, (int32_t)n, (int32_t)offset); tGetTagVal(p + offset, &tagVal, isJson); if (IS_VAR_DATA_TYPE(tagVal.type)) { debugPrintTagVal(tagVal.type, tagVal.pData, tagVal.nData, __func__, __LINE__); } else { debugPrintTagVal(tagVal.type, &tagVal.i64, tDataTypes[tagVal.type].bytes, __func__, __LINE__); } } printf("\n"); } #endif static int32_t tPutTagVal(uint8_t *p, STagVal *pTagVal, int8_t isJson) { int32_t n = 0; // key if (isJson) { n += tPutCStr(p ? p + n : p, pTagVal->pKey); } else { n += tPutI16v(p ? p + n : p, pTagVal->cid); ASSERTS(pTagVal->cid > 0, "Invalid tag cid:%" PRIi16, pTagVal->cid); } // type n += tPutI8(p ? p + n : p, pTagVal->type); // value if (IS_VAR_DATA_TYPE(pTagVal->type)) { n += tPutBinary(p ? p + n : p, pTagVal->pData, pTagVal->nData); } else { p = p ? p + n : p; n += tDataTypes[pTagVal->type].bytes; if (p) (void)memcpy(p, &(pTagVal->i64), tDataTypes[pTagVal->type].bytes); } return n; } static int32_t tGetTagVal(uint8_t *p, STagVal *pTagVal, int8_t isJson) { int32_t n = 0; // key if (isJson) { n += tGetCStr(p + n, &pTagVal->pKey); } else { n += tGetI16v(p + n, &pTagVal->cid); } // type n += tGetI8(p + n, &pTagVal->type); // value if (IS_VAR_DATA_TYPE(pTagVal->type)) { n += tGetBinary(p + n, &pTagVal->pData, &pTagVal->nData); } else { (void)memcpy(&(pTagVal->i64), p + n, tDataTypes[pTagVal->type].bytes); n += tDataTypes[pTagVal->type].bytes; } return n; } bool tTagIsJson(const void *pTag) { return (((const STag *)pTag)->flags & TD_TAG_JSON); } bool tTagIsJsonNull(void *data) { STag *pTag = (STag *)data; int8_t isJson = tTagIsJson(pTag); if (!isJson) return false; return ((STag *)data)->nTag == 0; } int32_t tTagNew(SArray *pArray, int32_t version, int8_t isJson, STag **ppTag) { int32_t code = 0; uint8_t *p = NULL; int16_t n = 0; int16_t nTag = taosArrayGetSize(pArray); int32_t szTag = 0; int8_t isLarge = 0; // sort if (isJson) { taosSort(pArray->pData, nTag, sizeof(STagVal), tTagValJsonCmprFn); } else { taosSort(pArray->pData, nTag, sizeof(STagVal), tTagValCmprFn); } // get size for (int16_t iTag = 0; iTag < nTag; iTag++) { szTag += tPutTagVal(NULL, (STagVal *)taosArrayGet(pArray, iTag), isJson); } if (szTag <= INT8_MAX) { szTag = szTag + sizeof(STag) + sizeof(int8_t) * nTag; } else { szTag = szTag + sizeof(STag) + sizeof(int16_t) * nTag; isLarge = 1; } ASSERT(szTag <= INT16_MAX); // build tag (*ppTag) = (STag *)taosMemoryCalloc(szTag, 1); if ((*ppTag) == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _err; } (*ppTag)->flags = 0; if (isJson) { (*ppTag)->flags |= TD_TAG_JSON; } if (isLarge) { (*ppTag)->flags |= TD_TAG_LARGE; } (*ppTag)->len = szTag; (*ppTag)->nTag = nTag; (*ppTag)->ver = version; if (isLarge) { p = (uint8_t *)&((int16_t *)(*ppTag)->idx)[nTag]; } else { p = (uint8_t *)&(*ppTag)->idx[nTag]; } n = 0; for (int16_t iTag = 0; iTag < nTag; iTag++) { if (isLarge) { ((int16_t *)(*ppTag)->idx)[iTag] = n; } else { (*ppTag)->idx[iTag] = n; } n += tPutTagVal(p + n, (STagVal *)taosArrayGet(pArray, iTag), isJson); } #ifdef TD_DEBUG_PRINT_TAG debugPrintSTag(*ppTag, __func__, __LINE__); #endif return code; _err: return code; } void tTagFree(STag *pTag) { if (pTag) taosMemoryFree(pTag); } char *tTagValToData(const STagVal *value, bool isJson) { if (!value) { return NULL; } char *data = NULL; int8_t typeBytes = 0; if (isJson) { typeBytes = CHAR_BYTES; } if (IS_VAR_DATA_TYPE(value->type)) { data = taosMemoryCalloc(1, typeBytes + VARSTR_HEADER_SIZE + value->nData); if (data == NULL) { return NULL; } if (isJson) { *data = value->type; } varDataLen(data + typeBytes) = value->nData; (void)memcpy(varDataVal(data + typeBytes), value->pData, value->nData); } else { data = ((char *)&(value->i64)) - typeBytes; // json with type } return data; } bool tTagGet(const STag *pTag, STagVal *pTagVal) { if (!pTag || !pTagVal) { return false; } int16_t lidx = 0; int16_t ridx = pTag->nTag - 1; int16_t midx; uint8_t *p; int8_t isJson = pTag->flags & TD_TAG_JSON; int8_t isLarge = pTag->flags & TD_TAG_LARGE; int16_t offset; STagVal tv; int c; if (isLarge) { p = (uint8_t *)&((int16_t *)pTag->idx)[pTag->nTag]; } else { p = (uint8_t *)&pTag->idx[pTag->nTag]; } pTagVal->type = TSDB_DATA_TYPE_NULL; pTagVal->pData = NULL; pTagVal->nData = 0; while (lidx <= ridx) { midx = (lidx + ridx) / 2; if (isLarge) { offset = ((int16_t *)pTag->idx)[midx]; } else { offset = pTag->idx[midx]; } (void)tGetTagVal(p + offset, &tv, isJson); if (isJson) { c = tTagValJsonCmprFn(pTagVal, &tv); } else { c = tTagValCmprFn(pTagVal, &tv); } if (c < 0) { ridx = midx - 1; } else if (c > 0) { lidx = midx + 1; } else { (void)memcpy(pTagVal, &tv, sizeof(tv)); return true; } } return false; } int32_t tEncodeTag(SEncoder *pEncoder, const STag *pTag) { return tEncodeBinary(pEncoder, (const uint8_t *)pTag, pTag->len); } int32_t tDecodeTag(SDecoder *pDecoder, STag **ppTag) { return tDecodeBinary(pDecoder, (uint8_t **)ppTag, NULL); } int32_t tTagToValArray(const STag *pTag, SArray **ppArray) { int32_t code = 0; uint8_t *p = NULL; STagVal tv = {0}; int8_t isLarge = pTag->flags & TD_TAG_LARGE; int16_t offset = 0; if (isLarge) { p = (uint8_t *)&((int16_t *)pTag->idx)[pTag->nTag]; } else { p = (uint8_t *)&pTag->idx[pTag->nTag]; } (*ppArray) = taosArrayInit(pTag->nTag + 1, sizeof(STagVal)); if (*ppArray == NULL) { code = terrno; goto _err; } for (int16_t iTag = 0; iTag < pTag->nTag; iTag++) { if (isLarge) { offset = ((int16_t *)pTag->idx)[iTag]; } else { offset = pTag->idx[iTag]; } (void)tGetTagVal(p + offset, &tv, pTag->flags & TD_TAG_JSON); if (taosArrayPush(*ppArray, &tv) == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _err; } } return code; _err: return code; } void tTagSetCid(const STag *pTag, int16_t iTag, int16_t cid) { uint8_t *p = NULL; int8_t isLarge = pTag->flags & TD_TAG_LARGE; int16_t offset = 0; if (isLarge) { p = (uint8_t *)&((int16_t *)pTag->idx)[pTag->nTag]; } else { p = (uint8_t *)&pTag->idx[pTag->nTag]; } if (isLarge) { offset = ((int16_t *)pTag->idx)[iTag]; } else { offset = pTag->idx[iTag]; } (void)tPutI16v(p + offset, cid); } // STSchema ======================================== STSchema *tBuildTSchema(SSchema *aSchema, int32_t numOfCols, int32_t version) { STSchema *pTSchema = taosMemoryCalloc(1, sizeof(STSchema) + sizeof(STColumn) * numOfCols); if (pTSchema == NULL) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } pTSchema->numOfCols = numOfCols; pTSchema->version = version; // timestamp column ASSERT(aSchema[0].type == TSDB_DATA_TYPE_TIMESTAMP); ASSERT(aSchema[0].colId == PRIMARYKEY_TIMESTAMP_COL_ID); pTSchema->columns[0].colId = aSchema[0].colId; pTSchema->columns[0].type = aSchema[0].type; pTSchema->columns[0].flags = aSchema[0].flags; pTSchema->columns[0].bytes = TYPE_BYTES[aSchema[0].type]; pTSchema->columns[0].offset = -1; // other columns for (int32_t iCol = 1; iCol < numOfCols; iCol++) { SSchema *pSchema = &aSchema[iCol]; STColumn *pTColumn = &pTSchema->columns[iCol]; pTColumn->colId = pSchema->colId; pTColumn->type = pSchema->type; pTColumn->flags = pSchema->flags; pTColumn->offset = pTSchema->flen; if (IS_VAR_DATA_TYPE(pSchema->type)) { pTColumn->bytes = pSchema->bytes; pTSchema->tlen += (TYPE_BYTES[pSchema->type] + pSchema->bytes); // todo: remove } else { pTColumn->bytes = TYPE_BYTES[pSchema->type]; pTSchema->tlen += TYPE_BYTES[pSchema->type]; // todo: remove } pTSchema->flen += TYPE_BYTES[pTColumn->type]; } #if 1 // todo : remove this pTSchema->tlen += (int32_t)TD_BITMAP_BYTES(numOfCols); #endif return pTSchema; } static int32_t tTColumnCompare(const void *p1, const void *p2) { if (((STColumn *)p1)->colId < ((STColumn *)p2)->colId) { return -1; } else if (((STColumn *)p1)->colId > ((STColumn *)p2)->colId) { return 1; } return 0; } const STColumn *tTSchemaSearchColumn(const STSchema *pTSchema, int16_t cid) { STColumn tcol = { .colId = cid, }; return taosbsearch(&tcol, pTSchema->columns, pTSchema->numOfCols, sizeof(STColumn), tTColumnCompare, TD_EQ); } // SColData ======================================== void tColDataDestroy(void *ph) { if (ph) { SColData *pColData = (SColData *)ph; tFree(pColData->pBitMap); tFree(pColData->aOffset); tFree(pColData->pData); } } void tColDataInit(SColData *pColData, int16_t cid, int8_t type, int8_t cflag) { pColData->cid = cid; pColData->type = type; pColData->cflag = cflag; tColDataClear(pColData); } void tColDataClear(SColData *pColData) { pColData->numOfNone = 0; pColData->numOfNull = 0; pColData->numOfValue = 0; pColData->nVal = 0; pColData->flag = 0; pColData->nData = 0; } void tColDataDeepClear(SColData *pColData) { pColData->pBitMap = NULL; pColData->aOffset = NULL; pColData->pData = NULL; tColDataClear(pColData); } static FORCE_INLINE int32_t tColDataPutValue(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; if (IS_VAR_DATA_TYPE(pColData->type)) { code = tRealloc((uint8_t **)(&pColData->aOffset), ((int64_t)(pColData->nVal + 1)) << 2); if (code) goto _exit; pColData->aOffset[pColData->nVal] = pColData->nData; if (nData) { code = tRealloc(&pColData->pData, pColData->nData + nData); if (code) goto _exit; (void)memcpy(pColData->pData + pColData->nData, pData, nData); pColData->nData += nData; } } else { ASSERT(pColData->nData == tDataTypes[pColData->type].bytes * pColData->nVal); code = tRealloc(&pColData->pData, pColData->nData + tDataTypes[pColData->type].bytes); if (code) goto _exit; if (pData) { (void)memcpy(pColData->pData + pColData->nData, pData, TYPE_BYTES[pColData->type]); } else { memset(pColData->pData + pColData->nData, 0, TYPE_BYTES[pColData->type]); } pColData->nData += tDataTypes[pColData->type].bytes; } pColData->nVal++; _exit: return code; } static FORCE_INLINE int32_t tColDataAppendValue00(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->flag = HAS_VALUE; pColData->numOfValue++; return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue01(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->flag = HAS_NONE; pColData->numOfNone++; pColData->nVal++; return 0; } static FORCE_INLINE int32_t tColDataAppendValue02(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->flag = HAS_NULL; pColData->numOfNull++; pColData->nVal++; return 0; } static FORCE_INLINE int32_t tColDataAppendValue10(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 0, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->flag |= HAS_VALUE; pColData->numOfValue++; if (pColData->nVal) { if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nOffset = sizeof(int32_t) * pColData->nVal; code = tRealloc((uint8_t **)(&pColData->aOffset), nOffset); if (code) return code; memset(pColData->aOffset, 0, nOffset); } else { pColData->nData = tDataTypes[pColData->type].bytes * pColData->nVal; code = tRealloc(&pColData->pData, pColData->nData); if (code) return code; memset(pColData->pData, 0, pColData->nData); } } return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue11(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->nVal++; pColData->numOfNone++; return 0; } static FORCE_INLINE int32_t tColDataAppendValue12(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 0, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->flag |= HAS_NULL; pColData->numOfNull++; pColData->nVal++; return code; } static FORCE_INLINE int32_t tColDataAppendValue20(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 0, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->flag |= HAS_VALUE; pColData->numOfValue++; if (pColData->nVal) { if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nOffset = sizeof(int32_t) * pColData->nVal; code = tRealloc((uint8_t **)(&pColData->aOffset), nOffset); if (code) return code; memset(pColData->aOffset, 0, nOffset); } else { pColData->nData = tDataTypes[pColData->type].bytes * pColData->nVal; code = tRealloc(&pColData->pData, pColData->nData); if (code) return code; memset(pColData->pData, 0, pColData->nData); } } return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue21(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 255, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); pColData->flag |= HAS_NONE; pColData->numOfNone++; pColData->nVal++; return code; } static FORCE_INLINE int32_t tColDataAppendValue22(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->nVal++; pColData->numOfNull++; return 0; } static FORCE_INLINE int32_t tColDataAppendValue30(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; pColData->flag |= HAS_VALUE; pColData->numOfValue++; uint8_t *pBitMap = NULL; code = tRealloc(&pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { SET_BIT2_EX(pBitMap, iVal, GET_BIT1(pColData->pBitMap, iVal)); } SET_BIT2_EX(pBitMap, pColData->nVal, 2); tFree(pColData->pBitMap); pColData->pBitMap = pBitMap; if (pColData->nVal) { if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nOffset = sizeof(int32_t) * pColData->nVal; code = tRealloc((uint8_t **)(&pColData->aOffset), nOffset); if (code) return code; memset(pColData->aOffset, 0, nOffset); } else { pColData->nData = tDataTypes[pColData->type].bytes * pColData->nVal; code = tRealloc(&pColData->pData, pColData->nData); if (code) return code; memset(pColData->pData, 0, pColData->nData); } } return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue31(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); pColData->numOfNone++; pColData->nVal++; return code; } static FORCE_INLINE int32_t tColDataAppendValue32(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->numOfNull++; pColData->nVal++; return code; } static FORCE_INLINE int32_t tColDataAppendValue40(SColData *pColData, uint8_t *pData, uint32_t nData) { pColData->numOfValue++; return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue41(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; pColData->flag |= HAS_NONE; pColData->numOfNone++; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 255, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue42(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; pColData->flag |= HAS_NULL; pColData->numOfNull++; int32_t nBit = BIT1_SIZE(pColData->nVal + 1); code = tRealloc(&pColData->pBitMap, nBit); if (code) return code; memset(pColData->pBitMap, 255, nBit); SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue50(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->numOfValue++; return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue51(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); pColData->numOfNone++; return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue52(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; pColData->flag |= HAS_NULL; pColData->numOfNull++; uint8_t *pBitMap = NULL; code = tRealloc(&pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { SET_BIT2_EX(pBitMap, iVal, GET_BIT1(pColData->pBitMap, iVal) ? 2 : 0); } SET_BIT2_EX(pBitMap, pColData->nVal, 1); tFree(pColData->pBitMap); pColData->pBitMap = pBitMap; return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue60(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 1); pColData->numOfValue++; return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue61(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; pColData->flag |= HAS_NONE; pColData->numOfNone++; uint8_t *pBitMap = NULL; code = tRealloc(&pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { SET_BIT2_EX(pBitMap, iVal, GET_BIT1(pColData->pBitMap, iVal) ? 2 : 1); } SET_BIT2_EX(pBitMap, pColData->nVal, 0); tFree(pColData->pBitMap); pColData->pBitMap = pBitMap; return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue62(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT1_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT1_EX(pColData->pBitMap, pColData->nVal, 0); pColData->numOfNull++; return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue70(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT2_EX(pColData->pBitMap, pColData->nVal, 2); pColData->numOfValue++; return tColDataPutValue(pColData, pData, nData); } static FORCE_INLINE int32_t tColDataAppendValue71(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT2_EX(pColData->pBitMap, pColData->nVal, 0); pColData->numOfNone++; return tColDataPutValue(pColData, NULL, 0); } static FORCE_INLINE int32_t tColDataAppendValue72(SColData *pColData, uint8_t *pData, uint32_t nData) { int32_t code = 0; code = tRealloc(&pColData->pBitMap, BIT2_SIZE(pColData->nVal + 1)); if (code) return code; SET_BIT2_EX(pColData->pBitMap, pColData->nVal, 1); pColData->numOfNull++; return tColDataPutValue(pColData, NULL, 0); } static int32_t (*tColDataAppendValueImpl[8][3])(SColData *pColData, uint8_t *pData, uint32_t nData) = { {tColDataAppendValue00, tColDataAppendValue01, tColDataAppendValue02}, // 0 {tColDataAppendValue10, tColDataAppendValue11, tColDataAppendValue12}, // HAS_NONE {tColDataAppendValue20, tColDataAppendValue21, tColDataAppendValue22}, // HAS_NULL {tColDataAppendValue30, tColDataAppendValue31, tColDataAppendValue32}, // HAS_NULL|HAS_NONE {tColDataAppendValue40, tColDataAppendValue41, tColDataAppendValue42}, // HAS_VALUE {tColDataAppendValue50, tColDataAppendValue51, tColDataAppendValue52}, // HAS_VALUE|HAS_NONE {tColDataAppendValue60, tColDataAppendValue61, tColDataAppendValue62}, // HAS_VALUE|HAS_NULL {tColDataAppendValue70, tColDataAppendValue71, tColDataAppendValue72}, // HAS_VALUE|HAS_NULL|HAS_NONE // VALUE NONE NULL }; int32_t tColDataAppendValue(SColData *pColData, SColVal *pColVal) { ASSERT(pColData->cid == pColVal->cid && pColData->type == pColVal->value.type); return tColDataAppendValueImpl[pColData->flag][pColVal->flag]( pColData, IS_VAR_DATA_TYPE(pColData->type) ? pColVal->value.pData : (uint8_t *)&pColVal->value.val, pColVal->value.nData); } static FORCE_INLINE int32_t tColDataUpdateValue10(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { pColData->numOfNone--; pColData->nVal--; if (pColData->numOfNone) { return tColDataAppendValue10(pColData, pData, nData); } else { pColData->flag = 0; return tColDataAppendValue00(pColData, pData, nData); } } static FORCE_INLINE int32_t tColDataUpdateValue12(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { pColData->numOfNone--; pColData->nVal--; if (pColData->numOfNone) { return tColDataAppendValue12(pColData, pData, nData); } else { pColData->flag = 0; return tColDataAppendValue02(pColData, pData, nData); } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue20(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (forward) { pColData->numOfNull--; pColData->nVal--; if (pColData->numOfNull) { return tColDataAppendValue20(pColData, pData, nData); } else { pColData->flag = 0; return tColDataAppendValue00(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue30(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 0) { // NONE ==> VALUE pColData->numOfNone--; pColData->nVal--; if (pColData->numOfNone) { return tColDataAppendValue30(pColData, pData, nData); } else { pColData->flag = HAS_NULL; return tColDataAppendValue20(pColData, pData, nData); } } else if (forward) { // NULL ==> VALUE pColData->numOfNull--; pColData->nVal--; if (pColData->numOfNull) { return tColDataAppendValue30(pColData, pData, nData); } else { pColData->flag = HAS_NONE; return tColDataAppendValue10(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue32(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 0) { // NONE ==> NULL pColData->numOfNone--; pColData->numOfNull++; if (pColData->numOfNone) { SET_BIT1(pColData->pBitMap, pColData->nVal - 1, 1); } else { pColData->flag = HAS_NULL; } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue40(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (forward) { // VALUE ==> VALUE pColData->nVal--; if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataPutValue(pColData, pData, nData); } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue42(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (forward) { // VALUE ==> NULL pColData->numOfValue--; pColData->nVal--; if (pColData->numOfValue) { if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataAppendValue42(pColData, pData, nData); } else { pColData->flag = 0; pColData->nData = 0; return tColDataAppendValue02(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue50(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 0) { // NONE ==> VALUE pColData->numOfNone--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNone) { return tColDataAppendValue50(pColData, pData, nData); } else { pColData->flag = HAS_VALUE; return tColDataAppendValue40(pColData, pData, nData); } } else if (forward) { // VALUE ==> VALUE pColData->nVal--; if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataPutValue(pColData, pData, nData); } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue52(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 0) { // NONE ==> NULL pColData->numOfNone--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNone) { return tColDataAppendValue52(pColData, pData, nData); } else { pColData->flag = HAS_VALUE; return tColDataAppendValue42(pColData, pData, nData); } } else if (forward) { // VALUE ==> NULL pColData->numOfValue--; pColData->nVal--; if (pColData->numOfValue) { if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataAppendValue52(pColData, pData, nData); } else { pColData->flag = HAS_NONE; pColData->nData = 0; return tColDataAppendValue12(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue60(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (forward) { if (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 0) { // NULL ==> VALUE pColData->numOfNull--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNull) { return tColDataAppendValue60(pColData, pData, nData); } else { pColData->flag = HAS_VALUE; return tColDataAppendValue40(pColData, pData, nData); } } else { // VALUE ==> VALUE pColData->nVal--; if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataPutValue(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue62(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { if (forward && (GET_BIT1(pColData->pBitMap, pColData->nVal - 1) == 1)) { // VALUE ==> NULL pColData->numOfValue--; pColData->nVal--; if (pColData->numOfValue) { if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataAppendValue62(pColData, pData, nData); } else { pColData->flag = HAS_NULL; pColData->nData = 0; return tColDataAppendValue20(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateValue70(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { int32_t code = 0; uint8_t bv = GET_BIT2(pColData->pBitMap, pColData->nVal - 1); if (bv == 0) { // NONE ==> VALUE pColData->numOfNone--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNone) { return tColDataAppendValue70(pColData, pData, nData); } else { for (int32_t iVal = 0; iVal < pColData->nVal; ++iVal) { SET_BIT1(pColData->pBitMap, iVal, GET_BIT2(pColData->pBitMap, iVal) - 1); } pColData->flag = (HAS_VALUE | HAS_NULL); return tColDataAppendValue60(pColData, pData, nData); } } else if (bv == 1) { // NULL ==> VALUE if (forward) { pColData->numOfNull--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNull) { return tColDataAppendValue70(pColData, pData, nData); } else { for (int32_t iVal = 0; iVal < pColData->nVal; ++iVal) { SET_BIT1(pColData->pBitMap, iVal, GET_BIT2(pColData->pBitMap, iVal) ? 1 : 0); } pColData->flag = (HAS_VALUE | HAS_NONE); return tColDataAppendValue50(pColData, pData, nData); } } } else if (bv == 2) { // VALUE ==> VALUE if (forward) { pColData->nVal--; if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataPutValue(pColData, pData, nData); } } else { ASSERT(0); } return 0; } static int32_t tColDataUpdateValue72(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { uint8_t bv = GET_BIT2(pColData->pBitMap, pColData->nVal - 1); if (bv == 0) { // NONE ==> NULL pColData->numOfNone--; pColData->nVal--; if (!IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData -= TYPE_BYTES[pColData->type]; } if (pColData->numOfNone) { return tColDataAppendValue72(pColData, pData, nData); } else { for (int32_t iVal = 0; iVal < pColData->nVal; ++iVal) { SET_BIT1(pColData->pBitMap, iVal, GET_BIT2(pColData->pBitMap, iVal) - 1); } pColData->flag = (HAS_VALUE | HAS_NULL); return tColDataAppendValue62(pColData, pData, nData); } } else if (bv == 2 && forward) { // VALUE ==> NULL pColData->numOfValue--; pColData->nVal--; if (pColData->numOfValue) { if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->nData = pColData->aOffset[pColData->nVal]; } else { pColData->nData -= TYPE_BYTES[pColData->type]; } return tColDataAppendValue72(pColData, pData, nData); } else { for (int32_t iVal = 0; iVal < pColData->nVal; ++iVal) { SET_BIT1(pColData->pBitMap, iVal, GET_BIT2(pColData->pBitMap, iVal)); } pColData->flag = (HAS_NULL | HAS_NONE); pColData->nData = 0; return tColDataAppendValue32(pColData, pData, nData); } } return 0; } static FORCE_INLINE int32_t tColDataUpdateNothing(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) { return 0; } static int32_t (*tColDataUpdateValueImpl[8][3])(SColData *pColData, uint8_t *pData, uint32_t nData, bool forward) = { {NULL, NULL, NULL}, // 0 {tColDataUpdateValue10, tColDataUpdateNothing, tColDataUpdateValue12}, // HAS_NONE {tColDataUpdateValue20, tColDataUpdateNothing, tColDataUpdateNothing}, // HAS_NULL {tColDataUpdateValue30, tColDataUpdateNothing, tColDataUpdateValue32}, // HAS_NULL|HAS_NONE {tColDataUpdateValue40, tColDataUpdateNothing, tColDataUpdateValue42}, // HAS_VALUE {tColDataUpdateValue50, tColDataUpdateNothing, tColDataUpdateValue52}, // HAS_VALUE|HAS_NONE {tColDataUpdateValue60, tColDataUpdateNothing, tColDataUpdateValue62}, // HAS_VALUE|HAS_NULL {tColDataUpdateValue70, tColDataUpdateNothing, tColDataUpdateValue72}, // HAS_VALUE|HAS_NULL|HAS_NONE // VALUE NONE NULL }; int32_t tColDataUpdateValue(SColData *pColData, SColVal *pColVal, bool forward) { ASSERT(pColData->cid == pColVal->cid && pColData->type == pColVal->value.type); ASSERT(pColData->nVal > 0); if (tColDataUpdateValueImpl[pColData->flag][pColVal->flag] == NULL) return 0; return tColDataUpdateValueImpl[pColData->flag][pColVal->flag]( pColData, IS_VAR_DATA_TYPE(pColData->type) ? pColVal->value.pData : (uint8_t *)&pColVal->value.val, pColVal->value.nData, forward); } static FORCE_INLINE void tColDataGetValue1(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_NONE *pColVal = COL_VAL_NONE(pColData->cid, pColData->type); } static FORCE_INLINE void tColDataGetValue2(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_NULL *pColVal = COL_VAL_NULL(pColData->cid, pColData->type); } static FORCE_INLINE void tColDataGetValue3(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_NULL|HAS_NONE switch (GET_BIT1(pColData->pBitMap, iVal)) { case 0: *pColVal = COL_VAL_NONE(pColData->cid, pColData->type); break; case 1: *pColVal = COL_VAL_NULL(pColData->cid, pColData->type); break; default: ASSERT(0); } } static FORCE_INLINE void tColDataGetValue4(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_VALUE SValue value = {.type = pColData->type}; if (IS_VAR_DATA_TYPE(pColData->type)) { if (iVal + 1 < pColData->nVal) { value.nData = pColData->aOffset[iVal + 1] - pColData->aOffset[iVal]; } else { value.nData = pColData->nData - pColData->aOffset[iVal]; } value.pData = pColData->pData + pColData->aOffset[iVal]; } else { (void)memcpy(&value.val, pColData->pData + tDataTypes[pColData->type].bytes * iVal, tDataTypes[pColData->type].bytes); } *pColVal = COL_VAL_VALUE(pColData->cid, value); } static FORCE_INLINE void tColDataGetValue5(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_VALUE|HAS_NONE switch (GET_BIT1(pColData->pBitMap, iVal)) { case 0: *pColVal = COL_VAL_NONE(pColData->cid, pColData->type); break; case 1: tColDataGetValue4(pColData, iVal, pColVal); break; default: ASSERT(0); } } static FORCE_INLINE void tColDataGetValue6(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_VALUE|HAS_NULL switch (GET_BIT1(pColData->pBitMap, iVal)) { case 0: *pColVal = COL_VAL_NULL(pColData->cid, pColData->type); break; case 1: tColDataGetValue4(pColData, iVal, pColVal); break; default: ASSERT(0); } } static FORCE_INLINE void tColDataGetValue7(SColData *pColData, int32_t iVal, SColVal *pColVal) { // HAS_VALUE|HAS_NULL|HAS_NONE switch (GET_BIT2(pColData->pBitMap, iVal)) { case 0: *pColVal = COL_VAL_NONE(pColData->cid, pColData->type); break; case 1: *pColVal = COL_VAL_NULL(pColData->cid, pColData->type); break; case 2: tColDataGetValue4(pColData, iVal, pColVal); break; default: ASSERT(0); } } static void (*tColDataGetValueImpl[])(SColData *pColData, int32_t iVal, SColVal *pColVal) = { NULL, // 0 tColDataGetValue1, // HAS_NONE tColDataGetValue2, // HAS_NULL tColDataGetValue3, // HAS_NULL | HAS_NONE tColDataGetValue4, // HAS_VALUE tColDataGetValue5, // HAS_VALUE | HAS_NONE tColDataGetValue6, // HAS_VALUE | HAS_NULL tColDataGetValue7 // HAS_VALUE | HAS_NULL | HAS_NONE }; void tColDataGetValue(SColData *pColData, int32_t iVal, SColVal *pColVal) { ASSERT(iVal >= 0 && iVal < pColData->nVal && pColData->flag); tColDataGetValueImpl[pColData->flag](pColData, iVal, pColVal); } uint8_t tColDataGetBitValue(const SColData *pColData, int32_t iVal) { switch (pColData->flag) { case HAS_NONE: return 0; case HAS_NULL: return 1; case (HAS_NULL | HAS_NONE): return GET_BIT1(pColData->pBitMap, iVal); case HAS_VALUE: return 2; case (HAS_VALUE | HAS_NONE): return (GET_BIT1(pColData->pBitMap, iVal)) ? 2 : 0; case (HAS_VALUE | HAS_NULL): return GET_BIT1(pColData->pBitMap, iVal) + 1; case (HAS_VALUE | HAS_NULL | HAS_NONE): return GET_BIT2(pColData->pBitMap, iVal); default: ASSERTS(0, "not possible"); return 0; } } int32_t tColDataCopy(SColData *pColDataFrom, SColData *pColData, xMallocFn xMalloc, void *arg) { int32_t code = 0; *pColData = *pColDataFrom; // bitmap switch (pColData->flag) { case (HAS_NULL | HAS_NONE): case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): pColData->pBitMap = xMalloc(arg, BIT1_SIZE(pColData->nVal)); if (pColData->pBitMap == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } (void)memcpy(pColData->pBitMap, pColDataFrom->pBitMap, BIT1_SIZE(pColData->nVal)); break; case (HAS_VALUE | HAS_NULL | HAS_NONE): pColData->pBitMap = xMalloc(arg, BIT2_SIZE(pColData->nVal)); if (pColData->pBitMap == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } (void)memcpy(pColData->pBitMap, pColDataFrom->pBitMap, BIT2_SIZE(pColData->nVal)); break; default: pColData->pBitMap = NULL; break; } // offset if (IS_VAR_DATA_TYPE(pColData->type) && (pColData->flag & HAS_VALUE)) { pColData->aOffset = xMalloc(arg, pColData->nVal << 2); if (pColData->aOffset == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } (void)memcpy(pColData->aOffset, pColDataFrom->aOffset, pColData->nVal << 2); } else { pColData->aOffset = NULL; } // value if (pColData->nData) { pColData->pData = xMalloc(arg, pColData->nData); if (pColData->pData == NULL) { code = TSDB_CODE_OUT_OF_MEMORY; goto _exit; } (void)memcpy(pColData->pData, pColDataFrom->pData, pColData->nData); } else { pColData->pData = NULL; } _exit: return code; } int32_t tColDataCompress(SColData *colData, SColDataCompressInfo *info, SBuffer *output, SBuffer *assist) { int32_t code; SBuffer local; ASSERT(colData->nVal > 0); (*info) = (SColDataCompressInfo){ .cmprAlg = info->cmprAlg, .columnFlag = colData->cflag, .flag = colData->flag, .dataType = colData->type, .columnId = colData->cid, .numOfData = colData->nVal, }; if (colData->flag == HAS_NONE || colData->flag == HAS_NULL) { return 0; } tBufferInit(&local); if (assist == NULL) { assist = &local; } // bitmap if (colData->flag != HAS_VALUE) { if (colData->flag == (HAS_NONE | HAS_NULL | HAS_VALUE)) { info->bitmapOriginalSize = BIT2_SIZE(colData->nVal); } else { info->bitmapOriginalSize = BIT1_SIZE(colData->nVal); } SCompressInfo cinfo = { .dataType = TSDB_DATA_TYPE_TINYINT, .cmprAlg = info->cmprAlg, .originalSize = info->bitmapOriginalSize, }; code = tCompressDataToBuffer(colData->pBitMap, &cinfo, output, assist); if (code) { tBufferDestroy(&local); return code; } info->bitmapCompressedSize = cinfo.compressedSize; } if (colData->flag == (HAS_NONE | HAS_NULL)) { tBufferDestroy(&local); return 0; } // offset if (IS_VAR_DATA_TYPE(colData->type)) { info->offsetOriginalSize = sizeof(int32_t) * info->numOfData; SCompressInfo cinfo = { .dataType = TSDB_DATA_TYPE_INT, .cmprAlg = info->cmprAlg, .originalSize = info->offsetOriginalSize, }; code = tCompressDataToBuffer(colData->aOffset, &cinfo, output, assist); if (code) { tBufferDestroy(&local); return code; } info->offsetCompressedSize = cinfo.compressedSize; } // data if (colData->nData > 0) { info->dataOriginalSize = colData->nData; SCompressInfo cinfo = { .dataType = colData->type, .cmprAlg = info->cmprAlg, .originalSize = info->dataOriginalSize, }; code = tCompressDataToBuffer(colData->pData, &cinfo, output, assist); if (code) { tBufferDestroy(&local); return code; } info->dataCompressedSize = cinfo.compressedSize; } tBufferDestroy(&local); return 0; } int32_t tColDataDecompress(void *input, SColDataCompressInfo *info, SColData *colData, SBuffer *assist) { int32_t code; SBuffer local; uint8_t *data = (uint8_t *)input; tBufferInit(&local); if (assist == NULL) { assist = &local; } tColDataClear(colData); colData->cid = info->columnId; colData->type = info->dataType; colData->cflag = info->columnFlag; colData->nVal = info->numOfData; colData->flag = info->flag; if (info->flag == HAS_NONE || info->flag == HAS_NULL) { goto _exit; } // bitmap if (info->bitmapOriginalSize > 0) { SCompressInfo cinfo = { .dataType = TSDB_DATA_TYPE_TINYINT, .cmprAlg = info->cmprAlg, .originalSize = info->bitmapOriginalSize, .compressedSize = info->bitmapCompressedSize, }; code = tRealloc(&colData->pBitMap, cinfo.originalSize); if (code) { tBufferDestroy(&local); return code; } code = tDecompressData(data, &cinfo, colData->pBitMap, cinfo.originalSize, assist); if (code) { tBufferDestroy(&local); return code; } data += cinfo.compressedSize; } if (info->flag == (HAS_NONE | HAS_NULL)) { goto _exit; } // offset if (info->offsetOriginalSize > 0) { SCompressInfo cinfo = { .cmprAlg = info->cmprAlg, .dataType = TSDB_DATA_TYPE_INT, .originalSize = info->offsetOriginalSize, .compressedSize = info->offsetCompressedSize, }; code = tRealloc((uint8_t **)&colData->aOffset, cinfo.originalSize); if (code) { tBufferDestroy(&local); return code; } code = tDecompressData(data, &cinfo, colData->aOffset, cinfo.originalSize, assist); if (code) { tBufferDestroy(&local); return code; } data += cinfo.compressedSize; } // data if (info->dataOriginalSize > 0) { colData->nData = info->dataOriginalSize; SCompressInfo cinfo = { .cmprAlg = info->cmprAlg, .dataType = colData->type, .originalSize = info->dataOriginalSize, .compressedSize = info->dataCompressedSize, }; code = tRealloc((uint8_t **)&colData->pData, cinfo.originalSize); if (code) { tBufferDestroy(&local); return code; } code = tDecompressData(data, &cinfo, colData->pData, cinfo.originalSize, assist); if (code) { tBufferDestroy(&local); return code; } data += cinfo.compressedSize; } _exit: switch (colData->flag) { case HAS_NONE: colData->numOfNone = colData->nVal; break; case HAS_NULL: colData->numOfNull = colData->nVal; break; case HAS_VALUE: colData->numOfValue = colData->nVal; break; default: for (int32_t i = 0; i < colData->nVal; i++) { uint8_t bitValue = tColDataGetBitValue(colData, i); if (bitValue == 0) { colData->numOfNone++; } else if (bitValue == 1) { colData->numOfNull++; } else { colData->numOfValue++; } } } tBufferDestroy(&local); return 0; } int32_t tColDataAddValueByDataBlock(SColData *pColData, int8_t type, int32_t bytes, int32_t nRows, char *lengthOrbitmap, char *data) { int32_t code = 0; if (data == NULL) { if (pColData->cflag & COL_IS_KEY) { code = TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; } else { for (int32_t i = 0; i < nRows; ++i) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NONE](pColData, NULL, 0); } } goto _exit; } if (IS_VAR_DATA_TYPE(type)) { // var-length data type for (int32_t i = 0; i < nRows; ++i) { int32_t offset = *((int32_t *)lengthOrbitmap + i); if (offset == -1) { if (pColData->cflag & COL_IS_KEY) { code = TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; goto _exit; } if ((code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0))) { goto _exit; } } else { if (varDataTLen(data + offset) > bytes) { uError("var data length invalid, varDataTLen(data + offset):%d > bytes:%d", (int)varDataTLen(data + offset), bytes); code = TSDB_CODE_PAR_VALUE_TOO_LONG; goto _exit; } code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, (uint8_t *)varDataVal(data + offset), varDataLen(data + offset)); } } } else { // fixed-length data type bool allValue = true; bool allNull = true; for (int32_t i = 0; i < nRows; ++i) { if (!colDataIsNull_f(lengthOrbitmap, i)) { allNull = false; } else { allValue = false; } } if ((pColData->cflag & COL_IS_KEY) && !allValue) { code = TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; goto _exit; } if (allValue) { // optimize (todo) for (int32_t i = 0; i < nRows; ++i) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, (uint8_t *)data + bytes * i, bytes); } } else if (allNull) { // optimize (todo) for (int32_t i = 0; i < nRows; ++i) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } } else { for (int32_t i = 0; i < nRows; ++i) { if (colDataIsNull_f(lengthOrbitmap, i)) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } else { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE](pColData, (uint8_t *)data + bytes * i, bytes); } } } } _exit: return code; } int32_t tColDataAddValueByBind(SColData *pColData, TAOS_MULTI_BIND *pBind, int32_t buffMaxLen) { int32_t code = 0; if (!(pBind->num == 1 && pBind->is_null && *pBind->is_null)) { ASSERT(pColData->type == pBind->buffer_type); } if (IS_VAR_DATA_TYPE(pColData->type)) { // var-length data type for (int32_t i = 0; i < pBind->num; ++i) { if (pBind->is_null && pBind->is_null[i]) { if (pColData->cflag & COL_IS_KEY) { code = TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; goto _exit; } code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } else if (pBind->length[i] > buffMaxLen) { uError("var data length too big, len:%d, max:%d", pBind->length[i], buffMaxLen); return TSDB_CODE_INVALID_PARA; } else { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE]( pColData, (uint8_t *)pBind->buffer + pBind->buffer_length * i, pBind->length[i]); } } } else { // fixed-length data type bool allValue; bool allNull; if (pBind->is_null) { bool same = (memcmp(pBind->is_null, pBind->is_null + 1, pBind->num - 1) == 0); allNull = (same && pBind->is_null[0] != 0); allValue = (same && pBind->is_null[0] == 0); } else { allNull = false; allValue = true; } if ((pColData->cflag & COL_IS_KEY) && !allValue) { code = TSDB_CODE_PAR_PRIMARY_KEY_IS_NULL; goto _exit; } if (allValue) { // optimize (todo) for (int32_t i = 0; i < pBind->num; ++i) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE]( pColData, (uint8_t *)pBind->buffer + TYPE_BYTES[pColData->type] * i, pBind->buffer_length); } } else if (allNull) { // optimize (todo) for (int32_t i = 0; i < pBind->num; ++i) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } } else { for (int32_t i = 0; i < pBind->num; ++i) { if (pBind->is_null[i]) { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_NULL](pColData, NULL, 0); if (code) goto _exit; } else { code = tColDataAppendValueImpl[pColData->flag][CV_FLAG_VALUE]( pColData, (uint8_t *)pBind->buffer + TYPE_BYTES[pColData->type] * i, pBind->buffer_length); } } } } _exit: return code; } static int32_t tColDataCopyRowCell(SColData *pFromColData, int32_t iFromRow, SColData *pToColData, int32_t iToRow) { int32_t code = TSDB_CODE_SUCCESS; if (IS_VAR_DATA_TYPE(pToColData->type)) { int32_t nData = (iFromRow < pFromColData->nVal - 1) ? pFromColData->aOffset[iFromRow + 1] - pFromColData->aOffset[iFromRow] : pFromColData->nData - pFromColData->aOffset[iFromRow]; if (iToRow == 0) { pToColData->aOffset[iToRow] = 0; } if (iToRow < pToColData->nVal - 1) { pToColData->aOffset[iToRow + 1] = pToColData->aOffset[iToRow] + nData; } (void)memcpy(pToColData->pData + pToColData->aOffset[iToRow], pFromColData->pData + pFromColData->aOffset[iFromRow], nData); } else { (void)memcpy(&pToColData->pData[TYPE_BYTES[pToColData->type] * iToRow], &pFromColData->pData[TYPE_BYTES[pToColData->type] * iFromRow], TYPE_BYTES[pToColData->type]); } return code; } static int32_t tColDataCopyRowSingleCol(SColData *pFromColData, int32_t iFromRow, SColData *pToColData, int32_t iToRow) { int32_t code = TSDB_CODE_SUCCESS; switch (pFromColData->flag) { case HAS_NONE: case HAS_NULL: break; case (HAS_NULL | HAS_NONE): { SET_BIT1(pToColData->pBitMap, iToRow, GET_BIT1(pFromColData->pBitMap, iFromRow)); } break; case HAS_VALUE: { (void)tColDataCopyRowCell(pFromColData, iFromRow, pToColData, iToRow); } break; case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): { SET_BIT1(pToColData->pBitMap, iToRow, GET_BIT1(pFromColData->pBitMap, iFromRow)); (void)tColDataCopyRowCell(pFromColData, iFromRow, pToColData, iToRow); } break; case (HAS_VALUE | HAS_NULL | HAS_NONE): { SET_BIT2(pToColData->pBitMap, iToRow, GET_BIT2(pFromColData->pBitMap, iFromRow)); (void)tColDataCopyRowCell(pFromColData, iFromRow, pToColData, iToRow); } break; default: return -1; } return code; } static int32_t tColDataCopyRow(SColData *aFromColData, int32_t iFromRow, SColData *aToColData, int32_t iToRow, int32_t nColData) { int32_t code = TSDB_CODE_SUCCESS; for (int32_t i = 0; i < nColData; i++) { code = tColDataCopyRowSingleCol(&aFromColData[i], iFromRow, &aToColData[i], iToRow); if (code != TSDB_CODE_SUCCESS) { return code; } } return code; } static int32_t tColDataCopyRowAppend(SColData *aFromColData, int32_t iFromRow, SColData *aToColData, int32_t nColData) { int32_t code = TSDB_CODE_SUCCESS; for (int32_t i = 0; i < nColData; i++) { SColVal cv = {0}; tColDataGetValue(&aFromColData[i], iFromRow, &cv); code = tColDataAppendValue(&aToColData[i], &cv); if (code != TSDB_CODE_SUCCESS) { return code; } } return code; } void tColDataArrGetRowKey(SColData *aColData, int32_t nColData, int32_t iRow, SRowKey *key) { SColVal cv; key->ts = ((TSKEY *)aColData[0].pData)[iRow]; key->numOfPKs = 0; for (int i = 1; i < nColData; i++) { if (aColData[i].cflag & COL_IS_KEY) { ASSERT(aColData->flag == HAS_VALUE); tColDataGetValue4(&aColData[i], iRow, &cv); key->pks[key->numOfPKs++] = cv.value; } else { break; } } } static int32_t tColDataMergeSortMerge(SColData *aColData, int32_t start, int32_t mid, int32_t end, int32_t nColData) { SColData *aDstColData = NULL; int32_t i = start, j = mid + 1, k = 0; SRowKey keyi, keyj; if (end > start) { aDstColData = taosMemoryCalloc(1, sizeof(SColData) * nColData); if (aDstColData == NULL) { return TSDB_CODE_OUT_OF_MEMORY; } for (int c = 0; c < nColData; ++c) { tColDataInit(&aDstColData[c], aColData[c].cid, aColData[c].type, aColData[c].cflag); } } tColDataArrGetRowKey(aColData, nColData, i, &keyi); tColDataArrGetRowKey(aColData, nColData, j, &keyj); while (i <= mid && j <= end) { if (tRowKeyCompare(&keyi, &keyj) <= 0) { (void)tColDataCopyRowAppend(aColData, i++, aDstColData, nColData); tColDataArrGetRowKey(aColData, nColData, i, &keyi); } else { (void)tColDataCopyRowAppend(aColData, j++, aDstColData, nColData); tColDataArrGetRowKey(aColData, nColData, j, &keyj); } } while (i <= mid) { (void)tColDataCopyRowAppend(aColData, i++, aDstColData, nColData); } while (j <= end) { (void)tColDataCopyRowAppend(aColData, j++, aDstColData, nColData); } for (i = start, k = 0; i <= end; ++i, ++k) { (void)tColDataCopyRow(aDstColData, k, aColData, i, nColData); } if (aDstColData) { for (int32_t i = 0; i < nColData; i++) { tColDataDestroy(&aDstColData[i]); } taosMemoryFree(aDstColData); } return TSDB_CODE_SUCCESS; } static int32_t tColDataMergeSort(SColData *aColData, int32_t start, int32_t end, int32_t nColData) { int32_t ret = TSDB_CODE_SUCCESS; int32_t mid; if (start >= end) { return TSDB_CODE_SUCCESS; } mid = (start + end) / 2; ret = tColDataMergeSort(aColData, start, mid, nColData); if (ret != TSDB_CODE_SUCCESS) { return ret; } ret = tColDataMergeSort(aColData, mid + 1, end, nColData); if (ret != TSDB_CODE_SUCCESS) { return ret; } return tColDataMergeSortMerge(aColData, start, mid, end, nColData); } static int32_t tColDataSort(SColData *aColData, int32_t nColData) { int32_t nVal = aColData[0].nVal; if (nVal < 2) return TSDB_CODE_SUCCESS; return tColDataMergeSort(aColData, 0, nVal - 1, nColData); } static void tColDataMergeImpl(SColData *pColData, int32_t iStart, int32_t iEnd /* not included */) { switch (pColData->flag) { case HAS_NONE: case HAS_NULL: { pColData->nVal -= (iEnd - iStart - 1); } break; case (HAS_NULL | HAS_NONE): { if (GET_BIT1(pColData->pBitMap, iStart) == 0) { for (int32_t i = iStart + 1; i < iEnd; ++i) { if (GET_BIT1(pColData->pBitMap, i) == 1) { SET_BIT1(pColData->pBitMap, iStart, 1); break; } } } for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { SET_BIT1(pColData->pBitMap, j, GET_BIT1(pColData->pBitMap, i)); } pColData->nVal -= (iEnd - iStart - 1); uint8_t flag = 0; for (int32_t i = 0; i < pColData->nVal; ++i) { uint8_t bv = GET_BIT1(pColData->pBitMap, i); if (bv == BIT_FLG_NONE) { flag |= HAS_NONE; } else if (bv == BIT_FLG_NULL) { flag |= HAS_NULL; } else { ASSERT(0); } if (flag == pColData->flag) break; } pColData->flag = flag; } break; case HAS_VALUE: { if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nDiff = pColData->aOffset[iEnd - 1] - pColData->aOffset[iStart]; memmove(pColData->pData + pColData->aOffset[iStart], pColData->pData + pColData->aOffset[iEnd - 1], pColData->nData - pColData->aOffset[iEnd - 1]); pColData->nData -= nDiff; for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { pColData->aOffset[j] = pColData->aOffset[i] - nDiff; } } else { memmove(pColData->pData + TYPE_BYTES[pColData->type] * iStart, pColData->pData + TYPE_BYTES[pColData->type] * (iEnd - 1), TYPE_BYTES[pColData->type] * (pColData->nVal - iEnd + 1)); pColData->nData -= (TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); } pColData->nVal -= (iEnd - iStart - 1); } break; case (HAS_VALUE | HAS_NONE): { uint8_t bv; int32_t iv; for (int32_t i = iEnd - 1; i >= iStart; --i) { bv = GET_BIT1(pColData->pBitMap, i); if (bv) { iv = i; break; } } if (bv) { // has a value if (IS_VAR_DATA_TYPE(pColData->type)) { if (iv != iStart) { memmove(&pColData->pData[pColData->aOffset[iStart]], &pColData->pData[pColData->aOffset[iv]], iv < (pColData->nVal - 1) ? pColData->aOffset[iv + 1] - pColData->aOffset[iv] : pColData->nData - pColData->aOffset[iv]); } // TODO ASSERT(0); } else { if (iv != iStart) { (void)memcpy(&pColData->pData[TYPE_BYTES[pColData->type] * iStart], &pColData->pData[TYPE_BYTES[pColData->type] * iv], TYPE_BYTES[pColData->type]); } memmove(&pColData->pData[TYPE_BYTES[pColData->type] * (iStart + 1)], &pColData->pData[TYPE_BYTES[pColData->type] * iEnd], TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); pColData->nData -= (TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); } SET_BIT1(pColData->pBitMap, iStart, 1); for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { SET_BIT1(pColData->pBitMap, j, GET_BIT1(pColData->pBitMap, i)); } uint8_t flag = HAS_VALUE; for (int32_t i = 0; i < pColData->nVal - (iEnd - iStart - 1); ++i) { if (GET_BIT1(pColData->pBitMap, i) == 0) { flag |= HAS_NONE; } if (flag == pColData->flag) break; } pColData->flag = flag; } else { // all NONE if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nDiff = pColData->aOffset[iEnd - 1] - pColData->aOffset[iStart]; memmove(&pColData->pData[pColData->aOffset[iStart]], &pColData->pData[pColData->aOffset[iEnd - 1]], pColData->nData - pColData->aOffset[iEnd - 1]); pColData->nData -= nDiff; for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { pColData->aOffset[j] = pColData->aOffset[i] - nDiff; } } else { memmove(pColData->pData + TYPE_BYTES[pColData->type] * (iStart + 1), pColData->pData + TYPE_BYTES[pColData->type] * iEnd, TYPE_BYTES[pColData->type] * (pColData->nVal - iEnd + 1)); pColData->nData -= (TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); } for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { SET_BIT1(pColData->pBitMap, j, GET_BIT1(pColData->pBitMap, i)); } } pColData->nVal -= (iEnd - iStart - 1); } break; case (HAS_VALUE | HAS_NULL): { if (IS_VAR_DATA_TYPE(pColData->type)) { int32_t nDiff = pColData->aOffset[iEnd - 1] - pColData->aOffset[iStart]; memmove(pColData->pData + pColData->aOffset[iStart], pColData->pData + pColData->aOffset[iEnd - 1], pColData->nData - pColData->aOffset[iEnd - 1]); pColData->nData -= nDiff; for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { pColData->aOffset[j] = pColData->aOffset[i] - nDiff; } } else { memmove(pColData->pData + TYPE_BYTES[pColData->type] * iStart, pColData->pData + TYPE_BYTES[pColData->type] * (iEnd - 1), TYPE_BYTES[pColData->type] * (pColData->nVal - iEnd + 1)); pColData->nData -= (TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); } for (int32_t i = iEnd - 1, j = iStart; i < pColData->nVal; ++i, ++j) { SET_BIT1(pColData->pBitMap, j, GET_BIT1(pColData->pBitMap, i)); } pColData->nVal -= (iEnd - iStart - 1); uint8_t flag = 0; for (int32_t i = 0; i < pColData->nVal; ++i) { if (GET_BIT1(pColData->pBitMap, i)) { flag |= HAS_VALUE; } else { flag |= HAS_NULL; } if (flag == pColData->flag) break; } pColData->flag = flag; } break; case (HAS_VALUE | HAS_NULL | HAS_NONE): { uint8_t bv; int32_t iv; for (int32_t i = iEnd - 1; i >= iStart; --i) { bv = GET_BIT2(pColData->pBitMap, i); if (bv) { iv = i; break; } } if (bv) { // TODO ASSERT(0); } else { // ALL NONE if (IS_VAR_DATA_TYPE(pColData->type)) { // TODO ASSERT(0); } else { memmove(pColData->pData + TYPE_BYTES[pColData->type] * (iStart + 1), pColData->pData + TYPE_BYTES[pColData->type] * iEnd, TYPE_BYTES[pColData->type] * (pColData->nVal - iEnd)); pColData->nData -= (TYPE_BYTES[pColData->type] * (iEnd - iStart - 1)); } for (int32_t i = iEnd, j = iStart + 1; i < pColData->nVal; ++i, ++j) { SET_BIT2(pColData->pBitMap, j, GET_BIT2(pColData->pBitMap, i)); } } pColData->nVal -= (iEnd - iStart - 1); } break; default: ASSERT(0); break; } } static void tColDataMerge(SColData *aColData, int32_t nColData) { int32_t iStart = 0; SRowKey keyStart, keyEnd; for (;;) { if (iStart >= aColData[0].nVal - 1) break; tColDataArrGetRowKey(aColData, nColData, iStart, &keyStart); int32_t iEnd = iStart + 1; while (iEnd < aColData[0].nVal) { tColDataArrGetRowKey(aColData, nColData, iEnd, &keyEnd); if (tRowKeyCompare(&keyStart, &keyEnd) != 0) break; iEnd++; } if (iEnd - iStart > 1) { for (int32_t i = 0; i < nColData; i++) { tColDataMergeImpl(&aColData[i], iStart, iEnd); } } iStart++; } } void tColDataSortMerge(SArray *colDataArr) { int32_t nColData = TARRAY_SIZE(colDataArr); SColData *aColData = (SColData *)TARRAY_DATA(colDataArr); if (aColData[0].nVal <= 1) goto _exit; ASSERT(aColData[0].type == TSDB_DATA_TYPE_TIMESTAMP); ASSERT(aColData[0].cid == PRIMARYKEY_TIMESTAMP_COL_ID); ASSERT(aColData[0].flag == HAS_VALUE); int8_t doSort = 0; int8_t doMerge = 0; // scan ------- SRowKey lastKey; tColDataArrGetRowKey(aColData, nColData, 0, &lastKey); for (int32_t iVal = 1; iVal < aColData[0].nVal; ++iVal) { SRowKey key; tColDataArrGetRowKey(aColData, nColData, iVal, &key); int32_t c = tRowKeyCompare(&lastKey, &key); if (c < 0) { lastKey = key; continue; } else if (c > 0) { doSort = 1; break; } else { doMerge = 1; } } // sort ------- if (doSort) { (void)tColDataSort(aColData, nColData); } if (doMerge != 1) { tColDataArrGetRowKey(aColData, nColData, 0, &lastKey); for (int32_t iVal = 1; iVal < aColData[0].nVal; ++iVal) { SRowKey key; tColDataArrGetRowKey(aColData, nColData, iVal, &key); int32_t c = tRowKeyCompare(&lastKey, &key); if (c == 0) { doMerge = 1; break; } lastKey = key; } } // merge ------- if (doMerge) { tColDataMerge(aColData, nColData); } _exit: return; } static int32_t tPutColDataVersion0(uint8_t *pBuf, SColData *pColData) { int32_t n = 0; n += tPutI16v(pBuf ? pBuf + n : NULL, pColData->cid); n += tPutI8(pBuf ? pBuf + n : NULL, pColData->type); n += tPutI32v(pBuf ? pBuf + n : NULL, pColData->nVal); n += tPutI8(pBuf ? pBuf + n : NULL, pColData->flag); // bitmap switch (pColData->flag) { case (HAS_NULL | HAS_NONE): case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): if (pBuf) (void)memcpy(pBuf + n, pColData->pBitMap, BIT1_SIZE(pColData->nVal)); n += BIT1_SIZE(pColData->nVal); break; case (HAS_VALUE | HAS_NULL | HAS_NONE): if (pBuf) (void)memcpy(pBuf + n, pColData->pBitMap, BIT2_SIZE(pColData->nVal)); n += BIT2_SIZE(pColData->nVal); break; default: break; } // value if (pColData->flag & HAS_VALUE) { if (IS_VAR_DATA_TYPE(pColData->type)) { if (pBuf) (void)memcpy(pBuf + n, pColData->aOffset, pColData->nVal << 2); n += (pColData->nVal << 2); n += tPutI32v(pBuf ? pBuf + n : NULL, pColData->nData); if (pBuf) (void)memcpy(pBuf + n, pColData->pData, pColData->nData); n += pColData->nData; } else { if (pBuf) (void)memcpy(pBuf + n, pColData->pData, pColData->nData); n += pColData->nData; } } return n; } static int32_t tGetColDataVersion0(uint8_t *pBuf, SColData *pColData) { int32_t n = 0; n += tGetI16v(pBuf + n, &pColData->cid); n += tGetI8(pBuf + n, &pColData->type); n += tGetI32v(pBuf + n, &pColData->nVal); n += tGetI8(pBuf + n, &pColData->flag); // bitmap switch (pColData->flag) { case (HAS_NULL | HAS_NONE): case (HAS_VALUE | HAS_NONE): case (HAS_VALUE | HAS_NULL): pColData->pBitMap = pBuf + n; n += BIT1_SIZE(pColData->nVal); break; case (HAS_VALUE | HAS_NULL | HAS_NONE): pColData->pBitMap = pBuf + n; n += BIT2_SIZE(pColData->nVal); break; default: break; } // value if (pColData->flag & HAS_VALUE) { if (IS_VAR_DATA_TYPE(pColData->type)) { pColData->aOffset = (int32_t *)(pBuf + n); n += (pColData->nVal << 2); n += tGetI32v(pBuf + n, &pColData->nData); pColData->pData = pBuf + n; n += pColData->nData; } else { pColData->pData = pBuf + n; pColData->nData = TYPE_BYTES[pColData->type] * pColData->nVal; n += pColData->nData; } } pColData->cflag = 0; return n; } static int32_t tPutColDataVersion1(uint8_t *pBuf, SColData *pColData) { int32_t n = tPutColDataVersion0(pBuf, pColData); n += tPutI8(pBuf ? pBuf + n : NULL, pColData->cflag); return n; } static int32_t tGetColDataVersion1(uint8_t *pBuf, SColData *pColData) { int32_t n = tGetColDataVersion0(pBuf, pColData); n += tGetI8(pBuf ? pBuf + n : NULL, &pColData->cflag); return n; } int32_t tPutColData(uint8_t version, uint8_t *pBuf, SColData *pColData) { if (version == 0) { return tPutColDataVersion0(pBuf, pColData); } else if (version == 1) { return tPutColDataVersion1(pBuf, pColData); } else { ASSERT(0); return -1; } } int32_t tGetColData(uint8_t version, uint8_t *pBuf, SColData *pColData) { if (version == 0) { return tGetColDataVersion0(pBuf, pColData); } else if (version == 1) { return tGetColDataVersion1(pBuf, pColData); } else { ASSERT(0); return -1; } } #define CALC_SUM_MAX_MIN(SUM, MAX, MIN, VAL) \ do { \ (SUM) += (VAL); \ if ((MAX) < (VAL)) (MAX) = (VAL); \ if ((MIN) > (VAL)) (MIN) = (VAL); \ } while (0) static FORCE_INLINE void tColDataCalcSMABool(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *sum = 0; *max = 0; *min = 1; *numOfNull = 0; int8_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((int8_t *)pColData->pData)[iVal] ? 1 : 0; CALC_SUM_MAX_MIN(*sum, *max, *min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((int8_t *)pColData->pData)[iVal] ? 1 : 0; CALC_SUM_MAX_MIN(*sum, *max, *min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMATinyInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *sum = 0; *max = INT8_MIN; *min = INT8_MAX; *numOfNull = 0; int8_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((int8_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((int8_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMATinySmallInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *sum = 0; *max = INT16_MIN; *min = INT16_MAX; *numOfNull = 0; int16_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((int16_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((int16_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *sum = 0; *max = INT32_MIN; *min = INT32_MAX; *numOfNull = 0; int32_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((int32_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((int32_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMABigInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *sum = 0; *max = INT64_MIN; *min = INT64_MAX; *numOfNull = 0; int64_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((int64_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((int64_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*sum, *max, *min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAFloat(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(double *)sum = 0; *(double *)max = -FLT_MAX; *(double *)min = FLT_MAX; *numOfNull = 0; float val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((float *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(double *)sum, *(double *)max, *(double *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((float *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(double *)sum, *(double *)max, *(double *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMADouble(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(double *)sum = 0; *(double *)max = -DBL_MAX; *(double *)min = DBL_MAX; *numOfNull = 0; double val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((double *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(double *)sum, *(double *)max, *(double *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((double *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(double *)sum, *(double *)max, *(double *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAUTinyInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(uint64_t *)sum = 0; *(uint64_t *)max = 0; *(uint64_t *)min = UINT8_MAX; *numOfNull = 0; uint8_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((uint8_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((uint8_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMATinyUSmallInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(uint64_t *)sum = 0; *(uint64_t *)max = 0; *(uint64_t *)min = UINT16_MAX; *numOfNull = 0; uint16_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((uint16_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((uint16_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAUInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(uint64_t *)sum = 0; *(uint64_t *)max = 0; *(uint64_t *)min = UINT32_MAX; *numOfNull = 0; uint32_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((uint32_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((uint32_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAUBigInt(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(uint64_t *)sum = 0; *(uint64_t *)max = 0; *(uint64_t *)min = UINT64_MAX; *numOfNull = 0; uint64_t val; if (HAS_VALUE == pColData->flag) { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { val = ((uint64_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); } } else { for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { switch (tColDataGetBitValue(pColData, iVal)) { case 0: case 1: (*numOfNull)++; break; case 2: val = ((uint64_t *)pColData->pData)[iVal]; CALC_SUM_MAX_MIN(*(uint64_t *)sum, *(uint64_t *)max, *(uint64_t *)min, val); break; default: ASSERT(0); break; } } } } static FORCE_INLINE void tColDataCalcSMAVarType(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) { *(uint64_t *)sum = 0; *(uint64_t *)max = 0; *(uint64_t *)min = 0; *numOfNull = 0; switch (pColData->flag) { case HAS_NONE: case HAS_NULL: case (HAS_NONE | HAS_NULL): *numOfNull = pColData->nVal; break; case HAS_VALUE: *numOfNull = 0; break; case (HAS_VALUE | HAS_NULL): case (HAS_VALUE | HAS_NONE): for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { if (GET_BIT1(pColData->pBitMap, iVal) == 0) { (*numOfNull)++; } } break; case (HAS_VALUE | HAS_NONE | HAS_NULL): for (int32_t iVal = 0; iVal < pColData->nVal; iVal++) { if (GET_BIT2(pColData->pBitMap, iVal) != 2) { (*numOfNull)++; } } break; default: ASSERT(0); break; } } void (*tColDataCalcSMA[])(SColData *pColData, int64_t *sum, int64_t *max, int64_t *min, int16_t *numOfNull) = { NULL, tColDataCalcSMABool, // TSDB_DATA_TYPE_BOOL tColDataCalcSMATinyInt, // TSDB_DATA_TYPE_TINYINT tColDataCalcSMATinySmallInt, // TSDB_DATA_TYPE_SMALLINT tColDataCalcSMAInt, // TSDB_DATA_TYPE_INT tColDataCalcSMABigInt, // TSDB_DATA_TYPE_BIGINT tColDataCalcSMAFloat, // TSDB_DATA_TYPE_FLOAT tColDataCalcSMADouble, // TSDB_DATA_TYPE_DOUBLE tColDataCalcSMAVarType, // TSDB_DATA_TYPE_VARCHAR tColDataCalcSMABigInt, // TSDB_DATA_TYPE_TIMESTAMP tColDataCalcSMAVarType, // TSDB_DATA_TYPE_NCHAR tColDataCalcSMAUTinyInt, // TSDB_DATA_TYPE_UTINYINT tColDataCalcSMATinyUSmallInt, // TSDB_DATA_TYPE_USMALLINT tColDataCalcSMAUInt, // TSDB_DATA_TYPE_UINT tColDataCalcSMAUBigInt, // TSDB_DATA_TYPE_UBIGINT tColDataCalcSMAVarType, // TSDB_DATA_TYPE_JSON tColDataCalcSMAVarType, // TSDB_DATA_TYPE_VARBINARY tColDataCalcSMAVarType, // TSDB_DATA_TYPE_DECIMAL tColDataCalcSMAVarType, // TSDB_DATA_TYPE_BLOB NULL, // TSDB_DATA_TYPE_MEDIUMBLOB tColDataCalcSMAVarType // TSDB_DATA_TYPE_GEOMETRY }; // SValueColumn ================================ int32_t tValueColumnInit(SValueColumn *valCol) { valCol->type = TSDB_DATA_TYPE_NULL; valCol->numOfValues = 0; tBufferInit(&valCol->data); tBufferInit(&valCol->offsets); return 0; } int32_t tValueColumnDestroy(SValueColumn *valCol) { valCol->type = TSDB_DATA_TYPE_NULL; valCol->numOfValues = 0; tBufferDestroy(&valCol->data); tBufferDestroy(&valCol->offsets); return 0; } int32_t tValueColumnClear(SValueColumn *valCol) { valCol->type = TSDB_DATA_TYPE_NULL; valCol->numOfValues = 0; tBufferClear(&valCol->data); tBufferClear(&valCol->offsets); return 0; } int32_t tValueColumnAppend(SValueColumn *valCol, const SValue *value) { int32_t code; if (valCol->numOfValues == 0) { valCol->type = value->type; } ASSERT(value->type == valCol->type); if (IS_VAR_DATA_TYPE(value->type)) { if ((code = tBufferPutI32(&valCol->offsets, tBufferGetSize(&valCol->data)))) { return code; } if ((code = tBufferPut(&valCol->data, value->pData, value->nData))) { return code; } } else { code = tBufferPut(&valCol->data, &value->val, tDataTypes[value->type].bytes); if (code) return code; } valCol->numOfValues++; return 0; } int32_t tValueColumnUpdate(SValueColumn *valCol, int32_t idx, const SValue *value) { int32_t code; if (idx < 0 || idx >= valCol->numOfValues) { return TSDB_CODE_OUT_OF_RANGE; } if (IS_VAR_DATA_TYPE(valCol->type)) { int32_t *offsets = (int32_t *)tBufferGetData(&valCol->offsets); int32_t nextOffset = (idx == valCol->numOfValues - 1) ? tBufferGetSize(&valCol->data) : offsets[idx + 1]; int32_t oldDataSize = nextOffset - offsets[idx]; int32_t bytesAdded = value->nData - oldDataSize; if (bytesAdded != 0) { if ((code = tBufferEnsureCapacity(&valCol->data, tBufferGetSize(&valCol->data) + bytesAdded))) return code; memmove(tBufferGetDataAt(&valCol->data, nextOffset + bytesAdded), tBufferGetDataAt(&valCol->data, nextOffset), tBufferGetSize(&valCol->data) - nextOffset); valCol->data.size += bytesAdded; for (int32_t i = idx + 1; i < valCol->numOfValues; i++) { offsets[i] += bytesAdded; } } return tBufferPutAt(&valCol->data, offsets[idx], value->pData, value->nData); } else { return tBufferPutAt(&valCol->data, idx * tDataTypes[valCol->type].bytes, &value->val, tDataTypes[valCol->type].bytes); } return 0; } int32_t tValueColumnGet(SValueColumn *valCol, int32_t idx, SValue *value) { if (idx < 0 || idx >= valCol->numOfValues) { return TSDB_CODE_OUT_OF_RANGE; } value->type = valCol->type; if (IS_VAR_DATA_TYPE(value->type)) { int32_t offset, nextOffset; SBufferReader reader = BUFFER_READER_INITIALIZER(idx * sizeof(offset), &valCol->offsets); (void)tBufferGetI32(&reader, &offset); if (idx == valCol->numOfValues - 1) { nextOffset = tBufferGetSize(&valCol->data); } else { (void)tBufferGetI32(&reader, &nextOffset); } value->nData = nextOffset - offset; value->pData = (uint8_t *)tBufferGetDataAt(&valCol->data, offset); } else { SBufferReader reader = BUFFER_READER_INITIALIZER(idx * tDataTypes[value->type].bytes, &valCol->data); (void)tBufferGet(&reader, tDataTypes[value->type].bytes, &value->val); } return 0; } int32_t tValueColumnCompress(SValueColumn *valCol, SValueColumnCompressInfo *info, SBuffer *output, SBuffer *assist) { int32_t code; ASSERT(valCol->numOfValues > 0); (*info) = (SValueColumnCompressInfo){ .cmprAlg = info->cmprAlg, .type = valCol->type, }; // offset if (IS_VAR_DATA_TYPE(valCol->type)) { SCompressInfo cinfo = { .cmprAlg = info->cmprAlg, .dataType = TSDB_DATA_TYPE_INT, .originalSize = valCol->offsets.size, }; code = tCompressDataToBuffer(valCol->offsets.data, &cinfo, output, assist); if (code) return code; info->offsetOriginalSize = cinfo.originalSize; info->offsetCompressedSize = cinfo.compressedSize; } // data SCompressInfo cinfo = { .cmprAlg = info->cmprAlg, .dataType = valCol->type, .originalSize = valCol->data.size, }; code = tCompressDataToBuffer(valCol->data.data, &cinfo, output, assist); if (code) return code; info->dataOriginalSize = cinfo.originalSize; info->dataCompressedSize = cinfo.compressedSize; return 0; } int32_t tValueColumnDecompress(void *input, const SValueColumnCompressInfo *info, SValueColumn *valCol, SBuffer *assist) { int32_t code; (void)tValueColumnClear(valCol); valCol->type = info->type; // offset if (IS_VAR_DATA_TYPE(valCol->type)) { valCol->numOfValues = info->offsetOriginalSize / tDataTypes[TSDB_DATA_TYPE_INT].bytes; SCompressInfo cinfo = { .dataType = TSDB_DATA_TYPE_INT, .cmprAlg = info->cmprAlg, .originalSize = info->offsetOriginalSize, .compressedSize = info->offsetCompressedSize, }; code = tDecompressDataToBuffer(input, &cinfo, &valCol->offsets, assist); if (code) { return code; } } else { valCol->numOfValues = info->dataOriginalSize / tDataTypes[valCol->type].bytes; } // data SCompressInfo cinfo = { .dataType = valCol->type, .cmprAlg = info->cmprAlg, .originalSize = info->dataOriginalSize, .compressedSize = info->dataCompressedSize, }; code = tDecompressDataToBuffer((char *)input + info->offsetCompressedSize, &cinfo, &valCol->data, assist); if (code) { return code; } return 0; } int32_t tValueColumnCompressInfoEncode(const SValueColumnCompressInfo *info, SBuffer *buffer) { int32_t code; uint8_t fmtVer = 0; if ((code = tBufferPutU8(buffer, fmtVer))) return code; if ((code = tBufferPutI8(buffer, info->cmprAlg))) return code; if ((code = tBufferPutI8(buffer, info->type))) return code; if (IS_VAR_DATA_TYPE(info->type)) { if ((code = tBufferPutI32v(buffer, info->offsetOriginalSize))) return code; if ((code = tBufferPutI32v(buffer, info->offsetCompressedSize))) return code; } if ((code = tBufferPutI32v(buffer, info->dataOriginalSize))) return code; if ((code = tBufferPutI32v(buffer, info->dataCompressedSize))) return code; return 0; } int32_t tValueColumnCompressInfoDecode(SBufferReader *reader, SValueColumnCompressInfo *info) { int32_t code; uint8_t fmtVer; if ((code = tBufferGetU8(reader, &fmtVer))) return code; if (fmtVer == 0) { if ((code = tBufferGetI8(reader, &info->cmprAlg))) return code; if ((code = tBufferGetI8(reader, &info->type))) return code; if (IS_VAR_DATA_TYPE(info->type)) { if ((code = tBufferGetI32v(reader, &info->offsetOriginalSize))) return code; if ((code = tBufferGetI32v(reader, &info->offsetCompressedSize))) return code; } else { info->offsetOriginalSize = 0; info->offsetCompressedSize = 0; } if ((code = tBufferGetI32v(reader, &info->dataOriginalSize))) return code; if ((code = tBufferGetI32v(reader, &info->dataCompressedSize))) return code; } else { ASSERT(0); } return 0; } int32_t tCompressData(void *input, // input SCompressInfo *info, // compress info void *output, // output int32_t outputSize, // output size SBuffer *buffer // assistant buffer provided by caller, can be NULL ) { int32_t extraSizeNeeded; int32_t code; extraSizeNeeded = (info->cmprAlg == NO_COMPRESSION) ? info->originalSize : info->originalSize + COMP_OVERFLOW_BYTES; ASSERT(outputSize >= extraSizeNeeded); if (info->cmprAlg == NO_COMPRESSION) { (void)memcpy(output, input, info->originalSize); info->compressedSize = info->originalSize; } else if (info->cmprAlg == ONE_STAGE_COMP || info->cmprAlg == TWO_STAGE_COMP) { SBuffer local; tBufferInit(&local); if (buffer == NULL) { buffer = &local; } if (info->cmprAlg == TWO_STAGE_COMP) { code = tBufferEnsureCapacity(buffer, extraSizeNeeded); if (code) { tBufferDestroy(&local); return code; } } info->compressedSize = tDataTypes[info->dataType].compFunc( // input, // input info->originalSize, // input size info->originalSize / tDataTypes[info->dataType].bytes, // number of elements output, // output outputSize, // output size info->cmprAlg, // compression algorithm buffer->data, // buffer buffer->capacity // buffer size ); if (info->compressedSize < 0) { tBufferDestroy(&local); return TSDB_CODE_COMPRESS_ERROR; } tBufferDestroy(&local); } else { DEFINE_VAR(info->cmprAlg) if ((l1 == L1_UNKNOWN && l2 == L2_UNKNOWN) || (l1 == L1_DISABLED && l2 == L2_DISABLED)) { (void)memcpy(output, input, info->originalSize); info->compressedSize = info->originalSize; return 0; } SBuffer local; tBufferInit(&local); if (buffer == NULL) { buffer = &local; } code = tBufferEnsureCapacity(buffer, extraSizeNeeded); info->compressedSize = tDataCompress[info->dataType].compFunc( // input, // input info->originalSize, // input size info->originalSize / tDataTypes[info->dataType].bytes, // number of elements output, // output outputSize, // output size info->cmprAlg, // compression algorithm buffer->data, // buffer buffer->capacity // buffer size ); if (info->compressedSize < 0) { tBufferDestroy(&local); return TSDB_CODE_COMPRESS_ERROR; } tBufferDestroy(&local); // new col compress } return 0; } int32_t tDecompressData(void *input, // input const SCompressInfo *info, // compress info void *output, // output int32_t outputSize, // output size SBuffer *buffer // assistant buffer provided by caller, can be NULL ) { int32_t code; ASSERT(outputSize >= info->originalSize); if (info->cmprAlg == NO_COMPRESSION) { ASSERT(info->compressedSize == info->originalSize); (void)memcpy(output, input, info->compressedSize); } else if (info->cmprAlg == ONE_STAGE_COMP || info->cmprAlg == TWO_STAGE_COMP) { SBuffer local; tBufferInit(&local); if (buffer == NULL) { buffer = &local; } if (info->cmprAlg == TWO_STAGE_COMP) { code = tBufferEnsureCapacity(buffer, info->originalSize + COMP_OVERFLOW_BYTES); if (code) { tBufferDestroy(&local); return code; } } int32_t decompressedSize = tDataTypes[info->dataType].decompFunc( input, // input info->compressedSize, // inputSize info->originalSize / tDataTypes[info->dataType].bytes, // number of elements output, // output outputSize, // output size info->cmprAlg, // compression algorithm buffer->data, // helper buffer buffer->capacity // extra buffer size ); if (decompressedSize < 0) { tBufferDestroy(&local); return TSDB_CODE_COMPRESS_ERROR; } ASSERT(decompressedSize == info->originalSize); tBufferDestroy(&local); } else { DEFINE_VAR(info->cmprAlg); if (l1 == L1_DISABLED && l2 == L2_DISABLED) { (void)memcpy(output, input, info->compressedSize); return 0; } SBuffer local; tBufferInit(&local); if (buffer == NULL) { buffer = &local; } code = tBufferEnsureCapacity(buffer, info->originalSize + COMP_OVERFLOW_BYTES); if (code) { return code; } int32_t decompressedSize = tDataCompress[info->dataType].decompFunc( input, // input info->compressedSize, // inputSize info->originalSize / tDataTypes[info->dataType].bytes, // number of elements output, // output outputSize, // output size info->cmprAlg, // compression algorithm buffer->data, // helper buffer buffer->capacity // extra buffer size ); if (decompressedSize < 0) { tBufferDestroy(&local); return TSDB_CODE_COMPRESS_ERROR; } ASSERT(decompressedSize == info->originalSize); tBufferDestroy(&local); } return 0; } int32_t tCompressDataToBuffer(void *input, SCompressInfo *info, SBuffer *output, SBuffer *assist) { int32_t code; code = tBufferEnsureCapacity(output, output->size + info->originalSize + COMP_OVERFLOW_BYTES); if (code) return code; code = tCompressData(input, info, tBufferGetDataEnd(output), output->capacity - output->size, assist); if (code) return code; output->size += info->compressedSize; return 0; } int32_t tDecompressDataToBuffer(void *input, SCompressInfo *info, SBuffer *output, SBuffer *assist) { int32_t code; code = tBufferEnsureCapacity(output, output->size + info->originalSize); if (code) return code; code = tDecompressData(input, info, tBufferGetDataEnd(output), output->capacity - output->size, assist); if (code) return code; output->size += info->originalSize; return 0; }