#==========================================================
# Database --
#
#   provides a layer of abstraction for common DB operations
#
#==========================================================
#
namespace eval Database {}


#----------------------------------------------------------
# getTableIndexes --
#
#   returns a list index names in a table
#
# Arguments:
#   table_  name of a view or table (required)
#   dbh_    the db handle (optional)
#
# Results:
#   list of names of the indexes on the table
#----------------------------------------------------------
#
proc ::Database::getTableIndexes {table_ {dbh_ ""}} {

    global CurrentDB

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}


    set id [::Connections::getIdFromHandle $dbh_]
    set V  $::Connections::Conn(pgversion,$id)

    if {$V < 7.3} {

        set sql "
            SELECT relname
              FROM pg_class
             WHERE oid IN (
            SELECT indexrelid
              FROM pg_index I, pg_class C
             WHERE (C.relname='$table_')
               AND (C.oid=I.indrelid))"

    } else {

        set sql "
            SELECT relname
              FROM pg_catalog.pg_class
             WHERE oid IN (
            SELECT indexrelid
              FROM pg_catalog.pg_attribute A, pg_catalog.pg_index I
             WHERE (A.attrelid='$table_'::regclass)
               AND (A.attrelid=I.indrelid))"

    }

    set tilist {}

    if {[catch {
        wpg_select $dbh_ $sql rec {
            lappend tilist $rec(relname)
        }
    } gterrmsg]} {
        showError $gterrmsg
    }

    return $tilist

}; # end proc ::Database::getTableIndexes


#----------------------------------------------------------
# getTableInfo --
#
#   returns a list (from an array) of info on a table
#
# Arguments:
#   table_  name of a view or table (required)
#   dbh_    the db handle (optional)
#
# Results:
#   a list of name-value array pairs of table info columns
#----------------------------------------------------------
#
proc ::Database::getTableInfo {table_ {dbh_ ""}} {

    global CurrentDB

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}


    set id [::Connections::getIdFromHandle $dbh_]
    set V  $::Connections::Conn(pgversion,$id)

    if {$V < 7.3} {

        set sql "
            SELECT attnum,attname,typname,attlen,attnotnull,atttypmod,
                   usename,usesysid,C.oid,relpages,reltuples,
                   relhaspkey,relhasrules,relacl
              FROM pg_user U, pg_attribute A,
                   pg_type T, pg_class C
             WHERE (C.relname='$table_')
               AND (C.oid=A.attrelid)
               AND (C.relowner=U.usesysid)
               AND (A.atttypid=T.oid)
          ORDER BY A.attnum"

    } else {

        set sql "
            SELECT attnum,attname,typname,attlen,attnotnull,atttypmod,
                   usename,usesysid,C.oid,relpages,reltuples,
                   relhaspkey,relhasrules,relacl
              FROM pg_catalog.pg_shadow U, pg_catalog.pg_attribute A,
                   pg_catalog.pg_type T, pg_catalog.pg_class C
             WHERE (A.attrelid='$table_'::regclass)
               AND (A.atttypid=T.oid)
               AND (A.attrelid=C.oid)
               AND (C.relowner=U.usesysid)
          ORDER BY A.attnum"

    }

    set tlist {}

    if {[catch {
        wpg_select $dbh_ $sql rec {
            lappend tlist [array get rec]
        }
    } gterrmsg]} {
        showError $gterrmsg
    }

    return $tlist

}; # end proc ::Database::getTableInfo


#----------------------------------------------------------
# getColumnsTypesList --
#
#   returns a list of names of columns and their types
#   in a given view or table
#
# Arguments:
#   table_   name of a view or table (required)
#   dbh_    the db handle (optional)
#
# Results:
#   a list of pairs of column names and types
#----------------------------------------------------------
#
proc ::Database::getColumnsTypesList {table_ {dbh_ ""}} {

    global CurrentDB

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}


    set id [::Connections::getIdFromHandle $dbh_]
    set V  $::Connections::Conn(pgversion,$id)

    if {$V < 7.3} {

        set sql "
            SELECT A.attname, count(A.attname), T.typname
              FROM pg_class C, pg_attribute A, pg_type T
             WHERE (C.relname='$table_')
               AND (C.oid=A.attrelid)
               AND (A.attnum>0)
               AND (A.atttypid=T.oid)
          GROUP BY A.attname, A.attnum, T.typname
          ORDER BY A.attnum"

    } else {

        set sql "
            SELECT A.attname, count(A.attname), T.typname
              FROM pg_catalog.pg_attribute A, pg_catalog.pg_type T
             WHERE (A.attrelid='$table_'::regclass)
               AND (A.attnum>0)
               AND (A.atttypid=T.oid)
          GROUP BY A.attname, A.attnum, T.typname
          ORDER BY A.attnum"

    }

    set ctlist {}

    if {[catch {
        wpg_select $dbh_ $sql rec {
            if {$rec(count)!=0} {
                lappend ctlist $rec(attname) $rec(typname)
            }
        }
    } gterrmsg]} {
        showError $gterrmsg
    }

    return $ctlist

}; # end proc ::Database::getColumnsTypesList


#----------------------------------------------------------
# getColumnsList --
#
#   returns a list of names of columns in a given view or table
#
# Arguments:
#   table_   name of a view or table (required)
#   dbh_    the db handle (optional)
#
# Results:
#   a list of column names
#----------------------------------------------------------
#
proc ::Database::getColumnsList {table_ {dbh_ ""}} {

    global CurrentDB

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}


    set id [::Connections::getIdFromHandle $dbh_]
    set V  $::Connections::Conn(pgversion,$id)

    if {$V < 7.3} {

        set sql "
            SELECT A.attname, count(A.attname)
              FROM pg_class C, pg_attribute A
             WHERE (C.relname='$table_')
               AND (C.oid=A.attrelid)
               AND (A.attnum>0)
          GROUP BY A.attname, A.attnum
          ORDER BY A.attnum"

    } else {

        set sql "
            SELECT A.attname, count(A.attname)
              FROM pg_catalog.pg_attribute A
             WHERE (A.attrelid='$table_'::regclass)
               AND (A.attnum>0)
          GROUP BY A.attname, A.attnum
          ORDER BY A.attnum"

    }

    set clist {}

    if {[catch {
        wpg_select $dbh_ $sql rec {
            if {$rec(count)!=0} {
                lappend clist $rec(attname)
            }
        }
    } gterrmsg]} {
        showError $gterrmsg
    }

    return $clist

}; # end proc ::Database::getColumnsList


#----------------------------------------------------------
# getViewsList --
#
#   returns a list of views in the currentdb
#
# Arguments:
#    dbh_    optionally supply the db handle
#
# Results:
#    a list of view names
#----------------------------------------------------------
#
proc ::Database::getViewsList {{dbh_ ""}} {

    global CurrentDB

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}

    set sql "
        SELECT C.relname, count(C.relname)
          FROM pg_class C, pg_rewrite R
         WHERE (relname !~ '^pg_')
           AND (R.ev_class = C.oid)
           AND (R.ev_type = '1')
      GROUP BY relname"

    set vlist {}

    if {[catch {
        wpg_select $dbh_ $sql rec {
            if {$rec(count)!=0} {
                lappend vlist $rec(relname)
            }
        }
    } gterrmsg]} {
        showError $gterrmsg
    }

    return $vlist

}; # end proc ::Database::getViewsList


#------------------------------------------------------------
# getTablesList --
#
#    returns a list of tables in the currentdb
#
# Arguments:
#    dbh_    optionally supply the db handle
#
# Results:
#    a list of table names
#------------------------------------------------------------
#
proc ::Database::getTablesList {{dbh_ ""}} {

    global CurrentDB PgAcVar

    if {[string match "" $dbh_]} {
        set dbh_ $CurrentDB
    }

    if {[string match "" $CurrentDB]} {return [list]}


    set id [::Connections::getIdFromHandle $dbh_]
    set V  $::Connections::Conn(pgversion,$id)

    if {![info exists ::Connections::Conn(viewsystem,$id)]} {
        set ::Connections::Conn(viewsystem,$id) $PgAcVar(pref,systemtables)
    }

    if {![info exists ::Connections::Conn(viewpgaccess,$id)]} {
        set ::Connections::Conn(viewpgaccess,$id) $PgAcVar(pref,pgaccesstables)
    }

    if {$V < 7.3} {

        set sql "
            SELECT c.relname AS table
              FROM Pg_class c
             WHERE c.relkind = 'r'"

        if {! $::Connections::Conn(viewsystem,$id)} {
            append sql " AND c.relname !~ '^pg_'"
        }

        if {! $::Connections::Conn(viewpgaccess,$id)} {
           append sql " AND c.relname !~ '^pga_'"
        }

    } else {
        
        set sql "
            SELECT n.nspname || '.' || '\"' || c.relname || '\"' AS table
              FROM Pg_catalog.Pg_class c
                    LEFT JOIN Pg_catalog.Pg_namespace n ON n.oid = c.relnamespace
             WHERE c.relkind = 'r'"

        #SELECT c.oid::regclass AS table
          #FROM Pg_catalog.Pg_class c
         #WHERE c.relkind = 'r'"

        if {! $::Connections::Conn(viewsystem,$id)} {
            append sql " AND n.nspname !~ '^pg_'"
        }

        if {! $::Connections::Conn(viewpgaccess,$id)} {
           append sql " AND n.nspname !~ '^pga_'"
        }
    }

    
    set tlist [list]
    if {[catch {wpg_select $dbh_ "$sql" rec {
                lappend tlist $rec(table)
            }

    } err]} {
        showError $err
    }

    return $tlist

}; # end proc getTablesList



# TO BE DELETED ?
proc ::Database::getTablesList-old {} {
global CurrentDB PgAcVar

    set sql(1) "
        SELECT c.relname,count(c.relname)
          FROM [::Database::qualifySysTable Pg_class] C, [::Database::qualifySysTable Pg_rewrite] R
         WHERE (r.ev_class = C.oid) 
           AND (r.ev_type = '1') 
      GROUP BY relname"

    set sql(2) "
        SELECT relname 
          FROM [::Database::qualifySysTable Pg_class]
         WHERE (relname !~ '^pg_') 
           AND (relkind='r') 
      ORDER BY relname"

    set sql(3) "
        SELECT relname 
          FROM [::Database::qualifySysTable Pg_class]
         WHERE (relkind='r') 
      ORDER BY relname"

    set tlist {}
    if {[catch {
        wpg_select $CurrentDB "$sql(1)" rec {
            if {$rec(count)!=0} {
                set itsaview($rec(relname)) 1
            }
        }
        if {! $PgAcVar(pref,systemtables)} {
            wpg_select $CurrentDB "$sql(2)" rec {
                if {![regexp "^pga_" $rec(relname)]} then {
                    if {![info exists itsaview($rec(relname))]} {
                        lappend tlist $rec(relname)
                    }
                }
            }
        } else {
            wpg_select $CurrentDB "$sql(3)" rec {
                if {![info exists itsaview($rec(relname))]} {
                    lappend tlist $rec(relname)
                }
            }
        }
    } gterrmsg]} {
        showError $gterrmsg
    }
    return $tlist
}; # end proc ::Database::getTablesList-old



#----------------------------------------------------------
#----------------------------------------------------------
#
proc ::Database::vacuum {} {
global PgAcVar CurrentDB
    if {$CurrentDB==""} return;
    set PgAcVar(statusline,dbname) [format [intlmsg "vacuuming database %s ..."] $PgAcVar(currentdb,dbname)]
    setCursor CLOCK
    set pgres [wpg_exec $CurrentDB "vacuum;"]
    catch {pg_result $pgres -clear}
    setCursor DEFAULT
    set PgAcVar(statusline,dbname) $PgAcVar(currentdb,dbname)
}; # end proc ::Database::vacuum


#----------------------------------------------------------
#----------------------------------------------------------
#
proc ::Database::getPgType {oid} {
global CurrentDB
    set temp "unknown"
    wpg_select $CurrentDB "select typname from pg_type where oid=$oid" rec {
        set temp $rec(typname)
    }
    return $temp
}; # end proc ::Database::getPgType


#----------------------------------------------------------
#----------------------------------------------------------
#
proc ::Database::executeUpdate {sqlcmd} {
global CurrentDB
    return [sql_exec noquiet $sqlcmd]
}; # end proc ::Database::executeUpdate


#----------------------------------------------------------
# ::Database::getPgVersion --
#
#    Gets the version of the PG database
#
# Arguments:
#    db_    This is the db handle of the DB. If it is
#           not supplied, then CurrentDB is assumed
#
# Results:
#    pgversion
#----------------------------------------------------------
#
proc ::Database::getPgVersion {{db_ ""}} {

    if {(![info exists ::CurrentDB]) || ([string match "" $::CurrentDB])} {return}

    if {[string match "" $db_]} {set db_ $::CurrentDB}

    if {[catch {wpg_select $db_ "
        SELECT version()" rec {
        set res $rec(version)
    }} err]} {

        return ""
    }

    regexp {PostgreSQL ([.\w]+)} $res m ver

    return $ver

}; # end proc ::Database::getPgVersion


#------------------------------------------------------------
# ::Database::qualifySysTable --
#
#    This just qualifies a system table; checking the PG
#    version number, and it >= 7.3, it will prepend
#    the Pg_catalog schema that is used for the 
#    system tables
#
# Arguments:
#    table_   the table name that needs qualified
#    dbh_    the db handle of the database to use. It defaults
#             to the current db handle (CurrentDB)
#
# Results:
#    none returned
#------------------------------------------------------------
#
proc ::Database::qualifySysTable {table_ {dbh_ ""}} {

    if {[string match "" $dbh_]} {
        set dbh_ $::CurrentDB
    }

    set V [string range [getPgVersion $dbh_] 0 2]

    if {$V >= 7.3} {
        return "pg_catalog.${table_}"
    }

    return $table_

}; # end proc ::Database::qualifySysTable

#------------------------------------------------------------
# ::Database::quoteObject --
#
#    This makes sure that an object is quoted properly,
#    especially if it is schema qualified.
#
# Arguments:
#    obj_     name of the object to quote
#
# Results:
#    returns the properly quoted object
#------------------------------------------------------------
#
proc ::Database::quoteObject {obj_} {

    set retval [list]
    foreach t [split "$obj_" .] {
        lappend retval \"[string trim $t \"]\"
    }

    return [join $retval .]
}; # end proc ::Database::quoteTable
