////////////////////////////////////////
///// JavaScript in JDBC Navigator /////
////////////////////////////////////////

JDBC Navigator contains the Rhino Javascript interpreter. It can be accessed
by opening a JavaScript window (in the Misc menu) and typing JavaScript
commands. The JavaScript window acts like a cross between a console (terminal)
and a text editor: you can move the cursor, make selections, and cut & paste,
and you can pass text to the JavaScript interpreter for execution by pressing
Enter. (You can insert a line break without executing anything by pressing
Shift-Enter.)
If you press Enter while there is no selection, the line that the cursor is on
is executed; if there is an active selection, that selection is executed.
The output generated by executing the JavaScript commands is pasted into the
window just below the executed commands.
The output is automatically selected; this is intended to make it easier to
back up, modify, and re-run commands: just hit Backspace and you're back at
the end of the last command. If, on the other hand, you do not want to back
up, but want to keep the output, you can simply press Enter again; in this
case, it does not execute the selection, but moves the cursor to just below
it.
You can interrupt the JavaScript interpreter by pressing Ctrl-. (control-dot).
Note that when you interrupt a JavaScript program that generates a lot of
output, it may take a while before you regain control, because the buffered
output may still be in the process of being displayed.


////////////////////////////////////////////////////////////
///// Summary of JDBC Navigator's JavaScript embedding /////
////////////////////////////////////////////////////////////

void print(...);                       // Print zero or more objects.
void println(...);                     // Print zero or more objects, followed
                                       // by a line break.
Object clipboard;                      // The JDBC Navigator clipboard.

TreeNode[] browsers;                   // Array containing all open browsers;
                                       // they appear here in the order in
                                       // which they were opened. Each browser
                                       // is represented by a tree node, which
                                       // may optionally contain one or two
                                       // additional levels of tree nodes (for
                                       // the JDBC "catalog" and "schema" name
                                       // spaces); below those are the Table
                                       // objects.

class TreeNode {
    String name;                       // Tree node name -- i.e., the
                                       // connection, catalog, or schema name
    int length;                        // Number of children

    // Child nodes/tables appear as additional members; e.g. a Schema node
    // containing three tables named "foo", "bar, and "baz", acts as if it has
    // these declarations: "Table foo, bar, baz".
    // Child nodes can also be retrieved using 0-based array indexing.
    
    void commit(Table, ...);           // Commits an arbitrary number of
                                       // tables simultaneously.

    // Low-level SQL access... the following functions create Statement,
    // PreparedStatement, and CallableStatement objects for the owning
    // database connection.

    Statement createStatement();
    PreparedStatement prepareStatement(String stmt);
    CallableStatement prepareCall(String call);
}

class Table {
    String name;                       // Table name
    int length;                        // Number of rows
    int width;                         // Number of columns
    String[] columns;                  // Column names
    PrimaryKey pk;                     // Primary key
    ForeignKey[] rks;                  // Referencing (exported) keys
    ForeignKey[] fks;                  // Foreign (referencing/imported) keys
    Index[] indexes;                   // Indexes

    // Table rows can be retrieved using 0-based array indexing.
    // The [] operator returns an instance of the Row class; see below.

    void load();                       // Load/reload table data from the DB
    void addRow();                     // Add new row at the bottom
    void removeRow(int n);             // Remove nth row
    void commit();                     // Commit all changes to the DB
    void rollback();                   // Roll back all changes since the last
                                       // load/reload
    int pk2row(Object, ...);           // Convert PK value to row index. PK may
                                       // be specified using an array, or using
                                       // multiple arguments.
    Object[] row2pk(int r);            // Convert row index to PK value. The
                                       // returned value is suitable for
                                       // passing to pk2row() and fk2rows().
    int[] fk2rows(int n, Object, ...); // Convert FK value for FK fks[n]
                                       // to zero or more row indexes. FK may
                                       // be specified using an array, or using
                                       // multiple arguments.
    Object[] row2fk(int n, int r);     // Convert row index r to FK value for
                                       // FK fks[n]. The returned value is
                                       // suitable for passing to pk2row() and
                                       // fk2rows().

    // NOTE: at the end of this file, there is an example showing how to use
    // the pk2row(), row2pk(), fk2rows(), and row2fk() methods.
}

class Row {
    int length;                        // Number of columns
    // The individual fields of the record (row) can be retrieved by name,
    // that is, as if they were additional members; and they can also be
    // retrieved using 0-based array indexing.
}

class PrimaryKey {
    String name;                       // Key name
    String[] columns;                  // Key columns
}

class ForeignKey {
    String name;                       // This key's name
    String[] columns;                  // This key's columns
    String reftable;                   // Referenced table
    String refname;                    // Referenced key's name
    String[] refcolumns;               // Referenced key's columns
}

class Index {
    String name;                       // Index name
    String[] columns;                  // Index columns
    boolean unique;                    // Unique index?
}

class Statement {
    int/ResultSet execute(String sql); // Execute a SQL statement. Queries
                                       // return a ResultSet; everything else
                                       // returns an update count.
    void close();                      // Close the statement and release its
                                       // resources (cursors etc.).
}

class PreparedStatement {
    void setObject(int col, Object obj [, int/String type [, int scale]]);
                                       // Sets a parameter. The optional
                                       // 'type' parameter may be an index or
                                       // a String; see java.sql.Types for
                                       // possible values.
    int/ResultSet execute();           // Executes the statement. Queries
                                       // return a ResultSet; everything else
                                       // returns an update count.
    void close();                      // Close the statement and release its
                                       // resources (cursors etc.).
}

class CallableStatement {
    void setObject(int col, Object obj [, int/String type [, int scale]]);
                                       // Sets a parameter. The optional
                                       // 'type' parameter may be an index or
                                       // a String; see java.sql.Types for
                                       // possible values.
    void registerOutParameter(int/String param, int/String type
                              [, int/String scale/typeName]);
                                       // Registers an OUT parameter so its
                                       // value so its value can be retrieved
                                       // later, with getObject().
    int/ResultSet execute();           // Executes the call. Can return a
                                       // result set or a count.
    Object getObject(int/String param);// Retrieves the value of an OUT
                                       // parameter.
    void close();                      // Close the statement and release its
                                       // resources (cursors etc.).
}

class ResultSet {
    int length;                        // Number of columns in the result set
    String[] columns;                  // Column names
    String[] dbtypes;                  // DB-specific column type names
    String[] sqltypes                  // java.sql.Types column type names
    String[] javatypes;                // equivalent Java class for col. types

    // Result Set columns appear as additional members; they can also be
    // retrieved using 0-based array indexing.
    
    boolean next();                    // Returns 'false' when no more data is
                                       // available.
    void close();                      // Close the result set and release its
                                       // resources (cursors etc.).
}


///////////////////////////////////////////////////////
///// A small example to try to clarify the usage /////
/////      of the row/key conversion methods      /////
///////////////////////////////////////////////////////

Say we have two tables, PERSON and FAMILY_TREE_NODE. PERSON is used to store
data about persons; FAMILY_TREE_NODE is used to describe family relationships
between persons. Let's say the tables are created as follows:

  create table PERSON
  (
      FIRSTNAME VARCHAR2(32) not null,
      LASTNAME VARCHAR2(32) not null,
      SSN VARCHAR2(9) not null,
      PHONE VARCHAR2(20),
      ADDRESS1 VARCHAR2(64),
      ADDRESS2 VARCHAR2(64),
      CITY VARCHAR2(32),
      ZIP VARCHAR2(9),
      constraint PERSON_PK primary key (SSN, LASTNAME, FIRSTNAME)
  );

  create table FAMILY_TREE_NODE
  (
      OWN_SSN VARCHAR2(9) not null,
      OWN_FIRSTNAME VARCHAR2(32) not null,
      OWN_LASTNAME VARCHAR2(32) not null,
      MOM_SSN VARCHAR2(9),
      MOM_FIRSTNAME VARCHAR2(32),
      MOM_LASTNAME VARCHAR2(32),
      DAD_SSN VARCHAR2(9),
      DAD_FIRSTNAME VARCHAR2(32),
      DAD_LASTNAME VARCHAR2(32),
      constraint FTN_PK primary key (OWN_SSN, OWN_FIRSTNAME, OWN_LASTNAME),
      constraint FTN_OWN_FK foreign key (OWN_SSN, OWN_LASTNAME, OWN_FIRSTNAME)
          references PERSON(SSN, LASTNAME, FIRSTNAME),
      constraint FTN_MOM_FK foreign key (MOM_SSN, MOM_LASTNAME, MOM_FIRSTNAME)
          references PERSON(SSN, LASTNAME, FIRSTNAME),
      constraint FTN_DAD_FK foreign key (DAD_SSN, DAD_LASTNAME, DAD_FIRSTNAME)
          references PERSON(SSN, LASTNAME, FIRSTNAME)
  );

We have created these tables in the THOMAS schema in an Oracle 8i database.
How do we find all the siblings of Thomas Okken, SSN 123-45-6789?

First, assign the PERSON and FAMILY_TREE_NODE tables to variables, to save some
typing later on. The value of the variable, which in this case is a pretty-
printed version of the table, is printed:

  p = browsers[0].thomas.table.person;
  ftn = browsers[0].thomas.table.family_tree_node;

If you do not have table viewing windows open on these tables, you may have to
tell JDBC Navigator to load them into memory. You can tell whether or not a
table is loaded by getting its length: if that returns -1, the table is not
loaded yet.

  p.load();
  ftn.load();

Find the row index of Thomas Okken 123-45-6789 in the PERSON table. Note that
the order in which the key components are specified must match the order in
which they appear in the primary key declaration part of the CREATE statement
(see above):

  p.pk2row('123-45-6789', 'Okken', 'Thomas');

If the primary key value does not exist, this will return 'null'; if it does
exist, it returns the row index, which is an integer; if you open a table
viewing window, the row index corresponds to the position of the record in the
window, with the topmost row having index 0.

Who is Tom's mom?
First, find Tom in the FAMILY_TREE_NODE table; then, print that row from the
table to see who his parents are. Note that the first argument to fk2rows() is
0 here, which means we're using the first foreign key, which is the key
representing the person him/herself (key 1 is his/her mom; key 2 is dad):

  idx = ftn.fk2rows(0, '123-45-6789', 'Okken', 'Thomas');
  if (idx.length == 0)
      println("Oops!");
  else
      ftn[idx[0]];

This last statement prints something like

  [ 123-45-6789 Thomas Okken 888-88-8888 Jane Roe 999-99-9999 John Doe ]

Note that the order of the key components in the primary and foreign keys do
not necessarily have to be the same; fk2rows() expects the components in the
order in which they appear in the referenced primary key. This may seem
confusing at first, but it has the advantage that keys returned by p.row2pk()
can be passed to ftn.fk2rows(), and keys returned by ftn.row2fk() can be passed
to p.pk2row(), without having to reshuffle the components first.

Who are Tom's siblings?

  tom = ftn.fk2rows(0, '123-45-6789', 'Okken', 'Thomas')[0];
  mom = ftn.row2fk(1, tom);
  dad = ftn.row2fk(2, tom);
  siblings1 = ftn.fk2rows(1, mom);
  siblings2 = ftn.fk2rows(2, dad);

The first call returns the row index corresponding to Tom in the
FAMILY_TREE_NODE table; the second and third find the key value for his mom and
dad (i.e. the PK values representing them in the PERSON table); and the fourth
and fifth return the row indexes corresponding to Tom's mother's children, and
Tom's father's children.

Print the details of Tom's siblings:

  for (i = 0; i < siblings1.length; i++)
      println(p[p.pk2row(ftn.row2fk(0, siblings1[i]))]);
  for (i = 0; i < siblings2.length; i++)
      println(p[p.pk2row(ftn.row2fk(0, siblings2[i]))]);

Go back.