На этом шаге мы рассмотрим особенности обработки исключений при доступе к базе данных.
Модуль sqlite3 поддерживает следующую иерархию исключений:
Exception Warning Error InterfaceError DatabaseError DataError ОperationalError IntegrityError InternalError ProgrammingError NotSupportedError
Базовым классом самого верхнего уровня является класс Exception. Все остальные исключения определены в модуле sqlite3. Поэтому при указании исключения в инструкции except следует предварительно указать название модуля (например, sqlite3.DatabaseError). Исключения возбуждаются в следующих случаях:
Для примера обработки исключений напишем программу, которая позволяет пользователям вводить название базы данных и SQL-команды в консоли: Листинг 18.19. Выполнение SQL-команд, введенных в консоли
# -*- coding: utf-8 -*- import sqlite3, sys, re def db_connect(db_name): try: db = sqlite3.connect (db_name, isolation_level=None) except (sqlite3.Error, sqlite3.Warning) as err: print("He удалось подключиться к БД") input() sys.exit(0) return db print ("Введите название базы данных:", end=" ") db_name = input () con = db_connect(db_name)# Подключаемся к базе cur = con.cursor() sql = "" print ("Чтобы закончить выполнение программы, введите <Q>+<Enter>") while True: tmp = input() if tmp in ["q", "Q"]: break if tmp.strip() == "": continue sql = "{0} {1}".format(sql, tmp) if sqlite3.complete_statement(sql): try: sql = sql.strip() cur.execute(sql) if re.match("SELECT ", sql, re.I): print(cur.fetchall()) except (sqlite3.Error, sqlite3.Warning) as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") sql = "" cur.close() con.close()
Результат работы приложения:
Введите название базы данных: :memory:
Чтобы закончить выполнение программы, введите <Q>+<Enter>
SELECT 10 > 5
;
Запрос успешно выполнен
SELECT 10 > 5;
[(1,)]
Запрос успешно выполнен
SELECT 10 > 5; SELECT 20 + 2;
Ошибка: You can only execute one statement at a time.
q
Чтобы SQL-запрос можно было разместить на нескольких строках, мы выполняем проверку завершенности запроса с помощью функции compiete_statement(<SQL-Запрос>). Функция возвращает True, если параметр содержит один или более полных SQL-запросов. Признаком завершенности запроса является точка с запятой. Никакой проверки правильности SQL-запроса не производится. Пример использования функции:
>>> sql = "SELECT 10 > 5;" >>> sqlite3.complete_statement (sql) True >>> sql = "SELECT 10 > 5" >>> sqlite3.complete_statement(sql) False >>> sql = "SELECT 10 > 5; SELECT 20 +2;" >>> sqlite3.complete_statement (sql) True
Язык Python также поддерживает протокол менеджеров контекста, который гарантирует выполнение завершающих действий вне зависимости от того, произошло исключение внутри блока кода или нет. В модуле sqlite3 объект соединения поддерживает этот протокол. Если внутри блока with не произошло исключение, то автоматически вызывается метод commit(). В противном случае все изменения отменяются с помощью метода rollback(). Для примера добавим три рубрики в таблицу rubr. В первом случае запрос будет без ошибок, а во втором случае выполним два запроса, последний из которых будет добавлять рубрику с уже существующим идентификатором:
# -*- coding: utf-8 -*- import sqlite3 con = sqlite3.connect(r"C:\book\catalog.db") try: with con: # Добавление новой рубрики con.execute("""INSERT INTO rubr VALUES (NULL, 'Мода')""") except sqlite3.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") try: with con: # Добавление новой рубрики con.execute("""INSERT INTO rubr VALUES (NULL, 'Спорт')""") # Рубрика с идентификатором 1 уже существует! con.execute("""INSERT INTO rubr VALUES (1, 'Казино')""") except sqlite3.DatabaseError as err: print("Ошибка:", err) else: print("Запрос успешно выполнен") con.close() input ()
Результат работы приложения:
Запрос успешно выполнен
Ошибка: UNIQUE constraint failed: rubr.id_rubr
Итак, в первом случае запрос не содержит ошибок, и рубрика мода будет успешно добавлена в таблицу. Во втором случае возникнет исключение IntegrityError. Поэтому ни рубрика Спорт, ни рубрика Казино в таблицу добавлены не будут, т. к. все изменения автоматически отменяются с помощью вызова метода rollback () (рисунок 1).
Рис.1. Содержимое таблицы rubr после выполнения программы
На следующем шаге мы рассмотрим трассировку выполняемых запросов.