Как обрабатывать исключения в с++: что такое throw, try и catch

Introduction

Suppose you execute a query with multiple steps. We want to get a message once each step completes. It helps to track the query progress. Usually, we use the SQL PRINT statement to print corresponding messages or track the variable values while query progress.

We also use interactions or multiple loops in a query with a while or for a loop. We can also use the SQL PRINT
statement to track the iteration.

We use the SQL Server PRINT statement to return messages to the client. We specify the message as string expressions input. SQL Server returns the message to the application.

In this article, we will explore several use cases of SQL PRINT statement, its limitations, and alternatives of SQL
PRINT statements.

Example 1: SQL Server PRINT statement to print a string

It is the simplest example. Execute the following query in SSMS, and it returns the following message in the output:

1 PRINT’My Name is Rajendra Gupta’;

In SSMS, we get the PRINT statement output in SSMS message window as shown in the above image.

Example 2: PRINT statement to print a variable value

We can use the SQL PRINT statement to print a variable value as well. Let’s define a message in a variable and later
print this message:

1
2

DECLARE@MsgVARCHAR(300)=’My Name is Rajendra Gupta’;

PRINT@Msg;

Example 3: SQL Server PRINT statement to print an integer value

We can use the SQL PRINT statement to print an integer value, as shown below:

1
2

DECLARE@aINT=1000

PRINT@a

We can specify only CHAR, NCHAR, VARCHAR or NVARCHAR data types in the PRINT statement. In this case, it implicitly converts an integer value to the VARCHAR data type internally.

Let’s use another example with an integer variable and a string in the PRINT statement. You get an error message in
data type conversion because SQL Server is trying to convert the varchar data type to integer. In data type
precedence, the integer data type has high precedence than the varchar data type:

1
2

DECLARE@aINT=1000

PRINT’Your queue no is ‘+@a

We explicitly convert the integer data type to varchar using the SQL CAST statement:

1
2

DECLARE@aINT=1000;

PRINT’Your queue no is ‘+CAST(@aASVARCHAR(10));

We can use the SQL CONCAT function as well, and it automatically does data type conversion for us. In the following
query, we get the output using the CONCAT function similar to the CAST operator:

1
2

DECLARE@aINT=1000;

PRINTCONCAT(‘Your queue no is : ‘,@a)

Example 4: SQL Server PRINT statement with XML type variable value

We can use XML data type as well with the PRINT statement, but it requires data conversion.

As shown in the following output, we cannot directly use an XML variable in the PRINT statement. It gives an error
message that implicit conversion from XML to nvarchar is not allowed:

1
2

DECLARE@aXML='<CustomerID=»1″ CustomerName=»Rajendra»/>’

PRINT@a

We can use SQL CAST or CONVERT function explicitly and get the required output:

1
2

@aXML='<Customer id=»1″ Name=»Rajendra»/>’

PRINT CAST(@aASVARCHAR(100))

Example 5: SQL Server PRINT Statement with IF conditions

Let’s use the PRINT statement to print the message satisfied in the IF condition. In this example, the variable @a
contains a string. The IF condition checks for the string and prints message satisfying the condition:

1
2
3
4
5
6

DECLARE@aVARCHAR(100)=’Mango’;

IF@a=’Mango’

PRINTN’It is a Fruit’;

ELSE

PRINTN’It is a vegetable’;

GO

Example 6: PRINT Statement with NULL values

We cannot print NULL in the message using the SQL PRINT statement. The following query does not return any result:

1 PrintNULL

Let’s use the following query that contains a variable with NULL values. In the PRINT statement, we use a string
along with this variable, and it does not return any message. The concatenation of a string and the variable @a
(NULL) that does not return any output:

1
2

DECLARE@aNVarChar(100)=NULL

PRINT’Hello’+@a

Example 7: SQL Server PRINT Statement in a WHILE loop

As stated earlier, many times, we require knowing information about each iteration when query running in a loop such as WHILE or FOR.

The following query uses WHILE loop and prints a message about each iteration:

1
2
3
4
5
6
7

DECLARE@aINT;

SET@a=1;

WHILE(@a<10)

BEGIN

PRINT CONCAT(‘This is Iteration no:’,@a)

SET@a=@a+1;

END;

It gives the following output. We can use a Print statement with an appropriate message and track query progress.

try…catch…finally

Подождите, это ещё не всё.

Конструкция может содержать ещё одну секцию: .

Если секция есть, то она выполняется в любом случае:

  • после , если не было ошибок,
  • после , если ошибки были.

Расширенный синтаксис выглядит следующим образом:

Попробуйте запустить такой код:

У кода есть два пути выполнения:

  1. Если вы ответите на вопрос «Сгенерировать ошибку?» утвердительно, то .
  2. Если ответите отрицательно, то .

Секцию часто используют, когда мы начали что-то делать и хотим завершить это вне зависимости от того, будет ошибка или нет.

Например, мы хотим измерить время, которое занимает функция чисел Фибоначчи . Естественно, мы можем начать измерения до того, как функция начнёт выполняться и закончить после. Но что делать, если при вызове функции возникла ошибка? В частности, реализация в коде ниже возвращает ошибку для отрицательных и для нецелых чисел.

Секция отлично подходит для завершения измерений несмотря ни на что.

Здесь гарантирует, что время будет измерено корректно в обеих ситуациях – и в случае успешного завершения и в случае ошибки:

Вы можете это проверить, запустив этот код и введя в – код завершится нормально, выполнится после . А затем введите – незамедлительно произойдёт ошибка, выполнение займёт . Оба измерения выполняются корректно.

Другими словами, неважно как завершилась функция: через или. Секция срабатывает в обоих случаях

Переменные внутри локальны

Обратите внимание, что переменные и в коде выше объявлены до. Если переменную объявить в блоке, например, в , то она не будет доступна после него

Если переменную объявить в блоке, например, в , то она не будет доступна после него.

и

Блок срабатывает при любом выходе из , в том числе и .

В примере ниже из происходит , но получает управление до того, как контроль возвращается во внешний код.

Конструкция без секции также полезна. Мы применяем её, когда не хотим здесь обрабатывать ошибки (пусть выпадут), но хотим быть уверены, что начатые процессы завершились.

В приведённом выше коде ошибка всегда выпадает наружу, потому что тут нет блока . Но отрабатывает до того, как поток управления выйдет из функции.

3.7.1. Создание сообщений

Для создания собственных сообщений (message) используется процедура SQL сервера sp_addmessage. В общем виде эта процедура выглядит следующим образом:

sp_addmessage  msg_id , 
     severity , 
     'msg' 
     'language' ] 
     'with_log' ] 
     'replace' ] 

Параметров не так уж и много, поэтому давайте рассмотрим их, прежде чем напишем реальный пример:

  1. Номер (идентификатор) сообщения, который должен начинаться с 500001;
  2. Уровень критичности. К нему предъявляются такие же правила, как и у функции RAISERROR;
  3. Текст сообщения, максимальный размер которого 255 символов;
  4. Язык сообщения. По умолчанию используется нулевое значение и язык, установленный в системе;
  5. Нужно ли писать о событии в журнал сообщений Windows. Если в этом параметре указано true, то в журнал будет записано сообщение об ошибке. Если false, сообщение не обязательно будет записано в журнал, тут уже все зависит от того, как оно было сгенерировано;
  6. Если сообщение с указанным номером существует, то в этом параметре можно указать команду REPLACE. Это означает, что существующую ошибку с указанным номером надо заменить.

Давайте создадим свое сообщение:

EXEC sp_addmessage 60001, 16, 
   'Ошибка добавления записи'

Чуть позже мы увидим, как воспользоваться сообщениями.

Давайте рассмотрим, как можно удалять сообщения. Для этого используется процедура sp_dropmessage:

sp_dropmessage  message_number 
     'language' ]

Процедуре передается два параметра: номер удаляемого сообщения и язык. Если язык не указан, то будут удалены сообщения для всех национальных языков, существующих в сообщении.

Examples

A. Returning error information from a CATCH block

The following code example shows how to use inside a block to cause execution to jump to the associated block. It also shows how to use to return information about the error that invoked the block.

BEGIN TRY
    -- RAISERROR with severity 11-19 will cause execution to
    -- jump to the CATCH block.
    RAISERROR ('Error raised in TRY block.', -- Message text.
               16, -- Severity.
               1 -- State.
               );
END TRY
BEGIN CATCH
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;

    SELECT
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();

    -- Use RAISERROR inside the CATCH block to return error
    -- information about the original error that caused
    -- execution to jump to the CATCH block.
    RAISERROR (@ErrorMessage, -- Message text.
               @ErrorSeverity, -- Severity.
               @ErrorState -- State.
               );
END CATCH;

B. Creating an ad hoc message in sys.messages

The following example shows how to raise a message stored in the sys.messages catalog view. The message was added to the sys.messages catalog view by using the system stored procedure as message number .

EXEC sp_addmessage @msgnum = 50005,
              @severity = 10,
              @msgtext = N'<\<%7.3s>>';
GO
RAISERROR (50005, -- Message id.
           10, -- Severity,
           1, -- State,
           N'abcde'); -- First argument supplies the string.
-- The message text returned is: <<    abc>>.
GO
EXEC sp_dropmessage @msgnum = 50005;
GO

C. Using a local variable to supply the message text

The following code example shows how to use a local variable to supply the message text for a statement.

DECLARE @StringVariable NVARCHAR(50);
SET @StringVariable = N'<\<%7.3s>>';

RAISERROR (@StringVariable, -- Message text.
           10, -- Severity,
           1, -- State,
           N'abcde'); -- First argument supplies the string.
-- The message text returned is: <<    abc>>.
GO

8.9. Raising and Handling Multiple Unrelated Exceptions¶

There are situations where it is necessary to report several exceptions that
have occurred. This is often the case in concurrency frameworks, when several
tasks may have failed in parallel, but there are also other use cases where
it is desirable to continue execution and collect multiple errors rather than
raise the first exception.

The builtin wraps a list of exception instances so
that they can be raised together. It is an exception itself, so it can be
caught like any other exception.

>>> def f():
...     excs = OSError('error 1'), SystemError('error 2')]
...     raise ExceptionGroup('there were problems', excs)
...
>>> f()
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 1, in <module>
  |   File "<stdin>", line 3, in f
  | ExceptionGroup: there were problems
  +-+---------------- 1 ----------------
    | OSError: error 1
    +---------------- 2 ----------------
    | SystemError: error 2
    +------------------------------------
>>> try
...     f()
... except Exception as e
...     print(f'caught {type(e)}: e')
...
caught <class 'ExceptionGroup'>: e
>>>

By using instead of , we can selectively
handle only the exceptions in the group that match a certain
type. In the following example, which shows a nested exception
group, each clause extracts from the group exceptions
of a certain type while letting all other exceptions propagate to
other clauses and eventually to be reraised.

>>> def f():
...     raise ExceptionGroup("group1",
...                          OSError(1),
...                           SystemError(2),
...                           ExceptionGroup("group2",
...                                          OSError(3), RecursionError(4)])])
...
>>> try
...     f()
... except* OSError as e
...     print("There were OSErrors")
... except* SystemError as e
...     print("There were SystemErrors")
...
There were OSErrors
There were SystemErrors
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  |   File "<stdin>", line 2, in f
  | ExceptionGroup: group1
  +-+---------------- 1 ----------------
    | ExceptionGroup: group2
    +-+---------------- 1 ----------------
      | RecursionError: 4
      +------------------------------------
>>>

Note that the exceptions nested in an exception group must be instances,
not types. This is because in practice the exceptions would typically
be ones that have already been raised and caught by the program, along
the following pattern:

Обзор обработки исключений

Обработка исключений на самом деле довольно проста, и следующие два абзаца охватывают большую часть того, что вам нужно помнить о ней.

Когда выбрасывается исключение (с помощью ), выполнение программы сразу же переходит к ближайшему, включающему эту инструкцию , блоку (при необходимости с распространением вверх по стеку, чтобы найти охватывающий блок – мы обсудим это более подробно в следующем уроке). Если какой-либо из обработчиков , прикрепленных к этому блоку , обрабатывает этот тип исключения, то этот обработчик выполняется, и исключение считается обработанным.

Если подходящих обработчиков нет, выполнение программы переходит к следующему охватывающему блоку . Если до конца программы не удается найти подходящего обработчика , то программа завершится с ошибкой исключения.

Обратите внимание, что при сопоставлении исключений с блоками компилятор не будет выполнять неявные преобразования или расширяющие преобразования (продвижения)! Например, исключение не будет соответствовать блоку. Исключение типа не соответствует блоку

Однако приведение производного класса к одному из его родительских классов будет выполнено.

Вот и всё, что нужно помнить. Остальная часть этой главы будет посвящена демонстрации работы этих принципов.

SQL Server RAISERROR statement

We can use an alternative to the SQL PRINT statement that is RAISERROR.

We require a few arguments in RAISERROR statements.

  • Message – It is the message that we want to print
  • Severity – It is a number between 0 and 25 and defines the severity of the messages. It treats
    the message differently with each severity. We will see a few examples of it in this article
  • State – It is a number between 0 and 255 to distinguish one error from another. It is good to
    use value 1 for this article

We need to use RAISERROR statement with NOWAIT clause; otherwise, it shows the same behavior as of SQL PRINT
statement:

1
2
3
4

RAISERROR(‘My Name is Rajendra Gupta’,,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’,,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

In the following output with SQL Server RAISERROR statements, we can note the following:

  • It gives the message from the first PRINT statement
  • Waits for 5 seconds
  • It gives the message for the second PRINT statement
  • Waits for another 5 seconds

Previously we used severity 0 in the RAISERROR statement. Let’s use the same query with severity 16:

1
2
3
4

RAISERROR(‘My Name is Rajendra Gupta’,16,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’,16,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

In the output, we can see the message appears in red, and it shows the message as an error instead of a regular message. You also get message id, level, and state as well:

Let’s execute the following query with severity 1 and severity 16. Severity 1 shows the message with additional information, but it does not show the message as an error. You can see the text color in black.

Another SQL Server RAISERROR shows the output message as an error:

1
2
3
4

RAISERROR(‘My Name is Rajendra Gupta’,1,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

RAISERROR(‘You are reading article on SQL PRINT statement’,16,1)WITHNOWAIT;

WAITFORDELAY’00:00:05′;

We cannot use SQL Server RAISERROR directly using the variables. We get the following output that is not the desired
output:

1
2
3
4
5
6
7

DECLARE@aINT;

SET@a=1;

WHILE(@a<15)

BEGIN

RAISERROR(‘This is Iteration no:’,@A,,1)WITHNOWAIT;

SET@a=@a+1;

END;

It prints the message but does not show the value of the variable:

We need to use the C-style print statements with RAISERROR. The following query shows the variable with the
RAISERROR. You can notice that we use %s and %d to print a string and integer value:

1
2
3
4
5
6
7
8

DECLARE@aINT;

SET@a=1;

DECLARE@SVARCHAR(100)=’This is iteration no’;

WHILE(@a<5)

BEGIN

RAISERROR(‘%s:%d’,,1,@s,@a)WITHNOWAIT;

SET@a=@a+1;

END;

We get the instant output in SQL Server RAISERROR along with WITH NOWAIT statement as per our requirement and does
not use buffer to display output once the query finishes:

You might confuse between RAISERROR statement that it is for raising error messages in SQL Server. We can use it as an alternative to the SQL PRINT statement as well. Usually, developers use PRINT statements only to gives messages in a query. You should explore RAISERROR statements for your queries, stored procedures.

Оператор throws

Оператор throws включается в сигнатуру метода с целью обозначения возожности возникновения
исключительной ситуации с определенным типом. Использовать данный оператор следует в описании тех методов,
которые могут возбуждать исключения, но сами их не обрабатывают. Таким образом, оператором throws метод
предупреждает другие методы, вызывающие данный, что у него могут быть вызваны необработанные исключения,
чтобы вызывающие методы могли защитить себя от этих исключений.

public class TestThrow 
{
    static void method() throws IllegalAccessException 
    { 
        System.out.println("inside method"); 
        // . . . 
        throw new IllegalAccessException 
                        ("Exception in method"); 
    } 
    public static void main(String args[])
    { 
        try { 
            method(); 
        } catch(IllegalAccessException  e) { 
            System.out.println("Catch inside main : " + 
                                e.getMessage()); 
        } 
    }
}

Файловые группы

  • Эта файловая группа содержит первичный файл данных и все вторичные файлы, не входящие в другие файловые группы.
  • Пользовательские файловые группы могут создаваться для удобства администрирования, распределения и размещения данных.

Например, , и могут быть созданы на трех дисках соответственно и отнесены к файловой группе . В этом случае можно создать таблицу на основе файловой группы . Запросы данных из таблицы будут распределены по трем дискам, и это улучшит производительность. Подобного улучшения производительности можно достичь и с помощью одного файла, созданного на чередующемся наборе дискового массива RAID. Тем не менее файлы и файловые группы позволяют без труда добавлять новые файлы на новые диски.

Все файлы данных хранятся в файловых группах, перечисленных в следующей таблице.

Файловая группа Описание
Первичная Файловая группа, содержащая первичный файл. Все системные таблицы являются частью первичной файловой группы.
Данные, оптимизированные для памяти В основе оптимизированной для памяти файловой группы лежит файловая группа файлового потока.
Файловый поток
Определяемые пользователем маршруты Любая файловая группа, созданная пользователем при создании или изменении базы данных.

Файловая группа по умолчанию (первичная)

Если в базе данных создаются объекты без указания файловой группы, к которой они относятся, они назначаются файловой группе по умолчанию. В любом случае только одна файловая группа создается как файловая группа по умолчанию. Файлы в файловой группе по умолчанию должны быть достаточно большими, чтобы вмещать новые объекты, не назначенные другим файловым группам.

Файловая группа PRIMARY является группой по умолчанию, если только она не была изменена инструкцией ALTER DATABASE. Системные объекты и таблицы распределяются внутри первичной файловой группы, а не новой файловой группой по умолчанию.

Файловая группа данных, оптимизированных для памяти

Дополнительные сведения об оптимизированных для памяти файловых группах см. в разделе Оптимизированные для памяти файловые группы.

Файловая группа файлового потока

Дополнительные сведения о файловых группах файлового потока см. в статьях и Создание базы данных с поддержкой FILESTREAM.

Пример файлов и файловых групп

В следующем примере создается база данных на основе экземпляра SQL Server. База данных содержит первичный файл данных, пользовательскую файловую группу и файл журнала. Первичный файл данных входит в состав первичной файловой группы, а пользовательская файловая группа состоит из двух вторичных файлов данных. Инструкция ALTER DATABASE придает пользовательской файловой группе статус файловой группы по умолчанию. Затем создается таблица, определяющая пользовательскую файловую группу. (В этом примере используется универсальный путь к , чтобы не указывать версию SQL Server.)

Данная иллюстрация обобщает все вышесказанное (кроме данных файлового потока).

Последовательность нескольких блоков catch

При определение нескольких блоков catch следует руководствоваться правилом обработки исключений от «младшего»
к старшему. Т.е. нельзя размещать первым блоком catch (Exception e) {…}, поскольку все остальные блоки catch()
уже не смогут перехватить исключение. Помните, что Exception является базовым классом, поэтому его стоит размещать
последним.

Рассмотрим следующую иерархию наследования исключений :


java.lang.Object
    java.lang.Throwable
        java.lang.Exception
            java.io.IOException
                java.io.EOFException
 

Cамым младшим исключением является EOFException, поэтому он должен располагаться перед IOException и Exception,
если используется несколько блоков catch с данными типами исключений. Следующий код является демонстрацией
данного принципа.

String x = "Hello";
try {
    if (!x.equals("Hello"))
        throw new IOException();
    else
        throw new EOFException();
} catch (EOFException e) {
    System.err.println("EOFException : " + e.getMessage());
} catch (IOException e) {
    System.err.println("IOException : " + e.getMessage());
} catch (Exception e) {
    System.err.println("Exception : " + e.getMessage());
}

3.7.3. Создание уведомления

У нас есть тревога и есть сообщение. Для создания уведомления необходимо связать эти два объекта, чтобы в ответ на тревогу генерировалось сообщение и направлялось оператору. Такую связь можно назвать уведомлением и для его создания используется процедура sp_add_notification, которая выглядит следующим образом:

sp_add_notification  'alert' , 
     'operator' , 
     notification_method

Здесь у нас три параметра:

  • @alert_name – имя тревоги;
  • @operator_name – имя оператора, который должен получать уведомление;
  • @notification_message – метод, которым оператор будет получать уведомления об ошибке:
    • 1 – на e-mail адрес;
    • 2 – на пейджер;
    • 4 – командой NET SEND.

Примеры использования

Прежде чем создавать уведомление добавим оператора:

exec sp_add_operator 
   @name = 'Администратор',
   @netsend_address ='192.168.77.11'

Чтобы наглядно увидеть результат работы, я задал IP адрес своего компьютера, чтобы получать NET SEND сообщение.

Следующий пример показывает, как создать уведомление, которое будет получать администратор для тревоги, созданной в разделе 3.7.2:

EXEC sp_add_notification 
  @alert_name='Тестовая тревога',
  @operator_name='Администратор',
  @notification_method=4

Одна тревога может направлять сообщения нескольким операторам. Следующий пример добавляет уведомление еще одного оператора для тревоги с именем ‘Тестовая тревога’:

EXEC sp_add_notification 
  @alert_name='Тестовая тревога',
  @operator_name='Михаил',
  @notification_method=4

Вот теперь вы можете увидеть результат работы на примере. Для этого необходимо сгенерировать сообщение с помощью функции RAISERROR (более подробно о RAISERROR мы поговорим в разделе 4.3.2):

RAISERROR (60001, 16, 1)

В ответ на это, я получил NET SEND сообщение.

Если вы используете NET SEND сообщения, то убедитесь, что в вашей ОС запущен сервис Messenger, без которого отправка сообщения будет невозможной.

Обновление

Для обновления уведомления используется процедура sp_update_notification, которая выглядит следующим образом:

sp_update_notification  'alert', 
     'operator', 
     notification

Параметры такие же, как и при создании уведомления sp_add_notification, только параметр @alert_name определяет тревогу, которую надо обновить, а параметр @operator_name определяет оператора. С помощью параметра и @notification_method можно задать новый метод уведомления.

Удаление

Для удаления уведомления используется процедура sp_delete_notification, которая в общем виде выглядит следующим образом:

sp_delete_notification  'alert' , 
     'operator'

Параметр @alert_name определяет тревогу, которую надо удалить, а параметр @operator_name определяет удаляемого оператора.

Операторы исключений

Java имеет пять ключевых операторов для определения блока исключений, перехвата и возбуждения исключений :

  1. try — начала блока кода, в котором может возникнуть исключение, и которое следует перехватить;
  2. catch — начала блока кода, предназначенного для перехвата и обработки исключений (параметром catch
    является тип ожидаемого исключения);
  3. throw — оператор для генерации исключений;
  4. throws — ключевое слово, используемое в сигнатуре метода, и обозначающее, что метод
    потенциально может вызвать исключение с определенным типом;
  5. finally — начала дополнительного блока кода, размещаемый после последнего блока catch. Блок finally
    не является обязательным, но всегда получает управление.

Общая структура «перехвата» исключительной ситуации выглядит следующим образом :

try { 
   // код программы, который может привести к ошибке 
} catch(Exception e ) {
   // код программы для обработки исключений 
} finally { 
   // выполнение блока программы независимо от наличия исключения
}
Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Вадлейд
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: