Column mappings are required to insert messages (#213)

* Column mappings are required to insert messages

* Remove references to PatterLayout supported queries from documentation. Remove unused code.

---------

Co-authored-by: Stephen Webb <swebb2066@gmail.com>
diff --git a/src/main/cpp/odbcappender.cpp b/src/main/cpp/odbcappender.cpp
index f6f9315..2e757b0 100644
--- a/src/main/cpp/odbcappender.cpp
+++ b/src/main/cpp/odbcappender.cpp
@@ -192,7 +192,7 @@
 
 bool ODBCAppender::requiresLayout() const
 {
-    return false;
+	return false;
 }
 
 void ODBCAppender::activateOptions(log4cxx::helpers::Pool&)
@@ -200,6 +200,10 @@
 #if !LOG4CXX_HAVE_ODBC
 	LogLog::error(LOG4CXX_STR("Can not activate ODBCAppender unless compiled with ODBC support."));
 #else
+	if (_priv->mappedName.empty())
+	{
+		LogLog::error(LOG4CXX_STR("ODBCAppender column mappings not defined, logging events will not be inserted"));
+	}
 	auto specs = getFormatSpecifiers();
 	for (auto& name : _priv->mappedName)
 	{
@@ -248,56 +252,11 @@
 
 LogString ODBCAppender::getLogStatement(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p) const
 {
-    return event->getMessage();
+    return LogString();
 }
 
 void ODBCAppender::execute(const LogString& sql, log4cxx::helpers::Pool& p)
 {
-#if LOG4CXX_HAVE_ODBC
-	SQLRETURN ret;
-	SQLHDBC con = SQL_NULL_HDBC;
-	SQLHSTMT stmt = SQL_NULL_HSTMT;
-
-	try
-	{
-		con = getConnection(p);
-
-		ret = SQLAllocHandle( SQL_HANDLE_STMT, con, &stmt);
-
-		if (ret < 0)
-		{
-			throw SQLException( SQL_HANDLE_DBC, con, "Failed to allocate sql handle", p);
-		}
-
-#if LOG4CXX_LOGCHAR_IS_WCHAR
-		ret = SQLExecDirectW(stmt, (SQLWCHAR*)sql.c_str(), SQL_NTS);
-#elif LOG4CXX_LOGCHAR_IS_UTF8
-		ret = SQLExecDirectA(stmt, (SQLCHAR*)sql.c_str(), SQL_NTS);
-#else
-		SQLWCHAR* wsql;
-		encode(&wsql, sql, p);
-		ret = SQLExecDirectW(stmt, wsql, SQL_NTS);
-#endif
-		if (ret < 0)
-		{
-			throw SQLException(SQL_HANDLE_STMT, stmt, "Failed to execute sql statement", p);
-		}
-	}
-	catch (SQLException&)
-	{
-		if (stmt != SQL_NULL_HSTMT)
-		{
-			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-		}
-
-		throw;
-	}
-
-	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-	closeConnection(con);
-#else
-	throw SQLException("log4cxx build without ODBC support");
-#endif
 }
 
 /* The default behavior holds a single connection open until the appender
@@ -609,25 +568,18 @@
 {
 	for (auto& logEvent : _priv->buffer)
 	{
-		try
-		{
-			if (!_priv->parameterValue.empty())
-			{
+		if (_priv->parameterValue.empty())
+			_priv->errorHandler->error(LOG4CXX_STR("ODBCAppender column mappings not defined"));
 #if LOG4CXX_HAVE_ODBC
-				if (0 == _priv->preparedStatement)
-					_priv->setPreparedStatement(getConnection(p), p);
-				_priv->setParameterValues(logEvent, p);
-				auto ret = SQLExecute(_priv->preparedStatement);
-				if (ret < 0)
-				{
-					throw SQLException(SQL_HANDLE_STMT, _priv->preparedStatement, "Failed to execute prepared statement", p);
-				}
-#endif
-			}
-			else
+		else try
+		{
+			if (0 == _priv->preparedStatement)
+				_priv->setPreparedStatement(getConnection(p), p);
+			_priv->setParameterValues(logEvent, p);
+			auto ret = SQLExecute(_priv->preparedStatement);
+			if (ret < 0)
 			{
-				auto sql = getLogStatement(logEvent, p);
-				execute(sql, p);
+				throw SQLException(SQL_HANDLE_STMT, _priv->preparedStatement, "Failed to execute prepared statement", p);
 			}
 		}
 		catch (SQLException& e)
@@ -635,6 +587,7 @@
 			_priv->errorHandler->error(LOG4CXX_STR("Failed to execute sql"), e,
 				ErrorCode::FLUSH_FAILURE);
 		}
+#endif
 	}
 
 	// clear the buffer of reported events
diff --git a/src/main/include/log4cxx/db/odbcappender.h b/src/main/include/log4cxx/db/odbcappender.h
index 6c65ad4..3b308ad 100644
--- a/src/main/include/log4cxx/db/odbcappender.h
+++ b/src/main/include/log4cxx/db/odbcappender.h
@@ -56,16 +56,6 @@
 using the <b>sql</b> parameter element
 or programatically by calling the <code>setSql(String sql)</code> method.
 
-If no <b>ColumnMapping</b> element is provided in the configuration file
-the sql statement is assumed to be a PatternLayout layout.
-In this case all the conversion patterns in PatternLayout
-can be used inside of the statement. (see the test cases for examples)
-
-If the <b>sql</b> element is not provided
-and no <b>ColumnMapping</b> element is provided
-the attached a PatternLayout layout element
-is assumed to contain the sql statement.
-
 The following <b>param</b> elements are optional:
 - one of <b>DSN</b>, <b>URL</b>, <b>ConnectionString</b> -
   The <b>serverName</b> parameter value in the <a href="https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlconnect-function">SQLConnect</a> call.
@@ -138,10 +128,6 @@
   <priority value ="INFO" />
   <appender-ref ref="ASYNC" />
 </root>
-<appender name="PatternAppender" class="ODBCAppender">
- <param name="DSN" value="LoggingDSN"/>
- <param name="sql" value="INSERT INTO [ApplicationLogs].[dbo].[UnitTestLog] ([Thread],[LogName],[LogTime],[LogLevel],[FileName],[FileLine],[Message],[MappedContext]) VALUES ('%t', '%c','%d{yyyy-MM-dd HH:mm:ss.SSSSSS}','%p','%f','%L','%m{'}','%J{'}')" />
-</appender>
 </log4j:configuration>
 ~~~
 
@@ -182,20 +168,14 @@
 
 	protected:
 		/**
-		* Sends the event to the attached PatternLayout object.
-		* The layout will format the given pattern into a workable SQL string.
-		*
+		* To be removed.
 		*/
 		LogString getLogStatement(const spi::LoggingEventPtr& event,
 			helpers::Pool& p) const;
 
 		/**
 		*
-		* Override this to provide an alternate method of getting
-		* connections (such as caching).  One method to fix this is to open
-		* connections at the start of flushBuffer() and close them at the
-		* end.  I use a connection pool outside of ODBCAppender which is
-		* accessed in an override of this method.
+		* To be removed.
 		* */
 		virtual void execute(const LogString& sql,
 			log4cxx::helpers::Pool& p) /*throw(SQLException)*/;
diff --git a/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
index c82c63b..04e278d 100644
--- a/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
+++ b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
@@ -25,15 +25,10 @@
     <param name="ConversionPattern" value="%d %-5p %c{2} - %m%n"/>
   </layout>
 </appender>
-<appender name="PatternAppender" class="ODBCAppender">
- <param name="DSN" value="Log4cxxTest"/>
- <param name="sql" value="INSERT INTO UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES ('%t','%c','%d{yyyy-MM-dd HH:mm:ss.SSSSSS}','%p','%f','%L','%m{'}')" />
- <!--param name="sql" value="INSERT INTO ApplicationLogs.dbo.UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES ('%t','%c','%d{yyyy-MM-dd HH:mm:ss.SSSSSS}','%p','%f','%L','%m{'}')" /-->
-</appender>
 <appender name="PreparedAppender" class="ODBCAppender">
  <param name="DSN" value="Log4cxxTest"/>
- <param name="sql" value="INSERT INTO UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" />
- <!--param name="sql" value="INSERT INTO ApplicationLogs.dbo.UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" /-->
+ <!--param name="sql" value="INSERT INTO UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" /-->
+ <param name="sql" value="INSERT INTO ApplicationLogs.dbo.UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" />
  <param name="ColumnMapping" value="thread"/>
  <param name="ColumnMapping" value="logger"/>
  <param name="ColumnMapping" value="time"/>