PostSqlForms (psf) is a simple, but powerfull tool for PostgreSQL and SQLite databases. It enables you:
PostSqlForms is implemented in Tcl/Tk, but there is no need for you to program anything in Tcl/Tk. You only need SQL for creating tables and views and for designing forms and links.
PostSqlForms makes no attempt to hide the underlying SQL. On the contrary, in most cases, it shows both the SQL statements it generates and the results it gets back.
PostSqlForms has been designed and tested with:
For more information on Tcl/Tk look at http://www.tcl.tk.
For more information on PostgreSQL look at http://www.postgresql.org.
For more information on SQLite look at: http://www.sqlite.org
The PostSqlForms distribution includes a "kbskit", which provides a Tcl/Tk run time environment, for the following architectures:
For these architectures there is no need for the user to install Tcl/Tk separately.
This kbskit also includes the Tcl "sqlite3" package which makes it possible to access an SQLite database from a Tcl program. For more information about kbskit, look at http://sourceforge.net/projects/kbskit/.
PostSqlForms includes pgintcl, which is a Tcl interface for interacting with the PostgreSQL database server. For more information on pgintcl, look at http://sourceforge.net/projects/pgintcl/.
To use PostSqlForms with PostgreSQL databases, you need to have access to a PostgreSQL database server, which either is installed locally on your computer or which you can access via TCP/IP. Additionally, you need a local installation of the the client program 'psql'.
To use PostSqlForms with SQLite databases you need a locally installed sqlite3 program. A copy of this program is included in the PostSqlForms package for the linux-x86 and windows-32 architectures.
PostSqlForms (psf) is a frontend for PostgreSQL and SQLite databases.
Copyright © 2013 Willem Herremans
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
A copy of the GNU General Public License can be displayed with the menu item 'License' under 'Help'.
The home page of psf can be found at
http://sourceforge.net/projects/postsqlforms/
There you can report bugs, request new features and get support.
When you start the PostSqlForms (psf) application, the main window shows up. It displays a list of databases.
When you start psf for the first time, the list will be empty.
You can add databases to the list, by clicking the 'Add' button.
Then you can choose the type of database: a PostgreSQL or an SQLite database. The default database type is an option. See menu Tools → Options → General
Adding a PostgreSQL database does not create a new database on the PostgreSQL database server. What you are really adding is just a set of connection parameters that will enable you to connect to a database that has already been created on the database server.
The default values for the connection parameters 'host', 'port' and 'user' are psf options. So, it might be a good idea to first set them using menu Tools → Options → PostgreSQL.
Just like 'Add', the 'Modify' and 'Remove' operations are purely local and have no effect on the database server.
To open a database, make the database you want to open highlighted on the main window and then either press 'Return' or click on the 'Open' button.
What happens next, depends on the value of the 'usePGPASSWORD' option (menu Tools → Options → PostgreSQL).
If 'usePGPASSWORD' is on, you will be prompted to enter your PostgreSQL password.
If 'usePGPASSWORD' is off, you will not have to enter a password, but that will only work if you have a properly configured PostgreSQL password file. You can use menu Tools → Edit password file to set up a PostgreSQL password file.
In fact, it is recommended to set up a password file because the use of the PGPASSWORD environment variable is a security risk. See PostgreSQL documentation section 'libpq - C library', subsection 'Environment variables'.
When adding an SQLite database you are prompted to specify a name and a fully qualified filename of the SQLite database file. If that file does not exist yet, it will be created.
Modifying or removing a database from the list will not modify or remove the corresponding database file.
To open a database, make the database you want to open highlighted on the main window and then either press 'Return' or click on the 'Open' button.
You can browse the Help file by selecting "Help file" in the "Help" menu on the main window.
Pressing F1 on the main window has the same effect.
The help file is displayed in the web browser of your choice. See 4. Configuring PostSqlForms for how to choose a web browser.
By clicking the right mouse button the "context menu" pops up.
It is a classic "edit menu" with menu items: Copy, Cut and Paste.
You can use it to copy text to/from the clipboard.
This menu is not restricted to the main window, but works on all the windows of psf.
Printing can be invoked from the 'Run SQL' window for printing the text in the output text area (menu Output → Print) or from any 'text window' (menu Text → Print).
How printing is handled completely depends on the value of the 'printcmd' option. See help for option 'printcmd': invoke menu 'Tools → Options' then press 'Help' button of option 'printcmd'.
The 'printcmd' calls an application that accepts plain text as input. It can be either a printer program that directly sends the text to a printer, or a text editor or word processor that accepts the text generated by psf as input and that offers you the possibility to choose things such as font, font size, margins, page orientation etc. before sending it to a printer from there on.
The command may contain parameters for which you will be prompted to provide a value when the command is called.
It is possible to operate psf without using the mouse. Not all keyboard procedures are explicitly mentioned in this manual because they follow some general rules which make it very easy to know them.
Clicking with the mouse on a widget moves the input focus to that widget. The widgets on a window that can receive input focus are arranged in a circular list. Pressing the Tab key moves the input focus forward along the list. Pressing Shift-Tab moves the input focus backward along the list.
Selecting "License" in the "Help" menu on the main window causes the "GNU General Public License" to be displayed.
Selecting "About" in the "Help" menu on the main window displays some general information about PostSqlForms, such as: version, copyright, contact information, Tcl/Tk run-time environment, interface to PostgreSQL and SQLite, installation directory.
After opening a database a database window pops up:
This window has 3 tabs:
When you open a database, psf checks for the presence of the pfm_* tables. These tables contain the definitions of forms and reports. The acronym "pfm" stems from "Postgress Forms", the predecessor of PostSqlForms. This acronym is kept in PostSqlForms for backward compatibilty with databases that were accessed with "Postgress Forms".
If psf does not find the pfm_* tables, you get a hint that you can install them using the 'SQL → Install pfm_* tables' or the 'SQL → Install example database ...' menus.
On a database without pfm_* tables, psf only allows you to execute SQL statements using the Execute SQL feature.
On a database with pfm_* tables, psf allows you to also design and use forms and reports.
When you open a database that was previously managed by an older version of psf, you will be asked whether you want to convert the pfm_* tables to the new format.
If you choose "yes", the pfm_* tables are converted to the format compatible with the new version of psf.
Note:
There is no way back: i.e. there is no automatic conversion from the new to the old format. So, to be sure, it is recommended to first make a backup of your database before answering "yes" to this question.
If you choose "no", the database is opened but not modified in any way. Since the format of the pfm_tables is not compatible with the new version of psf, some features like defining and using forms and reports may not work properly, but you will be able to execute SQL statements using the Execute SQL feature of psf.
You can close a database in any of the following ways:
Whatever method you use, both the database and the database window are closed.
To install the pfm_* tables in the currently opened database, select 'install pfm_* tables' from the SQL menu on the database window.
There are two example databases included in the psf package:
Using 'SQL → Install example database', you can install one of these example databases in the currently opened database. This database should be completely empty. It should not even contain the pfm_* tables.
See section 11. The example databases for more details on the structure of the example databases.
When you invoke the menu SQL →Execute SQL on the database window the SQL window is opened:
It has two text areas, one for the SQL statements, another for the output.
This tool is in fact nothing more than a GUI-frontend for the 'psql' or 'sqlite3' tools that are included in PostgreSQL and SQLite repectively.
When you press the [Run] button, all the text present in the SQL statement text area is sent to psql or sqlite3.
Don't forget to end SQL statements with ";". If you forget this, the statement is not executed until you type ";" and press [Run].
Sometimes, the Run SQL feature appears to be dead, i.e. it is not responding to commands anymore. In many cases that is due to mismatched parentheses or quotes. This happens more often with PostgreSQL than with SQLite databases. E.g. if you enter
SELECT * FROM pfm_form WHERE (name='pfm_attribute';
psql does not respond until it gets a ')'. If you run psql in an xterm, it displays a prompt ending with '(>', indicating that there is still an unmatched '('. In psf however, psql does not display this prompt, with the result that it does not seem to respond. If you cannot guess what is wrong, you can close and reopen the SQL window to recover from this frustrating situation.
Apart from SQL statements, you can also enter the psql "\" or the sqlite3 "." commands. E.g.
These functions can also be invoked from the Help and Sql menus on the "Run SQL" window.
Every time you press [Run] the commands in the SQL area are stored in the command history. You can scroll through the command history using the [Previous] end [Next] buttons.
Note:
Alt-p and Cntrl-Up are alternatives for the [Previous] button.
Alt-n and Cntrl-Down are alternatives for the [Next] button.
There is no need to press the Run button if you end an SQL statement with ';<Return>'. Similarly, if the command you are typing begins with a '\' (for psql) or with a '.' (for sqlite3), the command is sent to psql (sqlite3) when you press '<Return>'.
The output area is not cleared when another run is done, i.e. the output of a new run is appended to the text already present in the output area. You can clear the output area explicitly by invoking the menu Output → Clear.
Note:
The way in which the "run SQL" feature is implemented is fundamentally different for PostgreSQL and SQLite.
For PostgreSQL, the client program "psql" is called once when the SQL window is opened. Every time the [Run] button is pressed, the command(s) in the upper window are offered for execution to "psql", after which, "psql" continues to wait for new commands until the SQL window is closed.
For SQLite, a simlilar implementation would not work because the Windows implementation of the "sqlite3" command line shell buffers all output to "standard error" until "sqlite3.exe" exits. So, the implementation that was used for "psql" would not allow the user to see error messages generated by "sqlite3".
For SQLite, the command line shell "sqlite3" is not called when the SQL window is opened. Instead, every time the [Run] button is pressed, the "sqlite3" program is called and the command(s) in the upper window are offered for execution, after which "sqlite3" exits.
As a consequence, the "."-commands that modify the "state" of "sqlite3", such as .headers, .mode, .separator etc., do not work as expected because each time [Run] is pressed, another instance of the "sqlite3" command line shell is called which assumes the default settings for all the things that can be changed by "."-commands.
To alleviate this inconvenience a little, the SQL window for SQLite has some widgets that allow you to set the output mode, the headers and the separator. These settings are passed on to "sqlite3" via command line options on each sqlite3 invocation.
You can also import SQL statements from a file by selecting "Import SQL from file" in the SQL menu on the "Run SQL" window.
After having selected the file to be imported, you are asked to select the character encoding. The default is the same as your system's default encoding. Usually, that is OK, but if you know that the file to be imported was made with another character encoding, you can select it here.
You are also asked whether to offer the file to psql (sqlite3) for import or to import the file into the SQL window. For large files it is recommended to offer the file to psql (sqlite3).
Notes:
The text in the output area can be printed or saved to a file, by selecting respectively "Print" or "Save" in the "Output" menu on the "Run SQL" window.
PostSqlForms has a number of options that are stored in a text file. On UNIX platforms that file is
~/.postsqlforms
On Windows platforms that file is
APPDATA\postsqlforms\postsqlforms.conf
where APPDATA is the user's application dirctory, e.g.
C:\Documents and Settings\'username'\Application Data
These options can be viewed and modified by selecting the Tools → Options menu item:
For each option you can get dedicated help by clicking the button at the right.
If you start PostSqlForms for the first time, the options have their default values. On later occasions, the options are read from the options file.
When a form is opened, by pressing the [Open] button or the Return key on the database window, you will see a window such as this:
This window shows the SQL SELECT statement that will be executed to load the data into the form.
You can modify the 'WHERE' and 'ORDER BY' clauses.
The 'paste attribute Name' and 'past attribute Value' menus may help you to do so.
After pressing the 'Run' button, the SQL SELECT statement is executed, the form is displayed in a new tab of the current window and the data resulting from the SQL SELECT statement are loaded into the form's internal buffer.
The form window looks like this:
The upper text field shows some information on how the form was opened. This will become more interesting when you have followed one or more links.
The lower text field displays the SQL statements that were issued by psf. On this example, it shows the SQL statement that was issued to load the data in to the form's internal buffer.
The middle part of the window is the actual form. It shows:
Note:
If a LIMIT clause has been specified in the definition of the form, the behaviour is slightly different from the one described above. See 5.11. Forms with a LIMIT-clause in their definition for details.
The right side of the form window shows the link buttons which can be used to follow 'one-to-many' or 'many-to-one' relationships to other forms. See 5.7. Links to other tables for more details.
After you press the [Update] button, PostSqlForms reloads the current record.
If that is successful, the form's table attributes become modifiable, the buttons [OK] and [Cancel] are displayed and all the other buttons on the form are deactivated. The form's status becomes "Updating".
The calculated fields and the attributes of tables other than the form's main table are read-only and do not become modifiable.
The labels for the attributes that you can modify are shown in red colour.
If you press [OK]:
Note:
If a LIMIT clause has been specified in the definition of the form, the behaviour is slightly different from the one described above. See 5.11. Forms with a LIMIT-clause in their definition for details.
If you press [Cancel], the modifications are discarded.
While a record is displayed on your screen, it may be deleted by another user. This can happen :
In the first case, this is noticed by PostSqlForms when you press the [Update] button. In the second case it is noticed when you press the [OK] button. In both cases, the record is marked as deleted and the update operation is cancelled.
Similarly, while a record is displayed on your screen, it may be modified by another user. This can happen:
In the first case, the 'reload record', which is executed when you press [Update], refreshes the screen and the update proceeds as described above.
In the second case, PostSqlForms notices that the record has been modified by another user when you press [OK]. Then, the update operation is cancelled, and you are notified.
After you have pressed the [Add] button, the displayed record's attributes become modifiable in the same way as for "update record". The attributes for which a default value has been defined in pfm_attribute now get their default value on the screen. The buttons [OK] and [Cancel] are displayed and all the other buttons on the form are deactivated. The form's status becomes "Adding".
If you press [OK], the new record is stored, both in the data base and in the form's internal buffer. In the form's internal buffer, it becomes the last record. This means that even if the new record's values are such that the record would not be selected by the original query, it stays in the internal buffer until the form is closed.
Similarly, the record gets the last position in the internal buffer, even if the that is not the position it would get according to the initial "ORDER BY" clause.
If you press [Cancel], the new record is discarded, i.e. it is neither stored in the data base, nor in the form's internal buffer.
After pressing [OK]:
Note:
If a LIMIT clause has been specified in the definition of the form, the behaviour is slightly different from the one described above. See 5.11. Forms with a LIMIT-clause in their definition for details.
If you press the [Delete] button, the current record is deleted from the data base. It is not deleted from the internal buffer, but its attributes are all put to an empty string and its status field is put to "Deleted".
Note:
If a LIMIT clause has been specified in the definition of the form, the behaviour is slightly different from the one described above. See 5.11. Forms with a LIMIT-clause in their definition for details.
The status field is a property of a record in the form's internal buffer. It is kept in the internal buffer until the form is closed and it is displayed on the form. The possible values are:
Whenever there is a one-to-many relationship between two database tables, you can define links between the corresponding forms.
When a form is displayed, all the links originating from that form, are displayed as buttons in the 'links' pane.
So, links are navigation tools that allow you to follow the one-to-many relationships in a database.
Note that you can define a link in the "one-to-many" direction and another one in the "many-to-one" direction of a relationship.
Pressing a link button, brings you to another form in which the records, related to the originally displayed record, are loaded.
This form is opened in a new tab of the current window. By switching tabs you can move back to the form and record from which the link has originated.
Every time you follow a link, another tab is created. So, after having followed one or more links, you get a chain of records in which each record is represented by a tab on the window.
All tabs in the chain, except the last one, are 'locked'. That means that no operations are allowed on them.
You can 'unlock' a 'locked tab', by pressing the 'unlock tab' button or the 'Escape' key. That also deletes all tabs to the right of it.
Links are defined in the pfm_link table. See 6.5. pfm_link for details.
Note:
Altough "many-to-many" relationships do exist in the real world, they are implemented as two "one-to-many" relationships, using an auxilliary table.
E.g. if you have a database containing tables for persons and groups of persons, you can have a many-to-many relationship between persons and groups: a person can be member of more than one group, and a group can contain more than one person.
To implement such a database, you would not only need tables "person" and "group", but also a table "membership". The "many-to-many" relationship between "person" and "group" is then implemented as a "one-to-many" relationship between "person" and "membership" and another "one-to-many" relationship between "group" and "membership".
So, to find all the members of a group, you first follow the one-to-many relationship to the membership table, and from there the many-to-one relationship to the person table.
After pressing [Update] or [Add], you can fill out a form, i.e. provide values for the table's attributes.
You can provide values for the attributes in one of the following ways, depending on the attribute's "type of get" (see section 6.2. pfm_attribute):
Notes:
For a "view" it is possible to define a form in PostSqlForms in the same way as for a "table", but it is not possible to update a view.
The buttons [Update], [Add] and [Delete] are absent on a form for a view.
The "Search" menu on the "Form" window contains a list of the form's attributes. After having selected an attribute, you get a search bar in the form window in which you can specify a value for that attribute.
You can then search the internal buffer for the next record with the specified value for that attribute.
The designer of the form can specify a LIMIT-clause to limit the number of records that is loaded into the form's internal buffer. This makes it possible to handle large tables without causing excessive memory usage. It is almost, but not entirely transparent for the user of the form.
As an example, we consider a form for which 'sqllimit = 100' has been specified in pfm_form and for which there are 523 records in the database.
When the form is opened, not all 523, but only the first 100 records are loaded into the internal buffer. Since psf does not know how many records have not been loaded, it displays the the record number for records as '1/?', '2/?', ... '100/?'.
When you move beyond record 100, the internal buffer is first cleared and then reloaded with the next 100 records.
The same thing happens again when you move beyond records 200, 300 etc...
Finally, when moving beyond record 500, psf only finds 23 instead of 100 records. Then it assumes that 523 is the last record and displays the record numbers as '501/523', '502/523', ... '523/523'.
When moving backwards through the records in the internal buffer, e.g. when moving from record 501 to record 500, the internal buffer is first cleared and then reloaded with the records 401 through 500.
Also searching the internal buffer with the Search menu, causes psf to load another set of 100 records when required.
There are a few cases where this automatic clearing and reloading of the internal buffer has some confusing effects.
By invoking the "Help" menu on the "Form" window you get help information for filling out the currently displayed form.
This is the text that is stored in the "help" attribute of the table "pfm_form".
For the pfm_* forms, this help text is provided by psf, but for the other forms that information has to be provided by the designer (probably you) of the form. See also section 6.1. pfm_form
Per data base, psf uses tables with a name starting with "pfm_" to store the definition of forms and reports. The acronym "pfm" stems from "Postgress Forms", the predecessor of PostSqlForms. This acronym is kept in PostSqlForms for backward compatibilty with databases that were accessed with "Postgress Forms".
A form allows the user to administer the data of just one table. This table is henceforth referred to as "the form's main table".
However, a form also has an SQL SELECT statement, which generates the data that are displayed on it.
In the simplest case the SQL SELECT statement is just:
SELECT <attributes of main table> FROM <main table>
In that case, the data which can be administered and the data which are displayed on the form are the same.
In more complex cases, the <main table> can be JOINED with other tables, which makes it possible to display data of other related tables as well. These data cannot be modified by means of the form.
The table "pfm_form" has the following attributes:
Note:
If pkey is empty, or if pkey does not uniquely determine a record, all Update, Add and Delete operations will fail.
Notes:
LIMIT sqllimit OFFSET 0
is added to the form's SELECT when opening the form.
This means that only 'sqllimit' records are loaded into the form's internal buffer. When the user moves beyond the last record in the internal buffer, the internal buffer is first cleared and then reloaded with the next 'sqllimit' records by re-executing the form's SELECT but now with another OFFSET in the LIMIT clause.
The form's main table is defined by tablename. Only the data of that table can be administered by using the form.
All the data generated by the form's SQL SELECT statement can be displayed on the form. The SQL SELECT statement is defined by:
Note:
The WHERE clause provided by the user when opening the form, becomes a HAVING clause, if there is a GROUP BY clause.
The following rules should be observed when filling out sqlselect and sqlfrom:
tablename: person pkey: id sqlselect: id, christian_name, name, street, town, "ZIPcode", country, category, description sqlfrom: person groupby: -
tablename: memberlist pkey: group person sqlselect: memberlist."group", memberlist.person, p.christian_name, p.name sqlfrom: memberlist LEFT OUTER JOIN person p ON (p.id = memberlist.person) groupby: -
The table "pfm_attribute" defines all the properties of form attributes.
It has the following attributes:
Hint:
In general, all attribute values must be quoted, exept the values or expressions for numeric attributes.
Note:
Even with tgDirect it is possible to enter an expression as new value for an attribute, but then the expression is evaluated by PostgreSQL or SQLite whereas with tgExpression, the expression is first evaluated by Tcl before the SQL statement is issued.
Note:
All calculated attributes and all attributes from tables other than the form's main table should be declared 'read-only'. If this rule is not observed, the Add and Update operations on this form will fail.
Note :
The sqlselect may return more than 1 attribute. If so, all the attributes are displayed in the list-box, but only the first one is used for updating the attribute.
Example:
default: =SELECT nextval('seq_person_id')
In this example the default value is the next value of the sequenece 'seq_person_id'.
The table "pfm_value_list" contains all the value lists of all the forms.
Its only attribute is
The table "pfm_value" contains all the values of the lists defined in pfm_value_list.
It has the following attributes:
A link is a navigation tool which allows you to follow a "one-to-many" or "many-to-one" relationship from one form to another.
Every link is stored as a record in the pfm_link table, which has the following attributes:
Note:
PostSqlForms does not provide any checks to safeguard the referential integrity of the data base in case of updates or deletions. However, PostgreSQL and SQLite provides these functions as 'foreign key' table constraints.
The table pfm_report defines all the reports for the current data base.
pfm_report has the following attributes:
The sqlselect may contain one or more parameters for which a value is requested at "Run report" time. A parameter in the sqlwhere must be formatted as $(parameter_name).
Example:
sqlselect: SELECT g.name AS "group", g.description, p.id, p.name, p.christian_name, p.street, p."ZIPcode", p.town, p.country FROM "group" g LEFT JOIN memberlist m ON g.name = m."group" LEFT JOIN person p ON m.person = p.id WHERE "group" = '$(group)' ORDER BY g.name, p.name, p.christian_name
When the report is run, the user is prompted to enter a value for the parameter "group". Then the report data are generated by executing the sqlselect statement in which $(group) is replaced with the value entered by the user.
The data returned by the report's SQL SELECT statement may be considered as a table with a column for each 'field' specified after the word 'SELECT' and with a row for each record.
By specifying an 'ORDER BY' clause in the report's SQL SELECT statement, it is possible to group rows with the same values for some fields together.
The report generator has an "economy" algorithm which avoids printing the same data repeatedly.
To control this you have to distribute the fields (columns) of the table over n sections such that section 1 contains the fields that are changing least frequently (when moving from one row to the next), section 2 contains the fields that are changing more frequently, and section n contains the fields that are changing at every row.
When the data of the first row of the table are printed, the data of section 1 are printed first. Then, on the following line, indented by one tab stop, the data of section 2 are printed. Then, on the following line, indented by 2 tab stops, data of section 3 are printed, etc.
[section 1] <--- row 1 [section 2] <--- row 1 [section 3] <--- row 1
Then, when the next rows are being printed, data of the lower numbered sections are only printed if they are different from the data of the last printed section of the same number:
[section 1] [section 2] [section 3] <--- row 1 [section 3] <--- row 2 [section 3] <--- row 3 [section 2] [section 3] <--- row 4 [section 3] <--- row 5 [section 1] [section 2] [section 3] <--- row 6 [section 3] <--- row 7
The report generator also enables you to print a summary at every point where a higher numbered section is about to be followed by a lower numbered section:
[section 1] [section 2] [section 3] <--- row 1 [section 3] <--- row 2 [section 3] <--- row 3 [summary 3] [section 2] [section 3] <--- row 4 [section 3] <--- row 5 [summary 3] [summary 2] [section 1] [section 2] [section 3] <--- row 6 [section 3] <--- row 7 [summary 3] [summary 2] [summary 1]
A summary i is printed just before a lower numbered section j (j < i). Its data can be calculated:
In particular, summary 1 is printed at the end of the report, is calculated from all the sections of the report and may be calculated from all the fields.
A record in pfm_section defines a section and a summary of a report.
The table pfm_section has the following attributes:
The fieldlist is a space separated list of field specifiers:
field_spec_1 field_spec_2 ... field_spec_N
where each field specifier is formatted as follows:
{field_i label_i alignment_i max_length_i}
where :
Notes :
For every section, the layout can be defined as:
The summary must be formatted as a space separated list of summary specifiers:
summary_spec_1 summary_sepc_2 .... summary_sepc_N
where each summary_spec is formatted as follows:
{field_i aggregate_i format_i}
where:
In general, the aggregate functions, use the same "economy" algorithm that is used for printing section data.
When all the fields of a section, which is not the highest numbered section of the report, have the same values for a number of consecutive rows, this section's data are only printed once for these rows.
Similarly, these rows are only counted once by the aggregate functions applied to a field of this section.
The aggregate functions that can be used in a summary are:
SQRT (SUM( (value_i - AVG(value))**2 ) / (N - 1) )
where :
Here is a short overview of the 'ANSI C sprintf' formatting string. In general its form is:
%'MinWidth'.'Precision''Conversion'
where:
Example: %1d
prints an integer and uses as many characters as required.
Example: %1.2f
prints a floating point number wiht 2 digits after the decimal point and uses as many characters as required.
Example: %1.5E
prints a floating point number in the form x.yyyyy E±zz
Example: %1.4G
prints 2345.0 as 2345
prints 234567.0 as 2.346E+05
prints 0.003456 as 0.003456
prints 0.00003456 as 3.456E-05
The following drawing shows the structure the pfm_* tables:
The arrows represent "one-to-many" or "1 to n" relationships between the tables. The conditions shown on the arrows are the WHERE clauses that have to be applied when following the "one-to-many" relationships.
On the corresponding forms these one-to-many relationships are represented by "link buttons". They allow you to follow the "one-to-many" relationships.
Designing a form is in fact nothing more than filling out the data base tables pfm_forms, pfm_attribute, pfm_value_list, pfm_value and pfm_link defined in section 6. The pfm_* tables.
This is done using the forms that are predefined for these tables.
PostSqlForms does not offer the possibility to define data base tables or to modify their definition. That can be done using SQL statements CREATE TABLE ... or ALTER TABLE .... using the "Run SQL" function of PostSqlForms (see 3.1. Run SQL) or from 'psql' ('sqlite3'), the interactive SQL terminal of PostgreSQL (SQLite).
To modify or add a form:
First proceed as explained in the previous section to make the form you want to work on visible in the pfm_form form.
Then, follow the 'Attributes' link by pressing the [Attributes] button. This results in the form pfm_attribute being opened with all the attributes of the selected form loaded in the internal record buffer.
Here you can add, delete or modify the form's attribute definitions as appropriate. Note, that it is possible for the form to have less attributes than the table corresponding to the form, but all the attributes defined in the form must be returned by the SQL SELECT statement related to the form.
See section 6.2. pfm_attribute for a description of the attributes of pfm_attribute.
It may be necessary to add a value list for some attributes. See section 7.4. Defining value lists.
When finished, press [Back] to return to the form defining the selected form.
First proceed as explained in the section 7.1. Modifying or adding a form to make the form you want to work on visible in the pfm_form form.
To define links originating from the selected form, press the [Originating links] button. Then add, modify, delete links as appropriate. See section 6.5. pfm_link for a description of the attributes of pfm_link.
When finished, close the tab "pfm_link" to return to the form defining the selected form.
To define links terminating on the selected form, press the [Terminating links] button. Then add, modify, delete links as appropriate.
When finished, close the tab "pfm_link" to return to the form defining the selected form.
Links allow you to jump from one table/form to another while taking into account the relationships between the tables.
To modify an existing value list, select the form 'pfm_value_list' in the database window and press the [open] button.
Then, specify a "WHERE" clause to select that value list and press the [Run] button. This results in the selected value list to be displayed. Here you can modify it's name or delete it.
To add, modify or delete the list's values, press the [Values] button. This opens the pfm_value form with all the list's values. Here, you can modify, add, delete values.
To define a new value list, select the form 'pfm_value_list' in the database window and press the [open] button.
Then, don't specify a "WHERE"-clause, press [Add] and specify the name of the new value list.
To insert values for the list, press the [Values] link-button.
To run a report, select the 'Reports' tab on the database window, select a report on the list-box and press the [Run] button.
The output appears in a new window.
For some reports you may be prompted to specify a value for some parameters.
The first thing you need to design a report is an SQL SELECT statement which generates the data you need for the report.
You can use the "Run SQL" function of PostSqlForms to design this SQL SELECT statement.
Let us take an example from the addressbook sample database (see 11.1. The addressbook sample database for more information).
Let us assume that we want to list all groups and the persons that are member of these groups. The following SELECT statement generates the "raw" data that we need for that report:
SELECT g.name AS "group", g.description, p.id, p.name, p.christian_name, p.street, p."ZIPcode", p.town, p.country FROM "group" g LEFT JOIN memberlist m ON g.name = m."group" LEFT JOIN person p ON m.person = p.id ORDER BY g.name, p.name, p.christian_name
The result is:
group | description | id | name | christian_name | street | ZIPcode | town | country ---------+-------------------------------------+----+--------------+----------------+----------------------+---------+-----------+--------- cycling | Cycling companions | 10 | Van Horebeke | Norbert | Hoogstraat 3 | 9620 | Zottegem | Belgium family | Members of the family | 8 | Lemmens | Nancy | Copernicuslaan 198 | 9000 | Gent | Belgium family | Members of the family | 10 | Van Horebeke | Norbert | Hoogstraat 3 | 9620 | Zottegem | Belgium family | Members of the family | 6 | Van Riel | Hugo | Kerkstraat 56 | 2520 | Ranst | Belgium psf | Persons involved in the psf project | 1 | Brouwers | Adriaan | De Coninckstraat 23 | 8750 | Zwevezele | Belgium psf | Persons involved in the psf project | 2 | Van de Perre | Albert | Schanslaan 45 | 2600 | Berchem | Belgium tennis | Acquaintances from the tennis club | 10 | Van Horebeke | Norbert | Hoogstraat 3 | 9620 | Zottegem | Belgium tennis | Acquaintances from the tennis club | 6 | Van Riel | Hugo | Kerkstraat 56 | 2520 | Ranst | Belgium tennis | Acquaintances from the tennis club | 2 | Van de Perre | Albert | Schanslaan 45 | 2600 | Berchem | Belgium tennis | Acquaintances from the tennis club | 3 | Verdonck | Nelly | Azalealaan 33 | 9080 | Lochristi | Belgium work | Acquaintances from work | 1 | Brouwers | Adriaan | De Coninckstraat 23 | 8750 | Zwevezele | Belgium work | Acquaintances from work | 8 | Lemmens | Nancy | Copernicuslaan 198 | 9000 | Gent | Belgium work | Acquaintances from work | 12 | Van Geluwe | Adri | Jan Breydelstraat 21 | 8511 | Aalbeke | Belgium work | Acquaintances from work | 3 | Verdonck | Nelly | Azalealaan 33 | 9080 | Lochristi | Belgium (14 rows)
PostSqlForms has tools to easily layout a report for these data. That is explained in the next section.
Enter the design mode by selecting the 'Design' tab on the database window.
Then select the form pfm_report and open it.
Don't specify a "WHERE" clause, press [Run] and then [Add].
Then fill out the form as follows:
SELECT g.name AS "group", g.description, p.id, p.name, p.christian_name, p.street, p."ZIPcode", p.town, p.country FROM "group" g LEFT JOIN memberlist m ON g.name = m."group" LEFT JOIN person p ON m.person = p.id ORDER BY g.name, p.name, p.christian_name
Then press the [Sections] link button to define the sections and their layout.
A group having more than 1 member is listed more than once in the "raw" output of the report's SELECT statement.
Because of the order specified in the SELECT statement, all records related to a group are grouped together.
By defining a report with 2 sections (levels), we can avoid printing the data for a group more than once.
Section 1 will only print the fields related to group.
Section 2 will only print the fields related to a person.
When the report is printed, the records are printed in the order specified by the SELECT statement.
When the first record is printed, first section 1 data are printed, then section 2 data.
For every next record, the section 1 data are only printed if they differ from the data of the previously printed section 1 data. Then section 2 data are printed.
In this example, section 1 is a so called "group level" section.
In general, all sections, except the highest numbered section, are "group level" sections.
Possible section definitions for our example are shown below:
level : 1 layout : row fieldlist : {group group l} {description description l} summary : {group COUNT} level : 2 layout : table fieldlist : {id id r} {christian_name "Chr. name" l} {name name l} {street street l} {ZIPcode ZIP l} {town town l} {country country l} summary : {id COUNT}
See 6.7. pfm_section for more details on section definitions.
When this report is run (Tab 'Reports' on database window, Select "Persons and groups", press [Run]), this is the result:
Groups and persons ------------------ Description: Lists all groups and their members SQL : SELECT g.name AS "group", g.description, p.id, p.name, p.christian_name, p.street, p."ZIPcode", p.town, p.country FROM "group" g LEFT JOIN memberlist m ON g.name = m."group" LEFT JOIN person p ON m.person = p.id ORDER BY g.name, p.name, p.christian_name Date : 13-Dec-2004 group: cycling; description: Cycling companions; id | Chr. name | name | street | ZIP | town | country ----+-----------+--------------+----------------------+------+-----------+--------- 10 | Norbert | Van Horebeke | Hoogstraat 3 | 9620 | Zottegem | Belgium Summary: COUNT(id) = 1 group: family; description: Members of the family; id | Chr. name | name | street | ZIP | town | country ----+-----------+--------------+----------------------+------+-----------+--------- 8 | Nancy | Lemmens | Copernicuslaan 198 | 9000 | Gent | Belgium 10 | Norbert | Van Horebeke | Hoogstraat 3 | 9620 | Zottegem | Belgium 6 | Hugo | Van Riel | Kerkstraat 56 | 2520 | Ranst | Belgium Summary: COUNT(id) = 3 group: psf; description: Persons involved in the psf project; id | Chr. name | name | street | ZIP | town | country ----+-----------+--------------+----------------------+------+-----------+--------- 1 | Adriaan | Brouwers | De Coninckstraat 23 | 8750 | Zwevezele | Belgium 2 | Albert | Van de Perre | Schanslaan 45 | 2600 | Berchem | Belgium Summary: COUNT(id) = 2 group: tennis; description: Acquaintances from the tennis club; id | Chr. name | name | street | ZIP | town | country ----+-----------+--------------+----------------------+------+-----------+--------- 10 | Norbert | Van Horebeke | Hoogstraat 3 | 9620 | Zottegem | Belgium 6 | Hugo | Van Riel | Kerkstraat 56 | 2520 | Ranst | Belgium 2 | Albert | Van de Perre | Schanslaan 45 | 2600 | Berchem | Belgium 3 | Nelly | Verdonck | Azalealaan 33 | 9080 | Lochristi | Belgium Summary: COUNT(id) = 4 group: work; description: Acquaintances from work; id | Chr. name | name | street | ZIP | town | country ----+-----------+--------------+----------------------+------+-----------+--------- 1 | Adriaan | Brouwers | De Coninckstraat 23 | 8750 | Zwevezele | Belgium 8 | Nancy | Lemmens | Copernicuslaan 198 | 9000 | Gent | Belgium 12 | Adri | Van Geluwe | Jan Breydelstraat 21 | 8511 | Aalbeke | Belgium 3 | Nelly | Verdonck | Azalealaan 33 | 9080 | Lochristi | Belgium Summary: COUNT(id) = 4 Summary: COUNT(group) = 5
PostSqlForms internally uses the 16 bit UNICODE character encoding from Tcl. When PostSqlForms reads text from a file or from a Tk widget, it converts the text to the Tcl 16 bit UNICODE encoding. By default, PostSqlForms assumes that files are encoded using the system's default encoding. However, when importing an SQL-file you have the possibility to change the encoding which is used to read the file.
Both PostgreSQL and SQLite have the possibility to choose a character encoding at the time the database is created.
The Tcl packages "pgintcl" and "sqlite3", make sure that the necessary encoding conversions are made when text is transferred to/from the databases PostgreSQL and SQLite.
Notes:
Here are some hints about SQL. For full documentation see PostgreSQL documentation.
CREATE TABLE Customer ( Id serial, Name text);
is implicitly and silently converted to:
CREATE TABLE customer ( id serial, name text);
If you really want Customer, Id and Name, you have to write:
CREATE TABLE "Customer" ( "Id" serial, "Name" text);
CREATE TABLE order ( ...
is not accepted because 'order' is an SQL keyword, but you can use
CREATE TABLE "order"
to create a table with the name 'order'.
Using menu 'SQL → Install example database' on the database window, you can install one of the example databases in the currently opened database. This database should be completely empty. It should not even contain the pfm_* tables.
The first example database is the addressbook database. The following figure shows its structure:
This database shows a typical example of a many-to-many relationship: a person can belong to more than one group, a group can have more than 1 person as member. Such a relationship is implemented as a combination of 2 one-to-many relationships:
Both relationships are implemented as "links" in psf:
The referential integrity of the database is guaranteed by "foreign key constraints".
The second example data base which is included in the distribution is somewhat more complicated. It is a database with customers, invoices, invoice details and products.
The following image gives an overview of the structure of the customerdb.
Here follow the table definitions.
CREATE TABLE customer ( nr integer PRIMARY KEY, "first name" text, name text, address text, "E-mail" text, telephone text); CREATE TABLE invoice ( nr integer PRIMARY KEY, "date" date, customer integer, "discount%" numeric, FOREIGN KEY (customer) REFERENCES customer(nr) ON UPDATE CASCADE ON DELETE CASCADE); CREATE TABLE "invoice detail" ( invoice integer, product integer, quantity integer, "unit price" numeric, PRIMARY KEY (invoice, product), FOREIGN KEY (invoice) REFERENCES invoice(nr) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (product) REFERENCES product(nr) ON UPDATE CASCADE ON DELETE RESTRICT); CREATE TRIGGER unit_price AFTER INSERT ON "invoice detail" FOR EACH ROW BEGIN UPDATE "invoice detail" SET "unit price" = (SELECT price FROM product WHERE (product.nr = NEW.product)) WHERE ("invoice detail".invoice = NEW.invoice) AND ("invoice detail".product = NEW.product); END;
The foreign key constraints guarantee the referential integrity of the database.
The trigger "unit_price" copies the "price" from the product table to the "unit price" column of "invoice detail" table when a new "invoice detail" row is added, i.e. at the time of sale. When the "price" in the product table is changed after the time of sale, the "unit price" in the "invoice detail" table is not changed.