Date: Thursday, November 10, 2005 @ 12:03:34
  Author: gilles
    Path: /cvsroot/carob/carob

   Added: include/Statement.hpp (1.1) src/Statement.cpp (1.1)
          test/TestStatement.cpp (1.1) test/TestStatement.hpp (1.1)
Modified: Makefile (1.9 -> 1.10) include/Connection.hpp (1.16 -> 1.17)
          include/DriverResultSet.hpp (1.5 -> 1.6) src/Connection.cpp
          (1.18 -> 1.19) src/DriverResultSet.cpp (1.5 -> 1.6)
          test/CarobTestLauncher.cpp (1.2 -> 1.3)

Added Statement class and tests


-----------------------------+
 Makefile                    |    1 
 include/Connection.hpp      |   32 +++++
 include/DriverResultSet.hpp |   47 ++++++++
 include/Statement.hpp       |  189 ++++++++++++++++++++++++++++++++++
 src/Connection.cpp          |   33 ++++++
 src/DriverResultSet.cpp     |   19 +++
 src/Statement.cpp           |  141 ++++++++++++++++++++++++++
 test/CarobTestLauncher.cpp  |    2 
 test/TestStatement.cpp      |  227 ++++++++++++++++++++++++++++++++++++++++++
 test/TestStatement.hpp      |   74 +++++++++++++
 10 files changed, 761 insertions(+), 4 deletions(-)


Index: carob/Makefile
diff -u carob/Makefile:1.9 carob/Makefile:1.10
--- carob/Makefile:1.9  Wed Nov  9 14:36:54 2005
+++ carob/Makefile      Thu Nov 10 12:03:33 2005
@@ -67,6 +67,7 @@
                       ${TESTDIR}/TestConnect.o\
                       ${TESTDIR}/TestExecWriteRequest.o\
                       ${TESTDIR}/TestExecReadRequest.o\
+                      ${TESTDIR}/TestStatement.o\
                       ${TESTDIR}/CarobTestLauncher.o
 TEST_CFLAGS            = -g3 -Wall -I${INCDIR}
 TEST_LDFLAGS           = -Wl,-rpath,. -L. -l${LIB_CAROB} -ldl -l${LIB_CPPUNIT}
Index: carob/include/Connection.hpp
diff -u carob/include/Connection.hpp:1.16 carob/include/Connection.hpp:1.17
--- carob/include/Connection.hpp:1.16   Fri Nov  4 10:40:21 2005
+++ carob/include/Connection.hpp        Thu Nov 10 12:03:33 2005
@@ -43,7 +43,7 @@
 #define Close                                   30
 #define Reset                                   31
 //#define FetchNextResultSetRows                  32
-//#define CloseRemoteResultSet                    33
+#define CloseRemoteResultSet                    33
 //#define RestoreConnectionState                  34
 #define SetAutoCommit                           35
 //#define ConnectionGetCatalog                    36
@@ -76,11 +76,12 @@
 //#define DatabaseStaticMetadata                  80
 
 //Forward declarations to reduce includes
-class DriverResultSet;
-class SelectRequest;
 class AbstractRequest;
 class AbstractWriteRequest;
 class ConnectionParameters;
+class DriverResultSet;
+class SelectRequest;
+class Statement;
 /**
  * This class implements the communication protocol to the Controller.
  * <p>
@@ -138,6 +139,18 @@
   void              rollback() throw (SocketIOException, DriverException,
                         UnexpectedException);
   /**
+   * SQL statements without parameters are normally executed using
+   * <code>Statement</code> objects. If the same SQL statement is executed
+   * many times, it is more efficient to use a <code>PreparedStatement</code>.
+   * The <code>ResultSet</code> will be
+   * <code>TYPE_FORWARD_ONLY</cde>/<code>CONCUR_READ_ONLY</code>.
+   *
+   * @return a new <code>Statement</code> object
+   * @exception DriverSQLException passed through from the constructor
+   */
+  Statement*        createStatement() throw (SocketIOException,
+                        DriverException, UnexpectedException);
+  /**
    * Performs a write request and return the number of rows affected.
    * 
    * @param r the write request to execute
@@ -159,6 +172,19 @@
                         throw (SocketIOException, BackendException, 
                         ControllerException, ProtocolException,
                         NotImplementedException, UnexpectedException);
+  /**
+   * Closes the remote ResultSet given its cursor name.
+   * 
+   * @param cursorName cursor name of the ResultSet to close
+   * @throw DriverSQLException if an error occurs
+   * @throw SocketIOException if an error occurs on the socket
+   */
+  void closeRemoteResultSet(wstring cursorName) throw (SocketIOException,
+                                                       BackendException,
+                                                       ControllerException,
+                                                       ProtocolException,
+                                                       UnexpectedException);
+
 protected:
   /** 
    * Protected constructor so that no one but the connection pool 
Index: carob/include/DriverResultSet.hpp
diff -u carob/include/DriverResultSet.hpp:1.5 
carob/include/DriverResultSet.hpp:1.6
--- carob/include/DriverResultSet.hpp:1.5       Thu Nov  3 15:29:02 2005
+++ carob/include/DriverResultSet.hpp   Thu Nov 10 12:03:33 2005
@@ -30,6 +30,37 @@
 #include "SQLDataSerialization.hpp"
 
 /**
+ * The constant indicating the type for a <code>ResultSet</code> object
+ * whose cursor may move only forward.
+ */
+#define TYPE_FORWARD_ONLY 1003
+
+/**
+ * The constant indicating the type for a <code>ResultSet</code> object
+ * that is scrollable but generally not sensitive to changes made by others.
+ */
+#define TYPE_SCROLL_INSENSITIVE 1004
+
+/**
+ * The constant indicating the type for a <code>ResultSet</code> object
+ * that is scrollable and generally sensitive to changes made by others.
+ */
+#define TYPE_SCROLL_SENSITIVE 1005
+
+/**
+ * The constant indicating the concurrency mode for a
+ * <code>ResultSet</code> object that may NOT be updated.
+ */
+#define CONCUR_READ_ONLY 1007
+
+/**
+ * The constant indicating the concurrency mode for a
+ * <code>ResultSet</code> object that may be updated.
+ */
+#define CONCUR_UPDATABLE 1008
+
+class Statement;
+/**
  * A <code>ResultSet</code> provides access to a table of data generated by
  * executing a Statement. The table rows are retrieved in sequence. Within a 
row
  * its column values can be accessed in any order.
@@ -67,6 +98,7 @@
  */
 class DriverResultSet
 {
+friend class Statement;
 public:
   /**
    * Creates a DriverResultSet by deserializing infos from the given connection
@@ -113,6 +145,13 @@
    */
   int32_t* getInt(int32_t columnIndex) throw (SQLException,
       UnexpectedException);
+  /**
+   * Closes the remote ResultSet if the ResultSet was streamed else just closes
+   * the ResultSet locally.
+   * 
+   * @exception DriverSQLException if a database access error occurs
+   */
+  void close() throw (SQLException, UnexpectedException);
  
 protected:
   /**
@@ -123,6 +162,12 @@
    */
   void receiveRows() throw (SocketIOException, ProtocolException,
       UnexpectedException);
+  /**
+   * Sets the statement.
+   * 
+   * @param stmtPtr pointer to the statement to set
+   */
+  void setStatement(Statement* stmtPtr);
 private:
   /** Cursor to current row */
   int32_t                     currentRow;
@@ -158,7 +203,7 @@
   /** Warning chain */
   //protected SQLWarning                      warnings              = null;
   /** Statement corresponding to this ResultSet, if any (not for metadata) */
-  //protected transient Statement             owningStatement;
+  Statement*                  owningStatementPtr;
   /** The driver connection we were received from. Useful for streaming */
   Connection*                 connectionPtr;
 
Index: carob/include/Statement.hpp
diff -u /dev/null carob/include/Statement.hpp:1.1
--- /dev/null   Thu Nov 10 12:03:34 2005
+++ carob/include/Statement.hpp Thu Nov 10 12:03:33 2005
@@ -0,0 +1,189 @@
+/**
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2005 Emic Networks
+ * Contact: [EMAIL PROTECTED]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Gilles Rayrat
+ * Contributor(s): 
+ */
+#ifndef STATEMENT_HPP_
+#define STATEMENT_HPP_
+
+#include <string>
+#include <vector>
+
+#include "DriverResultSet.hpp"
+
+/**
+ * A <code>Statement</code> object is used for executing a static SQL
+ * statement and obtaining the results produced by it.
+ * <p>
+ * Only one <code>ResultSet</code> per <code>Statement</code> can be open at
+ * any point in time. Therefore, if the reading of one <code>ResultSet</code>
+ * is interleaved with the reading of another, each must have been generated by
+ * different <code>Statements</code>. All <code>Statements</code> execute
+ * methods implicitly close a statement's current <code>ResultSet</code> if an
+ * open one exists.
+ */
+class Statement
+{
+public:
+       Statement();
+  /** Constructs a Statement with the given connection */
+  Statement(Connection* c);
+       virtual ~Statement();
+  /**
+   * Executes a SQL statement that returns a single ResultSet
+   * @param sqlQuery typically a static SQL <code>SELECT</code> statement
+   * @param mustBroadcast whether the SelectRequest must be broadcasted on the
+   *        cluster (eg. for queries like SELECT FOR UPDATE)
+   * @return a ResulSet pointer that contains the data produced by the query
+   */
+  DriverResultSet*    executeQuery(std::wstring query, bool mustBroadcast)
+      throw (SocketIOException, BackendException, ControllerException,
+      ProtocolException, NotImplementedException, UnexpectedException);
+  /**
+   * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
+   * statements that return nothing such as SQL DDL statements can be executed
+   * 
+   * @param sql a SQL statement
+   * @return either a row count, or 0 for SQL commands
+   */
+  int                 executeUpdate(std::wstring sql) throw 
(SocketIOException, 
+    BackendException, ControllerException, ProtocolException,
+    UnexpectedException);
+
+  // GETTERS AND SETTERS
+  /**
+   * Returns the current result as an update count, if the result is a
+   * <code>ResultSet</code> or there are no more results, -1 is returned. It
+   * should only be called once per result.
+   * 
+   * @return the current result as an update count.
+   */
+  int getUpdateCount() { return updateCount; }
+  /**
+   * The queryTimeout limit is the number of seconds the driver will wait for a
+   * Statement to execute.
+   * 
+   * @return the current query timeout limit in seconds; 0 = unlimited
+   */
+  int getQueryTimeout() { return timeout; }
+  /**
+   * Sets the number of seconds the driver will wait for a
+   * <code>Statement</code> object to execute.
+   * 
+   * @param seconds the new query timeout limit in seconds; 0 means no timeout
+   * @throw DriverException if the condition
+   *              seconds >= 0 is not satisfied
+   */
+  void setQueryTimeout(int seconds) throw (DriverException, 
UnexpectedException);
+  /** Gets the fetch size */
+  int getFetchSize() { return fetchSize; }
+  /**
+   * Set the default fetch size for the produced ResultSet.
+   * 
+   * @param rows number of rows that should be fetched from the database
+   * @throw DriverException if the condition 0 <= size <= getMaxRows()
+   *        is not satisfied
+   */
+  void setFetchSize(int rows) throw (DriverException, UnexpectedException);
+  /**
+   * Retrieve the type of the generated <code>ResultSet</code>.
+   * 
+   * @return one of <code>TYPE_FORWARD_ONLY</code> or
+   *         <code>TYPE_SCROLL_INSENSITIVE</code>
+   */
+  /**
+   * Defines the SQL cursor name that will be used by subsequent execute
+   * methods. This name can then be used in SQL positioned update/delete
+   * statements to identify the current row in the ResultSet generated by this
+   * statement. If a database doesn't support positioned update/delete, this
+   * method is a no-op.
+   * 
+   * @param name the new cursor name
+   */
+  void setCursorName(wstring name) { cursorName = name; }
+  int getResultSetType() { return resultSetType; }
+  /**
+   * @param value an <code>int</code> value
+   */
+  void setResultSetType(int value) throw (DriverException, 
UnexpectedException);
+  /**
+   * Retrieves the concurrency mode for the <code>ResultSet</code>.
+   * @return <code>CONCUR_READ_ONLY</code> or <code>CONCUR_UPDATABLE</code>
+   */
+  int getResultSetConcurrency() { return resultSetConcurrency; }
+  /**
+   * Sets the concurrency mode for the <code>ResultSet</code>
+   * @param value an <code>int</code> value <code>CONCUR_READ_ONLY</code> or
+   * <code>CONCUR_UPDATABLE</code>
+   * @throw DriverException if the input value is not valid
+   */
+  void setResultSetConcurrency(int value) throw (DriverException,
+      UnexpectedException);
+  /**
+   * The maxRows limit is set to limit the number of rows that any
+   * <code>ResultSet</code> can contain. If the limit is exceeded, the excess
+   * rows are silently dropped.
+   * 
+   * @return the current maximum row limit; zero means unlimited
+   */
+  int getMaxRows() { return maxRows; }
+  /**
+   * Sets the maximum number of rows that any <code>ResultSet</code> can
+   * contain.
+   * 
+   * @param max the new max rows limit; 0 means unlimited
+   * @exception DriverException if the condition max >= 0 is not satisfied
+   */
+  void setMaxRows(int max) throw (DriverException, UnexpectedException);
+  /**
+   * If escape scanning is on (the default), the driver will do escape
+   * substitution before sending the SQL to the database.
+   * 
+   * @param enable true to enable; false to disable
+   */
+  void setEscapeProcessing(bool enable) { escapeProcessing = enable; }
+
+private:
+  /** Connection that created us */
+  Connection*         connectionPtr;
+  /** Current result for a read request */
+  DriverResultSet*    resultPtr;
+  /** Update count for a write request */
+  int                 updateCount;
+  /** Query timeout in seconds (0 means no timeout) */
+  int                 timeout;
+  /** Default ResultSet fetch size */
+  int                 fetchSize;
+  /** Cursor name used jointly with fetch size */
+  wstring             cursorName;
+  /** Type of the ResultSet defaults to TYPE_FORWARD_ONLY */
+  int                 resultSetType;
+  /** ResultSet Concurrency defaults to CONCUR_READ_ONLY */
+  int                 resultSetConcurrency;
+  /** Maximum number of rows */
+  int                 maxRows;
+  /** Should the driver to escape processing before sending to the DB? */
+  bool                escapeProcessing;
+  /**
+   * Tests if this statement is closed.
+   * @return <code>true</code> if this statement is closed
+   */
+  bool isClosed() { return (connectionPtr == NULL); }
+};
+
+#endif /*STATEMENT_HPP_*/
Index: carob/src/Connection.cpp
diff -u carob/src/Connection.cpp:1.18 carob/src/Connection.cpp:1.19
--- carob/src/Connection.cpp:1.18       Fri Nov  4 10:40:20 2005
+++ carob/src/Connection.cpp    Thu Nov 10 12:03:33 2005
@@ -25,6 +25,7 @@
 #include "ConnectionParameters.hpp"
 #include "DriverResultSet.hpp"
 #include "SelectRequest.hpp"
+#include "Statement.hpp"
 
 Connection::~Connection()
 {
@@ -406,6 +407,13 @@
   }
 }
 
+Statement* Connection::createStatement() throw (DriverException, 
+    SocketIOException, UnexpectedException)
+{
+  checkIfConnected();
+  return new Statement(this);
+}
+
 void Connection::checkIfConnected() const throw (SocketIOException,
     UnexpectedException)
 {
@@ -518,6 +526,31 @@
   return retVal;
 }
 
+void Connection::closeRemoteResultSet(wstring cursorName)
+    throw (SocketIOException, BackendException, ControllerException,
+    ProtocolException, UnexpectedException)
+{
+  wstring fctName(L"Connection::closeRemoteResultSet");
+  
+  connectionCS.Enter();
+  checkIfConnected();
+  try
+  {
+    *driverSocketPtr<<CloseRemoteResultSet;
+    *driverSocketPtr<<cursorName;
+    if (isDebugEnabled())
+      logDebug(fctName, L"Closing remote ResultSet");
+
+    receiveBoolOrException();
+  }
+  catch (...)
+  {
+    connectionCS.Leave();
+    throw;
+  }
+  connectionCS.Leave();
+}
+
 bool Connection::receiveBoolOrException() throw (SocketIOException,
                                                  BackendException,
                                                  ControllerException,
Index: carob/src/DriverResultSet.cpp
diff -u carob/src/DriverResultSet.cpp:1.5 carob/src/DriverResultSet.cpp:1.6
--- carob/src/DriverResultSet.cpp:1.5   Fri Oct 28 11:06:21 2005
+++ carob/src/DriverResultSet.cpp       Thu Nov 10 12:03:33 2005
@@ -23,6 +23,7 @@
 #include "Common.hpp"
 #include "DriverResultSet.hpp"
 #include "TypeTag.hpp"
+#include "Statement.hpp"
 
 DriverResultSet::~DriverResultSet()
 {
@@ -89,6 +90,17 @@
   return sRet;
 }
 
+void DriverResultSet::setStatement(Statement* stmtPtr)
+{
+  owningStatementPtr = stmtPtr;
+  if (stmtPtr != NULL)
+  {
+    fetchSize = stmtPtr->getFetchSize();
+    resultSetConcurrency = stmtPtr->getResultSetConcurrency();
+    resultSetType = stmtPtr->getResultSetType();
+  }
+}
+
 bool DriverResultSet::next() throw (SQLException, UnexpectedException)
 {
   checkIfClosed();
@@ -197,6 +209,13 @@
   return (int32_t*)((data[currentRow])[columnIndex - 1]);
 }
 
+void DriverResultSet::close() throw (SQLException, UnexpectedException)
+{
+  if (hasMoreData)
+    connectionPtr->closeRemoteResultSet(cursorName);
+  isClosed = true;
+}
+
 void DriverResultSet::receiveRows() throw (SocketIOException, 
ProtocolException,
     UnexpectedException)
 {
Index: carob/src/Statement.cpp
diff -u /dev/null carob/src/Statement.cpp:1.1
--- /dev/null   Thu Nov 10 12:03:34 2005
+++ carob/src/Statement.cpp     Thu Nov 10 12:03:33 2005
@@ -0,0 +1,141 @@
+#include "CarobException.hpp"
+#include "Common.hpp"
+#include "SelectRequest.hpp"
+#include "Statement.hpp"
+#include "UpdateRequest.hpp"
+
+using namespace std;
+
+Statement::Statement()
+{
+  Statement(NULL);
+}
+
+Statement::Statement(Connection* c) :
+connectionPtr(c),
+resultPtr(NULL),
+updateCount(-1),
+timeout(0),
+fetchSize(0),
+cursorName(L""),
+resultSetType(TYPE_FORWARD_ONLY),
+resultSetConcurrency(CONCUR_READ_ONLY),
+maxRows(0),
+escapeProcessing(true)
+{
+}
+
+Statement::~Statement()
+{
+  if (resultPtr != NULL)
+    delete resultPtr;
+}
+
+
+DriverResultSet* Statement::executeQuery(wstring query, bool mustBroadcast)
+    throw (SocketIOException, BackendException, ControllerException,
+    ProtocolException, NotImplementedException, UnexpectedException)
+{
+  wstring fctName(L"Statement::executeQuery");
+  //trims original string
+  query = trim(query);
+  if (isClosed())
+  {
+    throw DriverException(L"Unable to execute query on a closed statement");
+  }
+  updateCount = -1; // invalidate the last write result
+  if (resultPtr != NULL)
+  { // Discard the previous result
+    resultPtr->close();
+    resultPtr = NULL;
+  }
+
+  SelectRequest request(query, escapeProcessing, timeout, LINE_SEPARATOR);
+  request.setMaxRows(maxRows);
+  request.setFetchSize(fetchSize);
+  request.setCursorName(cursorName);
+  request.setMustBroadcast(mustBroadcast);
+  resultPtr = connectionPtr->execReadRequest(request);
+  resultPtr->setStatement(this);
+  return resultPtr;
+}
+
+int Statement::executeUpdate(wstring query) throw (SocketIOException, 
+    BackendException, ControllerException, ProtocolException,
+    UnexpectedException)
+{
+  wstring fctName(L"Statement::executeQuery");
+  //trims original string
+  query = trim(query);
+  if (resultPtr != NULL)
+  { // Discard the previous result
+    resultPtr->close();
+    resultPtr = NULL;
+  }
+  UpdateRequest request(query, escapeProcessing, timeout, LINE_SEPARATOR);
+  int updateCount = connectionPtr->execWriteRequest(request);
+  return updateCount;
+}
+
+// GETTERS AND SETTERS
+void Statement::setQueryTimeout(int seconds) throw (DriverException, 
UnexpectedException)
+{
+  if (seconds < 0)
+  {
+    throw DriverException(L"Invalid query timeout value: " + 
toWString(seconds));
+  }
+  timeout = seconds;
+}
+
+void Statement::setFetchSize(int rows) throw (DriverException, 
UnexpectedException)
+{
+  if (rows < 0
+  // The spec forgets the case maxRows = 0.
+      || 0 < maxRows && maxRows < rows)
+  {
+    throw DriverException(L"Invalid fetch size value: " + toWString(rows));
+  }
+  fetchSize = rows;
+}
+
+void Statement::setResultSetType(int value) throw (DriverException,
+    UnexpectedException)
+{
+  switch (value)
+  {
+    case TYPE_FORWARD_ONLY :
+    case TYPE_SCROLL_INSENSITIVE :
+      resultSetType = value;
+      break;
+    case TYPE_SCROLL_SENSITIVE :
+      throw DriverException(
+          L"TYPE_SCROLL_SENSITIVE is not a supported ResultSet type");
+    default :
+      throw DriverException(L"Invalid ResultSet type");
+  }
+}
+
+void Statement::setResultSetConcurrency(int value) throw (DriverException,
+    UnexpectedException)
+{
+  switch (value)
+  {
+    case CONCUR_READ_ONLY :
+    case CONCUR_UPDATABLE :
+      resultSetConcurrency = value;
+      break;
+    default :
+      throw DriverException(L"Invalid ResultSet concurrency mode: "
+          + toWString(value));
+  }
+}
+
+void Statement::setMaxRows(int max) throw (DriverException, 
UnexpectedException)
+{
+  if (max < 0)
+  {
+    throw DriverException(L"Invalid max rows limit: " + toWString(max));
+  }
+  // this may break fetchSize <= maxRows
+  maxRows = max;
+}
Index: carob/test/CarobTestLauncher.cpp
diff -u carob/test/CarobTestLauncher.cpp:1.2 
carob/test/CarobTestLauncher.cpp:1.3
--- carob/test/CarobTestLauncher.cpp:1.2        Fri Nov  4 11:27:44 2005
+++ carob/test/CarobTestLauncher.cpp    Thu Nov 10 12:03:33 2005
@@ -27,6 +27,7 @@
 #include "TestConnect.hpp"
 #include "TestExecReadRequest.hpp"
 #include "TestExecWriteRequest.hpp"
+#include "TestStatement.hpp"
 
 int main (int argc, char **argv)
 {
@@ -38,6 +39,7 @@
   runner.addTest(TestExecWriteRequest::suite());
   runner.addTest(TestExecReadRequest::suite());
   runner.addTest(TestBeginCommitRollback::suite());
+  runner.addTest(TestStatement::suite());
   runner.run();
   return 0;
 
Index: carob/test/TestStatement.cpp
diff -u /dev/null carob/test/TestStatement.cpp:1.1
--- /dev/null   Thu Nov 10 12:03:34 2005
+++ carob/test/TestStatement.cpp        Thu Nov 10 12:03:33 2005
@@ -0,0 +1,227 @@
+/**
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2005 Emic Networks
+ * Contact: [EMAIL PROTECTED]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Gilles Rayrat
+ * Contributor(s): 
+ */
+#include <iostream>
+
+#include "Common.hpp"
+#include "CarobException.hpp"
+#include "DriverResultSet.hpp"
+#include "TestStatement.hpp"
+#include "SelectRequest.hpp"
+#include "Statement.hpp"
+
+
+//EXECUTE QUERY
+void TestStatement::testExecuteQueryBadRequest()
+{
+  wstring fctName(L"TestStatement::testExecuteQueryBadRequest");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing dummy read");
+    }
+    statementPtr->executeQuery(L"dummy request;", false);
+    // We should receive an exception instead of coming here
+    CPPUNIT_ASSERT(false);
+  }
+  catch (BackendException sioe)
+  {
+    logError(fctName, L"BackendException "+sioe.description());
+    CPPUNIT_ASSERT(true);
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+void TestStatement::testExecuteQueryBadTable()
+{
+  wstring fctName(L"testExecuteQueryBadTable");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing bad table read");
+    }
+    statementPtr->executeQuery(L"select * from dummy;", false);
+    // We should receive an exception instead of coming here
+    CPPUNIT_ASSERT(false);
+  }
+  catch (BackendException be)
+  {
+    //logError(fctName, L"BackendException "+be.description());
+    CPPUNIT_ASSERT(true);
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+void TestStatement::testExecuteQueryGood()
+{
+  wstring fctName(L"TestStatement::testExecuteQueryGood");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing read");
+    }
+    DriverResultSet* drsPtr = statementPtr->executeQuery(
+        L"select * from address;", false);
+    logDebug(fctName, *drsPtr);
+    //Display five rows for debugging...
+//    wcerr<<L"Row\tId\tName\t\tCost"<<endl;
+    wcerr<<L"Row\tId\tFirstName\t\tLastName"<<endl;
+    for (int i=0; i<50; i++)
+    {
+      drsPtr->next();
+      int32_t* iPtr = drsPtr->getInt(1);
+      wstring* wsPtr = drsPtr->getString(2);
+      wstring* ws2Ptr = drsPtr->getString(3);
+      wcerr<<i+1<<L"\t"<<*iPtr<<L"\t"<<*wsPtr<<L"\t"<<*ws2Ptr<<endl;
+    }
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+
+//EXECUTE UPDATE
+
+void TestStatement::testExecuteUpdateBadRequest()
+{
+  wstring fctName(L"TestStatement::testExecuteUpdateBadRequest");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing dummy update");
+    }
+    statementPtr->executeUpdate(L"dummy request;");
+    // We should receive an exception instead of coming here
+    CPPUNIT_ASSERT(false);
+  }
+  catch (BackendException sioe)
+  {
+    logError(fctName, L"BackendException "+sioe.description());
+    CPPUNIT_ASSERT(true);
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+void TestStatement::testExecuteUpdateBadTable()
+{
+  wstring fctName(L"testExecuteUpdateBadTable");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing bad table update");
+    }
+    statementPtr->executeUpdate(L"update dummy set name='gotit' where id=0;");
+    // We should receive an exception instead of coming here
+    CPPUNIT_ASSERT(false);
+  }
+  catch (BackendException be)
+  {
+    //logError(fctName, L"BackendException "+be.description());
+    CPPUNIT_ASSERT(true);
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+void TestStatement::testExecuteUpdateGood()
+{
+  wstring fctName(L"TestStatement::testExecuteUpdateGood");
+  Statement* statementPtr = NULL;
+  try
+  {
+    statementPtr = connectionPtr->createStatement();
+    if (isDebugEnabled())
+    {
+      logDebug(fctName, L"Executing update");
+    }
+    int32_t nbRowsAffected = statementPtr->executeUpdate(
+        L"update product set name='changed by testExecuteUpdateGood' where 
id=0;");
+    CPPUNIT_ASSERT(nbRowsAffected == 1);
+  }
+  catch (...)
+  {
+    logError(fctName, L"Catched unexpected exception");
+    CPPUNIT_ASSERT(false);
+  }
+  delete statementPtr;
+}
+
+CppUnit::Test* TestStatement::suite()
+{
+  CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "TestStatement" );
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteQueryBadRequest", 
+                                 &TestStatement::testExecuteQueryBadRequest));
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteQueryBadTable", 
+                                 &TestStatement::testExecuteQueryBadTable));
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteQueryGood", 
+                                 &TestStatement::testExecuteQueryGood));
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteUpdateBadRequest", 
+                                 &TestStatement::testExecuteUpdateBadRequest));
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteUpdateBadTable", 
+                                 &TestStatement::testExecuteUpdateBadTable));
+  suiteOfTests->addTest(new CppUnit::TestCaller<TestStatement>(
+                                 "testExecuteUpdateGood", 
+                                 &TestStatement::testExecuteUpdateGood));
+
+  return suiteOfTests;
+}
Index: carob/test/TestStatement.hpp
diff -u /dev/null carob/test/TestStatement.hpp:1.1
--- /dev/null   Thu Nov 10 12:03:34 2005
+++ carob/test/TestStatement.hpp        Thu Nov 10 12:03:33 2005
@@ -0,0 +1,74 @@
+/**
+ * Sequoia: Database clustering technology.
+ * Copyright (C) 2005 Emic Networks
+ * Contact: [EMAIL PROTECTED]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Initial developer(s): Gilles Rayrat
+ * Contributor(s): 
+ */
+
+#ifndef TESTSTATEMENT_H_
+#define TESTSTATEMENT_H_
+
+#include "TestOnValidConnection.hpp"
+
+/**
+ * Test class for Statement class.
+ * This is basically a copy of exec read and exec write commands testing
+ * A controller *MUST* run locally for test success !!!
+ */
+class TestStatement : public TestOnValidConnection
+{
+public:
+  /** Suite of tests to be run */
+  static CppUnit::Test* suite();
+
+  //EXECUTE QUERY
+  /**
+   * Tries to send a dummy read request via a Statement to the controller and
+   * checks that the error is consistent.
+   */
+  void testExecuteQueryBadRequest();
+  /**
+   * Tries to do a good read request on a dummy table via a Statement and
+   * checks that the error is consistent.
+   */
+  void testExecuteQueryBadTable();
+  /**
+   * Sends a valid select request to the controller via a Statement and checks
+   * that there is no error. Also displays a part of the result for manual
+   * checking.
+   */
+  void testExecuteQueryGood();
+  
+  //EXECUTE UPDATE
+  /**
+   * Tries to send a dummy write request to the controller via a Statement and
+   * checks that the error is consistent.
+   */
+  void testExecuteUpdateBadRequest();
+  /**
+   * Tries to do a good write request on a dummy table via a Statement and
+   * checks that the error is consistent.
+   */
+  void testExecuteUpdateBadTable();
+  /**
+   * Sends a valid update request to the controller via a Statement and checks
+   * that there is no error.
+   */
+  void testExecuteUpdateGood();
+};
+
+#endif /*TESTSTATEMENT_H_*/

_______________________________________________
Carob-commits mailing list
[email protected]
https://forge.continuent.org/mailman/listinfo/carob-commits

Reply via email to