Keith Medcalf wrote:
>>>> You can easily reproduce this problem if you switch unit from month to
>>>> millisecond, e.g.
>>>> SELECT * FROM t WHERE strftime('%f') <> strftime('%f');
>>>> will non-deterministically return rows.
>
>>>> IMO, correct [= least surprise] behavior should be "timestamp used for
>>>> 'now' should cached on first row step, and reused in all following calls
>>>> [until SQLITE_OK/SQLITE_DONE returned]", but fixing that won't be easy :-|.
>
>>> That would require a change to SQLite itself. Without doing that I
>> believe this will solve those problems by using only one 'now', and also
>> only running each date() call once (even my previous solutions could
>> suffer from race conditions since they retrieved 'now' multiple times):
>>> sqlite> explain select *
>>> ...> from entry,
>>> ...> (select bdate, (select date(bdate, '+1 month'))
>> as edate
>>> ...> From (select date('now', 'localtime', 'start
>> of month') as bdate) as ttemp limit 1) as tstamp
>>> ...> where bankdate >= bdate
>>> ...> and bankdate < edate;
>> Unfortunately, it is not only extremely inconvenient to rewrite queries
>> this
>> way, but also this workaround relies on current implementation of query
>> optimizer. Next version may decide to e.g. flatten query, and break
>> assumption
>> that inner date() will be called only once.
>>
>>> It is a bit complicated though. It would be much simpler to do the
>> date calculation in the host language and pass the boundary values as
>> parameters to the query.
>>
>> In other words, "The way it is implemented currently, sqlite DATE()
>> function
>> considered harmful and should not be used." Huh.
>
> Not at all. They may be quite useful for formatting output. That the
> function date('now') returns the date now ought to be expected. If you
> wanted the date at the start of the transaction, one ought to reasonably
> expect to call date('start of transaction').
That `SELECT date('now'), date('now')` can randomly return mismatching results
is most certainly NOT what anyone would expect.
> CURRENT_TIMESTAMP is a static timestamp, but you still have the same issue if
> you wish to eliminate the repetitive calls to the date function. If that
> doesn't bother you then you ought to be able to replace 'now' with
> current_timestamp.
It *should be* static timestamp. But it is NOT in sqlite.
>> And same problem: some next version of query optimizer may decide to
>> change the way this query will be executed.
>
> Perhaps, and that is why you have to clearly declare what you want in your
> select statement.
Unfortunately, there are no way to do this in sqlite.
>> PS from postgresql documentation:
>> === cut
>> http://www.postgresql.org/docs/9.3/static/functions-
>> datetime.html#FUNCTIONS-DATETIME-CURRENT
>> ===
>> Since these functions return the start time of the current transaction,
>> their
>> values do not change during the transaction. This is considered a
>> feature: the
>> intent is to allow a single transaction to have a consistent notion of
>> the
>> "current" time, so that multiple modifications within the same
>> transaction bear
>> the same time stamp.
>> === cut ===
>> *That's* way to go.
>
> Well, that is one way to go. SQLite goes that way too.
> Instead of asking for datetime('now') which returns the current 'nowness'
> value, you can use CURRENT_TIMESTAMP/CURRENT_TIME/CURRENT_DATE which is a
> static value probably representing the first time is was called in the
> transaction (or perhaps statement).
Unfortunately, sqlite does NOT go this way. Repeated calls to CURRENT_DATE/
CURRENT_TIME within same statement still can return *different* results on
date/time boundary. Race window is smaller, but still present. Enjoy:
#!/usr/bin/perl
use DBI;
#DBI -> trace( 1 );
my $dbh = DBI->connect( "dbi:SQLite:dbname=/tmp/t.db3","","" );
$dbh->do("CREATE TABLE IF NOT EXISTS t (i INTEGER)");
$dbh->do( "begin immediate" );
unless(($dbh->selectrow_array("SELECT COUNT(*) FROM t"))) {
$dbh->do("INSERT INTO t VALUES (0)");
$dbh->do("INSERT INTO t VALUES (1)");
$dbh->do("INSERT INTO t VALUES (2)");
$dbh->do("INSERT INTO t VALUES (3)");
$dbh->do("INSERT INTO t VALUES (4)");
}
$dbh->do("commit");
my $sth = $dbh->prepare( << '__E__' );
SELECT * FROM t WHERE CURRENT_TIME <> CURRENT_TIME
__E__
### SELECT * FROM t WHERE strftime('%f') <> strftime('%f')
$dbh-> do( "begin" );
my $i = 0;
while(1) {
$sth -> execute;
if (my $row = $sth->fetch) {
print "Got bug: $sth->{Statement} returned @$row\n";
last;
}
$i++;
}
print "$i iterations\n";
$sth->finish;
$dbh->do("commit");
__END__
(fwiw, only difference with strftime('%f') variant: when it hit bug,
strftime('%f') variant will randomly return one row, but CURRENT_TIME variant
randomly returns *all* rows).
_______________________________________________
sqlite-users mailing list
[email protected]
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users