前言
在openGauss/MogDB中,有一条这样的规则
兼容O模式下,SQLCODE等于SQLSTATE
原生PG中,SQLCODE是整型数值,SQLSTATE是5字符的字符串;ORACLE中的SQLCODE也是数值类型。
于是之前基于ORACLE或者基于PG的应用程序,在迁移到openGauss/MogDB后,如果使用数值型变量接收sqlcode,或者对sqlcode进行数值大小判断时,都会可能出现类似这样的报错
ERROR: invalid input syntax for integer: “22P02”
如果期望不丢失任何信息,将SQLCODE存下来,要么改应用程序的相关数据模型或者代码,改成用字符类型来存SQLCODE,要么就只能通过一种算法,将SQLCODE转成数值,并且需要能还原回原本的字符串。
内核源码分析
在openGauss源码中,可以找到这样一段代码
if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) {
assign_text_var(sqlcode_var, plpgsql_get_sqlstate(prev_error->sqlerrcode));
} else {
sqlcode_var->value = Int32GetDatum(prev_error->sqlerrcode);
sqlcode_var->freeval = false;
sqlcode_var->isnull = false;
}
这个大意就是,如果是A兼容模式,就用plpgsql_get_sqlstate这个函数获得sqlstate;否则直接用Int32GetDatum(prev_error->sqlerrcode) 。
其实可以看到,无论是SQLCODE还是SQLSTATE,其实值的来源是一样,只是非A模式下,直接输出了整型;A模式下进行了个转换,输出了字符串。
继续挖转换规则
const char *plpgsql_get_sqlstate(int sqlcode)
{
if (sqlcode >= 0) {
return unpack_sql_state(sqlcode);
} else {
return plpgsql_code_int2cstring(sqlcode);
}
}
当sqlcode大于等于0时,使用unpack_sql_state进行转换;否则用plpgsql_code_int2cstring进行转换。后面这个其实是自定义异常代码,这个先不管,继续看前面的unpack_sql_state
const char* unpack_sql_state(int sql_state)
{
char* buf = t_thrd.buf_cxt.unpack_sql_state_buf;
int i;
for (i = 0; i >= 6;
}
buf[i] = '\0';
return buf;
}
这里可以看到它做了个循环来拼字符串buf,每次用PGUNSIXBIT算个值,然后把sql_state移动6位。
接着看PGUNSIXBIT
#define PGUNSIXBIT(val) (((val)&0x3F) + '0')
这里就是将val和 0x3F (即十进制63)做位与操作 ,然后再加上 ‘0’ ,注意此处的 ‘0’不是数字0 ,而是字符串’0’ ,对应的ascii码十进制为48。
到这里,这个使用数值型SQLCODE转换成字符串型的SQLSTATE的算法就完整的展现出来了,当然逆向将字符串的SQLSTATE转换回数值的SQLCODE也行。
我们可以通过使用PLPGSQL语言,来对这个算法进行模拟,方便直接在数据库中来进行转换
自定义函数模拟算法
数值转字符串
CREATE OR REPLACE FUNCTION unpack_sql_state(sql_state INTEGER)
RETURNS text AS $$
DECLARE
result text := '';
i INTEGER;
BEGIN
FOR i IN 1..5 LOOP
result := result||(chr((sql_state & 63) + ascii('0'))) ;
sql_state := sql_state >> 6;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
字符串转数值
CREATE OR REPLACE FUNCTION pack_sql_state(sql_state text)
RETURNS INTEGER AS $$
DECLARE
result INTEGER := 0;
i INTEGER;
BEGIN
FOR i IN 1..5 LOOP
result := result