changeset 184:4c746050969a

Add support for request (PostgreSQL done) Task: #199
author David Demelier <markand@malikania.fr>
date Mon, 11 Nov 2013 18:05:47 +0100
parents 73146c7c763f
children 523156bb3af5
files C++/Driver.cpp C++/Driver.h C++/DriverPostgres.cpp C++/DriverPostgres.h
diffstat 4 files changed, 298 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Driver.cpp	Tue Oct 29 21:33:00 2013 +0100
+++ b/C++/Driver.cpp	Mon Nov 11 18:05:47 2013 +0100
@@ -107,6 +107,82 @@
 }
 
 /* --------------------------------------------------------
+ * Request class
+ * -------------------------------------------------------- */
+
+Request::Request(const std::string &command)
+	: m_pos(0)
+	, m_params(0)
+	, m_command(command)
+{
+	int i = -1;
+
+	while ((i = command.find('#', i + 1)) != std::string::npos)
+		++ m_params;
+}
+
+Request::~Request()
+{
+}
+
+void Request::assertCorrect()
+{
+	if (m_params <= 0)
+		throw std::runtime_error("no more arguments to set");
+}
+
+void Request::setValue(const std::string &value)
+{
+	assertCorrect();
+
+	m_pos = m_command.find('#', m_pos);
+	m_command.replace(m_pos, 1, value);
+	m_pos += value.length();
+	m_params --;
+}
+
+template <>
+void Request::bind(bool value)
+{
+	setValue(bindBoolean(value));
+}
+
+template <>
+void Request::bind(Date value)
+{
+	setValue(bindDate(value));
+}
+
+template <>
+void Request::bind(double value)
+{
+	setValue(bindDouble(value));
+}
+
+template <>
+void Request::bind(int value)
+{
+	setValue(bindInt(value));
+}
+
+template <>
+void Request::bind(std::string value)
+{
+	setValue(bindString(value));
+}
+
+template <>
+void Request::bind(const char *value)
+{
+	setValue(bindString(value));
+}
+
+Request::operator std::string()
+{
+	return m_command;
+}
+
+/* --------------------------------------------------------
  * Driver class
  * -------------------------------------------------------- */
 
@@ -122,3 +198,8 @@
 {
 	return m_error;
 }
+
+Query::Ptr Driver::query(Request::Ptr request)
+{
+	return query(static_cast<std::string>(*request));
+}
--- a/C++/Driver.h	Tue Oct 29 21:33:00 2013 +0100
+++ b/C++/Driver.h	Mon Nov 11 18:05:47 2013 +0100
@@ -20,9 +20,9 @@
 #define _DRIVER_H_
 
 #include <iostream>
-#include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "Date.h"
 
@@ -71,7 +71,6 @@
 	};
 
 private:
-
 	/**
 	 * Check if the request is valid and throws an exception
 	 * on error.
@@ -191,6 +190,97 @@
 };
 
 /**
+ * @class Request
+ * @brief A secure helper for creating requests
+ *
+ * This helps creating class with SQL injection security and such.
+ */
+class Request {
+public:
+	friend class Driver;
+
+	using Ptr	= std::shared_ptr<Request>;
+
+private:
+	size_t		m_pos;
+	int		m_params;
+	std::string	m_command;
+
+	void assertCorrect();
+
+	void setValue(const std::string &value);
+
+protected:
+	/**
+	 * Bind a boolean.
+	 *
+	 * @param value the boolean
+	 * @return the string to use
+	 */
+	virtual std::string bindBoolean(bool value) = 0;
+
+	/**
+	 * Bind a date.
+	 *
+	 * @param value the date
+	 * @return the string to use
+	 */
+	virtual std::string bindDate(Date value) = 0;
+
+	/**
+	 * Bind a double.
+	 *
+	 * @param value the double
+	 * @return the string to use
+	 */
+	virtual std::string bindDouble(double value) = 0;
+
+	/**
+	 * Bind an integer.
+	 *
+	 * @param value the integer
+	 * @return the string to use
+	 */
+	virtual std::string bindInt(int value) = 0;
+
+	/**
+	 * Bind a string.
+	 *
+	 * @param value the string
+	 * @return the string to use
+	 */
+	virtual std::string bindString(std::string value) = 0;
+
+	/**
+	 * Default constructor with SQL command.
+	 *
+	 * @param command the SQL command
+	 */
+	Request(const std::string &command);
+
+public:
+	/**
+	 * Default destructor.
+	 */
+	virtual ~Request();
+
+	/**
+	 * Bind a value.
+	 *
+	 * @param value the value
+	 */
+	template <typename T>
+	void bind(T value);
+
+	/**
+	 * Convert the request to string.
+	 *
+	 * @return the request as a string
+	 */
+	operator std::string();
+};
+
+/**
  * @class Driver
  * @brief A generic SQL driver
  *
@@ -210,8 +300,8 @@
 	Driver();
 
 public:
-	typedef std::map<std::string, std::string> Params;
-	typedef std::shared_ptr<Driver> Ptr;
+	using Params	= std::unordered_map<std::string, std::string>;
+	using Ptr	= std::shared_ptr<Driver>;
 
 	/**
 	 * Create a new Driver object, some queries need to do additional
@@ -240,6 +330,15 @@
 	const std::string &getError() const;
 
 	/**
+	 * Wrapper for std::string variant.
+	 *
+	 * @param request the request to use
+	 * @return a result
+	 * @throw Query::Exception on failure
+	 */
+	Query::Ptr query(Request::Ptr request);
+
+	/**
 	 * Create a synchronous connection, it waits and block until
 	 * the connection is made up to a specified timeout max.
 	 *
@@ -248,6 +347,15 @@
 	virtual bool connect(const Params &params) = 0;
 
 	/**
+	 * Prepare a request with the specified SQL command.
+	 *
+	 * @param command the SQL command to parse and bind
+	 * @return a request to use with query
+	 * @see query
+	 */
+	virtual Request::Ptr prepare(const std::string &command) = 0;
+
+	/**
 	 * Execute a query.
 	 *
 	 * @param query the SQL command
--- a/C++/DriverPostgres.cpp	Tue Oct 29 21:33:00 2013 +0100
+++ b/C++/DriverPostgres.cpp	Mon Nov 11 18:05:47 2013 +0100
@@ -37,8 +37,7 @@
 	int idx = PQfnumber(m_result.get(), column.c_str());
 	long timestamp = static_cast<long>(time(0));
 
-	try
-	{
+	try {
 		std::ostringstream oss;
 		std::string value, req;
 
@@ -55,8 +54,7 @@
 
 		Query::Ptr result = m_driver->query(req);
 		timestamp = result->get<double>(0, "RESULT");
-	}
-	catch (...) { }
+	} catch (...) { }
 
 	return Date(static_cast<time_t>(timestamp));
 }
@@ -173,6 +171,58 @@
 }
 
 /* --------------------------------------------------------
+ * Request PostgreSQL
+ * -------------------------------------------------------- */
+
+RequestPostgres::RequestPostgres(const std::string &command)
+	: Request(command)
+{
+}
+
+std::string RequestPostgres::bindBoolean(bool value)
+{
+	return (value) ? "'t'" : "'f'";
+}
+
+std::string RequestPostgres::bindDate(Date value)
+{
+	std::ostringstream oss;
+
+	oss << "to_timestamp(";
+	oss << value.getTimestamp();
+	oss << ")";
+
+	return oss.str();
+}
+
+std::string RequestPostgres::bindDouble(double value)
+{
+	return std::to_string(value);
+}
+
+std::string RequestPostgres::bindInt(int value)
+{
+	return std::to_string(value);
+}
+
+std::string RequestPostgres::bindString(std::string value)
+{
+	std::ostringstream oss;
+
+	oss << "'";
+	for (auto c : value) {
+		if (c == '\'')
+			oss << "\\'";
+		else
+			oss << c;
+	}
+
+	oss << "'";
+
+	return oss.str();
+}
+
+/* --------------------------------------------------------
  * Driver PostgreSQL
  * -------------------------------------------------------- */
 
@@ -221,6 +271,11 @@
 	return true;
 }
 
+Request::Ptr DriverPostgres::prepare(const std::string &command)
+{
+	return std::make_shared<RequestPostgres>(command);
+}
+
 Query::Ptr DriverPostgres::query(const std::string &cmd)
 {
 	PGresult *info;
--- a/C++/DriverPostgres.h	Tue Oct 29 21:33:00 2013 +0100
+++ b/C++/DriverPostgres.h	Mon Nov 11 18:05:47 2013 +0100
@@ -24,9 +24,6 @@
 #define _DRIVER_PG_H_
 
 #include <cerrno>
-#include <map>
-#include <memory>
-#include <string>
 
 #include <libpq-fe.h>
 
@@ -36,8 +33,7 @@
  * @class QueryPostgres
  * @brief Query implementation for PostgreSQL.
  */
-class QueryPostgres : public Query
-{
+class QueryPostgres : public Query {
 public:
 	struct PQQueryDeleter
 	{
@@ -117,16 +113,52 @@
 };
 
 /**
+ * @class RequestPostgres
+ * @brief Request implementation for PostgreSQL.
+ */
+class RequestPostgres : public Request {
+protected:
+	/**
+	 * @copydoc Request::bindBoolean
+	 */
+	virtual std::string bindBoolean(bool value);
+
+	/**
+	 * @copydoc Request::bindDate
+	 */
+	virtual std::string bindDate(Date value);
+
+	/**
+	 * @copydoc Request::bindDouble
+	 */
+	virtual std::string bindDouble(double value);
+
+	/**
+	 * @copydoc Request::bindInt
+	 */
+	virtual std::string bindInt(int value);
+
+	/**
+	 * @copydoc Request::bindString
+	 */
+	virtual std::string bindString(std::string value);
+
+public:
+	/**
+	 * @copydoc Request::Request
+	 */
+	RequestPostgres(const std::string &command);
+};
+
+/**
  * @class DriverPostgres
  * @brief Driver implementation for PostgreSQL.
  */
-class DriverPostgres : public Driver
-{
+class DriverPostgres : public Driver {
 public:
 	friend class Driver;
 
-	struct PGDeleter
-	{
+	struct PGDeleter {
 		void operator()(PGconn *conn)
 		{
 			PQfinish(conn);
@@ -165,6 +197,11 @@
 	virtual bool connect(const Params &params);
 
 	/**
+	 * @copydoc Driver::prepare
+	 */
+	virtual Request::Ptr prepare(const std::string &command);
+
+	/**
 	 * @copydoc Driver::query
 	 */
 	virtual Query::Ptr query(const std::string &command);