Index: /pykota/trunk/pykota/storages/sql.py
===================================================================
--- /pykota/trunk/pykota/storages/sql.py (revision 3524)
+++ /pykota/trunk/pykota/storages/sql.py (revision 3531)
@@ -36,8 +36,8 @@
         user = StorageUser(self, username)
         user.ident = record.get("uid", record.get("userid", record.get("id")))
-        user.LimitBy = record.get("limitby") or "quota"
+        user.LimitBy = databaseToUnicode(record.get("limitby") or "quota")
         user.AccountBalance = record.get("balance")
         user.LifeTimePaid = record.get("lifetimepaid")
-        user.Email = record.get("email")
+        user.Email = databaseToUnicode(record.get("email"))
         user.Description = databaseToUnicode(record.get("description"))
         user.OverCharge = record.get("overcharge", 1.0)
@@ -49,5 +49,5 @@
         group = StorageGroup(self, groupname)
         group.ident = record.get("id")
-        group.LimitBy = record.get("limitby") or "quota"
+        group.LimitBy = databaseToUnicode(record.get("limitby") or "quota")
         group.AccountBalance = record.get("balance")
         group.LifeTimePaid = record.get("lifetimepaid")
@@ -85,5 +85,5 @@
         job.JobOptions = databaseToUnicode(record.get("options") or "")
         job.JobDate = record.get("jobdate")
-        job.JobHostName = record.get("hostname")
+        job.JobHostName = databaseToUnicode(record.get("hostname"))
         job.JobSizeBytes = record.get("jobsizebytes")
         job.JobMD5Sum = record.get("md5sum")
@@ -587,8 +587,8 @@
         self.doModify("INSERT INTO users (username, limitby, balance, lifetimepaid, email, overcharge, description) VALUES (%s, %s, %s, %s, %s, %s, %s)" % \
                                      (self.doQuote(unicodeToDatabase(user.Name)), \
-                                      self.doQuote(user.LimitBy or 'quota'), \
+                                      self.doQuote(unicodeToDatabase(user.LimitBy or 'quota')), \
                                       self.doQuote(user.AccountBalance or 0.0), \
                                       self.doQuote(user.LifeTimePaid or 0.0), \
-                                      self.doQuote(user.Email), \
+                                      self.doQuote(unicodeToDatabase(user.Email)), \
                                       self.doQuote(user.OverCharge), \
                                       self.doQuote(unicodeToDatabase(user.Description))))
@@ -607,5 +607,5 @@
         self.doModify("INSERT INTO groups (groupname, limitby, description) VALUES (%s, %s, %s)" % \
                               (self.doQuote(unicodeToDatabase(group.Name)), \
-                               self.doQuote(group.LimitBy or "quota"), \
+                               self.doQuote(unicodeToDatabase(group.LimitBy or "quota")), \
                                self.doQuote(unicodeToDatabase(group.Description))))
         group.isDirty = False
@@ -673,8 +673,8 @@
         """Saves the user to the database in a single operation."""
         self.doModify("UPDATE users SET limitby=%s, balance=%s, lifetimepaid=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \
-                               % (self.doQuote(user.LimitBy or 'quota'), \
+                               % (self.doQuote(unicodeToDatabase(user.LimitBy or 'quota')), \
                                   self.doQuote(user.AccountBalance or 0.0), \
                                   self.doQuote(user.LifeTimePaid or 0.0), \
-                                  self.doQuote(user.Email), \
+                                  self.doQuote(unicodeToDatabase(user.Email)), \
                                   self.doQuote(user.OverCharge), \
                                   self.doQuote(unicodeToDatabase(user.Description)), \
@@ -684,5 +684,5 @@
         """Saves the group to the database in a single operation."""
         self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \
-                               % (self.doQuote(group.LimitBy or 'quota'), \
+                               % (self.doQuote(unicodeToDatabase(group.LimitBy or 'quota')), \
                                   self.doQuote(unicodeToDatabase(group.Description)), \
                                   self.doQuote(group.ident)))
@@ -736,43 +736,94 @@
             # For legal reasons, we want to hide the title, filename and options
             title = filename = options = "hidden"
-        filename = unicodeToDatabase(filename)
-        title = unicodeToDatabase(title)
-        options = unicodeToDatabase(options)
-        jobbilling = unicodeToDatabase(jobbilling)
         if (not self.disablehistory) or (not printer.LastJob.Exists) :
             if jobsize is not None :
-                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice)))
+                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" \
+                                  % (self.doQuote(user.ident),
+                                     self.doQuote(printer.ident),
+                                     self.doQuote(jobid),
+                                     self.doQuote(pagecounter),
+                                     self.doQuote(action),
+                                     self.doQuote(jobsize),
+                                     self.doQuote(jobprice),
+                                     self.doQuote(unicodeToDatabase(filename)),
+                                     self.doQuote(unicodeToDatabase(title)),
+                                     self.doQuote(copies),
+                                     self.doQuote(unicodeToDatabase(options)),
+                                     self.doQuote(unicodeToDatabase(clienthost)),
+                                     self.doQuote(jobsizebytes),
+                                     self.doQuote(jobmd5sum),
+                                     self.doQuote(jobpages),
+                                     self.doQuote(unicodeToDatabase(jobbilling)),
+                                     self.doQuote(precomputedsize),
+                                     self.doQuote(precomputedprice)))
             else :
-                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice)))
+                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" \
+                                  % (self.doQuote(user.ident),
+                                     self.doQuote(printer.ident),
+                                     self.doQuote(jobid),
+                                     self.doQuote(pagecounter),
+                                     self.doQuote(action),
+                                     self.doQuote(unicodeToDatabase(filename)),
+                                     self.doQuote(unicodeToDatabase(title)),
+                                     self.doQuote(copies),
+                                     self.doQuote(unicodeToDatabase(options)),
+                                     self.doQuote(unicodeToDatabase(clienthost)),
+                                     self.doQuote(jobsizebytes),
+                                     self.doQuote(jobmd5sum),
+                                     self.doQuote(jobpages),
+                                     self.doQuote(unicodeToDatabase(jobbilling)),
+                                     self.doQuote(precomputedsize),
+                                     self.doQuote(precomputedprice)))
         else :
             # here we explicitly want to reset jobsize to NULL if needed
-            self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, hostname=%s, jobsizebytes=%s, md5sum=%s, pages=%s, billingcode=%s, precomputedjobsize=%s, precomputedjobprice=%s, jobdate=now() WHERE id=%s" % (self.doQuote(user.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice), self.doQuote(printer.LastJob.ident)))
+            self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, hostname=%s, jobsizebytes=%s, md5sum=%s, pages=%s, billingcode=%s, precomputedjobsize=%s, precomputedjobprice=%s, jobdate=now() WHERE id=%s" \
+                              % (self.doQuote(user.ident),
+                                 self.doQuote(jobid),
+                                 self.doQuote(pagecounter),
+                                 self.doQuote(action),
+                                 self.doQuote(jobsize),
+                                 self.doQuote(jobprice),
+                                 self.doQuote(unicodeToDatabase(filename)),
+                                 self.doQuote(unicodeToDatabase(title)),
+                                 self.doQuote(copies),
+                                 self.doQuote(unicodeToDatabase(options)),
+                                 self.doQuote(unicodeToDatabase(clienthost)),
+                                 self.doQuote(jobsizebytes),
+                                 self.doQuote(jobmd5sum),
+                                 self.doQuote(jobpages),
+                                 self.doQuote(unicodeToDatabase(jobbilling)),
+                                 self.doQuote(precomputedsize),
+                                 self.doQuote(precomputedprice),
+                                 self.doQuote(printer.LastJob.ident)))
 
     def saveUserPQuota(self, userpquota) :
         """Saves an user print quota entry."""
         self.doModify("UPDATE userpquota SET softlimit=%s, hardlimit=%s, warncount=%s, datelimit=%s, pagecounter=%s, lifepagecounter=%s, maxjobsize=%s WHERE id=%s" \
-                              % (self.doQuote(userpquota.SoftLimit), \
-                                 self.doQuote(userpquota.HardLimit), \
-                                 self.doQuote(userpquota.WarnCount or 0), \
-                                 self.doQuote(userpquota.DateLimit), \
-                                 self.doQuote(userpquota.PageCounter or 0), \
-                                 self.doQuote(userpquota.LifePageCounter or 0), \
-                                 self.doQuote(userpquota.MaxJobSize), \
+                              % (self.doQuote(userpquota.SoftLimit),
+                                 self.doQuote(userpquota.HardLimit),
+                                 self.doQuote(userpquota.WarnCount or 0),
+                                 self.doQuote(userpquota.DateLimit),
+                                 self.doQuote(userpquota.PageCounter or 0),
+                                 self.doQuote(userpquota.LifePageCounter or 0),
+                                 self.doQuote(userpquota.MaxJobSize),
                                  self.doQuote(userpquota.ident)))
 
     def writeUserPQuotaWarnCount(self, userpquota, warncount) :
         """Sets the warn counter value for a user quota."""
-        self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" % (self.doQuote(warncount), self.doQuote(userpquota.ident)))
+        self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" \
+                          % (self.doQuote(warncount),
+                             self.doQuote(userpquota.ident)))
 
     def increaseUserPQuotaWarnCount(self, userpquota) :
         """Increases the warn counter value for a user quota."""
-        self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" % self.doQuote(userpquota.ident))
+        self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" \
+                          % self.doQuote(userpquota.ident))
 
     def saveGroupPQuota(self, grouppquota) :
         """Saves a group print quota entry."""
         self.doModify("UPDATE grouppquota SET softlimit=%s, hardlimit=%s, datelimit=%s WHERE id=%s" \
-                              % (self.doQuote(grouppquota.SoftLimit), \
-                                 self.doQuote(grouppquota.HardLimit), \
-                                 self.doQuote(grouppquota.DateLimit), \
+                              % (self.doQuote(grouppquota.SoftLimit),
+                                 self.doQuote(grouppquota.HardLimit),
+                                 self.doQuote(grouppquota.DateLimit),
                                  self.doQuote(grouppquota.ident)))
 
@@ -800,5 +851,5 @@
             where.append("printerid=%s" % self.doQuote(printer.ident))
         if hostname is not None :
-            where.append("hostname=%s" % self.doQuote(hostname))
+            where.append("hostname=%s" % self.doQuote(unicodeToDatabase(hostname)))
         if billingcode is not None :
             where.append("billingcode=%s" % self.doQuote(unicodeToDatabase(billingcode)))
Index: /pykota/trunk/pykota/storages/sqlitestorage.py
===================================================================
--- /pykota/trunk/pykota/storages/sqlitestorage.py (revision 3528)
+++ /pykota/trunk/pykota/storages/sqlitestorage.py (revision 3531)
@@ -38,9 +38,13 @@
         """Opens the SQLite database connection."""
         BaseStorage.__init__(self, pykotatool)
-
+        self.doModify = self.doQuery
         self.tool.logdebug("Trying to open database (dbname=%s)..." % repr(dbname))
         self.database = sqlite.connect(dbname, isolation_level=None)
         self.cursor = self.database.cursor()
         self.closed = False
+        try :
+            self.doQuery("PRAGMA foreign_keys = True;")
+        except PyKotaStorageError :
+            pass
         self.tool.logdebug("Database opened (dbname=%s)" % repr(dbname))
 
@@ -68,6 +72,6 @@
         self.tool.logdebug("Transaction aborted.")
 
-    def doRawSearch(self, query) :
-        """Does a raw search query."""
+    def doQuery(self, query) :
+        """Executes an SQL query."""
         query = query.strip()
         if not query.endswith(';') :
@@ -77,8 +81,12 @@
             self.cursor.execute(query)
         except self.database.Error, msg :
+            self.tool.logdebug("Query failed : %s" % repr(msg))
             raise PyKotaStorageError, repr(msg)
-        else :
-            result = self.cursor.fetchall()
-            return result
+
+    def doRawSearch(self, query) :
+        """Executes a raw search query."""
+        self.doQuery(query)
+        result = self.cursor.fetchall()
+        return result
 
     def doSearch(self, query) :
@@ -101,16 +109,4 @@
                 rows.append(rowdict)
             return rows
-
-    def doModify(self, query) :
-        """Does a (possibly multiple) modify query."""
-        query = query.strip()
-        if not query.endswith(';') :
-            query += ';'
-        self.querydebug("QUERY : %s" % query)
-        try :
-            self.cursor.execute(query)
-        except self.database.Error, msg :
-            self.tool.logdebug("Query failed : %s" % repr(msg))
-            raise PyKotaStorageError, repr(msg)
 
     def doQuote(self, field) :
Index: /pykota/trunk/pykota/version.py
===================================================================
--- /pykota/trunk/pykota/version.py (revision 3508)
+++ /pykota/trunk/pykota/version.py (revision 3531)
@@ -24,5 +24,5 @@
 import time
 
-__version__ = "1.27alpha8_unofficial"
+__version__ = "1.27alpha9_unofficial"
 
 __doc__ = "PyKota : a complete Printing Quota Solution for CUPS."
