C++ 字符数组 - 相同的输入给出不同的输出
C++ char arrays - same input giving different outputs
我有一个实现 Netezza 用户定义函数的 cpp class(文档 here)。它接受一个参数,该参数将是某种日期格式的字符串,并将其转换为 YYYYMMDD 格式。如果它不是有效日期,它将 return“99991231”。每当我 运行 某些表上的代码时,对于相同的输入,我每次都会得到不同的输出。我假设存在一些我没有看到的内存问题。
从逻辑上讲,我们将 char 数组 retval 设置为等于 date 命令的输出。如果它给出空输出,我们设置为“99991231”。然后我们将临时字符数组设置为 retval 的前 9 个字节(最后一个是空终止符)。然后我们 memcpy 到 ret->data(我们必须 return 的结构的 char ptr)。
#include <stdarg.h>
#include <string.h>
#include "udxinc.h"
#include "udxhelpers.h"
using namespace nz::udx_ver2;
class Dateconvert: public Udf
{
public:
Dateconvert(UdxInit *pInit) : Udf(pInit){}
~Dateconvert(){}
static Udf* instantiate(UdxInit *pInit);
virtual ReturnValue evaluate()
{
StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;
string shell_arg = tempData;
shell_arg = "'" + shell_arg + "'";
string cmd="date -d " + shell_arg + " +%Y%m%d 2>/dev/null";
FILE *ls = popen(cmd.c_str(), "r");
char retval[100];
retval[0]='n';
fgets(retval, sizeof(retval), ls);
if(!isdigit(retval[0]))
{
strcpy(retval,"99991231");
}
pclose(ls);
char temp1[9];
memcpy(temp1, retval, 8);
temp1[8]='[=10=]';
ret->size = 9;
memcpy(ret->data, temp1, 9);
NZ_UDX_RETURN_STRING(ret);
}
};
Udf* Dateconvert::instantiate(UdxInit *pInit)
{
return new Dateconvert(pInit);
}
当我 运行 Netezza 中一个不同值的 UDF 时,它给出了预期的输出。但是,当我 运行 它遍历多个列时,输出有时正确,有时错误,看似随机。我认为这必须是一个内部存储器问题。示例:
input output
1) 8/11/2014 20140811
2) 8/11/2014 20140811
Fri 10/17/14 20141017
3) 8/11/2014 99991231
Fri 10/17/14 20141017
4) 8/11/2014 20140811
Fri 10/17/14 20141017
5) 8/11/2014 20140811
Fri 10/17/14 20141017
9-Nov-12 20121109
6) 8/11/2014 20140811
Fri 10/17/14 20141017
9-Nov-12 01241109 (what?)
7) 8/11/2014 99991231
Fri 10/17/14 20141017
9-Nov-12 20121109
只要函数只有一次调用,return就是正确答案。多次调用就出现这个问题,我不明白。为什么会有任何事情发生?在评估函数末尾将 return 值大小从 9 更改为 8 并不能解决问题。
这是调用函数的格式:
select a.val1, DATECONVERT(a.val1)
from
(
select '8/11/2014' as val1 from calendar
union
select 'Fri 10/17/14' as val1 from calendar
union
select '9-Nov-12' as val1 from calendar
) a
UDF 的编译命令:
nzudxcompile /export/home/nz/dateconvert.cpp -o dateconvert.o --sig "Dateconvert(VARCHAR(200))" --version 2 --return "VARCHAR(200)" --class Dateconvert --user user1 --pw mypw --db mydb
切入正题,这里的问题在于如何分配 tempData。
StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;
StringArg 不存储以 NUL 结尾的字符串,而是提供长度并希望您自己管理。
select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1
union all
select '9-Nov-12'::varchar(20) as val1
) a;
VAL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12 | 01221109
(2 rows)
在这个例子中,当第二个较短的字符串被分配给 tempData 时,第一个较长的字符串仍然有一个字符在内存中徘徊。最后悬挂的“2”像这样添加:
09-Nov-12
9-Nov-122
这些都是 date 的有效输入,很好地解释了您看到的输出。
$ date -d 09-Nov-12 +%Y%m%d
20121109
$ date -d 09-Nov-122 +%Y%m%d
01221109
更改分配以使用该长度,您将避免该问题。
//string tempData = datas;
string tempData(datas, datas+lengths);
然后你得到预期的输出:
select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1
union all
select '9-Nov-12'::varchar(20) as val1
) a;
VAL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12 | 20121109
(2 rows)
综上所述,我不知道您在此 UDF 中采用的总体方法是否可行。正如我上面的运行,这些行是在主机上生成的,因为它们是在SQL中硬编码的,并且date当然可用主人。但是,您不能指望在 MPP 后端(我们通常称为 SPU)上运行的代码具有与主机上相同的 linux 实用程序可用性,或者如果它们存在的话具有相同的能力。
如果我将日期移动到实际的 table,UDF 将在 SPU 上对其进行操作,并且它会给我错误的输出,因为 date 命令SPU 图像上的图像与主机上的图像明显不同,根本不理解这种输入格式。
select a.col1, admin.DATECONVERT(a.col1) from calendar a;
COL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 99991231
9-Nov-12 | 99991231
(2 rows)
我有一个实现 Netezza 用户定义函数的 cpp class(文档 here)。它接受一个参数,该参数将是某种日期格式的字符串,并将其转换为 YYYYMMDD 格式。如果它不是有效日期,它将 return“99991231”。每当我 运行 某些表上的代码时,对于相同的输入,我每次都会得到不同的输出。我假设存在一些我没有看到的内存问题。
从逻辑上讲,我们将 char 数组 retval 设置为等于 date 命令的输出。如果它给出空输出,我们设置为“99991231”。然后我们将临时字符数组设置为 retval 的前 9 个字节(最后一个是空终止符)。然后我们 memcpy 到 ret->data(我们必须 return 的结构的 char ptr)。
#include <stdarg.h>
#include <string.h>
#include "udxinc.h"
#include "udxhelpers.h"
using namespace nz::udx_ver2;
class Dateconvert: public Udf
{
public:
Dateconvert(UdxInit *pInit) : Udf(pInit){}
~Dateconvert(){}
static Udf* instantiate(UdxInit *pInit);
virtual ReturnValue evaluate()
{
StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;
string shell_arg = tempData;
shell_arg = "'" + shell_arg + "'";
string cmd="date -d " + shell_arg + " +%Y%m%d 2>/dev/null";
FILE *ls = popen(cmd.c_str(), "r");
char retval[100];
retval[0]='n';
fgets(retval, sizeof(retval), ls);
if(!isdigit(retval[0]))
{
strcpy(retval,"99991231");
}
pclose(ls);
char temp1[9];
memcpy(temp1, retval, 8);
temp1[8]='[=10=]';
ret->size = 9;
memcpy(ret->data, temp1, 9);
NZ_UDX_RETURN_STRING(ret);
}
};
Udf* Dateconvert::instantiate(UdxInit *pInit)
{
return new Dateconvert(pInit);
}
当我 运行 Netezza 中一个不同值的 UDF 时,它给出了预期的输出。但是,当我 运行 它遍历多个列时,输出有时正确,有时错误,看似随机。我认为这必须是一个内部存储器问题。示例:
input output
1) 8/11/2014 20140811
2) 8/11/2014 20140811
Fri 10/17/14 20141017
3) 8/11/2014 99991231
Fri 10/17/14 20141017
4) 8/11/2014 20140811
Fri 10/17/14 20141017
5) 8/11/2014 20140811
Fri 10/17/14 20141017
9-Nov-12 20121109
6) 8/11/2014 20140811
Fri 10/17/14 20141017
9-Nov-12 01241109 (what?)
7) 8/11/2014 99991231
Fri 10/17/14 20141017
9-Nov-12 20121109
只要函数只有一次调用,return就是正确答案。多次调用就出现这个问题,我不明白。为什么会有任何事情发生?在评估函数末尾将 return 值大小从 9 更改为 8 并不能解决问题。
这是调用函数的格式:
select a.val1, DATECONVERT(a.val1)
from
(
select '8/11/2014' as val1 from calendar
union
select 'Fri 10/17/14' as val1 from calendar
union
select '9-Nov-12' as val1 from calendar
) a
UDF 的编译命令:
nzudxcompile /export/home/nz/dateconvert.cpp -o dateconvert.o --sig "Dateconvert(VARCHAR(200))" --version 2 --return "VARCHAR(200)" --class Dateconvert --user user1 --pw mypw --db mydb
切入正题,这里的问题在于如何分配 tempData。
StringReturn* ret = stringReturnInfo();
StringArg *str;
str = stringArg(0);
int lengths = str->length;
char *datas = str->data;
string tempData = datas;
StringArg 不存储以 NUL 结尾的字符串,而是提供长度并希望您自己管理。
select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1
union all
select '9-Nov-12'::varchar(20) as val1
) a;
VAL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12 | 01221109
(2 rows)
在这个例子中,当第二个较短的字符串被分配给 tempData 时,第一个较长的字符串仍然有一个字符在内存中徘徊。最后悬挂的“2”像这样添加:
09-Nov-12
9-Nov-122
这些都是 date 的有效输入,很好地解释了您看到的输出。
$ date -d 09-Nov-12 +%Y%m%d
20121109
$ date -d 09-Nov-122 +%Y%m%d
01221109
更改分配以使用该长度,您将避免该问题。
//string tempData = datas;
string tempData(datas, datas+lengths);
然后你得到预期的输出:
select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
select '09-Nov-12'::varchar(20) as val1
union all
select '9-Nov-12'::varchar(20) as val1
) a;
VAL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 20121109
9-Nov-12 | 20121109
(2 rows)
综上所述,我不知道您在此 UDF 中采用的总体方法是否可行。正如我上面的运行,这些行是在主机上生成的,因为它们是在SQL中硬编码的,并且date当然可用主人。但是,您不能指望在 MPP 后端(我们通常称为 SPU)上运行的代码具有与主机上相同的 linux 实用程序可用性,或者如果它们存在的话具有相同的能力。
如果我将日期移动到实际的 table,UDF 将在 SPU 上对其进行操作,并且它会给我错误的输出,因为 date 命令SPU 图像上的图像与主机上的图像明显不同,根本不理解这种输入格式。
select a.col1, admin.DATECONVERT(a.col1) from calendar a;
COL1 | DATECONVERT
-----------+-------------
09-Nov-12 | 99991231
9-Nov-12 | 99991231
(2 rows)