Click here to Skip to main content
Click here to Skip to main content
Articles » Database » Database » General » Downloads
 
Add your own
alternative version

PostgreSQL/libpqxx Class Generator

, 18 Aug 2006
Automated generation of PostgreSQL data transfer classes.
#pragma once

/***************************************************************************
 PGWizTables.h  -  This is the page in which the user will specify which
                   tables are to have classes generated, and which fields
				   make it into those classes.

 begin     : December 2004
 copyright : (C) 2004 by Phil Cairns
 email     : oti169@hotmail.com
 $Id: PGWizTables.h 2080 2006-08-18 01:53:19Z phil $

 This code may be used in compiled form in any way you desire (including
 commercial use). The code may be redistributed unmodified by any means
 providing it is not sold for profit without the authors written consent,
 and providing that this notice and the authors name and all copyright
 notices remains intact.

 This software is provided "as is" without express or implied warranty. Use
 it at your own risk!
 ***************************************************************************/

#include "GlobalData.h"
#include "TableInfo.h"

class PGWizTables
	: public CPropertyPageImpl<PGWizTables>
	, public CPWinDataExchange<PGWizTables>
{
private:
	CCheckListViewCtrl _tables;
	CCheckListViewCtrl _columns;
	std::string _currTable;
	std::string _currColumn;

public:
	enum { IDD = IDD_TABLES };

	PGWizTables(void)
	{
		m_RSIndex = FALSE;
	}

	CString m_FileName;		// name of the file for the current table
	CString m_ClassName;	// name of the class for the current table
	CString m_BaseName;		// name of the base class for the current table
	CString m_PGType;		// database type for the current column
	CString m_RSName;		// name for the current column
	CString m_RSType;		// member type for the current column
	BOOL m_RSIndex;			// whether the current column is and index column

	BEGIN_MSG_MAP(PGWizTables)
		COMMAND_HANDLER(IDC_TICKALL, BN_CLICKED, OnBnClickedTickall)
		COMMAND_HANDLER(IDC_TICKNONE, BN_CLICKED, OnBnClickedTicknone)
		NOTIFY_HANDLER(IDC_TABLES, LVN_ITEMCHANGED, OnLvnItemchangedTables)
		NOTIFY_HANDLER(IDC_TABLES, LVN_ITEMCHANGING, OnLvnItemchangingTables)
		NOTIFY_HANDLER(IDC_COLUMNS, LVN_ITEMCHANGED, OnLvnItemchangedColumns)
		NOTIFY_HANDLER(IDC_COLUMNS, LVN_ITEMCHANGING, OnLvnItemchangingColumns)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		CHAIN_MSG_MAP(CPropertyPageImpl<PGWizTables>)
	END_MSG_MAP()

	BEGIN_DDX_MAP(PGWizTables)
		DDX_TEXT(IDC_CLASSNAME, m_ClassName)
		DDX_TEXT(IDC_BASECLASS, m_BaseName)
		DDX_TEXT(IDC_FILENAME, m_FileName)
		DDX_TEXT(IDC_PGTYPE, m_PGType)
		DDX_TEXT(IDC_RSNAME, m_RSName)
		DDX_TEXT(IDC_RSTYPE, m_RSType)
		DDX_CHECK(IDC_INDEX, m_RSIndex)
	END_DDX_MAP()
	IMPLEMENT_COMBOTEXTEXCH()

	// Set up the list boxes as CCheckListViewCtrls, and stock the
	// member type with the available types.
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		_tables.SubclassWindow(GetDlgItem(IDC_TABLES));
		_tables.InsertColumn(0, "Name", LVCFMT_LEFT, 80, 0);

		_columns.SubclassWindow(GetDlgItem(IDC_COLUMNS));
		_columns.InsertColumn(0, "Name", LVCFMT_LEFT, 80, 0);

		CComboBox cmb = GetDlgItem(IDC_RSTYPE);
		cmb.AddString("int");
		cmb.AddString("std::string");
		cmb.AddString("bool");
		cmb.AddString("double");
		cmb.AddString("COleDateTime");
		cmb.AddString("COleCurrency");
		return 1;
	}

	// Message handler that causes all tables to be ticked.
	LRESULT OnBnClickedTickall(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		int ctr = 0;
		while (ctr < _tables.GetItemCount())
		{
			_tables.SetCheckState(ctr, TRUE);
			++ctr;
		}
		return 0;
	}

	// Message handler that causes all tables to be UN-ticked.
	LRESULT OnBnClickedTicknone(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		int ctr = 0;
		while (ctr < _tables.GetItemCount())
		{
			_tables.SetCheckState(ctr, FALSE);
			++ctr;
		}
		return 0;
	}

	// Something about a list item in the table list has changed.
	// We're interested in this if an item that wasn't selected has
	// been selected, or if a checkmark has changed state.
	LRESULT OnLvnItemchangedTables(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/)
	{
		LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
		if ((pNMLV->uOldState & LVIS_SELECTED) == 0
			&& (pNMLV->uNewState & LVIS_SELECTED) != 0)
		{
			if (_tables.GetSelectedCount() == 1)
			{
				showTableInfo(pNMLV->iItem);
			}
			else
			{
				showTableInfo(-1);
			}
		}
		else
		{
			CString tableName;
			_tables.GetItemText(pNMLV->iItem, 0, tableName);
			TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();
			TABLEINFOMAP::iterator timit = tableInfo.find((const char*)tableName);
			if (timit != tableInfo.end())
			{
				timit->second.generate = (_tables.GetCheckState(pNMLV->iItem) == TRUE);
			}
			checkForNextEnabled();
		}
		return 0;
	}

	// Only care if items are being unselected.
	LRESULT OnLvnItemchangingTables(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/)
	{
		LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
		if ((pNMLV->uOldState & LVIS_SELECTED) != 0
			&& (pNMLV->uNewState & LVIS_SELECTED) == 0)
		{
			extractTableInfo(pNMLV->iItem);
		}
		return 0;
	}

	// This method extracts the table information from the fields in
	// the page and ensures that the specified table info structure
	// receives the class and file names.
	void extractTableInfo(int itemNo)
	{
		DoDataExchange(TRUE);

		CString tableName;
		_tables.GetItemText(itemNo, 0, tableName);
		TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();
		TABLEINFOMAP::iterator timit = tableInfo.find((const char*)tableName);
		if (timit != tableInfo.end())
		{
			timit->second.className = (const char*)m_ClassName;
			timit->second.fileName = (const char*)m_FileName;
			timit->second.baseClass = (const char*)m_BaseName;
		}
	}

	// Something about a list item in the column list has changed.
	// We're interested in this if an item that wasn't selected has
	// been selected, or if a checkmark has changed state.
	LRESULT OnLvnItemchangedColumns(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/)
	{
		LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
		if ((pNMLV->uOldState & LVIS_SELECTED) == 0
			&& (pNMLV->uNewState & LVIS_SELECTED) != 0)
		{
			if (_columns.GetSelectedCount() == 1)
			{
				showColumnInfo(pNMLV->iItem);
			}
			else
			{
				showColumnInfo(-1);
			}
		}
		else
		{
			TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();
			TABLEINFOMAP::iterator timit = tableInfo.find(_currTable);
			if (timit != tableInfo.end())
			{
				CString colName;
				_columns.GetItemText(pNMLV->iItem, 0, colName);
				COLUMNMAP::iterator cmit = timit->second.columns.find((const char*)colName);
				if (cmit != timit->second.columns.end())
				{
					cmit->second.generate = (_columns.GetCheckState(pNMLV->iItem) == TRUE);
				}
			}
		}
		return 0;
	}

	// Only care if items are being unselected
	LRESULT OnLvnItemchangingColumns(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/)
	{
		LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
		if ((pNMLV->uOldState & LVIS_SELECTED) != 0
			&& (pNMLV->uNewState & LVIS_SELECTED) == 0)
		{
			extractColumnInfo(pNMLV->iItem);
		}
		return 0;
	}

	// This method extracts the column information from the fields in
	// the page and ensures that the specified column info structure
	// receives the member, field and data type names.
	void extractColumnInfo(int itemNo)
	{
		DoDataExchange(TRUE);

		CString colName;
		_columns.GetItemText(itemNo, 0, colName);
		TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();
		TABLEINFOMAP::iterator timit = tableInfo.find(_currTable);
		if (timit != tableInfo.end())
		{
			COLUMNMAP::iterator cmit = timit->second.columns.find((const char*)colName);
			if (cmit != timit->second.columns.end())
			{
				cmit->second.memberName = (const char*)m_RSName;
				cmit->second.memberType = (const char*)m_RSType;
				cmit->second.isIndex = (m_RSIndex == TRUE);
			}
		}
	}

	// When this page is activated via the wizard, we want to make sure that
	// we put all the tables from the database into the tables list.
	// We also want to make sure that we have the columns for each table as
	// well.
	// This function pretty much rebuilds the GlobalData::_tableInfo object.
	int OnSetActive()
	{
		GlobalData& gd = GlobalDataS::instance();
		TABLEINFOMAP& tableInfo = gd.tableInfo();
		pqxx::connection* pConn = gd.dbConn();
		pqxx::work TRX(*pConn, "TableQuery");

		tableInfo.clear();
		_tables.DeleteAllItems();
		_columns.DeleteAllItems();

		GetDlgItem(IDC_BASECLASS).EnableWindow(gd.genBase());

		// Make a query that reads in the tables we're interested in.
		pqxx::result dbr;
		pqxx::result::const_iterator rit;
		std::string query = "select * from pg_tables where not tablename like 'pg_%'";
		int ctr = 0;
		for (int qType = 0; qType < 2; qType++)
		{
			if (gd.schema() != "")
			{
				query += " and schemaname='";
				query += pqxx::sqlesc(gd.schema()) + "'";
			}
			query += " order by ";
			query += (qType == 0) ? "tablename" : "viewname";

			dbr = TRX.exec(query);
			rit = dbr.begin();
			while (rit != dbr.end())
			{
				std::string tname;
				std::string extName;
				std::string baseName;
				rit->at((qType == 0) ? "tablename" : "viewname").to(tname);
				_tables.InsertItem(ctr, tname.c_str());
				_tables.SetCheckState(ctr++, TRUE);

				extName = tname;
				extName[0] = _toupper(extName[0]);
				baseName = (gd.usePrefix() ? "C" : "") + extName;
				if (gd.genBase() && gd.dropTrailingS() && *(baseName.end() - 1) == 's')
				{
					baseName = baseName.substr(0, baseName.length() - 1);
				}
				tableInfo[tname] = TableInfo((gd.usePrefix() ? "C" : "") + extName + "RS",
					tname + "rs.h",
					baseName);
				++rit;
			}
			query = "select * from pg_views where not viewname like 'pg_%'";
		}

		// Pull the columns in for each table in the list of tables.
		TABLEINFOMAP::iterator timit = tableInfo.begin();
		while (timit != tableInfo.end())
		{
			char tableName[MAX_PATH];
			strcpy(tableName, timit->first.c_str());
			query = "select pg_attribute.attname, pg_type.typname from pg_class"
				" left join pg_attribute on pg_class.oid=pg_attribute.attrelid"
				" left join pg_type on pg_attribute.atttypid=pg_type.oid"
				" where pg_class.relname='";
			query += pqxx::sqlesc(tableName) + "' and attnum > 0 order by attnum";
			dbr = TRX.exec(query);
			rit = dbr.begin();
			int ctr = 0;
			while (rit != dbr.end())
			{
				std::string colName;
				ColumnInfo ci;
				rit->at("attname").to(colName);
				rit->at("typname").to(ci.colPGType);
				ci.memberName = colName;
				ci.generate = true;
				ci.memberType = ColumnInfo::guessMemberType(ci.colPGType.c_str());
				timit->second.columns[colName] = ci;
				++rit;
			}
			++timit;
		}
		TRX.commit();

		// Because we've updated the list of tables in the list,
		// make sure that the column is as wide as the client
		// area of the table list. This is so that the horizontal
		// scrollbar doesn't show up.
		CRect rcClient;
		_tables.GetClientRect(rcClient);
		_tables.SetColumnWidth(0, rcClient.Width());

		return 1;
	}

	// When we are about to move on, we need to make sure that any
	// changes that have taken place are copied into the appropriate
	// maps.
	LRESULT OnWizardNext()
	{
		if (_tables.GetSelectedCount() == 1)
		{
			for (int ctr = _tables.GetItemCount() - 1; ctr >= 0; ctr--)
			{
				if (_tables.GetItemState(ctr, LVIS_SELECTED) == LVIS_SELECTED)
				{
					extractTableInfo(ctr);
				}
			}
		}
		if (_columns.GetSelectedCount() == 1)
		{
			for (int ctr = _columns.GetItemCount() - 1; ctr >= 0; ctr--)
			{
				if (_columns.GetItemState(ctr, LVIS_SELECTED) == LVIS_SELECTED)
				{
					extractColumnInfo(ctr);
				}
			}
		}
		return 0;
	}

	// If there is (at least) one item selected, put the list of
	// the tables columns in the column list.
	void showTableInfo(int itemNo)
	{
		TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();

		DoDataExchange(TRUE);
		_columns.DeleteAllItems();
		if (itemNo < 0)
		{
			_currTable = "";
			m_FileName = "";
			m_ClassName = "";
			m_BaseName = "";
		}
		else
		{
			CString tableName;
			_tables.GetItemText(itemNo, 0, tableName);
			_currTable = (const char*)tableName;
			TABLEINFOMAP::iterator timit = tableInfo.find(_currTable);
			if (timit != tableInfo.end())
			{
				m_FileName = timit->second.fileName.c_str();
				m_ClassName = timit->second.className.c_str();
				m_BaseName = timit->second.baseClass.c_str();
				COLUMNMAP::iterator cmit = timit->second.columns.begin();
				int ctr = 0;
				while (cmit != timit->second.columns.end())
				{
					bool shouldCheck = cmit->second.generate;
					_columns.InsertItem(ctr, cmit->first.c_str());
					_columns.SetCheckState(ctr++, (shouldCheck == true));
					++cmit;
				}
			}
			else
			{
				_currTable = "";
			}
		}

		// Clear out the column information if we change tables
		m_PGType = "";
		m_RSName = "";
		m_RSType = "";
		m_RSIndex = FALSE;

		// Put the data into the windows
		DoDataExchange(FALSE);

		// Because we've updated the list of columns in the list,
		// make sure that the column is as wide as the client
		// area of the column list. This is so that the horizontal
		// scrollbar doesn't show up.
		CRect rcClient;
		_columns.GetClientRect(rcClient);
		_columns.SetColumnWidth(0, rcClient.Width());
	}

	// Find out if the Next button should be enabled (there must
	// be at least one table's class being generated).
	void checkForNextEnabled()
	{
		int ctr = _tables.GetItemCount();
		DWORD dwFlags = PSWIZB_BACK;
		while (0 < ctr)
		{
			--ctr;
			if (_tables.GetCheckState(ctr))
			{
				dwFlags |= PSWIZB_NEXT;
				break;
			}
		}
		SetWizardButtons(dwFlags);
	}

	// Show a column's information on the RHS of the page.
	void showColumnInfo(int itemNo)
	{
		TABLEINFOMAP& tableInfo = GlobalDataS::instance().tableInfo();

		DoDataExchange(TRUE);
		if (itemNo < 0 || _currTable == "")
		{
			m_PGType = "";
			m_RSName = "";
			m_RSType = "";
			m_RSIndex = FALSE;
		}
		else
		{
			TABLEINFOMAP::iterator timit = tableInfo.find(_currTable);
			if (timit != tableInfo.end())
			{
				CString colName;
				_columns.GetItemText(itemNo, 0, colName);
				COLUMNMAP::iterator cmit = timit->second.columns.find((const char*)colName);
				if (cmit != timit->second.columns.end())
				{
					m_PGType = cmit->second.colPGType.c_str();
					m_RSName = cmit->second.memberName.c_str();
					m_RSType = cmit->second.memberType.c_str();
					m_RSIndex = cmit->second.isIndex;
				}
			}
		}
		DoDataExchange(FALSE);
	}
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

_oti
Other
Australia Australia
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 18 Aug 2006
Article Copyright 2005 by _oti
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid