Commit Graph

38 Commits

Author SHA1 Message Date
Nicolas Sebrecht
038a433f69 backport: sqlite: properly serialize operations on the databases
1. There is one database per folder and sqlite requires to serialize the
writings. Instead of locking at LocalStatusSQLiteFolder object level, introduce
a new DatabaseFileLock object which is shared across threads. This fixes the
concurrent writes issues that some users might experience by duplications or
flags restored to the previous state.

2. Close the database only when we are sure no other threads will use the
connection on a *per-file* basis. Previous fix 677afb8d8f is wrong
because the same lock is shared for all the databases.

Github-fix: https://github.com/OfflineIMAP/offlineimap/issues/350
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2016-07-26 02:00:17 +02:00
Nicolas Sebrecht
677afb8d8f sqlite: close the database when no more threads need access
It's required to have more than one connection to the database for the
maxconnections configuration option to work with threads. However,
connection.close() is closing all the connections. Only close the connection
when no more thread need it.

Backported-from: 856b74407bd7f634cae5a8c2d9b84e13d14c12d2
Github-fix: https://github.com/OfflineIMAP/offlineimap/issues/350
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2016-07-22 09:19:09 +02:00
Nicolas Sebrecht
3f70d9ef37 sqlite: open database when we use it rather than at instantiation time
Backported from 8e995a69bfa003ab822b55731429d84b3bc5626f.

We currently close the database as soon as possible while we handle the status
backend but this is still too early because autorefresh induces looping on this
code block.

Instead of delaying the closing outside of the loop, it's easier to delay the
opening as late as possible (inside the loop). The downside is that the database
is opened/closed more than once when autorefresh is enabled. The good news is
that this make the code much easier.

Fixes regression introduces by 6fb5700.

Reported-by: Łukasz Żarnowiecki <dolohow@outlook.com>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2016-06-08 01:59:39 +02:00
Nicolas Sebrecht
b840e66d59 SQLite: close db when done
Backported from 6fb5700f94.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2016-06-08 01:59:14 +02:00
Nicolas Sebrecht
efc4df1bd7 LocalStatusSQLite: labels: don't fail if database returns unexpected None value
This requires more researches. See
  https://github.com/OfflineIMAP/offlineimap/issues/103
.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-22 14:15:55 +01:00
Nicolas Sebrecht
a1383da9b3 LocalStatusSQLite: provide information on what is failing for OperationalError
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-16 10:31:21 +01:00
Nicolas Sebrecht
73952b8c2c offlineimap.conf: say what is the default value for the sep option
Some style improvements.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-13 17:43:29 +01:00
Nicolas Sebrecht
655c2b1fb9 sqlite: provide offending filename when open fails
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-13 17:18:07 +01:00
Nicolas Sebrecht
9e63fa3784 fix: folder/*: never set self.messagelist to None
Empty the list by setting an empty dict.
Introduce BaseFolder().ismessagelistempty().

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-13 17:07:18 +01:00
Nicolas Sebrecht
461554b7b1 more style consistency
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-02-10 17:25:00 +01:00
Nicolas Sebrecht
cb6790b804 minor: fix indentation
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-01-18 21:52:09 +01:00
Nicolas Sebrecht
d0cb96781c folder: LocalStatus(SQLite): avoid redefining unchanged Base method
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-01-18 21:52:09 +01:00
Nicolas Sebrecht
2af3d93a85 folder: LocalStatusSQLite: remove unused import
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2015-01-18 21:52:09 +01:00
Giovanni Mascellani
29e9b7ab39 Drop caches after having processed folders.
This enhances a lot memory consumption when you have many folders
to process.

Signed-off-by: Giovanni Mascellani <mascellani@poisson.phc.unipi.it>
2015-01-12 17:17:42 +01:00
Eygene Ryabinkin
e7fabf9e57 Properly re-raise exception to save original tracebacks
We usually mutate some exceptions to OfflineImapError() and it is
a whole lot better if such exception will show up with the original
traceback, so all valid occurrences of such mutations were transformed
to the 3-tuple form of "raise".  Had also added coding guidelines
document where this re-raise strategy is documented.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2015-01-12 17:51:21 +03:00
Eygene Ryabinkin
7df765cfdb Properly manipulate contents of messagelist for folder
Create initializer function that puts default values to all fields
of message list item.  Fix all code that directly assigns some hash
to the elements of messagelist: for direct assignments only initializer
is now permitted, all other modification are done in-place.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-08-03 16:47:26 +04:00
Eygene Ryabinkin
73e2c95acd Create SQLite DB directory if it doesn't exist yet
Also check if DB path doesn't point to a directory.

By: Nick Farrell
GitHub: https://github.com/OfflineIMAP/offlineimap/pull/102
Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-08-02 22:25:33 +04:00
Abdo Roig-Maranges
1b954c3b4c Add ability to migrate status data across backends
If when we request a LocalStatus folder, the folder has to be created,
we look whether the other backend has data, and if it does we migrate
it to the new backend.

The old backend data is left untouched, so that if you change back say
from sqlite to plaintext, the older data is still there.  That should
not lead to data loss, only a slower sync while the status folder gets
updated.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-05-06 23:37:11 +04:00
Abdo Roig-Maranges
214137eb7b Improvements to the SQlite-based local status folder
* Do not inherit LocalStatusSQLiteFolder class from the plaintext
    one.

  * Use some functions already in BaseFolder in both, plaintext and
    sqlite classes.

  * Add a saveall method. The idea is that saveall dumps the entire
    messagelist to disk, while save only commits the uncommited
    changes. Right now, save is noop for sqlite, and equivalent to
    saveall for plaintext, but it enables to be more clever on when we
    commit to disk in the future.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-05-06 23:37:11 +04:00
Abdó Roig-Maranges
0e4afa9132 Make GmailFolder sync GMail labels
When synclabels config flag is set to "yes" for the GMail repo,
offlineimap fetches the message labels along with the messages, and
embeds them into the body under the header X-Keywords (or whatever
'labelsheader' was set to), as a comma separated list.

It also adds an extra pass to savemessageto, that performs label
synchronization on existing messages from GMail to local, the same way
it is done with flags.

We also introduce GmailMaildir repository that adds functionality to
change message labels.  It keeps track of messages modification time,
so one can quickly detect when the labels may have changed.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-05-06 23:36:06 +04:00
Eygene Ryabinkin
6cbd2498ae Refactoring: make functions to be private if we can
Make external API of class/module to be smaller, explicitely mark
all internal functions.  Also annotate methods that are implemented
as the part of the parent class interface.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2014-04-30 19:35:56 +04:00
Eygene Ryabinkin
41cb0f577f Prune trailing whitespaces from code and documentation
They are redundant in all pruned cases and sometimes even create some
problems, e.g., when one tries to jump through paragraphs in vi.

Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
2013-07-21 23:00:23 +04:00
Sebastian Spaeth
5ef69e95c0 Prevent modifications on a folder level to occur in dry-run
Prevent savemessage(), and savemessageflags() to occur in dryrun mode in
all backends. Still need to protect against deletemessage().

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
2012-02-17 13:17:05 +01:00
Sebastian Spaeth
b33f2452f0 Use "from . import" for relative imports
Will fail in python3 otherwise.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
2012-02-06 17:41:43 +01:00
Sebastian Spaeth
81fc20c7ca Remove python<2.6 import workarounds (set & ssl)
'set' is builtin since python2.6, so remove the imports. Also 'ssl' exists
since 2.6 and has everything we need, so no need for conditional import
tests here anymore.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
2012-02-06 17:41:42 +01:00
Sebastian Spaeth
6dc74c9da5 Improve delete performance with SQLITE backend
When deleting many (eg 2000) mails using the SQLITE backend, this takes
a long time durig which OfflineImap can not be aborted via
CTRL-C. Thinking it had frozen permanently, I killed it hard, leaving a
corrupted db journal (which leads to awkwards complaints by OLI on
subsequent starts!). That shows that delete performance is critical and
needs improvement.

We were iterating through the list of messages to delete and deleted
them one-by-one execute()'ing a new SQL Query for each message. This
patch improves the situation by allowing us to use executemany(), which
is -despite still being one SQL query per message- much faster. This is
because rather than performing a commit() after each mail, we now do
only one commit() after all mails have been deleted.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
2012-01-17 04:17:15 +01:00
Sebastian Spaeth
80e87d0d99 Don't pass in 'root' as para to LocalStatusFolders
They have the Repository() which contains the root, so no need to pass
it in as an extra parameter. Rename repository.LocalStatus()'s
self.directory to self.root for consistency with other backends.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-09-16 12:54:12 +02:00
Sebastian Spaeth
ee75e0921f Remove 'config' as parameter from BaseFolder & derivatives
It is possible to get the config parameter from the Repository() which is
set in BaseFolder, so we set self.config there and remove the various
methods and 'config' parameters that are superfluous.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-09-16 12:54:12 +02:00
Sebastian Spaeth
410e2d35e9 Set accountname in BaseFolder, and don't pass it in initialization
We passed in the accountname to all derivatives of BaseFolder, such as
IMAPFolder(...,repository,...,accountname), although it is perfectly
possible to get the accountname from the Repository(). So remove this
unneeded parameter. Each backend had to define getaccountname() (although
the function is hardly used and most accessed .accountname directly).

On the other hand BaseFolder was using getaccountname but it never defined
the function. So make the sane thing, remove all definitions from backends
and define accountname() once in Basefolder. It was made a property and not
just a (public) attribute, so it will show up in our developer
documentation as public API.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-09-16 12:54:12 +02:00
Sebastian Spaeth
f0869e5c0f Fix sqlite upgrade code to use correct API call
We were using self.getfolderbasename(self.name) but the API is simply
getfolderbasename(). Fix this glitch.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-09-02 21:59:35 +02:00
Sebastian Spaeth
dcc50efc4f Don't call set().sort()
as that method doesn't exist on sets. Rather call the inbuilt
sorted(flags). This fixes Exceptions being thrown when using the sqlite
backend.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-23 20:59:48 +02:00
Sebastian Spaeth
466ded04d9 Make flags a set rather than a list
As this is essentially what it is, a set of values. This allows as
to do set arithmetics to see, e.g. the intersection of 2 flag sets
rather than clunkily having to do:

for flag in newflags:
  if flag not in oldflags:
    oldflags.append(flag)

Also some more code documenting.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-19 18:56:10 +02:00
Sebastian Spaeth
373e7cdbc1 Allow empty foldernames
Empty foldernames (as they could be created through nametrans) were
failing as the uidvalidity and status files names as determined by
folder/Base.py:getfolderbasename() lead to invalid file names ''.

Fix this by handling empty file names and translating them to '.' which
leads to the special file name 'dot'. (this special value existed before
and was not invented by this patch)

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-19 18:37:00 +02:00
Nicolas Sebrecht
b25a72f8c8 cleanup: remove uneeded imports
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-09 22:42:15 +02:00
Sebastian Spaeth
cff792d381 LocalStatusSQLite: Fix bug when deleting messages
The syntax was not right, and deleting messages from the LocalStatus
failed. (We passed in the full list of uids and we need to pass in one
uid at a time (as a tuple). Deleting messages works now.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-08 14:34:55 +02:00
Sebastian Spaeth
0af9ef70a7 Factor out SQL retries
Test if sqlite is multithreading-safe and bail out if not. sqlite
versions since at least 2008 are.
But, as it still causes errors when 2
threads try to write to the same connection simultanously (We get a
"cannot start transaction within a transaction" error), we protect
writes with a per class, ie per-connection lock. Factor out the retrying
to write when the database is locked.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-07 13:29:11 +02:00
Sebastian Spaeth
e1e9c8e831 Use self.doautosave rather than self.dofsync
doautosave was a useless variable before (it was *always* 1). So we
remove the self.dofsync variable and store in doautosave whether we
should fsync as often as possible (which really hurts performance).

The sqlite backend could (at one point) use the doautosave variable to
determine if it should autocommit after each modification.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-07 13:29:11 +02:00
Sebastian Spaeth
2fc12e875a Experimental LocalStatus stored in SQLite database
Based on patches by Stewart Smith, updated by Rob Browning.

plus:
- Inherit LocalStatusSQLFolder from LocalStatusFolder
  This lets us remove all functions that are available via our ancestors
  classes and are not needed.

- Don't fail if pysql import fails. Fail rather at runtime when needed.

- When creating the db file, create a metadata table which contains the
  format version info, so we can upgrade nicely to other formats.

- Create an upgrade_db() function which allows us to upgrade from any
  previous file format to the current one (including plain text)

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-07 13:29:11 +02:00