Hi,

Finally I've made Copy/Paste table(s) operations !

Usage is very simple and intuitive:
on right mouse menu for selected connected table or schema select
'Copy table(s)' and than on other connected schema on right mouse menu
select 'Paste table(s).

Source target server-db might be different but always connected.

Best regards,
Vladimir Kokovic, DP senior, Belgrade, Serbia

Attachment: frmMain-h.diff
Description: Binary data

Attachment: frmMain-cpp.diff
Description: Binary data

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2011, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmPasteObject.h - Copy/Paste table(s) functions
//
//////////////////////////////////////////////////////////////////////////

#ifndef FRMPASTEOBJECT_H
#define	FRMPASTEOBJECT_H

#include "frm/frmMain.h"
#include "schema/pgObject.h"

class frmPasteObject
{
public:
	frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj);
	void process();
	virtual ~frmPasteObject();
private:
	frmMain *mainform;
	pgObject *sourceobj;
	pgObject *targetobj;
};

#endif	/* FRMPASTEOBJECT_H */

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2011, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmPasteObject.cpp - Copy/Paste table(s) functions
//
//////////////////////////////////////////////////////////////////////////

// wxWindows headers
#include <wx/wx.h>
#include <wx/wfstream.h>

// PostgreSQL headers
#include <libpq-fe.h>

#include "pgAdmin3.h"
#include "frm/frmPasteObject.h"
#include "schema/pgSchema.h"
#include "schema/pgTable.h"
#include "schema/pgColumn.h"
#include "schema/pgSequence.h"
#include "schema/pgConstraints.h"
#include "utils/misc.h"
#include "schema/pgForeignKey.h"
#include "schema/pgIndexConstraint.h"
#include "schema/pgCheck.h"

#include <errno.h>

frmPasteObject::frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj)
{
	this->mainform = form;
	this->sourceobj= sourceobj;
	this->targetobj= targetobj;
}

wxArrayString *getSchemaTables(pgSchema *srcschema)
{
	wxArrayString *objArray = new wxArrayString();

	wxString query = wxT("SELECT relname ")
	                 wxT("FROM pg_namespace n ")
	                 wxT("LEFT JOIN pg_class c ON n.oid=c.relnamespace AND relkind='r' ")
	                 wxT("WHERE nspname='") + srcschema->GetIdentifier() + wxT("'");
	query += wxT("ORDER BY relname");

	pgSet *objects = srcschema->GetDatabase()->ExecuteSet(query);

	if (objects)
	{
		while (!objects->Eof())
		{
			if (!objects->GetVal(wxT("relname")).IsNull())
			{
				objArray->Add(objects->GetVal(wxT("relname")));
			}
			objects->MoveNext();
		}
		delete objects;
	}

	return objArray;
}


/*
 * Functions for handling COPY IN/OUT data transfer.
 *
 * If you want to use COPY TO STDOUT/FROM STDIN in your application,
 * this is the code to steal ;)
 */

/*
 * handleCopyOut
 * receives data as a result of a COPY ... TO STDOUT command
 *
 * conn should be a database connection that you just issued COPY TO on
 * and got back a PGRES_COPY_OUT result.
 * copystream is the file stream for the data to go to.
 *
 * result is true if successful, false if not.
 */
wxString
handleCopyOut(PGconn *conn, wxFile & copystream)
{
	bool		OK = true;
	char	   *buf;
	int			ret;
	PGresult   *res;
	wxString	lastError;

	for (;;)
	{
		ret = PQgetCopyData(conn, &buf, 0);

		if (ret < 0)
			break;				/* done or error */

		if (buf)
		{
			if (copystream.Write(wxString(buf, wxConvUTF8)) != ret)
			{
				if (OK)			/* complain only once, keep reading data */
					lastError.Format(_("could not write COPY data: %s\n"), strerror(errno));
				OK = false;
			}
			PQfreemem(buf);
		}
	}

	if (OK && copystream.Flush())
	{
		lastError.Format(_("could not write COPY data: %s\n"), strerror(errno));
		OK = false;
	}

	if (ret == -2)
	{
		lastError.Format(_("COPY data transfer failed: %s"), PQerrorMessage(conn));
		OK = false;
	}

	/* Check command status and return to normal libpq state */
	res = PQgetResult(conn);
	if (PQresultStatus(res) != PGRES_COMMAND_OK)
	{
		lastError.Format(wxT("%s"), PQerrorMessage(conn));
		OK = false;
	}
	PQclear(res);

	return lastError;
}

/*
 * handleCopyIn
 * sends data to complete a COPY ... FROM STDIN command
 *
 * conn should be a database connection that you just issued COPY FROM on
 * and got back a PGRES_COPY_IN result.
 * copystream is the file stream to read the data from.
 * isbinary can be set from PQbinaryTuples().
 *
 * result is true if successful, false if not.
 */

/* read chunk size for COPY IN - size is not critical */
#define COPYBUFSIZ 8192

wxString
handleCopyIn(PGconn *conn, wxFile & copystream, bool isbinary)
{
	bool		OK;
	char		buf[COPYBUFSIZ];
	PGresult   *res;
	wxString	lastError;
	int			counter = 0;

	OK = true;

	if (isbinary)
	{
		for (;;)
		{
			int			buflen;;
			buflen = copystream.Read(buf, 1);
			if (buflen <= 0)
				break;
			if (PQputCopyData(conn, buf, buflen) <= 0)
			{
				OK = false;
				break;
			}
		}
	}
	else
	{
		wxFileInputStream input(copystream);
		wxTextInputStream textfile(input);

		while (input.CanRead()) /* for each bufferload in line ... */
		{
			counter++;
			wxString buf1 = textfile.ReadLine() + wxT("\n");
			int buflen = buf1.Length();
			if (buf1 == wxT("\n"))
			{
				break;
			}
			const wxCharBuffer wc = buf1.ToUTF8();
			const char *tmp = wc.data();
			int lenc = strlen(tmp);
			if (PQputCopyData(conn, tmp, lenc) <= 0)
			{
				OK = false;
				break;
			}
		}
	}

	/* Terminate data transfer */
	const char *errmsg = "aborted because of read failure";
	if (PQputCopyEnd(conn, OK ? NULL : errmsg) <= 0)
		OK = false;

	/* Check command status and return to normal libpq state */
	res = PQgetResult(conn);
	if (PQresultStatus(res) != PGRES_COMMAND_OK)
	{		
		lastError = wxString::FromAscii(PQerrorMessage(conn));
		OK = false;
	}
	PQclear(res);

	return lastError;
}

/*
 * Execute a \copy command (frontend copy). We have to open a file, then
 * submit a COPY query to the backend and either feed it data from the
 * file or route its response into the file.
 */
wxString
do_copy(PGconn *conn, wxString & sql, wxFile & copystream)
{
	PGresult   *result;
	struct stat st;
	wxString	lastError;

	result = PQexec(conn, (const char *)sql.mb_str());
	switch (PQresultStatus(result))
	{
		case PGRES_COPY_OUT:
			lastError = ::handleCopyOut(conn, copystream);
			break;
		case PGRES_COPY_IN:
			lastError = ::handleCopyIn(conn, copystream, PQbinaryTuples(result));
			break;
		case PGRES_NONFATAL_ERROR:
		case PGRES_FATAL_ERROR:
		case PGRES_BAD_RESPONSE:
			lastError.Format(_("copy: %s"), PQerrorMessage(conn));
			break;
		default:
			lastError.Format(_("copy: unexpected response (%d)\n"), PQresultStatus(result));
			break;
	}

	PQclear(result);

	/*
	 * Make sure we have pumped libpq dry of results; else it may still be in
	 * ASYNC_BUSY state, leading to false readings in, eg, get_prompt().
	 */
	while ((result = PQgetResult(conn)) != NULL)
	{
		lastError.Format(_("copy: unexpected response (%d)\n"), PQresultStatus(result));
		/* if still in COPY IN state, try to get out of it */
		if (PQresultStatus(result) == PGRES_COPY_IN)
			PQputCopyEnd(conn, (const char *)_("trying to exit copy mode"));
		PQclear(result);
	}

	return lastError;
}

wxString copyTable(ctlTree *browser, pgSchema *srcschema, pgSchema *targetschema, pgTable *table1)
{
	pgConn *sourceconn = srcschema->GetConnection();
	pgConn *targetconn = targetschema->GetConnection();
	bool rc;

	wxString lastError;

	rc = targetconn->ExecuteVoid(wxT("BEGIN"));
	lastError = targetconn->GetLastError();
	if (!rc) return lastError;

	table1->ShowTreeDetail(browser);
	pgCollection *columns = table1->GetColumnCollection(browser);
	treeObjectIterator colIt(browser, columns);
	pgColumn *column;
	wxString colName, defval;
	while ((column = (pgColumn *)colIt.GetNextObject()) != 0)
	{
		column->ShowTreeDetail(browser);
		if (column->GetColNumber() > 0)
		{
			colName = column->GetName();
			defval = column->GetDefault();
			if (!defval.IsNull() && defval.StartsWith(wxT("nextval(")))
			{
				wxStringTokenizer parts(defval, wxT("'"));
				int i = 0;
				while (parts.HasMoreTokens())
				{
					wxString part = parts.GetNextToken();
					if (i == 1)
					{
						wxString seqsch, seqname;
						int subseq = part.Find('.');
						if (subseq == wxNOT_FOUND)
						{
							seqsch = wxT("public");
							seqname = part;
						}
						else
						{
							seqsch = part.Mid(0, subseq - 1);
							seqname = part.Mid(subseq + 1);
						}

						wxLongLong lastValue, minValue, maxValue, cacheValue, increment;
						bool cycled, called;
						wxString owner, comment, acl;
						pgSequence seq = pgSequence(srcschema, seqname);
						pgSet *seqcat = srcschema->GetDatabase()->ExecuteSet(
							wxT("SELECT pg_get_userbyid(relowner) AS seqowner, relacl, description\n")
							wxT("  FROM pg_class cl\n")
							wxT("  LEFT OUTER JOIN pg_description des ON des.objoid=cl.oid\n")
							wxT(" WHERE relkind = 'S' AND relnamespace  = ") + srcschema->GetOidStr() +
							wxT(" AND relname  = ") + seq.qtDbString(seqname) + wxT("\n"));
						if (seqcat)
						{
							comment = seqcat->GetVal(wxT("description"));
							owner = seqcat->GetVal(wxT("seqowner"));
							acl = seqcat->GetVal(wxT("relacl"));
							delete seqcat;
						}
						else
						{
							goto Error;
						}
						seq.iSetAcl(acl);
						pgSet *sequence = srcschema->GetDatabase()->ExecuteSet(
							wxT("SELECT last_value, min_value, max_value, cache_value, is_cycled, increment_by, is_called\n")
							wxT("  FROM ") + seq.GetQuotedFullIdentifier());
						if (sequence)
						{
							lastValue = sequence->GetLongLong(wxT("last_value"));
							minValue = sequence->GetLongLong(wxT("min_value"));
							maxValue = sequence->GetLongLong(wxT("max_value"));
							cacheValue = sequence->GetLongLong(wxT("cache_value"));
							increment = sequence->GetLongLong(wxT("increment_by"));
							cycled = sequence->GetBool(wxT("is_cycled"));
							called = sequence->GetBool(wxT("is_called"));
							delete sequence;
						}
						else
						{
							goto Error;
						}

						pgSequence seqnew = pgSequence(targetschema, seqname);
						wxString sql = wxT("-- Sequence: ") + seqnew.GetQuotedFullIdentifier() + wxT("\n\n")
							+ wxT("-- DROP SEQUENCE ") + seqnew.GetQuotedFullIdentifier() + wxT(";")
							+ wxT("\n\nCREATE SEQUENCE ") + seqnew.GetQuotedFullIdentifier()
							+ wxT("\n  INCREMENT ") + increment.ToString()
							+ wxT("\n  MINVALUE ") + minValue.ToString()
							+ wxT("\n  MAXVALUE ") + maxValue.ToString()
							+ wxT("\n  START ") + lastValue.ToString()
							+ wxT("\n  CACHE ") + cacheValue.ToString();
						if (cycled)
							sql += wxT("\n  CYCLE");
						sql += wxT(";\nALTER TABLE ")
							+ seqnew.GetQuotedFullIdentifier() + wxT(" OWNER TO ") + qtIdent(owner) + wxT(";\n");
						if (!seq.GetConnection()->BackendMinimumVersion(8, 2))
							sql += seq.GetGrant(wxT("arwdRxt"), wxT("TABLE ") + seqnew.GetQuotedFullIdentifier());
						else
							sql += seq.GetGrant(wxT("rwU"), wxT("TABLE ") + seqnew.GetQuotedFullIdentifier());
						wxString cmt;
						if (!comment.IsNull())
						{
							cmt = wxT("COMMENT ON SEQUENCE ") + seqnew.GetQuotedFullIdentifier()
								  + wxT(" IS ") + seqnew.qtDbString(comment) + wxT(";\n");
							sql += cmt;
						}

						rc = targetconn->ExecuteVoid(sql, false);
						if (!rc) goto Error;

						break;
					}
					i++;
				}
			}
		}
	}

	{
		wxString createsql = table1->GetSql(browser);
		createsql.Replace(srcschema->GetIdentifier() + wxT("."), targetschema->GetIdentifier() + wxT("."), true);
		rc = targetconn->ExecuteVoid(createsql, false);
		if (!rc) goto Error;

		if (sourceconn->GetDbname() + sourceconn->GetHost() == targetconn->GetDbname() + targetconn->GetHost())
		{
			wxString copysql = 
				wxT("\nINSERT INTO ")
				+ targetschema->GetQuotedPrefix() + table1->GetQuotedIdentifier()
				+ wxT(" (SELECT * FROM ")
				+ srcschema->GetQuotedPrefix() + table1->GetQuotedIdentifier()
				+ wxT(")\n\n");
			rc = targetconn->ExecuteVoid(copysql, false);
			if (!rc) goto Error;
		}
		else
		{
			wxString tmpFilename;
			wxFile tmpFile;
			tmpFilename = wxFileName::CreateTempFileName(wxT("copyobject"));
			tmpFile.Open(tmpFilename.c_str(), wxFile::write);
			if (!tmpFile.IsOpened())
			{
				lastError = _("Can't create temporary file: ") + tmpFilename;
				goto Error1;
			}
			wxString copysql =
				wxT("COPY ")
				+ srcschema->GetQuotedPrefix() + table1->GetQuotedIdentifier()
				+ wxT(" TO STDOUT");
			lastError = ::do_copy(sourceconn->connection(), copysql, tmpFile);
			if (lastError == wxEmptyString)
			{
				tmpFile.Close();
				tmpFile.Open(tmpFilename.c_str(), wxFile::read);
				if (!tmpFile.IsOpened())
				{
					lastError = _("Can't open temporary file: ") + tmpFilename;
					wxRemoveFile(tmpFilename);
					goto Error1;
				}
				copysql =
					wxT("COPY ")
					+ targetschema->GetQuotedPrefix() + table1->GetQuotedIdentifier()
					+ wxT(" FROM STDIN");
				lastError = ::do_copy(targetconn->connection(), copysql, tmpFile);
			}
			tmpFile.Close();
			wxRemoveFile(tmpFilename);
			if (lastError != wxEmptyString)
			{
				goto Error1;
			}
		}
		goto Success;
	}

Error:
	lastError = targetconn->GetLastError();
Error1:
	targetconn->ExecuteVoid(wxT("ROLLBACK"));
	return lastError;

Success:
	targetconn->ExecuteVoid(wxT("COMMIT"));
	return lastError;;
}

void frmPasteObject::process()
{
	if (!sourceobj || !targetobj)
	{
		return;
	}

	wxArrayString *srcObjArray;
	pgSchema *targetschema = (pgSchema *)targetobj;
	pgSchema *srcschema = 0;
	pgTable *table = (sourceobj->GetMetaType() == PGM_TABLE) ? (pgTable *)sourceobj : 0;
	if (table)
	{
		wxMessageBox(
			wxT("Paste source table\n") +
			table->GetSchema()->GetDatabase()->GetIdentifier() + wxT(".") + table->GetSchema()->GetIdentifier() + wxT(".") + table->GetIdentifier() + wxT("\n") +
			wxT(" into schema\n") + targetschema->GetDatabase()->GetIdentifier() + wxT(".") + targetschema->GetIdentifier());
	}
	else
	{
		srcschema = (pgSchema *)sourceobj;
		wxMessageBox(
			wxT("Paste source schema objects\n") +
			srcschema->GetDatabase()->GetIdentifier() + wxT(".") + srcschema->GetIdentifier() + wxT("\n") +
			wxT(" into schema\n") +
			targetschema->GetDatabase()->GetIdentifier() + wxT(".") + targetschema->GetIdentifier());
	}

	pgConn *sourceconn = sourceobj->GetConnection();
	pgConn *targetconn = targetobj->GetConnection();

	if (!sourceconn || !targetconn)
	{
		wxMessageBox(
			_("Both source and target schema connections should be established before paste object operation !"));
		return;
	}

	if (srcschema)
	{
		srcObjArray = ::getSchemaTables(srcschema);
	}
	else
	{
		srcObjArray = new wxArrayString();
		srcObjArray->Add(table->GetIdentifier());
		srcschema = table->GetSchema();
	}

	if (srcschema->GetIdentifier() == targetschema->GetIdentifier())
	{
		wxMessageBox(_("Source and target schema should be different schema for paste object operation !"));
		return;
	}

	srcschema->ShowTreeDetail(mainform->GetBrowser());

	wxString msg;
	for(unsigned int i = 0; i < srcObjArray->Count(); i++)
	{
		msg = _("COPY TABLE:") +
			srcschema->GetDatabase()->GetIdentifier() + wxT(".") + srcschema->GetIdentifier() + wxT(".") + srcObjArray->Item(i) +
			_(" INTO:") + targetschema->GetDatabase()->GetIdentifier() + wxT(".") + targetschema->GetIdentifier();
		mainform->GetStatusBar()->SetStatusText(msg, 1);
		//future implementation

		pgTable *table1 = 0;
		wxTreeItemIdValue schemacookie;
		wxTreeItemId schemaid = srcschema->GetId();
		wxTreeItemId schemaitem = mainform->GetBrowser()->GetFirstChild(schemaid, schemacookie);
		bool found = false;
		while (schemaitem && !found)
		{
			pgObject *obj = mainform->GetBrowser()->GetObject(schemaitem);
			if (obj && obj->GetMetaType() == PGM_TABLE)
			{
				wxTreeItemIdValue tablecookie;
				wxTreeItemId tableitem = mainform->GetBrowser()->GetFirstChild(obj->GetId(), tablecookie);
				while (tableitem)
				{
					table1 = (pgTable *)mainform->GetBrowser()->GetObject(tableitem);
					if (table1->GetIdentifier() == srcObjArray->Item(i))
					{
						found = true;
						break;
					}
					table1 = 0;
					tableitem = mainform->GetBrowser()->GetNextChild(obj->GetId(), tablecookie);
				}
			}
			schemaitem = mainform->GetBrowser()->GetNextChild(schemaid, schemacookie);
		}
		if (!table1)
		{
			msg = _("WARNING SOURCE TABLE DISAPEARED:\n") +
				srcschema->GetDatabase()->GetIdentifier() + wxT(".") + srcschema->GetIdentifier() + wxT(".") + srcObjArray->Item(i);
		}
		else
		{
			msg = ::copyTable(mainform->GetBrowser(), srcschema, targetschema, table1);
			if (msg != wxEmptyString)
			{
				wxMessageBox(msg,
					_("Cannot paste object:") +
					targetschema->GetDatabase()->GetIdentifier() + wxT(".") + targetschema->GetIdentifier() + wxT(".") + table1->GetIdentifier(),
					wxOK | wxICON_ERROR);
			}
		}
	}
	msg = wxString::Format(_("%d TABLE(s) COPIED FROM %s TO %s"), srcObjArray->Count(),
		(srcschema->GetDatabase()->GetIdentifier() + wxT(".") + srcschema->GetIdentifier()).c_str(),
		(targetschema->GetDatabase()->GetIdentifier() + wxT(".") + targetschema->GetIdentifier()).c_str());
	mainform->GetStatusBar()->SetStatusText(msg, 1);

	delete srcObjArray;
}

frmPasteObject::~frmPasteObject()
{
}

Attachment: module-mk.diff
Description: Binary data

-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to