/* * 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 "trow.h" const uint8_t tdVTypeByte[3] = { TD_VTYPE_NORM_BYTE, // TD_VTYPE_NORM TD_VTYPE_NONE_BYTE, // TD_VTYPE_NONE TD_VTYPE_NULL_BYTE, // TD_VTYPE_NULL }; // declaration static uint8_t tdGetBitmapByte(uint8_t byte); // implementation /** * @brief Compress bitmap bytes comprised of 2-bits to counterpart of 1-bit. * e.g. * TD_VTYPE_NORM 0x00U(00000000) to 00000000 Normal * TD_VTYPE_NULL 0x01U(00000001) to 00000001 Null * TD_VTYPE_NONE 0x02U(00000010) to 00000001 Null * * 00000000 0x00 0x00 * 01000000 0x40 0x08 * 10000000 0x80 0x08 * ... * @param byte * @return uint8_t */ static uint8_t tdGetMergedBitmapByte(uint8_t byte) { switch (byte) { case 0x00: return 0x00; case 0x40: return 0x08; case 0x80: return 0x08; case 0x10: return 0x04; case 0x50: return 0x0c; case 0x90: return 0x0c; case 0x20: return 0x04; case 0x60: return 0x0c; case 0xa0: return 0x0c; case 0x04: return 0x02; case 0x44: return 0x0a; case 0x84: return 0x0a; case 0x14: return 0x06; case 0x54: return 0x0e; case 0x94: return 0x0e; case 0x24: return 0x06; case 0x64: return 0x0e; case 0xa4: return 0x0e; case 0x08: return 0x02; case 0x48: return 0x0a; case 0x88: return 0x0a; case 0x18: return 0x06; case 0x58: return 0x0e; case 0x98: return 0x0e; case 0x28: return 0x06; case 0x68: return 0x0e; case 0xa8: return 0x0e; case 0x01: return 0x01; case 0x41: return 0x09; case 0x81: return 0x09; case 0x11: return 0x05; case 0x51: return 0x0d; case 0x91: return 0x0d; case 0x21: return 0x05; case 0x61: return 0x0d; case 0xa1: return 0x0d; case 0x05: return 0x03; case 0x45: return 0x0b; case 0x85: return 0x0b; case 0x15: return 0x07; case 0x55: return 0x0f; case 0x95: return 0x0f; case 0x25: return 0x07; case 0x65: return 0x0f; case 0xa5: return 0x0f; case 0x09: return 0x03; case 0x49: return 0x0b; case 0x89: return 0x0b; case 0x19: return 0x07; case 0x59: return 0x0f; case 0x99: return 0x0f; case 0x29: return 0x07; case 0x69: return 0x0f; case 0xa9: return 0x0f; case 0x02: return 0x01; case 0x42: return 0x09; case 0x82: return 0x09; case 0x12: return 0x05; case 0x52: return 0x0d; case 0x92: return 0x0d; case 0x22: return 0x05; case 0x62: return 0x0d; case 0xa2: return 0x0d; case 0x06: return 0x03; case 0x46: return 0x0b; case 0x86: return 0x0b; case 0x16: return 0x07; case 0x56: return 0x0f; case 0x96: return 0x0f; case 0x26: return 0x07; case 0x66: return 0x0f; case 0xa6: return 0x0f; case 0x0a: return 0x03; case 0x4a: return 0x0b; case 0x8a: return 0x0b; case 0x1a: return 0x07; case 0x5a: return 0x0f; case 0x9a: return 0x0f; case 0x2a: return 0x07; case 0x6a: return 0x0f; case 0xaa: return 0x0f; default: // make sure the bitmap area is set to 0 firstly ASSERT(0); return 0x0f; // return NULL bitmap for exception } } /** * @brief Merge bitmap from 2 bits to 1 bits, and the memory buffer should be guaranteed by the invoker. * * @param srcBitmap * @param srcLen * @param dstBitmap */ void tdMergeBitmap(uint8_t *srcBitmap, int32_t srcLen, uint8_t *dstBitmap) { int32_t i = 0, j = 0; if (srcLen > 0) { dstBitmap[j] = (tdGetMergedBitmapByte(srcBitmap[i]) << 4); } while ((++i) < srcLen) { if ((i & 1) == 0) { dstBitmap[j] = (tdGetMergedBitmapByte(srcBitmap[i]) << 4); } else { dstBitmap[j] |= tdGetMergedBitmapByte(srcBitmap[i]); ++j; } } } // static void dataColSetNEleNull(SDataCol *pCol, int nEle); static void tdMergeTwoDataCols(SDataCols *target, SDataCols *src1, int *iter1, int limit1, SDataCols *src2, int *iter2, int limit2, int tRows, bool forceSetNull); static FORCE_INLINE void dataColSetNullAt(SDataCol *pCol, int index, bool setBitmap, int8_t bitmapMode) { if (IS_VAR_DATA_TYPE(pCol->type)) { pCol->dataOff[index] = pCol->len; char *ptr = POINTER_SHIFT(pCol->pData, pCol->len); setVardataNull(ptr, pCol->type); pCol->len += varDataTLen(ptr); } else { setNull(POINTER_SHIFT(pCol->pData, TYPE_BYTES[pCol->type] * index), pCol->type, pCol->bytes); pCol->len += TYPE_BYTES[pCol->type]; } if (setBitmap) { tdSetBitmapValType(pCol->pBitmap, index, TD_VTYPE_NONE, bitmapMode); } } // static void dataColSetNEleNull(SDataCol *pCol, int nEle) { // if (IS_VAR_DATA_TYPE(pCol->type)) { // pCol->len = 0; // for (int i = 0; i < nEle; i++) { // dataColSetNullAt(pCol, i); // } // } else { // setNullN(pCol->pData, pCol->type, pCol->bytes, nEle); // pCol->len = TYPE_BYTES[pCol->type] * nEle; // } // } int32_t tdSetBitmapValTypeN(void *pBitmap, int16_t nEle, TDRowValT valType, int8_t bitmapMode) { TASSERT(valType < TD_VTYPE_MAX); int16_t nBytes = nEle / TD_VTYPE_PARTS; for (int i = 0; i < nBytes; ++i) { *(uint8_t *)pBitmap = tdVTypeByte[valType]; pBitmap = POINTER_SHIFT(pBitmap, 1); } int16_t nLeft = nEle - nBytes * TD_VTYPE_BITS; for (int j = 0; j < nLeft; ++j) { tdSetBitmapValType(pBitmap, j, valType, bitmapMode); } return TSDB_CODE_SUCCESS; } static FORCE_INLINE void dataColSetNoneAt(SDataCol *pCol, int index, bool setBitmap, int8_t bitmapMode) { if (IS_VAR_DATA_TYPE(pCol->type)) { pCol->dataOff[index] = pCol->len; char *ptr = POINTER_SHIFT(pCol->pData, pCol->len); setVardataNull(ptr, pCol->type); pCol->len += varDataTLen(ptr); } else { setNull(POINTER_SHIFT(pCol->pData, TYPE_BYTES[pCol->type] * index), pCol->type, pCol->bytes); pCol->len += TYPE_BYTES[pCol->type]; } if (setBitmap) { tdSetBitmapValType(pCol->pBitmap, index, TD_VTYPE_NONE, bitmapMode); } } static void dataColSetNEleNone(SDataCol *pCol, int nEle, int8_t bitmapMode) { if (IS_VAR_DATA_TYPE(pCol->type)) { pCol->len = 0; for (int i = 0; i < nEle; ++i) { dataColSetNoneAt(pCol, i, false, bitmapMode); } } else { setNullN(pCol->pData, pCol->type, pCol->bytes, nEle); pCol->len = TYPE_BYTES[pCol->type] * nEle; } #ifdef TD_SUPPORT_BITMAP tdSetBitmapValTypeN(pCol->pBitmap, nEle, TD_VTYPE_NONE, bitmapMode); #endif } #if 0 void trbSetRowInfo(SRowBuilder *pRB, bool del, uint16_t sver) { // TODO } void trbSetRowVersion(SRowBuilder *pRB, uint64_t ver) { // TODO } void trbSetRowTS(SRowBuilder *pRB, TSKEY ts) { // TODO } int trbWriteCol(SRowBuilder *pRB, void *pData, col_id_t cid) { // TODO return 0; } #endif STSRow *tdRowDup(STSRow *row) { STSRow *trow = taosMemoryMalloc(TD_ROW_LEN(row)); if (trow == NULL) return NULL; tdRowCpy(trow, row); return trow; } /** * @brief * * @param pCol * @param valType * @param val * @param numOfRows * @param maxPoints * @param bitmapMode default is 0(2 bits), otherwise 1(1 bit) * @return int */ int tdAppendValToDataCol(SDataCol *pCol, TDRowValT valType, const void *val, int numOfRows, int maxPoints, int8_t bitmapMode) { TASSERT(pCol != NULL); // Assume that the columns not specified during insert/upsert mean None. if (isAllRowsNone(pCol)) { if (tdValIsNone(valType)) { // all None value yet, just return return 0; } if (tdAllocMemForCol(pCol, maxPoints) < 0) return -1; if (numOfRows > 0) { // Find the first not None value, fill all previous values as None dataColSetNEleNone(pCol, numOfRows, bitmapMode); } } if (!tdValTypeIsNorm(valType)) { // TODO: // 1. back compatibility and easy to debug with codes of 2.0 to save NULL values. // 2. later on, considering further optimization, don't save Null/None for VarType. val = getNullValue(pCol->type); } if (IS_VAR_DATA_TYPE(pCol->type)) { // set offset pCol->dataOff[numOfRows] = pCol->len; // Copy data memcpy(POINTER_SHIFT(pCol->pData, pCol->len), val, varDataTLen(val)); // Update the length pCol->len += varDataTLen(val); } else { ASSERT(pCol->len == TYPE_BYTES[pCol->type] * numOfRows); memcpy(POINTER_SHIFT(pCol->pData, pCol->len), val, pCol->bytes); pCol->len += pCol->bytes; } #ifdef TD_SUPPORT_BITMAP tdSetBitmapValType(pCol->pBitmap, numOfRows, valType, bitmapMode); #endif return 0; } // internal static int32_t tdAppendTpRowToDataCol(STSRow *pRow, STSchema *pSchema, SDataCols *pCols) { ASSERT(pCols->numOfRows == 0 || dataColsKeyLast(pCols) < TD_ROW_KEY(pRow)); int rcol = 1; int dcol = 1; void *pBitmap = tdGetBitmapAddrTp(pRow, pSchema->flen); SDataCol *pDataCol = &(pCols->cols[0]); ASSERT(pDataCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID); tdAppendValToDataCol(pDataCol, TD_VTYPE_NORM, &pRow->ts, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); while (dcol < pCols->numOfCols) { pDataCol = &(pCols->cols[dcol]); if (rcol >= schemaNCols(pSchema)) { tdAppendValToDataCol(pDataCol, TD_VTYPE_NULL, NULL, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; continue; } STColumn *pRowCol = schemaColAt(pSchema, rcol); SCellVal sVal = {0}; if (pRowCol->colId == pDataCol->colId) { if (tdGetTpRowValOfCol(&sVal, pRow, pBitmap, pRowCol->type, pRowCol->offset - sizeof(TSKEY), rcol - 1) < 0) { return terrno; } tdAppendValToDataCol(pDataCol, sVal.valType, sVal.val, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; ++rcol; } else if (pRowCol->colId < pDataCol->colId) { ++rcol; } else { tdAppendValToDataCol(pDataCol, TD_VTYPE_NULL, NULL, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; } } ++pCols->numOfRows; return TSDB_CODE_SUCCESS; } // internal static int32_t tdAppendKvRowToDataCol(STSRow *pRow, STSchema *pSchema, SDataCols *pCols) { ASSERT(pCols->numOfRows == 0 || dataColsKeyLast(pCols) < TD_ROW_KEY(pRow)); int rcol = 0; int dcol = 1; int tRowCols = tdRowGetNCols(pRow) - 1; // the primary TS key not included in kvRowColIdx part int tSchemaCols = schemaNCols(pSchema) - 1; void *pBitmap = tdGetBitmapAddrKv(pRow, tdRowGetNCols(pRow)); SDataCol *pDataCol = &(pCols->cols[0]); ASSERT(pDataCol->colId == PRIMARYKEY_TIMESTAMP_COL_ID); tdAppendValToDataCol(pDataCol, TD_VTYPE_NORM, &pRow->ts, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); while (dcol < pCols->numOfCols) { pDataCol = &(pCols->cols[dcol]); if (rcol >= tRowCols || rcol >= tSchemaCols) { tdAppendValToDataCol(pDataCol, TD_VTYPE_NULL, NULL, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; continue; } SKvRowIdx *pIdx = tdKvRowColIdxAt(pRow, rcol); int16_t colIdx = -1; if (pIdx) { colIdx = POINTER_DISTANCE(pRow->data, pIdx) / sizeof(SKvRowIdx); } SCellVal sVal = {0}; if (pIdx->colId == pDataCol->colId) { if (tdGetKvRowValOfCol(&sVal, pRow, pBitmap, pIdx->offset, colIdx) < 0) { return terrno; } tdAppendValToDataCol(pDataCol, sVal.valType, sVal.val, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; ++rcol; } else if (pIdx->colId < pDataCol->colId) { ++rcol; } else { tdAppendValToDataCol(pDataCol, TD_VTYPE_NULL, NULL, pCols->numOfRows, pCols->maxPoints, pCols->bitmapMode); ++dcol; } } ++pCols->numOfRows; return TSDB_CODE_SUCCESS; } /** * @brief exposed * * @param pRow * @param pSchema * @param pCols * @param forceSetNull */ int32_t tdAppendSTSRowToDataCol(STSRow *pRow, STSchema *pSchema, SDataCols *pCols, bool forceSetNull) { if (TD_IS_TP_ROW(pRow)) { return tdAppendTpRowToDataCol(pRow, pSchema, pCols); } else if (TD_IS_KV_ROW(pRow)) { return tdAppendKvRowToDataCol(pRow, pSchema, pCols); } else { ASSERT(0); } return TSDB_CODE_SUCCESS; } int tdMergeDataCols(SDataCols *target, SDataCols *source, int rowsToMerge, int *pOffset, bool forceSetNull) { ASSERT(rowsToMerge > 0 && rowsToMerge <= source->numOfRows); ASSERT(target->numOfCols == source->numOfCols); int offset = 0; if (pOffset == NULL) { pOffset = &offset; } SDataCols *pTarget = NULL; if ((target->numOfRows == 0) || (dataColsKeyLast(target) < dataColsKeyAtRow(source, *pOffset))) { // No overlap ASSERT(target->numOfRows + rowsToMerge <= target->maxPoints); for (int i = 0; i < rowsToMerge; i++) { for (int j = 0; j < source->numOfCols; j++) { if (source->cols[j].len > 0 || target->cols[j].len > 0) { SCellVal sVal = {0}; if (tdGetColDataOfRow(&sVal, source->cols + j, i + (*pOffset), source->bitmapMode) < 0) { TASSERT(0); } tdAppendValToDataCol(target->cols + j, sVal.valType, sVal.val, target->numOfRows, target->maxPoints, target->bitmapMode); } } ++target->numOfRows; } (*pOffset) += rowsToMerge; } else { pTarget = tdDupDataCols(target, true); if (pTarget == NULL) goto _err; int iter1 = 0; tdMergeTwoDataCols(target, pTarget, &iter1, pTarget->numOfRows, source, pOffset, source->numOfRows, pTarget->numOfRows + rowsToMerge, forceSetNull); } tdFreeDataCols(pTarget); return 0; _err: tdFreeDataCols(pTarget); return -1; } // src2 data has more priority than src1 static void tdMergeTwoDataCols(SDataCols *target, SDataCols *src1, int *iter1, int limit1, SDataCols *src2, int *iter2, int limit2, int tRows, bool forceSetNull) { tdResetDataCols(target); ASSERT(limit1 <= src1->numOfRows && limit2 <= src2->numOfRows); while (target->numOfRows < tRows) { if (*iter1 >= limit1 && *iter2 >= limit2) break; TSKEY key1 = (*iter1 >= limit1) ? INT64_MAX : dataColsKeyAt(src1, *iter1); TKEY tkey1 = (*iter1 >= limit1) ? TKEY_NULL : dataColsTKeyAt(src1, *iter1); TSKEY key2 = (*iter2 >= limit2) ? INT64_MAX : dataColsKeyAt(src2, *iter2); // TKEY tkey2 = (*iter2 >= limit2) ? TKEY_NULL : dataColsTKeyAt(src2, *iter2); ASSERT(tkey1 == TKEY_NULL || (!TKEY_IS_DELETED(tkey1))); if (key1 < key2) { for (int i = 0; i < src1->numOfCols; i++) { ASSERT(target->cols[i].type == src1->cols[i].type); if (src1->cols[i].len > 0 || target->cols[i].len > 0) { SCellVal sVal = {0}; if (tdGetColDataOfRow(&sVal, src1->cols + i, *iter1, src1->bitmapMode) < 0) { TASSERT(0); } tdAppendValToDataCol(&(target->cols[i]), sVal.valType, sVal.val, target->numOfRows, target->maxPoints, target->bitmapMode); } } target->numOfRows++; (*iter1)++; } else if (key1 >= key2) { // if ((key1 > key2) || (key1 == key2 && !TKEY_IS_DELETED(tkey2))) { if ((key1 > key2) || (key1 == key2)) { for (int i = 0; i < src2->numOfCols; i++) { SCellVal sVal = {0}; ASSERT(target->cols[i].type == src2->cols[i].type); if (tdGetColDataOfRow(&sVal, src2->cols + i, *iter2, src2->bitmapMode) < 0) { TASSERT(0); } if (src2->cols[i].len > 0 && !tdValTypeIsNull(sVal.valType)) { tdAppendValToDataCol(&(target->cols[i]), sVal.valType, sVal.val, target->numOfRows, target->maxPoints, target->bitmapMode); } else if (!forceSetNull && key1 == key2 && src1->cols[i].len > 0) { if (tdGetColDataOfRow(&sVal, src1->cols + i, *iter1, src1->bitmapMode) < 0) { TASSERT(0); } tdAppendValToDataCol(&(target->cols[i]), sVal.valType, sVal.val, target->numOfRows, target->maxPoints, target->bitmapMode); } else if (target->cols[i].len > 0) { dataColSetNullAt(&target->cols[i], target->numOfRows, true, target->bitmapMode); } } target->numOfRows++; } (*iter2)++; if (key1 == key2) (*iter1)++; } ASSERT(target->numOfRows <= target->maxPoints); } } STSRow *mergeTwoRows(void *buffer, STSRow *row1, STSRow *row2, STSchema *pSchema1, STSchema *pSchema2) { #if 0 ASSERT(TD_ROW_KEY(row1) == TD_ROW_KEY(row2)); ASSERT(schemaVersion(pSchema1) == TD_ROW_SVER(row1)); ASSERT(schemaVersion(pSchema2) == TD_ROW_SVER(row2)); ASSERT(schemaVersion(pSchema1) >= schemaVersion(pSchema2)); #endif #if 0 SArray *stashRow = taosArrayInit(pSchema1->numOfCols, sizeof(SColInfo)); if (stashRow == NULL) { return NULL; } STSRow pRow = buffer; STpRow dataRow = memRowDataBody(pRow); memRowSetType(pRow, SMEM_ROW_DATA); dataRowSetVersion(dataRow, schemaVersion(pSchema1)); // use latest schema version dataRowSetLen(dataRow, (TDRowLenT)(TD_DATA_ROW_HEAD_SIZE + pSchema1->flen)); TDRowLenT dataLen = 0, kvLen = TD_MEM_ROW_KV_HEAD_SIZE; int32_t i = 0; // row1 int32_t j = 0; // row2 int32_t nCols1 = schemaNCols(pSchema1); int32_t nCols2 = schemaNCols(pSchema2); SColInfo colInfo = {0}; int32_t kvIdx1 = 0, kvIdx2 = 0; while (i < nCols1) { STColumn *pCol = schemaColAt(pSchema1, i); void * val1 = tdGetMemRowDataOfColEx(row1, pCol->colId, pCol->type, TD_DATA_ROW_HEAD_SIZE + pCol->offset, &kvIdx1); // if val1 != NULL, use val1; if (val1 != NULL && !isNull(val1, pCol->type)) { tdAppendColVal(dataRow, val1, pCol->type, pCol->offset); kvLen += tdGetColAppendLen(SMEM_ROW_KV, val1, pCol->type); setSColInfo(&colInfo, pCol->colId, pCol->type, val1); taosArrayPush(stashRow, &colInfo); ++i; // next col continue; } void *val2 = NULL; while (j < nCols2) { STColumn *tCol = schemaColAt(pSchema2, j); if (tCol->colId < pCol->colId) { ++j; continue; } if (tCol->colId == pCol->colId) { val2 = tdGetMemRowDataOfColEx(row2, tCol->colId, tCol->type, TD_DATA_ROW_HEAD_SIZE + tCol->offset, &kvIdx2); } else if (tCol->colId > pCol->colId) { // set NULL } break; } // end of while(jtype); } tdAppendColVal(dataRow, val2, pCol->type, pCol->offset); if (!isNull(val2, pCol->type)) { kvLen += tdGetColAppendLen(SMEM_ROW_KV, val2, pCol->type); setSColInfo(&colInfo, pCol->colId, pCol->type, val2); taosArrayPush(stashRow, &colInfo); } ++i; // next col } dataLen = TD_ROW_LEN(pRow); if (kvLen < dataLen) { // scan stashRow and generate SKVRow memset(buffer, 0, sizeof(dataLen)); STSRow tRow = buffer; memRowSetType(tRow, SMEM_ROW_KV); SKVRow kvRow = (SKVRow)memRowKvBody(tRow); int16_t nKvNCols = (int16_t) taosArrayGetSize(stashRow); kvRowSetLen(kvRow, (TDRowLenT)(TD_KV_ROW_HEAD_SIZE + sizeof(SColIdx) * nKvNCols)); kvRowSetNCols(kvRow, nKvNCols); memRowSetKvVersion(tRow, pSchema1->version); int32_t toffset = 0; int16_t k; for (k = 0; k < nKvNCols; ++k) { SColInfo *pColInfo = taosArrayGet(stashRow, k); tdAppendKvColVal(kvRow, pColInfo->colVal, true, pColInfo->colId, pColInfo->colType, toffset); toffset += sizeof(SColIdx); } ASSERT(kvLen == TD_ROW_LEN(tRow)); } taosArrayDestroy(stashRow); return buffer; #endif return NULL; } SDataCols *tdDupDataCols(SDataCols *pDataCols, bool keepData) { SDataCols *pRet = tdNewDataCols(pDataCols->maxCols, pDataCols->maxPoints); if (pRet == NULL) return NULL; pRet->numOfCols = pDataCols->numOfCols; pRet->bitmapMode = pDataCols->bitmapMode; pRet->sversion = pDataCols->sversion; if (keepData) pRet->numOfRows = pDataCols->numOfRows; for (int i = 0; i < pDataCols->numOfCols; i++) { pRet->cols[i].type = pDataCols->cols[i].type; pRet->cols[i].bitmap = pDataCols->cols[i].bitmap; pRet->cols[i].colId = pDataCols->cols[i].colId; pRet->cols[i].bytes = pDataCols->cols[i].bytes; pRet->cols[i].offset = pDataCols->cols[i].offset; if (keepData) { if (pDataCols->cols[i].len > 0) { if (tdAllocMemForCol(&pRet->cols[i], pRet->maxPoints) < 0) { tdFreeDataCols(pRet); return NULL; } pRet->cols[i].len = pDataCols->cols[i].len; memcpy(pRet->cols[i].pData, pDataCols->cols[i].pData, pDataCols->cols[i].len); if (IS_VAR_DATA_TYPE(pRet->cols[i].type)) { int dataOffSize = sizeof(VarDataOffsetT) * pDataCols->maxPoints; memcpy(pRet->cols[i].dataOff, pDataCols->cols[i].dataOff, dataOffSize); } if (!TD_COL_ROWS_NORM(pRet->cols + i)) { int32_t nBitmapBytes = (int32_t)TD_BITMAP_BYTES(pDataCols->numOfRows); memcpy(pRet->cols[i].pBitmap, pDataCols->cols[i].pBitmap, nBitmapBytes); } } } } return pRet; }