Errors and Exceptions

30 323 0
Tài liệu đã được kiểm tra trùng lặp
Errors and Exceptions

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

C H A P T E R 4    Errors and Exceptions As software developers, we often daydream of a Utopian world of bug-free software—developed under the shade of a palm tree on a remote island while sipping a long, fruity cocktail. But, alas, back in the real world, hordes of developers sit in cubicle farms gulping acrid coffee, fighting bugs that are not always their fault or under their control in any way. Exceptions can occur in even the most stringently tested software, simply because it is not possible to check every error condition in advance. For instance, do you know what will happen if a janitor, while cleaning the data-center floor, accidentally slops some mop water into the fan enclosure of the database server? It might crash, or it might not; it might just cause some component to fail somewhere deep in the app, sending up a strange error message. Although most exceptions won’t be so far out of the realm of testability, it is certainly important to understand how to deal with them when and if they occur. It is also imperative that SQL Server developers understand how to work with errors—both those thrown by the server itself and custom errors built specifically for when problems occur during the runtime of an application. Exceptions vs. Errors The terms exception and error, while often used interchangeably by developers, actually refer to slightly different conditions: An error can occur if something goes wrong during the course of a program, even though it can be purely informational in nature. For instance, the message displayed by a program informing a user that a question mark is an invalid character for a file name is considered to be an error message. However, this may or may not mean that the program itself is in an invalid state. An exception, on the other hand, is an error that is the result of an exceptional circumstance. For example, if a network library is being used to send packets, and the network connection is suddenly dropped due to someone unplugging a cable, the library might throw an exception. An exception tells the calling code that something went wrong and the routine aborted unexpectedly. If the caller does not handle the exception (i.e., capture it), its execution will also abort. This process will keep repeating until the exception is handled, or until it reaches the highest level of the call stack, at which point the entire program will fail. Another way to think about exceptions and errors is to think of errors as occurrences that are expected by the program. The error message that is displayed when a file name contains an invalid character is informational in nature because the developer of the program predicted that such an event would occur and created a code path specifically to deal with it. A dropped network connection, on the 71 CHAPTER 4  ERRORS AND EXCEPTIONS other hand, could be caused by any number of circumstances and therefore is much more difficult to handle specifically. Instead, the solution is to raise an exception and fail. The exception can then be handled by a routine higher in the call stack, which can decide what course of action to take in order to solve the problem. What Defines an "Exceptional" Circumstance? There is some debate in the software community as to whether exceptions should really be used only for exceptional circumstances, or whether it is acceptable to use them as part of the regular operation of a program. For example, some programmers choose to define a large number of different custom exceptions, and then deliberately raise these exceptions as a method of controlling the flow of an application. There are some possible benefits to this approach: raising an exception is a useful way to break out of a routine immediately, however deeply nested in the call stack that routine might be; and, an exception can be used to describe conditions that would be difficult to express in the normal return value of a method. However convenient it may seem to use exceptions in such scenarios, they clearly represent an abuse of the intended purpose of exception-handling code (which, remember, is to deal with exceptional circumstances). Furthermore, making lots of exception calls can make your code hard to read, and debugging can be made more difficult, since debuggers generally implement special behavior whenever exceptions are encountered. As a result, exception-laden code is more difficult to maintain when compared to code that relies on more standard control flow structures. In my opinion, you should raise exceptions only to describe truly exceptional circumstances. Furthermore, due to the fact that exceptions can cause abort conditions, they should be used sparingly. However, there is certainly an upside to using exceptions over errors, which is that it’s more difficult for the caller to ignore an exception, since it will cause code to abort if not properly handled. If you’re designing an interface that needs to ensure that the caller definitely sees a certain condition when and if it occurs, it might make sense to use an exception rather than an error. As in almost all cases, the decision is very dependent on the context of your application, so do not feel obliged to stick to my opinion! How Exceptions Work in SQL Server The first step in understanding how to handle errors and exceptions in SQL Server is to take a look at how the server itself deals with error conditions. Unlike many other programming languages, SQL Server has an exception model that involves different behaviors for different types of exceptions. This can cause unexpected behavior when error conditions do occur, so careful programming is essential when dealing with T-SQL exceptions. To begin with, think about connecting to a SQL Server and issuing some T-SQL. First, you must establish a connection to the server by issuing login credentials. The connection also determines what database will be used as the default for scope resolution (i.e., finding objects—more on this in a bit). Once connected, you can issue a batch of T-SQL. A batch consists of one or more T-SQL statements, which will be compiled together to form an execution plan. 72 Download at WoweBook.com CHAPTER 4  ERRORS AND EXCEPTIONS The behavior of the exceptions thrown by SQL Server mostly follows this same pattern: depending on the type of exception, a statement, a batch, or an entire connection may be aborted. Let’s take a look at some practical examples to see this in action. Statement-Level Exceptions A statement-level exception aborts only the current statement that is running within a batch of T-SQL, allowing any subsequent statements within the batch to run. To see this behavior, you can use SQL Server Management Studio to execute a batch that includes an exception, followed by a PRINT statement. For instance: SELECT POWER(2, 32); PRINT 'This will print!'; GO Running this batch results in the following output: Msg 232, Level 16, State 3, Line 1 Arithmetic overflow error for type int, value = 4294967296.000000. This will print! When this batch was run, the attempt to calculate POWER(2, 32) caused an integer overflow, which threw the exception. However, only the SELECT statement was aborted. The rest of the batch continued to run, which, in this case, meant that the PRINT statement still printed its message. Batch-Level Exceptions Unlike a statement-level exception, a batch-level exception does not allow the rest of the batch to continue running. The statement that throws the exception will be aborted, and any remaining statements in the batch will not be run. An example of a batch-aborting exception is an invalid conversion, such as the following: SELECT CONVERT(int, 'abc'); PRINT 'This will NOT print!'; GO The output of this batch is as follows: Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the varchar value 'abc' to data type int. 73 CHAPTER 4  ERRORS AND EXCEPTIONS In this case, the conversion exception occurred in the SELECT statement, which aborted the batch at that point. The PRINT statement was not allowed to run, although if the batch had contained any valid statements before the exception, these would have been executed successfully. Batch-level exceptions might be easily confused with connection-level exceptions (which drop the connection to the server), but after a batch-level exception, the connection is still free to send other batches. For instance: SELECT CONVERT(int, 'abc'); GO PRINT 'This will print!'; GO In this case there are two batches sent to SQL Server, separated by the batch separator, GO. The first batch throws a conversion exception, but the second batch is still run. This results in the following output: Msg 245, Level 16, State 1, Line 2 Conversion failed when converting the varchar value 'abc' to data type int. This will print! Batch-level exceptions do not affect only the scope in which the exception occurs. The exception will bubble up to the next level of execution, aborting every call in the stack. This can be illustrated by creating the following stored procedure: CREATE PROCEDURE ConversionException AS BEGIN SELECT CONVERT(int, 'abc'); END; GO Running this stored procedure followed by a PRINT shows that, even when an exception occurs in an inner scope (within the stored procedure), the outer batch is still aborted: EXEC ConversionException; PRINT 'This will NOT print!'; GO The result of this batch is the same as if no stored procedure was used: Msg 245, Level 16, State 1, Line 4 Conversion failed when converting the varchar value 'abc' to data type int. 74 CHAPTER 4  ERRORS AND EXCEPTIONS Parsing and Scope-Resolution Exceptions Exceptions that occur during parsing or during the scope-resolution phase of compilation appear at first to behave just like batch-level exceptions. However, they actually have a slightly different behavior. If the exception occurs in the same scope as the rest of the batch, these exceptions will behave just like a batch-level exception. If, on the other hand, an exception occurs in a lower level of scope, these exceptions will behave just like statement-level exceptions—at least, as far as the outer batch is concerned. As an example, consider the following batch, which includes a malformed SELECT statement (this is a parse exception): SELECTxzy FROM SomeTable; PRINT 'This will NOT print!'; GO In this case, the PRINT statement is not run, because the whole batch is discarded during the parse phase. The output is the following exception message: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'FROM'. To see the difference in behavior, the SELECT statement can be executed as dynamic SQL using the EXEC function. This causes the SELECT statement to execute in a different scope, changing the exception behavior from batch-like to statement-like. Try running the following T-SQL to observe the change: EXEC('SELECTxzy FROM SomeTable'); PRINT 'This will print!'; GO The PRINT statement is now executed, even though the exception occurred: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'FROM'. This will print! This type of exception behavior also occurs during scope resolution. Essentially, SQL Server processes queries in two phases. The first phase parses and validates the query and ensures that the T- SQL is well formed. The second phase is the compilation phase, during which an execution plan is built and objects referenced in the query are resolved. If a query is submitted to SQL Server via ad hoc SQL from an application or dynamic SQL within a stored procedure, these two phases happen together. However, within the context of stored procedures, SQL Server exploits late binding. This means that the parse phase happens when the stored procedure is created, and the compile phase (and therefore scope resolution) occurs only when the stored procedure is executed. 75 CHAPTER 4  ERRORS AND EXCEPTIONS To see what this means, create the following stored procedure (assuming that a table called SomeTable does not exist in the current database): CREATE PROCEDURE NonExistentTable AS BEGIN SELECT xyz FROM SomeTable; END; GO Although SomeTable does not exist, the stored procedure is created—the T-SQL parses without any errors. However, upon running the stored procedure, an exception is thrown: EXEC NonExistentTable; GO This leads to Msg 208, Level 16, State 1, Procedure NonExistentTable, Line 4 Invalid object name 'SomeTable'. Like the parse exception, scope-resolution exceptions behave similarly to batch-level exceptions within the same scope, and similarly to statement-level exceptions in the outer scope. Since the stored procedure creates a new scope, hitting this exception within the procedure aborts the rest of the procedure, but any T-SQL encountered in the calling batch after execution of the procedure will still run. For instance: EXEC NonExistentTable; PRINT 'This will print!'; GO leads to the following result: Msg 208, Level 16, State 1, Procedure NonExistentTable, Line 4 Invalid object name 'SomeTable'. This will print! Connection and Server-Level Exceptions Some exceptions thrown by SQL Server can be so severe that they abort the entire connection, or cause the server itself to crash. These types of connection- and server-level exceptions are generally caused by internal SQL Server bugs, and are thankfully quite rare. At the time of writing, I cannot provide any 76 CHAPTER 4  ERRORS AND EXCEPTIONS examples of these types of exceptions, as I am not aware of any reproducible conditions in SQL Server 2008 that cause them. The XACT_ABORT Setting Although users do not have much control over the behavior of exceptions thrown by SQL Server, there is one setting that can be modified on a per-connection basis. Turning on the XACT_ABORT setting makes all statement-level exceptions behave like batch-level exceptions. This means that control will always be immediately returned to the client any time an exception is thrown by SQL Server during execution of a query (assuming the exception is not handled). To enable XACT_ABORT for a connection, the following T-SQL is used: SET XACT_ABORT ON; This setting will remain enabled for the entire connection—even if it was set in a lower level of scope, such as in a stored procedure or dynamic SQL—until it is disabled using the following T-SQL: SET XACT_ABORT OFF; To illustrate the effect of this setting on the behavior of exceptions, let’s review a couple of the exceptions already covered. Recall that the following integer overflow exception operates at the statement level: SELECT POWER(2, 32); PRINT 'This will print!'; GO Enabling the XACT_ABORT setting before running this T-SQL changes the output, resulting in the PRINT statement not getting executed: SET XACT_ABORT ON; SELECT POWER(2, 32); PRINT 'This will NOT print!'; GO The output from running this batch is as follows: Msg 232, Level 16, State 3, Line 2 Arithmetic overflow error for type int, value = 4294967296.000000. Note that XACT_ABORT only affects the behavior of runtime errors, not those generated during compilation. Recall the previous example that demonstrated a parsing exception occurring in a lower scope using the EXEC function: EXEC('SELECTxzy FROM SomeTable'); PRINT 'This will print!'; GO The result of this code listing will remain the same, regardless of the XACT_ABORT setting, resulting in the PRINT statement being evaluated even after the exception occurs. 77 CHAPTER 4  ERRORS AND EXCEPTIONS In addition to controlling exception behavior, XACT_ABORT also modifies how transactions behave when exceptions occur. See the section “Transactions and Exceptions” later in this chapter for more information. Dissecting an Error Message A SQL Server exception has a few different component parts, each of which are represented within the text of the error message. Each exception has an associated error number, error level, and state. Error messages can also contain additional diagnostic information including line numbers and the name of the procedure in which the exception occurred. Error Number The error number of an exception is listed following the text Msg within the error text. For example, the error number of the following exception is 156: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'FROM'. SQL Server generally returns the error message with the exception, so having the error number usually doesn’t assist from a problem-solving point of view. However, there are times when knowing the error number can be of use. Examples include use of the @@ERROR function, or when doing specialized error handling using the TRY/CATCH syntax (see the sections “Exception ‘Handling’ Using @@ERROR” and “SQL Server’s TRY/CATCH Syntax” later in the chapter for details on these topics). The error number can also be used to look up the localized translation of the error message from the sys.messages catalog view. The message_id column contains the error number, and the language_id column can be used to get the message in the correct language. For example, the following T-SQL returns the English text for error 208: SELECT text FROM sys.messages WHERE message_id = 208 AND language_id = 1033; GO The output of this query is an error message template, shown here: Invalid object name '%.*ls'. See the section “SQL Server’s RAISERROR Function” for more information about error message templates. 78 CHAPTER 4  ERRORS AND EXCEPTIONS Error Level The Level tag within an error message indicates a number between 1 and 25. This number can sometimes be used to either classify an exception or determine its severity. Unfortunately, the key word is “sometimes”: the error levels assigned by SQL Server are highly inconsistent and should generally not be used in order to make decisions about how to handle exceptions. The following exception, based on its error message, is of error level 15: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'FROM'. The error levels for each exception can be queried from the sys.messages view, using the severity column. A severity of less than 11 indicates that a message is a warning. If severity is 11 or greater, the message is considered to be an error and can be broken down into the following documented categories: • Error levels 11 through 16 are documented as “errors that can be corrected by the user.” The majority of exceptions thrown by SQL Server are in this range, including constraint violations, parsing and compilation errors, and most other runtime exceptions. • Error levels 17 through 19 are more serious exceptions. These include out-of- memory exceptions, disk space exceptions, internal SQL Server errors, and other similar violations. Many of these are automatically logged to the SQL Server error log when they are thrown. You can identify those exceptions that are logged by examining the is_event_logged column of the sys.messages table. • Error levels 20 through 25 are fatal connection and server-level exceptions. These include various types of data corruption, network, logging, and other critical errors. Virtually all of the exceptions at this level are automatically logged. Although the error levels that make up each range are individually documented in Books Online (http://msdn2.microsoft.com/en-us/library/ms164086.aspx), this information is inconsistent or incorrect in many cases. For instance, according to documentation, severity level 11 indicates errors where “the given object or entity does not exist.” However, error 208, “Invalid object name,” is a level-16 exception. Many other errors have equally unpredictable levels, and it is recommended that you do not program client software to rely on the error levels for handling logic. In addition to inconsistency regarding the relative severity of different errors, there is, for the most part, no discernable pattern regarding the severity level of an error and whether that error will behave on the statement or batch level. For instance, both errors 245 (“Conversion failed”) and 515 (“Cannot insert the value NULL . . . column does not allow nulls”) are level-16 exceptions. However, 245 is a batch-level exception, whereas 515 acts at the statement level. Error State Each exception has a State tag, which contains information about the exception that is used internally by SQL Server. The values that SQL Server uses for this tag are not documented, so this tag is generally not helpful. The following exception has a state of 1: 79 CHAPTER 4  ERRORS AND EXCEPTIONS Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'FROM'. Additional Information In addition to the error number, level, and state, many errors also carry additional information about the line number on which the exception occurred and the procedure in which it occurred, if relevant. The following error message indicates that an invalid object name was referenced on line 4 of the procedure NonExistentTable: Msg 208, Level 16, State 1, Procedure NonExistentTable, Line 4 Invalid object name 'SomeTable'. If an exception does not occur within a procedure, the line number refers to the line in the batch in which the statement that caused the exception was sent. Be careful not to confuse batches separated with GO with a single batch. Consider the following T- SQL: SELECT 1; GO SELECT 2; GO SELECT 1/0; GO In this case, although a divide-by-zero exception occurs on line 5 of the code listing itself, the exception message will report that the exception was encountered on line 1: (1 row(s) affected) (1 row(s) affected) Msg 8134, Level 16, State 1, Line 1 Divide by zero error encountered. The reason for the reset of the line number is that GO is not actually a T-SQL command. GO is an identifier recognized by SQL Server client tools (e.g., SQL Server Management Studio and SQLCMD) that tells the client to separate the query into batches, sending each to SQL Server one after another. This seemingly erroneous line number reported in the previous example occurs because each batch is sent separately to the query engine. SQL Server does not know that on the client (e.g., in SQL Server Management Studio) these batches are all displayed together on the screen. As far as SQL Server is 80 [...]... generally a better idea to handle exceptions rather than errors If you can predict a condition and write a code path to handle it during development, that will usually provide a much more robust solution than trying to trap the exception once it occurs and handle it then 92 CHAPTER 4 ERRORS AND EXCEPTIONS Exception Handling and SQLCLR The NET Framework provides its own exception-handling mechanism, which... CHAPTER 4 ERRORS AND EXCEPTIONS Why Handle Exceptions in T-SQL? Exception handling in T-SQL should be thought of as no different from exception handling in any other language A generally accepted programming practice is to handle exceptions at the lowest possible scope, in order to keep them from interacting with higher levels of the application If an exception can be caught at a lower level and dealt... often much cheaper than rebuilding and redeploying the entire application It is also important to consider when not to encapsulate exceptions Make sure not to overhandle security problems, severe data errors, and other exceptions that the application and ultimately, the user—should probably be informed of There is definitely such a thing as too much exception handling, and falling into that trap can mean... 208 exceptions (“Object not found”) without corresponding error message events These exceptions are used internally by the SQL Server query optimizer during the scoperesolution phase of compilation, and can be safely ignored Exception Handling Understanding when, why, and how SQL Server throws exceptions is great, but the real goal is to actually do something when an exception occurs Exception handling... retry Exception Handling and Defensive Programming Exception handling is extremely useful, and its use in T-SQL is absolutely invaluable However, I hope that all readers keep in mind that exception handling is no substitute for proper checking of error conditions before they occur Whenever possible, code defensively—proactively look for problems, and if they can be both detected and handled, code around... between 1 and 10 result in a warning, levels between 11 and 18 are considered normal user errors, and those above 18 are considered serious and can only be raised by members of the sysadmin fixed-server role User exceptions raised over level 20, just like those raised by SQL Server, cause the connection to break Beyond these ranges, there is no real control afforded to user-raised exceptions, and all... batch-level exceptions, and the implications of exceptions that are thrown within transactions SQL Server’s TRY/CATCH syntax makes dealing with exceptions much easier, but it’s important to use the feature wisely Overuse can make detection and debugging of problems exceedingly difficult And whenever dealing with transactions in CATCH blocks, make sure to check the value of XACT_STATE Errors and exceptions will... rather than the generic T-SQL error 6522, and can be used to write specific code paths to deal with such an exception Transactions and Exceptions No discussion of exceptions in SQL Server can be complete without mentioning the interplay between transactions and exceptions This is a fairly simple area, but one that often confuses developers who don’t quite understand the role that transactions play SQL... catch { // Exception Handling code here // Optionally, rethrow the exception // throw new Exception("An exception occurred that couldn't be handled"); 95 CHAPTER 4 ERRORS AND EXCEPTIONS } return result; } Alternatively, you could create code paths that rely on parsing the contents of ERROR_MESSAGE() to identify the details of the original CLR exception specified in the stack trace The exceptions generated... encountered 86 CHAPTER 4 ERRORS AND EXCEPTIONS and the second statement returns a result set containing a single value, containing the error number associated with the previous error: ErrorNumber 8134 By checking to see whether the value of @@ERROR is nonzero, it is possible to perform some very primitive error handling Unfortunately, this is also quite error prone due to the nature of @@ERROR and the fact that . CHAPTER 4  ERRORS AND EXCEPTIONS Why Handle Exceptions in T-SQL? Exception handling in T-SQL should be thought of as no different from exception handling. 'abc' to data type int. 74 CHAPTER 4  ERRORS AND EXCEPTIONS Parsing and Scope-Resolution Exceptions Exceptions that occur during parsing or during

Ngày đăng: 05/10/2013, 08:48

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan