TcLTK


TcL/TK

Tcl/TK is another language providing a powerful interface to PostgreSQL. Tcl/TK is widely used, especially for implementing graphical user interfaces. In this section, you will learn to build simple PostgreSQL enabled Tcl/TK applications.

The History of TcL

Tcl was invented by John Ousterhout in the 1980s at the University of California at Berkeley. The project grew out of his work on design tools for integrated circuits. Ousterhout and his students had some interactive tools for IC design, such as Magic and Crystal. Not much time was invested in the programming languages for the tools and finally every tool had its own programming language. As a result, the languages were in general very weak and not suitable for all demands.

During a sabbatical at DEC's Western Research Laboratory in the fall of 1987, the idea of an interpreted, embeddable command language was born. The idea was to build a language that could be used as library package in many applications. Tcl (Tool Command Language) should provide basic components of a programming language, such as control structures, variables , and procedures, while the application in which Tcl has to be included should provide additional features.

Embeddability is the most important aspect when dealing with Tcl and is strongly related with the three major goals of the language:

  • The language must easily be extensible, and new features have to fit into the concept as if they were part of the language from the beginning.

  • The language must be easy so that interaction with applications is easy. The language must not restrict the features an application provides.

  • Tcl is designed to glue the extensions of an application together and must have good facilities for integration.

The work on Tcl started in early 1988 and was more for academic purpose than for anything else. Ousterhout had already stopped working on IC design tools at that time, and he didn't think that anybody other than him was interested in an embeddable language. Well, he was wrong.

During that time, GUIs become more and more popular, and Ousterhout feared that it would become impossible for small teams to develop interactive applications if essential parts of an application were not parts of reusable components. Components for building GUIs would play an especially important part for small groups of developers.

He started to create a set of GUI components for Tcl. Tcl could be used to compile the GUI components to a graphical interface. This set of components became very widespread and is now known as Tk. Because Tk was only a part-time project, it took about two years until Tk became "useful."

In 1989, Ousterhout gave copies of Tcl away because people and companies were interested in an embeddable language. In January 1999, a paper about Tcl was presented at the USENIX Conference. Hundreds of people attended Ousterhout's talk, and many people started asking for Tcl. The Tcl source code become freely available on Berkeley's FTP site. Tcl spread over the Internet. Don Libes of the National Institute of Standards and Technology was one of the people at the USENIX Conference. When he heard of Tcl, he started hacking on a software called expect . The software was freely available after presenting a paper in the Summer of 1990. expect became widespread among system administrators and was the first Tcl application that was widely used. In late 1990, a version of Tk was also available, and the popularity of Tcl started to grow.

The number of people using Tcl grew quickly because Tcl/Tk was the easiest way to write graphical user interfaces on UNIX systems. Other toolkits, such as Motif, were much more complicated and could not be used without writing C code. It became obvious that writing Tcl/Tk code was five to ten times faster than writing a C program using Motif.

Over the years, the Tcl community grew rapidly . A huge community was formed and people exchanged their ideas and thoughts in mailing lists and newsgroups. In 1993, a workshop took place at Berkley. The Tcl community contributed many features, and a lot of code was even included in the core distribution of Tcl.

During that period, new releases were made every 6 to 12 months. Most new features were suggested by the community, and Ousterhout even introduced some sort of ranking of which features have to be included first. At the Tcl conference, this voting became famous as the so called "Ouster- votes . These votes were heavily discussed, and some people say that the vote was not always totally objective.

After a 14-year academic career, Ousterhout left Berkley in 1994 and started to build up a Tcl developers team at Sun Microsystems. He felt that Tcl needed some sort of commercial background to grow even more rapidly. Up to that time, every line of Tcl and Tk code was entirely written by himself. At Sun Microsystems, dozens of members worked together to develop Tcl. Tcl and Tk were ported to Windows and to the Macintosh. Over time, a lot of code was handed over to Sun developers, and Tcl became a wonderful cross-platform development environment. In spite of Sun being involved in the development, Tcl stayed free software. The number of users was still increasing, and several hundred thousand people were developing software by 1997 with the help of Tcl.

In late 1997, Ousterhout left Sun Microsystem and founded Scriptics (now Tcl) ”a company focused entirely on the development of Tcl. A lot of former Sun programmers joined Scriptics, and the first product shipped in September 1998. Tcl is still free software, and it is still going to be free software in the future.

Connecting

The first thing to do when working with a database is establishing a connection. In this section, we will see how this can be done with the help of Tcl.

We create a database called mytcl that will be used in this section:

 [hs@duron tcl]$  createdb mytcl  CREATE DATABASE 

We will use pgtclsh as the Tcl shell client. pgtclsh is a Tcl interface that includes the functions used for PostgreSQL. Connecting to the server is a simple task:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] puts "Connection successfully established"; pg_disconnect $conn; 

In this example, we connect to mytcl , write a message on the screen, and disconnect again.

Let's execute the script:

 [hs@duron tcl]$  ./connect.tcl  Connection successfully established 

Before we get into further details concerning connecting to a PostgreSQL database, the following is a short overview of pg_connect 's syntax:

  pg_connect -conninfo connectOptions  pg_connect dbName [-host hostName]   [-port portNumber] [-tty pqtty]   [-options optionalBackendArgs] 

Look at the next example:

 #!/usr/bin/pgtclsh set conn [pg_connect mytcl -host 195.34.143.8 -port 5432] puts "Connection successfully established"; pg_disconnect $conn; 

We have now defined the IP address of the host and the port to which the database is listening. If we execute the script, the connection can be established successfully:

 [hs@duron tcl]$  ./connect.tcl  Connection successfully established 

If you want to define options directly for the backend, you can use the -options flag. All flags specified in the -option flag are directly passed to the backend processes.

It might sometimes be useful to find out a little bit more about the default connection parameters. The next example shows how this information can be obtained and displayed onscreen:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl host=195.34.147.7 port=5432"] puts "Connection successfully established"; puts [ pg_conndefaults]; pg_disconnect $conn; 

If we execute the script, we can see the default parameters:

 [hs@duron tcl]$  ./connect.tcl  Connection successfully established { authtype Database-Authtype D 20 { } }  { service Database-Service { }  20 { } } { user Database-User { }  20 hs}  { password Database-Password * 20 { } }  { dbname Database-Name { }  20 hs}  { host Database-Host { }  40 { } }  { hostaddr Database-Host-IPv4-Address { }  15 { } } { port Database-Port { }  6 5432}  { tty Backend-Debug-TTY D 40 { } }  { options Backend-Debug-Options D 40 { } } 

The result is returned as a list of all possible connection options in a sublist and the default value of these options. The information can actually be obtained by using pg_connect -conninfo .

The parameters in the sublists returned have a special format:

 { optname label dispchar dispsize value} 

Simple Examples

In the first example, we show you how you can retrieve values from a table. We have included a table storing the information that can typically be found in a menu:

 CREATE TABLE "menu" (         "name" text,         "price" numeric(6,2) ); COPY "menu"  FROM stdin; Pork Chop Suey  5.19 Fish Chop Suey  6.49 Hamburger       1.39 Cheeseburger    1.49 French Fries    0.99 \ . 

What follows is a simple script that selects and displays the values in the first column:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT name FROM menu "] set ntups [pg_result $res -numTuples] for { set i 0}  { $i < $ntups}  { incr i}  {         puts stdout [lindex [pg_result $res -getTuple $i] 0] } pg_disconnect $conn; 

First we connect to the database. If the connection can be established, the script continues and executes the SQL command using the pg_exec command. pg_result finds the number of tuples returned by the query. We go through the result line-by-line and display the result onscreen. The output of the script is as follows:

 [hs@duron tcl]$  ./simple.tcl  Pork Chop Suey Fish Chop Suey Hamburger Cheeseburger French Fries 

If we want both columns of the query to be displayed, we can use a script such as follows:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT * FROM menu "] set ntups [pg_result $res -numTuples] for { set i 0}  { $i < $ntups}  { incr i}  {         puts stdout "[lindex [pg_result $res -getTuple $i] 0], \                 [lindex [pg_result $res -getTuple $i] 1] "; } pg_disconnect $conn; 

When we execute the script, we will receive a result such as follows:

 [hs@duron tcl]$  ./simple.tcl  Pork Chop Suey,  5.19 Fish Chop Suey,  6.49 Hamburger,  1.39 Cheeseburger,  1.49 French Fries,  0.99 

Up to now, we have not checked whether a query has been executed successfully. Tcl offers a very easy method to check whether PostgreSQL returned an error. Look at the following example:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT * FROM nothere "] set ntups [pg_result $res -numTuples] set error [pg_result $res -error] puts stdout "ntups: $ntups"; puts stdout "error: $error"; pg_disconnect $conn; 

The table called nothere is not in the database, so the query can not be executed:

 [hs@duron tcl]$  ./simple.tcl  ntups: 0 error: ERROR:  Relation 'nothere' does not exist 

error contains the description of the error. You can also see that the number of rows returned is .

You do not have to check the error string to find whether an error has occurred, because you can simply check the status of the query:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT * FROM nothere "] set status [pg_result $res -status] puts stdout "status: $status"; pg_disconnect $conn; 

The query returns a fatal error because the table does not exist in the database:

 [hs@duron tcl]$  ./simple.tcl  status: PGRES_FATAL_ERROR 

Sometimes, it can be useful to find how many attributes are returned by a query:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT name, price FROM menu "] set attrs [pg_result $res -numAttrs] puts stdout "attrs: $attrs"; pg_disconnect $conn; 

We select two columns from the table, so the number of attributes is 2 . -numAttrs is used to find the number of attributes.

 [hs@duron tcl]$  ./simple.tcl  attrs: 2 

As we expected, the script returns 2 .

It can also be useful to clear the result of a query. For that purpose, pg_result offers the appropriate functionality:

 #!/usr/bin/pgtclsh set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "SELECT name FROM menu "] set code [pg_result $res -clear] set ntups [pg_result $res -numTuples] puts stdout "ntups: $ntups"; pg_disconnect $conn; 

In this example, we try to access the result of a query that has already been reset. This will lead to an error:

 [hs@duron tcl]$  ./simple.tcl  Invalid result handlepgsql3.0 is not a valid query result     while executing "pg_result $res -numTuples"     invoked from within "set ntups [pg_result $res -numTuples]"     (file "./simple.tcl" line 8) 

We do not have a valid query result anymore, and the script fails.

Using PL/Tcl

Tcl is designed to be an embedded language. PostgreSQL provides an embedded version of Tcl as well, and you will take a closer look at it in this section.

An Overview of PL/Tcl

As we have already mentioned, PostgreSQL does not only offer one embedded language. Many people don't want to use PL/pgSQl for their function; they use PL/Tcl instead. PL/Tcl provides Tcl functionalities for PostgreSQL. Because Tcl is designed to be an embedded language, it is also suitable for PostgreSQL.

PL/Tcl code is executed in a safe Tcl interpreter. The user can perform almost all operations possible with C code. The only restriction is that only a few command are available to access the database via the SPI interface. There is also no way to access internal information about the backend process.

One of the most important restrictions of PL/Tcl is that no I/O functions can be created for new databases. In a way, this is a little bit uncomfortable because Tcl would be comfortable language for a purpose like that.

As we have mentioned before, PL/Tcl code is usually executed by a safe Tcl interpreter, which means that PL/Tcl can do nothing that harms the rest of the system (such as killing files or things such as that). If you want to build applications that use "unsafe" features, you can use PL/TclU (U for stands for untrusted). PL/TclU has to be installed as an untrusted language and functions can only be created by the superuser. The problem with untrusted functions is that a certain function may be used to damage a system. You have to keep in mind that a user can do the same things from inside the database that he or she can do with a shell program. This can lead to severe security holes.

Simple Examples

Let's get to some practical stuff. If we want to use PL/Tcl functions in our applications, we have to enable Tcl in the database:

 [hs@duron tcl]$  createlang pltcl mytcl  

This can simply be done using the createlang command. If the command has been executed successfully, the work can begin. Let's start with a simple example:

 CREATE FUNCTION mysum(int4, int4) RETURNS int4 AS '         return [expr  + ] ' LANGUAGE 'pltcl'; 

This function can be used to add two integer values. You can see that the head of the function looks exactly the same as the header of a PL/pgSQL function. The actual code of the function has to be passed to the database with single quotes.

After inserting the function into the database using a simple Makefile, we can try the function:

 mytcl=#  SELECT mysum(10, 30);  mysum -------     40 (1 row) 
Function Overloading

Functions in PostgreSQL can have the same name if the number of input parameters or the type of these parameter differs . This is a very convenient feature because it allows you to build applications that are much easier to understand.

Function overloading is not supported by Tcl itself. To enable function overloading for PL/Tcl, the internal procedure names contain the object ID of the procedures pg_proc row. With the help of this little trick, the name of the function is unique and can be used by Tcl.

To make the process clear, we have included an example:

 CREATE FUNCTION mysum(int4, int4) RETURNS int4 AS '         return [expr  + ] ' LANGUAGE 'pltcl'; CREATE FUNCTION mysum(float, float) RETURNS float AS '         return [expr  + ] ' LANGUAGE 'pltcl'; 

Both functions have the same name, and the only difference between the two functions is that the first function accepts integer values, while the second function can be used for floats only:

 mytcl=#  SELECT mysum(10.1, 30.1);  mysum -------   40.2 (1 row) 

In this example, the second function is used because the function is called with two floats.

If we take a closer look at pg_proc , we can see that the only difference between the two functions is the parameter list:

 mytcl=#  SELECT proname, prolang, proargtypes FROM pg_proc WHERE proname='mysum';  proname  prolang   proargtypes ---------+----------+-------------  mysum    11687448        23 23  mysum    11687448      701 701 (2 rows) 
Accessing a Database with PL/Tcl

PL/Tcl offers a lot of commands to access the database from inside a PL/Tcl function. One of the most important commands when accessing the database is spi_exec . The prefix spi in the name of the function does not occur perchance. Tcl uses the PostgreSQL SPI interface to interact with the database. We have included a very simple example:

 CREATE FUNCTION priceupdate(text, numeric) RETURNS bool AS '         spi_exec -array C "UPDATE menu SET price= WHERE name=''''"         return "t"; ' LANGUAGE 'pltcl'; 

priceupdate sets the price of the product defined by the first parameter to the price passed to the function in the second parameter.

Note

''$1'' has to appear with two single-quotes because it is a string that has to be quoted in SQL. Using only one single-quote would lead to a syntax error.


The function can easily be called. The only thing we need to be sure of is that the parameters passed to the function are cast to the right data type:

 mytcl=#  SELECT priceupdate('Hamburger'::text, '1.59'::numeric(4,2));  priceupdate -------------  t (1 row) 

If we look at the record for Hamburger , we can see that the price has changed.

 mytcl=#  SELECT * FROM menu WHERE name='Hamburger';  name     price  red_price -----------+-------+-----------  Hamburger   1.59  (1 row) 

UPDATE operations are an easy task because no result has to be extracted from the query. If you want to perform SELECT statements, this is different because you will need the result for further calculations.

We have included a simple example to show you how the result can be accessed by using a simple array:

 CREATE FUNCTION priceupdate(text) RETURNS text AS '         spi_exec -array C "SELECT * FROM menu WHERE name='''' LIMIT 1"         return "$C(name) - $C(price)"; ' LANGUAGE 'pltcl'; 

The result of the SQL statement is selected into an array called C . We can easily access it by indexing the array with the names of the columns we want to extract. If we want to find the price of a Hamburger , we can simply use the following SQL statement:

 mytcl=#  SELECT priceupdate('Hamburger'::text);  priceupdate ------------------  Hamburger - 1.59 (1 row) 

A similar result can also be achieved differently. In the next example, we prepare the query explicitly and execute the query plan in the next step:

 CREATE FUNCTION prodtext(text) RETURNS text AS '         set GD(plan) [ spi_prepare \ \                 "SELECT * FROM menu WHERE name='''' LIMIT 1" text ]         spi_execp -array C $GD(plan)          return "$C(name)"; ' LANGUAGE 'pltcl'; 

The result of this query is selected into the array called C again. The only difference in the output is that we don't display the price of the product:

 mytcl=#  SELECT prodtext('Hamburger'::text);  prodtext -----------  Hamburger (1 row) 

In some cases, it may be very useful to write small PL/Tcl functions that either add some logging information to the logfile or insert data into a separate logging table, especially for logging. However, the easiest way is to print some output into PostgreSQL's standard logfile. Tcl provides a function called elog :

 CREATE FUNCTION makelog(text, text) RETURNS text AS '         elog           return "t" ' LANGUAGE 'pltcl'; 

The first parameter defines the logging level. The levels supported by PostgreSQL are NOTICE , ERROR , FATAL , DEBUG , and NOIND .

Note

These levels are the same for the C function called elog .


If we execute the function, the message will be displayed onscreen and written into the logfile:

 mytcl=#  SELECT makelog('NOTICE', 'Function executed successfully');  NOTICE:  Function executed successfully  makelog ---------  t (1 row) 

Depending on how PostgreSQL is configured, the entry in the logfile may look different. The following is the entry declared by the test system:

 2001-05-28 16:54:45 [20309]  NOTICE:  Function executed successfully 
Writing Triggers with PL/Tcl

You sometimes have to write a trigger to make special events happen automatically. PL/Tcl is a good language for writing triggers, and we will take a closer look at how this can be done in this section.

Triggers can be defined on functions that return opaque . The return value of a trigger can be OK , which means that the trigger has been executed successfully, SKIP if the trigger manager has to suppress the operation, or a list as returned by the Tcl command array get .

Some important information is given to the function by the trigger manager. We have compiled an overview of all variables available in the PL/Tcl function:

  • $TG_name Contains the name of the trigger fired .

  • $TG_relid Contains the object ID of the table in which the trigger has been fired.

  • $TG_relatts $TG_relatts is a list with an empty element in first position that contains all entries in the pg_attribute system table. The position of an element in the list is the same as in pg_attribute .

  • $TG_when Contains either BEFORE or AFTER , depending on the configuration of the trigger.

  • $TG_level Contains ROW or STATEMENT , depending on the event for which the trigger is fired.

  • $TG_op Contains INSERT , UPDATE , or DELETE , depending on the event for which the trigger is fired.

  • $NEW In case of INSERT and UPDATE operations, $NEW contains the values of the new table row. In case of DELETE , the variable is empty.

  • $OLD In case of UPDATE and DELETE , $OLD contains the old values of the row that has to be updated. In case of an INSERT operation, the field is empty.

  • $GD $GD is the global data status array.

  • $args Contains a list of arguments passed to the function as defined in the CREATE TRIGGER statement.

To show you how a simple trigger works, we add a column called red_price to the menu table.

  ALTER TABLE menu ADD COLUMN red_price int4;  

This column will contain the reduced price of a product. To keep the function simple, the price is always set to 1 :

 mytcl=#  \   d   menu  Table "menu"  Attribute      Type      Modifier -----------+--------------+----------  name       text           price      numeric(6,2)   red_price  integer 

The code of the function is very easy indeed:

 CREATE FUNCTION trigfunc_addprice() RETURNS OPAQUE AS '         set NEW() 1;         return [array get NEW]; ' LANGUAGE 'pltcl'; CREATE TRIGGER trigger_addprice BEFORE INSERT ON menu         FOR EACH ROW EXECUTE PROCEDURE trigfunc_ addprice('price', 'red_price'); 

We first create a function called trigfunc_addprice that sets the value of the second argument to 1 . $NEW is returned by the function to tell PostgreSQL which values have to inserted into the database. After creating the function, we create the trigger on the menu table. The trigger has to be fired before inserting a value into the table. If we used the same function after the INSERT , nothing would happen because no value would be changed. Two parameters are passed to the function. In the previous example, these two parameters match the names of the column. We have included this here so that you can see how $NEW can be accessed.

Let's insert a value:

  INSERT INTO menu VALUES('Schnitzel', 10);  

menu has three columns, but we call INSERT with just two values; the third value will automatically be added by the trigger.

After the INSERT operation, the table will look as follows:

 mytcl=#  SELECT * FROM menu;  name       price  red_price ----------------+-------+-----------  Pork Chop Suey   5.19   Fish Chop Suey   6.49   Hamburger        1.39   Cheeseburger     1.49   French Fries     0.99   Schnitzel       10.00          1 (6 rows) 

We have added Schnitzel to the table and red_price has been set to 1 .

Removing a trigger from a table is very easy. You can see how the function and the trigger can be removed in the following:

  DROP TRIGGER trigger_addprice ON menu;   DROP FUNCTION trigfunc_addprice();  

Building a Simple Tcl/Tk Frontend for PostgreSQL

In this section, we will show you how you can build a simple user interface for Tcl/Tk. The shell used to build Tk applications in combination with Tcl is called wish . wish is used by a lot of scripts, such as the graphical configuration tool of the Linux kernel (try make xconfig ).

For our purposes, we cannot use wish because we need the PostgreSQL interface for Tcl. Instead of using pgtclsh as we did before, we have to use pgtksh . pgtksh is a Tcl shell with Tk and PostgreSQL functions. In reality, it is nothing more than wish with libpgtcl loaded.

In this section, we will present a simple example of a tool for querying a PostgreSQL database. We have tried to make the script as simple as possible:

 #!/bin/sh # the next line restarts using wish \ exec pgtksh " 
 #!/bin/sh # the next line restarts using wish \ exec pgtksh "$0" "$@" wm title . ExecLog # creates a frame frame .top -borderwidth 10 pack .top -side top -fill x # generate a command buttons button .top.quit -text Quit -command exit set but [button .top.run -text "Run it" -command Run] pack .top.quit .top.run -side right # label and entry field for the command label .top.l -text Command: -padx 0 entry .top.cmd -width 20 -relief sunken \ -textvariable command pack .top.l -side left pack .top.cmd -side left -fill x -expand true # binding functions to the buttons bind .top.cmd <Return> Run bind .top.cmd <Control-c> Stop focus .top.cmd # generting text widget for logging the output frame .t set log [text .t.log -width 70 -height 30 \ -borderwidth 2 -relief raised -setgrid true \ -yscrollcommand { .t.scroll set} ] scrollbar .t.scroll -command { .t.log yview} pack .t.scroll -side right -fill y pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true # connecting to the database and displaying the output on the screen proc Run { } { global command input log but puts stdout "$command" set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "$command "] set ntups [pg_result $res -numTuples] set attrs [pg_result $res -numAttrs] for { set i 0} { $i < $ntups} { incr i} { for { set j 0} { $j <= $attrs} { incr j} { $log insert end "[lindex [pg_result $res -getTuple $i] $j] " $log see end } $log insert end "\ n" $log see end } $log insert end "\ n" $log see end } # stop proc Stop { } { $but config -text "Run it" -command Run } 
" "$@" wm title . ExecLog # creates a frame frame .top -borderwidth 10 pack .top -side top -fill x # generate a command buttons button .top.quit -text Quit -command exit set but [button .top.run -text "Run it" -command Run] pack .top.quit .top.run -side right # label and entry field for the command label .top.l -text Command: -padx 0 entry .top.cmd -width 20 -relief sunken \ -textvariable command pack .top.l -side left pack .top.cmd -side left -fill x -expand true # binding functions to the buttons bind .top.cmd <Return> Run bind .top.cmd <Control-c> Stop focus .top.cmd # generting text widget for logging the output frame .t set log [text .t.log -width 70 -height 30 \ -borderwidth 2 -relief raised -setgrid true \ -yscrollcommand { .t.scroll set} ] scrollbar .t.scroll -command { .t.log yview} pack .t.scroll -side right -fill y pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true # connecting to the database and displaying the output on the screen proc Run { } { global command input log but puts stdout "$command" set conn [pg_connect -conninfo "dbname=mytcl"] set res [pg_exec $conn "$command "] set ntups [pg_result $res -numTuples] set attrs [pg_result $res -numAttrs] for { set i 0} { $i < $ntups} { incr i} { for { set j 0} { $j <= $attrs} { incr j} { $log insert end "[lindex [pg_result $res -getTuple $i] $j] " $log see end } $log insert end "\ n" $log see end } $log insert end "\ n" $log see end } # stop proc Stop { } { $but config -text "Run it" -command Run }

Take a good look at the first three lines of the script. Depending on how you have installed your system, the location of pgtksh may differ . This can be a little bit uncomfortable, especially if you want to run your software on multiple systems. Good old Bourne Shell offers a little trick to get around the problem:

 #!/bin/sh # the next line restarts using wish \ exec pgtksh " 
 #!/bin/sh # the next line restarts using wish \ exec pgtksh "$0" "$@" 
" "$@"

The first line must contain the full path to the shell. The second line contains a comment. In the third line, we call exec , replace the current shell (Bourne Shell), and pass all arguments passed to the Bourne Shell to the new shell. The only thing we have to take be sure is that the path to pgtksh is included in $PATH . It may be a real advantage if you don't have to take care where the shell can be found, specially if you have a lot of scripts, but let's have a look at the rest of the previous code.We first set the title of the main widget. In the second step, we create a frame and generate two command buttons. The first button is labeled "Run it," and the second button is labeled "Quit." Buttons are only useful if you bind certain operations to them. In our case, we bind the functions Run and Stop to the two buttons. Every time somebody clicks Run It, Run will be executed.

To display the output, we create widget frame for the text.

Run establishes a connection to the database and executes the command the user enters into the command line. The result is displayed in the field we created for the logging in the widget. After every query, a newline is displayed so the results of various queries can easily be distinguished.

Let's execute the script:

 [hs@duron tcl]$  ./tk.sh  

A widget will be displayed, as shown in Figure 9.5:

Figure 9.5. A tool for executing SQL commands.

graphics/09fig05.gif

With only 64 lines of code (including empty lines), we have written a quite comfortable user interface. I guess that this really shows how easy user interfaces can be built with the help of Tcl and Tk.



PostgreSQL Developer's Handbook2001
PostgreSQL Developer's Handbook2001
ISBN: N/A
EAN: N/A
Year: 2004
Pages: 125

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net