Почему модуль Python Sqlite3 правильно анализирует первый экземпляр этой подстановки параметров, а затем нет? Кэширование?

У меня есть таблица Sqlite3, в которой есть столбец LastUpdated, содержащий дату и время UTC в формате "2013-12-24 07:11:21", и все строки в этой таблице были обновлены 2 дня назад.

Я хочу написать оператор SELECT, чтобы возвращать только те строки, которые давно не обновлялись:

SELECT LastUpdated FROM UserToken WHERE DATETIME(LastUpdated) < DATETIME('now', '-4 days');

Я пытаюсь запустить этот запрос из Python, используя стандартную библиотеку sqlite3, и я хочу, чтобы устаревший период был переменным. В целях безопасности я попытался использовать подстановку параметров, упомянутую в документации по стандартной библиотеке sqlite3. использовать переменную stale_delta_parameter:

dbcursor.execute("SELECT LastUpdated FROM UserToken WHERE TokenValid = 1 AND DATETIME(LastUpdated) < DATETIME('now', ?)", (stale_delta_parameter,))

В первый раз, когда я запустил его, я установил stale_delta_parameter = '-4 days', и он правильно вернул нулевые строки. Затем я изменил значение stale_delta_parameter на «-1 дней» и выполнил запрос. Вместо ожидаемого результата для всех строк запрос продолжал возвращать 0 строк.

Когда я перезагружаю свой компьютер, кажется, что запрос работает нормально в первый раз, но опять же, если я запускаю скрипт python с одним дельта-значением, как только я изменяю дельта-значение, результаты продолжают быть значением из первого экземпляра выполнения запроса. Кроме того, если я напишу два оператора SELECT, где query1 установлен на '-4 days', который не должен возвращать строки, и query2 на '-1 days', который должен возвращать все строки, а затем после однократного запуска скрипта я переключаю значения в этих запросах, вывод запросов не не переключаться.

Я подумал, что это могут быть неправильные запросы sql, поэтому я попытался жестко закодировать дельту и запустить запрос в оболочке Sqlite3. Работает так, как я ожидаю каждый раз, поэтому запрос не является неправильным.

Затем я попытался расширить переменную stale_delta_parameter с '-n days' до DATETIME('now', '-n days') на тот случай, если подстановка параметров не работает правильно внутри специальной функции Sqlite DATETIME(). Странное поведение не изменилось.

Есть ли какое-то кеширование в стандартной библиотеке Sqlite3 или в Python DB-API, которое может помешать обновлению запроса, переданного в базовую базу данных?

Я ничего не могу найти в документах, но это единственная теория, которую я могу предложить, которая, кажется, соответствует этому поведению.

Я попытался найти способ распечатать собранный запрос, который передается в БД из Python, поэтому я могу убедиться, что БД не получает обновленную версию запроса, но я не могу найти какой-либо метод для печати собранный запрос из dbcursor.execute(...).

Вот фактический код:

til_user_tokens_go_stale = '4 days'
stale_delta_parameter = "DATETIME('now','-%s')" % til_user_tokens_go_stale  
dbcursor.execute('''
                 SELECT UserToken, UserID, AppID 
                 FROM UserToken 
                 WHERE TokenValid = 1 AND DATETIME(LastUpdated) < ?
                 ''', (stale_delta_parameter,))
all_tokens = dbcursor.fetchall()
print len(all_tokens) # For debugging, shows me how many rows are returned

person Jeff Widman    schedule 26.12.2013    source источник
comment
Покажите фактический код, который обновляет переменную, а затем выполняет запрос.   -  person CL.    schedule 26.12.2013
comment
Добавлен фактический код внизу @CL.   -  person Jeff Widman    schedule 26.12.2013
comment
Это не тот код, который приводился ранее, и код -1 days отсутствует.   -  person CL.    schedule 28.12.2013
comment
@КЛ. Это именно тот код, который я использую — ранее в примере я упростил его, чтобы выделить наиболее важные части, но вы попросили фактический код, поэтому я оставил его неупрощенным. Как я упоминал в своем объяснении, я переключаю значение параметра til_user_tokens_go_stale между -4 days и -1 days, чтобы проверить, работает ли запрос должным образом, поэтому код -1 days не отсутствует, просто измените параметр.   -  person Jeff Widman    schedule 29.12.2013
comment
Что именно вы имеете в виду под переключением? Код не делает ничего подобного.   -  person CL.    schedule 30.12.2013


Ответы (1)


Из документации по SQLite:

Токен «переменная» или «параметр» задает заполнитель в выражении для значения, которое заполняется во время выполнения с использованием семейства интерфейсов sqlite3_bind() C/C++.

Эти интерфейсы включают базовые типы (строки, числа, BLOB-объекты), но не такие функции, как datetime.

Один из способов выполнить то, что вам нужно, — указать смещение в unixepoch или juliandays и переформатировать запрос для выполнения арифметического вычитания.

Однако первая показанная вами параметризованная версия должна работать; это делает в Lua:

> db = sqlite3.open(':memory:')
> db:execute'create table t (d);'
> db:execute"insert into t values ('2013-12-26 14:20:00');"
> st = db:prepare "select * from t where d < datetime('now',?);"
> st:bind(1,'+1 day');
> for row in st:urows() do print (row) end
2013-12-26 14:20:00
> st:bind(1,'-1 day');
> for row in st:urows() do print (row) end
> 
person Doug Currie    schedule 26.12.2013
comment
Спасибо, Дуг, за изучение этого вопроса, но я почти уверен, что проблема кроется где-то в модуле Python Sqlite3, а не в ядре sqlite. Я также не уверен, происходит ли параметризация даты и времени для модуля python sqlite3 внутри python или внутри самого ядра sqlite - в документах модуля python упоминается, что параметризация совместима с DB-API Python, поэтому запрос может быть полностью "построен" внутри python до того, как он будет передан sqlite. - person Jeff Widman; 27.12.2013