diff --git a/src/client/src/tscSQLParser.c b/src/client/src/tscSQLParser.c index a6298e631ff98bb48c98f0bb460c0453eb81ac74..e83976ac902c8461599e49c2d8b550188f811774 100644 --- a/src/client/src/tscSQLParser.c +++ b/src/client/src/tscSQLParser.c @@ -253,6 +253,11 @@ int32_t readFromFile(char *name, uint32_t *len, void **buf) { *len = fileStat.st_size; + if (*len <= 0) { + tscError("file %s is empty", name); + return TSDB_CODE_TSC_FILE_EMPTY; + } + *buf = calloc(1, *len); if (*buf == NULL) { return TSDB_CODE_TSC_OUT_OF_MEMORY; @@ -277,18 +282,22 @@ int32_t readFromFile(char *name, uint32_t *len, void **buf) { } -int32_t handleCreateFunc(SSqlObj* pSql, struct SSqlInfo* pInfo) { +int32_t handleUserDefinedFunc(SSqlObj* pSql, struct SSqlInfo* pInfo) { const char *msg1 = "function name is too long"; const char *msg2 = "path is too long"; + const char *msg3 = "invalid outputtype"; SSqlCmd *pCmd = &pSql->cmd; switch (pInfo->type) { case TSDB_SQL_CREATE_FUNCTION: { SCreateFuncInfo *createInfo = &pInfo->pMiscInfo->funcOpt; - SCreateFuncMsg *pMsg = (SCreateFuncMsg *)pSql->cmd.payload; uint32_t len = 0; void *buf = NULL; + if (createInfo->output.type == (uint8_t)-1 || createInfo->output.bytes < 0) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3); + } + createInfo->name.z[createInfo->name.n] = 0; strdequote(createInfo->name.z); @@ -297,8 +306,6 @@ int32_t handleCreateFunc(SSqlObj* pSql, struct SSqlInfo* pInfo) { return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); } - strcpy(pMsg->name, createInfo->name.z); - createInfo->path.z[createInfo->path.n] = 0; strdequote(createInfo->path.z); @@ -307,25 +314,35 @@ int32_t handleCreateFunc(SSqlObj* pSql, struct SSqlInfo* pInfo) { return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2); } - strcpy(pMsg->path, createInfo->path.z); - int32_t ret = readFromFile(createInfo->path.z, &len, &buf); if (ret) { return ret; } + + //TODO CHECK CODE if (len + sizeof(SCreateFuncMsg) > pSql->cmd.allocSize) { ret = tscAllocPayload(&pSql->cmd, len + sizeof(SCreateFuncMsg)); if (ret) { + tfree(buf); return ret; } } + SCreateFuncMsg *pMsg = (SCreateFuncMsg *)pSql->cmd.payload; + + strcpy(pMsg->name, createInfo->name.z); + strcpy(pMsg->path, createInfo->path.z); + + pMsg->outputType = createInfo->output.type; + pMsg->outputLen = htons(createInfo->output.bytes); + pMsg->codeLen = htonl(len); memcpy(pMsg->code, buf, len); + tfree(buf); break; } @@ -467,7 +484,7 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) { case TSDB_SQL_CREATE_FUNCTION: case TSDB_SQL_DROP_FUNCTION: { - code = handleCreateFunc(pSql, pInfo); + code = handleUserDefinedFunc(pSql, pInfo); if (code != TSDB_CODE_SUCCESS) { return code; } diff --git a/src/inc/taosdef.h b/src/inc/taosdef.h index 2870819e1b321752dfb1b637d694a40e478fe8ae..97a668202c6f10732313caf0b8501836764ec999 100644 --- a/src/inc/taosdef.h +++ b/src/inc/taosdef.h @@ -185,6 +185,7 @@ do { \ #define TSDB_DB_NAME_LEN 33 #define TSDB_FUNC_NAME_LEN 128 #define TSDB_FUNC_CODE_LEN (4096 - 512) +#define TSDB_TYPE_STR_MAX_LEN 32 #define TSDB_TABLE_FNAME_LEN (TSDB_ACCT_ID_LEN + TSDB_DB_NAME_LEN + TSDB_TABLE_NAME_LEN) #define TSDB_COL_NAME_LEN 65 #define TSDB_MAX_SAVED_SQL_LEN TSDB_MAX_COLUMNS * 64 diff --git a/src/inc/taoserror.h b/src/inc/taoserror.h index 20984b5c7f9cbc002edaf3279885aff3a7181b75..e4e199de947d8b5d3d6630c22ddc5ff7a373fecb 100644 --- a/src/inc/taoserror.h +++ b/src/inc/taoserror.h @@ -434,3 +434,4 @@ int32_t* taosGetErrno(); #endif #endif //TDENGINE_TAOSERROR_H + \ No newline at end of file diff --git a/src/inc/taosmsg.h b/src/inc/taosmsg.h index 990b7c6af30740b75013fb214428151549561139..bdbb364e7b7fea8b4331b5861bbfbf4d8aa216a4 100644 --- a/src/inc/taosmsg.h +++ b/src/inc/taosmsg.h @@ -577,6 +577,8 @@ typedef struct { typedef struct { char name[TSDB_FUNC_NAME_LEN]; char path[PATH_MAX]; + uint8_t outputType; + int16_t outputLen; int32_t codeLen; char code[]; } SCreateFuncMsg; diff --git a/src/mnode/inc/mnodeDef.h b/src/mnode/inc/mnodeDef.h index 0a6f66c44b020216d3583f19abb52f16f03cb72d..f9a5ccbcf6e3da52e5899238d2da5878e739cab1 100644 --- a/src/mnode/inc/mnodeDef.h +++ b/src/mnode/inc/mnodeDef.h @@ -220,6 +220,8 @@ typedef struct SFuncObj { int32_t codeLen; char code[TSDB_FUNC_CODE_LEN]; int64_t createdTime; + uint8_t outputType; + int16_t outputLen; int8_t reserved[64]; int8_t updateEnd[4]; int32_t refCount; diff --git a/src/mnode/inc/mnodeFunc.h b/src/mnode/inc/mnodeFunc.h index 4cf2c16936d91b2c970e3eccbc7e9d3b5cf675db..3bf8473e23607c9d59619d382140567d1968b80d 100644 --- a/src/mnode/inc/mnodeFunc.h +++ b/src/mnode/inc/mnodeFunc.h @@ -31,7 +31,7 @@ void mnodeCancelGetNextFunc(void *pIter); void mnodeIncFuncRef(SFuncObj *pFunc); void mnodeDecFuncRef(SFuncObj *pFunc); -int32_t mnodeCreateFunc(SAcctObj *pAcct, char *name, int32_t codeLen, char *code, char *path, SMnodeMsg *pMsg); +int32_t mnodeCreateFunc(SAcctObj *pAcct, char *name, int32_t codeLen, char *code, char *path, uint8_t outputType, int16_t outputLen, SMnodeMsg *pMsg); #ifdef __cplusplus } diff --git a/src/mnode/src/mnodeFunc.c b/src/mnode/src/mnodeFunc.c index 47e45871cff508746c17a75401d1642e7e35a258..08e30f8013d5759da09a020e60d6cf795dad5682 100644 --- a/src/mnode/src/mnodeFunc.c +++ b/src/mnode/src/mnodeFunc.c @@ -186,7 +186,7 @@ static int32_t mnodeUpdateFunc(SFuncObj *pFunc, void *pMsg) { return code; } */ -int32_t mnodeCreateFunc(SAcctObj *pAcct, char *name, int32_t codeLen, char *codeScript, char *path, SMnodeMsg *pMsg) { +int32_t mnodeCreateFunc(SAcctObj *pAcct, char *name, int32_t codeLen, char *codeScript, char *path, uint8_t outputType, int16_t outputLen, SMnodeMsg *pMsg) { if (grantCheck(TSDB_GRANT_TIME) != TSDB_CODE_SUCCESS) { return TSDB_CODE_GRANT_EXPIRED; } @@ -230,6 +230,8 @@ int32_t mnodeCreateFunc(SAcctObj *pAcct, char *name, int32_t codeLen, char *code tstrncpy(pFunc->code, codeScript, TSDB_FUNC_CODE_LEN); pFunc->codeLen = codeLen; pFunc->createdTime = taosGetTimestampMs(); + pFunc->outputType = outputType; + pFunc->outputLen = outputLen; SSdbRow row = { .type = SDB_OPER_GLOBAL, @@ -293,6 +295,12 @@ static int32_t mnodeGetFuncMeta(STableMetaMsg *pMeta, SShowObj *pShow, void *pCo pSchema[cols].bytes = htons(pShow->bytes[cols]); cols++; + pShow->bytes[cols] = TSDB_TYPE_STR_MAX_LEN + VARSTR_HEADER_SIZE; + pSchema[cols].type = TSDB_DATA_TYPE_BINARY; + strcpy(pSchema[cols].name, "outputtype"); + pSchema[cols].bytes = htons(pShow->bytes[cols]); + cols++; + pShow->bytes[cols] = 8; pSchema[cols].type = TSDB_DATA_TYPE_TIMESTAMP; strcpy(pSchema[cols].name, "create_time"); @@ -328,11 +336,30 @@ static int32_t mnodeGetFuncMeta(STableMetaMsg *pMeta, SShowObj *pShow, void *pCo return 0; } +static void* mnodeGenTypeStr(char *buf, int32_t buflen, uint8_t type, int16_t len) { + char *msg = "unknown"; + if (type >= sizeof(tDataTypes)/sizeof(tDataTypes[0])) { + return msg; + } + + if (type == TSDB_DATA_TYPE_NCHAR || type == TSDB_DATA_TYPE_BINARY) { + int32_t bytes = len > 0 ? (int)(len - VARSTR_HEADER_SIZE) : len; + + snprintf(buf, buflen - 1, "%s(%d)", tDataTypes[type].name, type == TSDB_DATA_TYPE_NCHAR ? bytes/4 : bytes); + buf[buflen - 1] = 0; + + return buf; + } + + return tDataTypes[type].name; +} + static int32_t mnodeRetrieveFuncs(SShowObj *pShow, char *data, int32_t rows, void *pConn) { int32_t numOfRows = 0; SFuncObj *pFunc = NULL; int32_t cols = 0; char *pWrite; + char buf[TSDB_TYPE_STR_MAX_LEN]; while (numOfRows < rows) { pShow->pIter = mnodeGetNextFunc(pShow->pIter, &pFunc); @@ -348,6 +375,10 @@ static int32_t mnodeRetrieveFuncs(SShowObj *pShow, char *data, int32_t rows, voi STR_WITH_MAXSIZE_TO_VARSTR(pWrite, pFunc->path, pShow->bytes[cols]); cols++; + pWrite = data + pShow->offset[cols] * rows + pShow->bytes[cols] * numOfRows; + STR_WITH_MAXSIZE_TO_VARSTR(pWrite, mnodeGenTypeStr(buf, TSDB_TYPE_STR_MAX_LEN, pFunc->outputType, pFunc->outputLen), pShow->bytes[cols]); + cols++; + pWrite = data + pShow->offset[cols] * rows + pShow->bytes[cols] * numOfRows; *(int64_t *)pWrite = pFunc->createdTime; cols++; @@ -372,8 +403,9 @@ static int32_t mnodeRetrieveFuncs(SShowObj *pShow, char *data, int32_t rows, voi static int32_t mnodeProcessCreateFuncMsg(SMnodeMsg *pMsg) { SCreateFuncMsg *pCreate = pMsg->rpcMsg.pCont; pCreate->codeLen = htonl(pCreate->codeLen); + pCreate->outputLen = htons(pCreate->outputLen); - return mnodeCreateFunc(pMsg->pUser->pAcct, pCreate->name, pCreate->codeLen, pCreate->code, pCreate->path, pMsg); + return mnodeCreateFunc(pMsg->pUser->pAcct, pCreate->name, pCreate->codeLen, pCreate->code, pCreate->path, pCreate->outputType, pCreate->outputLen, pMsg); } static int32_t mnodeProcessDropFuncMsg(SMnodeMsg *pMsg) { diff --git a/src/query/src/qExecutor.c b/src/query/src/qExecutor.c index 4b3cd86c0799a2cf875efe0e475416333fdce432..b57a578f2a3215ef89632b73c8c242d214eab05e 100644 --- a/src/query/src/qExecutor.c +++ b/src/query/src/qExecutor.c @@ -99,7 +99,7 @@ static UNUSED_FUNC void* u_realloc(void* p, size_t __size) { #define QUERY_IS_INTERVAL_QUERY(_q) ((_q)->interval.interval > 0) uint64_t queryHandleId = 0; -` + int32_t getMaximumIdleDurationSec() { return tsShellActivityTimer * 2; } @@ -6093,7 +6093,7 @@ FORCE_INLINE bool checkQIdEqual(void *qHandle, uint64_t qId) { SQInfo* createQInfoImpl(SQueryTableMsg* pQueryMsg, SSqlGroupbyExpr* pGroupbyExpr, SExprInfo* pExprs, SExprInfo* pSecExprs, STableGroupInfo* pTableGroupInfo, SColumnInfo* pTagCols, bool stableQuery, - char* sql) { + char* sql, uint64_t *qId) { int16_t numOfCols = pQueryMsg->numOfCols; int16_t numOfOutput = pQueryMsg->numOfOutput; diff --git a/src/util/src/terror.c b/src/util/src/terror.c index 0c88c9fc8bc35f337ba865ad8c6f9814d937cd99..8bf8a7f85a8a724270220e3968a4cc37dbc58745 100644 --- a/src/util/src/terror.c +++ b/src/util/src/terror.c @@ -112,6 +112,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_TSC_SQL_SYNTAX_ERROR, "Syntax error in SQL") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_DB_NOT_SELECTED, "Database not specified or available") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_TABLE_NAME, "Table does not exist") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_EXCEED_SQL_LIMIT, "SQL statement too long, check maxSQLLength config") +TAOS_DEFINE_ERROR(TSDB_CODE_TSC_FILE_EMPTY, "File is empty") // mnode TAOS_DEFINE_ERROR(TSDB_CODE_MND_MSG_NOT_PROCESSED, "Message not processed") diff --git a/tests/script/general/parser/udf.sim b/tests/script/general/parser/udf.sim new file mode 100644 index 0000000000000000000000000000000000000000..a1fa19b0df04f205f00ebb4915082c6aa27583cc --- /dev/null +++ b/tests/script/general/parser/udf.sim @@ -0,0 +1,609 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/cfg.sh -n dnode1 -c walLevel -v 1 +system sh/cfg.sh -n dnode1 -c maxtablesPerVnode -v 2 +system sh/exec.sh -n dnode1 -s start +system sh/prepare_udf.sh + +sleep 100 +sql connect +print ======================== dnode1 start + +sql create function n01 as '/tmp/normal' outputtype int; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n01 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != INT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n01; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n02 as '/tmp/normal' outputtype bool; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n02 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != BOOL then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n02; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + +sql create function n03 as '/tmp/normal' outputtype TINYINT; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n03 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != TINYINT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n03; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + +sql create function n04 as '/tmp/normal' outputtype SMALLINT; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n04 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != SMALLINT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n04; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + + +sql create function n05 as '/tmp/normal' outputtype INT; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n05 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != INT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n05; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + + + + + + +sql create function n06 as '/tmp/normal' outputtype BIGINT; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n06 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != BIGINT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n06; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + + + + +sql create function n07 as '/tmp/normal' outputtype FLOAT; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n07 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != FLOAT then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n07; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + + + +sql create function n08 as '/tmp/normal' outputtype DOUBLE; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n08 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != DOUBLE then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n08; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + + + + +sql create function n09 as '/tmp/normal' outputtype BINARY; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n09 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != BINARY(0) then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n09; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n10 as '/tmp/normal' outputtype BINARY(10); +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n10 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != BINARY(10) then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n10; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n11 as '/tmp/normal' outputtype TIMESTAMP; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n11 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != TIMESTAMP then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n11; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n12 as '/tmp/normal' outputtype NCHAR; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n12 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != NCHAR(0) then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n12; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n13 as '/tmp/normal' outputtype NCHAR(10); +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n13 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data02 != NCHAR(10) then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n13; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + + +sql create function n14 as '/tmp/normal' outputtype TINYINT UNSIGNED; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n14 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n14; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + +sql create function n15 as '/tmp/normal' outputtype SMALLINT UNSIGNED; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n15 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n15; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function n16 as '/tmp/normal' outputtype INT UNSIGNED; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n16 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n16; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + + +sql create function n17 as '/tmp/normal' outputtype BIGINT UNSIGNED; +sql show functions; +if $rows != 1 then + return -1 +endi + + +if $data00 != n17 then + return -1 +endi +if $data01 != /tmp/normal then + return -1 +endi +if $data04 != 5 then + return -1 +endi +if $data05 != hello then + return -1 +endi + +sql drop function n17; + +sql show functions; +if $rows != 0 then + return -1 +endi + + + +sql create function t01 as '/tmp/normal' outputtype INT; +sql_error create function t01 as '/tmp/normal' outputtype SMALLINT; +sql drop function t01; +sql create function t01 as '/tmp/normal' outputtype INT; +sql create function t02 as '/tmp/normal' outputtype SMALLINT; +sql show functions; +if $rows != 2 then + return -1 +endi + + + + + + +sql_error create function e1 as '/tmp/normal'; +sql_error create function e2 as '/tmp/normal' outputtype; +sql_error create function e3 as '/tmp/normal' a; +sql_error create function e4 as '/tmp/normal' outputtype a; +sql_error create function e5 as '/tmp/normal' outputtype bool int; +sql_error create function as '/tmp/normal' outputtype; +sql_error create function e6 as '/tmp/empty' outputtype int; +sql_error create function e7 as '/tmp/big' outputtype int; +sql_error create function e8 as '/tmp/noexistfile' outputtype int; +sql_error create function e0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 as '/tmp/normal' outputtype int; +sql_error create function e9 as outputtype int; + + +system sh/exec.sh -n dnode1 -s stop -x SIGINT diff --git a/tests/script/sh/prepare_udf.sh b/tests/script/sh/prepare_udf.sh new file mode 100644 index 0000000000000000000000000000000000000000..1328ea113d3f0e7cc5f8d71f07e524a144b09e8a --- /dev/null +++ b/tests/script/sh/prepare_udf.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +echo -n "hello" > /tmp/normal +echo -n "" > /tmp/empty +dd if=/dev/zero bs=3584 of=/tmp/big count=1 +