diff --git a/include/common/trow.h b/include/common/trow.h index 40462a7eef..83bcc67bb5 100644 --- a/include/common/trow.h +++ b/include/common/trow.h @@ -684,6 +684,43 @@ static int32_t tdSRowResetBuf(SRowBuilder *pBuilder, void *pBuf) { return TSDB_CODE_SUCCESS; } +/** + * @brief The invoker is responsible for memory alloc/dealloc. + * + * @param pBuilder + * @param pBuf Output buffer of STSRow + */ +static int32_t tdSRowGetBuf(SRowBuilder *pBuilder, void *pBuf) { + pBuilder->pBuf = (STSRow *)pBuf; + if (!pBuilder->pBuf) { + TASSERT(0); + terrno = TSDB_CODE_INVALID_PARA; + return terrno; + } + + TASSERT(pBuilder->nBitmaps > 0 && pBuilder->flen > 0); + + uint32_t len = 0; + switch (pBuilder->rowType) { + case TD_ROW_TP: +#ifdef TD_SUPPORT_BITMAP + pBuilder->pBitmap = tdGetBitmapAddrTp(pBuilder->pBuf, pBuilder->flen); +#endif + break; + case TD_ROW_KV: +#ifdef TD_SUPPORT_BITMAP + pBuilder->pBitmap = tdGetBitmapAddrKv(pBuilder->pBuf, pBuilder->nBoundCols); +#endif + break; + default: + TASSERT(0); + terrno = TSDB_CODE_INVALID_PARA; + return terrno; + } + return TSDB_CODE_SUCCESS; +} + + /** * @brief 由调用方管理存储空间的分配及释放,一次输入多个参数 * diff --git a/include/libs/parser/parser.h b/include/libs/parser/parser.h index a7377655c7..58482735ba 100644 --- a/include/libs/parser/parser.h +++ b/include/libs/parser/parser.h @@ -76,6 +76,7 @@ typedef struct SQuery { } SQuery; int32_t qParseQuerySql(SParseContext* pCxt, SQuery** pQuery); +bool isInsertSql(const char* pStr, size_t length); void qDestroyQuery(SQuery* pQueryNode); @@ -87,7 +88,8 @@ int32_t qCloneStmtDataBlock(void** pDst, void* pSrc); void qFreeStmtDataBlock(void* pDataBlock); int32_t qRebuildStmtDataBlock(void** pDst, void* pSrc); void qDestroyStmtDataBlock(void* pBlock); -int32_t qBindStmtColsValue(void *pDataBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32_t msgBufLen); +int32_t qBindStmtColsValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32_t msgBufLen); +int32_t qBindStmtSingleColValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32_t msgBufLen, int32_t colIdx, int32_t rowNum); int32_t qBuildStmtColFields(void *pDataBlock, int32_t *fieldNum, TAOS_FIELD** fields); int32_t qBuildStmtTagFields(void *pBlock, void *boundTags, int32_t *fieldNum, TAOS_FIELD** fields); int32_t qBindStmtTagsValue(void *pBlock, void *boundTags, int64_t suid, SName *pName, TAOS_BIND_v2 *bind, char *msgBuf, int32_t msgBufLen); diff --git a/source/client/inc/clientStmt.h b/source/client/inc/clientStmt.h index 2b9fe4005f..219257ba74 100644 --- a/source/client/inc/clientStmt.h +++ b/source/client/inc/clientStmt.h @@ -51,6 +51,8 @@ typedef struct SStmtBindInfo { bool needParse; uint64_t tbUid; uint64_t tbSuid; + int32_t sBindRowNum; + int32_t sBindLastIdx; int8_t tbType; void* boundTags; char* tbName; @@ -77,6 +79,7 @@ typedef struct SStmtSQLInfo { typedef struct STscStmt { STscObj* taos; SCatalog* pCatalog; + int32_t affectedRows; SStmtSQLInfo sql; SStmtExecInfo exec; diff --git a/source/client/src/clientMain.c b/source/client/src/clientMain.c index 63ed4ff20f..1e6f7c4fa1 100644 --- a/source/client/src/clientMain.c +++ b/source/client/src/clientMain.c @@ -662,13 +662,13 @@ int taos_stmt_bind_single_param_batch(TAOS_STMT *stmt, TAOS_BIND_v2 *bind, int c return terrno; } - if (colIdx <= 0) { + if (colIdx < 0) { tscError("invalid bind column idx %d", colIdx); terrno = TSDB_CODE_INVALID_PARA; return terrno; } - return stmtBindBatch(stmt, bind, colIdx); /* TODO */ + return stmtBindBatch(stmt, bind, colIdx); } int taos_stmt_add_batch(TAOS_STMT *stmt) { diff --git a/source/client/src/clientStmt.c b/source/client/src/clientStmt.c index 987bf4fa4b..e6a47714d9 100644 --- a/source/client/src/clientStmt.c +++ b/source/client/src/clientStmt.c @@ -185,7 +185,7 @@ int32_t stmtCleanSQLInfo(STscStmt* pStmt) { void *pIter = taosHashIterate(pStmt->sql.pTableCache, NULL); while (pIter) { - SStmtTableCache* pCache = *(SStmtTableCache**)pIter; + SStmtTableCache* pCache = (SStmtTableCache*)pIter; qDestroyStmtDataBlock(pCache->pDataBlock); destroyBoundColumnInfo(pCache->boundTags); @@ -265,6 +265,20 @@ int32_t stmtGetFromCache(STscStmt* pStmt) { return TSDB_CODE_SUCCESS; } +int32_t stmtResetStmt(STscStmt* pStmt) { + STMT_ERR_RET(stmtCleanSQLInfo(pStmt)); + + pStmt->sql.pTableCache = taosHashInit(100, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), false, HASH_NO_LOCK); + if (NULL == pStmt->sql.pTableCache) { + terrno = TSDB_CODE_TSC_OUT_OF_MEMORY; + STMT_ERR_RET(terrno); + } + + pStmt->sql.status = STMT_INIT; + + return TSDB_CODE_SUCCESS; +} + TAOS_STMT *stmtInit(TAOS *taos) { STscObj* pObj = (STscObj*)taos; @@ -294,7 +308,7 @@ int stmtPrepare(TAOS_STMT *stmt, const char *sql, unsigned long length) { STscStmt* pStmt = (STscStmt*)stmt; if (pStmt->sql.status >= STMT_PREPARE) { - STMT_ERR_RET(stmtCleanSQLInfo(pStmt)); + STMT_ERR_RET(stmtResetStmt(pStmt)); } STMT_ERR_RET(stmtSwitchStatus(pStmt, STMT_PREPARE)); @@ -424,8 +438,23 @@ int stmtBindBatch(TAOS_STMT *stmt, TAOS_BIND_v2 *bind, int32_t colIdx) { tscError("table uid %" PRIx64 "not found in exec blockHash", pStmt->bInfo.tbUid); STMT_ERR_RET(TSDB_CODE_QRY_APP_ERROR); } - - qBindStmtColsValue(*pDataBlock, bind, pStmt->exec.pRequest->msgBuf, pStmt->exec.pRequest->msgBufLen); + + if (colIdx < 0) { + qBindStmtColsValue(*pDataBlock, bind, pStmt->exec.pRequest->msgBuf, pStmt->exec.pRequest->msgBufLen); + } else { + if (colIdx != (pStmt->bInfo.sBindLastIdx + 1) && colIdx != 0) { + tscError("bind column index not in sequence"); + STMT_ERR_RET(TSDB_CODE_QRY_APP_ERROR); + } + + pStmt->bInfo.sBindLastIdx = colIdx; + + if (0 == colIdx) { + pStmt->bInfo.sBindRowNum = bind->num; + } + + qBindStmtSingleColValue(*pDataBlock, bind, pStmt->exec.pRequest->msgBuf, pStmt->exec.pRequest->msgBufLen, colIdx, pStmt->bInfo.sBindRowNum); + } return TSDB_CODE_SUCCESS; } @@ -453,6 +482,8 @@ int stmtExec(TAOS_STMT *stmt) { STMT_ERR_JRET(pStmt->exec.pRequest->code); + pStmt->affectedRows += taos_affected_rows(pStmt->exec.pRequest); + _return: stmtCleanExecInfo(pStmt, (code ? false : true)); @@ -464,7 +495,9 @@ _return: int stmtClose(TAOS_STMT *stmt) { - return TSDB_CODE_SUCCESS; + STscStmt* pStmt = (STscStmt*)stmt; + + STMT_RET(stmtCleanSQLInfo(pStmt)); } const char *stmtErrstr(TAOS_STMT *stmt) { @@ -482,18 +515,24 @@ const char *stmtErrstr(TAOS_STMT *stmt) { } int stmtAffectedRows(TAOS_STMT *stmt) { - return TSDB_CODE_SUCCESS; + return ((STscStmt*)stmt)->affectedRows; } int stmtIsInsert(TAOS_STMT *stmt, int *insert) { STscStmt* pStmt = (STscStmt*)stmt; - *insert = (STMT_TYPE_INSERT == pStmt->sql.type || STMT_TYPE_MULTI_INSERT == pStmt->sql.type); + if (pStmt->sql.type) { + *insert = (STMT_TYPE_INSERT == pStmt->sql.type || STMT_TYPE_MULTI_INSERT == pStmt->sql.type); + } else { + *insert = isInsertSql(pStmt->sql.sqlStr, 0); + } return TSDB_CODE_SUCCESS; } int stmtGetParamNum(TAOS_STMT *stmt, int *nums) { + STMT_ERR_RET(stmtFetchColFields(stmt, nums, NULL)); + return TSDB_CODE_SUCCESS; } diff --git a/source/libs/parser/src/parInsert.c b/source/libs/parser/src/parInsert.c index b6d835846c..2a38552049 100644 --- a/source/libs/parser/src/parInsert.c +++ b/source/libs/parser/src/parInsert.c @@ -1318,7 +1318,8 @@ int32_t qBindStmtColsValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32 SRowBuilder* pBuilder = &pDataBlock->rowBuilder; SMemParam param = {.rb = pBuilder}; SMsgBuf pBuf = {.buf = msgBuf, .len = msgBufLen}; - + int32_t rowNum = bind->num; + CHECK_CODE(initRowBuilder(&pDataBlock->rowBuilder, pDataBlock->pTableMeta->sversion, &pDataBlock->boundColumnInfo)); CHECK_CODE(allocateMemForSize(pDataBlock, extendedRowSize * bind->num)); @@ -1330,6 +1331,14 @@ int32_t qBindStmtColsValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32 // 1. set the parsed value from sql string for (int c = 0; c < spd->numOfBound; ++c) { SSchema* pColSchema = &pSchema[spd->boundColumns[c] - 1]; + + if (bind[c].buffer_type != pColSchema->type) { + return buildInvalidOperationMsg(&pBuf, "column type mis-match with buffer type"); + } + + if (bind[c].num != rowNum) { + return buildInvalidOperationMsg(&pBuf, "row number in each bind param should be the same"); + } param.schema = pColSchema; getSTSRowAppendInfo(pBuilder->rowType, spd, c, ¶m.toffset, ¶m.colIdx); @@ -1367,7 +1376,7 @@ int32_t qBindStmtColsValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32 pDataBlock->size += extendedRowSize; } - + SSubmitBlk *pBlocks = (SSubmitBlk *)(pDataBlock->pData); if (TSDB_CODE_SUCCESS != setBlockInfo(pBlocks, pDataBlock, bind->num)) { return buildInvalidOperationMsg(&pBuf, "too many rows in sql, total number of rows should be less than 32767"); @@ -1376,18 +1385,100 @@ int32_t qBindStmtColsValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32 return TSDB_CODE_SUCCESS; } +int32_t qBindStmtSingleColValue(void *pBlock, TAOS_BIND_v2 *bind, char *msgBuf, int32_t msgBufLen, int32_t colIdx, int32_t rowNum) { + STableDataBlocks *pDataBlock = (STableDataBlocks *)pBlock; + SSchema* pSchema = getTableColumnSchema(pDataBlock->pTableMeta); + int32_t extendedRowSize = getExtendedRowSize(pDataBlock); + SParsedDataColInfo* spd = &pDataBlock->boundColumnInfo; + SRowBuilder* pBuilder = &pDataBlock->rowBuilder; + SMemParam param = {.rb = pBuilder}; + SMsgBuf pBuf = {.buf = msgBuf, .len = msgBufLen}; + bool rowStart = (0 == colIdx); + bool rowEnd = ((colIdx + 1) == spd->numOfBound); -int32_t buildBoundFields(SParsedDataColInfo *boundInfo, SSchema *pSchema, int32_t *fieldNum, TAOS_FIELD** fields) { - *fields = taosMemoryCalloc(boundInfo->numOfBound, sizeof(TAOS_FIELD)); - if (NULL == *fields) { - return TSDB_CODE_OUT_OF_MEMORY; + if (rowStart) { + CHECK_CODE(initRowBuilder(&pDataBlock->rowBuilder, pDataBlock->pTableMeta->sversion, &pDataBlock->boundColumnInfo)); + CHECK_CODE(allocateMemForSize(pDataBlock, extendedRowSize * bind->num)); + } + + for (int32_t r = 0; r < bind->num; ++r) { + STSRow* row = (STSRow*)(pDataBlock->pData + pDataBlock->size + extendedRowSize * r); // skip the SSubmitBlk header + if (rowStart) { + tdSRowResetBuf(pBuilder, row); + } else { + tdSRowGetBuf(pBuilder, row); + } + + SSchema* pColSchema = &pSchema[spd->boundColumns[colIdx] - 1]; + + if (bind->buffer_type != pColSchema->type) { + return buildInvalidOperationMsg(&pBuf, "column type mis-match with buffer type"); + } + + if (bind->num != rowNum) { + return buildInvalidOperationMsg(&pBuf, "row number in each bind param should be the same"); + } + + param.schema = pColSchema; + getSTSRowAppendInfo(pBuilder->rowType, spd, colIdx, ¶m.toffset, ¶m.colIdx); + + if (bind->is_null && bind->is_null[r]) { + if (pColSchema->colId == PRIMARYKEY_TIMESTAMP_COL_ID) { + return buildInvalidOperationMsg(&pBuf, "primary timestamp should not be NULL"); + } + + CHECK_CODE(MemRowAppend(&pBuf, NULL, 0, ¶m)); + } else { + int32_t colLen = pColSchema->bytes; + if (IS_VAR_DATA_TYPE(pColSchema->type)) { + colLen = bind->length[r]; + } + + CHECK_CODE(MemRowAppend(&pBuf, (char *)bind->buffer + bind->buffer_length * r, colLen, ¶m)); + } + + if (PRIMARYKEY_TIMESTAMP_COL_ID == pColSchema->colId) { + TSKEY tsKey = TD_ROW_KEY(row); + checkTimestamp(pDataBlock, (const char *)&tsKey); + } + + // set the null value for the columns that do not assign values + if (rowEnd && (spd->numOfBound < spd->numOfCols) && TD_IS_TP_ROW(row)) { + for (int32_t i = 0; i < spd->numOfCols; ++i) { + if (spd->cols[i].valStat == VAL_STAT_NONE) { // the primary TS key is not VAL_STAT_NONE + tdAppendColValToTpRow(pBuilder, TD_VTYPE_NONE, getNullValue(pSchema[i].type), true, pSchema[i].type, i, + spd->cols[i].toffset); + } + } + } } - for (int32_t i = 0; i < boundInfo->numOfBound; ++i) { - SSchema* pTagSchema = &pSchema[boundInfo->boundColumns[i] - 1]; - strcpy((*fields)[i].name, pTagSchema->name); - (*fields)[i].type = pTagSchema->type; - (*fields)[i].bytes = pTagSchema->bytes; + if (rowEnd) { + pDataBlock->size += extendedRowSize * bind->num; + + SSubmitBlk *pBlocks = (SSubmitBlk *)(pDataBlock->pData); + if (TSDB_CODE_SUCCESS != setBlockInfo(pBlocks, pDataBlock, bind->num)) { + return buildInvalidOperationMsg(&pBuf, "too many rows in sql, total number of rows should be less than 32767"); + } + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t buildBoundFields(SParsedDataColInfo *boundInfo, SSchema *pSchema, int32_t *fieldNum, TAOS_FIELD** fields) { + if (fields) { + *fields = taosMemoryCalloc(boundInfo->numOfBound, sizeof(TAOS_FIELD)); + if (NULL == *fields) { + return TSDB_CODE_OUT_OF_MEMORY; + } + + for (int32_t i = 0; i < boundInfo->numOfBound; ++i) { + SSchema* pTagSchema = &pSchema[boundInfo->boundColumns[i] - 1]; + strcpy((*fields)[i].name, pTagSchema->name); + (*fields)[i].type = pTagSchema->type; + (*fields)[i].bytes = pTagSchema->bytes; + } } *fieldNum = boundInfo->numOfBound; @@ -1421,7 +1512,9 @@ int32_t qBuildStmtColFields(void *pBlock, int32_t *fieldNum, TAOS_FIELD** fields SSchema* pSchema = getTableColumnSchema(pDataBlock->pTableMeta); if (pDataBlock->boundColumnInfo.numOfBound <= 0) { *fieldNum = 0; - *fields = NULL; + if (fields) { + *fields = NULL; + } return TSDB_CODE_SUCCESS; } diff --git a/source/libs/parser/src/parser.c b/source/libs/parser/src/parser.c index 1674ef0ace..e2e2207c70 100644 --- a/source/libs/parser/src/parser.c +++ b/source/libs/parser/src/parser.c @@ -18,7 +18,11 @@ #include "parInt.h" #include "parToken.h" -static bool isInsertSql(const char* pStr, size_t length) { +bool isInsertSql(const char* pStr, size_t length) { + if (NULL == pStr) { + return false; + } + int32_t index = 0; do { diff --git a/tests/script/api/batchprepare.c b/tests/script/api/batchprepare.c index 2bbb563d56..5a0610400c 100644 --- a/tests/script/api/batchprepare.c +++ b/tests/script/api/batchprepare.c @@ -34,15 +34,20 @@ typedef struct { int32_t gVarCharSize = 10; int32_t gVarCharLen = 5; +int32_t gExecLoopTimes = 1; // no change + + int insertMBSETest1(TAOS_STMT *stmt); int insertMBSETest2(TAOS_STMT *stmt); int insertMBMETest1(TAOS_STMT *stmt); int insertMBMETest2(TAOS_STMT *stmt); int insertMBMETest3(TAOS_STMT *stmt); int insertMBMETest4(TAOS_STMT *stmt); +int insertMPMETest1(TAOS_STMT *stmt); int32_t shortColList[] = {TSDB_DATA_TYPE_TIMESTAMP, TSDB_DATA_TYPE_INT}; int32_t longColList[] = {TSDB_DATA_TYPE_TIMESTAMP, TSDB_DATA_TYPE_BOOL, TSDB_DATA_TYPE_TINYINT, TSDB_DATA_TYPE_UTINYINT, TSDB_DATA_TYPE_SMALLINT, TSDB_DATA_TYPE_USMALLINT, TSDB_DATA_TYPE_INT, TSDB_DATA_TYPE_UINT, TSDB_DATA_TYPE_BIGINT, TSDB_DATA_TYPE_UBIGINT, TSDB_DATA_TYPE_FLOAT, TSDB_DATA_TYPE_DOUBLE, TSDB_DATA_TYPE_BINARY, TSDB_DATA_TYPE_NCHAR}; +int32_t bindColTypeList[] = {TSDB_DATA_TYPE_TIMESTAMP, TSDB_DATA_TYPE_FLOAT}; #define tListLen(x) (sizeof(x) / sizeof((x)[0])) @@ -50,7 +55,7 @@ int32_t longColList[] = {TSDB_DATA_TYPE_TIMESTAMP, TSDB_DATA_TYPE_BOOL, TSDB_DAT typedef struct { char caseDesc[128]; int32_t colNum; - int32_t *colList; + int32_t *colList; // full table column list bool autoCreate; bool fullCol; int32_t (*runFn)(TAOS_STMT*); @@ -66,7 +71,7 @@ CaseCfg gCase[] = { #if 0 {"insert:MBSE1-FULL", tListLen(shortColList), shortColList, false, true, insertMBSETest1, 1, 10, 10, 0, 0, 10}, {"insert:MBSE1-FULL", tListLen(shortColList), shortColList, false, true, insertMBSETest1, 10, 100, 10, 0, 0, 10}, - + {"insert:MBSE1-FULL", tListLen(longColList), longColList, false, true, insertMBSETest1, 10, 10, 2, 0, 0, 1}, {"insert:MBSE1-C012", tListLen(longColList), longColList, false, false, insertMBSETest1, 10, 10, 2, 12, 0, 1}, {"insert:MBSE1-C002", tListLen(longColList), longColList, false, false, insertMBSETest1, 10, 10, 2, 2, 0, 1}, @@ -86,17 +91,39 @@ CaseCfg gCase[] = { {"insert:MBME3-FULL", tListLen(longColList), longColList, false, true, insertMBMETest3, 10, 10, 2, 0, 0, 1}, {"insert:MBME3-C012", tListLen(longColList), longColList, false, false, insertMBMETest3, 10, 10, 2, 12, 0, 1}, {"insert:MBME3-C002", tListLen(longColList), longColList, false, false, insertMBMETest3, 10, 10, 2, 2, 0, 1}, + + {"insert:MBME4-FULL", tListLen(longColList), longColList, false, true, insertMBMETest4, 10, 10, 2, 0, 0, 1}, + {"insert:MBME4-C012", tListLen(longColList), longColList, false, false, insertMBMETest4, 10, 10, 2, 12, 0, 1}, + {"insert:MBME4-C002", tListLen(longColList), longColList, false, false, insertMBMETest4, 10, 10, 2, 2, 0, 1}, #endif -// {"insert:MBME4-FULL", tListLen(longColList), longColList, false, true, insertMBMETest4, 10, 10, 2, 0, 0, 1}, - {"insert:MBME4-C012", tListLen(longColList), longColList, false, false, insertMBMETest4, 10, 10, 2, 12, 0, 1}, -// {"insert:MBME4-C002", tListLen(longColList), longColList, false, false, insertMBMETest4, 10, 10, 2, 2, 0, 1}, + {"insert:MPME1-C012", tListLen(longColList), longColList, false, false, insertMPMETest1, 10, 10, 2, 12, 0, 1}, }; CaseCfg *gCurCase = NULL; -int32_t gTestNull = 1; -bool gTestAutoCreate = false; + +typedef struct { + int32_t bindNullNum; + bool autoCreate; + bool checkParamNum; + int32_t bindColNum; + int32_t bindRowNum; + int32_t bindColTypeNum; + int32_t* bindColTypeList; +} CaseCtrl; + +CaseCtrl gCaseCtrl = { + .bindNullNum = 0, + .autoCreate = false, + .bindColNum = 0, + .bindRowNum = 0, + .bindColTypeNum = 0, + .bindColTypeList = NULL, + .checkParamNum = false, +// .bindColTypeNum = tListLen(bindColTypeList), +// .bindColTypeList = bindColTypeList, +}; int32_t taosGetTimeOfDay(struct timeval *tv) { return gettimeofday(tv, NULL); @@ -217,6 +244,9 @@ void generateDataType(BindData *data, int32_t bindIdx, int32_t colIdx, int32_t * if (gCurCase->fullCol) { *dataType = gCurCase->colList[bindIdx]; return; + } else if (gCaseCtrl.bindColTypeNum) { + *dataType = gCaseCtrl.bindColTypeList[colIdx]; + return; } else if (0 == colIdx) { *dataType = TSDB_DATA_TYPE_TIMESTAMP; return; @@ -251,49 +281,49 @@ int32_t prepareColData(BindData *data, int32_t bindIdx, int32_t rowIdx, int32_t data->pBind[bindIdx].buffer_length = sizeof(bool); data->pBind[bindIdx].buffer = data->boolData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_TINYINT: data->pBind[bindIdx].buffer_length = sizeof(int8_t); data->pBind[bindIdx].buffer = data->tinyData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_SMALLINT: data->pBind[bindIdx].buffer_length = sizeof(int16_t); data->pBind[bindIdx].buffer = data->smallData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_INT: data->pBind[bindIdx].buffer_length = sizeof(int32_t); data->pBind[bindIdx].buffer = data->intData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_BIGINT: data->pBind[bindIdx].buffer_length = sizeof(int64_t); data->pBind[bindIdx].buffer = data->bigData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_FLOAT: data->pBind[bindIdx].buffer_length = sizeof(float); data->pBind[bindIdx].buffer = data->floatData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_DOUBLE: data->pBind[bindIdx].buffer_length = sizeof(double); data->pBind[bindIdx].buffer = data->doubleData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_VARCHAR: data->pBind[bindIdx].buffer_length = gVarCharSize; data->pBind[bindIdx].buffer = data->binaryData + rowIdx * gVarCharSize; data->pBind[bindIdx].length = data->binaryLen; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_TIMESTAMP: data->pBind[bindIdx].buffer_length = sizeof(int64_t); @@ -305,31 +335,31 @@ int32_t prepareColData(BindData *data, int32_t bindIdx, int32_t rowIdx, int32_t data->pBind[bindIdx].buffer_length = gVarCharSize; data->pBind[bindIdx].buffer = data->binaryData + rowIdx * gVarCharSize; data->pBind[bindIdx].length = data->binaryLen; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_UTINYINT: data->pBind[bindIdx].buffer_length = sizeof(uint8_t); data->pBind[bindIdx].buffer = data->utinyData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_USMALLINT: data->pBind[bindIdx].buffer_length = sizeof(uint16_t); data->pBind[bindIdx].buffer = data->usmallData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_UINT: data->pBind[bindIdx].buffer_length = sizeof(uint32_t); data->pBind[bindIdx].buffer = data->uintData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; case TSDB_DATA_TYPE_UBIGINT: data->pBind[bindIdx].buffer_length = sizeof(uint64_t); data->pBind[bindIdx].buffer = data->ubigData + rowIdx; data->pBind[bindIdx].length = NULL; - data->pBind[bindIdx].is_null = data->isNull + rowIdx; + data->pBind[bindIdx].is_null = data->isNull ? (data->isNull + rowIdx) : NULL; break; default: printf("invalid col type:%d", dataType); @@ -417,6 +447,69 @@ void destroyData(BindData *data) { taosMemoryFree(data->binaryLen); taosMemoryFree(data->isNull); taosMemoryFree(data->pBind); + taosMemoryFree(data->colTypes); +} + +int32_t bpBindParam(TAOS_STMT *stmt, TAOS_BIND_v2 *bind) { + static int32_t n = 0; + + if (gCurCase->bindRowNum > 1) { + if (0 == (n++%2)) { + if (taos_stmt_bind_param_batch(stmt, bind)) { + printf("taos_stmt_bind_param_batch error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } else { + for (int32_t i = 0; i < gCurCase->bindColNum; ++i) { + if (taos_stmt_bind_single_param_batch(stmt, bind++, i)) { + printf("taos_stmt_bind_single_param_batch error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } + } + } else { + if (taos_stmt_bind_param(stmt, bind)) { + printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } + + return 0; +} + +void bpCheckIsInsert(TAOS_STMT *stmt) { + int32_t isInsert = 0; + if (taos_stmt_is_insert(stmt, &isInsert)) { + printf("taos_stmt_is_insert error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + + if (0 == isInsert) { + printf("is insert failed\n"); + exit(1); + } +} + +void bpCheckParamNum(TAOS_STMT *stmt) { + int32_t num = 0; + if (taos_stmt_num_params(stmt, &num)) { + printf("taos_stmt_num_params error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + + if (gCurCase->bindColNum != num) { + printf("is insert failed\n"); + exit(1); + } +} + +void bpCheckAffectedRows(TAOS_STMT *stmt, int32_t times) { + int32_t rows = taos_stmt_affected_rows(stmt); + int32_t insertNum = gCurCase->rowNum * gCurCase->tblNum * times; + if (insertNum != rows) { + printf("affected rows %d mis-match with insert num %d\n", rows, insertNum); + exit(1); + } } @@ -433,6 +526,8 @@ int insertMBSETest1(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t t = 0; t< gCurCase->tblNum; ++t) { if (gCurCase->tblNum > 1) { @@ -444,10 +539,13 @@ int insertMBSETest1(TAOS_STMT *stmt) { exit(1); } } + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } @@ -463,6 +561,11 @@ int insertMBSETest1(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); + return 0; } @@ -480,6 +583,8 @@ int insertMBSETest2(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } - + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } + if (taos_stmt_add_batch(stmt)) { printf("taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); exit(1); @@ -511,6 +619,11 @@ int insertMBSETest2(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); + return 0; } @@ -527,6 +640,8 @@ int insertMBMETest1(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t t = 0; t< gCurCase->tblNum; ++t) { if (gCurCase->tblNum > 1) { @@ -538,10 +653,13 @@ int insertMBMETest1(TAOS_STMT *stmt) { exit(1); } } + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } @@ -557,6 +675,10 @@ int insertMBMETest1(TAOS_STMT *stmt) { } } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); return 0; } @@ -574,6 +696,8 @@ int insertMBMETest2(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t t = 0; t< gCurCase->tblNum; ++t) { if (gCurCase->tblNum > 1) { @@ -587,11 +711,14 @@ int insertMBMETest2(TAOS_STMT *stmt) { } for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } - + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } + if (taos_stmt_add_batch(stmt)) { printf("taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); exit(1); @@ -604,6 +731,10 @@ int insertMBMETest2(TAOS_STMT *stmt) { } } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); return 0; } @@ -621,6 +752,8 @@ int insertMBMETest3(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t t = 0; t< gCurCase->tblNum; ++t) { if (gCurCase->tblNum > 1) { @@ -632,6 +765,10 @@ int insertMBMETest3(TAOS_STMT *stmt) { exit(1); } } + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } for (int32_t b = 0; b tblNum > 1) { @@ -644,8 +781,7 @@ int insertMBMETest3(TAOS_STMT *stmt) { } } - if (taos_stmt_bind_param_batch(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } @@ -661,6 +797,10 @@ int insertMBMETest3(TAOS_STMT *stmt) { } } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); return 0; } @@ -679,6 +819,8 @@ int insertMBMETest4(TAOS_STMT *stmt) { exit(1); } + bpCheckIsInsert(stmt); + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { - printf("taos_stmt_bind_param error:%s\n", taos_stmt_errstr(stmt)); + if (bpBindParam(stmt, data.pBind + t*bindTimes*gCurCase->bindColNum + b*gCurCase->bindColNum)) { exit(1); } - + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } + if (taos_stmt_add_batch(stmt)) { printf("taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); exit(1); @@ -710,9 +855,81 @@ int insertMBMETest4(TAOS_STMT *stmt) { } } + bpCheckIsInsert(stmt); + bpCheckAffectedRows(stmt, 1); + + destroyData(&data); + return 0; } +/* [prepare [settbname [bind add] exec]] */ +int insertMPMETest1(TAOS_STMT *stmt) { + int32_t loop = 0; + + while (gCurCase->bindColNum >= 2) { + BindData data = {0}; + prepareData(&data); + + printf("SQL: %s\n", data.sql); + + int code = taos_stmt_prepare(stmt, data.sql, 0); + if (code != 0){ + printf("failed to execute taos_stmt_prepare. error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + + bpCheckIsInsert(stmt); + + int32_t bindTimes = gCurCase->rowNum/gCurCase->bindRowNum; + for (int32_t t = 0; t< gCurCase->tblNum; ++t) { + if (gCurCase->tblNum > 1) { + char buf[32]; + sprintf(buf, "t%d", t); + code = taos_stmt_set_tbname(stmt, buf); + if (code != 0){ + printf("taos_stmt_set_tbname error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } + + if (gCaseCtrl.checkParamNum) { + bpCheckParamNum(stmt); + } + + for (int32_t b = 0; b bindColNum + b*gCurCase->bindColNum)) { + exit(1); + } + + if (taos_stmt_add_batch(stmt)) { + printf("taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } + + if (taos_stmt_execute(stmt) != 0) { + printf("taos_stmt_execute error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } + } + + bpCheckIsInsert(stmt); + + destroyData(&data); + + gCurCase->bindColNum -= 2; + loop++; + } + + bpCheckAffectedRows(stmt, loop); + + gExecLoopTimes = loop; + + return 0; +} + + #if 0 int stmt_func1(TAOS_STMT *stmt) { struct { @@ -4015,7 +4232,7 @@ void prepareCheckResult(TAOS *taos) { sprintf(buf, "t%d", 0); } - prepareCheckResultImpl(taos, buf, 1, gCurCase->rowNum); + prepareCheckResultImpl(taos, buf, 1, gCurCase->rowNum * gExecLoopTimes); } } @@ -4347,8 +4564,17 @@ void* runcase(TAOS *taos) { gCurCase->bindColNum = gCurCase->colNum; } - gCurCase->bindNullNum = gTestNull; - gCurCase->autoCreate = gTestAutoCreate; + gCurCase->bindNullNum = gCaseCtrl.bindNullNum; + gCurCase->autoCreate = gCaseCtrl.autoCreate; + if (gCaseCtrl.bindColNum) { + gCurCase->bindColNum = gCaseCtrl.bindColNum; + } + if (gCaseCtrl.bindRowNum) { + gCurCase->bindRowNum = gCaseCtrl.bindRowNum; + } + if (gCaseCtrl.bindColTypeNum) { + gCurCase->bindRowNum = gCaseCtrl.bindColTypeNum; + } for (int32_t n = 0; n < gCurCase->runTimes; ++n) { prepare(taos, gCurCase->colNum, gCurCase->colList, gCurCase->autoCreate); @@ -4362,6 +4588,8 @@ void* runcase(TAOS *taos) { (*gCurCase->runFn)(stmt); prepareCheckResult(taos); + + taos_stmt_close(stmt); } printf("* Case %d - %s End *\n", i, gCurCase->caseDesc);