PostgreSQL 插入中文以及数据绑定
参数绑定
libpg-fe.h中PQexecParams函数:
PGresult *PQexecParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);
该函数的参数是:
conn
要在其中发送命令的连接对象。command
要执行的 SQL 命令字符串。如果使用了参数,它们在该命令字符串中被引用为 2等。nParams
提供的参数数量。它是数组paramTypes[]、paramValues[]、paramLengths[]和paramFormats[]的长度(当nParams为零时,数组指针可以是NULL)。paramTypes[]
通过 OID 指定要赋予给参数符号的数据类型。如果paramTypes为NULL或者该数组中任何特定元素为零,服务器会用对待未知类型文字串的方式为参数符号推测一种数据类型。paramValues[]
指定参数的实际值。这个数组中的一个空指针表示对应的参数为空,否则该指针指向一个以零终止的文本字符串(用于文本格式)或者以服务器所期待格式的二进制数据(用于二进制格式)。paramLengths[]
指定二进制格式参数的实际数据长度。它对空参数和文本格式参数被忽略。当没有二进制参数时,该数组指针可以为空。paramFormats[]
指定参数是否为文本(在参数相应的数组项中放一个零)或二进制(在参数相应的数组项中放一个一)。如果该数组指针为空,那么所有参数都会被假定为文本串。
以二进制格式传递的值要求后端所期待的内部表示形式的知识。例如,整数必须以网络字节序被传递。传递numeric值要求关于服务器存储格式的知识,正如src/backend/utils/adt/numeric.c::numeric_send()以及src/backend/utils/adt/numeric.c::numeric_recv()中所实现的。resultFormat
指定零来得到文本格式的结果,或者指定一来得到二进制格式的结果(目前没有规定要求以不同格式得到不同的结果列,尽管在底层协议中这是可以实现的)。
示例代码:
// fieldValue 中用 string.empty 表示空值。
__int64 Update(PGconn *pConn, string strTableName, vector<string> fieldsName, vector<string> fieldsValue, int OID)
{
if (pConn == NULL)
return false;
if (fieldsName.size() != fieldsValue.size())
return false;
string strSQL = "";
PGresult* pResult = NULL;
//sql
strSQL += "UPDATE ";
strSQL += strTableName;
strSQL += " SET ";
int nCount = fieldsName.size();
for (int i = 0; i < nCount; ++i)
{
strSQL += fieldsName[i];
char bufInt[21] = "";
_itoa(i + 1, bufInt, 10);
strSQL += " = $";
strSQL += bufInt;
if (i != nCount - 1)
strSQL += ", ";
}
strSQL += " WHERE ";
strSQL += OBJECTID;
strSQL += " = ";
char bufInt[21] = "";
_itoa(nCount + 1, bufInt, 10);
strSQL += " = $";
strSQL += bufInt;
//data
char** paramValues = NULL; // 数据
int* paramLengths = NULL; // 数据长度
int* paramFormats = NULL; // 数据格式 0 文本 1 二进制
int resultFormat = 0; // 返回格式
paramValues = new char*[nCount + 1];
paramLengths = new int[nCount + 1];
paramFormats = new int[nCount + 1];
for (int i = 0; i < nCount; ++i)
{
string strValue = fieldsValue[i];
if (strValue.empty())
{
paramValues[i] = NULL;
paramLengths[i] = 0;
paramFormats[i] = 0;
}
else
{
string strBufUTF8 = CodeConvert::ANSIToUFT8(strValue);
int nLen = strBufUTF8.size();
paramValues[i] = (char*)calloc(nLen + 1, sizeof(char));
strcpy_s(paramValues[i], nLen + 1, strBufUTF8.c_str());
paramLengths[i] = nLen; //去掉 最后的 \0
paramFormats[i] = 0;
}
}
char szID[21] = "";
_itoa(OID, szID, 10);
paramValues[nCount] = (char*)calloc(21, sizeof(char));
strcpy_s(paramValues[nCount],21, szID);
paramLengths[nCount] = 21;
paramFormats[nCount] = 0;
pResult = PQexecParams(pConn, strSQL.c_str(), nCount + 1, NULL, paramValues, paramLengths, paramFormats, resultFormat);
if (PQresultStatus(pResult) != PGRES_COMMAND_OK)
{
cout << "UPDATE ERROR: " << PQresultErrorMessage(pResult) << endl;
}
PQclear(pResult);
return OID;
}
问题,绑定参数时中文乱码导致插入失败问题,通常需要转换成 utf-8 来插入,但转换成 utf-8 后还有可能报错:invalid byte sequence for encoding “UTF8”: 0x00,这时需要去掉绑定参数后的 ‘\0’,代码中注释。其中CodeVonver代码如下:(参照博客:https://blog.csdn.net/suhiymof/article/details/74908862)
// Author: xiongw
// Date: 2018-03-22
// filename: CodeConver.h
#ifndef _CODECONVERT_H_
#define _CODECONVERT_H_
#include <string>
class CodeConvert
{
public:
static std::wstring ANSIToUnicode(const std::string&);
static std::string UnicodeToUTF8(const std::wstring&);
static std::wstring UTF8ToUnicode(const std::string&);
static std::string UnicodeToANSI(const std::wstring&);
static std::string UTF8ToANSI(const std::string&);
static std::string ANSIToUFT8(const std::string&);
};
#endif // !_CODECONVERT_H_
#include "CodeConver.h"
#include "LibiconvCode.h"
#ifdef WIN32
#include <Windows.h>
#endif // WIN32
using namespace std;
const int nkLne = 512;
std::wstring CodeConvert::ANSIToUnicode(const std::string &str)
{
#ifdef WIN32
int nLen = 0;
int nUnicodeLen = 0;
wchar_t* pszUnicode = NULL;
wstring rt;
nLen = str.length();
nUnicodeLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
pszUnicode = new wchar_t[nUnicodeLen + 1];
memset(pszUnicode, 0, (nUnicodeLen + 1) * sizeof(wchar_t));
::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, (LPWSTR)pszUnicode, nUnicodeLen);
rt = (wchar_t*)pszUnicode;
if (pszUnicode != NULL)
{
delete pszUnicode;
pszUnicode = NULL;
}
return rt;
#else
wstring rt;
return rt;
#endif // WIN32
}
std::string CodeConvert::UnicodeToUTF8(const std::wstring &str)
{
#ifdef WIN32
char* pszElementText = NULL;
int nTextLen = 0;
string strText;
nTextLen = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
pszElementText = new char[nTextLen + 1];
memset((void*)pszElementText, 0, (nTextLen + 1) * sizeof(char));
::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, pszElementText, nTextLen, NULL, NULL);
strText = pszElementText;
if (pszElementText != NULL)
{
delete[]pszElementText;
pszElementText = NULL;
}
return strText;
#else
string strText;
return strText;
#endif // WIN32
}
std::wstring CodeConvert::UTF8ToUnicode(const std::string &str)
{
#ifdef WIN32
wchar_t* pszUnicode = NULL;
int nLen = 0;
int nUnicodeLen = 0;
wstring rt;
nLen = str.length();
nUnicodeLen = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
pszUnicode = new wchar_t[nUnicodeLen + 1];
memset(pszUnicode, 0, (nUnicodeLen + 1) * sizeof(wchar_t));
::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, (LPWSTR)pszUnicode, nUnicodeLen);
rt = (wchar_t*)pszUnicode;
if (pszUnicode != NULL)
{
delete pszUnicode;
pszUnicode = NULL;
}
return rt;
#else
wstring rt;
return rt;
#endif // WIN32
}
std::string CodeConvert::UnicodeToANSI(const std::wstring &str)
{
#ifdef WIN32
char* pszElementText = NULL;
int nTextLen = 0;
string strText;
nTextLen = WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
pszElementText = new char[nTextLen + 1];
memset((void*)pszElementText, 0, (nTextLen + 1) * sizeof(char));
::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, pszElementText, nTextLen, NULL, NULL);
strText = pszElementText;
if (pszElementText != NULL)
{
delete[]pszElementText;
pszElementText = NULL;
}
return strText;
#else
string strText;
return strText;
#endif // WIN32
}
std::string CodeConvert::UTF8ToANSI(const std::string &str)
{
#ifdef WIN32
return UnicodeToANSI(UTF8ToUnicode(str));
#else
static char szOut[nkLne] = { 0 };
memset(szOut, 0, nkLne);
LibiconvCode lc("utf-8", "gb2312");
lc.Convert((char*)str.c_str(), str.length(), szOut, nkLne);
::string strTmp(szOut, strlen(szOut));
return strTmp;
#endif // WIN32
}
std::string CodeConvert::ANSIToUFT8(const std::string &str)
{
#ifdef WIN32
return UnicodeToUTF8(ANSIToUnicode(str));
#else
static char szOut[nkLne] = { 0 };
memset(szOut, 0, nkLne);
LibiconvCode lc("gb2312", "utf-8");
lc.Convert((char*)str.c_str(), str.length(), szOut, nkLne);
::string strTmp(szOut, strlen(szOut));
return strTmp;
#endif // WIN32
}