使用二进制数据更新 PostgreSQL table

Updating PostgreSQL table with binary data

我有一个包含此序列化二进制数据的 std::stringstream strs 变量:

�G�{SSF��>%�����hgRQ;Tjh A "ʐk�R3 1[Z�yA _�Kx O��� ���f��' ����t %��+>, ���~� 삾�+/ Tb�Ҷ�7 �(���� �Q1�5m& ��( G#�bm 3O�AN ) �DP߇g �0=ʆ�0 ���j�u E�3�� �G�#�" \��!�o% L.�� �WMG?B- 3����}& �.�S� (�B� �j&� �@��%&, 65��0 !G�5R ��N��0 ��b�� hv) �8�� x�%7 5e��: w|7ώJ 8���� ����X/ v�c�h2 3��i� o^���
�A��� �oG��0 +���Ȑ" n�� ���4 F#�>b .��m�=; � �X ��< � c(= ���7Y: �� �� Q��O� w�k�q! �D��G�8 O���l�1 j��DH ��rhJ v͑UF� �P���; �| ���h �U��z�* 0 Ԏ��6 @I� ��,K�� �R�B� ��ﲒi ��o�H�0 �"�� ���B,� QP�@ YŽ�05 �8�s�� �>���:> ���*� Kp1>�~< ����� x�5 05 S?�V�" �m�7 )����z$ �Ye��- �nKPz ~8�쩳 dF �̄5 �ɼ��% v�x�O�# 9�B�/�6 �5��[. ��P%:� ���V�tG'�O ��#bQ�9 �����) �0%�[0 f�(? e( �5 rt �O
�[�۠ �ɴKG� �'$�_s ��g:] Aߞ,�Q

这是使用GDB后的相同数据:

"4G1{SSF11*>7%513i\b461hgRQ62[=22=]1;Tjh[=22=]2[=22=]0[=22=]0[=22=]0A[=22=]0[=22=]0[=22=]0[=22=]1[=22=]0[=22=]0[=22=]0\"ʐk3R3[=22=]01[Z2yA[=22=]1[=22=]0_06K[=22=]4x[=22=]5[=22=]0O6\f2\a50[=22=]0621f05'[=22=]065715t0[=22=]0%632+>,[=22=]07617~33[=22=]0삾14+/[=22=]0Tb5Ҷ37[=22=]02(76667[=22=]07Q105m&[=22=]04675[=22=]5(\a[=22=]00G#5bm7[=22=]03O4AN )[=22=]05DP߇g\a[=22=]050=ʆ00[=22=]0272j4u[=22=]6[=22=]0E4325\v\f[=22=]03G34#7\"[=22=]0\3177!0o%[=22=]0[=22=]5L5.56[=22=]2[=22=]07WMG?B-[=22=]035234}&[=22=]057.7S41[=22=]0(63B45\n[=22=]064j&11\n[=22=]06@64%&,[=22=]06564130[=22=]0!G2\v5R0[=22=]02\f3N510[=22=]0175b433[=22=]0h1577v)[=22=]027062w\b[=22=]0x<[=22=]16n}4[=22=]0Җp{2F\t[=22=]002m6ŵ8[=22=]0(66ۡ:7[=22=]0460b=5\"[=22=]014[)222[=22=]0 m6fd50[=22=]0[VAl63[=22=]6[=22=]054o1'9#[=22=]03272724[=22=]0|'66K{1[=22=]0B60v72+[=22=]0x63056![=22=]0qO6[=22=]5wz*[=22=]0T6*12H;[=22=]0\ba[=22=]6$647[=22=]037\a023[=22=]3[=22=]01,m401[=22=]4[=22=]0DË72T [=22=]071Ţ507[=22=]0;:33062[=22=]0~74021\r[=22=]04>417%7[=22=]054e65:[=22=]6[=22=]0w|7ώJ[=22=]6[=22=]0020036\n[=22=]05307[=22=]5X/[=22=]0v5c0h52[=22=]03534i2\r[=22=]0o^132\n7[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0\t4A7144[=22=]04oG2470[=22=]0+420Ȑ\"[=22=]0n4005?\b[=22=]0[=22=]441[=22=]6046[=22=]0F#5>b0 [=22=]0.14m6=;[=22=]05\t72X44[=22=]023[=22=]507<7[=22=]062 c[=22=]5(=[=22=]01027Y:[=15=]0367\n437[=15=]0[=15=]2Q46O73[=15=]0w23k3q![=15=]03D41G10[=15=]0O266l71[=15=]0j45DH32[=15=]0055rhJ\n[=15=]0v͑UF7 [=15=]00P217[=15=]5;[=15=]02770\b|2[=15=]07752h\n6[=15=]06U05z7*[=15=]00[=15=]0Ԏ116[=15=]05\b@4I72[=15=]052,K544[=15=]02R5B36[=15=]1[=15=]014ﲒi\r[=15=]063o7H50[=15=]03\"6500\n[=15=]0364B,0[=15=]6[=15=]0QP5@7[=15=]0YŽ6305[=15=]0601s427[=15=]00>100:>[=15=]0706[=15=]5*66[=15=]0Kp1>2~<[=15=]05[=15=]232501[=15=]0x15[=15=]0051[=15=]0S?1V1\"[=15=]0[=15=]06m7[=15=]307[=15=]4[=15=]0)2511z$[=15=]06Ye733-[=15=]05nK6Pz\v[=15=]0~83쩳\r[=15=]0dF\r1̄5[=15=]06\aɼ35%[=15=]0v5x4O6#[=15=]010B3/46[=15=]0456[=15=]34[.[=15=]033P%:13[=15=]0410V7t[=15=]6[=15=]0G[=22=]5'3O7[=22=]020#bQ41[=22=]0627767)[=22=]040%1[0[=16=]07\rf73(?[=16=]0e(\r45\r[=22=]0rt[=22=]0[=22=]50O*[=22=]05[4۠x\b[=22=]07ɴKG4\v[=22=]03'$1_s6[=22=]050\fg:][=22=]2[=22=]0Aߞ,3Q\t[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0[=22=]0"

我正在尝试将此数据(存储在 std::stringstream strs 中)上传到使用 UTF8 编码的 PostgreSQL 数据库并上传到 byte_info 列,但 bytea 使用libpqxx图书馆:

 pqxx::connection Con("My con information");
 pqxx::work W(Con);
 W.exec("UPDATE " + tableName + " SET byte_info =" + strs.str() + " WHERE id = 1;");
 W.commit();

但我得到的只是这个错误:

ERROR: invalid byte sequence for encoding "UTF8": 0xfc

我在这里遗漏了什么或做错了什么?

根据 PostgreSQL 手册bytea,有两种写包含二进制流的语句的方法。

对于字符串"\x4A\xC3\xA1\xF2\x18"

  1. 十六进制 bytea : E'\x4AC3A1F218'
  2. escaped bytea : E'J\303\241\362\030'::bytea -- 将 \ 转义为 \\,将 ' 转义为 \' 并将不可打印的转义为\three-digit-octal

所以你可以想出这样的函数。

std::string ascii_to_hex_bytea(std::string_view sv) {
  std::ostringstream os;
  os << R"(E'\x)" << std::hex << std::uppercase;
  for (unsigned char ch : sv) {
    os << std::setfill('0') << std::setw(2) << static_cast<uint16_t>(ch);
  }
  os << "'";
  return os.str();

}

std::string ascii_to_escaped_bytea(std::string_view sv) {
  std::ostringstream os;
  os << "E'" << std::oct;
  for (unsigned char ch : sv) {
    if (isprint(ch))
      switch (ch) {
        case('\') : os << R"(\\)"; break; // escape back slash
        case('\'') : os << R"(\')"; break;   // escape single quote
        default    : os << ch;               // simply put printable char
      }
    else // escape the rest as an octal with back slash leading
      os << R"(\)" << std::setfill('0') << std::setw(3) << static_cast<uint16_t>(ch);
  }
  os << "'::bytea";
  return os.str();

}

假设你有 ss 作为 stringstream 和一些数据(为了演示,我们只是在这里 ramdom 它)

  std::stringstream ss;
  { // random bits for length of 1024
    std::string str(1024,'[=11=]');
    for (char* addr = str.data(); addr < str.data() + str.size(); ++addr )
      *addr = static_cast<char>(std::experimental::randint<uint16_t>(0,255) );
    ss.str(str);
  }

您可以使用这些函数编写语句

  auto hex_str = ascii_to_hex_bytea(ss.str() );
  std::cout << hex_str << "\n";

  std::string tableName{"table_name"};
  std::string statement1 = "UPDATE " + tableName + " SET byte_info = " + hex_str + " WHERE id = 1;";
  std::cout << statement1 << "\n\n";

  auto escaped_str = ascii_to_escaped_bytea(ss.str() );
  std::cout << escaped_str << "\n";

  std::string statement2 = "UPDATE " + tableName + " SET byte_info = " + escaped_str + " WHERE id = 1;";
  std::cout << statement2 << "\n";

打印

E'\x4AC3A1F218E1ED92AB0B3966C3E99CC5BD8419B4A91D504F85AE7621525F305A...'
UPDATE table_name SET byte_info = E'\x4AC3A1F218E1ED92AB0B3966C3E99C...' WHERE id = 1;

E'J\303\241\362\030\341\355\222\253\0139f\303\351\234\30...'::bytea
UPDATE table_name SET byte_info = E'J\303\241\362\030\341\355\...'::bytea WHERE id = 1;

godbolt.org/g/8Ctgcu

wandbox.org/permlink/eaaAWz7pCbGTLcbC