DAS 1.1测试时内部服务器在处理接收的单据时崩溃的情况(有产生crash文件).导致程序不再进行后续处理.
在测试环境已出现两种错误配置导致崩溃的情况。
产生异常的代码位置及说明如下:
for (int col=0;col<fld_num;col++) {
FieldDescriptor *fd = rs->GetFieldInfo(col);const char *val = rs->GetFieldValue(row,col); ///< @todo 很大的性能提升空间
stFieldInfo *fi = ti->FindField(fd->name);
/// fi==NULL,导致下面的CvtFieldConstValueString访问时异常
/// 如果在启动前校验过规则,则此处可以assert(fi!=0)
string ret_val;
if (ISEMPTY_STR(val)) {
GetFieldDefaultValue(pdbor,fi->type2_,ret_val);
}
else {
if (CvtFieldConstValueString(pdbor,val,ret_val)) {
return -1;
}
}
ti是关于表tsm_returnsheet_jk的信息.该表没有Refsheetid字段(fd->name).导致: fi==NULL
说明dxi_ddd的配置文件错误.检查后确定错误的配置项如下指示:
<dest_db><ds_name>JXJ_DB</ds_name> <!--目标数据源名称,缺省为默认目标数据源-->
<master_table_name>tsm_returnsheet_jk</master_table_name> <!-- 目标主表名称,仅用于业务主键冲突时删除原数据-->
<key_field prop="0">sheetid,bizstoreid</key_field> <!--prop表示属性 bit0: 1表示是自增长字段,注意填写导入目标表中的主键,需要填写目标表中的字段名-->
</dest_db>
<tables><!--表映射 -->
<table>
<name>tsm_returnsheet_jk</name><!--目标主表名 -->
<command>
select
bizcode as bizstoreid,
sheet_id as sheetid,
f007v_1105 as bizsupplyid,
F011v_1105 as shopid,
Refsheet_id as retntsheetid(修正前为refsheetid),
Reserve4 as sheetstatus,
f018d_1105 as returndate,
Dep_id as deptid,
Reserve3 as totalvalue
from tb_1105,tb_jxjorgcode
where sheet_id='@sheet_id' and src_orgid=@src_orgid
and orgid=src_orgid
</command>
</table>
</tables>
修改CRule::check_dest_db,在插件加载时检查规则.在该函数中增加以下代码:
USEDBC(pdbor,src_ds_->dbc_name_.c_str()); ///< 检查select语句配置是否正确,包括是否是有效的SELECT,及目标表是否存在对应字段 /// cmd_可能已经有where条件了,且可能有参数:@参数名 SelectNode parser; parser.SetDbAccessor(pdbor); try { parser.Parse(table->cmd_.cmd_.c_str()); }catch(...) { return -1; } ///< 不查任何记录,只用来检查字段 parser.SetClauseString(SelectNode::SC_WHERE,"0>0"); string sql = parser.GetCookedsql(); AUTO_QUERY_RECORDSET(CRecordset,prs,pdbor); prs = pdbor->Query(adCmdText,sql.c_str()); if (prs==0) { return -1; } int fld_num = prs->GetFieldCount(); for (int i=0;i<fld_num;i++) { FieldInfo fld; prs->GetFieldInfo(i,&fld); CAutoVector<stFieldInfo*>::iterator it_fld = ti->fields_.begin(); bool found = false; while(it_fld!=ti->fields_.end()) { stFieldInfo *fi = *it_fld; if (stricmp(fld.m_strName,fi->name_.c_str())==0) { found = true; break; } it_fld++; } if (!found) { nlogger_->log(LO_STDOUT|LO_FILE,SEVERITY_ERROR,"CRule::check_dest_db,表%s映射语句错误,没有字段%s.\n",dest_ds_->dbc_name_.c_str(),table->name_.c_str(),fld.m_strName); return -1; } }
@H_301_9@情形2: 任务主键列在<src_db>的<key_field>中不存在
规则的<src_db>的<key_field>内容和tb_change_log的app_key的列不一致.
导致以下代码位置产生问题:
int dxi_ddd_ns::CTask::HandleTable(CTableRelationNode *node,CMsg *msg) {
string tbl_name = node->GetName();
CTable *table = rule_->find_table(tbl_name.c_str());
RuleCommand *rc = &table->cmd_;
///< 确定对应表的抽取语句(参数化sql)
string sql = rc->cmd_;
///< 确定参数(父表对应字段的值)
int para_num = rc->GetParamNum();
vector<string> vs;
for (int k=0;k<para_num;k++) {
stParamPair *pp = rc->GetParam(k);
CTaskKey *key = find_key(pp->name_.c_str());
///< @note 如果上述配置错误,会导致key==0,下面的语句产生异常.
///< 增加启动时检查配置后可以assert(key!=0)
string val = key->field_val_;
vs.push_back(val);
}
///< 生成最终执行的sql语句
char *buffer;
if (rc->Instance(&buffer,vs))
return -1;
sql = buffer;
delete []buffer;
USEDBC(pdbor,rule_->src_ds_->dbc_name_.c_str());
AUTO_QUERY_RECORDSET(CRecordset,pdbor);
prs = pdbor->Query(adCmdText,sql.c_str());
if (prs==0) {
return -1;
}
if (prs->ISEOf()) ////< 如果没有查找到数据则不继续处理
return 0;
int rs_num = msg->GetRowsets();
string para_name = LogMsg(INNER_TABLE_FMT,rs_num+1);
msg->AddParam(para_name.c_str(),tbl_name.c_str());
CRowset *rs = new CRowset;
msg->AddRowset(rs);
stRowsetInfo *ri = new stRowsetInfo;
ri->rs_ = rs;
ri->tbl_name_ = tbl_name;
RSS_[tbl_name] = ri;
if (RecordFieldToRowset(prs,rs))
return -2;
while(!prs->ISEOf()) {
if (RecordToRowset(prs,rs))
return -4;
if (HandleDetail(node,msg))
return -5;
prs->Move();
}
return 0;
}
通过增加启动时校验规则,发现和报告此类配置错误.
增改的代码如下:
(1)dxi_ddd.cpp
CRule::check_src_db增加以下代码
///< 检查主表的映射语句中的参数是否与key_field一致
CTable *table = this->find_table(dest_master_table_name_.c_str());
RuleCommand *rc = &table->cmd_;
string sql = rc->cmd_;
int para_num = rc->GetParamNum();
vector<string> vs;
for (int k=0;k<para_num;k++) {
stParamPair *pp = rc->GetParam(k);
CTaskKey *key = ::find_key(pp->name_.c_str(),this->src_keys_);
if (key==0) {
nlogger_->log(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CRule::check_src_db()%s.配置错误:主表的<command>参数没有出现在<key_field>中.\n",
src_ds_->dbc_name_.c_str(),ti->name_.c_str());
return -1;
}
}
////////////////////////////////////////////////////////////////////////////////
CTaskKey* find_key(const char *key_name,CAutoVector<CTaskKey*> &keys) {
CAutoVector<dxi_ddd_ns::CTaskKey*>::iterator iter = keys.begin();
while(iter!=keys.end()) {
dxi_ddd_ns::CTaskKey *key = *iter;
if (stricmp(key->field_name_.c_str(),key_name)==0)
return key;
iter++;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
CTaskKey* CTask::find_key(const char *key_name) {
return ::find_key(key_name,keys_);
}
(2)dxi_ddd.h using namespace dxi_ddd_ns; CTaskKey* find_key(const char *key_name,CAutoVector<CTaskKey*> &keys);