Feeds:
Posts
Comments

There are lots of great backup tools and utilities out there, but utilizing a simple script and cron task to target specific PostgreSQL database is often the fastest way to a locally based backup procedure.

The below shell script is run as the postgres user on a Linux version 2.6.9-42.0.3.ELsmp (Red Hat 3.4.6-3) with PostgreSQL 8.2.0 that has support for command-line execution. Not that there is anything fancy in the code that would require such specific versions.

Unique to the code is the ability to pass in a database name, or none at all to backup the entire cluster. This code only connects and writes files locally. Assumes appropriate permissions given to executing user. Maybe this works just fine for you, otherwise feel free to make your own modifications.

Personally I gzip the output, but improvements could be made to prevent hard disk saturation on larger databases and archiving utilities.

crontab

# CRON table for postgres user.

# run backup every night at 22:00 hours (10PM)
0 22 * * * /var/lib/pgsql/backups/backup.sh database_name

# run backup every week at midnight hour on sunday
0 0 * * 0 /var/lib/pgsql/backups/backup.sh

backup.sh

#!/bin/bash
# This script will backup the postgresql database
# and store it in a specified directory

# PARAMETERS
# $1 database name (if none specified run pg_dumpall)

# CONSTANTS
# postgres home folder backups directory
# !! DO NOT specify trailing '/' as it is included below for readability !!
BACKUP_DIRECTORY="/var/lib/pgsql/backups"

# Date stamp (formated YYYYMMDD)
# just used in file name
CURRENT_DATE=$(date "+%Y%m%d")

# !!! Important pg_dump command does not export users/groups tables
# still need to maintain a pg_dumpall for full disaster recovery !!!

# this checks to see if the first command line argument is null
if [ -z "$1" ]
then
# No database specified, do a full backup using pg_dumpall
pg_dumpall | gzip - > $BACKUP_DIRECTORY/pg_dumpall_$CURRENT_DATE.sql.gz

else
# Database named (command line argument) use pg_dump for targed backup
pg_dump $1 | gzip - > $BACKUP_DIRECTORY/$1_$CURRENT_DATE.sql.gz

fi

Standard installation of 4Dv11 comes with a set of macros to use in design mode.

The one I find most useful is the Header macro. This macro takes my name, date time stamp, the method name and places it directly into whatever type of method I’m working in.

Most analogous to function/class/package documentation blocks in other languages, they are also an excellent starting point for other developers looking at your code. In 4D specifically, header blocks also serve as insurance against a corrupted structure file.

After repairing a corrupted structure file, orphan method(s) can appear without context. The orphan methods are not linked to the original code, and the type of method (object/form/project) and original method name are lost.

Worse, the repair process could have replaced the original code in it’s entirety with a comment “automatically repaired method“.

The method_name tag of the macro generates the text as shown in the title portion of the method editor. So a project method will render Method: Project_Method while form and object methods will render Method: Form Method [Table]Form and Method: Object Method [Table].Form.Object respectively.

A header documents all the missing information to find the original code location and the confidence to delete or restore the orphan code. Having the headers in place has definitely saved me lots of time analyzing and recovering from a structure corruption.

Example Macro

<macro name="Header">
<text>` ----------------------------------------------------
` User name (OS): <user_os/>
` Date and time: <date format="0"/>, <time format="0"/>
` ----------------------------------------------------
` Method: <method_name/>
` Description
` <caret/>
`
` Parameters
` ----------------------------------------------------
</text>

Note The above macro is formatted for v11. 4D v12 has // for commenting out lines

A tech tip on creating header content automatically.
4D Tech Tip – Header Macro

In 4D v11/12 there is not a strict concept of try/catch blocks. Instead there is ON ERR CALL that can be used to trap errors.

A common challenge in production environments is identifying errors and reporting them without user initiative. The following code when included with the ON ERR CALL procedure is valuable in tracking down these problems.

The sample code builds a string with all the relevant error information. Additional information is added for each type of error. Here it displays an alert box, but this starting point could easily be written to database, or passed into an e-mail.

Feedback welcome, sound off in the comments, what do you do for error handling?

  ` ----------------------------------------------------
  ` User name (OS):
  ` Date and time: 
  ` ----------------------------------------------------
  ` Method: Generic_Error_Proc
  ` Description
  ` Specify with ON ERR CALL(&quot;Generic_Error_Proc&quot;) to
  ` capture and report 4D errors. This method can handle
  ` 4D Engine Errors, SQL Engine Errors and Trigger Errors
  ` It also collects some information about the execution context
  ` The diagnostic information included is intended for developers, not end users.
  ` 
  ` Parameters
  ` ----------------------------------------------------
  ` 4D does not pass in params to this function instead it sets
  ` System Variables Error, Error method, and Error Line (the latter two only in v12 http://kb.4d.com/search/assetid=76150)

C_LONGINT($errCode;$i)
C_TEXT($errText;$vt_TableName;$errODBC)
C_LONGINT($errSQLServer)

C_LONGINT($Error)
C_TEXT($Message)

C_POINTER($vp_ParentTable)
C_LONGINT($vl_RecordNum)

SQL GET LAST ERROR($errCode;$errText;$errODBC;$errSQLServer)

ARRAY INTEGER($codesArray;0)
ARRAY STRING(50;$internalCompArray;0)
ARRAY STRING(255;$textArray;0)

GET LAST ERROR STACK($codesArray;$internalCompArray;$textArray)

  ` general 4D error stack only if there is an error
If (Error#0)
	$Message:=$Message+&quot;General Message(Error Stack):&quot;+Char(Carriage return )
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
	$Message:=$Message+&quot;Error: &quot;+String(Error)+Char(Carriage return )
	  ` Available in 4Dv12 is Error Line and Error method process variables
	For ($i;1;Size of array($codesArray))
		  ` Do something with the element
		$Message:=$Message+String($codesArray{$i})+Char(Tab Key )+$internalCompArray{$i}+Char(Carriage return )+$textArray{$i}+Char(Carriage return )+Char(Carriage return )
	End for 
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
End if 

  ` return information if it is a trigger based error too
C_LONGINT($tLevel)
$tLevel:=Trigger level
If ($tLevel#0)
	C_LONGINT($dbEvent;$tableNum;$recordNum)
	TRIGGER PROPERTIES($tLevel;$dbEvent;$tableNum;$recordNum)
	
	$Message:=$Message+&quot;Trigger Message: &quot;+Char(Carriage return )
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
	$Message:=$Message+&quot;Table Name - Record Number: &quot;+Table name($tableNum)+&quot; - &quot;+String($recordNum)+Char(Carriage return )
	` Choose is 0 based, Database event is 1 based. so pad the results with a blank string
	$Message:=$Message+&quot;Event: &quot;+Choose($dbEvent;&quot;&quot;;&quot;Saving New&quot;;&quot;Saving Existing&quot;;&quot;Deleting&quot;)+Char(Carriage return )
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
	$Message:=$Message+Char(Carriage return )
	
End if 

  ` 4D SQL error stack (thrown by new SQL engine)
If ($errCode#0)
	$Message:=$Message+&quot;SQL MESSAGE: &quot;+Char(Carriage return )
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
	$Message:=$Message+&quot;Data Source: &quot;+Get current data source+Char(Carriage return )
	$Message:=$Message+&quot;Error Code: &quot;+String($errCode)+Char(Carriage return )
	$Message:=$Message+&quot;Error Text: &quot;+$errText+Char(Carriage return )
	$Message:=$Message+&quot;Error ODBC: &quot;+$errODBC+Char(Carriage return )
	$Message:=$Message+&quot;Error SQL Server: &quot;+String($errSQLServer)+Char(Carriage return )
	$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
	$Message:=$Message+Char(Carriage return )
End if 

  ` Finally lets collect some user information and timestamps
$vp_ParentTable:=Current form table
If (Not(Nil($vp_ParentTable)))
	$vl_RecordNum:=Record number($vp_ParentTable-&gt;)
	$vt_TableName:=Table name($vp_ParentTable)
Else 
	$vl_RecordNum:=-1
	$vt_TableName:=&quot;Nil&quot;
End if 

$Message:=$Message+Char(Carriage return )+&quot;Client Information: &quot;+Char(Carriage return )
$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )
$Message:=$Message+&quot;Current User: &quot;+Current user+Char(Carriage return )
$Message:=$Message+&quot;Current Machine: &quot;+Current machine+Char(Carriage return )
$Message:=$Message+&quot;Current form table: &quot;+$vt_TableName+Char(Carriage return )
$Message:=$Message+&quot;Current record num: &quot;+String($vl_RecordNum)+Char(Carriage return )
  ` only in 4D V12
  ` $Message:=$Message+&quot;Error method: &quot;+Error method+Char(Carriage return )
  ` $Message:=$Message+&quot;Error line: &quot;+String(Error Line)+Char(Carriage return )
$Message:=$Message+&quot;Date:  &quot;+String(Current date(*);Internal date long )+Char(Carriage return )
$Message:=$Message+&quot;Time:  &quot;+String(Current time(*);HH MM AM PM )+Char(Carriage return )
$Message:=$Message+&quot;_____________________________________&quot;+Char(Carriage return )

  ` alert this to the user. or store it in the database in the above for loops.
ALERT($Message)
Design a site like this with WordPress.com
Get started