*** a/pgadmin/db/pgConn.cpp --- b/pgadmin/db/pgConn.cpp *************** *** 704,709 **** bool pgConn::ExecuteVoid(const wxString &sql, bool reportError) --- 704,738 ---- } + ////////////////////////////////////////////////////////////////////////// + // Execute SQL with optional result set + ////////////////////////////////////////////////////////////////////////// + PGresult * pgConn::ExecuteOptionalResult(const wxString &sql, bool reportError) + { + PGresult *qryRes = 0; + + if (GetStatus() != PGCONN_OK) + return qryRes; + + // Execute the query and get the status. + + + wxLogSql(wxT("Void query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str()); + qryRes = PQexec(conn, sql.mb_str(*conv)); + lastResultStatus = PQresultStatus(qryRes); + SetLastResultError(qryRes); + + // Check for errors + if (lastResultStatus != PGRES_COMMAND_OK && lastResultStatus != PGRES_COPY_IN && lastResultStatus != PGRES_COPY_OUT) + { + LogError(!reportError); + PQclear(qryRes); + qryRes = 0; + } + + return qryRes; + } + wxString pgConn::ExecuteScalar(const wxString &sql) { *** a/pgadmin/frm/frmMain.cpp --- b/pgadmin/frm/frmMain.cpp *************** *** 78,83 **** --- 78,84 ---- #include "slony/slCluster.h" #include "slony/slSet.h" #include "schema/pgForeignKey.h" + #include "frm/frmPasteObject.h" #if wxDIALOG_UNIT_COMPATIBILITY *************** *** 90,95 **** frmMain::frmMain(const wxString &title) --- 91,97 ---- msgLevel = 0; lastPluginUtility = NULL; pluginUtilityCount = 0; + copytObject = NULL; dlgName = wxT("frmMain"); SetMinSize(wxSize(600, 450)); *************** *** 356,361 **** void frmMain::CreateMenus() --- 358,367 ---- new reassignDropOwnedFactory(menuFactories, editMenu, 0); editMenu->AppendSeparator(); + new copyObjectFactory(menuFactories, editMenu, 0); + new pasteObjectFactory(menuFactories, editMenu, 0); + editMenu->AppendSeparator(); + new separatorFactory(menuFactories); toolBar->AddSeparator(); *************** *** 1392,1394 **** wxWindow *bugReportFactory::StartDialog(frmMain *form, pgObject *obj) --- 1398,1507 ---- DisplayHelp(wxT("bugreport"), HELP_PGADMIN); return 0; } + + + copyObjectFactory::copyObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) + { + mnu->Append(id, _("&Copy table(s)..."), _("Store table(s) reference for later paste")); + } + + + wxWindow *copyObjectFactory::StartDialog(frmMain *form, pgObject *obj) + { + if (!frmPasteObject::isActive()) + { + winMain->SetCopytObject(obj); + form->GetMenuFactories()->CheckMenu(obj, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar()); + } + return 0; + } + + + bool copyObjectFactory::CheckEnable(pgObject *obj) + { + if (!obj || frmPasteObject::isActive()) + return false; + + if (obj->GetConnection()) + { + //tree item 'Tables' has GetMetaType() == PGM_TABLE ! + pgTable *table = (obj->GetMetaType() == PGM_TABLE) ? dynamic_cast(obj) : 0; + if (table) + return true; + //tree item 'Schemas' has GetMetaType() == PGM_SCHEMA ! + pgSchema *schema = (obj->GetMetaType() == PGM_SCHEMA) ? dynamic_cast(obj) : 0; + if (schema) + return true; + } + return false; + } + + + pasteObjectFactory::pasteObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list) + { + mnu->Append(id, _("&Paste table(s)..."), _("Paste table(s)")); + } + + + wxWindow *pasteObjectFactory::StartDialog(frmMain *form, pgObject *obj) + { + //Until the pgadmin has thread list or task list, copy/paste tables is singleton ! + //That means that only one instance is allowed. + //Additional requests are simple(silently) ignored ! + pgObject *copyobj = winMain->GetCopytObject(); + pgDatabase *targetdatabase = ((pgSchema *)obj)->GetDatabase(); + if (copyobj) + { + pgTable *table = dynamic_cast(copyobj); + if (table == 0) + { + pgSchema *schema = dynamic_cast(copyobj); + if (schema == 0) + { + copyobj = NULL; + winMain->SetCopytObject(NULL); + } + } + } + if (copyobj && !frmPasteObject::isActive()) + { + frmPasteObject::setActive(true); + wxString targetschemaname = ((pgSchema *)obj)->GetIdentifier(); + frmPasteObject frm(winMain, copyobj, obj); + int copied = frm.process(); + frmPasteObject::setActive(false); + winMain->SetCopytObject(NULL); + if (copied && targetdatabase) + { + pgSchema *targetschema = frm.findSchema(targetdatabase, targetschemaname); + if (targetschema) + { + form->Refresh(targetschema); + targetschema = frm.findSchema(targetdatabase, targetschemaname); + if (targetschema) + { + targetschema->ShowTreeDetail(form->GetBrowser()); + form->GetMenuFactories()->CheckMenu(targetschema, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar()); + } + } + } + form->GetStatusBar()->SetStatusText(frm.getPastemsg(), 1); + } + return 0; + } + + + bool pasteObjectFactory::CheckEnable(pgObject *obj) + { + if (!obj) + return false; + + if (obj->GetConnection()) + { + //tree item 'Schemas' has GetMetaType() == PGM_SCHEMA ! + pgSchema *schema = (obj->GetMetaType() == PGM_SCHEMA) ? dynamic_cast(obj) : 0; + if (schema && winMain->GetCopytObject()) + return true; + } + return false; + } *** /dev/null --- b/pgadmin/frm/frmPasteObject.cpp *************** *** 0 **** --- 1,1430 ---- + ////////////////////////////////////////////////////////////////////////// + // + // 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 + + // PostgreSQL headers + #include + + #include "frm/frmPasteObject.h" + #include "schema/pgColumn.h" + #include "schema/pgSequence.h" + #include "schema/pgIndexConstraint.h" + #include "schema/pgForeignKey.h" + #include "schema/pgCheck.h" + + #include + + #include + WX_DEFINE_OBJARRAY(pgTableCopyPasteArray); + + bool frmPasteObject::active = false; + + frmPasteObject::frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj) + { + this->mainform = form; + this->sourceobj= sourceobj; + this->targetobj= targetobj; + } + + wxString frmPasteObject::getPastemsg() const { + return pastemsg; + } + + bool frmPasteObject::tableExists(pgSchema *srcschema, pgTable *table1) + { + ctlTree *browser = mainform->GetBrowser(); + pgCollection *tables = browser->FindCollection(tableFactory, srcschema->GetId()); + if (tables) + { + pgObject *obj; + pgTable *srctable; + treeObjectIterator colIt(browser, tables); + while ((obj = colIt.GetNextObject()) != 0) { + srctable = (pgTable *)obj; + if (srctable->GetId() == table1->GetId()) + return true; + } + } + return false; + } + + pgSchema *frmPasteObject::findSchema(pgDatabase *database, wxString schemaname) + { + ctlTree *browser = mainform->GetBrowser(); + pgCollection *schemas = browser->FindCollection(schemaFactory, database->GetId()); + if (schemas) + { + pgObject *obj; + pgSchema *srcschema; + treeObjectIterator colIt(browser, schemas); + while ((obj = colIt.GetNextObject()) != 0) { + srcschema = (pgSchema *)obj; + if (srcschema->GetIdentifier() == schemaname) + return srcschema; + } + } + return 0; + } + + void frmPasteObject::GetLastResultError(pgConn *conn, PGresult *res, const wxString &msg) + { + if (res) + { + lastResultError.severity = wxString(PQresultErrorField(res, PG_DIAG_SEVERITY), *conn->GetConv()); + lastResultError.sql_state = wxString(PQresultErrorField(res, PG_DIAG_SQLSTATE), *conn->GetConv()); + lastResultError.msg_primary = wxString(PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY), *conn->GetConv()); + lastResultError.msg_detail = wxString(PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL), *conn->GetConv()); + lastResultError.msg_hint = wxString(PQresultErrorField(res, PG_DIAG_MESSAGE_HINT), *conn->GetConv()); + lastResultError.statement_pos = wxString(PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION), *conn->GetConv()); + lastResultError.internal_pos = wxString(PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION), *conn->GetConv()); + lastResultError.internal_query = wxString(PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY), *conn->GetConv()); + lastResultError.context = wxString(PQresultErrorField(res, PG_DIAG_CONTEXT), *conn->GetConv()); + lastResultError.source_file = wxString(PQresultErrorField(res, PG_DIAG_SOURCE_FILE), *conn->GetConv()); + lastResultError.source_line = wxString(PQresultErrorField(res, PG_DIAG_SOURCE_LINE), *conn->GetConv()); + lastResultError.source_function = wxString(PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION), *conn->GetConv()); + } + else + { + lastResultError.severity = wxEmptyString; + lastResultError.sql_state = wxEmptyString; + if (msg.IsEmpty()) + lastResultError.msg_primary = conn->GetLastError(); + else + lastResultError.msg_primary = msg; + lastResultError.msg_detail = wxEmptyString; + lastResultError.msg_hint = wxEmptyString; + lastResultError.statement_pos = wxEmptyString; + lastResultError.internal_pos = wxEmptyString; + lastResultError.internal_query = wxEmptyString; + lastResultError.context = wxEmptyString; + lastResultError.source_file = wxEmptyString; + lastResultError.source_line = wxEmptyString; + lastResultError.source_function = wxEmptyString; + } + + wxString errMsg; + + if (lastResultError.severity != wxEmptyString && lastResultError.msg_primary != wxEmptyString) + errMsg = lastResultError.severity + wxT(": ") + lastResultError.msg_primary; + else if (lastResultError.msg_primary != wxEmptyString) + errMsg = lastResultError.msg_primary; + + if (!lastResultError.sql_state.IsEmpty()) + { + if (!errMsg.EndsWith(wxT("\n"))) + errMsg += wxT("\n"); + errMsg += _("SQL state: "); + errMsg += lastResultError.sql_state; + } + + if (!lastResultError.msg_detail.IsEmpty()) + { + if (!errMsg.EndsWith(wxT("\n"))) + errMsg += wxT("\n"); + errMsg += _("Detail: "); + errMsg += lastResultError.msg_detail; + } + + if (!lastResultError.msg_hint.IsEmpty()) + { + if (!errMsg.EndsWith(wxT("\n"))) + errMsg += wxT("\n"); + errMsg += _("Hint: "); + errMsg += lastResultError.msg_hint; + } + + if (!lastResultError.statement_pos.IsEmpty()) + { + if (!errMsg.EndsWith(wxT("\n"))) + errMsg += wxT("\n"); + errMsg += _("Character: "); + errMsg += lastResultError.statement_pos; + } + + if (!lastResultError.context.IsEmpty()) + { + if (!errMsg.EndsWith(wxT("\n"))) + errMsg += wxT("\n"); + errMsg += _("Context: "); + errMsg += lastResultError.context; + } + lastResultError.formatted_msg = errMsg; + } + + /* + * 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. + */ + void frmPasteObject::handleCopyOut(pgConn *conn, wxFile & copystream) + { + bool OK = true; + char *buf; + int ret; + PGresult *res; + + lastResultError.formatted_msg = wxT(""); + for (;;) + { + ret = PQgetCopyData(conn->connection(), &buf, 0); + + if (ret < 0) + break; /* done or error */ + + if (buf) + { + int n = copystream.Write(wxString(buf, wxConvUTF8)); + if (n != 1) + { + if (OK) /* complain only once, keep reading data */ + lastResultError.formatted_msg.Format(_("could not write COPY data: %s\n"), strerror(errno)); + OK = false; + } + PQfreemem(buf); + } + } + + if (OK && !copystream.Flush()) + { + lastResultError.formatted_msg.Format(_("could not write COPY data: %s\n"), strerror(errno)); + OK = false; + } + + if (ret == -2) + { + lastResultError.formatted_msg.Format(_("COPY data transfer failed: %s"), PQerrorMessage(conn->connection())); + OK = false; + } + + /* Check command status and return to normal libpq state */ + res = PQgetResult(conn->connection()); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + if (lastResultError.formatted_msg.IsEmpty()) { + GetLastResultError(conn, res); + } + OK = false; + } + PQclear(res); + } + + /* + * 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 + + void frmPasteObject::handleCopyIn(pgConn *conn, wxFile & copystream, bool isbinary) + { + bool OK; + char buf[COPYBUFSIZ]; + PGresult *res; + int counter = 0; + + OK = true; + + if (isbinary) + { + for (;;) + { + int buflen;; + buflen = copystream.Read(buf, 1); + if (buflen <= 0) + break; + if (PQputCopyData(conn->connection(), 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->connection(), tmp, lenc) <= 0) + { + OK = false; + break; + } + } + } + + /* Terminate data transfer */ + const char *errmsg = "aborted because of read failure"; + if (PQputCopyEnd(conn->connection(), OK ? NULL : errmsg) <= 0) + OK = false; + + /* Check command status and return to normal libpq state */ + res = PQgetResult(conn->connection()); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + GetLastResultError(conn, res); + OK = false; + } + PQclear(res); + } + + /* + * 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. + */ + void frmPasteObject::do_copy(pgConn *conn, wxString & sql, wxFile & copystream) + { + PGresult *result; + struct stat st; + + result = conn->ExecuteOptionalResult(sql); + switch (PQresultStatus(result)) + { + case PGRES_COPY_OUT: + handleCopyOut(conn, copystream); + break; + case PGRES_COPY_IN: + handleCopyIn(conn, copystream, PQbinaryTuples(result)); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + lastResultError.formatted_msg.Format(_("copy error: %s\n%s\n"), PQerrorMessage(conn->connection()), sql.c_str()); + break; + default: + lastResultError.formatted_msg.Format( + _("copy error: unexpected response (%d)\n%s\n"), PQresultStatus(result), sql.c_str()); + 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->connection())) != NULL) + { + lastResultError.formatted_msg.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->connection(), (const char *)_("trying to exit copy mode")); + PQclear(result); + } + } + + void frmPasteObject::myLogNotice(const wxChar *szFormat, ...) + { + wxLogLevel savedlogLevel = sysLogger::logLevel; + sysLogger::logLevel = LOG_NOTICE; + va_list argptr; + va_start(argptr, szFormat); + wxVLogNotice(szFormat, argptr); + va_end(argptr); + sysLogger::logLevel = savedlogLevel; + } + + void frmPasteObject::copyTable(struct transfer_tag *transfer) + { + lastResultError.formatted_msg = wxT(""); + bool rc; + + myLogNotice(wxT("CopyPaste=\"\n%s\""), transfer->createsql.c_str()); + rc = transfer->targetconn->ExecuteVoid(transfer->createsql, false); + if (!rc) + { + lastResultError = transfer->targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + if (!transfer->searchPath.IsEmpty()) + { + rc = transfer->targetconn->ExecuteVoid(wxT("SET search_path=") + transfer->searchPath, false); + if (!rc) + { + lastResultError = transfer->targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + + if (transfer->sourceconn->GetDbname() == transfer->targetconn->GetDbname() && + transfer->sourceconn->GetHost() == transfer->targetconn->GetHost() && + transfer->sourceconn->GetPort() == transfer->targetconn->GetPort()) + { + //same DB server + wxString tablenamenew = transfer->table->Getsrctable()->GetQuotedIdentifier(); + if (transfer->srcschema->GetId() == transfer->targetschema->GetId()) + { + if (tablenamenew.StartsWith(wxT("\""))) + { + tablenamenew = tablenamenew.Mid(1, tablenamenew.length() - 1) + transfer->table->copysuffix; + tablenamenew = wxT("\"") + tablenamenew + wxT("\""); + } + else + tablenamenew += transfer->table->copysuffix; + } + wxString copysql = + wxT("\nINSERT INTO ") + + transfer->targetschema->GetQuotedPrefix() + tablenamenew + + wxT(" (SELECT * FROM ") + + transfer->srcschema->GetQuotedPrefix() + transfer->table->Getsrctable()->GetQuotedIdentifier() + + wxT(")\n\n"); + rc = transfer->targetconn->ExecuteVoid(copysql, false); + if (!rc) + { + lastResultError = transfer->targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + else + { + //different DB servers + wxString tmpFilename; + wxFile tmpFile; + tmpFilename = wxFileName::CreateTempFileName(wxT("copytable")); + tmpFile.Open(tmpFilename.c_str(), wxFile::write); + if (!tmpFile.IsOpened()) + { + lastResultError.formatted_msg = _("Can't create temporary file: ") + tmpFilename; + return; + } + wxString copysql = + wxT("COPY ") + + transfer->srcschema->GetQuotedPrefix() + transfer->table->Getsrctable()->GetQuotedIdentifier() + + wxT(" TO STDOUT"); + do_copy(transfer->sourceconn, copysql, tmpFile); + if (lastResultError.formatted_msg.IsEmpty()) + { + tmpFile.Close(); + tmpFile.Open(tmpFilename.c_str(), wxFile::read); + if (!tmpFile.IsOpened()) + { + lastResultError.formatted_msg = _("Can't open temporary file: ") + tmpFilename; + wxRemoveFile(tmpFilename); + return; + } + copysql = + wxT("COPY ") + + transfer->targetschema->GetQuotedPrefix() + transfer->table->Getsrctable()->GetQuotedIdentifier() + + wxT(" FROM STDIN"); + do_copy(transfer->targetconn, copysql, tmpFile); + } + tmpFile.Close(); + wxRemoveFile(tmpFilename); + } + } + + void *copyPasteThread::Entry() + { + transfer->THIS->copyTable(transfer); + + return(NULL); + } + + int frmPasteObject::process() + { + if (!sourceobj || !targetobj) + { + return 0; + } + + pgSchema *targetschema = (pgSchema *)targetobj; + pgSchema *srcschema = 0; + + //tree item 'Tables' has GetMetaType() == PGM_TABLE ! + pgTable *table = (sourceobj->GetMetaType() == PGM_TABLE) ? dynamic_cast(sourceobj) : 0; + if (table) + { + if (wxMessageBox( + _("Paste source table ?\n") + + table->GetSchema()->GetDatabase()->GetQuotedIdentifier() + wxT(".") + table->GetSchema()->GetQuotedIdentifier() + + wxT(".") + table->GetQuotedIdentifier() + wxT("\n") + + wxT(" into schema\n") + targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + + targetschema->GetQuotedIdentifier(), _("Paste table"), wxYES_NO) == wxNO) + { + return 0; + } + srcschema = table->GetSchema(); + } + else + { + //tree item 'Schemas' has GetMetaType() == PGM_SCHEMA ! + srcschema = dynamic_cast(sourceobj); + if (!srcschema) { + //tree item 'Tables' has GetMetaType() == PGM_TABLE or + //tree item 'Schemas' has GetMetaType() == PGM_SCHEMA ! + return 0; + } + if (wxMessageBox( + _("Paste schema tables ?\n") + + srcschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + srcschema->GetQuotedIdentifier() + wxT("\n") + + wxT(" into schema\n") + + targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + targetschema->GetQuotedIdentifier(), + _("Paste schema tables"), wxYES_NO) == wxNO) + { + return 0; + } + } + + pgConn *sourceconn = sourceobj->GetConnection(); + pgConn *targetconn = targetobj->GetConnection(); + + if (!sourceconn || !targetconn) + { + wxMessageBox( + _("Both source and target schema connections should be established before paste table(s) operation !")); + return 0; + } + + static wxString copysuffix = wxT("_copy_1"); + if (srcschema->GetId() == targetschema->GetId()) + { + copysuffix = wxGetTextFromUser( + _("Target schema is the same as source schema.\n" + "Enter suffix name for all the table copied objects"), + _("New Suffix Name"), + copysuffix); + } + + ctlTree *browser = mainform->GetBrowser(); + srcschema->ShowTreeDetail(browser); + targetschema->ShowTreeDetail(browser); + wxString msg; + int copied = 0; + pgTableCopyPasteArray tableCopyPasteArray; + pgTableCopyPaste *tablenew; + pgCollection *tables = browser->FindCollection(tableFactory, srcschema->GetId()); + pgCollection *sequences = browser->FindCollection(sequenceFactory, srcschema->GetId()); + + if (table) + { + table->ShowTreeDetail(browser); + pgCollection *constraints = browser->FindCollection(primaryKeyFactory, table->GetId()); + if (constraints) + { + constraints->ShowTreeDetail(browser); + treeObjectIterator consIt(browser, constraints); + pgObject *data; + while ((data = consIt.GetNextObject()) != 0) + { + data->ShowTreeDetail(browser); + } + } + pgCollection *columns = browser->FindCollection(columnFactory, table->GetId()); + if (columns) + columns->ShowTreeDetail(browser); + tablenew = new pgTableCopyPaste(table, targetschema, copysuffix, columns, constraints, tables, sequences); + tableCopyPasteArray.Add(tablenew); + } + else + { + if (tables) + { + pgObject *obj; + pgTable *srctable; + treeObjectIterator colIt(browser, tables); + while ((obj = colIt.GetNextObject()) != 0) { + srctable = (pgTable *)obj; + srctable->ShowTreeDetail(browser); + pgCollection *constraints = browser->FindCollection(primaryKeyFactory, srctable->GetId()); + if (constraints) + { + constraints->ShowTreeDetail(browser); + treeObjectIterator consIt(browser, constraints); + pgObject *data; + while ((data = consIt.GetNextObject()) != 0) + { + data->ShowTreeDetail(browser); + } + } + pgCollection *columns = browser->FindCollection(columnFactory, srctable->GetId()); + if (columns) + columns->ShowTreeDetail(browser); + tablenew = new pgTableCopyPaste(srctable, targetschema, copysuffix, columns, constraints, tables, sequences); + tableCopyPasteArray.Add(tablenew); + } + } + } + + struct transfer_tag transfervar; + struct transfer_tag *transfer = &transfervar; + + transfer->THIS = this; + transfer->srcdatabase = srcschema->GetDatabase(); + transfer->targetdatabase = targetschema->GetDatabase(); + + wxString srcdatabasename = transfer->srcdatabase->GetQuotedIdentifier(); + transfer->srcschemaname = srcschema->GetIdentifier(); + + wxString targetdatabasename = transfer->targetdatabase->GetQuotedIdentifier(); + transfer->targetschemaname = targetschema->GetIdentifier(); + + pgConn *newtargetconn = 0; + pgConn *newsourceconn = sourceconn->Duplicate(); + if (newsourceconn->GetDbname() == targetconn->GetDbname() && + newsourceconn->GetHost() == targetconn->GetHost() && + newsourceconn->GetPort() == targetconn->GetPort()) + { + //same DB server + newtargetconn = newsourceconn; + } + else + { + newtargetconn = targetconn->Duplicate(); + } + + transfer->sourceconn = newsourceconn; + transfer->targetconn = newtargetconn; + + for(unsigned int i = 0; i < tableCopyPasteArray.Count(); i++) + { + pgTableCopyPaste &item = tableCopyPasteArray.Item(i); + transfer->table = &item; + pastemsg = _("COPY TABLE:") + + srcdatabasename + wxT(".") + qtIdent(transfer->srcschemaname) + wxT(".") + + item.Getsrctable()->GetQuotedIdentifier() + + _(" INTO:") + targetdatabasename + wxT(".") + qtIdent(transfer->targetschemaname); + mainform->GetStatusBar()->SetStatusText(pastemsg, 1); + pastemsg = _("COPY TABLE:\n") + + srcdatabasename + wxT(".") + qtIdent(transfer->srcschemaname) + wxT(".") + + item.Getsrctable()->GetQuotedIdentifier() + + _("\nINTO:\n") + targetdatabasename + wxT(".") + qtIdent(transfer->targetschemaname); + + copyPasteThread *thread = new copyPasteThread(transfer); + wxThreadError rc = thread->Create(); + bool err = false; + if (rc != wxTHREAD_NO_ERROR) + { + lastResultError.formatted_msg.Format(_("Create thread error: rc=%d\n"), rc); + } + else + { + transfer->srcschema = findSchema(transfer->srcdatabase, transfer->srcschemaname); + if (!transfer->srcschema) + { + lastResultError.formatted_msg = pastemsg + wxT("\n") + _("Source schema disappeared"); + err = true; + } + if (!err) + { + transfer->targetschema = findSchema(transfer->targetdatabase, transfer->targetschemaname); + if (!transfer->targetschema) + { + lastResultError.formatted_msg = pastemsg + wxT("\n") + _("Target schema disappeared"); + err = true; + } + } + + if (!err && transfer->srcschema->GetId() != transfer->targetschema->GetId()) + { + err = tableExists(transfer->targetschema, item.Getsrctable()); + if (err) + { + lastResultError.formatted_msg = wxT("TABLE EXISTS"); + } + } + if (!err) + { + bool rc = transfer->targetconn->ExecuteVoid(wxT("BEGIN")); + if (!rc) + { + lastResultError = transfer->targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + } + else + { + transfer->createsql = transfer->table->GetSql(browser); + transfer->searchPath = transfer->targetconn->ExecuteScalar(wxT("SHOW search_path")); + transfer->createsql = wxT("SET search_path=") + transfer->targetschema->GetQuotedIdentifier() + + wxT(";\n") + transfer->createsql; + thread->Run(); + while (thread && thread->IsRunning()) + { + wxMilliSleep(10); + wxTheApp->Yield(true); + } + } + if (thread) + { + thread->Delete(); + delete thread; + } + } + } + + if (lastResultError.formatted_msg != wxT("TABLE EXISTS")) + { + if (lastResultError.formatted_msg.IsEmpty()) + { + newtargetconn->ExecuteVoid(wxT("COMMIT")); + lastResultError = newtargetconn->GetLastResultError(); + if (!lastResultError.formatted_msg.IsEmpty()) + { + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + msg = lastResultError.formatted_msg; + } + } + else + { + newtargetconn->ExecuteVoid(wxT("ROLLBACK")); + msg = lastResultError.formatted_msg; + } + } + if (!lastResultError.formatted_msg.IsEmpty()) + { + wxMessageBox(msg, + _("Cannot paste table:") + + targetdatabasename + wxT(".") + transfer->targetschemaname + + wxT(".") + item.Getsrctable()->GetQuotedIdentifier(), + wxOK | wxICON_ERROR); + continue; + } + copied++; + msg = wxString::Format(_("%d of %d TABLE(s) COPIED FROM %s TO %s"), copied, tableCopyPasteArray.Count(), + (srcdatabasename + wxT(".") + transfer->srcschemaname).c_str(), + (targetdatabasename + wxT(".") + transfer->targetschemaname).c_str()); + myLogNotice(wxT("CopyPaste=\"\n%s\""), msg.c_str()); + } + + if (newsourceconn->GetDbname() == newtargetconn->GetDbname() && + newsourceconn->GetHost() == newtargetconn->GetHost() && + newsourceconn->GetPort() == newtargetconn->GetPort()) + { + //same DB server + } + else + { + delete newtargetconn; + } + delete newsourceconn; + + pastemsg = wxString::Format(_("%d of %d TABLE(s) COPIED FROM %s TO %s"), copied, tableCopyPasteArray.Count(), + (srcdatabasename + wxT(".") + transfer->srcschemaname).c_str(), + (targetdatabasename + wxT(".") + transfer->targetschemaname).c_str()); + tableCopyPasteArray.Empty(); + + return copied; + } + + frmPasteObject::~frmPasteObject() + { + } + + pgTableCopyPaste::pgTableCopyPaste(pgTable *srctable, pgSchema *targetschema, wxString copysuffix1, + pgCollection *columns1, pgCollection *constraints1, pgCollection *tables1, pgCollection *sequences1) + { + this->srctable = srctable; + this->targetschema = targetschema; + this->copysuffix = copysuffix1; + this->columns = columns1; + this->constraints = constraints1; + this->tables = tables1; + this->sequences = sequences1; + } + + wxString pgTableCopyPaste::GetSequenceName(wxString definition, bool schemaonly) + { + //Supported formats: + //1. nextval('"""".id_gk_vrsta_naloga'::regclass) + //2. nextval(('ps_nalog_id_seq'::text)::regclass) + + //Schema and sequence name combinations: + //1. "schema.name"."seq.name" + //2. "schema.name".seq_name + //3 schema_name."seq.name" + //4 schema_name.seq_name + //5 "seq.name" + //6 seq_name + + wxString defval = definition; + wxString schemaname, seqname; + int pos = wxString(wxT("nextval(")).length(); + if (wxString(defval[pos]) == wxT("(")) + { + pos++; //skip left brace + } + pos++; //skip starting apostrophe + if (wxString(defval[pos]) == wxT("\"")) + { + //start parsing after the first " + wxString newdefval = defval.Mid(pos + 1); + //eliminate Ambiguous ". from the definition field + newdefval.Replace(wxT("\"\""), wxT(" "), true); + + int subseq = newdefval.Find(wxT("\".")); + if (subseq == wxNOT_FOUND) + { + //case 5 quoted sequence name without schema name + subseq = newdefval.rfind(wxT("\"'")); + if (subseq == wxNOT_FOUND) + return wxEmptyString; + schemaname = wxT("public"); + seqname = defval.Mid(pos, subseq + 1); + } + else + { + //quoted schema name + schemaname = defval.Mid(pos + 1, subseq); + schemaname.Replace(wxT("\"\""), wxT("\""), true); + schemaname = wxT("\"") + schemaname + wxT("\""); + subseq += 3; + int subseq1 = newdefval.Mid(subseq + 2).rfind(wxT("'")); + if (subseq1 == wxNOT_FOUND) + return wxEmptyString; + //case 1, 2 quoted schema name + seqname = defval.Mid(pos + subseq, subseq1 + 3); + } + if (wxString(seqname[0]) == wxT("\"")) + { + seqname = seqname.Mid(1, seqname.length() - 2); + seqname.Replace(wxT("\"\""), wxT("\""), true); + seqname.Replace(wxT("''"), wxT("'"), true); + seqname = wxT("\"") + seqname + wxT("\""); + } + } + else + { + //case 3, 4, 5, 6 not quoted schema name + wxString newdefval = defval.Mid(pos); + int subseq = newdefval.Find(wxT(".")); + if (subseq == wxNOT_FOUND) + { + //case 5, 6 sequence name without schema name + subseq = newdefval.rfind(wxT("'")); + if (subseq == wxNOT_FOUND) + return wxEmptyString; + schemaname = wxT("public"); + seqname = defval.Mid(pos, subseq); + } + else + { + //case 3, 4 not quoted schema name + schemaname = defval.Mid(pos, subseq); + subseq += 1; + int subseq1 = newdefval.Mid(subseq + 1).rfind(wxT("'")); + if (subseq1 == wxNOT_FOUND) + return wxEmptyString; + seqname = defval.Mid(pos + subseq, subseq1 + 1); + } + if (wxString(seqname[0]) == wxT("\"")) + { + seqname = seqname.Mid(1, seqname.length() - 2); + seqname.Replace(wxT("\"\""), wxT("\""), true); + seqname.Replace(wxT("''"), wxT("'"), true); + seqname = wxT("\"") + seqname + wxT("\""); + } + } + + return (schemaonly) ? schemaname : seqname; + } + + wxString pgTableCopyPaste::GetSql(ctlTree *browser) + { + wxString colDetails, conDetails; + wxString prevComment; + wxString cols_sql; + wxString sql; + wxString sqlcreate; + wxString sqlsequence = wxT("\n"); + wxString columnPrivileges; + wxString newtablename; + wxString qtfullident; + wxString qtfullidentsrc = srctable->GetQuotedFullIdentifier(); + bool sameschema = (srctable->GetSchema()->GetId() == targetschema->GetId()); + + if (sameschema) + { + newtablename = srctable->GetIdentifier() + copysuffix; + if (!tables) + return sql; + pgObject *obj; + pgTable *srctable; + bool fnd = false; + treeObjectIterator colIt(browser, tables); + while ((obj = colIt.GetNextObject()) != 0) { + srctable = (pgTable *)obj; + if (srctable->GetIdentifier() == newtablename) + { + fnd = true; + break; + } + } + if (!fnd) + { + qtfullident = targetschema->GetQuotedPrefix() + qtIdent(newtablename); + } + } + else + { + qtfullident = targetschema->GetQuotedPrefix() + srctable->GetQuotedIdentifier(); + } + + sqlcreate = wxT("-- Table: ") + qtfullident + wxT("\n\n") + + wxT("-- DROP TABLE ") + qtfullident + wxT(";"); + sql = wxT("\n\nCREATE "); + if (srctable->GetUnlogged()) + sql += wxT("UNLOGGED "); + sql += wxT("TABLE ") + qtfullident; + + // of type (9.0 material) + if (srctable->GetOfTypeOid() > 0) + sql += wxT("\nOF ") + qtIdent(srctable->GetOfType()); + + // Get a count of the constraints. + int consCount = 0; + if (constraints) + consCount = browser->GetChildrenCount(constraints->GetId()); + + // Get the columns + if (columns) + { + treeObjectIterator colIt1(browser, columns); + treeObjectIterator colIt2(browser, columns); + + int lastRealCol = 0; + int currentCol = 0; + pgColumn *column; + + // Iterate the columns to find the last 'real' one + while ((column = (pgColumn *) colIt1.GetNextObject()) != 0) + { + currentCol++; + + if (column->GetInheritedCount() == 0) + lastRealCol = currentCol; + } + + // Now build the actual column list + int colCount = 0; + while ((column = (pgColumn *) colIt2.GetNextObject()) != 0) + { + if (column->GetColNumber() > 0) + { + if (colCount) + { + // Only add a comma if this isn't the last 'real' column, or if there are constraints + if (colCount != lastRealCol || consCount) + cols_sql += wxT(","); + if (!prevComment.IsEmpty()) + cols_sql += wxT(" -- ") + firstLineOnly(prevComment); + + cols_sql += wxT("\n"); + } + + if (column->GetInheritedCount() > 0) + { + if (!column->GetIsLocal()) + { + cols_sql += wxString::Format(wxT("-- %s "), _("Inherited")) + + wxT("from table ") + column->GetInheritedTableName() + wxT(":"); + } + } + + if (srctable->GetOfTypeOid() > 0) + { + if (column->GetDefinition().Length() == 0) + { + cols_sql += wxString::Format(wxT("-- %s "), _("Inherited")) + + wxT("from type ") + srctable->GetOfType() + wxT(": ") + + column->GetQuotedIdentifier(); + } + else + { + cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" WITH OPTIONS ") + + column->GetDefinition(); + } + } + else + { + wxString coldef = column->GetDefault(); + wxString colseq = column->GetSerialSequence(); + if (colseq.IsEmpty() && coldef.StartsWith(wxT("nextval("))) + { + wxString newseqname, origseqname; + bool fnd = false; + wxString seqname = GetSequenceName(coldef, false); + if (!seqname.IsEmpty()) + { + if (srctable->GetSchema()->GetId() == targetschema->GetId()) + { + if (!sequences) + return sql; + pgObject *obj; + pgSequence *srcsequence = 0, *tmpsequence; + bool fnd = false; + newseqname = origseqname = seqname; + if (seqname.StartsWith(wxT("\""))) + { + newseqname = seqname.Mid(1, seqname.length() - 2); + origseqname = seqname.Mid(1, seqname.length() - 2); + } + newseqname += copysuffix; + treeObjectIterator colIt(browser, sequences); + while ((obj = colIt.GetNextObject()) != 0) { + tmpsequence = (pgSequence *)obj; + if (tmpsequence->GetIdentifier() == newseqname) + { + fnd = true; + break; + } + if (tmpsequence->GetIdentifier() == origseqname) + { + srcsequence = tmpsequence; + } + } + if (fnd) + return sql; + pgSequenceCopyPaste seqcp(srcsequence, targetschema, copysuffix); + sqlsequence += seqcp.GetSql(browser); + wxString schseqname = GetSequenceName(coldef, true); + if (schseqname.StartsWith(wxT("\""))) + { + schseqname = schseqname.Mid(1, schseqname.length() - 2); + } + wxString coldef = column->GetDefinition(); + newseqname = qtIdent(newseqname); + if (newseqname.StartsWith(wxT("\""))) + { + newseqname = newseqname.Mid(1, newseqname.length() - 2); + } + wxString qtfullidentseq = targetschema->GetQuotedPrefix() + qtIdent(newseqname); + qtfullidentseq.Replace(wxT("'"), wxT("''")); + origseqname.Replace(wxT("'"), wxT("''")); + coldef.Replace(qtIdent(schseqname) + wxT(".") + qtIdent(origseqname), qtfullidentseq, true); + cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" ") + coldef; + } + else + { + if (!tables) + return sql; + pgObject *obj; + pgSequence *srcsequence; + wxString seqname1 = seqname; + if (seqname1.StartsWith(wxT("\""))) + { + seqname1 = seqname1.Mid(1, seqname1.length() - 2); + } + treeObjectIterator colIt(browser, sequences); + while ((obj = colIt.GetNextObject()) != 0) { + srcsequence = (pgSequence *)obj; + if (srcsequence->GetIdentifier() == seqname1) + { + fnd = true; + break; + } + } + if (!fnd) + return sql; + pgSequenceCopyPaste seqcp(srcsequence, targetschema, copysuffix); + sqlsequence += seqcp.GetSql(browser); + wxString schseqname = GetSequenceName(coldef, true); + if (schseqname.StartsWith(wxT("\""))) + { + schseqname = schseqname.Mid(1, schseqname.length() - 2); + } + wxString coldef = column->GetDefinition(); + wxString qtfullidentseq = targetschema->GetQuotedPrefix() + qtIdent(seqname1); + qtfullidentseq.Replace(wxT("'"), wxT("''")); + seqname1.Replace(wxT("'"), wxT("''")); + coldef.Replace(qtIdent(schseqname) + wxT(".") + qtIdent(seqname1), qtfullidentseq, true); + cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" ") + coldef; + } + } + } + else + cols_sql += wxT(" ") + column->GetQuotedIdentifier() + wxT(" ") + + column->GetDefinition(); + } + + prevComment = column->GetComment(); + + // Whilst we are looping round the columns, grab their comments as well. + wxString comment = column->GetCommentSql(); + comment.Replace(qtfullidentsrc, qtfullident, false); + colDetails += comment; + if (colDetails.Length() > 0) + if (colDetails.Last() != '\n') + colDetails += wxT("\n"); + colDetails += column->GetStorageSql(); + if (colDetails.Length() > 0) + if (colDetails.Last() != '\n') + colDetails += wxT("\n"); + colDetails += column->GetAttstattargetSql(); + if (colDetails.Length() > 0) + if (colDetails.Last() != '\n') + colDetails += wxT("\n"); + + colCount++; + columnPrivileges += column->GetPrivileges(); + } + } + } + + // Now iterate the constraints + if (constraints) + { + wxString constraintname; + treeObjectIterator consIt(browser, constraints); + + pgObject *data; + + while ((data = consIt.GetNextObject()) != 0) + { + cols_sql += wxT(","); + + if (!prevComment.IsEmpty()) + cols_sql += wxT(" -- ") + firstLineOnly(prevComment); + + constraintname = data->GetQuotedIdentifier(); + if (sameschema) + { + if (constraintname.StartsWith(wxT("\""))) + { + constraintname = constraintname.Mid(1, constraintname.length() - 1) + copysuffix; + constraintname = wxT("\"") + constraintname + wxT("\""); + } + else + constraintname += copysuffix; + } + cols_sql += wxT("\n CONSTRAINT ") + constraintname + + wxT(" ") + data->GetTypeName().Upper() + + wxT(" "); + + prevComment = data->GetComment(); + if (!data->GetComment().IsEmpty()) + conDetails += wxT("COMMENT ON CONSTRAINT ") + constraintname + + wxT(" ON ") + srctable->GetQuotedFullIdentifier() + + wxT(" IS ") + srctable->qtDbString(data->GetComment()) + wxT(";\n"); + + switch (data->GetMetaType()) + { + case PGM_PRIMARYKEY: + case PGM_UNIQUE: + case PGM_EXCLUDE: + cols_sql += ((pgIndexConstraint *) data)->GetDefinition(); + break; + case PGM_FOREIGNKEY: + cols_sql += ((pgForeignKey *) data)->GetDefinition(); + break; + case PGM_CHECK: + cols_sql += wxT("(") + ((pgCheck *) data)->GetDefinition() + wxT(")"); + break; + } + } + } + if (!prevComment.IsEmpty()) + cols_sql += wxT(" -- ") + firstLineOnly(prevComment); + + sql += wxT("\n(\n") + cols_sql + wxT("\n)"); + + if (srctable->GetInheritedTableCount()) + { + sql += wxT("\nINHERITS (") + srctable->GetQuotedInheritedTables() + wxT(")"); + } + + if (srctable->GetConnection()->BackendMinimumVersion(8, 2)) + { + sql += wxT("\nWITH ("); + if (srctable->GetFillFactor().Length() > 0) + sql += wxT("\n FILLFACTOR=") + srctable->GetFillFactor() + wxT(", "); + if (srctable->GetAppendOnly().Length() > 0) + sql += wxT("APPENDONLY=") + srctable->GetAppendOnly() + wxT(", "); + if (srctable->GetCompressLevel().Length() > 0) + sql += wxT("COMPRESSLEVEL=") + srctable->GetCompressLevel() + wxT(", "); + if (srctable->GetOrientation().Length() > 0) + sql += wxT("ORIENTATION=") + srctable->GetOrientation() + wxT(", "); + if (srctable->GetCompressType().Length() > 0) + sql += wxT("COMPRESSTYPE=") + srctable->GetCompressType() + wxT(", "); + if (srctable->GetBlocksize().Length() > 0) + sql += wxT("BLOCKSIZE=") + srctable->GetBlocksize() + wxT(", "); + if (srctable->GetChecksum().Length() > 0) + sql += wxT("CHECKSUM=") + srctable->GetChecksum() + wxT(", "); + if (srctable->GetHasOids()) + sql += wxT("\n OIDS=TRUE"); + else + sql += wxT("\n OIDS=FALSE"); + if (srctable->GetConnection()->BackendMinimumVersion(8, 4)) { + if (srctable->GetCustomAutoVacuumEnabled()) { + if (srctable->GetAutoVacuumEnabled() == 1) + sql += wxT(",\n autovacuum_enabled=true"); + else if (srctable->GetCustomAutoVacuumEnabled() == 0) + sql += wxT(",\n autovacuum_enabled=false"); + if (!srctable->GetAutoVacuumVacuumThreshold().IsEmpty()) + { + sql += wxT(",\n autovacuum_vacuum_threshold=") + srctable->GetAutoVacuumVacuumThreshold(); + } + if (!srctable->GetAutoVacuumVacuumScaleFactor().IsEmpty()) + { + sql += wxT(",\n autovacuum_vacuum_scale_factor=") + srctable->GetAutoVacuumVacuumScaleFactor(); + } + if (!srctable->GetAutoVacuumAnalyzeThreshold().IsEmpty()) + { + sql += wxT(",\n autovacuum_analyze_threshold=") + srctable->GetAutoVacuumAnalyzeThreshold(); + } + if (!srctable->GetAutoVacuumAnalyzeScaleFactor().IsEmpty()) + { + sql += wxT(",\n autovacuum_analyze_scale_factor=") + srctable->GetAutoVacuumAnalyzeScaleFactor(); + } + if (!srctable->GetAutoVacuumVacuumCostDelay().IsEmpty()) + { + sql += wxT(",\n autovacuum_vacuum_cost_delay=") + srctable->GetAutoVacuumVacuumCostDelay(); + } + if (!srctable->GetAutoVacuumVacuumCostLimit().IsEmpty()) + { + sql += wxT(",\n autovacuum_vacuum_cost_limit=") + srctable->GetAutoVacuumVacuumCostLimit(); + } + if (!srctable->GetAutoVacuumFreezeMinAge().IsEmpty()) + { + sql += wxT(",\n autovacuum_freeze_min_age=") + srctable->GetAutoVacuumFreezeMinAge(); + } + if (!srctable->GetAutoVacuumFreezeMaxAge().IsEmpty()) + { + sql += wxT(",\n autovacuum_freeze_max_age=") + srctable->GetAutoVacuumFreezeMaxAge(); + } + if (!srctable->GetAutoVacuumFreezeTableAge().IsEmpty()) + { + sql += wxT(",\n autovacuum_freeze_table_age=") + srctable->GetAutoVacuumFreezeTableAge(); + } + } + if (srctable->GetHasToastTable() && srctable->GetToastCustomAutoVacuumEnabled()) + { + if (srctable->GetToastAutoVacuumEnabled() == 1) + sql += wxT(",\n toast.autovacuum_enabled=true"); + else if (srctable->GetToastAutoVacuumEnabled() == 0) + sql += wxT(",\n toast.autovacuum_enabled=false"); + if (!srctable->GetToastAutoVacuumVacuumThreshold().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_vacuum_threshold=") + srctable->GetToastAutoVacuumVacuumThreshold(); + } + if (!srctable->GetToastAutoVacuumVacuumScaleFactor().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_vacuum_scale_factor=") + srctable->GetToastAutoVacuumVacuumScaleFactor(); + } + if (!srctable->GetToastAutoVacuumAnalyzeThreshold().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_analyze_threshold=") + srctable->GetToastAutoVacuumAnalyzeThreshold(); + } + if (!srctable->GetToastAutoVacuumAnalyzeScaleFactor().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_analyze_scale_factor=") + srctable->GetToastAutoVacuumAnalyzeScaleFactor(); + } + if (!srctable->GetToastAutoVacuumVacuumCostDelay().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_vacuum_cost_delay=") + srctable->GetToastAutoVacuumVacuumCostDelay(); + } + if (!srctable->GetToastAutoVacuumVacuumCostLimit().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_vacuum_cost_limit=") + srctable->GetToastAutoVacuumVacuumCostLimit(); + } + if (!srctable->GetToastAutoVacuumFreezeMinAge().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_freeze_min_age=") + srctable->GetToastAutoVacuumFreezeMinAge(); + } + if (!srctable->GetToastAutoVacuumFreezeMaxAge().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_freeze_max_age=") + srctable->GetToastAutoVacuumFreezeMaxAge(); + } + if (!srctable->GetToastAutoVacuumFreezeTableAge().IsEmpty()) + { + sql += wxT(",\n toast.autovacuum_freeze_table_age=") + srctable->GetToastAutoVacuumFreezeTableAge(); + } + } + } + sql += wxT("\n)"); + } + else + { + if (srctable->GetHasOids()) + sql += wxT("\nWITH OIDS"); + else + sql += wxT("\nWITHOUT OIDS"); + } + + if (srctable->GetConnection()->BackendMinimumVersion(8, 0) && + srctable->GetTablespace() != srctable->GetDatabase()->GetDefaultTablespace()) + sql += wxT("\nTABLESPACE ") + qtIdent(srctable->GetTablespace()); + + if (srctable->GetConnection()->GetIsGreenplum()) + { + // Add Greenplum DISTRIBUTED BY + if (srctable->GetDistributionIsRandom()) + { + sql += wxT("\nDISTRIBUTED RANDOMLY"); + } + else if (srctable->GetDistributionColNumbers().Length() == 0) + { + // catalog table or other non-distributed table + } + else + { + // convert list of columns numbers to column names + wxStringTokenizer collist(srctable->GetDistributionColNumbers(), wxT(",")); + wxString cn; + wxString distributionColumns; + while (collist.HasMoreTokens()) + { + cn = collist.GetNextToken(); + pgSet *set = srctable->ExecuteSet( + wxT("SELECT attname\n") + wxT(" FROM pg_attribute\n") + wxT(" WHERE attrelid=") + srctable->GetOidStr() + wxT(" AND attnum IN (") + cn + wxT(")")); + if (set) + { + if (!distributionColumns.IsNull()) + { + distributionColumns += wxT(", "); + } + distributionColumns += qtIdent(set->GetVal(0)); + delete set; + } + } + + sql += wxT("\nDISTRIBUTED BY ("); + sql += distributionColumns; + + sql += wxT(")"); + } + + if (srctable->GetIsPartitioned()) + if (srctable->GetConnection()->BackendMinimumVersion(8, 2, 9) && srctable->GetConnection()->GetIsGreenplum()) + if (srctable->GetPartitionDef().Length() == 0) + { + wxString query = wxT("SELECT pg_get_partition_def("); + query += srctable->GetOidStr(); + query += wxT(", true) "); + wxString partition_def = srctable->GetDatabase()->ExecuteScalar(query); + srctable->iSetPartitionDef(partition_def); + // pg_get_partition_def() doesn't work on partitions + if (srctable->GetPartitionDef().Length() == 0) + srctable->iSetPartitionDef(wxT("-- This partition has subpartitions")); + } + if (srctable->GetPartitionDef().Length() > 0) + sql += wxT("\n") + srctable->GetPartitionDef() + wxT("\n"); + } + + + sql += wxT(";\n") + + srctable->GetOwnerSql(7, 3, wxT("TABLE ") + qtfullident); + + if (srctable->GetConnection()->BackendMinimumVersion(8, 2)) + sql += srctable->GetGrant(wxT("arwdxt"), wxT("TABLE ") + qtfullident); + else + sql += srctable->GetGrant(wxT("arwdRxt"), wxT("TABLE ") + qtfullident); + + wxString comment = srctable->GetCommentSql(); + comment.Replace(qtfullidentsrc, qtfullident); + sql += comment; + + // Column/constraint comments + if (!colDetails.IsEmpty()) + sql += colDetails + wxT("\n"); + + if (!conDetails.IsEmpty()) + sql += conDetails + wxT("\n"); + + if (!columnPrivileges.IsEmpty()) { + sql += columnPrivileges + wxT("\n"); + } + + return sqlcreate + sqlsequence + sql; + } + + pgTableCopyPaste::~pgTableCopyPaste() + { + } + + pgSequenceCopyPaste::pgSequenceCopyPaste(pgSequence *srcseq, pgSchema *targetschema, wxString copysuffix1) + { + this->srcseq = srcseq; + this->targetschema = targetschema; + this->copysuffix = copysuffix1; + } + + wxString pgSequenceCopyPaste::GetSql(ctlTree *browser) + { + wxString sql = wxEmptyString; + wxString qtfullident; + wxString newtablename; + bool sameschema = (srcseq->GetSchema()->GetId() == targetschema->GetId()); + if (sameschema) + { + newtablename = srcseq->GetIdentifier() + copysuffix; + qtfullident = targetschema->GetQuotedPrefix() + qtIdent(newtablename); + } + else + qtfullident = targetschema->GetQuotedPrefix() + srcseq->GetQuotedIdentifier();; + wxString qtfullidentsrc = srcseq->GetQuotedFullIdentifier(); + + srcseq->UpdateValues(); + sql = wxT("-- Sequence: ") + qtfullident + wxT("\n\n") + + wxT("-- DROP SEQUENCE ") + qtfullident + wxT(";") + + wxT("\n\nCREATE SEQUENCE ") + qtfullident + + wxT("\n INCREMENT ") + srcseq->GetIncrement().ToString() + + wxT("\n MINVALUE ") + srcseq->GetMinValue().ToString() + + wxT("\n MAXVALUE ") + srcseq->GetMaxValue().ToString() + + wxT("\n START ") + srcseq->GetLastValue().ToString() + + wxT("\n CACHE ") + srcseq->GetCacheValue().ToString(); + if (srcseq->GetCycled()) + sql += wxT("\n CYCLE"); + sql += wxT(";\n") + + srcseq->GetOwnerSql(7, 3, wxT("TABLE ") + qtfullident); + + if (!srcseq->GetConnection()->BackendMinimumVersion(8, 2)) + sql += srcseq->GetGrant(wxT("arwdRxt"), wxT("TABLE ") + qtfullident); + else + sql += srcseq->GetGrant(wxT("rwU"), wxT("TABLE ") + qtfullident); + + wxString comment = srcseq->GetCommentSql(); + comment.Replace(qtfullidentsrc, qtfullident); + sql += comment; + + return sql; + } + + pgSequenceCopyPaste::~pgSequenceCopyPaste() + { + } *** a/pgadmin/frm/module.mk --- b/pgadmin/frm/module.mk *************** *** 33,39 **** pgadmin3_SOURCES += \ $(srcdir)/frm/frmRestore.cpp \ $(srcdir)/frm/frmSplash.cpp \ $(srcdir)/frm/frmStatus.cpp \ ! $(srcdir)/frm/plugins.cpp EXTRA_DIST += \ $(srcdir)/frm/module.mk --- 33,40 ---- $(srcdir)/frm/frmRestore.cpp \ $(srcdir)/frm/frmSplash.cpp \ $(srcdir)/frm/frmStatus.cpp \ ! $(srcdir)/frm/plugins.cpp \ ! $(srcdir)/frm/frmPasteObject.cpp EXTRA_DIST += \ $(srcdir)/frm/module.mk *** a/pgadmin/include/db/pgConn.h --- b/pgadmin/include/db/pgConn.h *************** *** 124,129 **** public: --- 124,130 ---- void Close(); bool Reconnect(); bool ExecuteVoid(const wxString &sql, bool reportError = true); + PGresult * ExecuteOptionalResult(const wxString &sql, bool reportError = true); wxString ExecuteScalar(const wxString &sql); pgSet *ExecuteSet(const wxString &sql); wxString GetHostAddr() const *** a/pgadmin/include/frm/frmMain.h --- b/pgadmin/include/frm/frmMain.h *************** *** 146,151 **** public: --- 146,159 ---- return pluginsMenu; } + pgObject *GetCopytObject() { + return copytObject; + } + + void SetCopytObject(pgObject *obj) { + copytObject = obj; + } + wxString GetCurrentNodePath(); bool SetCurrentNode(wxTreeItemId node, const wxString &path); *************** *** 176,181 **** private: --- 184,190 ---- actionFactory *reportMenuFactory; actionFactory *scriptingMenuFactory; actionFactory *viewdataMenuFactory; + pgObject *copytObject; wxStopWatch stopwatch; wxString timermsg; *************** *** 337,340 **** private: --- 346,365 ---- bool enableButton; }; + class copyObjectFactory : public contextActionFactory + { + public: + copyObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar); + wxWindow *StartDialog(frmMain *form, pgObject *obj); + bool CheckEnable(pgObject *obj); + }; + + class pasteObjectFactory : public contextActionFactory + { + public: + pasteObjectFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar); + wxWindow *StartDialog(frmMain *form, pgObject *obj); + bool CheckEnable(pgObject *obj); + }; + #endif *** /dev/null --- b/pgadmin/include/frm/frmPasteObject.h *************** *** 0 **** --- 1,125 ---- + ////////////////////////////////////////////////////////////////////////// + // + // pgAdmin III - PostgreSQL Tools + // + // Copyright (C) 2002 - 2011, The pgAdmin Development Team + // This software is released under the PostgreSQL Licence + // + // frmPasteObject.h - Copy/Paste object functions + // + ////////////////////////////////////////////////////////////////////////// + + #ifndef FRMPASTEOBJECT_H + #define FRMPASTEOBJECT_H + + #include + #include + + #include "pgAdmin3.h" + #include "frm/frmMain.h" + #include "schema/pgObject.h" + #include "schema/pgSchema.h" + #include "schema/pgTable.h" + #include "schema/pgSequence.h" + + class frmPasteObject; + class pgTableCopyPaste; + + struct transfer_tag + { + frmPasteObject *THIS; + pgDatabase *srcdatabase; + wxString srcschemaname; + pgSchema *srcschema; + pgDatabase *targetdatabase; + wxString targetschemaname; + pgSchema *targetschema; + pgTableCopyPaste *table; + wxString createsql; + wxString searchPath; + pgConn *sourceconn; + pgConn *targetconn; + }; + + class copyPasteThread : public wxThread + { + private: + struct transfer_tag *transfer; + public: + copyPasteThread(struct transfer_tag *transfer1) + : wxThread(wxTHREAD_JOINABLE), transfer(transfer1) + {} + virtual void *Entry(); + }; + + class pgTableCopyPaste + { + public: + pgTableCopyPaste(pgTable *srctable, pgSchema *targetschema, wxString copysuffix, + pgCollection *columns1, pgCollection *constraints1, pgCollection *tables1, pgCollection *sequences1); + virtual ~pgTableCopyPaste(); + virtual wxString GetSql(ctlTree *browser); + pgTable *Getsrctable() { return srctable; } + pgSchema *Gettargetschema() { return targetschema; } + wxString copysuffix; + private: + pgTable *srctable; + pgSchema *targetschema; + pgCollection *columns; + pgCollection *constraints; + pgCollection *tables; + pgCollection *sequences; + wxString GetSequenceName(wxString definition, bool schemaonly = false); + }; + + class frmPasteObject + { + public: + frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj); + int process(); + virtual ~frmPasteObject(); + void copyTable(struct transfer_tag *transfer); + pgSchema *findSchema(pgDatabase *database, wxString schemaname); + frmMain *Getmainform() + { + return mainform; + } + static void setActive(bool active) + { + frmPasteObject::active = active; + } + static bool isActive() { + return frmPasteObject::active; + } + wxString getPastemsg() const; + private: + bool tableExists(pgSchema *srcschema, pgTable *table1); + void GetLastResultError(pgConn *conn, PGresult *res, const wxString &msg = wxT("")); + void handleCopyOut(pgConn *conn, wxFile & copystream); + void handleCopyIn(pgConn *conn, wxFile & copystream, bool isbinary); + void do_copy(pgConn *conn, wxString & sql, wxFile & copystream); + void myLogNotice(const wxChar *szFormat, ...); + + frmMain *mainform; + pgObject *sourceobj; + pgObject *targetobj; + pgError lastResultError; + wxString pastemsg; + static bool active; + }; + + class pgSequenceCopyPaste + { + public: + pgSequenceCopyPaste(pgSequence *srcseq, pgSchema *targetschema, wxString copysuffix); + virtual ~pgSequenceCopyPaste(); + virtual wxString GetSql(ctlTree *browser); + private: + pgSequence *srcseq; + pgSchema *targetschema; + wxString copysuffix; + }; + + WX_DECLARE_OBJARRAY(pgTableCopyPaste, pgTableCopyPasteArray); + #endif /* FRMPASTEOBJECT_H */ + *** a/pgadmin/include/frm/module.mk --- b/pgadmin/include/frm/module.mk *************** *** 1,38 **** ! ####################################################################### ! # ! # pgAdmin III - PostgreSQL Tools ! # ! # Copyright (C) 2002 - 2011, The pgAdmin Development Team ! # This software is released under the PostgreSQL Licence ! # ! # module.mk - pgadmin/include/frm/ Makefile fragment ! # ! ####################################################################### ! ! pgadmin3_SOURCES += \ ! $(srcdir)/include/frm/frmAbout.h \ ! $(srcdir)/include/frm/frmBackup.h \ ! $(srcdir)/include/frm/frmBackupGlobals.h \ ! $(srcdir)/include/frm/frmBackupServer.h \ ! $(srcdir)/include/frm/frmConfig.h \ ! $(srcdir)/include/frm/frmEditGrid.h \ ! $(srcdir)/include/frm/frmExport.h \ ! $(srcdir)/include/frm/frmGrantWizard.h \ ! $(srcdir)/include/frm/frmHbaConfig.h \ ! $(srcdir)/include/frm/frmHint.h \ ! $(srcdir)/include/frm/frmMain.h \ ! $(srcdir)/include/frm/frmMainConfig.h \ ! $(srcdir)/include/frm/frmMaintenance.h \ ! $(srcdir)/include/frm/frmOptions.h \ ! $(srcdir)/include/frm/frmPassword.h \ ! $(srcdir)/include/frm/frmPgpassConfig.h \ ! $(srcdir)/include/frm/frmQuery.h \ ! $(srcdir)/include/frm/frmReport.h \ ! $(srcdir)/include/frm/frmRestore.h \ ! $(srcdir)/include/frm/frmSplash.h \ ! $(srcdir)/include/frm/frmStatus.h \ ! $(srcdir)/include/frm/menu.h ! ! EXTRA_DIST += \ ! $(srcdir)/include/frm/module.mk ! --- 1,39 ---- ! ####################################################################### ! # ! # pgAdmin III - PostgreSQL Tools ! # ! # Copyright (C) 2002 - 2011, The pgAdmin Development Team ! # This software is released under the PostgreSQL Licence ! # ! # module.mk - pgadmin/include/frm/ Makefile fragment ! # ! ####################################################################### ! ! pgadmin3_SOURCES += \ ! $(srcdir)/include/frm/frmAbout.h \ ! $(srcdir)/include/frm/frmBackup.h \ ! $(srcdir)/include/frm/frmBackupGlobals.h \ ! $(srcdir)/include/frm/frmBackupServer.h \ ! $(srcdir)/include/frm/frmConfig.h \ ! $(srcdir)/include/frm/frmEditGrid.h \ ! $(srcdir)/include/frm/frmExport.h \ ! $(srcdir)/include/frm/frmGrantWizard.h \ ! $(srcdir)/include/frm/frmHbaConfig.h \ ! $(srcdir)/include/frm/frmHint.h \ ! $(srcdir)/include/frm/frmMain.h \ ! $(srcdir)/include/frm/frmMainConfig.h \ ! $(srcdir)/include/frm/frmMaintenance.h \ ! $(srcdir)/include/frm/frmOptions.h \ ! $(srcdir)/include/frm/frmPassword.h \ ! $(srcdir)/include/frm/frmPgpassConfig.h \ ! $(srcdir)/include/frm/frmQuery.h \ ! $(srcdir)/include/frm/frmReport.h \ ! $(srcdir)/include/frm/frmRestore.h \ ! $(srcdir)/include/frm/frmSplash.h \ ! $(srcdir)/include/frm/frmStatus.h \ ! $(srcdir)/include/frm/menu.h \ ! $(srcdir)/include/frm/frmPasteObject.h ! ! EXTRA_DIST += \ ! $(srcdir)/include/frm/module.mk ! *** a/pgadmin/include/schema/pgTable.h --- b/pgadmin/include/schema/pgTable.h *************** *** 117,122 **** public: --- 117,126 ---- distributionColNumbers = s; // for Greenplum if (s.Length() > 0) distributionIsRandom = false; } + bool GetDistributionIsRandom() const + { + return distributionIsRandom; // for Greenplum + } void iSetDistributionIsRandom() { distributionIsRandom = true; *** a/pgadmin/schema/pgColumn.cpp --- b/pgadmin/schema/pgColumn.cpp *************** *** 298,303 **** wxString pgColumn::GetDefinition() --- 298,304 ---- { wxString sql = wxEmptyString; wxString seqDefault1, seqDefault2; + bool sequence9 = (GetDatabase()->BackendMinimumVersion(9, 0)); if (table->GetOfTypeOid() == 0) sql += GetQuotedTypename(); *************** *** 324,330 **** wxString pgColumn::GetDefinition() if ((sql == wxT("integer") || sql == wxT("bigint") || sql == wxT("pg_catalog.integer") || sql == wxT("pg_catalog.bigint")) ! && (GetDefault() == seqDefault1 || GetDefault() == seqDefault2)) { if (sql.Right(6) == wxT("bigint")) sql = wxT("bigserial"); --- 325,332 ---- if ((sql == wxT("integer") || sql == wxT("bigint") || sql == wxT("pg_catalog.integer") || sql == wxT("pg_catalog.bigint")) ! && ((sequence9 && !GetSerialSequence().IsEmpty()) || ! (!sequence9 && (GetDefault() == seqDefault1 || GetDefault() == seqDefault2)))) { if (sql.Right(6) == wxT("bigint")) sql = wxT("bigserial");