1 //
2 // Diagnostics.h
3 //
4 // Library: Data/ODBC
5 // Package: ODBC
6 // Module:  Diagnostics
7 //
8 // Definition of Diagnostics.
9 //
10 // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
11 // and Contributors.
12 //
13 // SPDX-License-Identifier:	BSL-1.0
14 //
15 
16 
17 #ifndef Data_ODBC_Diagnostics_INCLUDED
18 #define Data_ODBC_Diagnostics_INCLUDED
19 
20 
21 #include "Poco/Data/ODBC/ODBC.h"
22 #include <vector>
23 #include <cstring>
24 #ifdef POCO_OS_FAMILY_WINDOWS
25 #include <windows.h>
26 #endif
27 #include <sqlext.h>
28 
29 
30 namespace Poco {
31 namespace Data {
32 namespace ODBC {
33 
34 
35 template <typename H, SQLSMALLINT handleType>
36 class Diagnostics
37 	/// Utility class providing functionality for retrieving ODBC diagnostic
38 	/// records. Diagnostics object must be created with corresponding handle
39 	/// as constructor argument. During construction, diagnostic records fields
40 	/// are populated and the object is ready for querying.
41 {
42 public:
43 
44 	static const unsigned int SQL_STATE_SIZE = SQL_SQLSTATE_SIZE + 1;
45 	static const unsigned int SQL_MESSAGE_LENGTH = SQL_MAX_MESSAGE_LENGTH + 1;
46 	static const unsigned int SQL_NAME_LENGTH = 128;
47 	static const std::string  DATA_TRUNCATED;
48 
49 	struct DiagnosticFields
50 	{
51 		/// SQLGetDiagRec fields
52 		SQLCHAR    _sqlState[SQL_STATE_SIZE];
53 		SQLCHAR    _message[SQL_MESSAGE_LENGTH];
54 		SQLINTEGER _nativeError;
55 	};
56 
57 	typedef std::vector<DiagnosticFields> FieldVec;
58 	typedef typename FieldVec::const_iterator Iterator;
59 
Diagnostics(const H & handle)60 	explicit Diagnostics(const H& handle): _handle(handle)
61 		/// Creates and initializes the Diagnostics.
62 	{
63 		std::memset(_connectionName, 0, sizeof(_connectionName));
64 		std::memset(_serverName, 0, sizeof(_serverName));
65 		diagnostics();
66 	}
67 
~Diagnostics()68 	~Diagnostics()
69 		/// Destroys the Diagnostics.
70 	{
71 	}
72 
sqlState(int index)73 	std::string sqlState(int index) const
74 		/// Returns SQL state.
75 	{
76 		poco_assert (index < count());
77 		return std::string((char*) _fields[index]._sqlState);
78 	}
79 
message(int index)80 	std::string message(int index) const
81 		/// Returns error message.
82 	{
83 		poco_assert (index < count());
84 		return std::string((char*) _fields[index]._message);
85 	}
86 
nativeError(int index)87 	long nativeError(int index) const
88 		/// Returns native error code.
89 	{
90 		poco_assert (index < count());
91 		return _fields[index]._nativeError;
92 	}
93 
connectionName()94 	std::string connectionName() const
95 		/// Returns the connection name.
96 		/// If there is no active connection, connection name defaults to NONE.
97 		/// If connection name is not applicable for query context (such as when querying environment handle),
98 		/// connection name defaults to NOT_APPLICABLE.
99 	{
100 		return std::string((char*) _connectionName);
101 	}
102 
serverName()103 	std::string serverName() const
104 		/// Returns the server name.
105 		/// If the connection has not been established, server name defaults to NONE.
106 		/// If server name is not applicable for query context (such as when querying environment handle),
107 		/// connection name defaults to NOT_APPLICABLE.
108 	{
109 		return std::string((char*) _serverName);
110 	}
111 
count()112 	int count() const
113 		/// Returns the number of contained diagnostic records.
114 	{
115 		return (int) _fields.size();
116 	}
117 
reset()118 	void reset()
119 		/// Resets the diagnostic fields container.
120 	{
121 		_fields.clear();
122 	}
123 
fields()124 	const FieldVec& fields() const
125 	{
126 		return _fields;
127 	}
128 
begin()129 	Iterator begin() const
130 	{
131 		return _fields.begin();
132 	}
133 
end()134 	Iterator end() const
135 	{
136 		return _fields.end();
137 	}
138 
diagnostics()139 	const Diagnostics& diagnostics()
140 	{
141 		DiagnosticFields df;
142 		SQLSMALLINT count = 1;
143 		SQLSMALLINT messageLength = 0;
144 		static const std::string none = "None";
145 		static const std::string na = "Not applicable";
146 
147 		reset();
148 
149 		while (!Utility::isError(SQLGetDiagRec(handleType,
150 			_handle,
151 			count,
152 			df._sqlState,
153 			&df._nativeError,
154 			df._message,
155 			SQL_MESSAGE_LENGTH,
156 			&messageLength)))
157 		{
158 			if (1 == count)
159 			{
160 				// success of the following two calls is optional
161 				// (they fail if connection has not been established yet
162 				//  or return empty string if not applicable for the context)
163 				if (Utility::isError(SQLGetDiagField(handleType,
164 					_handle,
165 					count,
166 					SQL_DIAG_CONNECTION_NAME,
167 					_connectionName,
168 					sizeof(_connectionName),
169 					&messageLength)))
170 				{
171 					std::size_t len = sizeof(_connectionName) > none.length() ?
172 						none.length() : sizeof(_connectionName) - 1;
173 					std::memcpy(_connectionName, none.c_str(), len);
174 				}
175 				else if (0 == _connectionName[0])
176 				{
177 					std::size_t len = sizeof(_connectionName) > na.length() ?
178 						na.length() : sizeof(_connectionName) - 1;
179 					std::memcpy(_connectionName, na.c_str(), len);
180 				}
181 
182 				if (Utility::isError(SQLGetDiagField(handleType,
183 					_handle,
184 					count,
185 					SQL_DIAG_SERVER_NAME,
186 					_serverName,
187 					sizeof(_serverName),
188 					&messageLength)))
189 				{
190 					std::size_t len = sizeof(_serverName) > none.length() ?
191 						none.length() : sizeof(_serverName) - 1;
192 					std::memcpy(_serverName, none.c_str(), len);
193 				}
194 				else if (0 == _serverName[0])
195 				{
196 					std::size_t len = sizeof(_serverName) > na.length() ?
197 						na.length() : sizeof(_serverName) - 1;
198 					std::memcpy(_serverName, na.c_str(), len);
199 				}
200 			}
201 
202 			_fields.push_back(df);
203 
204 			std::memset(df._sqlState, 0, SQL_STATE_SIZE);
205 			std::memset(df._message, 0, SQL_MESSAGE_LENGTH);
206 			df._nativeError = 0;
207 
208 			++count;
209 		}
210 
211 		return *this;
212 	}
213 
214 private:
215 
216 	Diagnostics();
217 
218 	/// SQLGetDiagField fields
219 	SQLCHAR _connectionName[SQL_NAME_LENGTH];
220 	SQLCHAR _serverName[SQL_NAME_LENGTH];
221 
222 	/// Diagnostics container
223 	FieldVec _fields;
224 
225 	/// Context handle
226 	const H& _handle;
227 };
228 
229 
230 typedef Diagnostics<SQLHENV, SQL_HANDLE_ENV> EnvironmentDiagnostics;
231 typedef Diagnostics<SQLHDBC, SQL_HANDLE_DBC> ConnectionDiagnostics;
232 typedef Diagnostics<SQLHSTMT, SQL_HANDLE_STMT> StatementDiagnostics;
233 typedef Diagnostics<SQLHDESC, SQL_HANDLE_DESC> DescriptorDiagnostics;
234 
235 
236 } } } // namespace Poco::Data::ODBC
237 
238 
239 #endif
240