*** 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,1464 ---- 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) + { + form->SetCopytObject(obj); + return 0; + } + + + bool copyObjectFactory::CheckEnable(pgObject *obj) + { + if (!obj) + 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) + { + pgObject *copyobj = form->GetCopytObject(); + if (copyobj) { + frmPasteObject *frm = new frmPasteObject(form, copyobj, obj); + frm->process(); + } + 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,958 ---- + ////////////////////////////////////////////////////////////////////////// + // + // 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 + + frmPasteObject::frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj) + { + this->mainform = form; + this->sourceobj= sourceobj; + this->targetobj= targetobj; + } + + wxArrayString frmPasteObject::getSchemaTables(pgSchema *srcschema) + { + wxArrayString objArray; + wxString srcname = srcschema->GetQuotedIdentifier(); + if (wxString(srcname[0]) == wxT("\"")) + { + srcname = srcname.Mid(1, srcname.length() - 2); + srcname.Replace(wxT("'"), wxT("''"), true); + } + + 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='") + srcname + 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; + } + + bool frmPasteObject::tableExists(pgSchema *srcschema, wxString quotedtablename) + { + wxString nspname = srcschema->GetQuotedIdentifier(); + if (wxString(nspname[0]) == wxT("\"")) + { + nspname.Replace(wxT("'"), wxT("''"), true); + nspname = nspname.Mid(1, nspname.length() - 2); + } + wxString relname = quotedtablename; + if (wxString(relname[0]) == wxT("\"")) + { + relname.Replace(wxT("'"), wxT("''"), true); + relname = relname.Mid(1, relname.length() - 2); + } + 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='") + nspname + wxT("' AND relname='") + relname + wxT("'"); + wxString result = srcschema->GetDatabase()->ExecuteScalar(query); + return !result.IsEmpty(); + } + + 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); + } + } + + wxArrayString frmPasteObject::stringToArray(wxString & src, wxString pattern) + { + wxArrayString resArray; + int pos, prevpos = 0; + + pos = src.find(pattern); + while (pos != wxNOT_FOUND) + { + wxString subs = src.Mid(prevpos, pos - prevpos); + if (!subs.IsEmpty()) + { + resArray.Add(subs); + } + resArray.Add(pattern); + prevpos = pos + pattern.length(); + pos = src.find(pattern, prevpos); + } + if (!resArray.IsEmpty()){ + wxString subs = src.Mid(prevpos); + if (!subs.IsEmpty()) + { + resArray.Add(subs); + } + } + + return resArray; + } + + void frmPasteObject::copyTable(ctlTree *browser, pgSchema *srcschema, pgSchema *targetschema, pgTable *table1) + { + lastResultError.formatted_msg = wxT(""); + + pgConn *sourceconn = srcschema->GetConnection(); + pgConn *targetconn = targetschema->GetConnection(); + bool rc; + + rc = tableExists(targetschema, table1->GetQuotedIdentifier()); + if (rc) + { + lastResultError.formatted_msg = wxT("TABLE EXISTS"); + return; + } + + rc = targetconn->ExecuteVoid(wxT("BEGIN")); + if (!rc) + { + lastResultError = targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + + table1->ShowTreeDetail(browser); + pgCollection *columns = table1->GetColumnCollection(browser); + treeObjectIterator colIt(browser, columns); + pgColumn *column; + wxString colName, defval, serialname; + while ((column = (pgColumn *)colIt.GetNextObject()) != 0) + { + column->ShowTreeDetail(browser); + if (column->GetColNumber() > 0) + { + colName = column->GetName(); + serialname = column->GetSerialSequence(); + defval = column->GetDefault(); + //uniqueid integer DEFAULT nextval(('ps_nalog_uniqueid_seq'::text)::regclass), + if (!defval.IsNull() && serialname.IsEmpty() && defval.StartsWith(wxT("nextval("))) + { + wxString oidstr; + wxString seqsch, seqname; + int pos = wxString(wxT("nextval(")).length(); + if (wxString(defval[pos]) == wxT("(")) + { + pos++; + } + pos++; + if (wxString(defval[pos]) == wxT("\"")) + { + wxString newdefval = defval.Mid(pos + 1); + newdefval.Replace(wxT("\"\""), wxT(" "), true); + + int subseq = newdefval.Find(wxT("\".")); + if (subseq == wxNOT_FOUND) + { + lastResultError.formatted_msg = _("Can't find schema sequence delimiter:") + defval; + return; + } + seqsch = defval.Mid(pos + 1, subseq); + seqsch.Replace(wxT("\"\""), wxT("\""), true); + seqsch = wxT("\"") + seqsch + wxT("\""); + subseq += 2; + int subseq1 = newdefval.Mid(subseq + 2).Find(wxT("'::text)")); + if (subseq1 == wxNOT_FOUND) + { + subseq1 = newdefval.Mid(subseq + 2).Find(wxT("'::regclass)")); + if (subseq1 == wxNOT_FOUND) + { + lastResultError.formatted_msg = _("Can't find sequence name:") + defval; + return; + } + } + seqname = defval.Mid(pos + subseq + 1, subseq1 + 2); + if (wxString(seqname[0]) == wxT("\"")) + { + seqname = seqname.Mid(1, seqname.length() - 2); + seqname.Replace(wxT("\"\""), wxT("\""), true); + seqname = wxT("\"") + seqname + wxT("\""); + } + } + else + { + bool nextval = false; + wxArrayString resarray = stringToArray(defval, wxT("nextval(")); + for(unsigned int j = 0; j < resarray.Count(); j++) + { + wxString part = resarray.Item(j); + if (part == wxT("nextval(")) + { + nextval = true; + continue; + } + if (!nextval) + { + continue; + } + nextval = false; + int subseq = part.Find('.'); + if (subseq == wxNOT_FOUND) + { + seqsch = wxT("public"); + seqname = part; + if (seqname.StartsWith(wxT("("))) + { + seqname = seqname.Mid(1); + } + if (seqname.StartsWith(wxT("'"))) + { + seqname = seqname.Mid(1); + } + int pos = seqname.find(wxT("'::text)")); + if (pos == wxNOT_FOUND) + { + pos = seqname.find(wxT("'::regclass)")); + if (pos == wxNOT_FOUND) + { + lastResultError.formatted_msg = _("Can't find sequence name delimiter::regclass)") + defval; + return; + } + } + seqname = seqname.Mid(0, pos); + } else + { + if (part.StartsWith(wxT("("))) + { + part = part.Mid(1); + } + seqsch = part.Mid(1, subseq - 1); + seqname = part.Mid(subseq + 1); + int subseq = seqname.Find(wxT("'")); + if (subseq == wxNOT_FOUND) + { + lastResultError.formatted_msg = _("Can't find sequence name delimiter:") + defval; + return; + } + seqname = seqname.Mid(0, subseq); + } + break; + } + } + if (seqsch == srcschema->GetIdentifier()) + { + oidstr = srcschema->GetOidStr(); + } + else + { + wxString seqsch1 = seqsch; + if (wxString(seqsch[0]) == wxT("\"")) + { + seqsch1 = seqsch.Mid(1, seqsch.length() - 2); + } + pgSet *seqcat = srcschema->GetDatabase()->ExecuteSet( + wxT("SELECT oid\n") + wxT(" FROM pg_namespace\n") + wxT(" WHERE nspname = '") + seqsch1 + wxT("'\n")); + if (seqcat) + { + OID nspoid = seqcat->GetOid(wxT("oid")); + oidstr = NumToStr(nspoid) + wxT("::oid"); + delete seqcat; + } else + { + lastResultError = sourceconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + + 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 = ") + oidstr + + wxT(" AND relname = '") + seqname + wxT("'\n")); + if (seqcat) + { + comment = seqcat->GetVal(wxT("description")); + owner = seqcat->GetVal(wxT("seqowner")); + acl = seqcat->GetVal(wxT("relacl")); + delete seqcat; + } else + { + lastResultError = sourceconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + 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 + { + lastResultError = sourceconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + + wxString QuotedFullIdentifier = targetschema->GetQuotedIdentifier() + wxT("."); + QuotedFullIdentifier += seqname; + wxString sql = wxT("-- Sequence: ") + QuotedFullIdentifier + wxT("\n\n") + + wxT("-- DROP SEQUENCE ") + QuotedFullIdentifier + wxT(";") + + wxT("\n\nCREATE SEQUENCE ") + QuotedFullIdentifier + + 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 ") + + QuotedFullIdentifier + wxT(" OWNER TO ") + qtIdent(owner) + wxT(";\n"); + if (!seq.GetConnection()->BackendMinimumVersion(8, 2)) + sql += seq.GetGrant(wxT("arwdRxt"), wxT("TABLE ") + QuotedFullIdentifier); + else + sql += seq.GetGrant(wxT("rwU"), wxT("TABLE ") + QuotedFullIdentifier); + wxString cmt; + if (!comment.IsNull()) + { + cmt = wxT("COMMENT ON SEQUENCE ") + QuotedFullIdentifier + + wxT(" IS ") + targetschema->qtDbString(comment) + wxT(";\n"); + sql += cmt; + } + + rc = targetconn->ExecuteVoid(sql, false); + if (!rc) + { + lastResultError = targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + } + } + + { + wxString createsql = table1->GetSql(browser); + createsql.Replace(srcschema->GetQuotedIdentifier() + wxT("."), targetschema->GetQuotedIdentifier() + wxT("."), true); + + wxString createsql1; + bool nextval = false; + wxArrayString resarray = stringToArray(createsql, wxT("nextval(")); + if (resarray.Count() > 1) + { + for(unsigned int j = 0; j < resarray.Count(); j++) + { + wxString token = resarray.Item(j); + if (token == wxT("nextval(")) + { + createsql1 += token; + nextval = true; + } + else + { + if (!nextval) + { + createsql1 += token; + continue; + } + else + { + nextval = false; + int pos = token.find(wxT("'::text)")); + if (pos == wxNOT_FOUND) + { + pos = token.find(wxT("'::regclass)")); + if (pos == wxNOT_FOUND) + { + createsql1 += token; + continue; + } + } + wxString token1 = token.Mid(0, pos); + bool haspar = false; + if (token1.StartsWith(wxT("("))) + { + token1 = token1.Mid(1); + haspar = true; + } + token1 = token1.Mid(1); + wxString token2 = token1; + token2.Replace(wxT("\"\""), wxT(" "), true); + int subseq = token2.Find(wxT("\".")); + if (subseq == wxNOT_FOUND) + { + int subseq = token2.Find(wxT(".")); + if (subseq == wxNOT_FOUND) + { + token1 = targetschema->GetQuotedIdentifier() + wxT(".") + token1; + } + else + { + token1 = targetschema->GetQuotedIdentifier() + wxT(".") + token1.Mid(subseq + 1); + } + } + else + { + token1 = targetschema->GetQuotedIdentifier() + wxT(".") + token1.Mid(subseq + 2); + } + token1.Replace(wxT("'"), wxT("''"), true); + token1 = wxT("'") + token1; + if (haspar) + { + token1 = wxT("(") + token1; + } + token = token1 + token.Mid(pos); + createsql1 += token; + } + } + } + createsql = createsql1; + } + + wxString searchPath = targetconn->ExecuteScalar(wxT("SHOW search_path")); + createsql = wxT("SET search_path=") + targetschema->GetQuotedIdentifier() + wxT(";") + createsql; + rc = targetconn->ExecuteVoid(createsql, false); + if (!rc) + { + lastResultError = targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + if (!searchPath.IsEmpty()) + { + rc = targetconn->ExecuteVoid(wxT("SET search_path=") + searchPath, false); + if (!rc) + { + lastResultError = targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + + if (sourceconn->GetDbname() == targetconn->GetDbname() && sourceconn->GetHost() == 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) + { + lastResultError = targetconn->GetLastResultError(); + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + return; + } + } + else + { + 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 ") + + srcschema->GetQuotedPrefix() + table1->GetQuotedIdentifier() + + wxT(" TO STDOUT"); + do_copy(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 ") + + targetschema->GetQuotedPrefix() + table1->GetQuotedIdentifier() + + wxT(" FROM STDIN"); + do_copy(targetconn, copysql, tmpFile); + } + tmpFile.Close(); + wxRemoveFile(tmpFilename); + } + } + } + + void frmPasteObject::process() + { + if (!sourceobj || !targetobj) + { + return; + } + + wxArrayString srcObjArray; + 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; + } + } + 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; + } + 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; + } + } + + 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; + } + + if (srcschema) + { + srcObjArray = getSchemaTables(srcschema); + } + else + { + srcObjArray.Add(table->GetIdentifier()); + srcschema = table->GetSchema(); + } + + if (srcschema->GetQuotedIdentifier() == targetschema->GetQuotedIdentifier()) + { + wxMessageBox(_("Source and target schema should be different schema for paste table(s) operation !")); + return; + } + + srcschema->ShowTreeDetail(mainform->GetBrowser()); + + wxString msg; + int copied = 0; + for(unsigned int i = 0; i < srcObjArray.Count(); i++) + { + pastemsg = _("COPY TABLE:") + + srcschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + srcschema->GetQuotedIdentifier() + wxT(".") + + srcObjArray.Item(i) + + _(" INTO:") + targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + targetschema->GetQuotedIdentifier(); + mainform->GetStatusBar()->SetStatusText(pastemsg, 1); + pastemsg = _("COPY TABLE:\n") + + srcschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + srcschema->GetQuotedIdentifier() + wxT(".") + + srcObjArray.Item(i) + + _("\nINTO:\n") + targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + targetschema->GetQuotedIdentifier(); + + 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()->GetQuotedIdentifier() + wxT(".") + srcschema->GetQuotedIdentifier() + wxT(".") + + srcObjArray.Item(i); + continue; + } + else + { + copyTable(mainform->GetBrowser(), srcschema, targetschema, table1); + if (lastResultError.formatted_msg == wxT("TABLE EXISTS")) + { + continue; + } + if (lastResultError.formatted_msg.IsEmpty()) + { + targetschema->GetConnection()->ExecuteVoid(wxT("COMMIT")); + lastResultError = targetschema->GetConnection()->GetLastResultError(); + if (!lastResultError.formatted_msg.IsEmpty()) + { + lastResultError.formatted_msg = pastemsg + wxT("\n") + lastResultError.formatted_msg; + msg = lastResultError.formatted_msg; + } + } + else + { + targetschema->GetConnection()->ExecuteVoid(wxT("ROLLBACK")); + msg = lastResultError.formatted_msg; + } + if (!lastResultError.formatted_msg.IsEmpty()) + { + wxMessageBox(msg, + _("Cannot paste table:") + + targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + targetschema->GetQuotedIdentifier() + + wxT(".") + table1->GetQuotedIdentifier(), + wxOK | wxICON_ERROR); + continue; + } + } + copied++; + } + msg = wxString::Format(_("%d of %d TABLE(s) COPIED FROM %s TO %s"), copied, srcObjArray.Count(), + (srcschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + srcschema->GetQuotedIdentifier()).c_str(), + (targetschema->GetDatabase()->GetQuotedIdentifier() + wxT(".") + targetschema->GetQuotedIdentifier()).c_str()); + if (copied) { + mainform->Refresh(targetobj); + } + mainform->GetStatusBar()->SetStatusText(msg, 1); + } + + frmPasteObject::~frmPasteObject() + { + } *** 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,47 ---- + ////////////////////////////////////////////////////////////////////////// + // + // 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 "pgAdmin3.h" + #include "frm/frmMain.h" + #include "schema/pgObject.h" + #include "schema/pgSchema.h" + #include "schema/pgTable.h" + + class frmPasteObject + { + public: + frmPasteObject(frmMain *form, pgObject *sourceobj, pgObject *targetobj); + void process(); + virtual ~frmPasteObject(); + private: + wxArrayString getSchemaTables(pgSchema *srcschema); + bool tableExists(pgSchema *srcschema, wxString quotedtablename); + 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); + wxArrayString stringToArray(wxString & src, wxString pattern); + void copyTable(ctlTree *browser, pgSchema *srcschema, pgSchema *targetschema, pgTable *table1); + + frmMain *mainform; + pgObject *sourceobj; + pgObject *targetobj; + pgError lastResultError; + wxString pastemsg; + }; + + #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/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");