This is pgin.tcl/INTERNALS, notes on internal implementation of pgin.tcl.
Last updated for pgin.tcl-1.3.9 on 2003-02-13
-----------------------------------------------------------------------------

INTERNAL IMPLEMENTATION NOTES:

This information is provided for maintenance, test, and debugging.

A connection handle is just a Tcl socket channel.

Multiple connections should work OK but have not been extensively tested.
Some variables are global to all connections (for example, what to
substitute for NULL).

Internal procedures, result structures, and other data are stored in a
namespace called "pgtcl".

A result structure is implemented as a variable result$N in the pgtcl
namespace, where N is an integer. (The value of N is stored in pgtcl::rn
and is incremented each time a new result structure is needed.) The result
handle is passed back to the caller as $N, just the integer. The result
structure is an array with three kinds of indexes, used to store values
which apply to the result as a whole, per-attribute values, and data
values.

The result structure array indexes in use are:

  Overall result values:
    result(conn)      The connection handle (the socket channel)
    result(nattr)     Number of attributes
    result(ntuple)    Number of tuples
    result(nmb)       Number of bytes in the data null-map
    result(status)    PostgreSQL status code, e.g. PGRES_TUPLES_OK
    result(error)     Error message if status is PGRES_FATAL_ERROR
    result(complete)  Command completion status, e.g. "INSERT 10101"
  Per-attribute values for each attribute K (K = 0, 1, ... nattr-1):
    result($K:name)   Name of Kth attribute
    result($K:type)   Type OID of Kth attribute
    result($K:size)   Length of Kth attribute in bytes or -1 if variable
    result($K:modif)  Modifier for attribute (e.g. varchar length+4)
  Data values for each tuple I (I = 0, 1, ... ntuples-1):
    result($I,$attr)  Data for tuple I, attribute named $attr.

The pg_exec call creates a new result structure. The pg_result call
retrieves information from the result structure and also frees the result
structure with the -clear option.  The result structure innards are also
directly accessed by some other routines, such as pg_select and pg_execute.
All result structures associated with a connection handle are freed when
the connection handle is closed by pg_disconnect.

The entire result of a query is stored before anything else happens (that
is, before pg_exec returns, and before pg_execute and pg_select process the
first row).  This is also true of libpq and libpgtcl, but Tcl is much
slower reading from the backend, and performance will suffer when queries
return large amounts of data. (The main slow-down seems to be when Tcl
needs to read null-terminated data from the socket, and has to get it one
character at a time.)

-----------------------------------------------------------------------------
NOTIFICATIONS

An array pgtcl::notify keeps track of notifications you want. The array is
indexed as pgtcl::notify(connection,name) where connection is the
connection handle (socket name) and name is the parameter used in
pg_listen. The value of an array element is the command to execute on
notification. Note that no data is passed - just the fact that the
notification occurred.

-----------------------------------------------------------------------------
LARGE OBJECTS

The large object calls are implemented using the PostgreSQL "fast-path"
function call interface (same as libpq). See the next section for more
information.

The pg_lo_creat command takes a mode argument. According to the PostgreSQL
libpq documentation, lo_creat should take "INV_READ", "INV_WRITE", or
"INV_READ|INV_WRITE".  (pgin.tcl accepts "r", "w", and "rw" as equivalent
to those respectively, but this is not compatible with libpgtcl.) It isn't
clear why you would ever create a large object with other than
"INV_READ|INV_WRITE".

The pg_lo_open command also takes a mode argument. According to the
PostgreSQL libpq documentation, lo_open takes the same mode values as
lo_creat.  But in libpgtcl the pg_lo_open command takes "r", "w", or "rw"
for the mode, for some reason. pgin.tcl accepts either form for mode,
but to be compatible with libpgtcl you should use "r", "w", or "rw"
with pg_lo_open instead of INV_READ, INV_WRITE, or INV_READ|INV_WRITE.


-----------------------------------------------------------------------------
FAST-PATH FUNCTION CALLS

Access to the PostgreSQL "Fast-path function call" interface is available
in pgin.tcl. This was written to implement the large object calls, and
general use is discouraged. See the libpq documentation for more details on
what this interface is and how to use it.

Internally, backend functions are called by their PostgreSQL OID, but
pgin.tcl handles the mapping of function name to OID for you.  The
fast-path function interface in pgin.tcl uses an array pgtcl::fnoids to
cache object IDs of the PostgreSQL functions.  One instance of this array
is shared among all connections, under the assumption that these OIDs are
common to all databases. (It is possible that if you have simultaneous
connections to multiple database servers running different versions of
PostgreSQL this could break.)

There is currently no provision for calling overloaded functions (same
name, different number or types of argument). The function is looked up by
name only, and results are unpredictable if the name is not unique.

If you supply the wrong number of arguments to the backend fast-path
function, the backend and front-end will lose synchronization and the
channel will be closed. This is true about libpq as well.  (What happens
is that the error message from the backend is sent without reading the
parameters, then the already-sent parameters are taken as an invalid
message.) This behavior is documented in the backend source code, and
it doesn't sound like they want to fix it. It also says there that a
function call inside an aborted transaction may also cause this disconnect.


The command is:

   pg_callfn $db "fname" result "arginfo" arg...

     Call a PostgreSQL backend function and store the result.
     Returns the size of the result in bytes.

     Parameters:

       $db is the connection handle.

       "fname" is the PostgreSQL function name. Must be unique; see above.

       "result" is the name of a variable where the PostgreSQL backend
       function returned value is to be stored. The number of bytes
       stored in "result" is returned as the value of pg_callfn.

       "arginfo" is a list of argument descriptors. Each list element is
       one of the following:
           I    An integer32 argument is expected.
           S    A Tcl string argument is expected. The length of the
                string is used (remember Tcl strings can contain null bytes).
           n (an integer > 0)
                A Tcl string argument is expected, and exactly this many
                bytes of the string argument are passed (padding with null
                bytes if needed).

       arg...   Zero or more arguments to the PostgreSQL function follow.
                The number of arguments must match the number of elements
                in the "arginfo" list. The values are passed to the backend
                function according to the corresponding descriptor in
                "arginfo".

     Examples:
        Note: These examples demonstrate the command, but in both of these
        cases you would be better off using an SQL query instead.

           set n [pg_callfn $db version result ""]
        This calls the backend function version() and stores the return
        value in $result and the result length in $n. 

           pg_callfn $db encode result {S S} $str base64
        This calls the backend function encode($str, "base64") with 2
        string arguments and stores the result in $result.


For PostgreSQL backend functions which return a single integer32 argument,
the following simplified interface is available:

   pg_callfn_int $db "fname" "arginfo" arg...
      
       The db, fname, arginfo, and other arguments are the same as
       for pg_callfn. The return value from pg_callfn_int is the
       integer32 value returned by the PostgreSQL backend function.

-----------------------------------------------------------------------------
MD5 AUTHENTICATION

MD5 authentication was added at PostgreSQL-7.2. This is a
challenge/response protocol which avoids having clear-text passwords passed
over the network. To activate this, the PostgreSQL administrator puts "md5"
in the pg_hba.conf file instead of "password". Pgin.tcl supports this
transparently; that is, if the backend requests MD5 authentication during
the connection, pg_connect will use this protocol. The MD5 implementation
was coded by the original author of pgin.tcl. It does not use the tcllib
implementation, which is significantly faster but much more complex.

-----------------------------------------------------------------------------
