diff C++/Driver.h @ 214:6c49e5e3ecc8

Driver: now data is shared internally
author David Demelier <markand@malikania.fr>
date Tue, 25 Mar 2014 20:42:11 +0100
parents d263f85f43a4
children 1aceace80f61
line wrap: on
line diff
--- a/C++/Driver.h	Mon Mar 24 13:54:28 2014 +0100
+++ b/C++/Driver.h	Tue Mar 25 20:42:11 2014 +0100
@@ -1,7 +1,7 @@
 /*
  * Driver.h -- generic SQL driver access
  *
- * Copyright (c) 2013, David Demelier <markand@malikania.fr>
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,20 +19,19 @@
 #ifndef _DRIVER_H_
 #define _DRIVER_H_
 
-#include <iostream>
+#include <ctime>
 #include <memory>
 #include <string>
 #include <unordered_map>
-
-#include "Date.h"
+#include <type_traits>
 
 /**
- * @enum ColumnType
+ * @enum DriverColumn
  * @brief The column type request
  *
  * Used for the drivers.
  */
-enum class ColumnType {
+enum class DriverColumn {
 	Invalid,			//! not found
 	Boolean,			//! bool or 0 / 1
 	Date,				//! date see Common/Date.h
@@ -41,6 +40,9 @@
 	String,				//! varchar to std::string
 };
 
+template <typename T>
+struct DriverTypeInfo : std::false_type { };
+
 /**
  * @class Query
  * @brief Class for querying the database
@@ -51,24 +53,101 @@
  *
  * @see Driver::query
  */
-class Query {
+class DriverQuery {
 public:
-	using Ptr = std::shared_ptr<Query>;
+	friend struct DriverTypeInfo<bool>;
+	friend struct DriverTypeInfo<time_t>;
+	friend struct DriverTypeInfo<double>;
+	friend struct DriverTypeInfo<int>;
+	friend struct DriverTypeInfo<std::string>;
+
+	class Impl {
+	public:
+		/**
+		 * Get a bool.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return the value
+		 */
+		virtual bool getBoolean(int row, const std::string &column) = 0;
+
+		/**
+		 * Get a Date.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return the value
+		 */
+		virtual time_t getDate(int row, const std::string &column) = 0;
+
+		/**
+		 * Get a double.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return the value
+		 */
+		virtual double getDouble(int row, const std::string &column) = 0;
+
+		/**
+		 * Get a integer.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return the value
+		 */
+		virtual int getInt(int row, const std::string &column) = 0;
 
-	/**
-	 * @class Error
-	 * @brief Query exception on query error
-	 */
-	class Error : public std::exception {
-	private:
-		std::string m_error;
+		/**
+		 * Get a string.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return the value
+		 */
+		virtual std::string getString(int row, const std::string &column) = 0;
+
+		/**
+		 * Returns the type of a named column.
+		 *
+		 * @param column the column name
+		 * @return the type
+		 */
+		virtual DriverColumn type(const std::string &column) const = 0;
+
+		/**
+		 * Tells how many rows has been fetched.
+		 *
+		 * @return the number of rows
+		 */
+		virtual int countRows() = 0;
 
-	public:
-		Error(const std::string &error);
+		/**
+		 * Tells how many number of columns are present for each
+		 * row.
+		 *
+		 * @return the number of columns
+		 */
+		virtual int countColumns() = 0;
 
-		virtual const char *what() const throw();
+		/**
+		 * Tells if the column is null or not.
+		 *
+		 * @param row the row number
+		 * @param column the column
+		 * @return an true if null
+		 */
+		virtual bool isNull(int row, const std::string &column) = 0;
+
+		/**
+		 * Dump all rows and columns.
+		 */
+		virtual void dump() = 0;
 	};
 
+	using Ptr	= std::shared_ptr<Impl>;
+
 private:
 	/**
 	 * Check if the request is valid and throws an exception
@@ -79,59 +158,18 @@
 	 * @param type
 	 * @throw Error on error
 	 */
-	void assertRequest(int row, const std::string &column, ColumnType type);
+	void assertRequest(int row, const std::string &column, DriverColumn type);
 
 protected:
-	/**
-	 * Get a bool.
-	 *
-	 * @param row the row number
-	 * @param column the column
-	 * @return the value
-	 */
-	virtual bool getBoolean(int row, const std::string &column) = 0;
-
-	/**
-	 * Get a Date.
-	 *
-	 * @param row the row number
-	 * @param column the column
-	 * @return the value
-	 */
-	virtual Date getDate(int row, const std::string &column) = 0;
-
-	/**
-	 * Get a double.
-	 *
-	 * @param row the row number
-	 * @param column the column
-	 * @return the value
-	 */
-	virtual double getDouble(int row, const std::string &column) = 0;
-
-	/**
-	 * Get a integer.
-	 *
-	 * @param row the row number
-	 * @param column the column
-	 * @return the value
-	 */
-	virtual int getInt(int row, const std::string &column) = 0;
-
-	/**
-	 * Get a string.
-	 *
-	 * @param row the row number
-	 * @param column the column
-	 * @return the value
-	 */
-	virtual std::string getString(int row, const std::string &column) = 0;
+	Ptr		m_impl;
 
 public:
+	DriverQuery(Ptr impl);
+
 	/**
 	 * Default destructor.
 	 */
-	virtual ~Query();
+	virtual ~DriverQuery();
 
 	/**
 	 * Get a variable from a row and column.
@@ -149,7 +187,14 @@
 	 * @throw Query::Error on error
 	 */
 	template <class T>
-	T get(int row, const std::string &column);
+	T get(int row, const std::string &column)
+	{
+		static_assert(DriverTypeInfo<T>::value, "unsupported type");
+
+		assertRequest(row, column, DriverTypeInfo<T>::type);
+	
+		return DriverTypeInfo<T>::get(*this, row, column);
+	}
 
 	/**
 	 * Returns the type of a named column.
@@ -157,14 +202,14 @@
 	 * @param column the column name
 	 * @return the type
 	 */
-	virtual ColumnType type(const std::string &column) const = 0;
+	DriverColumn type(const std::string &column) const;
 
 	/**
 	 * Tells how many rows has been fetched.
 	 *
 	 * @return the number of rows
 	 */
-	virtual int countRows() = 0;
+	int countRows();
 
 	/**
 	 * Tells how many number of columns are present for each
@@ -172,7 +217,7 @@
 	 *
 	 * @return the number of columns
 	 */
-	virtual int countColumns() = 0;
+	int countColumns();
 
 	/**
 	 * Tells if the column is null or not.
@@ -181,12 +226,12 @@
 	 * @param column the column
 	 * @return an true if null
 	 */
-	virtual bool isNull(int row, const std::string &column) = 0;
+	bool isNull(int row, const std::string &column);
 
 	/**
 	 * Dump all rows and columns.
 	 */
-	virtual void dump() = 0;
+	void dump();
 };
 
 /**
@@ -195,74 +240,76 @@
  *
  * This helps creating class with SQL injection security and such.
  */
-class Request {
+class DriverRequest {
 public:
-	friend class Driver;
+	friend struct DriverTypeInfo<bool>;
+	friend struct DriverTypeInfo<time_t>;
+	friend struct DriverTypeInfo<double>;
+	friend struct DriverTypeInfo<int>;
+	friend struct DriverTypeInfo<std::string>;
+
+	class Impl {
+	public:
+		/**
+		 * 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(time_t value) = 0;
 
-	using Ptr	= std::shared_ptr<Request>;
+		/**
+		 * 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 bindInteger(int value) = 0;
+
+		/**
+		 * Bind a string.
+		 *
+		 * @param value the string
+		 * @return the string to use
+		 */
+		virtual std::string bindString(std::string value) = 0;
+	};
+
+	using Ptr	= std::shared_ptr<Impl>;
 
 private:
 	size_t		m_pos;
 	int		m_params;
 	std::string	m_command;
+	Ptr		m_impl;
 
 	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;
+public:
+	DriverRequest(Ptr impl, const std::string &command);
 
 	/**
-	 * 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();
+	virtual ~DriverRequest();
 
 	/**
 	 * Bind a value.
@@ -270,7 +317,12 @@
 	 * @param value the value
 	 */
 	template <typename T>
-	void bind(T value);
+	void bind(T value)
+	{
+		static_assert(DriverTypeInfo<T>::value, "unsupported type");
+
+		setValue(DriverTypeInfo<T>::bind(value));
+	}
 
 	/**
 	 * Convert the request to string.
@@ -288,46 +340,66 @@
  * does not include any DBMS code and just call virtual functions for
  * a simpler integration of new DBMS drivers.
  */
-class Driver : public std::enable_shared_from_this<Driver> {
-protected:
-	std::string m_error;
+class Driver {
+public:
+	class Impl;
+
+	using Params	= std::unordered_map<std::string, std::string>;
+	using Ptr	= std::shared_ptr<Impl>;
+
+	class Impl {
+	public:
+		/**
+		 * Create a synchronous connection, it waits and block until
+		 * the connection is made up to a specified timeout max.
+		 *
+		 * @param params a list of parameters.
+		 */
+		virtual void connect(const Params &params) = 0;
 
-	/**
-	 * Default constructor. Should not be used.
-	 *
-	 * @see Driver::create
-	 */
-	Driver();
+		/**
+		 * 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 DriverRequest prepare(const std::string &command) = 0;
+	
+		/**
+		 * Execute a query.
+		 *
+		 * @param query the SQL command
+		 * @return a result
+		 * @throw Query::Error on failure
+		 */
+		virtual DriverQuery query(const std::string &command) = 0;
+
+		/**
+		 * Get the driver connection description.
+		 *
+		 * @return the description
+		 */
+		virtual std::string description() const = 0;
+
+		/**
+		 * Get the driver version as a string.
+		 *
+		 * @return the version
+		 */
+		virtual std::string version() const = 0;
+	};
+
+protected:
+	Ptr		m_impl;
 
 public:
-	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
-	 * internal requests so it's easier to use shared_ptr.
-	 *
-	 * @return a shared_ptr
-	 */
-	template <class T>
-	static Ptr create()
-	{
-		T *t = new T();
-
-		return std::shared_ptr<T>(t);
-	}
+	Driver() = default;
 
 	/**
 	 * Virtual destructor.
 	 */
-	virtual ~Driver();
-
-	/**
-	 * Get the error.
-	 *
-	 * @return the error
-	 */
-	const std::string &getError() const;
+	virtual ~Driver() = default;
 
 	/**
 	 * Wrapper for std::string variant.
@@ -336,7 +408,7 @@
 	 * @return a result
 	 * @throw Query::Error on failure
 	 */
-	Query::Ptr query(Request::Ptr request);
+	DriverQuery query(DriverRequest request);
 
 	/**
 	 * Create a synchronous connection, it waits and block until
@@ -344,7 +416,7 @@
 	 *
 	 * @param params a list of parameters.
 	 */
-	virtual bool connect(const Params &params) = 0;
+	void connect(const Params &params);
 
 	/**
 	 * Prepare a request with the specified SQL command.
@@ -353,7 +425,7 @@
 	 * @return a request to use with query
 	 * @see query
 	 */
-	virtual Request::Ptr prepare(const std::string &command) = 0;
+	DriverRequest prepare(const std::string &command);
 
 	/**
 	 * Execute a query.
@@ -362,21 +434,97 @@
 	 * @return a result
 	 * @throw Query::Error on failure
 	 */
-	virtual Query::Ptr query(const std::string &command) = 0;
+	DriverQuery query(const std::string &command);
 
 	/**
 	 * Get the driver connection description.
 	 *
 	 * @return the description
 	 */
-	virtual std::string description() const = 0;
+	std::string description() const;
 
 	/**
 	 * Get the driver version as a string.
 	 *
 	 * @return the version
 	 */
-	virtual std::string version() const = 0;
+	std::string version() const;
+};
+
+template <>
+struct DriverTypeInfo<bool> : std::true_type {
+	static const DriverColumn	type = DriverColumn::Boolean;
+
+	static bool get(DriverQuery &query, int row, const std::string &column)
+	{
+		return query.m_impl->getBoolean(row, column);
+	}
+
+	static void bind(DriverRequest &request, bool value)
+	{
+		request.m_impl->bindBoolean(value);
+	}
+};
+
+template <>
+struct DriverTypeInfo<time_t> : std::true_type {
+	static const DriverColumn	type = DriverColumn::Date;
+
+	static time_t get(DriverQuery &query, int row, const std::string &column)
+	{
+		return query.m_impl->getDate(row, column);
+	}
+
+	static void bind(DriverRequest &request, time_t value)
+	{
+		request.m_impl->bindDate(value);
+	}
 };
 
+template <>
+struct DriverTypeInfo<double> : std::true_type {
+	static const DriverColumn	type = DriverColumn::Double;
+
+	static double get(DriverQuery &query, int row, const std::string &column)
+	{
+		return query.m_impl->getDouble(row, column);
+	}
+
+	static void bind(DriverRequest &request, double value)
+	{
+		request.m_impl->bindDouble(value);
+	}
+};
+
+template <>
+struct DriverTypeInfo<int> : std::true_type {
+	static const DriverColumn	type = DriverColumn::Integer;
+
+	static int get(DriverQuery &query, int row, const std::string &column)
+	{
+		return query.m_impl->getInt(row, column);
+	}
+
+	static void bind(DriverRequest &request, int value)
+	{
+		request.m_impl->bindInteger(value);
+	}
+};
+
+template <>
+struct DriverTypeInfo<std::string> : std::true_type {
+	static const DriverColumn	type = DriverColumn::String;
+
+	static std::string get(DriverQuery &query, int row, const std::string &column)
+	{
+		return query.m_impl->getString(row, column);
+	}
+
+	static void bind(DriverRequest &request, const std::string &value)
+	{
+		request.m_impl->bindString(value);
+	}
+};
+
+
 #endif // !_DRIVER_H_