如何防止从 Informix 存储过程返回的长文本值中出现换行符

How to prevent line breaks in long text value returned from an Informix stored procedure

我正在编写一个模拟 Informix UNLOAD 的存储过程,这样我就可以将输出放入一个竖线分隔的文本文件中,以便传输到另一个系统。我的程序按预期工作,但是当我将输出重定向到我的 shell 脚本中的文本文件时,这些行在 66 个字符后中断。我需要每条记录都在自己的行上,记录中没有换行符。

每条记录returns的文本字符串最长可达350个字符。是否存在导致这种情况发生的 Informix 设置?我的 shell 脚本中有什么?

整个过程如下。我还添加了显示如何从 shell 脚本调用此 SP 的代码。

我们正在使用 Solaris 10 和 Informix IDS 11.70。

我之前看到 answer 一个关于在存储过程中执行 UNLOAD 的 SO 问题。目前,首选是避免使用临时 table 和外部 table。 (但如果我们不能让这种方法起作用,我们可能会回到那个解决方案。)

# Real script receives these as args or calculates using today's date
begin_date="2019-04-26 00:00:00"
end_date="2019-04-26 23:59:59"
dbname="mydatabase"
STORED_PROCEDURE="sp_unload_mytable"
OUTFILE="return.dat"

rm -f $OUTFILE
echo "execute procedure ${STORED_PROCEDURE}('${begin_date}','${end_date}') " | dbaccess $dbname \
| sed 's,(expression),,' | sed 's/,$//' | sed -e '/^$/d' | sed 's/^[ ]*//' > $OUTFILE
create procedure sp_unload_mytable(start_date datetime year to second, end_date datetime year to second)
   returning lvarchar(500);

   DEFINE p_rep_serial      integer;
   DEFINE p_post_flag       integer;
   DEFINE p_recording_date  varchar(8);
   DEFINE p_lastname        varchar(35);
   DEFINE p_firstname       varchar(20);
   DEFINE p_middlename      varchar(20);
   DEFINE p_generation      varchar(4);
   DEFINE p_role        varchar(8);
   DEFINE p_corporperson    varchar(1);
   DEFINE p_party_type      varchar(1);
   DEFINE p_b           varchar(5);
   DEFINE p_p           varchar(4);
   DEFINE p_item_number     varchar(4);
   DEFINE p_i_code          varchar(3);
   DEFINE p_i_desc          varchar(10);
   DEFINE p_par_id      varchar(12);
   DEFINE p_ref_b               varchar(5);
   DEFINE p_ref_p           varchar(4);
   DEFINE p_remark_1        varchar(20);
   DEFINE p_remark_2        varchar(20);
   DEFINE p_i_id            varchar(10);
   DEFINE p_ret_code        varchar(12);
   DEFINE p_nbr_of_attempts varchar(12);
   DEFINE p_insert_timestamp    datetime year to second;
   DEFINE p_edit_flag       varchar(12);
   DEFINE p_document_id     varchar(12);
   DEFINE p_version     varchar(6);
   DEFINE p_attempts        varchar(6);
   DEFINE p_return      lvarchar(500);

   FOREACH SELECT rep_serial, post_flag, nvl(recording_date, ""),
        nvl(lastname, ""), nvl(firstname, ""), nvl(middlename, ""), 
        nvl(trim(generation), ""), nvl(trim(role), ""), 
        nvl(trim(corporperson), ""), nvl(trim(party_type), ""), 
                  nvl(trim(b), ""), nvl(trim(p), ""), nvl(trim(item_number), ""), 
                  nvl(trim(i_code), ""), nvl(trim(i_desc), ""), 
                  nvl(par_id, ""), nvl(trim(ref_b), ""), 
                  nvl(trim(ref_p), ""), nvl(remark_1, ""), nvl(remark_2, ""), 
                  nvl(i_id, ""), nvl(ret_code, ""), nvl(nbr_of_attempts, ""), 
                  nvl(insert_timestamp, ""), nvl(edit_flag, ""), nvl(document_id, ""), 
                  nvl(version, ""), nvl(attempts, "")
        INTO p_rep_serial, p_post_flag, p_recording_date, p_lastname, p_firstname, 
             p_middlename, p_generation, p_role, p_corporperson, p_party_type, 
             p_b, p_p, p_item_number, p_i_code, p_i_desc, 
             p_par_id, p_ref_b, p_ref_p, p_remark_1, 
             p_remark_2, p_i_id, p_ret_code, p_nbr_of_attempts, 
             p_insert_timestamp, p_edit_flag, p_document_id, p_version, p_attempts
        from mytable
        where insert_timestamp between start_date and end_date

        LET p_return = p_rep_serial || "|" || p_post_flag || "|" || p_recording_date || "|" || p_lastname || "|" || p_firstname || "|" || p_middlename || "|" || p_generation || "|" || p_role || "|" || p_corporperson || "|" || p_party_type || "|" || p_b || "|" || p_p || "|" || p_item_number || "|" || p_i_code || "|" || p_i_desc || "|" || p_par_id || "|" || p_ref_b || "|" || p_ref_p || "|" || p_remark_1 || "|" || p_remark_2 || "|" || p_i_id || "|" || p_ret_code || "|" || p_nbr_of_attempts || "|" || p_insert_timestamp || "|" || p_edit_flag || "|" || p_document_id || "|" || p_version || "|" || p_attempts;

        RETURN p_return WITH RESUME;

   END FOREACH;

end procedure;

问题出在 DB-Access 上。

如果您使用的是足够新的 Informix 版本(12.10.xC12 或 14.10.xC1 或更高版本,在每个版本系列中),您可以使用环境变量 DBACCESS_COLUMNS 来设置有效宽度'screen'(输出)。但是,如果数据本身包含换行符,那么这些将反映在输出中。除了将输出强制为 JSON(这不是特别容易,但如果你好奇的话,你可以查找 genBSON),我不知道有什么方法可以删除换行符或替换他们使用 \n 或类似的方法,而不是通过 post 处理 DB-Access 输出。

由于您的 Informix (11.70) 版本太旧,无法支持 DBACCESS_COLUMNS,我不确定是否有真正好的替代方案。

在紧要关头,您可以使用我的 SQLCMD 程序(与 Microsoft 的同名 johnny-come-lately interloper 无关)。要在那里获得它,您必须加入 IIUG(国际 Informix 用户组),它是免费的并且不涉及太多电子邮件(除非您订阅任何邮件列表,否则每月一两封)。 SQLCMD 相对于 DB-Access 的优势在于它会自动执行 'as wide as necessary' 输出,但它没有“不直接包含换行符”的输出模式,除非它是 JSON 样式的输出(我看到我需要修复 v91.03 中的错误,它比 IIUG 网站上的更新——目前它还不是一个足够有用的选项)。我会考虑添加一个 'C-style escapes output' 选项(或者它可能只是 JSON 样式,因为无论如何我都需要更好地处理 JSON — 待定)。 SQLCMD 应该可以与您可以使用的几乎任何版本的 Informix (ESQL/C) 一起编译。