| 1613 | } |
| 1614 | |
| 1615 | int HTTPServer::queryResultsCSV(http::Context& ctx, reindexer::QueryResults& res, const IQRSerializingOption& qrOption) { |
| 1616 | if (!res.GetAggregationResults().empty()) { |
| 1617 | throw Error(errForbidden, "Aggregations are not supported in CSV"); |
| 1618 | } |
| 1619 | if (!res.GetExplainResults().empty()) { |
| 1620 | throw Error(errForbidden, "Explain is not supported in CSV"); |
| 1621 | } |
| 1622 | |
| 1623 | const size_t kChunkMaxSize = 0x1000; |
| 1624 | auto createChunk = [](const WrSerializer& from, WrSerializer& to) { |
| 1625 | char szBuf[64]; |
| 1626 | size_t l = u32toax(from.Len(), szBuf) - szBuf; |
| 1627 | to << std::string_view(szBuf, l); |
| 1628 | to << "\r\n"; |
| 1629 | to << from.Slice(); |
| 1630 | to << "\r\n"; |
| 1631 | }; |
| 1632 | |
| 1633 | auto createCSVHeaders = [&res](WrSerializer& ser, const CsvOrdering& ordering) { |
| 1634 | const auto& tm = res.GetTagsMatcher(0); |
| 1635 | for (auto it = ordering.begin(); it != ordering.end(); ++it) { |
| 1636 | if (it != ordering.begin()) { |
| 1637 | ser << ','; |
| 1638 | } |
| 1639 | ser << tm.tag2name(*it); |
| 1640 | } |
| 1641 | if (res.ToLocalQr().joined_.empty()) { |
| 1642 | ser << "\n"; |
| 1643 | } else { |
| 1644 | ser << ",joined_namespaces\n"; |
| 1645 | } |
| 1646 | }; |
| 1647 | |
| 1648 | auto wrSerRes = makeRestrictedWrSerializer(ctx); |
| 1649 | WrSerializer wrSerChunk; |
| 1650 | const auto schema = res.GetSchema(0); |
| 1651 | const bool withSchema = schema && !schema->IsEmpty(); |
| 1652 | if (!res.IsLocal() && !withSchema) { |
| 1653 | throw Error(errLogic, "Uploads in csv format without a namespace scheme are allowed only for local queries"); |
| 1654 | } |
| 1655 | |
| 1656 | const auto limit = qrOption.ExternalLimit(); |
| 1657 | const auto offset = qrOption.ExternalOffset(); |
| 1658 | auto ordering = |
| 1659 | withSchema ? CsvOrdering{schema->MakeCsvTagOrdering(res.GetTagsMatcher(0))} : res.ToLocalQr().MakeCSVTagOrdering(limit, offset); |
| 1660 | |
| 1661 | createCSVHeaders(wrSerChunk, ordering); |
| 1662 | auto it = res.begin() + offset; |
| 1663 | for (size_t i = 0; it != res.end() && i < limit; ++i, ++it) { |
| 1664 | auto err = it.GetCSV(wrSerChunk, ordering); |
| 1665 | if (!err.ok()) { |
| 1666 | throw Error(err.code(), "Unable to get {} item as CSV: {}", i, err.whatStr()); |
| 1667 | } |
| 1668 | |
| 1669 | wrSerChunk << '\n'; |
| 1670 | |
| 1671 | if (wrSerChunk.Len() > kChunkMaxSize) { |
| 1672 | createChunk(wrSerChunk, wrSerRes); |
nothing calls this directly
no test coverage detected