We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Running jupyterhub upgrade-db with PostgreSQL database fails
**How to reproduce the issue**

Run `jupyterhub upgrade-db` with a PostgreSQL database to upgrade to 99a28a4418e1.

**What you expected to happen**

Successful schema update.

**What actually happens**

It fails with an sqlalchemy `ProgrammingError` message that originates here:

https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/alembic/versions/99a28a4418e1_user_created.py#L40

in particular I think that should be `IS NOT NULL` not just `NOT NULL`.  I substituted this live and it allowed the upgrade to proceed.

**Share what version of JupyterHub you are using**

Latest master.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `jupyterhub/alembic/versions/99a28a4418e1_user_created.py`
Content:
```
1 """user.created and spawner.started
2 
3 Revision ID: 99a28a4418e1
4 Revises: 56cc5a70207e
5 Create Date: 2018-03-21 14:27:17.466841
6 
7 """
8 
9 # revision identifiers, used by Alembic.
10 revision = '99a28a4418e1'
11 down_revision = '56cc5a70207e'
12 branch_labels = None
13 depends_on = None
14 
15 
16 from alembic import op
17 import sqlalchemy as sa
18 
19 from datetime import datetime
20 
21 def upgrade():
22     op.add_column('users', sa.Column('created', sa.DateTime, nullable=True))
23     c = op.get_bind()
24     # fill created date with current time
25     now = datetime.utcnow()
26     c.execute("""
27         UPDATE users
28         SET created='%s'
29         """ % (now,)
30     )
31 
32     tables = c.engine.table_names()
33 
34     if 'spawners' in tables:
35         op.add_column('spawners', sa.Column('started', sa.DateTime, nullable=True))
36         # fill started value with now for running servers
37         c.execute("""
38             UPDATE spawners
39             SET started='%s'
40             WHERE server_id NOT NULL
41             """ % (now,)
42         )
43 
44 
45 def downgrade():
46     op.drop_column('users', 'created')
47     op.drop_column('spawners', 'started')
48
```

Path: `jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py`
Content:
```
1 """token tracking
2 
3 Revision ID: 56cc5a70207e
4 Revises: 1cebaf56856c
5 Create Date: 2017-12-19 15:21:09.300513
6 
7 """
8 
9 # revision identifiers, used by Alembic.
10 revision = '56cc5a70207e'
11 down_revision = '1cebaf56856c'
12 branch_labels = None
13 depends_on = None
14 
15 from alembic import op
16 import sqlalchemy as sa
17 
18 
19 def upgrade():
20     tables = op.get_bind().engine.table_names()
21     op.add_column('api_tokens', sa.Column('created', sa.DateTime(), nullable=True))
22     op.add_column('api_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))
23     op.add_column('api_tokens', sa.Column('note', sa.Unicode(length=1023), nullable=True))
24     if 'oauth_access_tokens' in tables:
25         op.add_column('oauth_access_tokens', sa.Column('created', sa.DateTime(), nullable=True))
26         op.add_column('oauth_access_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))
27         op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
28         op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
29 
30 
31 def downgrade():
32     op.drop_constraint(None, 'oauth_codes', type_='foreignkey')
33     op.drop_constraint(None, 'oauth_access_tokens', type_='foreignkey')
34     op.drop_column('oauth_access_tokens', 'last_activity')
35     op.drop_column('oauth_access_tokens', 'created')
36     op.drop_column('api_tokens', 'note')
37     op.drop_column('api_tokens', 'last_activity')
38     op.drop_column('api_tokens', 'created')
39
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py b/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py
--- a/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py
+++ b/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py
@@ -15,6 +15,9 @@
 from alembic import op
 import sqlalchemy as sa
 
+import logging
+logger = logging.getLogger('alembic')
+
 
 def upgrade():
     tables = op.get_bind().engine.table_names()
@@ -24,8 +27,11 @@
     if 'oauth_access_tokens' in tables:
         op.add_column('oauth_access_tokens', sa.Column('created', sa.DateTime(), nullable=True))
         op.add_column('oauth_access_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))
-        op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
-        op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
+        if op.get_context().dialect.name == 'sqlite':
+            logger.warning("sqlite cannot use ALTER TABLE to create foreign keys. Upgrade will be incomplete.")
+        else:
+            op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
+            op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')
 
 
 def downgrade():
diff --git a/jupyterhub/alembic/versions/99a28a4418e1_user_created.py b/jupyterhub/alembic/versions/99a28a4418e1_user_created.py
--- a/jupyterhub/alembic/versions/99a28a4418e1_user_created.py
+++ b/jupyterhub/alembic/versions/99a28a4418e1_user_created.py
@@ -37,7 +37,7 @@
         c.execute("""
             UPDATE spawners
             SET started='%s'
-            WHERE server_id NOT NULL
+            WHERE server_id IS NOT NULL
             """ % (now,)
         )

{"golden_diff": "diff --git a/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py b/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py\n--- a/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py\n+++ b/jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py\n@@ -15,6 +15,9 @@\n from alembic import op\n import sqlalchemy as sa\n \n+import logging\n+logger = logging.getLogger('alembic')\n+\n \n def upgrade():\n     tables = op.get_bind().engine.table_names()\n@@ -24,8 +27,11 @@\n     if 'oauth_access_tokens' in tables:\n         op.add_column('oauth_access_tokens', sa.Column('created', sa.DateTime(), nullable=True))\n         op.add_column('oauth_access_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))\n-        op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n-        op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n+        if op.get_context().dialect.name == 'sqlite':\n+            logger.warning(\"sqlite cannot use ALTER TABLE to create foreign keys. Upgrade will be incomplete.\")\n+        else:\n+            op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n+            op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n \n \n def downgrade():\ndiff --git a/jupyterhub/alembic/versions/99a28a4418e1_user_created.py b/jupyterhub/alembic/versions/99a28a4418e1_user_created.py\n--- a/jupyterhub/alembic/versions/99a28a4418e1_user_created.py\n+++ b/jupyterhub/alembic/versions/99a28a4418e1_user_created.py\n@@ -37,7 +37,7 @@\n         c.execute(\"\"\"\n             UPDATE spawners\n             SET started='%s'\n-            WHERE server_id NOT NULL\n+            WHERE server_id IS NOT NULL\n             \"\"\" % (now,)\n         )\n", "issue": "Running jupyterhub upgrade-db with PostgreSQL database fails\n**How to reproduce the issue**\r\n\r\nRun `jupyterhub upgrade-db` with a PostgreSQL database to upgrade to 99a28a4418e1.\r\n\r\n**What you expected to happen**\r\n\r\nSuccessful schema update.\r\n\r\n**What actually happens**\r\n\r\nIt fails with an sqlalchemy `ProgrammingError` message that originates here:\r\n\r\nhttps://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/alembic/versions/99a28a4418e1_user_created.py#L40\r\n\r\nin particular I think that should be `IS NOT NULL` not just `NOT NULL`.  I substituted this live and it allowed the upgrade to proceed.\r\n\r\n**Share what version of JupyterHub you are using**\r\n\r\nLatest master.\n", "before_files": [{"content": "\"\"\"user.created and spawner.started\n\nRevision ID: 99a28a4418e1\nRevises: 56cc5a70207e\nCreate Date: 2018-03-21 14:27:17.466841\n\n\"\"\"\n\n# revision identifiers, used by Alembic.\nrevision = '99a28a4418e1'\ndown_revision = '56cc5a70207e'\nbranch_labels = None\ndepends_on = None\n\n\nfrom alembic import op\nimport sqlalchemy as sa\n\nfrom datetime import datetime\n\ndef upgrade():\n    op.add_column('users', sa.Column('created', sa.DateTime, nullable=True))\n    c = op.get_bind()\n    # fill created date with current time\n    now = datetime.utcnow()\n    c.execute(\"\"\"\n        UPDATE users\n        SET created='%s'\n        \"\"\" % (now,)\n    )\n\n    tables = c.engine.table_names()\n\n    if 'spawners' in tables:\n        op.add_column('spawners', sa.Column('started', sa.DateTime, nullable=True))\n        # fill started value with now for running servers\n        c.execute(\"\"\"\n            UPDATE spawners\n            SET started='%s'\n            WHERE server_id NOT NULL\n            \"\"\" % (now,)\n        )\n\n\ndef downgrade():\n    op.drop_column('users', 'created')\n    op.drop_column('spawners', 'started')\n", "path": "jupyterhub/alembic/versions/99a28a4418e1_user_created.py"}, {"content": "\"\"\"token tracking\n\nRevision ID: 56cc5a70207e\nRevises: 1cebaf56856c\nCreate Date: 2017-12-19 15:21:09.300513\n\n\"\"\"\n\n# revision identifiers, used by Alembic.\nrevision = '56cc5a70207e'\ndown_revision = '1cebaf56856c'\nbranch_labels = None\ndepends_on = None\n\nfrom alembic import op\nimport sqlalchemy as sa\n\n\ndef upgrade():\n    tables = op.get_bind().engine.table_names()\n    op.add_column('api_tokens', sa.Column('created', sa.DateTime(), nullable=True))\n    op.add_column('api_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))\n    op.add_column('api_tokens', sa.Column('note', sa.Unicode(length=1023), nullable=True))\n    if 'oauth_access_tokens' in tables:\n        op.add_column('oauth_access_tokens', sa.Column('created', sa.DateTime(), nullable=True))\n        op.add_column('oauth_access_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))\n        op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n        op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n\n\ndef downgrade():\n    op.drop_constraint(None, 'oauth_codes', type_='foreignkey')\n    op.drop_constraint(None, 'oauth_access_tokens', type_='foreignkey')\n    op.drop_column('oauth_access_tokens', 'last_activity')\n    op.drop_column('oauth_access_tokens', 'created')\n    op.drop_column('api_tokens', 'note')\n    op.drop_column('api_tokens', 'last_activity')\n    op.drop_column('api_tokens', 'created')\n", "path": "jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py"}], "after_files": [{"content": "\"\"\"user.created and spawner.started\n\nRevision ID: 99a28a4418e1\nRevises: 56cc5a70207e\nCreate Date: 2018-03-21 14:27:17.466841\n\n\"\"\"\n\n# revision identifiers, used by Alembic.\nrevision = '99a28a4418e1'\ndown_revision = '56cc5a70207e'\nbranch_labels = None\ndepends_on = None\n\n\nfrom alembic import op\nimport sqlalchemy as sa\n\nfrom datetime import datetime\n\ndef upgrade():\n    op.add_column('users', sa.Column('created', sa.DateTime, nullable=True))\n    c = op.get_bind()\n    # fill created date with current time\n    now = datetime.utcnow()\n    c.execute(\"\"\"\n        UPDATE users\n        SET created='%s'\n        \"\"\" % (now,)\n    )\n\n    tables = c.engine.table_names()\n\n    if 'spawners' in tables:\n        op.add_column('spawners', sa.Column('started', sa.DateTime, nullable=True))\n        # fill started value with now for running servers\n        c.execute(\"\"\"\n            UPDATE spawners\n            SET started='%s'\n            WHERE server_id IS NOT NULL\n            \"\"\" % (now,)\n        )\n\n\ndef downgrade():\n    op.drop_column('users', 'created')\n    op.drop_column('spawners', 'started')\n", "path": "jupyterhub/alembic/versions/99a28a4418e1_user_created.py"}, {"content": "\"\"\"token tracking\n\nRevision ID: 56cc5a70207e\nRevises: 1cebaf56856c\nCreate Date: 2017-12-19 15:21:09.300513\n\n\"\"\"\n\n# revision identifiers, used by Alembic.\nrevision = '56cc5a70207e'\ndown_revision = '1cebaf56856c'\nbranch_labels = None\ndepends_on = None\n\nfrom alembic import op\nimport sqlalchemy as sa\n\nimport logging\nlogger = logging.getLogger('alembic')\n\n\ndef upgrade():\n    tables = op.get_bind().engine.table_names()\n    op.add_column('api_tokens', sa.Column('created', sa.DateTime(), nullable=True))\n    op.add_column('api_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))\n    op.add_column('api_tokens', sa.Column('note', sa.Unicode(length=1023), nullable=True))\n    if 'oauth_access_tokens' in tables:\n        op.add_column('oauth_access_tokens', sa.Column('created', sa.DateTime(), nullable=True))\n        op.add_column('oauth_access_tokens', sa.Column('last_activity', sa.DateTime(), nullable=True))\n        if op.get_context().dialect.name == 'sqlite':\n            logger.warning(\"sqlite cannot use ALTER TABLE to create foreign keys. Upgrade will be incomplete.\")\n        else:\n            op.create_foreign_key(None, 'oauth_access_tokens', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n            op.create_foreign_key(None, 'oauth_codes', 'oauth_clients', ['client_id'], ['identifier'], ondelete='CASCADE')\n\n\ndef downgrade():\n    op.drop_constraint(None, 'oauth_codes', type_='foreignkey')\n    op.drop_constraint(None, 'oauth_access_tokens', type_='foreignkey')\n    op.drop_column('oauth_access_tokens', 'last_activity')\n    op.drop_column('oauth_access_tokens', 'created')\n    op.drop_column('api_tokens', 'note')\n    op.drop_column('api_tokens', 'last_activity')\n    op.drop_column('api_tokens', 'created')\n", "path": "jupyterhub/alembic/versions/56cc5a70207e_token_tracking.py"}]}