pgAgent-3.3.0-Source/0000755000175200017520000000000012025076213013264 5ustar dpagedpagepgAgent-3.3.0-Source/pgaevent/0000755000175200017520000000000012025076213015075 5ustar dpagedpagepgAgent-3.3.0-Source/pgaevent/MSG00001.bin0000644000175200017520000000003412025076213016573 0ustar dpagedpage %1 pgAgent-3.3.0-Source/pgaevent/pgaevent.c0000644000175200017520000000141412025076213017052 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgaevent.c - win32 message format dll // ////////////////////////////////////////////////////////////////////////// #include #include #include HANDLE g_module = NULL; BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); // // DllMain --- is an optional entry point into a DLL. // BOOL WINAPI DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) g_module = hModule; return TRUE; } pgAgent-3.3.0-Source/pgaevent/pgamsgevent.rc0000644000175200017520000000275012025076213017747 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgaevent.rc - win32 Resources // ////////////////////////////////////////////////////////////////////////// LANGUAGE 0x9,0x1 1 11 MSG00001.bin #include // Icon (Don't remove the aaa prefix - it makes it the default icon) aaaPGAEVENT ICON DISCARDABLE "../include/pgagent.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION 3,2,1,0 PRODUCTVERSION 3,2,1,0 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "FileVersion", "3.2.1", "\0" VALUE "File Version", "3.2.1", "\0" VALUE "FileDescription", "pgaevent - pgAgent Event Log Message DLL", "\0" VALUE "LegalCopyright", "\251 2002 - 2012, The pgAdmin Development Team", "\0" VALUE "LegalTrademarks", "This software is released under the PostgreSQL Licence.", "\0" VALUE "InternalName", "pgaevent", "\0" VALUE "OriginalFilename","pgaevent.dll", "\0" VALUE "ProductName", "pgAgent", "\0" VALUE "ProductVersion", "3.2.1", "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 0x04E4 END END pgAgent-3.3.0-Source/pgaevent/CMakeLists.txt0000644000175200017520000000131212025076213017632 0ustar dpagedpage####################################################################### # # pgAgent - PostgreSQL tools # Copyright (C) 2002 - 2012, The pgAdmin Development Team # This software is released under the PostgreSQL Licence # # pgaevent/CMakeLists.txt - CMake build configuration # ####################################################################### ################################################################################ # Let's rock! ################################################################################ IF(WIN32) SET(_srcs pgaevent.c pgaevent.def pgamsgevent.rc) ADD_LIBRARY(pgaevent MODULE ${_srcs}) INSTALL(TARGETS pgaevent DESTINATION .) ENDIF(WIN32) pgAgent-3.3.0-Source/pgaevent/README0000644000175200017520000000144312025076213015757 0ustar dpagedpageThis whole directory is shamelessly adapted from PostgreSQL's src/bin/pgevent Note that to get the version resources etc. into the DLL, the only file generated by MC that we actually use is MSG00001.bin. ------------------------------------------------------------------------------- MSG000001.bin is a binary file, result of Microsoft MC compiler. MC compiler can be downloaded for free with MS Core SDK but it is not included with MSYS tools and I didn't find an alternative way to compile MC file. To summarize: the command "MC pgmsgevent.mc" generates pgmsgevent.h, pgmsgevent.rc, and MSG00001.bin files. In MC file, we declare a string with %s format, so we can write anything we want in the future without needing to change the definition of this string. Laurent Ballester pgAgent-3.3.0-Source/pgaevent/pgaevent.def0000644000175200017520000000070012025076213017363 0ustar dpagedpage; //////////////////////////////////////////////////////////////////////// ; // ; // pgAgent - PostgreSQL Tools ; // ; // Copyright (C) 2002 - 2012 The pgAdmin Development Team ; // This software is released under the PostgreSQL Licence ; // ; // pgaeventdef - pgaevent.dll exports ; // ; //////////////////////////////////////////////////////////////////////// ; dlltool --output-def pgaevent.def pgaevent.o pgamsgevent.o EXPORTS pgAgent-3.3.0-Source/pgaevent/pgamsgevent.mc0000644000175200017520000000011112025076213017727 0ustar dpagedpageMessageId=0 SymbolicName=PGADMIN_EVENTLOG_MSG Language=English %1 . pgAgent-3.3.0-Source/pgaevent/pgamsgevent.h0000644000175200017520000000061512025076213017570 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgamsgevent.h - Message id declarations // ////////////////////////////////////////////////////////////////////////// #define PGADMIN_EVENTLOG_MSG 0x00000000L pgAgent-3.3.0-Source/misc.cpp0000644000175200017520000000420612025076213014725 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012 The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // misc.cpp - misc functions // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" #include "connection.h" #ifndef __WXMSW__ #include #endif // In unix.c or win32.c void usage(const wxString &executable); wxString getArg(int &argc, char **&argv) { wxString s; if (argv[0][2]) s = wxString::FromAscii(argv[0] + 2); else { if (argc >= 1) { argc--; argv++; s = wxString::FromAscii(argv[0]); } else { // very bad! LogMessage(_("Invalid command line argument"), LOG_ERROR); } } return s; } void setOptions(int argc, char **argv, const wxString &executable) { while (argc-- > 0) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 't': { int val = atoi(getArg(argc, argv).mb_str(wxConvUTF8)); if (val > 0) shortWait = val; break; } case 'r': { int val = atoi(getArg(argc, argv).mb_str(wxConvUTF8)); if (val >= 10) longWait = val; break; } case 'l': { int val = atoi(getArg(argc, argv).mb_str(wxConvUTF8)); if (val >= 0 && val <= 2) minLogLevel = val; break; } #ifndef __WXMSW__ case 'f': { runInForeground = true; break; } case 's': { logFile = getArg(argc, argv); break; } #endif default: { usage(executable); exit(1); } } } else { if (connectString != wxT("")) connectString += wxT(" "); connectString += wxString::FromAscii(*argv); if (**argv == '"') connectString = connectString.substr(1, connectString.length() - 2); } argv++; } } void WaitAWhile(const bool waitLong) { int count; if (waitLong) count = longWait; else count = shortWait; while (count--) { #ifdef WIN32 CheckForInterrupt(); Sleep(1000); #else sleep(1); #endif } } wxString NumToStr(const long l) { wxString buf; buf.Printf(wxT("%ld"), l); return buf; } pgAgent-3.3.0-Source/pgagent_upgrade.sql0000644000175200017520000000170212025076213017141 0ustar dpagedpage/* // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012 The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgagent_upgrade.sql - Upgrade pgAgent tables and functions // */ CREATE OR REPLACE FUNCTION pgagent.pgagent_schema_version() RETURNS int2 AS ' BEGIN -- RETURNS PGAGENT MAJOR VERSION -- WE WILL CHANGE THE MAJOR VERSION, ONLY IF THERE IS A SCHEMA CHANGE RETURN 3; END; ' LANGUAGE 'plpgsql' VOLATILE; ALTER TABLE pgagent.pga_jobstep ADD COLUMN jstconnstr text NOT NULL DEFAULT '' CONSTRAINT pga_jobstep_connstr_check CHECK ((jstconnstr != '' AND jstkind = 's' ) OR (jstconnstr = '' AND (jstkind = 'b' OR jstdbname != ''))); ALTER TABLE pgagent.pga_jobstep DROP CONSTRAINT pga_jobstep_check; ALTER TABLE pgagent.pga_jobstep ADD CONSTRAINT pga_jobstep_dbname_check CHECK ((jstdbname != '' AND jstkind = 's' ) OR (jstdbname = '' AND (jstkind = 'b' OR jstconnstr != ''))); pgAgent-3.3.0-Source/CMakeLists.txt0000755000175200017520000001605612025076213016037 0ustar dpagedpage####################################################################### # # pgAgent - PostgreSQL tools # Copyright (C) 2002 - 2012, The pgAdmin Development Team # This software is released under the PostgreSQL Licence # # CMakeLists.txt - CMake build configuration # ####################################################################### ################################################################################ # Initial setup ################################################################################ IF(APPLE) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8) ELSE() CMAKE_MINIMUM_REQUIRED(VERSION 2.6) ENDIF(APPLE) IF(COMMAND cmake_policy) CMAKE_POLICY(SET CMP0003 NEW) ENDIF(COMMAND cmake_policy) SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) SET(CMAKE_FIND_LIBRARY_PREFIXES "") SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") SET(STATIC_BUILD YES CACHE BOOL "Statically link the executable?") ################################################################################ # Apple stuff ################################################################################ IF(APPLE) # Setup default values IF(NOT HAVE_CACHED_VALUES) IF(EXISTS ${CMAKE_OSX_SYSROOT}) SET(CMAKE_OSX_SYSROOT ${CMAKE_OSX_SYSROOT} CACHE FILEPATH "isysroot used for universal binary support" FORCE) ENDIF(EXISTS ${CMAKE_OSX_SYSROOT}) IF(NOT ${CMAKE_OSX_ARCHITECTURES} STREQUAL "") SET(CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES} CACHE STRING "Build architectures for OSX" FORCE) ELSE() SET(CMAKE_OSX_ARCHITECTURES "ppc;i386" CACHE STRING "Build architectures for OSX" FORCE) ENDIF(NOT ${CMAKE_OSX_ARCHITECTURES} STREQUAL "") ENDIF(NOT HAVE_CACHED_VALUES) # Target Tiger SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.4") ENDIF(APPLE) ################################################################################ # Project config ################################################################################ PROJECT(pgagent) # If changing the version number, remember to change here and under the CPack # settings in this file, as well as the definition for pgagent_schema_version() # in pgagent.sql and upgrade_pgagent.sql if the major version number is # changed. The full version number also needs to be included in pgAgent.rc and # pgaevent/pgamsgevent.rc at present. SET(VERSION 3.3.0) # CPack stuff SET(CPACK_PACKAGE_VERSION_MAJOR 3) SET(CPACK_PACKAGE_VERSION_MINOR 3) SET(CPACK_PACKAGE_VERSION_PATCH 0) SET(CPACK_PACKAGE_NAME "pgAgent") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "pgAgent is a job scheduling engine for PostgreSQL") SET(CPACK_PACKAGE_VENDOR "the pgAdmin Development Team") IF(WIN32) SET(CPACK_GENERATOR ZIP) ELSE(WIN32) SET(CPACK_GENERATOR TGZ) ENDIF(WIN32) SET(CPACK_SOURCE_GENERATOR TGZ) SET(CPACK_SOURCE_IGNORE_FILES "\\\\.DS_Store;/CVS/;/\\\\.svn/;\\\\.swp$;\\\\.#;/#;.*~;cscope.*") ADD_DEFINITIONS(-DPGAGENT_VERSION_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR}) # This must come after we set the CPACK variables!! INCLUDE(CPack) ################################################################################ # Find wxWidgets ################################################################################ SET(WX_DEBUG NO CACHE BOOL "Use the debug build of wxWidgets?") SET(WX_STATIC ${STATIC_BUILD}) SET(WX_VERSION "2.8") SET(WX_UNICODE YES) SET(WX_MODULES "base") FIND_PACKAGE(WX REQUIRED) INCLUDE_DIRECTORIES(${WX_INCLUDE_DIRS}) ADD_DEFINITIONS(${WX_DEFINITIONS}) LINK_DIRECTORIES(${WX_LIBRARY_DIRS}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WX_CXX_FLAGS}") ################################################################################ # Find PostgreSQL ################################################################################ SET(PG_STATIC ${WX_STATIC}) FIND_PACKAGE(PG REQUIRED) INCLUDE_DIRECTORIES(${PG_INCLUDE_DIRS}) LINK_DIRECTORIES(${PG_LIBRARY_DIRS}) ################################################################################ # Let's rock! ################################################################################ INCLUDE_DIRECTORIES(${pgagent_SOURCE_DIR} ${pgagent_SOURCE_DIR}/include) FILE(GLOB _cpp_files *.cpp) FILE(GLOB _h_files include/*.h) SET(_srcs ${_cpp_files} ${_h_files}) IF(WIN32) SET(_srcs ${_srcs} pgagent.rc) ENDIF(WIN32) ADD_EXECUTABLE(pgagent ${_srcs}) TARGET_LINK_LIBRARIES(pgagent ${PG_LIBRARIES} ${WX_LIBRARIES}) # Installation IF (WIN32) INSTALL(TARGETS pgagent DESTINATION .) INSTALL(FILES ${pgagent_SOURCE_DIR}/pgagent.sql DESTINATION .) INSTALL(FILES ${pgagent_SOURCE_DIR}/pgagent_upgrade.sql DESTINATION .) ELSE(WIN32) INSTALL(TARGETS pgagent DESTINATION bin) INSTALL(FILES ${pgagent_SOURCE_DIR}/pgagent.sql DESTINATION share) INSTALL(FILES ${pgagent_SOURCE_DIR}/pgagent_upgrade.sql DESTINATION share) ENDIF(WIN32) INSTALL(FILES ${pgagent_SOURCE_DIR}/README DESTINATION .) INSTALL(FILES ${pgagent_SOURCE_DIR}/LICENSE DESTINATION .) ################################################################################ # pgaevent ################################################################################ IF(WIN32) ADD_SUBDIRECTORY(pgaevent) ENDIF(WIN32) ################################################################################ # Build summary ################################################################################ MESSAGE(STATUS " ") MESSAGE(STATUS "================================================================================") MESSAGE(STATUS "Configuration summary:") MESSAGE(STATUS " ") MESSAGE(STATUS " Project : ${PROJECT_NAME}") MESSAGE(STATUS " Description : ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") MESSAGE(STATUS " Version : ${VERSION}") MESSAGE(STATUS " ") MESSAGE(STATUS " PostgreSQL version : ${PG_VERSION_STRING}") MESSAGE(STATUS " PostgreSQL path : ${PG_ROOT_DIR}") MESSAGE(STATUS " PostgreSQL config binary : ${PG_CONFIG_PATH}") MESSAGE(STATUS " PostgreSQL include path : ${PG_INCLUDE_DIRS}") MESSAGE(STATUS " PostgreSQL library path : ${PG_LIBRARY_DIRS}") MESSAGE(STATUS " ") MESSAGE(STATUS " wxWidgets version : ${WX_VERSION_STRING}") MESSAGE(STATUS " wxWidgets path : ${WX_ROOT_DIR}") MESSAGE(STATUS " wxWidgets config binary : ${WX_CONFIG_PATH}") MESSAGE(STATUS " wxWidgets Static linking : ${WX_STATIC}") MESSAGE(STATUS " wxWidgets Debug? : ${WX_DEBUG}") MESSAGE(STATUS "================================================================================") MESSAGE(STATUS " ") ################################################################################ # Give ourselves a hint that we have cached values - must be last! ################################################################################ IF(NOT HAVE_CACHED_VALUES) SET(HAVE_CACHED_VALUES 1 CACHE INTERNAL "Flag to indicate that we have cached values") ENDIF(NOT HAVE_CACHED_VALUES) pgAgent-3.3.0-Source/LICENSE0000644000175200017520000000202312025076213014266 0ustar dpagedpagepgAgent Copyright (c) 2002 - 2012, The pgAdmin Development Team Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE PGADMIN DEVELOPMENT TEAM BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE PGADMIN DEVELOPMENT TEAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE PGADMIN DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE PGADMIN DEVELOPMENT TEAM HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pgAgent-3.3.0-Source/pgagent.sql0000644000175200017520000006450112025076213015440 0ustar dpagedpage/* // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012 The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgagent.sql - pgAgent tables and functions // */ BEGIN TRANSACTION; CREATE SCHEMA pgagent; COMMENT ON SCHEMA pgagent IS 'pgAgent system tables'; CREATE TABLE pgagent.pga_jobagent ( jagpid int4 NOT NULL PRIMARY KEY, jaglogintime timestamptz NOT NULL DEFAULT current_timestamp, jagstation text NOT NULL ) WITHOUT OIDS; COMMENT ON TABLE pgagent.pga_jobagent IS 'Active job agents'; CREATE TABLE pgagent.pga_jobclass ( jclid serial NOT NULL PRIMARY KEY, jclname text NOT NULL ) WITHOUT OIDS; CREATE UNIQUE INDEX pga_jobclass_name ON pgagent.pga_jobclass(jclname); COMMENT ON TABLE pgagent.pga_jobclass IS 'Job classification'; INSERT INTO pgagent.pga_jobclass (jclname) VALUES ('Routine Maintenance'); INSERT INTO pgagent.pga_jobclass (jclname) VALUES ('Data Import'); INSERT INTO pgagent.pga_jobclass (jclname) VALUES ('Data Export'); INSERT INTO pgagent.pga_jobclass (jclname) VALUES ('Data Summarisation'); INSERT INTO pgagent.pga_jobclass (jclname) VALUES ('Miscellaneous'); CREATE TABLE pgagent.pga_job ( jobid serial NOT NULL PRIMARY KEY, jobjclid int4 NOT NULL REFERENCES pgagent.pga_jobclass (jclid) ON DELETE RESTRICT ON UPDATE RESTRICT, jobname text NOT NULL, jobdesc text NOT NULL DEFAULT '', jobhostagent text NOT NULL DEFAULT '', jobenabled bool NOT NULL DEFAULT true, jobcreated timestamptz NOT NULL DEFAULT current_timestamp, jobchanged timestamptz NOT NULL DEFAULT current_timestamp, jobagentid int4 NULL REFERENCES pgagent.pga_jobagent(jagpid) ON DELETE SET NULL ON UPDATE RESTRICT, jobnextrun timestamptz NULL, joblastrun timestamptz NULL ) WITHOUT OIDS; COMMENT ON TABLE pgagent.pga_job IS 'Job main entry'; COMMENT ON COLUMN pgagent.pga_job.jobagentid IS 'Agent that currently executes this job.'; CREATE TABLE pgagent.pga_jobstep ( jstid serial NOT NULL PRIMARY KEY, jstjobid int4 NOT NULL REFERENCES pgagent.pga_job (jobid) ON DELETE CASCADE ON UPDATE RESTRICT, jstname text NOT NULL, jstdesc text NOT NULL DEFAULT '', jstenabled bool NOT NULL DEFAULT true, jstkind char NOT NULL CHECK (jstkind IN ('b', 's')), -- batch, sql jstcode text NOT NULL, jstconnstr text NOT NULL DEFAULT '' CHECK ((jstconnstr != '' AND jstkind = 's' ) OR (jstconnstr = '' AND (jstkind = 'b' OR jstdbname != ''))), jstdbname name NOT NULL DEFAULT '' CHECK ((jstdbname != '' AND jstkind = 's' ) OR (jstdbname = '' AND (jstkind = 'b' OR jstconnstr != ''))), jstonerror char NOT NULL CHECK (jstonerror IN ('f', 's', 'i')) DEFAULT 'f', -- fail, success, ignore jscnextrun timestamptz NULL ) WITHOUT OIDS; CREATE INDEX pga_jobstep_jobid ON pgagent.pga_jobstep(jstjobid); COMMENT ON TABLE pgagent.pga_jobstep IS 'Job step to be executed'; COMMENT ON COLUMN pgagent.pga_jobstep.jstkind IS 'Kind of jobstep: s=sql, b=batch'; COMMENT ON COLUMN pgagent.pga_jobstep.jstonerror IS 'What to do if step returns an error: f=fail the job, s=mark step as succeeded and continue, i=mark as fail but ignore it and proceed'; CREATE TABLE pgagent.pga_schedule ( jscid serial NOT NULL PRIMARY KEY, jscjobid int4 NOT NULL REFERENCES pgagent.pga_job (jobid) ON DELETE CASCADE ON UPDATE RESTRICT, jscname text NOT NULL, jscdesc text NOT NULL DEFAULT '', jscenabled bool NOT NULL DEFAULT true, jscstart timestamptz NOT NULL DEFAULT current_timestamp, jscend timestamptz NULL, jscminutes bool[60] NOT NULL DEFAULT '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', jschours bool[24] NOT NULL DEFAULT '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', jscweekdays bool[7] NOT NULL DEFAULT '{f,f,f,f,f,f,f}', jscmonthdays bool[32] NOT NULL DEFAULT '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', jscmonths bool[12] NOT NULL DEFAULT '{f,f,f,f,f,f,f,f,f,f,f,f}', CONSTRAINT pga_schedule_jscminutes_size CHECK (array_upper(jscminutes, 1) = 60), CONSTRAINT pga_schedule_jschours_size CHECK (array_upper(jschours, 1) = 24), CONSTRAINT pga_schedule_jscweekdays_size CHECK (array_upper(jscweekdays, 1) = 7), CONSTRAINT pga_schedule_jscmonthdays_size CHECK (array_upper(jscmonthdays, 1) = 32), CONSTRAINT pga_schedule_jscmonths_size CHECK (array_upper(jscmonths, 1) = 12) ) WITHOUT OIDS; CREATE INDEX pga_jobschedule_jobid ON pgagent.pga_schedule(jscjobid); COMMENT ON TABLE pgagent.pga_schedule IS 'Schedule for a job'; CREATE TABLE pgagent.pga_exception ( jexid serial NOT NULL PRIMARY KEY, jexscid int4 NOT NULL REFERENCES pgagent.pga_schedule (jscid) ON DELETE CASCADE ON UPDATE RESTRICT, jexdate date NULL, jextime time NULL ) WITHOUT OIDS; CREATE INDEX pga_exception_jexscid ON pgagent.pga_exception (jexscid); CREATE UNIQUE INDEX pga_exception_datetime ON pgagent.pga_exception (jexdate, jextime); COMMENT ON TABLE pgagent.pga_schedule IS 'Job schedule exceptions'; CREATE TABLE pgagent.pga_joblog ( jlgid serial NOT NULL PRIMARY KEY, jlgjobid int4 NOT NULL REFERENCES pgagent.pga_job (jobid) ON DELETE CASCADE ON UPDATE RESTRICT, jlgstatus char NOT NULL CHECK (jlgstatus IN ('r', 's', 'f', 'i', 'd')) DEFAULT 'r', -- running, success, failed, internal failure, aborted jlgstart timestamptz NOT NULL DEFAULT current_timestamp, jlgduration interval NULL ) WITHOUT OIDS; CREATE INDEX pga_joblog_jobid ON pgagent.pga_joblog(jlgjobid); COMMENT ON TABLE pgagent.pga_joblog IS 'Job run logs.'; COMMENT ON COLUMN pgagent.pga_joblog.jlgstatus IS 'Status of job: r=running, s=successfully finished, f=failed, i=no steps to execute, d=aborted'; CREATE TABLE pgagent.pga_jobsteplog ( jslid serial NOT NULL PRIMARY KEY, jsljlgid int4 NOT NULL REFERENCES pgagent.pga_joblog (jlgid) ON DELETE CASCADE ON UPDATE RESTRICT, jsljstid int4 NOT NULL REFERENCES pgagent.pga_jobstep (jstid) ON DELETE CASCADE ON UPDATE RESTRICT, jslstatus char NOT NULL CHECK (jslstatus IN ('r', 's', 'i', 'f', 'd')) DEFAULT 'r', -- running, success, ignored, failed, aborted jslresult int4 NULL, jslstart timestamptz NOT NULL DEFAULT current_timestamp, jslduration interval NULL, jsloutput text ) WITHOUT OIDS; CREATE INDEX pga_jobsteplog_jslid ON pgagent.pga_jobsteplog(jsljlgid); COMMENT ON TABLE pgagent.pga_jobsteplog IS 'Job step run logs.'; COMMENT ON COLUMN pgagent.pga_jobsteplog.jslstatus IS 'Status of job step: r=running, s=successfully finished, f=failed stopping job, i=ignored failure, d=aborted'; COMMENT ON COLUMN pgagent.pga_jobsteplog.jslresult IS 'Return code of job step'; CREATE OR REPLACE FUNCTION pgagent.pgagent_schema_version() RETURNS int2 AS ' BEGIN -- RETURNS PGAGENT MAJOR VERSION -- WE WILL CHANGE THE MAJOR VERSION, ONLY IF THERE IS A SCHEMA CHANGE RETURN 3; END; ' LANGUAGE 'plpgsql' VOLATILE; CREATE OR REPLACE FUNCTION pgagent.pga_next_schedule(int4, timestamptz, timestamptz, _bool, _bool, _bool, _bool, _bool) RETURNS timestamptz AS ' DECLARE jscid ALIAS FOR $1; jscstart ALIAS FOR $2; jscend ALIAS FOR $3; jscminutes ALIAS FOR $4; jschours ALIAS FOR $5; jscweekdays ALIAS FOR $6; jscmonthdays ALIAS FOR $7; jscmonths ALIAS FOR $8; nextrun timestamp := ''1970-01-01 00:00:00-00''; runafter timestamp := ''1970-01-01 00:00:00-00''; bingo bool := FALSE; gotit bool := FALSE; foundval bool := FALSE; daytweak bool := FALSE; minutetweak bool := FALSE; i int2 := 0; d int2 := 0; nextminute int2 := 0; nexthour int2 := 0; nextday int2 := 0; nextmonth int2 := 0; nextyear int2 := 0; BEGIN -- No valid start date has been specified IF jscstart IS NULL THEN RETURN NULL; END IF; -- The schedule is past its end date IF jscend IS NOT NULL AND jscend < now() THEN RETURN NULL; END IF; -- Get the time to find the next run after. It will just be the later of -- now() + 1m and the start date for the time being, however, we might want to -- do more complex things using this value in the future. IF date_trunc(''MINUTE'', jscstart) > date_trunc(''MINUTE'', (now() + ''1 Minute''::interval)) THEN runafter := date_trunc(''MINUTE'', jscstart); ELSE runafter := date_trunc(''MINUTE'', (now() + ''1 Minute''::interval)); END IF; -- -- Enter a loop, generating next run timestamps until we find one -- that falls on the required weekday, and is not matched by an exception -- WHILE bingo = FALSE LOOP -- -- Get the next run year -- nextyear := date_part(''YEAR'', runafter); -- -- Get the next run month -- nextmonth := date_part(''MONTH'', runafter); gotit := FALSE; FOR i IN (nextmonth) .. 12 LOOP IF jscmonths[i] = TRUE THEN nextmonth := i; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; IF gotit = FALSE THEN FOR i IN 1 .. (nextmonth - 1) LOOP IF jscmonths[i] = TRUE THEN nextmonth := i; -- Wrap into next year nextyear := nextyear + 1; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; END IF; -- -- Get the next run day -- -- If the year, or month have incremented, get the lowest day, -- otherwise look for the next day matching or after today. IF (nextyear > date_part(''YEAR'', runafter) OR nextmonth > date_part(''MONTH'', runafter)) THEN nextday := 1; FOR i IN 1 .. 32 LOOP IF jscmonthdays[i] = TRUE THEN nextday := i; foundval := TRUE; EXIT; END IF; END LOOP; ELSE nextday := date_part(''DAY'', runafter); gotit := FALSE; FOR i IN nextday .. 32 LOOP IF jscmonthdays[i] = TRUE THEN nextday := i; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; IF gotit = FALSE THEN FOR i IN 1 .. (nextday - 1) LOOP IF jscmonthdays[i] = TRUE THEN nextday := i; -- Wrap into next month IF nextmonth = 12 THEN nextyear := nextyear + 1; nextmonth := 1; ELSE nextmonth := nextmonth + 1; END IF; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; END IF; END IF; -- Was the last day flag selected? IF nextday = 32 THEN IF nextmonth = 1 THEN nextday := 31; ELSIF nextmonth = 2 THEN IF pgagent.pga_is_leap_year(nextyear) = TRUE THEN nextday := 29; ELSE nextday := 28; END IF; ELSIF nextmonth = 3 THEN nextday := 31; ELSIF nextmonth = 4 THEN nextday := 30; ELSIF nextmonth = 5 THEN nextday := 31; ELSIF nextmonth = 6 THEN nextday := 30; ELSIF nextmonth = 7 THEN nextday := 31; ELSIF nextmonth = 8 THEN nextday := 31; ELSIF nextmonth = 9 THEN nextday := 30; ELSIF nextmonth = 10 THEN nextday := 31; ELSIF nextmonth = 11 THEN nextday := 30; ELSIF nextmonth = 12 THEN nextday := 31; END IF; END IF; -- -- Get the next run hour -- -- If the year, month or day have incremented, get the lowest hour, -- otherwise look for the next hour matching or after the current one. IF (nextyear > date_part(''YEAR'', runafter) OR nextmonth > date_part(''MONTH'', runafter) OR nextday > date_part(''DAY'', runafter) OR daytweak = TRUE) THEN nexthour := 0; FOR i IN 1 .. 24 LOOP IF jschours[i] = TRUE THEN nexthour := i - 1; foundval := TRUE; EXIT; END IF; END LOOP; ELSE nexthour := date_part(''HOUR'', runafter); gotit := FALSE; FOR i IN (nexthour + 1) .. 24 LOOP IF jschours[i] = TRUE THEN nexthour := i - 1; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; IF gotit = FALSE THEN FOR i IN 1 .. nexthour LOOP IF jschours[i] = TRUE THEN nexthour := i - 1; -- Wrap into next month IF (nextmonth = 1 OR nextmonth = 3 OR nextmonth = 5 OR nextmonth = 7 OR nextmonth = 8 OR nextmonth = 10 OR nextmonth = 12) THEN d = 31; ELSIF (nextmonth = 4 OR nextmonth = 6 OR nextmonth = 9 OR nextmonth = 11) THEN d = 30; ELSE IF pgagent.pga_is_leap_year(nextyear) = TRUE THEN d := 29; ELSE d := 28; END IF; END IF; IF nextday = d THEN nextday := 1; IF nextmonth = 12 THEN nextyear := nextyear + 1; nextmonth := 1; ELSE nextmonth := nextmonth + 1; END IF; ELSE nextday := nextday + 1; END IF; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; END IF; END IF; -- -- Get the next run minute -- -- If the year, month day or hour have incremented, get the lowest minute, -- otherwise look for the next minute matching or after the current one. IF (nextyear > date_part(''YEAR'', runafter) OR nextmonth > date_part(''MONTH'', runafter) OR nextday > date_part(''DAY'', runafter) OR nexthour > date_part(''HOUR'', runafter) OR daytweak = TRUE) THEN nextminute := 0; IF minutetweak = TRUE THEN d := 1; ELSE d := date_part(''YEAR'', runafter)::int2; END IF; FOR i IN d .. 60 LOOP IF jscminutes[i] = TRUE THEN nextminute := i - 1; foundval := TRUE; EXIT; END IF; END LOOP; ELSE nextminute := date_part(''MINUTE'', runafter); gotit := FALSE; FOR i IN (nextminute + 1) .. 60 LOOP IF jscminutes[i] = TRUE THEN nextminute := i - 1; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; IF gotit = FALSE THEN FOR i IN 1 .. nextminute LOOP IF jscminutes[i] = TRUE THEN nextminute := i - 1; -- Wrap into next hour IF (nextmonth = 1 OR nextmonth = 3 OR nextmonth = 5 OR nextmonth = 7 OR nextmonth = 8 OR nextmonth = 10 OR nextmonth = 12) THEN d = 31; ELSIF (nextmonth = 4 OR nextmonth = 6 OR nextmonth = 9 OR nextmonth = 11) THEN d = 30; ELSE IF pgagent.pga_is_leap_year(nextyear) = TRUE THEN d := 29; ELSE d := 28; END IF; END IF; IF nexthour = 23 THEN nexthour = 0; IF nextday = d THEN nextday := 1; IF nextmonth = 12 THEN nextyear := nextyear + 1; nextmonth := 1; ELSE nextmonth := nextmonth + 1; END IF; ELSE nextday := nextday + 1; END IF; ELSE nexthour := nexthour + 1; END IF; gotit := TRUE; foundval := TRUE; EXIT; END IF; END LOOP; END IF; END IF; -- Build the result, and check it is not the same as runafter - this may -- happen if all array entries are set to false. In this case, add a minute. nextrun := (nextyear::varchar || ''-''::varchar || nextmonth::varchar || ''-'' || nextday::varchar || '' '' || nexthour::varchar || '':'' || nextminute::varchar)::timestamptz; IF nextrun = runafter AND foundval = FALSE THEN nextrun := nextrun + INTERVAL ''1 Minute''; END IF; -- If the result is past the end date, exit. IF nextrun > jscend THEN RETURN NULL; END IF; -- Check to ensure that the nextrun time is actually still valid. Its -- possible that wrapped values may have carried the nextrun onto an -- invalid time or date. IF ((jscminutes = ''{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}'' OR jscminutes[date_part(''MINUTE'', nextrun) + 1] = TRUE) AND (jschours = ''{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}'' OR jschours[date_part(''HOUR'', nextrun) + 1] = TRUE) AND (jscmonthdays = ''{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}'' OR jscmonthdays[date_part(''DAY'', nextrun)] = TRUE OR (jscmonthdays = ''{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,t}'' AND ((date_part(''MONTH'', nextrun) IN (1,3,5,7,8,10,12) AND date_part(''DAY'', nextrun) = 31) OR (date_part(''MONTH'', nextrun) IN (4,6,9,11) AND date_part(''DAY'', nextrun) = 30) OR (date_part(''MONTH'', nextrun) = 2 AND ((pgagent.pga_is_leap_year(date_part(''DAY'', nextrun)::int2) AND date_part(''DAY'', nextrun) = 29) OR date_part(''DAY'', nextrun) = 28))))) AND (jscmonths = ''{f,f,f,f,f,f,f,f,f,f,f,f}'' OR jscmonths[date_part(''MONTH'', nextrun)] = TRUE)) THEN -- Now, check to see if the nextrun time found is a) on an acceptable -- weekday, and b) not matched by an exception. If not, set -- runafter = nextrun and try again. -- Check for a wildcard weekday gotit := FALSE; FOR i IN 1 .. 7 LOOP IF jscweekdays[i] = TRUE THEN gotit := TRUE; EXIT; END IF; END LOOP; -- OK, is the correct weekday selected, or a wildcard? IF (jscweekdays[date_part(''DOW'', nextrun) + 1] = TRUE OR gotit = FALSE) THEN -- Check for exceptions SELECT INTO d jexid FROM pgagent.pga_exception WHERE jexscid = jscid AND ((jexdate = nextrun::date AND jextime = nextrun::time) OR (jexdate = nextrun::date AND jextime IS NULL) OR (jexdate IS NULL AND jextime = nextrun::time)); IF FOUND THEN -- Nuts - found an exception. Increment the time and try again runafter := nextrun + INTERVAL ''1 Minute''; bingo := FALSE; minutetweak := TRUE; daytweak := FALSE; ELSE bingo := TRUE; END IF; ELSE -- We''re on the wrong week day - increment a day and try again. runafter := nextrun + INTERVAL ''1 Day''; bingo := FALSE; minutetweak := FALSE; daytweak := TRUE; END IF; ELSE runafter := nextrun + INTERVAL ''1 Minute''; bingo := FALSE; minutetweak := TRUE; daytweak := FALSE; END IF; END LOOP; RETURN nextrun; END; ' LANGUAGE 'plpgsql' VOLATILE; COMMENT ON FUNCTION pgagent.pga_next_schedule(int4, timestamptz, timestamptz, _bool, _bool, _bool, _bool, _bool) IS 'Calculates the next runtime for a given schedule'; -- -- Test code -- -- SELECT pgagent.pga_next_schedule( -- 2, -- Schedule ID -- '2005-01-01 00:00:00', -- Start date -- '2006-10-01 00:00:00', -- End date -- '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', -- Minutes -- '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', -- Hours -- '{f,f,f,f,f,f,f}', -- Weekdays -- '{f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f}', -- Monthdays -- '{f,f,f,f,f,f,f,f,f,f,f,f}' -- Months -- ); CREATE OR REPLACE FUNCTION pgagent.pga_is_leap_year(int2) RETURNS bool AS ' BEGIN IF $1 % 4 != 0 THEN RETURN FALSE; END IF; IF $1 % 100 != 0 THEN RETURN TRUE; END IF; RETURN $1 % 400 = 0; END; ' LANGUAGE 'plpgsql' IMMUTABLE; COMMENT ON FUNCTION pgagent.pga_is_leap_year(int2) IS 'Returns TRUE if $1 is a leap year'; CREATE OR REPLACE FUNCTION pgagent.pga_job_trigger() RETURNS "trigger" AS ' BEGIN IF NEW.jobenabled THEN IF NEW.jobnextrun IS NULL THEN SELECT INTO NEW.jobnextrun MIN(pgagent.pga_next_schedule(jscid, jscstart, jscend, jscminutes, jschours, jscweekdays, jscmonthdays, jscmonths)) FROM pgagent.pga_schedule WHERE jscenabled AND jscjobid=OLD.jobid; END IF; ELSE NEW.jobnextrun := NULL; END IF; RETURN NEW; END; ' LANGUAGE 'plpgsql' VOLATILE; COMMENT ON FUNCTION pgagent.pga_job_trigger() IS 'Update the job''s next run time.'; CREATE TRIGGER pga_job_trigger BEFORE UPDATE ON pgagent.pga_job FOR EACH ROW EXECUTE PROCEDURE pgagent.pga_job_trigger(); COMMENT ON TRIGGER pga_job_trigger ON pgagent.pga_job IS 'Update the job''s next run time.'; CREATE OR REPLACE FUNCTION pgagent.pga_schedule_trigger() RETURNS trigger AS ' BEGIN IF TG_OP = ''DELETE'' THEN -- update pga_job from remaining schedules -- the actual calculation of jobnextrun will be performed in the trigger UPDATE pgagent.pga_job SET jobnextrun = NULL WHERE jobenabled AND jobid=OLD.jscjobid; RETURN OLD; ELSE UPDATE pgagent.pga_job SET jobnextrun = NULL WHERE jobenabled AND jobid=NEW.jscjobid; RETURN NEW; END IF; END; ' LANGUAGE 'plpgsql'; COMMENT ON FUNCTION pgagent.pga_schedule_trigger() IS 'Update the job''s next run time whenever a schedule changes'; CREATE TRIGGER pga_schedule_trigger AFTER INSERT OR UPDATE OR DELETE ON pgagent.pga_schedule FOR EACH ROW EXECUTE PROCEDURE pgagent.pga_schedule_trigger(); COMMENT ON TRIGGER pga_schedule_trigger ON pgagent.pga_schedule IS 'Update the job''s next run time whenever a schedule changes'; CREATE OR REPLACE FUNCTION pgagent.pga_exception_trigger() RETURNS "trigger" AS ' DECLARE jobid int4 := 0; BEGIN IF TG_OP = ''DELETE'' THEN SELECT INTO jobid jscjobid FROM pgagent.pga_schedule WHERE jscid = OLD.jexscid; -- update pga_job from remaining schedules -- the actual calculation of jobnextrun will be performed in the trigger UPDATE pgagent.pga_job SET jobnextrun = NULL WHERE jobenabled AND jobid=jobid; RETURN OLD; ELSE SELECT INTO jobid jscjobid FROM pgagent.pga_schedule WHERE jscid = NEW.jexscid; UPDATE pgagent.pga_job SET jobnextrun = NULL WHERE jobenabled AND jobid=jobid; RETURN NEW; END IF; END; ' LANGUAGE 'plpgsql' VOLATILE; COMMENT ON FUNCTION pgagent.pga_exception_trigger() IS 'Update the job''s next run time whenever an exception changes'; CREATE TRIGGER pga_exception_trigger AFTER INSERT OR UPDATE OR DELETE ON pgagent.pga_exception FOR EACH ROW EXECUTE PROCEDURE pgagent.pga_exception_trigger(); COMMENT ON TRIGGER pga_exception_trigger ON pgagent.pga_exception IS 'Update the job''s next run time whenever an exception changes'; COMMIT TRANSACTION; pgAgent-3.3.0-Source/pgAgent-3.3.0-Source/0000755000175200017520000000000012025076213016546 5ustar dpagedpagepgAgent-3.3.0-Source/precomp.cpp0000644000175200017520000000057012025076213015437 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // precomp.cpp - pgAgent precompiled headers // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" pgAgent-3.3.0-Source/connection.cpp0000644000175200017520000002777012025076213016144 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // connection.cpp - database connection // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" #include #include DBconn *DBconn::primaryConn = NULL; wxString DBconn::basicConnectString; static wxMutex s_PoolLock; DBconn::DBconn(const wxString &connectString, const wxString &db) { inUse = false; next = 0; prev = 0; majorVersion = 0; minorVersion = 0; dbname = db; connStr = connectString; if (connectString.IsEmpty()) { // This is a sql call to a local database. // No connection string found. Use basicConnectString. Connect(basicConnectString + wxT(" dbname=") + dbname); } else { Connect(connectString); } } bool DBconn::Connect(const wxString &connectString) { LogMessage(wxString::Format(_("Creating DB connection: %s"), connectString.c_str()), LOG_DEBUG); wxCharBuffer cstrUTF = connectString.mb_str(wxConvUTF8); conn = PQconnectdb(cstrUTF); if (PQstatus(conn) != CONNECTION_OK) { lastError = wxString::FromAscii(PQerrorMessage(conn)); PQfinish(conn); conn = 0; } return IsValid(); } DBconn::~DBconn() { // clear a single connection if (conn) { PQfinish(conn); conn = 0; } } wxString DBconn::qtDbString(const wxString &value) { wxString result = value; result.Replace(wxT("\\"), wxT("\\\\")); result.Replace(wxT("'"), wxT("''")); result.Append(wxT("'")); if (BackendMinimumVersion(8, 1)) { if (result.Contains(wxT("\\"))) result.Prepend(wxT("E'")); else result.Prepend(wxT("'")); } else result.Prepend(wxT("'")); return result; } bool DBconn::BackendMinimumVersion(int major, int minor) { if (!majorVersion) { wxString version = ExecuteScalar(wxT("SELECT version();")) ; sscanf(version.ToAscii(), "%*s %d.%d", &majorVersion, &minorVersion); } return majorVersion > major || (majorVersion == major && minorVersion >= minor); } DBconn *DBconn::InitConnection(const wxString &connectString) { wxMutexLocker lock(s_PoolLock); basicConnectString = connectString; wxString dbname; connInfo cnInfo = connInfo::getConnectionInfo(connectString); if (cnInfo.isValid) { dbname = cnInfo.dbname; basicConnectString = cnInfo.getConnectionString(); primaryConn = new DBconn(cnInfo.getConnectionString(), dbname); if (!primaryConn) LogMessage(_("Failed to create primary connection!"), LOG_ERROR); primaryConn->dbname = dbname; primaryConn->inUse = true; } else { primaryConn = NULL; LogMessage(wxT("Primary connection string is not valid!"), LOG_ERROR); } return primaryConn; } DBconn *DBconn::Get(const wxString &connStr, const wxString &db) { if (db.IsEmpty() && connStr.IsEmpty()) { LogMessage(_("Cannot allocate connection - no database or connection string specified!"), LOG_WARNING); return NULL; } wxMutexLocker lock(s_PoolLock); DBconn *thisConn = primaryConn, *lastConn; // find an existing connection do { if (thisConn && ((!db.IsEmpty() && db == thisConn->dbname && connStr.IsEmpty()) || (!connStr.IsEmpty() && connStr == thisConn->connStr)) && !thisConn->inUse) { LogMessage(wxString::Format(_("Allocating existing connection to database %s"), thisConn->dbname.c_str()), LOG_DEBUG); thisConn->inUse = true; return thisConn; } lastConn = thisConn; thisConn = thisConn->next; } while (thisConn != 0); // No suitable connection was found, so create a new one. DBconn *newConn = NULL; newConn = new DBconn(connStr, db); if (newConn->conn) { LogMessage(wxString::Format(_("Allocating new connection to database %s"), newConn->dbname.c_str()), LOG_DEBUG); newConn->inUse = true; newConn->prev = lastConn; lastConn->next = newConn; } else { wxString warnMsg; if (connStr.IsEmpty()) warnMsg = wxString::Format(_("Failed to create new connection to database '%s':'%s'"), db.c_str(), newConn->GetLastError().c_str()); else warnMsg = wxString::Format(_("Failed to create new connection for connection string '%s':%s"), connStr.c_str(), newConn->GetLastError().c_str()); LogMessage(warnMsg, LOG_STARTUP); return NULL; } return newConn; } void DBconn::Return() { wxMutexLocker lock(s_PoolLock); // Cleanup this->ExecuteVoid(wxT("RESET ALL")); this->lastError.Empty(); LogMessage(wxString::Format(_("Returning connection to database %s"), dbname.c_str()), LOG_DEBUG); inUse = false; } void DBconn::ClearConnections(bool all) { wxMutexLocker lock(s_PoolLock); if (all) LogMessage(_("Clearing all connections"), LOG_DEBUG); else LogMessage(_("Clearing inactive connections"), LOG_DEBUG); DBconn *thisConn = primaryConn, *deleteConn; int total = 0, free = 0, deleted = 0; if (thisConn) { total++; // Find the last connection while (thisConn->next != 0) { total++; if (!thisConn->inUse) free++; thisConn = thisConn->next; } if (!thisConn->inUse) free++; // Delete connections as required // If a connection is not in use, delete it, and reset the next and previous // pointers appropriately. If it is in use, don't touch it. while (thisConn->prev != 0) { if ((!thisConn->inUse) || all) { deleteConn = thisConn; thisConn = deleteConn->prev; thisConn->next = deleteConn->next; if (deleteConn->next) deleteConn->next->prev = deleteConn->prev; delete deleteConn; deleted++; } else { thisConn = thisConn->prev; } } if (all) { delete thisConn; deleted++; } wxString tmp; tmp.Printf(_("Connection stats: total - %d, free - %d, deleted - %d"), total, free, deleted); LogMessage(tmp, LOG_DEBUG); } else LogMessage(_("No connections found!"), LOG_DEBUG); } DBresult *DBconn::Execute(const wxString &query) { DBresult *res = new DBresult(this, query); if (!res->IsValid()) { // error handling here delete res; return 0; } return res; } wxString DBconn::ExecuteScalar(const wxString &query) { int rows = -1; DBresult *res = Execute(query); wxString data; if (res) { data = res->GetString(0); rows = res->RowsAffected(); delete res; return data; } return wxEmptyString; } int DBconn::ExecuteVoid(const wxString &query) { int rows = -1; DBresult *res = Execute(query); if (res) { rows = res->RowsAffected(); delete res; } return rows; } wxString DBconn::GetLastError() { // Return the last error message, minus any trailing line ends if (lastError.substr(lastError.length() - 2, 2) == wxT("\r\n")) // DOS return lastError.substr(0, lastError.length() - 2); else if (lastError.substr(lastError.length() - 1, 1) == wxT("\n")) // Unix return lastError.substr(0, lastError.length() - 1); else if (lastError.substr(lastError.length() - 1, 1) == wxT("\r")) // Mac return lastError.substr(0, lastError.length() - 1); else return lastError; } ///////////////////////////////////////////////////////7 DBresult::DBresult(DBconn *conn, const wxString &query) { wxCharBuffer cstrUTF = query.mb_str(wxConvUTF8); result = PQexec(conn->conn, cstrUTF); currentRow = 0; maxRows = 0; if (result) { int rc = PQresultStatus(result); conn->SetLastResult(rc); if (rc == PGRES_TUPLES_OK) maxRows = PQntuples(result); else if (rc != PGRES_COMMAND_OK) { conn->lastError = wxString::FromAscii(PQerrorMessage(conn->conn)); LogMessage(wxT("Query error: ") + conn->lastError, LOG_WARNING); PQclear(result); result = 0; } } else conn->lastError = wxString::FromAscii(PQerrorMessage(conn->conn)); } DBresult::~DBresult() { if (result) PQclear(result); } wxString DBresult::GetString(int col) const { wxString str; if (result && currentRow < maxRows && col >= 0) { str = wxString::FromAscii(PQgetvalue(result, currentRow, col)); } return str; } wxString DBresult::GetString(const wxString &colname) const { wxCharBuffer cstrUTF = colname.mb_str(wxConvUTF8); int col = PQfnumber(result, cstrUTF); if (col < 0) { // fatal: not found return wxT(""); } return GetString(col); } ///////////////////////////////////////////////////////7 bool connInfo::IsValidIP() { if (host.IsEmpty()) return false; // check for IPv4 format wxStringTokenizer tkip4(host, wxT(".")); int count = 0; while (tkip4.HasMoreTokens()) { unsigned long val = 0; if (!tkip4.GetNextToken().ToULong(&val)) break; if (count == 0 || count == 3) if (val > 0 && val < 255) count++; else break; else if (val >= 0 && val < 255) count++; else break; } if (count == 4) return true; // check for IPv6 format wxStringTokenizer tkip6(host, wxT(":")); count = 0; while (tkip6.HasMoreTokens()) { unsigned long val = 0; wxString strVal = tkip6.GetNextToken(); if (strVal.Length() > 4 || !strVal.ToULong(&val, 16)) return false; count++; } if (count <= 8) return true; // TODO:: We're not supporting mix mode (x:x:x:x:x:x:d.d.d.d) // i.e. ::ffff:12.34.56.78 return false; } wxString connInfo::getConnectionString() { wxString connStr; // Check if it has valid connection info if (!isValid) return connStr; // User connStr = wxT("user=") + user; // Port if (port != 0) { wxString portStr; portStr.Printf(wxT("%ld"), port); connStr += wxT(" port=") + portStr; } // host or hostaddr if (!host.IsEmpty()) if (IsValidIP()) connStr += wxT(" hostaddr=") + host; else connStr += wxT(" host=") + host; // connection timeout if (connection_timeout != 0) { wxString val; val.Printf(wxT("%ld"), connection_timeout); connStr += wxT(" connection_timeout=") + val; } // password if (!password.IsEmpty()) connStr += wxT(" password=") + password; if (!dbname.IsEmpty()) connStr += wxT(" dbname=") + dbname; LogMessage(wxString::Format(_("Connection Information:")), LOG_DEBUG); LogMessage(wxString::Format(_(" user : %s"), user.c_str()), LOG_DEBUG); LogMessage(wxString::Format(_(" port : %ld"), port), LOG_DEBUG); LogMessage(wxString::Format(_(" host : %s"), host.c_str()), LOG_DEBUG); LogMessage(wxString::Format(_(" dbname : %s"), dbname.c_str()), LOG_DEBUG); LogMessage(wxString::Format(_(" password : %s"), password.c_str()), LOG_DEBUG); LogMessage(wxString::Format(_(" conn timeout : %ld"), connection_timeout), LOG_DEBUG); return connStr; } connInfo connInfo::getConnectionInfo(wxString connStr) { connInfo cnInfo; wxRegEx propertyExp; // Remove the white-space(s) to match the following format // i.e. prop=value bool res = propertyExp.Compile(wxT("(([ ]*[\t]*)+)=")); propertyExp.ReplaceAll(&connStr, wxT("=")); res = propertyExp.Compile(wxT("=(([ ]*[\t]*)+)")); propertyExp.ReplaceAll(&connStr, wxT("=")); // Seperate all the prop=value patterns wxArrayString tokens = wxStringTokenize(connStr, wxT("\t \n\r")); unsigned int index = 0; while (index < tokens.Count()) { wxString prop, value; wxArrayString pairs = wxStringTokenize(tokens[index++], wxT("=")); if (pairs.GetCount() != 2) return cnInfo; prop = pairs[0]; value = pairs[1]; if (prop.CmpNoCase(wxT("user")) == 0) cnInfo.user = value; else if (prop.CmpNoCase(wxT("host")) == 0 || prop.CmpNoCase(wxT("hostAddr")) == 0) cnInfo.host = value; else if (prop.CmpNoCase(wxT("port")) == 0) { if (!value.ToULong(&cnInfo.port)) // port must be an unsigned integer return cnInfo; } else if (prop.CmpNoCase(wxT("password")) == 0) cnInfo.password = value; else if (prop.CmpNoCase(wxT("connection_timeout")) == 0) { if (!value.ToULong(&cnInfo.connection_timeout)) // connection timeout must be an unsigned interger return cnInfo; } else if (prop.CmpNoCase(wxT("dbname")) == 0) cnInfo.dbname = value; else // Not valid property found return cnInfo; } // If user, dbname & host all are blank than we will consider this an invalid connection string if (cnInfo.user.IsEmpty() && cnInfo.dbname.IsEmpty() && cnInfo.host.IsEmpty()) cnInfo.isValid = false; else cnInfo.isValid = true; return cnInfo; } pgAgent-3.3.0-Source/README0000644000175200017520000000372112025076213014147 0ustar dpagedpagepgAgent ======= This document describes the compilation of pgAgent, a job scheduler for PostgreSQL. pgAgent is managed using pgAdmin (http://www.pgadmin.org). The pgAdmin documentation contains details of the setup and use of pgAgent with your PostgreSQL system. The latest build of the documentation can be found at http://www.pgadmin.org/docs/dev/pgagent.html. Building pgAgent ---------------- You will need: - A C/C++ compiler, such as GCC or Microsoft Visual C++ on Windows. - CMake 2.6 (from www.cmake.org) - A wxWidgets 2.8.x installation, configured per the requirements for pgAdmin: http://git.postgresql.org/gitweb/?p=pgadmin3.git;a=blob_plain;f=INSTALL;hb=HEAD - A PostgreSQL 8.3 or higher installation 1) Unpack the pgAgent source code 2) Create a build directory in which the code will be built. 3) Run ccmake from the build directory (on Windows, use the CMake graphical interface). By default, ccmake will generate Unix Makefiles - consult the documentation if you wish to generate other types of output: $ ccmake /path/to/pgagent 4) If required, press 'c' to generate a default configuration: CMAKE_BUILD_TYPE Release CMAKE_INSTALL_PREFIX /usr/local CMAKE_OSX_ARCHITECTURES ppc;i386 CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk PostgreSQL_CONFIG_EXECUTABLE /usr/local/pgsql/bin/pg_config wxWidgets_CONFIG_EXECUTABLE /usr/local/bin/wx-config wxWidgets_USE_DEBUG OFF wxWidgets_USE_STATIC ON wxWidgets_USE_UNICODE ON wxWidgets_wxrc_EXECUTABLE /usr/bin/wxrc 5) Use the ccmake interface to adjust any settings as required. When configured as required, press 'c' to re-configure (if required) and 'g' to generate the build files and exit. 6) Run 'make' to build pgAgent on Mac or Unix, or open the generated project files in VC++ on Windows and build the solution in the desired configuration. pgAgent-3.3.0-Source/cmake/0000755000175200017520000000000012025076213014344 5ustar dpagedpagepgAgent-3.3.0-Source/cmake/FindPG.cmake0000644000175200017520000001021212025076213016451 0ustar dpagedpage####################################################################### # # FindPg.cmake - A CMake module for locating PostgreSQL # # Dave Page, EnterpriseDB UK Ltd. # This code is released under the PostgreSQL Licence # ####################################################################### # To use this module, simply include it in your CMake project. # If set, PostgreSQL will be assumed to be in the location specified # by the PGDIR environment variable. Otherwise, it will be searched # for in a number of standard locations. # # For statically linked builds, the PG_STATIC variable can be set to # true. # # The following CMake variable will be set: # # PG_FOUND - Set to TRUE if PostgreSQL is located # PG_CONFIG_PATH - The pg_config executable path # PG_ROOT_DIR - The base install directory for PostgreSQL # PG_INCLUDE_DIRS - The directory containing the PostgreSQL headers. # PG_LIBRARIES - The PostgreSQL client libraries. # PG_LIBRARY_DIRS - The directory containing the PostgreSQL client libraries. # PG_PKG_LIBRARY_DIRS - The directory containing the PostgreSQL package libraries. # PG_VERSION_STRING - The PostgreSQL version number. IF(NOT PG_STATIC OR PG_STATIC STREQUAL "") SET(_static "no") ELSE(NOT PG_STATIC OR PG_STATIC STREQUAL "") IF(PG_STATIC) SET(_static "yes") ELSE(PG_STATIC) SET(_static "no") ENDIF(PG_STATIC) ENDIF(NOT PG_STATIC OR PG_STATIC STREQUAL "") IF(NOT $ENV{PGDIR} STREQUAL "") FIND_PROGRAM(PG_CONFIG_PATH pg_config PATH $ENV{PGDIR}/bin DOC "Path to the pg_config executable" NO_DEFAULT_PATH) ELSE(NOT $ENV{PGDIR} STREQUAL "") FIND_PROGRAM(PG_CONFIG_PATH pg_config PATH /usr/local/pgsql/bin /opt/PostgreSQL/*/bin /Library/PostgreSQL/*/bin $ENV{ProgramFiles}/PostgreSQL/*/bin $ENV{SystemDrive}/PostgreSQL/*/bin DOC "Path to the pg_config executable") ENDIF(NOT $ENV{PGDIR} STREQUAL "") EXEC_PROGRAM(${PG_CONFIG_PATH} ARGS --version OUTPUT_VARIABLE PG_VERSION_STRING RETURN_VALUE _retval) IF(NOT _retval) SET(PG_FOUND TRUE) # Strip the bin and pg_config from the path GET_FILENAME_COMPONENT(PG_ROOT_DIR ${PG_CONFIG_PATH} PATH) GET_FILENAME_COMPONENT(PG_ROOT_DIR ${PG_ROOT_DIR} PATH) IF(WIN32 AND NOT CYGWIN AND NOT MSYS) SET(PG_INCLUDE_DIRS "${PG_ROOT_DIR}/include") SET(PG_LIBRARY_DIRS "${PG_ROOT_DIR}/lib") SET(PG_PKG_LIBRARY_DIRS "${PG_ROOT_DIR}/lib") # There iare no static libraries on VC++ builds of PG. LIST(APPEND PG_LIBRARIES libpq.lib) ELSE(WIN32 AND NOT CYGWIN AND NOT MSYS) EXEC_PROGRAM(${PG_CONFIG_PATH} ARGS --includedir OUTPUT_VARIABLE PG_INCLUDE_DIRS) EXEC_PROGRAM(${PG_CONFIG_PATH} ARGS --libdir OUTPUT_VARIABLE PG_LIBRARY_DIRS) EXEC_PROGRAM(${PG_CONFIG_PATH} ARGS --pkglibdir OUTPUT_VARIABLE PG_PKG_LIBRARY_DIRS) IF(_static) LIST(APPEND PG_LIBRARIES ${PG_LIBRARY_DIRS}/libpq.a) # Check for SSL and Kerberos EXEC_PROGRAM("nm" ARGS ${PG_LIBRARY_DIRS}/libpq.a OUTPUT_VARIABLE _op) IF(_op MATCHES "SSL_connect") LIST(APPEND PG_LIBRARIES "ssl") ENDIF(_op MATCHES "SSL_connect") IF(_op MATCHES "krb5_free_principal") LIST(APPEND PG_LIBRARIES "krb5") ENDIF(_op MATCHES "krb5_free_principal") IF(_op MATCHES "ldap_init") LIST(APPEND PG_LIBRARIES "ldap") ENDIF(_op MATCHES "ldap_init") LIST(APPEND PG_LIBRARIES "crypto") IF(NOT APPLE) LIST(APPEND PG_LIBRARIES "crypt") ENDIF(NOT APPLE) ELSE(_static) LIST(APPEND PG_LIBRARIES pq) ENDIF(_static) ENDIF(WIN32 AND NOT CYGWIN AND NOT MSYS) ELSE(NOT _retval) SET(PG_FOUND FALSE) SET(PG_ROOT_DIR PG_ROOT_DIR-NO_FOUND) IF(PG_FIND_REQUIRED) MESSAGE(FATAL_ERROR "No PostgreSQL installation could be found.") ELSE(PG_FIND_REQUIRED) MESSAGE(STATUS "No PostgreSQL installation could be found.") ENDIF(PG_FIND_REQUIRED) ENDIF(NOT _retval) pgAgent-3.3.0-Source/cmake/FindWX.cmake0000644000175200017520000003357712025076213016524 0ustar dpagedpage####################################################################### # # FindPg.cmake - A CMake module for locating wxWidgets # # Dave Page, EnterpriseDB UK Ltd. # This code is released under the PostgreSQL Licence # ####################################################################### # To use this module, simply include it in your CMake project. # If set, wxWidgets will be assumed to be in the location specified # by the WXWIN environment variable. Otherwise, it will be searched # for in a number of standard locations. # # The following CMake variables can be set to control the build type. # If not set, the default values shown will be used. Booleans must # be either YES or NO: # # WX_VERSION = "2.8" # WX_DEBUG = NO # WX_STATIC = NO # WX_UNICODE = YES # WX_MODULES = "base" (a list). # # The following CMake variable will be set: # # WX_FOUND - Set to TRUE if wxWidgets is located # WX_ROOT_DIR - The base install directory for wxWidgets # WX_VERSION_STRING - The wxWidgets version number. # # Unix only: # WX_CONFIG_PATH - The wx-config executable path # # Unix & Win32: # WX_INCLUDE_DIRS - The wxWidgets header directories. # WX_DEFINITIONS - The wxWidgets preprocessor definitions # WX_LIBRARIES - The wxWidgets libraries # WX_LIBRARY_DIRS - The wxWidgets library directories. ############################################################################### # Macros ############################################################################### # Check for a library on Windows MACRO(WIN32_CHECK_LIB M_LIB_NAME M_LIB_REL_FILENAME M_LIB_DBG_FILENAME M_LIB_PATH) LIST(FIND WX_MODULES ${M_LIB_NAME} _pos) IF(NOT _pos EQUAL -1) SET(_tmp CACHE INTERNAL "_tmp-NOTFOUND") FIND_FILE(_tmp NAMES ${M_LIB_REL_FILENAME} PATHS ${M_LIB_PATH} NO_DEFAULT_PATH) IF(_tmp STREQUAL "_tmp-NOTFOUND") SET(_error TRUE) ELSE(_tmp STREQUAL "_tmp-NOTFOUND") LIST(APPEND WX_LIBRARIES "optimized;${M_LIB_REL_FILENAME}") ENDIF(_tmp STREQUAL "_tmp-NOTFOUND") SET(_tmp CACHE INTERNAL "_tmp-NOTFOUND") FIND_FILE(_tmp NAMES ${M_LIB_DBG_FILENAME} PATHS ${M_LIB_PATH} NO_DEFAULT_PATH) IF(_tmp STREQUAL "_tmp-NOTFOUND") SET(_error TRUE) ELSE(_tmp STREQUAL "_tmp-NOTFOUND") LIST(APPEND WX_LIBRARIES "debug;${M_LIB_DBG_FILENAME}") ENDIF(_tmp STREQUAL "_tmp-NOTFOUND") ENDIF(NOT _pos EQUAL -1) ENDMACRO(WIN32_CHECK_LIB) ############################################################################### # Arguments ############################################################################### IF(NOT WX_VERSION OR WX_VERSION STREQUAL "") SET(_version "2.8") ELSE(NOT WX_VERSION OR WX_VERSION STREQUAL "") SET(_version ${WX_VERSION}) ENDIF(NOT WX_VERSION OR WX_VERSION STREQUAL "") IF(NOT WX_DEBUG OR WX_DEBUG STREQUAL "") SET(_debug "no") ELSE(NOT WX_DEBUG OR WX_DEBUG STREQUAL "") IF(WX_DEBUG) SET(_debug "yes") ELSE(WX_DEBUG) SET(_debug "no") ENDIF(WX_DEBUG) ENDIF(NOT WX_DEBUG OR WX_DEBUG STREQUAL "") IF(NOT WX_STATIC OR WX_STATIC STREQUAL "") SET(_static "no") ELSE(NOT WX_STATIC OR WX_STATIC STREQUAL "") IF(WX_STATIC) SET(_static "yes") ELSE(WX_STATIC) SET(_static "no") ENDIF(WX_STATIC) ENDIF(NOT WX_STATIC OR WX_STATIC STREQUAL "") IF(NOT WX_UNICODE OR WX_UNICODE STREQUAL "") SET(_unicode "yes") ELSE(NOT WX_UNICODE OR WX_UNICODE STREQUAL "") IF(WX_UNICODE) SET(_unicode "yes") ELSE(WX_UNICODE) SET(_unicode "no") ENDIF(WX_UNICODE) ENDIF(NOT WX_UNICODE OR WX_UNICODE STREQUAL "") IF(NOT WX_MODULES OR WX_MODULES STREQUAL "") SET(_modules "base") ELSE(NOT WX_MODULES OR WX_MODULES STREQUAL "") SET(_modules ${WX_MODULES}) ENDIF(NOT WX_MODULES OR WX_MODULES STREQUAL "") SET(_build_desc "version: ${_version}, debug: ${_debug}, static: ${_static}, unicode: ${_unicode}, modules: ${_modules}") ############################################################################### # Here we go... ############################################################################### # MSVC++ IF(WIN32 AND NOT CYGWIN AND NOT MSYS) # The VC++ libraries are found in an entirely different way than # the *nix libraries because we don't have a wx-config file. # Figure out the build suffix SET(_suffix "") # Possibly-unicode libraries IF(_unicode) SET(_suffix "${_suffix}u") ENDIF(_unicode) # Figure out the build prefix directory IF(_static) SET(_prefix "vc_lib") ELSE(_static) SET(_prefix "vc_dll") ENDIF(_static) # Find the Unix configure script. We'll attempt to extract a version number from it. IF(NOT $ENV{WXWIN} STREQUAL "") FIND_PATH(WX_ROOT_DIR NAMES configure PATHS $ENV{WXWIN} DOC "Path to the wxWidgets installation" NO_DEFAULT_PATH) ELSE(NOT $ENV{WXWIN} STREQUAL "") FIND_PATH(WX_ROOT_DIR NAMES configure PATHS $ENV{ProgramFiles}/wxWidgets-* $ENV{SystemDrive}/wxWidgets-* DOC "Path to the wxWidgets installation") ENDIF(NOT $ENV{WXWIN} STREQUAL "") # Attempt to read the version number from the configure script FILE(STRINGS ${WX_ROOT_DIR}/configure _line REGEX "PACKAGE_VERSION=") STRING(REGEX REPLACE "PACKAGE_VERSION='([0-9]+\\.[0-9]+\\.[0-9]+)'" "\\1" WX_VERSION_STRING "${_line}") IF(NOT ${WX_VERSION_STRING} STREQUAL "") SET(WX_FOUND TRUE) LIST(APPEND WX_INCLUDE_DIRS "${WX_ROOT_DIR}/include" "${WX_ROOT_DIR}/contrib/include") LIST(APPEND WX_LIBRARY_DIRS "${WX_ROOT_DIR}/lib/${_prefix}") # Got through the modules list and add libraries for those requested. # If any of them don't seem to exist, throw an error, or got to not found mode STRING(REGEX REPLACE "^([0-9])+\\.([0-9])+\\.[0-9]+" "\\1\\2" _shortver "${WX_VERSION_STRING}") SET(_error FALSE) SET(_libpath ${WX_ROOT_DIR}/lib/${_prefix}) WIN32_CHECK_LIB("adv" wxmsw${_shortver}${_suffix}_adv.lib wxmsw${_shortver}${_suffix}d_adv.lib ${_libpath}) WIN32_CHECK_LIB("aui" wxmsw${_shortver}${_suffix}_aui.lib wxmsw${_shortver}${_suffix}d_aui.lib ${_libpath}) WIN32_CHECK_LIB("base" wxbase${_shortver}${_suffix}.lib wxbase${_shortver}${_suffix}d.lib ${_libpath}) WIN32_CHECK_LIB("core" wxmsw${_shortver}${_suffix}_core.lib wxmsw${_shortver}${_suffix}d_core.lib ${_libpath}) WIN32_CHECK_LIB("dbgrid" wxmsw${_shortver}${_suffix}_dbgrid.lib wxmsw${_shortver}${_suffix}d_dbgrid.lib ${_libpath}) WIN32_CHECK_LIB("gl" wxmsw${_shortver}${_suffix}_gl.lib wxmsw${_shortver}${_suffix}d_gl.lib ${_libpath}) WIN32_CHECK_LIB("html" wxmsw${_shortver}${_suffix}_html.lib wxmsw${_shortver}${_suffix}d_html.lib ${_libpath}) WIN32_CHECK_LIB("media" wxmsw${_shortver}${_suffix}_media.lib wxmsw${_shortver}${_suffix}d_media.lib ${_libpath}) WIN32_CHECK_LIB("net" wxbase${_shortver}${_suffix}_net.lib wxbase${_shortver}${_suffix}d_net.lib ${_libpath}) WIN32_CHECK_LIB("odbc" wxbase${_shortver}${_suffix}_odbc.lib wxbase${_shortver}${_suffix}d_odbc.lib ${_libpath}) WIN32_CHECK_LIB("qa" wxmsw${_shortver}${_suffix}_qa.lib wxmsw${_shortver}${_suffix}d_qa.lib ${_libpath}) WIN32_CHECK_LIB("richtext" wxmsw${_shortver}${_suffix}_richtext.lib wxmsw${_shortver}${_suffix}d_richtext.lib ${_libpath}) WIN32_CHECK_LIB("xml" wxbase${_shortver}${_suffix}_xml.lib wxbase${_shortver}${_suffix}d_xml.lib ${_libpath}) WIN32_CHECK_LIB("xrc" wxmsw${_shortver}${_suffix}_xrc.lib wxmsw${_shortver}${_suffix}d_xrc.lib ${_libpath}) # Contribs WIN32_CHECK_LIB("fl" wxmsw${_shortver}${_suffix}_fl.lib wxmsw${_shortver}${_suffix}d_fl.lib ${_libpath}) WIN32_CHECK_LIB("foldbar" wxmsw${_shortver}${_suffix}_foldbar.lib wxmsw${_shortver}${_suffix}d_foldbar.lib ${_libpath}) WIN32_CHECK_LIB("gizmos" wxmsw${_shortver}${_suffix}_gizmos.lib wxmsw${_shortver}${_suffix}d_gizmos.lib ${_libpath}) WIN32_CHECK_LIB("gizmos_xrc" wxmsw${_shortver}${_suffix}_gizmos_xrc.lib wxmsw${_shortver}${_suffix}d_gizmos_xrc.lib ${_libpath}) WIN32_CHECK_LIB("mmedia" wxmsw${_shortver}${_suffix}_mmedia.lib wxmsw${_shortver}${_suffix}d_mmedia.lib ${_libpath}) WIN32_CHECK_LIB("netutils" wxmsw${_shortver}${_suffix}_netutils.lib wxmsw${_shortver}${_suffix}d_netutils.lib ${_libpath}) WIN32_CHECK_LIB("ogl" wxmsw${_shortver}${_suffix}_ogl.lib wxmsw${_shortver}${_suffix}d_ogl.lib ${_libpath}) WIN32_CHECK_LIB("plot" wxmsw${_shortver}${_suffix}_plot.lib wxmsw${_shortver}${_suffix}d_plot.lib ${_libpath}) WIN32_CHECK_LIB("stc" wxmsw${_shortver}${_suffix}_stc.lib wxmsw${_shortver}${_suffix}d_stc.lib ${_libpath}) WIN32_CHECK_LIB("svg" wxmsw${_shortver}${_suffix}_svg.lib wxmsw${_shortver}${_suffix}d_svg.lib ${_libpath}) # Add some default libraries we'll need LIST(APPEND WX_LIBRARIES "debug;wxexpatd.lib" "optimized;wxexpat.lib" "debug;wxjpegd.lib" "optimized;wxjpeg.lib" "debug;wxpngd.lib" "optimized;wxpng.lib" "debug;wxregex${_suffix}d.lib" "optimized;wxregex${_suffix}.lib" "debug;wxtiffd.lib" "optimized;wxtiff.lib" "debug;wxzlibd.lib" "optimized;wxzlib.lib") LIST(APPEND WX_LIBRARIES winmm comctl32 rpcrt4 wsock32) # Preprocessor definitions SET(${WX_DEFINITIONS} "-D__WXMSW__") IF(_unicode) SET(WX_DEFINITIONS "${WX_DEFINITIONS};-DUNICODE") ENDIF(_unicode) IF(NOT _static) SET(WX_DEFINITIONS "${WX_DEFINITIONS};-DWXUSINGDLL") ENDIF(NOT _static) # Bail out if there was an error IF(_error) SET(WX_FOUND FALSE) SET(WX_ROOT_DIR WX_ROOT_DIR-NO_FOUND) SET(WX_VERSION_STRING "") SET(WX_INCLUDE_DIRS "") SET(WX_DEFINITIONS "") SET(WX_LIBRARIES "") SET(WX_LIBRARY_DIRS "") IF(WX_FIND_REQUIRED) MESSAGE(FATAL_ERROR "The selected wxWidgets configuration (${_build_desc}) is not available.") ELSE(WX_FIND_REQUIRED) MESSAGE(STATUS "The selected wxWidgets configuration (${_build_desc}) is not available.") ENDIF(WX_FIND_REQUIRED) ENDIF(_error) ELSE(NOT ${WX_VERSION_STRING} STREQUAL "") SET(WX_FOUND FALSE) SET(WX_ROOT_DIR WX_ROOT_DIR-NO_FOUND) SET(WX_VERSION_STRING "") IF(WX_FIND_REQUIRED) MESSAGE(FATAL_ERROR "No wxWidgets installation could be found.") ELSE(WX_FIND_REQUIRED) MESSAGE(STATUS "No wxWidgets installation could be found.") ENDIF(WX_FIND_REQUIRED) ENDIF(NOT ${WX_VERSION_STRING} STREQUAL "") # Unix-style ELSE(WIN32 AND NOT CYGWIN AND NOT MSYS) # Set up the wx-config command line SET(_args "--version=${_version} --debug=${_debug} --static=${_static} --unicode=${_unicode} ${_modules}") IF(NOT $ENV{WXWIN} STREQUAL "") FIND_PROGRAM(WX_CONFIG_PATH wx-config PATH $ENV{WXWIN}/bin DOC "Path to the wx-config executable" NO_DEFAULT_PATH) ELSE(NOT $ENV{WXWIN} STREQUAL "") FIND_PROGRAM(WX_CONFIG_PATH wx-config DOC "Path to the wx-config executable") ENDIF(NOT $ENV{WXWIN} STREQUAL "") EXEC_PROGRAM(${WX_CONFIG_PATH} ARGS ${_args} --version OUTPUT_VARIABLE WX_VERSION_STRING RETURN_VALUE _retval) IF(_retval EQUAL 1) SET(WX_FOUND FALSE) SET(WX_ROOT_DIR WX_ROOT_DIR-NO_FOUND) SET(WX_VERSION_STRING "") IF(WX_FIND_REQUIRED) MESSAGE(FATAL_ERROR "The selected wxWidgets configuration (${_build_desc}) is not available.") ELSE(WX_FIND_REQUIRED) MESSAGE(STATUS "The selected wxWidgets configuration (${_build_desc}) is not available.") ENDIF(WX_FIND_REQUIRED) ENDIF(_retval EQUAL 1) IF(NOT _retval) SET(WX_FOUND TRUE) # Strip the bin and pg_config from the path GET_FILENAME_COMPONENT(WX_ROOT_DIR ${WX_CONFIG_PATH} PATH) GET_FILENAME_COMPONENT(WX_ROOT_DIR ${WX_ROOT_DIR} PATH) EXEC_PROGRAM(${WX_CONFIG_PATH} ARGS ${_args} --cppflags OUTPUT_VARIABLE _cppflags) EXEC_PROGRAM(${WX_CONFIG_PATH} ARGS ${_args} --libs OUTPUT_VARIABLE _ldflags) # Parse the compiler options STRING(STRIP "${_cppflags}" WX_CPPFLAGS) SEPARATE_ARGUMENTS(WX_CPPFLAGS) # Get the definitions, and drop them from the flags STRING(REGEX MATCHALL "-D[^;]+" WX_DEFINITIONS "${WX_CPPFLAGS}") STRING(REGEX REPLACE "-D[^;]+(;|$)" "" WX_CPPFLAGS "${WX_CPPFLAGS}") # Get the include dirs. STRING(REGEX MATCHALL "-I[^;]+" WX_INCLUDE_DIRS "${WX_CPPFLAGS}") STRING(REPLACE "-I" "" WX_INCLUDE_DIRS "${WX_INCLUDE_DIRS}") STRING(REGEX REPLACE "-I[^;]+(;|$)" "" WX_CPPFLAGS "${WX_CPPFLAGS}") # Parse the libraries STRING(STRIP "${_ldflags}" WX_LIBRARIES) SEPARATE_ARGUMENTS(WX_LIBRARIES) STRING(REPLACE "-framework;" "-framework " WX_LIBRARIES "${WX_LIBRARIES}") STRING(REPLACE "-arch;" "-arch " WX_LIBRARIES "${WX_LIBRARIES}") STRING(REPLACE "-isysroot;" "-isysroot " WX_LIBRARIES "${WX_LIBRARIES}") # extract linkdirs (-L) for rpath (i.e., LINK_DIRECTORIES) STRING(REGEX MATCHALL "-L[^;]+" WX_LIBRARY_DIRS "${WX_LIBRARIES}") STRING(REPLACE "-L" "" WX_LIBRARY_DIRS "${WX_LIBRARY_DIRS}") ELSE(NOT _retval) SET(WX_FOUND FALSE) SET(WX_ROOT_DIR WX_ROOT_DIR-NO_FOUND) SET(WX_VERSION_STRING "") IF(WX_FIND_REQUIRED) MESSAGE(FATAL_ERROR "No wxWidgets installation could be found.") ELSE(WX_FIND_REQUIRED) MESSAGE(STATUS "No wxWidgets installation could be found.") ENDIF(WX_FIND_REQUIRED) ENDIF(NOT _retval) ENDIF(WIN32 AND NOT CYGWIN AND NOT MSYS) pgAgent-3.3.0-Source/win32.cpp0000644000175200017520000003243212025076213014736 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012 The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // win32.cpp - pgAgent win32 specific functions // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" // This is for Win32 only!! #ifdef WIN32 #include #include #include // for debugging purposes, we can start the service paused #define START_SUSPENDED 0 static SERVICE_STATUS serviceStatus; static SERVICE_STATUS_HANDLE serviceStatusHandle; static wxString serviceName; static wxString user = wxT(".\\Administrator"), password; static HANDLE threadHandle = 0; static bool serviceIsRunning; static bool pgagentInitialized; static HANDLE serviceSync; static HANDLE eventHandle; bool stopService(); // This will be called from MainLoop, if pgagent is initialized properly void Initialized() { pgagentInitialized = true; } // This will be called periodically to check if the service is to be paused. void CheckForInterrupt() { serviceIsRunning = false; long prevCount; ReleaseSemaphore(serviceSync, 1, &prevCount); // if prevCount is zero, the service should be paused. // We're waiting for the semaphore to get signaled again. if (!prevCount) WaitForSingleObject(serviceSync, INFINITE); serviceIsRunning = true; } void LogMessage(wxString msg, int level) { if (eventHandle) { LPCTSTR *tmp; tmp = (LPCTSTR *)malloc(sizeof(LPCTSTR)); tmp[0] = _wcsdup(msg.wc_str()); switch (level) { case LOG_DEBUG: if (minLogLevel >= LOG_DEBUG) ReportEvent(eventHandle, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, tmp, NULL); break; case LOG_WARNING: if (minLogLevel >= LOG_WARNING) ReportEvent(eventHandle, EVENTLOG_WARNING_TYPE, 0, 0, NULL, 1, 0, tmp, NULL); break; case LOG_ERROR: ReportEvent(eventHandle, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, tmp, NULL); stopService(); // Set pgagent initialized to true, as initService // is waiting for it to be intialized pgagentInitialized = true; // Change service status serviceStatus.dwCheckPoint = 0; serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; // Log startup/connection warnings (valid for any log level) case LOG_STARTUP: ReportEvent(eventHandle, EVENTLOG_WARNING_TYPE, 0, 0, NULL, 1, 0, tmp, NULL); break; } } else { switch (level) { case LOG_DEBUG: if (minLogLevel >= LOG_DEBUG) wxPrintf(_("DEBUG: %s\n"), msg); break; case LOG_WARNING: if (minLogLevel >= LOG_WARNING) wxPrintf(_("WARNING: %s\n"), msg); break; case LOG_ERROR: wxPrintf(_("ERROR: %s\n"), msg); pgagentInitialized = true; exit(1); break; // Log startup/connection warnings (valid for any log level) case LOG_STARTUP: wxPrintf(_("WARNING: %s\n"), msg); break; } } } // The main working thread of the service unsigned int __stdcall threadProcedure(void *unused) { MainLoop(); return 0; } //////////////////////////////////////////////////////////// // a replacement popen for windows. // // _popen doesn't work in Win2K from a service so we have to // do it the fun way :-) HANDLE win32_popen_r(const TCHAR *command) { HANDLE hWrite, hRead; SECURITY_ATTRIBUTES saAttr; BOOL ret = FALSE; PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; TCHAR *cmd; cmd = _wcsdup(command); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&hRead, &hWrite, &saAttr, 0)) return NULL; // Ensure the read handle to the pipe for STDOUT is not inherited. SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0); // Now create the child process. ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = hWrite; siStartInfo.hStdOutput = hWrite; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; ret = CreateProcess(NULL, cmd, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION if (!ret) return NULL; else { CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); } // Close the write end of the pipe and return the read end. if (!CloseHandle(hWrite)) return NULL; return hRead; } //////////////////////////////////////////////////////////// // service control functions bool pauseService() { WaitForSingleObject(serviceSync, shortWait * 1000 - 30); if (!serviceIsRunning) { SuspendThread(threadHandle); return true; } return false; } bool continueService() { ReleaseSemaphore(serviceSync, 1, 0); ResumeThread(threadHandle); return true; } bool stopService() { pauseService(); CloseHandle (threadHandle); threadHandle = 0; return true; } bool initService() { serviceSync = CreateSemaphore(0, 1, 1, 0); unsigned int tid; pgagentInitialized = false; threadHandle = (HANDLE)_beginthreadex(0, 0, threadProcedure, 0, 0, &tid); while (!pgagentInitialized) { if (eventHandle) { serviceStatus.dwWaitHint += 1000 ; serviceStatus.dwCheckPoint++; SetServiceStatus(serviceStatusHandle, (LPSERVICE_STATUS) &serviceStatus); } Sleep(1000); } return (threadHandle != 0); } void CALLBACK serviceHandler(DWORD ctl) { switch (ctl) { case SERVICE_CONTROL_STOP: { serviceStatus.dwCheckPoint++; serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); stopService(); serviceStatus.dwCheckPoint = 0; serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; } case SERVICE_CONTROL_PAUSE: { pauseService(); serviceStatus.dwCurrentState = SERVICE_PAUSED; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; } case SERVICE_CONTROL_CONTINUE: { continueService(); serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; } default: { break; } } } void CALLBACK serviceMain(DWORD argc, LPTSTR *argv) { serviceName.Printf(wxT("%s"), (const char *)argv[0]); serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; serviceStatus.dwCurrentState = SERVICE_START_PENDING; serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; serviceStatus.dwWin32ExitCode = 0; serviceStatus.dwCheckPoint = 0; serviceStatus.dwWaitHint = 15000; serviceStatusHandle = RegisterServiceCtrlHandler(serviceName.c_str(), serviceHandler); if (serviceStatusHandle) { SetServiceStatus(serviceStatusHandle, &serviceStatus); if (initService()) { serviceStatus.dwCurrentState = SERVICE_RUNNING; serviceStatus.dwWaitHint = 1000; } else serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); } } //////////////////////////////////////////////////////////// // installation and removal bool installService(const wxString &serviceName, const wxString &executable, const wxString &args, const wxString &displayname, const wxString &user, const wxString &password) { DWORD dwData; bool done = false; SC_HANDLE manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if (manager) { wxString cmd = executable + wxT(" ") + args; wxString quser; if (!user.Contains(wxT("\\"))) quser = wxT(".\\") + user; else quser = user; SC_HANDLE service = CreateService(manager, serviceName.c_str(), displayname.c_str(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmd.c_str(), 0, 0, 0, quser.c_str(), password.c_str()); if (service) { done = true; CloseServiceHandle(service); } else { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); wxString error; error.Printf(wxT("%s"), lpMsgBuf); LogMessage(error, LOG_ERROR); } CloseServiceHandle(manager); } // Setup the event message DLL wxRegKey *msgKey = new wxRegKey(wxT("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") + serviceName); if(!msgKey->Exists()) { if (!msgKey->Create()) LogMessage(_("Could not open the message source registry key."), LOG_WARNING); } wxString path = executable.BeforeLast('\\') + wxT("\\pgaevent.dll"); if (!msgKey->SetValue(wxT("EventMessageFile"), path)) LogMessage(_("Could not set the event message file registry value."), LOG_WARNING); dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (!msgKey->SetValue(wxT("TypesSupported"), dwData)) LogMessage(_("Could not set the supported types."), LOG_WARNING);; return done; } bool removeService(const wxString &serviceName) { bool done = false; SC_HANDLE manager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if (manager) { SC_HANDLE service = OpenService(manager, serviceName.c_str(), SERVICE_ALL_ACCESS); if (service) { SERVICE_STATUS serviceStatus; ControlService(service, SERVICE_CONTROL_STOP, &serviceStatus); int retries; for (retries = 0 ; retries < 5 ; retries++) { if (QueryServiceStatus(service, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { DeleteService(service); done = true; break; } Sleep(1000L); } } CloseServiceHandle(service); } CloseServiceHandle(manager); } // Remove the event message DLL wxRegKey *msgKey = new wxRegKey(wxT("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") + serviceName); msgKey->DeleteSelf(); return done; } void usage(const wxString &executable) { wxFileName *fn = new wxFileName(executable); wxPrintf(_("Usage:\n")); wxPrintf(fn->GetName() + _(" REMOVE \n")); wxPrintf(fn->GetName() + _(" INSTALL [options] \n")); wxPrintf(fn->GetName() + _(" DEBUG [options] \n")); wxPrintf(_("options:\n")); wxPrintf(_("-u \n")); wxPrintf(_("-p \n")); wxPrintf(_("-d \n")); wxPrintf(_("-t \n")); wxPrintf(_("-r =10, default 30)>\n")); wxPrintf(_("-l \n")); } //////////////////////////////////////////////////////////// void setupForRun(int argc, char **argv, bool debug, const wxString &executable) { if (!debug) { eventHandle = RegisterEventSource(0, serviceName.c_str()); if (!eventHandle) LogMessage(_("Couldn't register event handle."), LOG_ERROR); } setOptions(argc, argv, executable); } void main(int argc, char **argv) { // Statup wx wxInitialize(); wxFileName file = wxString::FromAscii(*argv++); file.MakeAbsolute(); wxString executable = file.GetFullPath(); if (argc < 3) { usage(executable); return; } wxString command; command = wxString::FromAscii(*argv++); if (command != wxT("DEBUG")) { serviceName = wxString::FromAscii(*argv++); argc -= 3; } else argc -= 2; if (command == wxT("INSTALL")) { wxString displayname = _("PostgreSQL Scheduling Agent - ") + serviceName; wxString args = wxT("RUN ") + serviceName; while (argc-- > 0) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'u': { user = getArg(argc, argv); break; } case 'p': { password = getArg(argc, argv); break; } case 'd': { displayname = getArg(argc, argv); break; } default: { args += wxT(" ") + wxString::FromAscii(*argv); break; } } } else { args += wxT(" ") + wxString::FromAscii(*argv); } argv++; } bool rc = installService(serviceName, executable, args, displayname, user, password); } else if (command == wxT("REMOVE")) { bool rc = removeService(serviceName); } else if (command == wxT("DEBUG")) { setupForRun(argc, argv, true, executable); initService(); #if START_SUSPENDED continueService(); #endif WaitForSingleObject(threadHandle, INFINITE); } else if (command == wxT("RUN")) { wxString app = _("pgAgent Service"); SERVICE_TABLE_ENTRY serviceTable[] = { (LPWSTR)app.wc_str(), serviceMain, 0, 0}; setupForRun(argc, argv, false, executable); if (!StartServiceCtrlDispatcher(serviceTable)) { DWORD rc = GetLastError(); if (rc) { } } } else { usage(executable); } return; } #endif // WIN32 pgAgent-3.3.0-Source/include/0000755000175200017520000000000012025076213014707 5ustar dpagedpagepgAgent-3.3.0-Source/include/misc.h0000644000175200017520000000110212025076213016005 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // misc.h - misc functions // ////////////////////////////////////////////////////////////////////////// #ifndef MISC_H #define MISC_H void WaitAWhile(const bool waitLong = false); void setOptions(int argc, char **argv, const wxString &executable); wxString getArg(int &argc, char **&argv); wxString NumToStr(const long l); #endif // MISC_H pgAgent-3.3.0-Source/include/pgAgent.ico0000644000175200017520000004631612025076213017002 0ustar dpagedpagehv 00h. 00>- I( @iEa.b/g/f/h.e/a2a5d4g6b2b2d2d1b5c5d6a8b9d9c9e9i?h8h:h=b2e1e0c4h?eAmFkHpKqHrJvWvV|Y}X}]]^`aaaknnuwuu}|ıĵŷȴµĸŷǿǺǾȿŶźɼɻ̻c#)k6/0&- ej=]+oA_l=(Z;bg"_AMCnBD" Q Z^,Q 94'FjrN8!Q$:2cQ8MM.@JNQV9DZVeF8+60VjQ $F_F EC 2Q Jrrrm][rc( @tY7~Z2z_2~_0}[4}]5t]:z_:}a3wb<|c;w_@kcGtbF}eArfLxdN}kK|mR~pR^/_1_1[5]6\5^3_6_7^:^;^4a-d+`/c,e.f.h.h-c.c/e.f.d/h.h/a2a1d2a6a5e7b1b1d2e1a5a5d4d4c9b9e9d;e>b8h?h;b2c2e1e0b5b5d4e6c1h2b8dCkFkCkLlIqAsLvIsOtNzOoPvSqTqWtX}^{[{U~UvY~[\u`ub{`{i`Z[VYdcljijisppuyw}egcknsxstq|}|~ïĴǺλŶȷŽìäƫɫέЬҭ±żɵ˲ɹ͹ȼʼο̻ѲӼܿմزռܿv]vz2G=GGAG==/A===(=$G=( gߖ==G((GZҕG-G( U̒ G-$$GSv{GG$$=rA G(($=hA$=TlG=(/=S=GG}=PPPGGG=֩A=/zcPGGPP-/=rv=*G=W}AA$-=PGGGPUG**=Q(-==G-=ZxԑAG**=2==A]GGG-GAiA***= ==TQPGGGP]Q\]U=**/=fD=GPQTG*P mUTGG**=cS===USP==E ň$rE$=G*=nSnjP=PGTb2bV(Q==GG*=nq إ=PG=S}a[*vP$GG*=nʶ،GGGGͱpGeG/=G*=csS=G====Vp*=eP/=G==Sr=====O*$E==[r=-===(A$$====P(G-G=bÁ=G=c=PP=GPb]G===P=G-PA æ=GPj A=GP=P =P==G/ATAP =====AjΆ2==A\E2AQݱW2 QkR222QŴn2\z{`8?<?(0` ^M8YS5y[,gS3mS3lY6dW8kU:fZ=n[9sV2}V4t[4z[3tV;{V9t];{\:}b,nc4vc6|`3ua={b;h;iXAw[Bk`GtaA|cC~iCwcI|kK|qNnjT|kUzqY|vd],\2[3V9\:_:_6b.b-i-i-e-c.h.b4d3h1h1c:c9i=d2d1h2i3a9b9j9j8jFpFpHpLgRlSoPqTsR|Uu]|[{T~Q|Z~Zvd|b^]^]eecgojjlkyrst|{ydmnmtsqzz||q{{}«IJɳóζ¸ʺѼȻн˼œğè¥ũƪȭ̬ɳ¼ȹòƱ˲̲ǿ̻ͻѷҵѻѾٽβӵڴڷջٻUUqf (6666( [ 6=6<<<5IK<266>>6!6=+:>46 6616684<(:(80=<00<)v+<60=108=)l+@11=<08<)X+<<<3<08=)RƮ+A<4=408<)J+<=>=601<@DΖ&#[:-=>3406<@Eofi,@>80041::D(1 :6<80421:dIITza)6(Hi+<>8804/(5%$^gcy:12>J\*:@<66<4>6(nաX<22<5Ht(@<<8@8><<85i66<4>1!&O:<<@::<<=><6 `Z :566<1:r Q B=<>6@@<=?>6E668><6k:;F5336::B=4?<(ߏ<42426R666lw43<76<4436Eg{\624466DԺ566:Dا<=<5<<==>(jn#TD:<<4426Eا:666+6=>5==?<5#:8224<69س561@:rF)==8==<:Rk%^r6<222<6:52<6:\!,@<<=<@: D MP(<2224<5D.?><6UK+A=426:!bm+r62>224>6G62=<:*\%+@<<>7 Y((pY+:F<2<<<416E+666/ qE6:::>B58(tW.|f*<1<<<326D((5:B6:@:>8G (qj.0(KN(@6<<>4155DZ56<@@2<:W MZv.21D,<0@><226:!Mܥ(<<=A2<5Eاgx#/2< -<0<<4425bi{p'<<<=<>5b%/==*k->0<<<426(瀽51<<<<61<6E<<=4>>2446KPuvQ(<68856<64.7p126)6*A>18<>486(tr 5((((,:<>68766>426~k(261(;33886567.X502/>@:66861:D511228:<<6<:><==<@7Y)<<>6K<6<<6<6<<tv5<3<666B6<643<<:: 66<<6q :6<<@<<<@"޾:1<6<6666<2?>:: R6><): :6<6@<@>5t:66;66666C<:FӿI5:56:{6668868:e58686566::YR(;5;nx'8866866:T`6866..6Ftm'0<.6}jD  H^W55MM5([ܧ||HEO|? ?`?? 0( @ɸkH|Ytc2f1aae0g/rJ䣁^f/g/h?ʾΪĴ}]f1h.c9򲞄b2ŸӭvVf3g/b3Ǻ~ʆd9f0nGȶc5d6d2e2︦e0e1mFb7µb/d3e0h:ǾĹ]e/e0İb6c4~d:d1c4nvW݉c6e/e0f9c2weAe1c9ĵqH|iĵb3e2e/IJiEuc4c4h=`b5཭f1e2f0ĸ}d0d2d2ǿȴb1ůd2e3e2t}Xa.c6e3e3d1d1uad3€`8c3c3i?`5d3d3e2e3pKb5a5Ž񵩎h8b3b9g6a1d8kŷd4f:ù( @ ǙjqTjʜp~Z2_1`2_6qȱ~fBe2d0f1f1d;|d@d2a4e4h/`2}񼪍a9f1c1f2h/`2g黦_6g/f/g/h/`3\͸`7e.f0g/i/`3sOƷ_7g0e/g/h._2lHĻʳŲ^7f0e0g/h.c6fBɝkƼ|mRpα];e3g0h.h.b1c;Ҙe`0a7øʹkcG^;e4g0h.h.b0a6ʸxdNu`ƴe>c,e2nOƱìе`_8e4f2f0g.e2_3ŷȶȺϊlEd4f-g/b5|hI¶̡̻w`7c6d4d6e3e0e0c3oPʵк}]4d5d1g1b1kĘutbF|Wܿ~pRe6d1d4b8c5d0h2d0cśgd2e0g1d3qJսn~_7c8c:˰a-d/d2d6e2f.f2a6ȰӾmJe0f.f.c3e~fCb2e5\6ʫa.c0e2d4e1f/c2tNÊs˦ðc;f1f.f.c2nηz_:e4c1a7tXѰe9c4e2c0d2d3c7~rfLȊubĵ㣋lc5e/e-f.c3hֽ~b;c1d1d7hJŧgC`7e1c/c3c9uOҭiGoMŒqWvՆiEc0e/d,g/d3~[h?e0f1c5dE¦jM^;e1f.d5z`;Ġ}[4XزmKjJ˷Ƕ[5e0e2e/f.c1|VjEb2b0b1iIƨjDa9d5f2h8{b=Ӵqb3aӳk@b2r\5e0d4e0f.d1Z|dB|_6lB^|^ϭf8c5b8d2c3nIˆz\}e8}[ֻqBd.fBڣ|`7g/e4e0f-d1\ʓbr_;r¸Ь`1c4b6d0d1mE˜}}^ɜzOc/\6ʸҜia5h,d3e0f.d1Zܿή~`/d1d3c0e1b5ƵտžҥYd0^3ѕu[c5k-e3e0g.e2{TœftͺϼthBe1f0f2d3e0f0qAͲڡVc.b2jϔwXe6g,e3e1f0e2nDԽh~^6_0^/a8b8f2f1g2d4d2g.e.c1u\<Ưi>e1c1~U͘`c4b0g4e3d2e3e6ŷïb6d/d+e2d4e4e4e4e2d2c2c2e1yYֺcd4e4d3|Uز}]6b2d4d4b2c3b3}Z࿋vSa6c1c1e4d3d3d3e/f/d3`7d>й~^5c2d1c4}`̽w_@c8c2c3d4d3c4wb<ˮtY7a5d2e4d3c2c2g3e3e7w^9}ȭnMc7d4b6}\7⴦zg=c1e3d2f3e4`8y`ƞwyg=d2e3e3c1c1`6`:oGǺմj@b2e5c8cDĻvIc5z_2{`3}`5cE{iƹڴoCb2~_0~_0a3kGzаʨ]a1b.~`1oKĴȷóȴĹȽǙylq°??????8(0`軩yd~f}soR3{X4|\4yZ3sV2zbCt\<\2a2c0c0f5^2tX5ᑂfuY3f5e0e1f1g1f2a5nP̈́rT{^7e3b-b3f3h1g/d3|a>ñٿzjK}a:d3e2`9d8g2i/d1tW0е~dCb9g4e.a5e5h1h/d2X0̰a>]0g0i-d0f/i.h/e5X1xȭ`=a4e/j/e1f0i.h0d4Y3nȭ];b6b-j/e1f0i.i0c3[5_ũ^<`5f1i/d0f0i.i0e4Y3}Zſɭ];`5f1i0d0f0i.h/a1\5rO˼붴ħ];`5e1i0d0f0i.h/d4b;kHк{rfneRk`Gd¸]<_5e1h0d0f0i.h/d4a9eC§kEzY0v̼wjW:dW8uмy[B_ƻ½审e\?VP2ĬƱzZ@`;d6f2i0i.h-h/c.d6`9нrmWkTwb윈o|]6b0e0_4~iNʵ¼锎v]W9˰ζ|Z;a9e6g2g1h0h.h.b-a2`6ų|m[^M8xgQr}yȰz^=e7b+e+i7yZ5qTٽӞa\8a8e6f3f4f2f0h/i4e5]2pƺ~[\2g5e,g,g0e6y_;nSѷ}[6c9c7d5e4e6e4f2f0e/f4b4wa@z㽵y[3f9e4g2g0g1e1x[3~lP׿影~s^l\B~{bkX7f:c3d3e4d6c8d6e2e0g1f2d3v_9mxbxY2f:b4c2e1h3b-e;ͽ⤖gW@r`Cs[7~Q׳nY7h:c/e1e3d7a;b8d3e0j4i3a.qF{bEc4d3f2h3f1d4]0k˺w_<|_:f=b9i?ΰ~nHa/e0d/d2e6c8d6e0f.j4c1\1ب߿y\8g2f/g-h.e/d6{]6ƶ`|^1c1a/c9{X5u婠xu^)e/c/d1g4e5f2g.g-c.b6lHËz蝕ǿ䝅hy[4e1f0g.g.c.d6g@u`@`5e3g5f;|Y4dIǦza0c1d1f2f2c5e2e0f0h5]2e㟍{mmX䷮wjeCc:g2f0g.h.d/c5kDĮt^=c7g4d0b6^6vW:ʼh;`1e5g4d/c2d2e2d1c3{_4ğϵqZCnkQɿjRBôλ{^9g9f1e/f-h/e0c4hA̵w_a1g/g2d.e,h/f1d6xY2ͻҌpKb1e.g0e3d6Y7fpU[?`c3d7c8c9f1f3}]4rPlZ6{`1x\-xដgc.h0}^*pUՌu]\9g6h.e5e2f0g.d-c2~a4äs`@v`;zb;lGñԸfĦڴc6c3c5c7b8d-d0a7~[ܻf[D|Uvd7e㵟~]+e0d2z\>̼˂gN_:d1i.e5e2f0g.f.e4c7ͭඨnMgV4{[ؾֳÜ\-f5c4c5b7f.g1a3oH­|Ͱ|onVѽ_.b-f4|W5Ѿ`F_:d.h*d4e2f0h.d-e4c7ƨp|ɳݪm\,e2d2d4c5e/f0d4`7ƴ޶oqp]ʭ`/e0b1X1jŹZ>_7h0l,e4e2f0h/f.d2y\/Ͼׯӵ«e=d2f0f1e2e4a.d/g4d4mAϬǪe4e0e3e9pJV9a7j2m,e4e2f0h.f/f5}`4¯ށpWu^>uPq|xX~[:`7e2g0g0g2f4e5f2f/g/a,f8sħª̸`/c,d0`1d8׻V9c:k1j)d4e2f0h/g0e4y\.~訓|rV6a:]2~[1\3\8b?b9f3g0g0i1h2c5c2h/i,g,f2x_3x^a5d4a0g4g5e3d2g5f4b/f>ϱÄkD`3d1e.e,e0e5e4e4e4e4e4e4f0e1d2c2b3e6d2weCȳӢ]`1g5f4g5c2z`5òӆqR{\6c5`/f5e5c3b2d4d3d2y^6|ݭypV/b7d3e1c0e4e4e4e4e4e4e4g-f.e0d4a6dgRo⾸Zb3|`2z]0x[.{^1|_2e9{[۽Ӵ^v^9c4~b+{^,z`1xa9dİŭʿȹϲ||ȹϴ|mZmZAs`?wd=pH^κ`??? ?( @xxxxxxxxxxxx<0?pgAgent-3.3.0-Source/include/job.h0000644000175200017520000000150312025076213015631 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // job.h - agent job // ////////////////////////////////////////////////////////////////////////// #ifndef JOB_H #define JOB_H #include class Job { public: Job(DBconn *conn, const wxString &jid); ~Job(); int Execute(); bool Runnable() { return status == wxT("r"); } protected: DBconn *threadConn; wxString jobid, logid; wxString status; }; class JobThread : public wxThread { public: JobThread(const wxString &jid); ~JobThread(); bool Runnable() { return runnable; } virtual void *Entry(); private: wxString jobid; bool runnable; Job *job; }; #endif // JOB_H pgAgent-3.3.0-Source/include/connection.h0000644000175200017520000000641412025076213017224 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // connection.h - database connection // ////////////////////////////////////////////////////////////////////////// #ifndef CONNECTION_H #define CONNECTION_H #include class DBresult; class DBconn { protected: DBconn(const wxString &, const wxString &); ~DBconn(); public: wxString qtDbString(const wxString &value); bool BackendMinimumVersion(int major, int minor); static DBconn *Get(const wxString &connStr, const wxString &db); static DBconn *InitConnection(const wxString &connectString); static void ClearConnections(bool allIncludingPrimary = false); static void SetBasicConnectString(const wxString &bcs) { basicConnectString = bcs; } static const wxString &GetBasicConnectString() { return basicConnectString; } wxString GetLastError(); wxString GetDBname() { return dbname; } bool IsValid() { return conn != 0; } bool LastCommandOk() { return IsCommandOk((ExecStatusType)lastResult); } bool IsCommandOk(ExecStatusType ret) { switch (ret) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: case PGRES_COPY_OUT: case PGRES_COPY_IN: #if (PG_VERSION_NUM >= 90100) case PGRES_COPY_BOTH: #endif return true; default: return false; }; } void SetLastResult(int res) { lastResult = res; } int GetLastResult() { return lastResult; } DBresult *Execute(const wxString &query); wxString ExecuteScalar(const wxString &query); int ExecuteVoid(const wxString &query); void Return(); private: bool Connect(const wxString &connectString); int minorVersion, majorVersion; protected: static wxString basicConnectString; static DBconn *primaryConn; wxString dbname, lastError, connStr; PGconn *conn; DBconn *next, *prev; bool inUse; int lastResult; friend class DBresult; }; class DBresult { protected: DBresult(DBconn *conn, const wxString &query); public: ~DBresult(); wxString GetString(int col) const; wxString GetString(const wxString &colname) const; bool IsValid() const { return result != NULL; } bool HasData() const { return currentRow < maxRows; } void MoveNext() { if (currentRow < maxRows) currentRow++; } long RowsAffected() const { return atol(PQcmdTuples(result)); } protected: PGresult *result; int currentRow, maxRows; friend class DBconn; }; class connInfo { public: connInfo() { isValid = false; connection_timeout = 0; port = 0; } private: wxString user; unsigned long port; wxString host; wxString dbname; unsigned long connection_timeout; wxString password; bool isValid; wxString getConnectionString(); static connInfo getConnectionInfo(wxString connStr); protected: bool IsValidIP(); friend class DBconn; }; #endif // CONNECTION_H pgAgent-3.3.0-Source/include/pgAgent.h0000644000175200017520000000237212025076213016451 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgAgent.h - main include // ////////////////////////////////////////////////////////////////////////// #ifndef PGAGENT_H #define PGAGENT_H // Disable all the GUI classes that might get pulled in through the headers #define wxUSE_GUI 0 #include #include "misc.h" #include "connection.h" #include "job.h" extern long longWait; extern long shortWait; extern long minLogLevel; extern wxString connectString; extern wxString serviceDBname; extern wxString backendPid; #ifndef __WXMSW__ extern bool runInForeground; extern wxString logFile; #endif // Log levels enum { LOG_ERROR = 0, LOG_WARNING, LOG_DEBUG, // NOTE: // "STARTUP" will be used to log messages for any LogLevel // Use it for logging database connection errors which we // don't want to abort the whole shebang. LOG_STARTUP = 15 }; // Prototypes void LogMessage(wxString msg, int level); void MainLoop(); #ifdef __WIN32__ #include void CheckForInterrupt(); HANDLE win32_popen_r(const TCHAR *command); #endif #endif // PGAGENT_H pgAgent-3.3.0-Source/job.cpp0000644000175200017520000002231312025076213014543 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012 The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // job.cpp - pgAgent job // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" #include #include #include #include #include #ifndef __WIN32__ #include #include #endif Job::Job(DBconn *conn, const wxString &jid) { threadConn = conn; jobid = jid; status = wxT(""); LogMessage(wxString::Format(_("Starting job: %s"), jobid.c_str()), LOG_DEBUG); int rc = threadConn->ExecuteVoid( wxT("UPDATE pgagent.pga_job SET jobagentid=") + backendPid + wxT(", joblastrun=now() ") wxT(" WHERE jobagentid IS NULL AND jobid=") + jobid); if (rc == 1) { DBresult *id = threadConn->Execute( wxT("SELECT nextval('pgagent.pga_joblog_jlgid_seq') AS id")); if (id) { logid = id->GetString(wxT("id")); DBresult *res = threadConn->Execute( wxT("INSERT INTO pgagent.pga_joblog(jlgid, jlgjobid, jlgstatus) ") wxT("VALUES (") + logid + wxT(", ") + jobid + wxT(", 'r')")); if (res) { status = wxT("r"); delete res; } delete id; } } } Job::~Job() { if (status != wxT("")) { threadConn->ExecuteVoid( wxT("UPDATE pgagent.pga_joblog ") wxT(" SET jlgstatus='") + status + wxT("', jlgduration=now() - jlgstart ") wxT(" WHERE jlgid=") + logid + wxT(";\n") wxT("UPDATE pgagent.pga_job ") wxT(" SET jobagentid=NULL, jobnextrun=NULL ") wxT(" WHERE jobid=") + jobid ); } threadConn->Return(); LogMessage(wxString::Format(_("Completed job: %s"), jobid.c_str()), LOG_DEBUG); } int Job::Execute() { int rc = 0; bool succeeded = false; DBresult *steps = threadConn->Execute( wxT("SELECT * ") wxT(" FROM pgagent.pga_jobstep ") wxT(" WHERE jstenabled ") wxT(" AND jstjobid=") + jobid + wxT(" ORDER BY jstname, jstid")); if (!steps) { status = wxT("i"); return -1; } while (steps->HasData()) { DBconn *stepConn; wxString jslid, stepid, jpecode, output; stepid = steps->GetString(wxT("jstid")); DBresult *id = threadConn->Execute( wxT("SELECT nextval('pgagent.pga_jobsteplog_jslid_seq') AS id")); if (id) { jslid = id->GetString(wxT("id")); DBresult *res = threadConn->Execute( wxT("INSERT INTO pgagent.pga_jobsteplog(jslid, jsljlgid, jsljstid, jslstatus) ") wxT("SELECT ") + jslid + wxT(", ") + logid + wxT(", ") + stepid + wxT(", 'r'") wxT(" FROM pgagent.pga_jobstep WHERE jstid=") + stepid); if (res) { rc = res->RowsAffected(); delete res; } else rc = -1; } delete id; if (rc != 1) { status = wxT("i"); return -1; } switch ((int) steps->GetString(wxT("jstkind"))[0]) { case 's': { wxString jstdbname = steps->GetString(wxT("jstdbname")); wxString jstconnstr = steps->GetString(wxT("jstconnstr")); stepConn = DBconn::Get(jstconnstr, jstdbname); if (stepConn) { LogMessage(wxString::Format(_("Executing SQL step %s (part of job %s)"), stepid.c_str(), jobid.c_str()), LOG_DEBUG); rc = stepConn->ExecuteVoid(steps->GetString(wxT("jstcode"))); succeeded = stepConn->LastCommandOk(); output = stepConn->GetLastError(); stepConn->Return(); } else { output = _("Couldn't get a connection to the database!"); succeeded = false; } break; } case 'b': { // Batch jobs are more complex thank SQL, for obvious reasons... LogMessage(wxString::Format(_("Executing batch step %s (part of job %s)"), stepid.c_str(), jobid.c_str()), LOG_DEBUG); // Get a temporary filename, then reuse it to create an empty directory. wxString dirname = wxFileName::CreateTempFileName(wxT("pga_")); if (dirname.Length() == 0) { output = _("Couldn't get a temporary filename!"); LogMessage(_("Couldn't get a temporary filename!"), LOG_WARNING); rc = -1; break; } ; if (!wxRemoveFile(dirname)) { output.Printf(_("Couldn't remove temporary file: %s"), dirname.c_str()); LogMessage(output, LOG_WARNING); rc = -1; break; } if (!wxMkdir(dirname, 0700)) { output.Printf(_("Couldn't create temporary directory: %s"), dirname.c_str()); LogMessage(output, LOG_WARNING); rc = -1; break; } #ifdef __WIN32__ wxString filename = dirname + wxT("\\") + jobid + wxT("_") + stepid + wxT(".bat"); #else wxString filename = dirname + wxT("/") + jobid + wxT("_") + stepid + wxT(".scr"); #endif // Write the script wxFile *file = new wxFile(); if (!file->Create(filename, true, wxS_IRUSR | wxS_IWUSR | wxS_IXUSR)) { output.Printf(_("Couldn't create temporary script file: %s"), filename.c_str()); LogMessage(output, LOG_WARNING); rc = -1; break; } if (!file->Open(filename, wxFile::write)) { output.Printf(_("Couldn't open temporary script file: %s"), filename.c_str()); LogMessage(output, LOG_WARNING); wxRemoveFile(filename); wxRmdir(dirname); rc = -1; break; } wxString code = steps->GetString(wxT("jstcode")); // Cleanup the code. If we're on Windows, we need to make all line ends \r\n, // If we're on Unix, we need \n code.Replace(wxT("\r\n"), wxT("\n")); #ifdef __WIN32__ code.Replace(wxT("\n"), wxT("\r\n")); #endif if (!file->Write(code)) { output.Printf(_("Couldn't write to temporary script file: %s"), filename.c_str()); LogMessage(output, LOG_WARNING); wxRemoveFile(filename); wxRmdir(dirname); rc = -1; break; } file->Close(); LogMessage(wxString::Format(_("Executing script file: %s"), filename.c_str()), LOG_DEBUG); // Execute the file and capture the output #ifdef __WIN32__ // The Windows way HANDLE h_script; DWORD dwRead; char chBuf[4098]; h_script = win32_popen_r(filename.wc_str()); if (!h_script) { output.Printf(_("Couldn't execute script: %s, GetLastError() returned %d, errno = %d"), filename.c_str(), GetLastError(), errno); LogMessage(output, LOG_WARNING); rc = -1; break; } // Read output from the child process if (h_script) { for (;;) { if(!ReadFile(h_script, chBuf, 4096, &dwRead, NULL) || dwRead == 0) break; chBuf[dwRead] = 0; output += wxString::FromAscii(chBuf); } } CloseHandle(h_script); rc = 1; #else // The *nix way. FILE *fp_script; char buf[4098]; fp_script = popen(filename.mb_str(wxConvUTF8), "r"); if (!fp_script) { output.Printf(_("Couldn't execute script: %s, errno = %d"), filename.c_str(), errno); LogMessage(output, LOG_WARNING); rc = -1; break; } while(!feof(fp_script)) { if (fgets(buf, 4096, fp_script) != NULL) output += wxString::FromAscii(buf); } rc = pclose(fp_script); if (WIFEXITED(rc)) rc = WEXITSTATUS(rc); else rc = -1; // set success status for batch runs, be pessimistic bt default if (rc == 0) succeeded = true; #endif // Delete the file/directory. If we fail, don't overwrite the script output in the log, just throw warnings. if (!wxRemoveFile(filename)) { LogMessage(wxString::Format(_("Couldn't remove temporary script file: %s"), filename.c_str()), LOG_WARNING); wxRmdir(dirname); break; } if (!wxRmdir(dirname)) { LogMessage(wxString::Format(_("Couldn't remove temporary directory: "), dirname.c_str()), LOG_WARNING); break; } break; } default: { output = _("Invalid step type!"); status = wxT("i"); return -1; } } wxString stepstatus; if (succeeded) stepstatus = wxT("s"); else stepstatus = steps->GetString(wxT("jstonerror")); rc = threadConn->ExecuteVoid( wxT("UPDATE pgagent.pga_jobsteplog ") wxT(" SET jslduration = now() - jslstart, ") wxT(" jslresult = ") + NumToStr(rc) + wxT(", jslstatus = '") + stepstatus + wxT("', ") wxT(" jsloutput = ") + threadConn->qtDbString(output) + wxT(" ") wxT(" WHERE jslid=") + jslid); if (rc != 1 || stepstatus == wxT("f")) { status = wxT("f"); return -1; } steps->MoveNext(); } delete steps; status = wxT("s"); return 0; } JobThread::JobThread(const wxString &jid) : wxThread(wxTHREAD_DETACHED) { LogMessage(wxString::Format(_("Creating job thread for job %s"), jid.c_str()), LOG_DEBUG); runnable = false; jobid = jid; DBconn *threadConn = DBconn::Get(DBconn::GetBasicConnectString(), serviceDBname); if (threadConn) { job = new Job(threadConn, jobid); if (job->Runnable()) runnable = true; } } JobThread::~JobThread() { LogMessage(wxString::Format(_("Destroying job thread for job %s"), jobid.c_str()), LOG_DEBUG); } void *JobThread::Entry() { if (runnable) { job->Execute(); delete job; } return(NULL); } pgAgent-3.3.0-Source/unix.cpp0000644000175200017520000000475412025076213014765 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // unix.cpp - pgAgent unix specific functions // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" // *nix only!! #ifndef WIN32 #include #include #include void usage(const wxString &executable) { wxFileName *fn = new wxFileName(executable); wxPrintf(_("Usage:\n")); wxPrintf(fn->GetName() + _(" [options] \n")); wxPrintf(_("options:\n")); wxPrintf(_("-f run in the foreground (do not detach from the terminal)\n")); wxPrintf(_("-t \n")); wxPrintf(_("-r =10, default 30)>\n")); wxPrintf(_("-s \n")); wxPrintf(_("-l \n")); } void LogMessage(wxString msg, int level) { wxFFile file; if (logFile.IsEmpty()) { file.Attach(stdout); } else { file.Open(logFile.c_str(), wxT("a")); } if (!file.IsOpened()) { wxFprintf(stderr, _("Can not open the logfile!")); return; } switch (level) { case LOG_DEBUG: if (minLogLevel >= LOG_DEBUG) file.Write(_("DEBUG: ") + msg + wxT("\n")); break; case LOG_WARNING: if (minLogLevel >= LOG_WARNING) file.Write(_("WARNING: ") + msg + wxT("\n")); break; case LOG_ERROR: file.Write(_("ERROR: ") + msg + wxT("\n")); exit(1); break; case LOG_STARTUP: file.Write(_("WARNING: ") + msg + wxT("\n")); break; } if (logFile.IsEmpty()) { file.Detach(); } else { file.Close(); } } // Shamelessly lifted from pg_autovacuum... static void daemonize(void) { pid_t pid; pid = fork(); if (pid == (pid_t) - 1) { LogMessage(_("Cannot disassociate from controlling TTY"), LOG_ERROR); exit(1); } else if (pid) exit(0); #ifdef HAVE_SETSID if (setsid() < 0) { LogMessage(_("Cannot disassociate from controlling TTY"), LOG_ERROR); exit(1); } #endif } int main(int argc, char **argv) { // Statup wx wxInitialize(); wxString executable; executable = wxString::FromAscii(argv[0]); if (argc < 2) { usage(executable); return 1; } argc--; argv++; setOptions(argc, argv, executable); if (!runInForeground) daemonize(); MainLoop(); return 0; } #endif // !WIN32 pgAgent-3.3.0-Source/pgAgent.rc0000644000175200017520000000260612025076213015203 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // $Id$ // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgAgent.rc - win32 Resources // ////////////////////////////////////////////////////////////////////////// #include // Icon (Don't remove the aaa prefix - it makes it the default icon) aaaPGAGENT ICON DISCARDABLE "include/pgAgent.ico" VS_VERSION_INFO VERSIONINFO FILEVERSION 3,3,0,0 PRODUCTVERSION 3,3,0,0 FILEOS VOS__WINDOWS32 FILETYPE VFT_APP BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "FileVersion", "3.3.0", "\0" VALUE "File Version", "3.3.0", "\0" VALUE "FileDescription", "pgAgent - PostgreSQL Scheduling Agent", "\0" VALUE "LegalCopyright", "\251 2002 - 2012, The pgAdmin Development Team", "\0" VALUE "LegalTrademarks", "This software is released under the PostgreSQL Licence.", "\0" VALUE "InternalName", "pgAgent", "\0" VALUE "OriginalFilename","pgagent.exe", "\0" VALUE "ProductName", "pgAgent", "\0" VALUE "ProductVersion", "3.3.0", "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 0x04E4 END END pgAgent-3.3.0-Source/pgAgent.cpp0000644000175200017520000001716112025076213015363 0ustar dpagedpage////////////////////////////////////////////////////////////////////////// // // pgAgent - PostgreSQL Tools // // Copyright (C) 2002 - 2012, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // pgAgent.cpp - pgAgent main entry // ////////////////////////////////////////////////////////////////////////// #include "pgAgent.h" #ifndef __WXMSW__ #include #endif wxString connectString; wxString serviceDBname; wxString backendPid; long longWait = 30; long shortWait = 5; long minLogLevel = LOG_ERROR; #define MAXATTEMPTS 10 #ifndef __WXMSW__ bool runInForeground = false; wxString logFile = wxEmptyString; #else //pgAgent Initialized void Initialized(); #endif int MainRestartLoop(DBconn *serviceConn) { // clean up old jobs int rc; LogMessage(_("Clearing zombies"), LOG_DEBUG); rc = serviceConn->ExecuteVoid(wxT("CREATE TEMP TABLE pga_tmp_zombies(jagpid int4)")); if (serviceConn->BackendMinimumVersion(9, 2)) { rc = serviceConn->ExecuteVoid( wxT("INSERT INTO pga_tmp_zombies (jagpid) ") wxT("SELECT jagpid ") wxT(" FROM pgagent.pga_jobagent AG ") wxT(" LEFT JOIN pg_stat_activity PA ON jagpid=pid ") wxT(" WHERE pid IS NULL") ); } else { rc = serviceConn->ExecuteVoid( wxT("INSERT INTO pga_tmp_zombies (jagpid) ") wxT("SELECT jagpid ") wxT(" FROM pgagent.pga_jobagent AG ") wxT(" LEFT JOIN pg_stat_activity PA ON jagpid=procpid ") wxT(" WHERE procpid IS NULL") ); } if (rc > 0) { // There are orphaned agent entries // mark the jobs as aborted rc = serviceConn->ExecuteVoid( wxT("UPDATE pgagent.pga_joblog SET jlgstatus='d' WHERE jlgid IN (") wxT("SELECT jlgid ") wxT("FROM pga_tmp_zombies z, pgagent.pga_job j, pgagent.pga_joblog l ") wxT("WHERE z.jagpid=j.jobagentid AND j.jobid = l.jlgjobid AND l.jlgstatus='r');\n") wxT("UPDATE pgagent.pga_jobsteplog SET jslstatus='d' WHERE jslid IN ( ") wxT("SELECT jslid ") wxT("FROM pga_tmp_zombies z, pgagent.pga_job j, pgagent.pga_joblog l, pgagent.pga_jobsteplog s ") wxT("WHERE z.jagpid=j.jobagentid AND j.jobid = l.jlgjobid AND l.jlgid = s.jsljlgid AND s.jslstatus='r');\n") wxT("UPDATE pgagent.pga_job SET jobagentid=NULL, jobnextrun=NULL ") wxT(" WHERE jobagentid IN (SELECT jagpid FROM pga_tmp_zombies);\n") wxT("DELETE FROM pgagent.pga_jobagent ") wxT(" WHERE jagpid IN (SELECT jagpid FROM pga_tmp_zombies);\n") ); } rc = serviceConn->ExecuteVoid(wxT("DROP TABLE pga_tmp_zombies")); wxString hostname = wxGetFullHostName(); rc = serviceConn->ExecuteVoid( wxT("INSERT INTO pgagent.pga_jobagent (jagpid, jagstation) SELECT pg_backend_pid(), '") + hostname + wxT("'")); if (rc < 0) return rc; while (1) { bool foundJobToExecute = false; LogMessage(_("Checking for jobs to run"), LOG_DEBUG); DBresult *res = serviceConn->Execute( wxT("SELECT J.jobid ") wxT(" FROM pgagent.pga_job J ") wxT(" WHERE jobenabled ") wxT(" AND jobagentid IS NULL ") wxT(" AND jobnextrun <= now() ") wxT(" AND (jobhostagent = '' OR jobhostagent = '") + hostname + wxT("')") wxT(" ORDER BY jobnextrun")); if (res) { while(res->HasData()) { wxString jobid = res->GetString(wxT("jobid")); JobThread *jt = new JobThread(jobid); if (jt->Runnable()) { jt->Create(); jt->Run(); foundJobToExecute = true; } else { // Failed to launch the thread. Insert an entry with // "internal error" status in the joblog table, to leave // a trace of fact that we tried to launch the job. DBresult *res = serviceConn->Execute( wxT("INSERT INTO pgagent.pga_joblog(jlgid, jlgjobid, jlgstatus) ") wxT("VALUES (nextval('pgagent.pga_joblog_jlgid_seq'), ") + jobid + wxT(", 'i')")); if (res) delete res; // A thread object that's started will destroy itself when // it's finished, but one that never starts we'll have to // destory ourselves. delete jt; } res->MoveNext(); } delete res; LogMessage(_("Sleeping..."), LOG_DEBUG); WaitAWhile(); } else { LogMessage(_("Failed to query jobs table!"), LOG_ERROR); } if (!foundJobToExecute) DBconn::ClearConnections(); } return 0; } void MainLoop() { int attemptCount = 1; // OK, let's get down to business do { LogMessage(_("Creating primary connection"), LOG_DEBUG); DBconn *serviceConn = DBconn::InitConnection(connectString); if (serviceConn && serviceConn->IsValid()) { serviceDBname = serviceConn->GetDBname(); // Basic sanity check, and a chance to get the serviceConn's PID LogMessage(_("Database sanity check"), LOG_DEBUG); DBresult *res = serviceConn->Execute(wxT("SELECT count(*) As count, pg_backend_pid() AS pid FROM pg_class cl JOIN pg_namespace ns ON ns.oid=relnamespace WHERE relname='pga_job' AND nspname='pgagent'")); if (res) { wxString val = res->GetString(wxT("count")); if (val == wxT("0")) LogMessage(_("Could not find the table 'pgagent.pga_job'. Have you run pgagent.sql on this database?"), LOG_ERROR); backendPid = res->GetString(wxT("pid")); delete res; res = NULL; } // Check for particular version bool hasSchemaVerFunc = false; wxString sqlCheckSchemaVersion = wxT("SELECT COUNT(*) ")\ wxT("FROM pg_proc ")\ wxT("WHERE proname = 'pgagent_schema_version' AND ")\ wxT(" pronamespace = (SELECT oid ")\ wxT(" FROM pg_namespace ")\ wxT(" WHERE nspname = 'pgagent') AND ")\ wxT(" prorettype = (SELECT oid ")\ wxT(" FROM pg_type ")\ wxT(" WHERE typname = 'int2') AND ")\ wxT(" proargtypes = '' "); res = serviceConn->Execute(sqlCheckSchemaVersion); if (res) { if (res->IsValid() && res->GetString(0) == wxT("1")) hasSchemaVerFunc = true; delete res; res = NULL; } if (!hasSchemaVerFunc) { LogMessage(_("Couldn't find the function 'pgagent_schema_version' - please run pgagent_upgrade.sql."), LOG_ERROR); } wxString strPgAgentSchemaVer = serviceConn->ExecuteScalar(wxT("SELECT pgagent.pgagent_schema_version()")); wxString currentPgAgentVersion; currentPgAgentVersion.Printf(_("%d"), PGAGENT_VERSION_MAJOR); if (strPgAgentSchemaVer != currentPgAgentVersion) { wxString strSchemaVerMisMatch; strSchemaVerMisMatch.Printf(_("Unsupported schema version: %s. Version %s is required - please run pgagent_upgrade.sql."), strPgAgentSchemaVer.c_str(), currentPgAgentVersion.c_str()); LogMessage(strSchemaVerMisMatch, LOG_ERROR); } #ifdef WIN32 Initialized(); #endif MainRestartLoop(serviceConn); } LogMessage(wxString::Format(_("Couldn't create the primary connection (attempt %d): %s"), attemptCount, serviceConn->GetLastError().c_str()), LOG_STARTUP); DBconn::ClearConnections(true); // Try establishing primary connection upto MAXATTEMPTS times if (attemptCount++ >= (int)MAXATTEMPTS) { LogMessage(wxString::Format(_("Stopping pgAgent: Couldn't establish the primary connection with the database server.")), LOG_ERROR); } WaitAWhile(); } while (1); }