From b39224047b1a287143930aa4b88f1c9f13bb23d7 Mon Sep 17 00:00:00 2001
From: Artur Sadurski <asadurski@users.noreply.github.com>
Date: Fri, 16 Jul 2021 11:14:49 +0200
Subject: [PATCH 1/4] restrict SQLAlchemy to versions before 1.4

Fixes "Can't operate on closed transaction inside context manager." error on file save.
The code uses old-style connections.
---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index d055913..00b017d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ Jinja2>=2.7.3
 Mako>=1.0.0
 MarkupSafe>=0.23
 Pygments>=2.0.1
-SQLAlchemy>=1.0.5
+SQLAlchemy>=1.0.5,<1.4
 alembic>=0.7.6
 backports.ssl-match-hostname>=3.4.0.2
 certifi>=14.05.14

From 6e4f8182c2d4ef97e86daea9b234e4b1664d8c46 Mon Sep 17 00:00:00 2001
From: artur <a.sadurski@gmail.com>
Date: Tue, 26 Mar 2024 19:18:21 +0100
Subject: [PATCH 2/4] IPython compatibility patch

---
 pgcontents/utils/ipycompat.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pgcontents/utils/ipycompat.py b/pgcontents/utils/ipycompat.py
index 7e59d90..f480629 100644
--- a/pgcontents/utils/ipycompat.py
+++ b/pgcontents/utils/ipycompat.py
@@ -3,7 +3,7 @@
 """
 import IPython
 
-SUPPORTED_VERSIONS = {3, 4, 5}
+SUPPORTED_VERSIONS = {3, 4, 5, 6, 7, 8}
 IPY_MAJOR = IPython.version_info[0]
 if IPY_MAJOR not in SUPPORTED_VERSIONS:
     raise ImportError("IPython version %d is not supported." % IPY_MAJOR)

From a89903573a2a06f1cb208606b05df66264bbadf5 Mon Sep 17 00:00:00 2001
From: artur <a.sadurski@gmail.com>
Date: Tue, 9 Jul 2024 18:03:57 +0200
Subject: [PATCH 3/4] pin SQLAlchemy version

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 00b017d..6288799 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ Jinja2>=2.7.3
 Mako>=1.0.0
 MarkupSafe>=0.23
 Pygments>=2.0.1
-SQLAlchemy>=1.0.5,<1.4
+SQLAlchemy==1.4.52
 alembic>=0.7.6
 backports.ssl-match-hostname>=3.4.0.2
 certifi>=14.05.14

From 3319a95a7571e6e70fe3c47b41a27f10ac8e4c86 Mon Sep 17 00:00:00 2001
From: artur <a.sadurski@gmail.com>
Date: Thu, 18 Jul 2024 12:21:59 +0200
Subject: [PATCH 4/4] upgrade sessions code to SQLAlchemy 1.4 style

---
 pgcontents/pgmanager.py | 11 ++++++----
 pgcontents/query.py     | 45 +++++++++++++++++++++--------------------
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/pgcontents/pgmanager.py b/pgcontents/pgmanager.py
index c4eea1f..ed27bdb 100644
--- a/pgcontents/pgmanager.py
+++ b/pgcontents/pgmanager.py
@@ -17,6 +17,8 @@
 """
 from __future__ import unicode_literals
 from itertools import chain
+
+from sqlalchemy.orm import Session
 from tornado import web
 
 from .api_utils import (
@@ -331,13 +333,14 @@ def save(self, model, path):
         if model['type'] not in ('file', 'directory', 'notebook'):
             self.do_400("Unhandled contents type: %s" % model['type'])
         try:
-            with self.engine.begin() as db:
+            session = Session(self.engine)
+            with session.begin():
                 if model['type'] == 'notebook':
-                    validation_message = self._save_notebook(db, model, path)
+                    validation_message = self._save_notebook(session, model, path)
                 elif model['type'] == 'file':
-                    validation_message = self._save_file(db, model, path)
+                    validation_message = self._save_file(session, model, path)
                 else:
-                    validation_message = self._save_directory(db, path)
+                    validation_message = self._save_directory(session, path)
         except (web.HTTPError, PathOutsideRoot):
             raise
         except FileTooLarge:
diff --git a/pgcontents/query.py b/pgcontents/query.py
index 0140d81..bc1437f 100644
--- a/pgcontents/query.py
+++ b/pgcontents/query.py
@@ -298,6 +298,7 @@ def _file_default_fields():
         files.c.name,
         files.c.created_at,
         files.c.parent_name,
+        files.c.user_id,
     ]
 
 
@@ -472,32 +473,32 @@ def save_file(db, user_id, path, content, max_size_bytes):
     """
     check_content(content, max_size_bytes)
     directory, name = split_api_filepath(path)
-    with db.begin_nested() as savepoint:
-        try:
+    savepoint = db.begin_nested()
+    try:
+        res = db.execute(
+            files.insert().values(
+                name=name,
+                user_id=user_id,
+                parent_name=directory,
+                content=content,
+            )
+        )
+    except IntegrityError as error:
+        # The file already exists, so overwrite its content with the newer
+        # version.
+        if is_unique_violation(error):
+            savepoint.rollback()
             res = db.execute(
-                files.insert().values(
-                    name=name,
-                    user_id=user_id,
-                    parent_name=directory,
+                files.update().where(
+                    _file_where(user_id, path),
+                ).values(
                     content=content,
+                    created_at=func.now(),
                 )
             )
-        except IntegrityError as error:
-            # The file already exists, so overwrite its content with the newer
-            # version.
-            if is_unique_violation(error):
-                savepoint.rollback()
-                res = db.execute(
-                    files.update().where(
-                        _file_where(user_id, path),
-                    ).values(
-                        content=content,
-                        created_at=func.now(),
-                    )
-                )
-            else:
-                # Unknown error.  Reraise
-                raise
+        else:
+            # Unknown error.  Reraise
+            raise
 
     return res