scraps

Abandon all hope, ye who enter here.
git clone https://git.neptards.moe/neptards/scraps.git
Log | Files | Refs | Submodules | README | LICENSE

popen_sql.cpp (3345B)


      1 #include "scraps/format/rags_sql/sql.hpp" // IWYU pragma: associated // don't crash iwyu
      2 #include "scraps/format/rags_sql/popen_sql.hpp" // IWYU pragma: associated
      3 
      4 #include <libshit/assert.hpp>
      5 #include <libshit/memory_utils.hpp>
      6 #include <libshit/wtf8.hpp>
      7 
      8 #include <boost/endian/buffers.hpp>
      9 
     10 #include <algorithm>
     11 #include <sstream>
     12 
     13 namespace Scraps::Format::RagsSql
     14 {
     15 
     16   template <typename T>
     17   auto PopenSql::Read()
     18   {
     19     T dst;
     20     p.ReadChk(&dst, sizeof(T));
     21     return dst.value();
     22   }
     23 
     24   std::string PopenSql::ReadStr()
     25   {
     26     auto len = Read<U32>();
     27     std::string res(len, '\0');
     28     p.ReadChk(res.data(), len);
     29     return res;
     30   }
     31 
     32   std::string PopenSql::ReadWStr()
     33   {
     34     auto len = Read<U32>();
     35     auto buf = Libshit::MakeUnique<char16_t[]>(
     36       len / sizeof(char16_t), Libshit::uninitialized);
     37     p.ReadChk(buf.get(), len);
     38     return Libshit::Utf16LEToUtf8({buf.get(), len / sizeof(char16_t)});
     39   }
     40 
     41   std::vector<Sql::Column> PopenSql::Execute(Libshit::StringView cmd)
     42   {
     43     {
     44       auto wcmd = Libshit::Wtf8ToWtf16LE(cmd);
     45       LIBSHIT_ASSERT(wcmd.size() < 0xffffffff/2);
     46 
     47       std::uint8_t zero = 0;
     48       p.WriteChk(&zero, sizeof(zero));
     49       boost::endian::little_uint32_buf_at size{
     50         static_cast<std::uint32_t>(2 * wcmd.size())};
     51       p.WriteChk(&size, sizeof(size));
     52       p.WriteChk(wcmd.data(), sizeof(char16_t) * wcmd.size());
     53     }
     54     p.Flush();
     55 
     56     auto code = Read<U8>();
     57     if (code == 0) return {};
     58     if (code != 1) HandleError(code);
     59 
     60     auto n_cols = Read<U32>();
     61     std::vector<Sql::Column> res;
     62     res.reserve(n_cols);
     63     for (std::size_t i = 0; i < n_cols; ++i)
     64     {
     65       auto type = Read<U32>();
     66       auto flags = Read<U32>();
     67       auto name = ReadWStr();
     68       res.push_back({
     69           static_cast<Type>(type), static_cast<ColumnFlags>(flags), name});
     70     }
     71     return res;
     72   }
     73 
     74   bool PopenSql::HasRow()
     75   {
     76     switch (auto code = Read<U8>())
     77     {
     78     case 0:  return false;
     79     case 1:  return true;
     80     default: HandleError(code);
     81     }
     82   }
     83 
     84   Sql::CellInfo PopenSql::GetCellInfo()
     85   {
     86     auto is_blob = Read<U8>();
     87     if (is_blob >= 2) HandleError(is_blob);
     88     auto status = Read<U32>();
     89     rem_size = is_blob ? CellInfo::STREAM : Read<U32>();
     90     return {static_cast<DbStatus>(status), rem_size};
     91   }
     92 
     93   std::size_t PopenSql::GetChunk(void* buf, std::size_t len)
     94   {
     95     if (rem_size == CellInfo::STREAM) // BLOB chunk
     96     {
     97       switch (auto code = Read<U8>())
     98       {
     99       case 0:
    100         return 0;
    101       case 1:
    102         rem_size = Read<U32>();
    103         break;
    104       default:
    105         HandleError(code);
    106       }
    107     }
    108 
    109     auto to_read = std::min<std::size_t>(len, rem_size);
    110     p.ReadChk(buf, to_read);
    111     if ((rem_size -= to_read) == 0) rem_size = CellInfo::STREAM;
    112     return to_read;
    113   }
    114 
    115   [[noreturn]] void PopenSql::HandleError(std::uint8_t code)
    116   {
    117     switch (code)
    118     {
    119     case 0xff: // COM error
    120     {
    121       auto hr = Read<U32>();
    122       auto msg = ReadWStr();
    123       std::stringstream ss;
    124       ss << std::hex << hr;
    125       LIBSHIT_THROW(SqlError, "SQLCE COM Error", "HRESULT", ss.str(),
    126                     "Message", msg);
    127     }
    128     case 0xfe: // custom error
    129     {
    130       auto msg = ReadStr();
    131       LIBSHIT_THROW(SqlError, msg);
    132     }
    133     default:
    134       LIBSHIT_THROW(SqlError, "Invalid protocol message", "Code", int(code));
    135     }
    136   }
    137 }