006f095191c8d9da0de1a722f08ce28eb651519d
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntitySqlException.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntitySqlException.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data
11 {
12     using System;
13     using System.Data.Common.EntitySql;
14     using System.Data.Entity;
15     using System.Diagnostics;
16     using System.Globalization;
17     using System.Runtime.Serialization;
18     using System.Security;
19     using System.Security.Permissions;
20     using System.Text;
21
22     /// <summary>
23     /// Represents an eSQL Query compilation exception;
24     /// The class of exceptional conditions that may cause this exception to be raised are mainly:
25     /// 1) Syntax Errors: raised during query text parsing and when a given query does not conform to eSQL formal grammar;
26     /// 2) Semantic Errors: raised when semantic rules of eSQL language are not met such as metadata or schema information
27     ///    not accurate or not present, type validation errors, scoping rule violations, user of undefined variables, etc.
28     /// For more information, see eSQL Language Spec.
29     /// </summary>
30     [Serializable]
31     public sealed class EntitySqlException : EntityException
32     {
33         #region Private Fields
34         /// <summary>
35         /// error message description. 
36         /// </summary>
37         private string _errorDescription;
38
39         /// <summary>
40         /// information about the context where the error occurred 
41         /// </summary>
42         private string _errorContext;
43
44         /// <summary>
45         /// error line number
46         /// </summary>
47         private int _line;
48
49         /// <summary>
50         /// error column number
51         /// </summary>
52         private int _column;
53         #endregion
54
55         #region Public Constructors
56         /// <summary>
57         /// Initializes a new instance of <see cref="EntitySqlException"/> with the generic error message.
58         /// </summary>
59         public EntitySqlException()
60             : this(System.Data.Entity.Strings.GeneralQueryError)
61         {
62             HResult = HResults.InvalidQuery;
63         }
64
65         /// <summary>
66         /// Initializes a new instance of <see cref="EntitySqlException"/> with the given message.
67         /// </summary>
68         public EntitySqlException(string message)
69             : base(message)
70         {
71             HResult = HResults.InvalidQuery;
72         }
73
74         /// <summary>
75         /// Initializes a new instance of <see cref="EntitySqlException"/> with the given message and innerException instance.
76         /// </summary>
77         public EntitySqlException(string message, Exception innerException)
78             : base(message, innerException)
79         {
80             HResult = HResults.InvalidQuery;
81         }
82
83         /// <summary>
84         /// Initializes a new instance <see cref="EntitySqlException"/> with the given serializationInfo and streamingContext.
85         /// </summary>
86         /// <param name="serializationInfo"></param>
87         /// <param name="streamingContext"></param>
88         private EntitySqlException(SerializationInfo serializationInfo, StreamingContext streamingContext)
89             : base(serializationInfo, streamingContext)
90         {
91             HResult = HResults.InvalidQuery;
92             _errorDescription = serializationInfo.GetString("ErrorDescription");
93             _errorContext = serializationInfo.GetString("ErrorContext");
94             _line = serializationInfo.GetInt32("Line");
95             _column = serializationInfo.GetInt32("Column");
96         }
97         #endregion
98
99         #region Internal Constructors
100         /// <summary>
101         /// Initializes a new instance EntityException with an ErrorContext instance and a given error message.
102         /// </summary>
103         internal static EntitySqlException Create(ErrorContext errCtx, string errorMessage, Exception innerException)
104         {
105             return EntitySqlException.Create(errCtx.CommandText, errorMessage, errCtx.InputPosition, errCtx.ErrorContextInfo, errCtx.UseContextInfoAsResourceIdentifier, innerException);
106         }
107
108         /// <summary>
109         /// Initializes a new instance EntityException with contextual information to allow detailed error feedback.
110         /// </summary>
111         internal static EntitySqlException Create(string commandText,
112                                                   string errorDescription,
113                                                   int errorPosition,
114                                                   string errorContextInfo,
115                                                   bool loadErrorContextInfoFromResource,
116                                                   Exception innerException)
117         {
118             int line;
119             int column;
120             string errorContext = FormatErrorContext(commandText, errorPosition, errorContextInfo, loadErrorContextInfoFromResource, out line, out column);
121
122             string errorMessage = FormatQueryError(errorDescription, errorContext);
123
124             return new EntitySqlException(errorMessage, errorDescription, errorContext, line, column, innerException);
125         }
126
127         /// <summary>
128         /// core constructor
129         /// </summary>
130         private EntitySqlException(string message, string errorDescription, string errorContext, int line, int column, Exception innerException)
131             : base(message, innerException)
132         {
133             _errorDescription = errorDescription;
134             _errorContext = errorContext;
135             _line = line;
136             _column = column;
137
138             HResult = HResults.InvalidQuery;
139         }
140
141         #endregion
142
143         #region Public Properties
144         /// <summary>
145         /// Gets the error description explaining the reason why the query was not accepted or an empty String.Empty
146         /// </summary>
147         public string ErrorDescription
148         {
149             get
150             {
151                 return _errorDescription ?? String.Empty;
152             }
153         }
154
155         /// <summary>
156         /// Gets the aproximate context where the error occurred if available.
157         /// </summary>
158         public string ErrorContext
159         {
160             get
161             {
162                 return _errorContext ?? String.Empty;
163             }
164         }
165
166         /// <summary>
167         /// Returns the the aproximate line number where the error occurred
168         /// </summary>
169         public int Line
170         {
171             get { return _line; }
172         }
173
174         /// <summary>
175         /// Returns the the aproximate column number where the error occurred
176         /// </summary>
177         public int Column
178         {
179             get { return _column; }
180         }
181         #endregion
182
183         #region Helpers
184         internal static string GetGenericErrorMessage(string commandText, int position)
185         {
186             int lineNumber = 0;
187             int colNumber = 0;
188             return FormatErrorContext(commandText, position, EntityRes.GenericSyntaxError, true, out lineNumber, out colNumber);
189         }
190
191         /// <summary>
192         /// Returns error context in the format [[errorContextInfo, ]line ddd, column ddd].
193         /// Returns empty string if errorPosition is less than 0 and errorContextInfo is not specified.
194         /// </summary>
195         internal static string FormatErrorContext(
196             string commandText,
197             int errorPosition,
198             string errorContextInfo,
199             bool loadErrorContextInfoFromResource,
200             out int lineNumber,
201             out int columnNumber)
202         {
203             Debug.Assert(errorPosition > -1, "position in input stream cannot be < 0");
204             Debug.Assert(errorPosition <= commandText.Length, "position in input stream cannot be greater than query text size");
205
206             if (loadErrorContextInfoFromResource)
207             {
208                 errorContextInfo = !String.IsNullOrEmpty(errorContextInfo) ? EntityRes.GetString(errorContextInfo) : String.Empty;
209             }
210
211             //
212             // Replace control chars and newLines for single representation characters
213             //
214             StringBuilder sb = new StringBuilder(commandText.Length);
215             for (int i = 0; i < commandText.Length; i++)
216             {
217                 Char c = commandText[i];
218                 if (CqlLexer.IsNewLine(c))
219                 {
220                     c = '\n';
221                 }
222                 else if ((Char.IsControl(c) || Char.IsWhiteSpace(c)) && ('\r' != c))
223                 {
224                     c = ' ';
225                 }
226                 sb.Append(c);
227             }
228             commandText = sb.ToString().TrimEnd(new char[] { '\n' });
229
230             //
231             // Compute line and column
232             //
233             string[] queryLines = commandText.Split(new char[] { '\n' }, StringSplitOptions.None);
234             for (lineNumber = 0, columnNumber = errorPosition;
235                  lineNumber < queryLines.Length && columnNumber > queryLines[lineNumber].Length;
236                  columnNumber -= (queryLines[lineNumber].Length + 1), ++lineNumber) ;
237
238             ++lineNumber; // switch lineNum and colNum to 1-based indexes
239             ++columnNumber;
240
241             //
242             // Error context format: "[errorContextInfo,] line ddd, column ddd"
243             //
244             sb = new Text.StringBuilder();
245             if (!String.IsNullOrEmpty(errorContextInfo))
246             {
247                 sb.AppendFormat(CultureInfo.CurrentCulture, "{0}, ", errorContextInfo);
248             }
249
250             if (errorPosition >= 0)
251             {
252                 sb.AppendFormat(CultureInfo.CurrentCulture,
253                                 "{0} {1}, {2} {3}",
254                                 System.Data.Entity.Strings.LocalizedLine,
255                                 lineNumber,
256                                 System.Data.Entity.Strings.LocalizedColumn,
257                                 columnNumber);
258             }
259
260             return sb.ToString();
261         }
262
263         /// <summary>
264         /// Returns error message in the format: "error such and such[, near errorContext]."
265         /// </summary>
266         private static string FormatQueryError(string errorMessage, string errorContext)
267         {
268             //
269             // Message format: error such and such[, near errorContextInfo].
270             //
271             StringBuilder sb = new StringBuilder();
272             sb.Append(errorMessage);
273             if (!String.IsNullOrEmpty(errorContext))
274             {
275                 sb.AppendFormat(CultureInfo.CurrentCulture, " {0} {1}", System.Data.Entity.Strings.LocalizedNear, errorContext);
276             }
277
278             return sb.Append(".").ToString();
279         }
280         #endregion
281
282         #region ISerializable implementation
283         /// <summary>
284         /// sets the System.Runtime.Serialization.SerializationInfo
285         /// with information about the exception.
286         /// </summary>
287         /// <param name="info">The System.Runtime.Serialization.SerializationInfo that holds the serialized
288         /// object data about the exception being thrown.
289         /// </param>
290         /// <param name="context"></param>
291         [SecurityCritical]
292         [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
293         public override void GetObjectData(SerializationInfo info, StreamingContext context)
294         {
295             base.GetObjectData(info, context);
296             info.AddValue("ErrorDescription", _errorDescription);
297             info.AddValue("ErrorContext", _errorContext);
298             info.AddValue("Line", _line);
299             info.AddValue("Column", _column);
300         }
301         #endregion
302     }
303 }