List:MySQL++« Previous MessageNext Message »
From:Waba Date:February 4 2006 9:16am
Subject:Re: Patch: Nullable fields in SQLSS
View as plain text  
On Sat, Feb 04, 2006 at 03:40:29AM -0500, Chris Frey wrote:
> Side note: looks like svn didn't pick up your new custom7.cpp in its diff
> output.  It's missing in the patch, but I see traces of it in Makefile.am :-)
Ooops! I forgot to svn-add it, sorry. Here is an updated version of the
patch, that includes custom7.cpp.

-Waba.

Index: lib/datetime.h
===================================================================
--- lib/datetime.h	(revision 1163)
+++ lib/datetime.h	(working copy)
@@ -31,7 +31,6 @@
 
 #include "defs.h"
 
-#include "coldata.h"
 #include "stream2string.h"
 #include "tiny_int.h"
 
@@ -164,19 +163,12 @@
 	/// \brief Initialize object from a MySQL date-and-time string
 	///
 	/// \sa DateTime(cchar*)
-	DateTime(const ColData& str)
+	template<class Str>
+	DateTime(const Str &str)
 	{
 		convert(str.c_str());
 	}
 
-	/// \brief Initialize object from a MySQL date-and-time string
-	///
-	/// \sa DateTime(cchar*)
-	DateTime(const std::string& str)
-	{
-		convert(str.c_str());
-	}
-
 	/// \brief Compare this datetime to another.
 	///
 	/// Returns < 0 if this datetime is before the other, 0 of they are
@@ -259,16 +251,9 @@
 	/// \brief Initialize object from a MySQL date string
 	///
 	/// \sa Date(cchar*)
-	Date(const ColData& str) { convert(str.c_str()); }
-
-	/// \brief Initialize object from a MySQL date string
-	///
-	/// \sa Date(cchar*)
-	Date(const std::string& str)
-	{
-		convert(str.c_str());
-	}
-
+	template<class Str>
+	Date(const Str& str) { convert(str.c_str()); }
+	
 	/// \brief Compare this date to another.
 	///
 	/// Returns < 0 if this date is before the other, 0 of they are
@@ -342,16 +327,9 @@
 	/// \brief Initialize object from a MySQL time string
 	///
 	/// \sa Time(cchar*)
-	Time(const ColData& str) { convert(str.c_str()); }
+	template<class Str>
+	Time(const Str& str) { convert(str.c_str()); }
 
-	/// \brief Initialize object from a MySQL time string
-	///
-	/// \sa Time(cchar*)
-	Time(const std::string& str)
-	{
-		convert(str.c_str());
-	}
-
 	/// \brief Parse a MySQL time string into this object.
 	MYSQLPP_EXPORT cchar* convert(cchar*);
 
Index: lib/coldata.h
===================================================================
--- lib/coldata.h	(revision 1163)
+++ lib/coldata.h	(working copy)
@@ -149,6 +149,9 @@
 	/// \brief Template for converting data from one type to another.
 	template <class Type> Type conv(Type dummy) const;
 
+	template <class T, class B> Null<T, B> conv (Null<T, B>) const
+			{ return get_null<T, B> (); }
+	
 	/// \brief Set a flag indicating that this object is a SQL null.
 	void it_is_null() { null_ = true; }
 
@@ -161,6 +164,19 @@
 		return buf_;
 	}
 	
+	/** \brief Convert to a nullable type.
+	 *
+	 * You have to explicitely provide the template types to the function,
+	 * like this:
+	 * <code>
+	 * Null<T, B> n (col_data.get_null<T, B> ());
+	 * </code>
+	 *
+	 * Or use conv(), to which you can give a sample of the desired
+	 * return type.
+	 */
+	template <class T, class B> Null<T, B> get_null () const;
+
 	/// \brief Returns a const char pointer to the string form of
 	/// this object's data.
 	operator cchar*() const { return buf_.c_str(); }
@@ -222,8 +238,19 @@
 	/// \brief Converts this object's string data to a bool
 	operator bool() const { return conv(0); }
 
-	template <class T, class B> operator Null<T, B>() const;
-
+	/** \brief Dummy conversion to Null.
+	 *
+	 * This is meant to be ambiguous to the compiler. Without this trick,
+	 * Null would be constructible from the other implicit conversion 
+	 * operators, and would in the end be counter-intuitive to the user
+	 * who would lose the is-null information. This way, constructing a 
+	 * Null from a ColData implicitely is impossible.
+	 *
+	 * Use get_null() or conv() explicitely instead.
+	 */
+	template<class T, class B> operator Null<T, B> () const 
+			{ T (1)/1; return get_null<T, B> (); }
+	
 private:
 	mysql_type_info type_;
 	std::string buf_;
@@ -295,13 +322,13 @@
 /// the object is exactly equal to "NULL".  Else, it constructs an empty
 /// object of type T and tries to convert it to Null<T, B>.
 template <class Str> template<class T, class B>
-ColData_Tmpl<Str>::operator Null<T, B>() const
+Null<T, B> ColData_Tmpl<Str>::get_null () const
 {
 	if ((Str::size() == 4) &&
-			(*this)[0] == 'N' &&
-			(*this)[1] == 'U' &&
-			(*this)[2] == 'L' &&
-			(*this)[3] == 'L') {
+		static_cast<Str>(*this)[0] == 'N' &&
+		static_cast<Str>(*this)[1] == 'U' &&
+		static_cast<Str>(*this)[2] == 'L' &&
+		static_cast<Str>(*this)[3] == 'L') {
 		return Null<T, B>(null);
 	}
 	else {
Index: lib/null.h
===================================================================
--- lib/null.h	(revision 1163)
+++ lib/null.h	(working copy)
@@ -35,6 +35,7 @@
 #include "exceptions.h"
 
 #include <iostream>
+#include <string>
 
 namespace mysqlpp {
 
@@ -273,6 +274,9 @@
 		return o << n.data;
 }
 
+/// \brief The correct Null template types for nullable STL strings
+typedef Null<std::string, NullisBlank> NullString;
+
 } // end namespace mysqlpp
 
 #endif
Index: lib/datetime.cpp
===================================================================
--- lib/datetime.cpp	(revision 1163)
+++ lib/datetime.cpp	(working copy)
@@ -40,9 +40,9 @@
 {
 	char fill = os.fill('0');
 	ios::fmtflags flags = os.setf(ios::right);
-	os		<< setw(4) << d.year << '-' 
-			<< setw(2) << d.month << '-'
-			<< setw(2) << d.day;
+	os		<< setw(4) << static_cast<int>(d.year) << '-'
+			<< setw(2) << static_cast<int>(d.month) << '-'
+			<< setw(2) << static_cast<int>(d.day);
 	os.flags(flags);
 	os.fill(fill);
 	return os;
@@ -53,9 +53,9 @@
 {
 	char fill = os.fill('0');
 	ios::fmtflags flags = os.setf(ios::right);
-	os		<< setw(2) << t.hour << ':' 
-			<< setw(2) << t.minute << ':'
-			<< setw(2) << t.second;
+	os		<< setw(2) << static_cast<int>(t.hour) << ':' 
+			<< setw(2) << static_cast<int>(t.minute) << ':'
+			<< setw(2) << static_cast<int>(t.second);
 	os.flags(flags);
 	os.fill(fill);
 	return os;
Index: lib/custom.pl
===================================================================
--- lib/custom.pl	(revision 1163)
+++ lib/custom.pl	(working copy)
@@ -262,7 +262,7 @@
 	$parm_simple2c_b .= ", " unless $j == $i;
 	$defs  .= "    T$j I$j;";
 	$defs  .= "\n" unless $j == $i;
-	$popul .= "    s->I$j = static_cast<T$j>(row.at(O$j));";
+	$popul .= "    s->I$j = row.at(O$j).conv (T$j ());";
 	$popul .= "\n" unless $j == $i;
         $names .= "    N$j ";
 	$names .= ",\n" unless $j == $i;
Index: lib/manip.h
===================================================================
--- lib/manip.h	(revision 1163)
+++ lib/manip.h	(working copy)
@@ -201,6 +201,18 @@
 	return *o.ostr << '\'' << in << '\'';
 }
 
+
+template <class T, class B>
+MYSQLPP_EXPORT inline std::ostream& operator <<(quote_type1 o,
+		const Null<T, B>& in)
+{
+	if (in.is_null)
+		return *o.ostr << "NULL";
+	else
+		return o << in.data; // quote again
+}
+
+
 #endif // !defined(DOXYGEN_IGNORE)
 
 
@@ -313,6 +325,18 @@
 	return *o.ostr << '\'' << in << '\'';
 }
 
+
+template <class T, class B>
+MYSQLPP_EXPORT inline std::ostream& operator <<(quote_only_type1 o,
+		const Null<T, B>& in)
+{
+	if (in.is_null)
+		return *o.ostr << "NULL";
+	else
+		return o << in.data; // quote again
+}
+
+
 #endif // !defined(DOXYGEN_IGNORE)
 
 
@@ -427,6 +451,18 @@
 	return *o.ostr << '"' << in << '"';
 }
 
+
+template <class T, class B>
+MYSQLPP_EXPORT inline std::ostream& operator <<(quote_double_only_type1 o,
+		const Null<T, B>& in)
+{
+	if (in.is_null)
+		return *o.ostr << "NULL";
+	else
+		return o << in.data; // quote again
+}
+
+
 #endif // !defined(DOXYGEN_IGNORE)
 
 
Index: lib/convert.h
===================================================================
--- lib/convert.h	(revision 1163)
+++ lib/convert.h	(working copy)
@@ -34,8 +34,10 @@
 #include "platform.h"
 
 #include "defs.h"
+#include "datetime.h"
 
 #include <stdlib.h>
+#include <string>
 
 namespace mysqlpp {
 
@@ -113,6 +115,63 @@
 
 #endif // !defined(DOXYGEN_IGNORE)
 
+template <>
+class mysql_convert<std::string>
+{
+	std::string m_str;
+	
+public:
+	mysql_convert (const char* str, const char *& end) :
+	m_str (str)
+	{
+		end = str + m_str.size ();
+	}
+	
+	operator std::string () { return m_str; }
+};
+
+template <>
+class mysql_convert<Date>
+{
+	Date m_data;
+	
+public:
+	mysql_convert (const char* str, const char *& end)
+	{
+		end = m_data.convert (str);
+	}
+	
+	operator Date () { return m_data; }
+};
+
+template <>
+class mysql_convert<DateTime>
+{
+	DateTime m_data;
+	
+public:
+	mysql_convert (const char* str, const char *& end)
+	{
+		end = m_data.convert (str);
+	}
+	
+	operator DateTime () { return m_data; }
+};
+
+template <>
+class mysql_convert<Time>
+{
+	Time m_data;
+	
+public:
+	mysql_convert (const char* str, const char *& end)
+	{
+		end = m_data.convert (str);
+	}
+	
+	operator Time () { return m_data; }
+};
+
 } // end namespace mysqlpp
 
 #endif
Index: examples/custom7.cpp
===================================================================
--- examples/custom7.cpp	(revision 0)
+++ examples/custom7.cpp	(revision 0)
@@ -0,0 +1,155 @@
+/***********************************************************************
+ custom7.cpp - This example demonstrates the use of the Null types
+               inside SSQLS structures. You should get a grasp of the
+               involved concepts through the previous examples first.
+ 
+ Copyright (c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by
+ MySQL AB, and (c) 2004, 2005 by Educational Technology Resources, Inc.
+ Others may also hold copyrights on code in this file.  See the CREDITS
+ file in the top directory of the distribution for details.
+
+ This file is part of MySQL++.
+
+ MySQL++ is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ MySQL++ is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with MySQL++; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ USA
+***********************************************************************/
+
+#include "util.h"
+
+#include <mysql++.h>
+#include <custom.h>
+
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+#include <vector>
+
+using namespace std;
+
+// This is the same kind of structures than in the previous custom examples,
+// however this one deals with null strings, int and dates.
+sql_create_4(stock2,
+			 1, 4,
+			 int, id,
+			 mysqlpp::NullString, name,
+			 mysqlpp::Null<int>, qtty,
+			 mysqlpp::Null<mysqlpp::Date>, eta)
+
+// Simple exception class for reporting testing errors
+struct TestError : mysqlpp::Exception
+{ TestError (const std::string &w) : mysqlpp::Exception (w) {} };
+
+int
+main(int argc, char *argv[])
+{
+	// Wrap all MySQL++ interactions in one big try block, so any
+	// errors are handled gracefully.
+	try {						
+		// Establish the connection to the database server.
+		mysqlpp::Connection con(mysqlpp::use_exceptions);
+		if (!connect_to_db(argc, argv, con)) {
+			return 1;
+		}
+		mysqlpp::Query q = con.query ();
+
+		// Let's start by inserting some rows with null items in our
+		// stock2 table.
+		cout << "Populating stock2..." << endl;
+		q << "delete from stock2";
+		q.execute ();
+		// Notice that we have to give a string to the stock2 ctor
+		// here, not a char* as C++ does not allow for more than
+		// one explicit construction (char* -> string -> Null<string>)
+		q.insert (stock2 (0, string ("Pickle Relish"), 5, 
+		                  mysqlpp::null));
+		q.execute ();
+		q.insert (stock2 (1, mysqlpp::null, mysqlpp::null, 
+		                  mysqlpp::Date (2006, 2, 3)));
+		q.execute ();
+		q.insert (stock2 (2, mysqlpp::null, mysqlpp::null, 
+		                  mysqlpp::null));
+		q.execute ();
+
+		// Display its contents to the user, in a manner independant of
+		// the new code (null-in-SSQLS)
+		q << "select * from stock2";
+		mysqlpp::Result res = q.store ();
+		cout << "stock2 now contains:" << endl;
+		cout << " ID | Name          | Qtty | ETA        " << endl;
+		cout << "----+---------------+------+------------" << endl;
+		for (size_t i = 0; i < res.size (); i++)
+		{
+			mysqlpp::Row row = res.at (i);
+			cout << setw(3)  << row["id"]   << " | "
+			     << setw(13) << row["name"] << " | "
+			     << setw(4)  << row["qtty"] << " | "
+			     << row["eta"] << endl;
+		}
+		cout << endl;
+
+		// Now read back the values in SSQLS mode and compare them.
+		cout << "Reading again the values in SSQLS mode:" << endl;
+		vector<stock2> res2;
+		q << "select * from stock2";
+		q.storein (res2);
+		if (res.size () != res2.size ())
+			throw TestError ("The size of the results differ!");
+		for (size_t i = 0; i < res.size (); i++)
+		{
+			mysqlpp::Row row = res.at (i);
+			stock2 st = res2.at (i);
+			if (row["name"].is_null () != st.name.is_null
+			 || row["qtty"].is_null () != st.qtty.is_null
+			 || row["eta"].is_null ()  != st.eta.is_null)
+				throw TestError ("At least one null status differs!");
+
+			stringstream ss1, ss2;
+			mysqlpp::Null<int> sample_null_int;
+			mysqlpp::Null<mysqlpp::Date> sample_null_date;
+			// In order to get the same quoting mode, we have to 
+			// convert the ColData to Nulls. We'll use the conv()
+			// member function for that. It takes a sample of the
+			// type we request.
+			ss1 << row["name"].conv (mysqlpp::NullString ()) << "," 
+			    << row["qtty"].conv (sample_null_int) << "," 
+			    << row["eta"].conv (sample_null_date);
+			ss2 << st.name     << "," << st.qtty     << "," <<
st.eta;
+			if (ss1.str () != ss2.str ())
+				throw TestError ("At least one row differs:\n"+
+				                 ss1.str ()+"\n"+ss2.str ()+"\n");
+		}
+
+		cout << "They are exactly identical!" << endl;
+	}
+	catch (const mysqlpp::BadQuery& er) {
+		// Handle any query errors
+		cerr << "Query error: " << er.what() << endl;
+		return -1;
+	}
+	catch (const mysqlpp::BadConversion& er) {
+		// Handle bad conversions; e.g. type mismatch populating 'stock'
+		cerr << "Conversion error: " << er.what() << endl <<
+				"\tretrieved data size: " << er.retrieved <<
+				", actual size: " << er.actual_size << endl;
+		return -1;
+	}
+	catch (const mysqlpp::Exception& er) {
+		// Catch-all for any other MySQL++ exceptions
+		cerr << "Error: " << er.what() << endl;
+		return -1;
+	}
+
+	return 0;
+}

Property changes on: examples/custom7.cpp
___________________________________________________________________
Name: svn:keywords
   + Id LastChangedRevision

Index: examples/Makefile.am
===================================================================
--- examples/Makefile.am	(revision 1163)
+++ examples/Makefile.am	(working copy)
@@ -27,7 +27,7 @@
 
 noinst_PROGRAMS = resetdb simple1 simple2 simple3 usequery multiquery \
 		custom1 custom2 custom3 custom4 custom5 custom6 fieldinf1 \
-		dbinfo updel load_file cgi_image
+		dbinfo updel load_file cgi_image custom7
 
 noinst_HEADERS = util.h
 
@@ -70,6 +70,9 @@
 custom6_SOURCES = custom6.cpp util.cpp
 custom6_DEPENDENCIES = $(MYSQLPP_LIB)
 
+custom7_SOURCES = custom7.cpp util.cpp
+custom7_DEPENDENCIES = $(MYSQLPP_LIB)
+
 fieldinf1_SOURCES = fieldinf1.cpp util.cpp
 fieldinf1_DEPENDENCIES = $(MYSQLPP_LIB)
 
Index: examples/resetdb.cpp
===================================================================
--- examples/resetdb.cpp	(revision 1163)
+++ examples/resetdb.cpp	(working copy)
@@ -60,6 +60,7 @@
 			// really care, as it'll get created next.
 			cout << "Dropping existing stock table..." << endl;
 			query.execute("drop table stock");
+			query.execute("drop table stock2");
 		}
 		else {
 			// Database doesn't exist yet, so create and select it.
@@ -105,6 +106,12 @@
 
 		cout << (new_db ? "Created" : "Reinitialized") <<
 				" sample database successfully." << endl;
+	
+		// The stock2 table introduces a null string, needed to test
+		// custom7.
+		cout << "Creating new stock2 table..." << endl;
+		query.execute ("create table stock2 (id int primary key, "
+		               " name varchar(20), qtty int, eta date)");
 	}
 	catch (const mysqlpp::BadQuery& er) {
 		// Handle any query errors

Attachment: [application/pgp-signature] Digital signature signature.asc
Thread
Patch: Nullable fields in SQLSSWaba4 Feb
  • Re: Patch: Nullable fields in SQLSSChris Frey4 Feb
    • Re: Patch: Nullable fields in SQLSSWaba4 Feb
  • Re: Patch: Nullable fields in SQLSSWarren Young6 Feb
  • Re: Patch: Nullable fields in SQLSSWarren Young5 Nov
Re: Patch: Nullable fields in SQLSSddneilson7 Nov
  • Re: Patch: Nullable fields in SQLSSWarren Young7 Nov
Re: Patch: Nullable fields in SQLSSDavid A. Betz14 Nov
  • Re: Patch: Nullable fields in SQLSSWarren Young14 Nov
Re: Patch: Nullable fields in SQLSSDavid A. Betz17 Nov
  • Re: Patch: Nullable fields in SQLSSWarren Young18 Nov