view C++/Driver.h @ 296:5806c767aec7

Zip: fix resize if read less than requested Task: #312
author David Demelier <markand@malikania.fr>
date Thu, 13 Nov 2014 21:10:13 +0100
parents 1aceace80f61
children
line wrap: on
line source

/*
 * Driver.h -- generic SQL driver access
 *
 * 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
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _DRIVER_H_
#define _DRIVER_H_

#include <ctime>
#include <memory>
#include <string>
#include <unordered_map>
#include <type_traits>

/**
 * @enum DriverColumn
 * @brief The column type request
 *
 * Used for the drivers.
 */
enum class DriverColumn {
	Invalid,			//! not found
	Boolean,			//! bool or 0 / 1
	Date,				//! date see Common/Date.h
	Double,				//! double
	Integer,			//! 32 or 64 bit int
	String,				//! varchar to std::string
};

template <typename T>
struct DriverTypeInfo : std::false_type { };

/**
 * @class Query
 * @brief Class for querying the database
 *
 * That class is returned when a SQL query succeed. It can retrieve the
 * number of rows, columns and retrieve the results independantly from the
 * driver.
 *
 * @see Driver::query
 */
class DriverQuery {
public:
	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;

		/**
		 * 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;

		/**
		 * Tells how many number of columns are present for each
		 * row.
		 *
		 * @return the number of columns
		 */
		virtual int countColumns() = 0;

		/**
		 * 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
	 * on error.
	 *
	 * @param row the row number
	 * @param column the column name
	 * @param type
	 * @throw Error on error
	 */
	void assertRequest(int row, const std::string &column, DriverColumn type);

protected:
	Ptr		m_impl;

public:
	DriverQuery(Ptr impl);

	/**
	 * Default destructor.
	 */
	virtual ~DriverQuery();

	/**
	 * Get a variable from a row and column.
	 *
	 * Specialization available:
	 *	- bool
	 *	- Date
	 *	- double
	 *	- int
	 *	- std::string
	 *
	 * @param row the row number (starts from 0)
	 * @param column the the column name
	 * @return the value
	 * @throw Query::Error on error
	 */
	template <class T>
	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.
	 *
	 * @param column the column name
	 * @return the type
	 */
	DriverColumn type(const std::string &column) const;

	/**
	 * Tells how many rows has been fetched.
	 *
	 * @return the number of rows
	 */
	int countRows();

	/**
	 * Tells how many number of columns are present for each
	 * row.
	 *
	 * @return the number of columns
	 */
	int countColumns();

	/**
	 * Tells if the column is null or not.
	 *
	 * @param row the row number
	 * @param column the column
	 * @return an true if null
	 */
	bool isNull(int row, const std::string &column);

	/**
	 * Dump all rows and columns.
	 */
	void dump();
};

/**
 * @class Request
 * @brief A secure helper for creating requests
 *
 * This helps creating class with SQL injection security and such.
 */
class DriverRequest {
public:
	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;

		/**
		 * 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);

public:
	DriverRequest(Ptr impl, const std::string &command);

	/**
	 * Default destructor.
	 */
	virtual ~DriverRequest();

	/**
	 * Bind a value.
	 *
	 * @param value the value
	 */
	template <typename T>
	void bind(T value)
	{
		static_assert(DriverTypeInfo<T>::value, "unsupported type");

		setValue(DriverTypeInfo<T>::bind(value));
	}

	/**
	 * Convert the request to string.
	 *
	 * @return the request as a string
	 */
	operator std::string();
};

/**
 * @class Driver
 * @brief A generic SQL driver
 *
 * This class is used to connect to a database and execute SQL queries. It
 * does not include any DBMS code and just call virtual functions for
 * a simpler integration of new DBMS drivers.
 */
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;

		/**
		 * 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:
	Driver() = default;

	/**
	 * Virtual destructor.
	 */
	virtual ~Driver() = default;

	/**
	 * Wrapper for std::string variant.
	 *
	 * @param request the request to use
	 * @return a result
	 * @throw Query::Error on failure
	 */
	DriverQuery query(DriverRequest request);

	/**
	 * 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.
	 */
	void connect(const Params &params);

	/**
	 * 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
	 */
	DriverRequest prepare(const std::string &command);

	/**
	 * Execute a query.
	 *
	 * @param query the SQL command
	 * @return a result
	 * @throw Query::Error on failure
	 */
	DriverQuery query(const std::string &command);

	/**
	 * Get the driver connection description.
	 *
	 * @return the description
	 */
	std::string description() const;

	/**
	 * Get the driver version as a string.
	 *
	 * @return the version
	 */
	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_