<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[15314] CalendarServer/trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/15314">15314</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-11-16 07:15:23 -0800 (Mon, 16 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Provide option to do schema check in calendarserver_upgrade tool. Provide better error reporting of failed upgrades.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarservertapcaldavpy">CalendarServer/trunk/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolscmdlinepy">CalendarServer/trunk/calendarserver/tools/cmdline.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolsupgradepy">CalendarServer/trunk/calendarserver/tools/upgrade.py</a></li>
<li><a href="#CalendarServertrunkrequirementscstxt">CalendarServer/trunk/requirements-cs.txt</a></li>
<li><a href="#CalendarServertrunktwistedcaldavstdconfigpy">CalendarServer/trunk/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresqlpy">CalendarServer/trunk/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoreupgradesqlupgradepy">CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrade.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tap/caldav.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tap/caldav.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -1556,7 +1556,8 @@
</span><span class="cx">                 pps.addStep(
</span><span class="cx">                     UpgradeDatabaseSchemaStep(
</span><span class="cx">                         store, uid=overrideUID, gid=overrideGID,
</span><del>-                        failIfUpgradeNeeded=config.FailIfUpgradeNeeded
</del><ins>+                        failIfUpgradeNeeded=config.FailIfUpgradeNeeded,
+                        checkExistingSchema=config.CheckExistingSchema,
</ins><span class="cx">                     )
</span><span class="cx">                 )
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolscmdlinepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/cmdline.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/cmdline.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/calendarserver/tools/cmdline.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -29,9 +29,8 @@
</span><span class="cx"> 
</span><span class="cx"> from twisted.application.service import Service
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, succeed
</span><del>-from twisted.logger import STDLibLogObserver
</del><ins>+from twisted.logger import STDLibLogObserver, FileLogObserver, formatEventAsClassicLogText
</ins><span class="cx"> from twisted.python.logfile import LogFile
</span><del>-from twisted.python.log import addObserver
</del><span class="cx"> 
</span><span class="cx"> import sys
</span><span class="cx"> from errno import ENOENT, EACCES
</span><span class="lines">@@ -105,8 +104,9 @@
</span><span class="cx">             rotateLength=config.ErrorLogRotateMB * 1024 * 1024,
</span><span class="cx">             maxRotatedFiles=config.ErrorLogMaxRotatedFiles
</span><span class="cx">         )
</span><del>-        utilityLogObserver = Logger.makeFilteredFileLogObserver(utilityLogFile)
-        addObserver(utilityLogObserver)
</del><ins>+        utilityLogObserver = FileLogObserver(utilityLogFile, lambda event: formatEventAsClassicLogText(event))
+        utilityLogObserver._encoding = &quot;utf-8&quot;
+        Logger.beginLoggingTo([utilityLogObserver, ])
</ins><span class="cx"> 
</span><span class="cx">         config.ProcessType = &quot;Utility&quot;
</span><span class="cx">         config.UtilityServiceClass = _makeValidService
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolsupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/upgrade.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/upgrade.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/calendarserver/tools/upgrade.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -20,13 +20,15 @@
</span><span class="cx"> This tool allows any necessary upgrade to complete, then exits.
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from __future__ import print_function
</del><span class="cx"> import os
</span><span class="cx"> import sys
</span><span class="cx"> import time
</span><span class="cx"> 
</span><ins>+from txdav.common.datastore.sql import CommonDataStore
+
</ins><span class="cx"> from twisted.internet.defer import succeed
</span><span class="cx"> from twisted.logger import LogLevel, formatEvent
</span><ins>+from twisted.logger import FilteringLogObserver, LogLevelFilterPredicate
</ins><span class="cx"> from twisted.python.text import wordWrap
</span><span class="cx"> from twisted.python.usage import Options, UsageError
</span><span class="cx"> 
</span><span class="lines">@@ -77,6 +79,7 @@
</span><span class="cx"> 
</span><span class="cx">     optFlags = [
</span><span class="cx">         ['status', 's', &quot;Check database status and exit.&quot;],
</span><ins>+        ['check', 'c', &quot;Check current database schema and exit.&quot;],
</ins><span class="cx">         ['postprocess', 'p', &quot;Perform post-database-import processing.&quot;],
</span><span class="cx">         ['debug', 'D', &quot;Print log messages to STDOUT.&quot;],
</span><span class="cx">     ]
</span><span class="lines">@@ -146,6 +149,11 @@
</span><span class="cx">         # If we get this far the database is OK
</span><span class="cx">         if self.options[&quot;status&quot;]:
</span><span class="cx">             self.output.write(&quot;Database OK.\n&quot;)
</span><ins>+        elif self.options[&quot;check&quot;]:
+            if hasattr(CommonDataStore, &quot;checkSchemaResults&quot;):
+                self.output.write(&quot;Database check failed:\n{}\n&quot;.format(&quot;\n&quot;.join(CommonDataStore.checkSchemaResults))) #@UndefinedVariable
+            else:
+                self.output.write(&quot;Database check OK.\n&quot;)
</ins><span class="cx">         else:
</span><span class="cx">             self.output.write(&quot;Upgrade complete, shutting down.\n&quot;)
</span><span class="cx">         UpgraderService.started = True
</span><span class="lines">@@ -154,10 +162,15 @@
</span><span class="cx"> 
</span><span class="cx">     def doWorkWithoutStore(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Immediately stop.  The upgrade will have been run before this.
</del><ins>+        Immediately stop.  The upgrade will have been run before this and failed.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         if self.options[&quot;status&quot;]:
</span><span class="cx">             self.output.write(&quot;Upgrade needed.\n&quot;)
</span><ins>+        elif self.options[&quot;check&quot;]:
+            if hasattr(CommonDataStore, &quot;checkSchemaResults&quot;):
+                self.output.write(&quot;Database check failed:\n{}\n&quot;.format(&quot;\n&quot;.join(CommonDataStore.checkSchemaResults))) #@UndefinedVariable
+            else:
+                self.output.write(&quot;Database check OK.\n&quot;)
</ins><span class="cx">         else:
</span><span class="cx">             self.output.write(&quot;Upgrade failed.\n&quot;)
</span><span class="cx">         UpgraderService.started = True
</span><span class="lines">@@ -220,9 +233,12 @@
</span><span class="cx">         output.write(logDateString() + &quot; &quot; + text + &quot;\n&quot;)
</span><span class="cx">         output.flush()
</span><span class="cx"> 
</span><del>-    if not options[&quot;status&quot;]:
-        log.levels().setLogLevelForNamespace(None, LogLevel.debug)
-        log.observer.addObserver(onlyUpgradeEvents)
</del><ins>+    if not options[&quot;status&quot;] and not options[&quot;check&quot;]:
+        # When doing an upgrade always send L{LogLevel.warn} logging to the tool output
+        log.observer.addObserver(FilteringLogObserver(
+            onlyUpgradeEvents,
+            [LogLevelFilterPredicate(defaultLogLevel=LogLevel.warn), ]
+        ))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def customServiceMaker():
</span><span class="lines">@@ -232,9 +248,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _patchConfig(config):
</span><del>-        config.FailIfUpgradeNeeded = options[&quot;status&quot;]
</del><ins>+        config.FailIfUpgradeNeeded = options[&quot;status&quot;] or options[&quot;check&quot;]
+        config.CheckExistingSchema = options[&quot;check&quot;]
</ins><span class="cx">         if options[&quot;prefix&quot;]:
</span><span class="cx">             config.UpgradeHomePrefix = options[&quot;prefix&quot;]
</span><ins>+        if not options[&quot;status&quot;] and not options[&quot;check&quot;]:
+            config.DefaultLogLevel = &quot;debug&quot;
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def _onShutdown():
</span></span></pre></div>
<a id="CalendarServertrunkrequirementscstxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/requirements-cs.txt (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/requirements-cs.txt        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/requirements-cs.txt        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -7,7 +7,7 @@
</span><span class="cx">     zope.interface==4.1.3
</span><span class="cx">             setuptools==18.5
</span><span class="cx"> 
</span><del>-    --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@15306#egg=twextpy
</del><ins>+    --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@15312#egg=twextpy
</ins><span class="cx">         cffi==1.3.0
</span><span class="cx">             pycparser==2.14
</span><span class="cx">         #twisted
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/stdconfig.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -248,6 +248,10 @@
</span><span class="cx">                                    # tools from running if the database needs a schema
</span><span class="cx">                                    # upgrade.
</span><span class="cx"> 
</span><ins>+    &quot;CheckExistingSchema&quot;: False,  # Set to True to check the current database schema
+                                   # against the schema file matching the database schema
+                                   # version.
+
</ins><span class="cx">     &quot;UpgradeHomePrefix&quot;: &quot;&quot;,    # When upgrading, only upgrade homes where the owner UID starts with
</span><span class="cx">                                 # the specified prefix. The upgrade will only be partial and only
</span><span class="cx">                                 # apply to upgrade pieces that affect entire homes. The upgrade will
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -62,6 +62,7 @@
</span><span class="cx"> from txdav.common.datastore.sql_apn import APNSubscriptionsMixin
</span><span class="cx"> from txdav.common.datastore.sql_directory import DelegatesAPIMixin, \
</span><span class="cx">     GroupsAPIMixin, GroupCacherAPIMixin
</span><ins>+from txdav.common.datastore.sql_dump import dumpSchema
</ins><span class="cx"> from txdav.common.datastore.sql_imip import imipAPIMixin
</span><span class="cx"> from txdav.common.datastore.sql_notification import NotificationCollection
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_OWN, _BIND_STATUS_ACCEPTED, \
</span><span class="lines">@@ -390,7 +391,33 @@
</span><span class="cx">             returnValue((False, None,))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    @inlineCallbacks
+    def checkSchema(self, expected_schema, schema_name):
+        &quot;&quot;&quot;
+        Check the schema actually in the database against the one in the supplied
+        schema file.
</ins><span class="cx"> 
</span><ins>+        @param expected_schema: the expected schema
+        @type expected_schema: L{Schema}
+        &quot;&quot;&quot;
+        txn = yield self.newTransaction(label=&quot;CommonDataStore.checkSchema&quot;)
+        try:
+            actual_schema = yield dumpSchema(txn, &quot;actual&quot;, schema_name)
+            results = actual_schema.compare(expected_schema)
+            if results:
+                if not hasattr(self.__class__, &quot;checkSchemaResults&quot;):
+                    self.__class__.checkSchemaResults = results
+                log.warn(&quot;Schema comparison mismatch:\n{}&quot;.format(&quot;\n&quot;.join(results)))
+            else:
+                log.warn(&quot;Schema comparison match&quot;)
+        except Exception as e:
+            log.error(&quot;Schema comparison match failed: {}&quot;.format(e))
+            yield txn.abort()
+        else:
+            yield txn.commit()
+
+
+
</ins><span class="cx"> class TransactionStatsCollector(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Used to log each SQL query and statistics about that query during the course of a single transaction.
</span><span class="lines">@@ -990,7 +1017,11 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         for stmt in splitSQLString(sql):
</span><span class="cx">             if not stmt.startswith(&quot;--&quot;):
</span><del>-                yield self.execSQL(stmt)
</del><ins>+                try:
+                    yield self.execSQL(stmt)
+                except (RuntimeError, StandardError) as e:
+                    e.stmt = &quot;SQLBlock statement failed: {}&quot;.format(stmt)
+                    raise
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def commit(self):
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoreupgradesqlupgradepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrade.py (15313 => 15314)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrade.py        2015-11-15 18:17:50 UTC (rev 15313)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/upgrade.py        2015-11-16 15:15:23 UTC (rev 15314)
</span><span class="lines">@@ -22,6 +22,7 @@
</span><span class="cx"> 
</span><span class="cx"> import re
</span><span class="cx"> 
</span><ins>+from twext.enterprise.dal.parseschema import schemaFromPath
</ins><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> 
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="lines">@@ -29,6 +30,8 @@
</span><span class="cx"> from twisted.python.modules import getModule
</span><span class="cx"> from twisted.python.reflect import namedObject
</span><span class="cx"> 
</span><ins>+from twistedcaldav.config import config
+
</ins><span class="cx"> from txdav.common.datastore.upgrade.sql.others import attachment_migration
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -96,7 +99,7 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     log = Logger()
</span><span class="cx"> 
</span><del>-    def __init__(self, sqlStore, uid=None, gid=None, failIfUpgradeNeeded=False):
</del><ins>+    def __init__(self, sqlStore, uid=None, gid=None, failIfUpgradeNeeded=False, checkExistingSchema=False):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Initialize the service.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -104,6 +107,7 @@
</span><span class="cx">         self.uid = uid
</span><span class="cx">         self.gid = gid
</span><span class="cx">         self.failIfUpgradeNeeded = failIfUpgradeNeeded
</span><ins>+        self.checkExistingSchema = checkExistingSchema
</ins><span class="cx">         self.schemaLocation = getModule(__name__).filePath.parent().parent().sibling(&quot;sql_schema&quot;)
</span><span class="cx">         self.pyLocation = getModule(__name__).filePath.parent()
</span><span class="cx"> 
</span><span class="lines">@@ -133,6 +137,14 @@
</span><span class="cx"> 
</span><span class="cx">         if required_version == actual_version:
</span><span class="cx">             self.log.warn(&quot;{vers} version check complete: no upgrade needed.&quot;, vers=self.versionDescriptor.capitalize())
</span><ins>+            if self.checkExistingSchema:
+                if dialect == &quot;postgres-dialect&quot;:
+                    expected_schema = self.schemaLocation.child(&quot;current.sql&quot;)
+                    schema_name = &quot;public&quot;
+                else:
+                    expected_schema = self.schemaLocation.child(&quot;current-oracle-dialect.sql&quot;)
+                    schema_name = config.DatabaseConnection.user
+                yield self.sqlStore.checkSchema(schemaFromPath(expected_schema), schema_name)
</ins><span class="cx">         elif required_version &lt; actual_version:
</span><span class="cx">             msg = &quot;Actual %s version %s is more recent than the expected version %s. The service cannot be started&quot; % (
</span><span class="cx">                 self.versionDescriptor, actual_version, required_version,
</span><span class="lines">@@ -140,6 +152,13 @@
</span><span class="cx">             self.log.error(msg)
</span><span class="cx">             raise RuntimeError(msg)
</span><span class="cx">         elif self.failIfUpgradeNeeded:
</span><ins>+            if self.checkExistingSchema:
+                expected_schema = self.schemaLocation.child(&quot;old&quot;).child(dialect).child(&quot;v{}.sql&quot;.format(actual_version))
+                if dialect == &quot;postgres-dialect&quot;:
+                    schema_name = &quot;public&quot;
+                else:
+                    schema_name = config.DatabaseConnection.user
+                yield self.sqlStore.checkSchema(schemaFromPath(expected_schema), schema_name)
</ins><span class="cx">             raise NotAllowedToUpgrade()
</span><span class="cx">         else:
</span><span class="cx">             self.sqlStore.setUpgrading(True)
</span><span class="lines">@@ -213,7 +232,7 @@
</span><span class="cx">             self.log.error(&quot;Database {vers} upgrade failed using: {path}&quot;, vers=self.versionDescriptor, path=fp.basename())
</span><span class="cx">             raise
</span><span class="cx"> 
</span><del>-        self.log.warn(&quot;{vers} upgraded from version {fr} to {o}.&quot;, vers=self.versionDescriptor.capitalize(), fr=fromVersion, to=toVersion)
</del><ins>+        self.log.warn(&quot;{vers} upgraded from version {fr} to {to}.&quot;, vers=self.versionDescriptor.capitalize(), fr=fromVersion, to=toVersion)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def getPathToUpgrades(self, dialect):
</span><span class="lines">@@ -318,7 +337,13 @@
</span><span class="cx">             sql = fp.getContent()
</span><span class="cx">             yield sqlTxn.execSQLBlock(sql)
</span><span class="cx">             yield sqlTxn.commit()
</span><del>-        except RuntimeError:
</del><ins>+        except (RuntimeError, StandardError) as e:
+            if hasattr(e, &quot;stmt&quot;):
+                self.log.error(&quot;Apply upgrade failed for '{basename}' on statement: {stmt}\n{err}&quot;.format(
+                    basename=fp.basename(),
+                    stmt=e.stmt,
+                    err=e,
+                ))
</ins><span class="cx">             f = Failure()
</span><span class="cx">             yield sqlTxn.abort()
</span><span class="cx">             f.raiseException()
</span></span></pre>
</div>
</div>

</body>
</html>