# Buggy code
def upload():
    if not config.config_uploading:
        abort(404)
    if request.method == "POST" and "btn-upload" in request.files:
        for requested_file in request.files.getlist("btn-upload"):
            # create the function for sorting...
            db.session.connection().connection.connection.create_function(
                "title_sort", 1, db.title_sort
            )
            db.session.connection().connection.connection.create_function(
                "uuid4", 0, lambda: str(uuid4())
            )

            # check if file extension is correct
            if "." in requested_file.filename:
                file_ext = requested_file.filename.rsplit(".", 1)[-1].lower()
                if file_ext not in EXTENSIONS_UPLOAD:
                    flash(
                        _(
                            "File extension '%(ext)s' is not allowed to be uploaded to this server",
                            ext=file_ext,
                        ),
                        category="error",
                    )
                    return redirect(url_for("index"))
            else:
                flash(_("File to be uploaded must have an extension"), category="error")
                return redirect(url_for("index"))

            # extract metadata from file
            meta = uploader.upload(requested_file)
            title = meta.title
            authr = meta.author
            tags = meta.tags
            series = meta.series
            series_index = meta.series_id
            title_dir = helper.get_valid_filename(title)
            author_dir = helper.get_valid_filename(authr)
            filepath = os.path.join(config.config_calibre_dir, author_dir, title_dir)
            saved_filename = os.path.join(filepath, title_dir + meta.extension.lower())

            # check if file path exists, otherwise create it, copy file to calibre path and delete temp file
            if not os.path.exists(filepath):
                try:
                    os.makedirs(filepath)
                except OSError:
                    flash(
                        _(
                            "Failed to create path %(path)s (Permission denied).",
                            path=filepath,
                        ),
                        category="error",
                    )
                    return redirect(url_for("index"))
            try:
                copyfile(meta.file_path, saved_filename)
            except OSError:
                flash(
                    _(
                        "Failed to store file %(file)s (Permission denied).",
                        file=saved_filename,
                    ),
                    category="error",
                )
                return redirect(url_for("index"))
            try:
                os.unlink(meta.file_path)
            except OSError:
                flash(
                    _(
                        "Failed to delete file %(file)s (Permission denied).",
                        file=meta.file_path,
                    ),
                    category="warning",
                )

            if meta.cover is None:
                has_cover = 0
                copyfile(
                    os.path.join(config.get_main_dir, "cps/static/generic_cover.jpg"),
                    os.path.join(filepath, "cover.jpg"),
                )
            else:
                has_cover = 1
                move(meta.cover, os.path.join(filepath, "cover.jpg"))

            # handle authors
            is_author = (
                db.session.query(db.Authors).filter(db.Authors.name == authr).first()
            )
            if is_author:
                db_author = is_author
            else:
                db_author = db.Authors(authr, helper.get_sorted_author(authr), "")
                db.session.add(db_author)

            # handle series
            db_series = None
            is_series = (
                db.session.query(db.Series).filter(db.Series.name == series).first()
            )
            if is_series:
                db_series = is_series
            elif series != "":
                db_series = db.Series(series, "")
                db.session.add(db_series)

            # add language actually one value in list
            input_language = meta.languages
            db_language = None
            if input_language != "":
                input_language = isoLanguages.get(name=input_language).part3
                hasLanguage = (
                    db.session.query(db.Languages)
                    .filter(db.Languages.lang_code == input_language)
                    .first()
                )
                if hasLanguage:
                    db_language = hasLanguage
                else:
                    db_language = db.Languages(input_language)
                    db.session.add(db_language)

            # combine path and normalize path from windows systems
            path = os.path.join(author_dir, title_dir).replace("\\", "/")
            db_book = db.Books(
                title,
                "",
                db_author.sort,
                datetime.datetime.now(),
                datetime.datetime(101, 1, 1),
                series_index,
                datetime.datetime.now(),
                path,
                has_cover,
                db_author,
                [],
                db_language,
            )
            db_book.authors.append(db_author)
            if db_series:
                db_book.series.append(db_series)
            if db_language is not None:
                db_book.languages.append(db_language)
            file_size = os.path.getsize(saved_filename)
            db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir)

            # handle tags
            input_tags = tags.split(",")
            input_tags = list(map(lambda it: it.strip(), input_tags))
            if input_tags[0] != "":
                modify_database_object(
                    input_tags, db_book.tags, db.Tags, db.session, "tags"
                )

            # flush content, get db_book.id available
            db_book.data.append(db_data)
            db.session.add(db_book)
            db.session.flush()

            # add comment
            book_id = db_book.id
            upload_comment = Markup(meta.description).unescape()
            if upload_comment != "":
                db.session.add(db.Comments(upload_comment, book_id))

            # save data to database, reread data
            db.session.commit()
            db.session.connection().connection.connection.create_function(
                "title_sort", 1, db.title_sort
            )
            book = (
                db.session.query(db.Books)
                .filter(db.Books.id == book_id)
                .filter(common_filters())
                .first()
            )

            # upload book to gdrive if nesseccary and add "(bookid)" to folder name
            if config.config_use_google_drive:
                gdriveutils.updateGdriveCalibreFromLocal()
            error = helper.update_dir_stucture(book.id, config.config_calibre_dir)
            db.session.commit()
            if config.config_use_google_drive:
                gdriveutils.updateGdriveCalibreFromLocal()
            if error:
                flash(error, category="error")
            uploadText = _("File %(file)s uploaded", file=book.title)
            helper.global_WorkerThread.add_upload(
                current_user.nickname,
                '<a href="'
                + url_for("show_book", book_id=book.id)
                + '">'
                + uploadText
                + "</a>",
            )

            # create data for displaying display Full language name instead of iso639.part3language
            if db_language is not None:
                book.languages[0].language_name = _(meta.languages)
            author_names = []
            for author in db_book.authors:
                author_names.append(author.name)
            if len(request.files.getlist("btn-upload")) < 2:
                cc = (
                    db.session.query(db.Custom_Columns)
                    .filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions))
                    .all()
                )
                if current_user.role_edit() or current_user.role_admin():
                    return render_title_template(
                        "book_edit.html",
                        book=book,
                        authors=author_names,
                        cc=cc,
                        title=_("edit metadata"),
                        page="upload",
                    )
                book_in_shelfs = []
                return render_title_template(
                    "detail.html",
                    entry=book,
                    cc=cc,
                    title=book.title,
                    books_shelfs=book_in_shelfs,
                    page="upload",
                )
    return redirect(url_for("index"))


# Traceback (OperationalError)
ERROR:cps.web:Exception on /book/125 [GET]
Traceback (most recent call last):
File "/Users/jgillman/Code/calibre-web/vendor/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/jgillman/Code/calibre-web/vendor/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/jgillman/Code/calibre-web/vendor/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/jgillman/Code/calibre-web/vendor/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/jgillman/Code/calibre-web/vendor/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/jgillman/Code/calibre-web/vendor/flask_login.py", line 717, in decorated_view
return func(*args, **kwargs)
File "/Users/jgillman/Code/calibre-web/cps/web.py", line 381, in show_book
return render_template('detail.html', entry=entries,  cc=cc, title=entries.title, books_shelfs=book_in_shelfs)
File "/Users/jgillman/Code/calibre-web/vendor/flask/templating.py", line 128, in render_template
context, ctx.app)
File "/Users/jgillman/Code/calibre-web/vendor/flask/templating.py", line 110, in _render
rv = template.render(context)
File "/Users/jgillman/Code/calibre-web/vendor/jinja2/environment.py", line 969, in render
return self.environment.handle_exception(exc_info, True)
File "/Users/jgillman/Code/calibre-web/vendor/jinja2/environment.py", line 742, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/jgillman/Code/calibre-web/cps/templates/detail.html", line 1, in top-level template code
{% extends "layout.html" %}
File "/Users/jgillman/Code/calibre-web/cps/templates/layout.html", line 136, in top-level template code
{% block body %}{% endblock %}
File "/Users/jgillman/Code/calibre-web/cps/templates/detail.html", line 66, in block "body"
{% if entry['custom_column_' ~ c.id]|length > 0 %}
File "/Users/jgillman/Code/calibre-web/vendor/jinja2/environment.py", line 387, in getitem
return getattr(obj, attr)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/attributes.py", line 316, in __get__
return self.impl.get(instance_state(instance), dict_)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/attributes.py", line 613, in get
value = self.callable_(state, passive)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/strategies.py", line 524, in _load_for_state
return self._emit_lazyload(session, state, ident_key, passive)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/strategies.py", line 585, in _emit_lazyload
result = q.all()
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/query.py", line 2241, in all
return list(self)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/query.py", line 2353, in __iter__
return self._execute_and_instances(context)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/orm/query.py", line 2368, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/base.py", line 662, in execute
params)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/base.py", line 761, in _execute_clauseelement
compiled_sql, distilled_params
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/base.py", line 874, in _execute_context
context)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/base.py", line 1024, in _handle_dbapi_exception
exc_info
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/util/compat.py", line 196, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/base.py", line 867, in _execute_context
context)
File "/Users/jgillman/Code/calibre-web/vendor/sqlalchemy/engine/default.py", line 324, in do_execute
cursor.execute(statement, parameters)
OperationalError: (OperationalError) no such table: books_custom_column_1_link u'SELECT custom_column_1.id AS custom_column_1_id, custom_column_1.value AS custom_column_1_value \
nFROM custom_column_1, books_custom_column_1_link \nWHERE ? = books_custom_column_1_link.book AND custom_column_1.id = books_custom_column_1_link.value' (125,)
ERROR:tornado.access:500 GET /book/125 (::1) 89.60ms

# Fixed code
def upload():
    if not config.config_uploading:
        abort(404)
    if request.method == "POST" and "btn-upload" in request.files:
        for requested_file in request.files.getlist("btn-upload"):
            # create the function for sorting...
            db.session.connection().connection.connection.create_function(
                "title_sort", 1, db.title_sort
            )
            db.session.connection().connection.connection.create_function(
                "uuid4", 0, lambda: str(uuid4())
            )

            # check if file extension is correct
            if "." in requested_file.filename:
                file_ext = requested_file.filename.rsplit(".", 1)[-1].lower()
                if file_ext not in EXTENSIONS_UPLOAD:
                    return Response(
                        _(
                            "File extension '%(ext)s' is not allowed to be uploaded to this server",
                            ext=file_ext,
                        )
                    ), 422
            else:
                return Response(_("File to be uploaded must have an extension")), 422

            # extract metadata from file
            meta = uploader.upload(requested_file)
            title = meta.title
            authr = meta.author
            tags = meta.tags
            series = meta.series
            series_index = meta.series_id
            title_dir = helper.get_valid_filename(title)
            author_dir = helper.get_valid_filename(authr)
            filepath = os.path.join(config.config_calibre_dir, author_dir, title_dir)
            saved_filename = os.path.join(filepath, title_dir + meta.extension.lower())

            # check if file path exists, otherwise create it, copy file to calibre path and delete temp file
            if not os.path.exists(filepath):
                try:
                    os.makedirs(filepath)
                except OSError:
                    return Response(
                        _(
                            "Failed to create path %(path)s (Permission denied).",
                            path=filepath,
                        )
                    ), 422
            try:
                copyfile(meta.file_path, saved_filename)
            except OSError:
                return Response(
                    _(
                        "Failed to store file %(file)s (Permission denied).",
                        file=saved_filename,
                    )
                ), 422

            try:
                os.unlink(meta.file_path)
            except OSError:
                flash(
                    _(
                        "Failed to delete file %(file)s (Permission denied).",
                        file=meta.file_path,
                    ),
                    category="warning",
                )

            if meta.cover is None:
                has_cover = 0
                copyfile(
                    os.path.join(config.get_main_dir, "cps/static/generic_cover.jpg"),
                    os.path.join(filepath, "cover.jpg"),
                )
            else:
                has_cover = 1
                move(meta.cover, os.path.join(filepath, "cover.jpg"))

            # handle authors
            is_author = (
                db.session.query(db.Authors).filter(db.Authors.name == authr).first()
            )
            if is_author:
                db_author = is_author
            else:
                db_author = db.Authors(authr, helper.get_sorted_author(authr), "")
                db.session.add(db_author)

            # handle series
            db_series = None
            is_series = (
                db.session.query(db.Series).filter(db.Series.name == series).first()
            )
            if is_series:
                db_series = is_series
            elif series != "":
                db_series = db.Series(series, "")
                db.session.add(db_series)

            # add language actually one value in list
            input_language = meta.languages
            db_language = None
            if input_language != "":
                input_language = isoLanguages.get(name=input_language).part3
                hasLanguage = (
                    db.session.query(db.Languages)
                    .filter(db.Languages.lang_code == input_language)
                    .first()
                )
                if hasLanguage:
                    db_language = hasLanguage
                else:
                    db_language = db.Languages(input_language)
                    db.session.add(db_language)

            # combine path and normalize path from windows systems
            path = os.path.join(author_dir, title_dir).replace("\\", "/")
            db_book = db.Books(
                title,
                "",
                db_author.sort,
                datetime.datetime.now(),
                datetime.datetime(101, 1, 1),
                series_index,
                datetime.datetime.now(),
                path,
                has_cover,
                db_author,
                [],
                db_language,
            )
            db_book.authors.append(db_author)
            if db_series:
                db_book.series.append(db_series)
            if db_language is not None:
                db_book.languages.append(db_language)
            file_size = os.path.getsize(saved_filename)
            db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir)

            # handle tags
            input_tags = tags.split(",")
            input_tags = list(map(lambda it: it.strip(), input_tags))
            if input_tags[0] != "":
                modify_database_object(
                    input_tags, db_book.tags, db.Tags, db.session, "tags"
                )

            # flush content, get db_book.id available
            db_book.data.append(db_data)
            db.session.add(db_book)
            db.session.flush()

            # add comment
            book_id = db_book.id
            upload_comment = Markup(meta.description).unescape()
            if upload_comment != "":
                db.session.add(db.Comments(upload_comment, book_id))

            # save data to database, reread data
            db.session.commit()
            db.session.connection().connection.connection.create_function(
                "title_sort", 1, db.title_sort
            )
            book = (
                db.session.query(db.Books)
                .filter(db.Books.id == book_id)
                .filter(common_filters())
                .first()
            )

            # upload book to gdrive if nesseccary and add "(bookid)" to folder name
            if config.config_use_google_drive:
                gdriveutils.updateGdriveCalibreFromLocal()
            error = helper.update_dir_stucture(book.id, config.config_calibre_dir)
            db.session.commit()
            if config.config_use_google_drive:
                gdriveutils.updateGdriveCalibreFromLocal()
            if error:
                flash(error, category="error")
            uploadText = _("File %(title)s", title=book.title)
            helper.global_WorkerThread.add_upload(
                current_user.nickname,
                '<a href="'
                + url_for("show_book", book_id=book.id)
                + '">'
                + uploadText
                + "</a>",
            )

            # create data for displaying display Full language name instead of iso639.part3language
            if db_language is not None:
                book.languages[0].language_name = _(meta.languages)
            author_names = []
            for author in db_book.authors:
                author_names.append(author.name)
            if len(request.files.getlist("btn-upload")) < 2:
                if current_user.role_edit() or current_user.role_admin():
                    resp = {"location": url_for("edit_book", book_id=db_book.id)}
                    return Response(json.dumps(resp), mimetype="application/json")
                else:
                    resp = {"location": url_for("show_book", book_id=db_book.id)}
                    return Response(json.dumps(resp), mimetype="application/json")
    return Response(
        json.dumps({"location": url_for("index")}), mimetype="application/json"
    )