Я веду состояние патчинга в небольшом файле SQLite и запускаю его через sqlite3 :memory: < main.sql. Мне нужны хосты, которые последний раз патчились до 2026-10-01 и у которых нет активного maintenance hold. Активный — это until >= '2026-11-02'.

По моим подсчётам, таких хостов 5. Просроченных всего 6, и живой hold есть только у db-la-01. Запрос ниже не возвращает вообще ничего. Код выхода 0, никаких предупреждений.

SELECT hostname, env, last_patched
FROM hosts
WHERE last_patched < '2026-10-01'
  AND id NOT IN (
    SELECT host_id FROM maint_holds WHERE until >= '2026-11-02'
  )
ORDER BY last_patched;

Версия с count выводит вот что:

candidates
----------
         0

Что я проверил:

  • Списки inventory и holds выглядят правильно.
  • Подзапрос сам по себе возвращает ожидаемые строки.
  • until имеет тип TEXT, но все значения в ISO 8601 и формат везде одинаковый.
  • Замена >= на > ничего не изменила.

Список активных holds для справки:

id  host_id  hostname_hint    until
--  -------  -------------  ----------
 1        3  db-la-01       2026-11-15
 3           db-la-02       2026-12-01

Второй hold относится к db-la-02, которого ещё нет в hosts. Я пока не резолвил hostname, поэтому host_id пустой. Это недоделанная фича, и я не понимаю, почему она должна влиять на запрос про хосты, которые существуют.

Так почему фильтр убирает все строки, а не только db-la-01? Я подозревал сравнение текста, но с датами всё в порядке. Что я упускаю?