diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-06-28 18:47:14 +0100 |
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-06-28 18:47:14 +0100 |
| commit | ad2a2ff05af8df1ba552d906e377afa345a449a7 (patch) | |
| tree | 38b6f21bdcc0ed89825a9b837366f134a013d580 | |
| parent | 22a85bd4e1f6005455b91bb3edff68d4019526a1 (diff) | |
| download | webstat-ad2a2ff05af8df1ba552d906e377afa345a449a7.tar.bz2 webstat-ad2a2ff05af8df1ba552d906e377afa345a449a7.tar.xz webstat-ad2a2ff05af8df1ba552d906e377afa345a449a7.zip | |
Fix entity function sometimes returning null/wrong id
Includes tests over the errant behaviour.
Didn't match on type and control flow could return multiple rows.
| -rw-r--r-- | src/schema.sql | 24 | ||||
| -rw-r--r-- | test/test-ingest.cpp | 51 |
2 files changed, 62 insertions, 13 deletions
diff --git a/src/schema.sql b/src/schema.sql index 81080ff..6f28f2d 100644 --- a/src/schema.sql +++ b/src/schema.sql @@ -57,11 +57,6 @@ DECLARE recid integer; nulldetail boolean; BEGIN - IF newValue IS NULL THEN - RETURN query - VALUES (NULL, - NULL); - END IF; INSERT INTO entities(value, type) SELECT newValue, @@ -72,26 +67,31 @@ BEGIN FROM entities WHERE - md5(value) = md5(newValue)) + md5(value) = md5(newValue) + AND type = newType) ON CONFLICT DO NOTHING RETURNING entities.id, - entities.detail IS NULL INTO recid, + entities.detail IS NULL + INTO + recid, nulldetail; IF recid IS NULL THEN - RETURN query + RETURN QUERY SELECT e.id, e.detail IS NULL FROM entities e WHERE - md5(e.value) = md5(newValue); + md5(e.value) = md5(newValue) + AND e.type = newType; + ELSE + RETURN QUERY + VALUES (recid, + nulldetail); END IF; - RETURN query -VALUES (recid, - nulldetail); END; $$ LANGUAGE plpgSQL diff --git a/test/test-ingest.cpp b/test/test-ingest.cpp index 408e720..9860f08 100644 --- a/test/test-ingest.cpp +++ b/test/test-ingest.cpp @@ -7,6 +7,7 @@ #include <selectcommandUtil.impl.h> #include <ingestor.hpp> +#include <sql.hpp> #include <uaLookup.hpp> namespace { @@ -290,8 +291,19 @@ public: mutable size_t logsWritten = 0; }; -BOOST_FIXTURE_TEST_SUITE(I, TestIngestor); +static constexpr std::array<std::string_view, 9> ENTITY_TYPE_VALUES {{ + {"host"}, + {"virtual_host"}, + {"path"}, + {"query_string"}, + {"referrer"}, + {"user_agent"}, + {"unparsable_line"}, + {"uninsertable_line"}, + {"content_type"}, +}}; +BOOST_FIXTURE_TEST_SUITE(I, TestIngestor); BOOST_TEST_DECORATOR(*boost::unit_test::depends_on("ExtractFields")) BOOST_DATA_TEST_CASE(StoreLogLine, @@ -537,6 +549,43 @@ BOOST_AUTO_TEST_CASE(LogResetSignal) BOOST_CHECK_EQUAL(stats, Stats {}); } +using CreateEntitiesData = std::tuple<std::string_view, WebStat::EntityType, int>; + +BOOST_DATA_TEST_CASE(CreateEntities, + boost::unit_test::data::make<CreateEntitiesData>({ + {"host1", WebStat::EntityType::Host, 31}, + {"host2", WebStat::EntityType::Host, 33}, + {"host3", WebStat::EntityType::Host, 35}, + {"host1", WebStat::EntityType::Host, 31}, + {"host2", WebStat::EntityType::Host, 33}, + {"host3", WebStat::EntityType::Host, 35}, + {"host1", WebStat::EntityType::VirtualHost, 40}, + {"host2", WebStat::EntityType::VirtualHost, 42}, + {"host3", WebStat::EntityType::VirtualHost, 44}, + {"host1", WebStat::EntityType::VirtualHost, 40}, + {"host2", WebStat::EntityType::VirtualHost, 42}, + {"host3", WebStat::EntityType::VirtualHost, 44}, + {"host2", WebStat::EntityType::Host, 33}, + {"host1", WebStat::EntityType::VirtualHost, 40}, + {"host3", WebStat::EntityType::Host, 35}, + {"host1", WebStat::EntityType::Host, 31}, + }), + value, type, expectedId) +{ + auto dbc = dbpool->get(); + auto entityInsert = dbc->select(WebStat::SQL::ENTITY_INSERT, WebStat::SQL::ENTITY_INSERT_OPTS); + entityInsert->bindParamS(0, value); + entityInsert->bindParamS(1, ENTITY_TYPE_VALUES[std::to_underlying(type)]); + BOOST_REQUIRE_NO_THROW(entityInsert->execute()); + size_t rows = 0; + for (auto [entityId, detailIsNull] : entityInsert->as<int, bool>()) { + ++rows; + BOOST_REQUIRE_EQUAL(rows, 1); + BOOST_CHECK_EQUAL(entityId, expectedId); + BOOST_CHECK_EQUAL(detailIsNull, true); + } +} + BOOST_AUTO_TEST_SUITE_END(); BOOST_AUTO_TEST_CASE(FetchRealUserAgentDetail, *boost::unit_test::disabled()) |
