# Makefile for Java JDBC interface
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.3 1998/01/11 21:14:29 scrappy Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.4 1998/01/13 02:19:10 scrappy Exp $
#
#-------------------------------------------------------------------------
--- /dev/null
+package example;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.sql.*;
+import postgresql.largeobject.*;
+
+/**
+ * This example is a small application that stores and displays images
+ * held on a postgresql database.
+ *
+ * Before running this application, you need to create a database, and
+ * on the first time you run it, select "Initialise" in the "PostgreSQL"
+ * menu.
+ *
+ * Important note: You will notice we import the postgresql.largeobject
+ * package, but don't import the postgresql package. The reason for this is
+ * that importing postgresql can confuse javac (we have conflicting class names
+ * in postgresql.* and java.sql.*). This doesn't cause any problems, as long
+ * as no code imports postgresql.
+ *
+ * Under normal circumstances, code using any jdbc driver only needs to import
+ * java.sql, so this isn't a problem.
+ *
+ * It's only if you use the non jdbc facilities, do you have to take this into
+ * account.
+ *
+ */
+
+public class ImageViewer implements ItemListener
+{
+ Connection db;
+ Statement stat;
+ LargeObjectManager lom;
+ Frame frame;
+ Label label; // Label used to display the current name
+ List list; // The list of available images
+ imageCanvas canvas; // Canvas used to display the image
+ String currentImage; // The current images name
+
+ // This is a simple component to display our image
+ public class imageCanvas extends Canvas
+ {
+ private Image image;
+
+ public imageCanvas()
+ {
+ image=null;
+ }
+
+ public void setImage(Image img)
+ {
+ image=img;
+ repaint();
+ }
+
+ // This defines our minimum size
+ public Dimension getMinimumSize()
+ {
+ return new Dimension(400,400);
+ }
+
+ public Dimension getPreferedSize()
+ {
+ return getMinimumSize();
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ public void paint(Graphics g)
+ {
+ g.setColor(Color.gray);
+ g.fillRect(0,0,getSize().width,getSize().height);
+
+ if(image!=null)
+ g.drawImage(image,0,0,this);
+ }
+ }
+
+ public ImageViewer(Frame f,String url,String user,String password) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
+ {
+ frame = f;
+
+ MenuBar mb = new MenuBar();
+ Menu m;
+ MenuItem i;
+
+ f.setMenuBar(mb);
+ mb.add(m = new Menu("PostgreSQL"));
+ m.add(i= new MenuItem("Initialise"));
+ i.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ImageViewer.this.init();
+ }
+ });
+
+ m.add(i= new MenuItem("Exit"));
+ ActionListener exitListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ImageViewer.this.close();
+ }
+ };
+ m.addActionListener(exitListener);
+
+ mb.add(m = new Menu("Image"));
+ m.add(i= new MenuItem("Import"));
+ ActionListener importListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ImageViewer.this.importImage();
+ }
+ };
+ i.addActionListener(importListener);
+
+ m.add(i= new MenuItem("Remove"));
+ ActionListener removeListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ImageViewer.this.removeImage();
+ }
+ };
+ i.addActionListener(removeListener);
+
+ // To the north is a label used to display the current images name
+ f.add("North",label = new Label());
+
+ // We have a panel to the south of the frame containing the controls
+ Panel p = new Panel();
+ p.setLayout(new FlowLayout());
+ Button b;
+ p.add(b=new Button("Refresh List"));
+ b.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ImageViewer.this.refreshList();
+ }
+ });
+ p.add(b=new Button("Import new image"));
+ b.addActionListener(importListener);
+ p.add(b=new Button("Remove image"));
+ b.addActionListener(removeListener);
+ p.add(b=new Button("Quit"));
+ b.addActionListener(exitListener);
+ f.add("South",p);
+
+ // And a panel to the west containing the list of available images
+ f.add("West",list=new List());
+ list.addItemListener(this);
+
+ // Finally the centre contains our image
+ f.add("Center",canvas = new imageCanvas());
+
+ // Load the driver
+ Class.forName("postgresql.Driver");
+
+ // Connect to database
+ System.out.println("Connecting to Database URL = " + url);
+ db = DriverManager.getConnection(url, user, password);
+
+ // Create a statement
+ stat = db.createStatement();
+
+ // Also, get the LargeObjectManager for this connection
+ lom = ((postgresql.Connection)db).getLargeObjectAPI();
+
+ // Now refresh the image selection list
+ refreshList();
+ }
+
+
+ /**
+ * This method initialises the database by creating a table that contains
+ * the image names, and Large Object OID's
+ */
+ public void init()
+ {
+ try {
+ stat.executeUpdate("create table images (imgname name,imgoid oid)");
+ label.setText("Initialised database");
+ } catch(SQLException ex) {
+ label.setText(ex.toString());
+ }
+ }
+
+ /**
+ * This closes the connection, and ends the application
+ */
+ public void close()
+ {
+ try {
+ db.close();
+ } catch(SQLException ex) {
+ System.err.println(ex.toString());
+ }
+ System.exit(0);
+ }
+
+ /**
+ * This imports an image into the database.
+ *
+ * This is the most efficient method, using the large object extension.
+ */
+ public void importImage()
+ {
+ FileDialog d = new FileDialog(frame,"Import Image",FileDialog.LOAD);
+ d.setVisible(true);
+ String name = d.getFile();
+ String dir = d.getDirectory();
+ d.dispose();
+
+ // Now the real import stuff
+ if(name!=null && dir!=null) {
+ try {
+ System.out.println("Importing file");
+ // A temporary buffer - this can be as large as you like
+ byte buf[] = new byte[2048];
+
+ // Open the file
+ System.out.println("Opening file "+dir+"/"+name);
+ FileInputStream fis = new FileInputStream(new File(dir,name));
+
+ // Gain access to large objects
+ System.out.println("Gaining LOAPI");
+
+ // Now create the large object
+ System.out.println("creating blob");
+ int oid = lom.create();
+
+ System.out.println("Opening "+oid);
+ LargeObject blob = lom.open(oid);
+
+ // Now copy the file into the object.
+ //
+ // Note: we dont use write(buf), as the last block is rarely the same
+ // size as our buffer, so we have to use the amount read.
+ System.out.println("Importing file");
+ int s,t=0;
+ while((s=fis.read(buf,0,buf.length))>0) {
+ System.out.println("Block s="+s+" t="+t);t+=s;
+ blob.write(buf,0,s);
+ }
+
+ // Close the object
+ System.out.println("Closing blob");
+ blob.close();
+
+ // Now store the entry into the table
+ stat.executeUpdate("insert into images values ('"+name+"',"+oid+")");
+ stat.close();
+
+ // Finally refresh the names list, and display the current image
+ refreshList();
+ displayImage(name);
+ } catch(Exception ex) {
+ label.setText(ex.toString());
+ }
+ }
+ }
+
+ /**
+ * This refreshes the list of available images
+ */
+ public void refreshList()
+ {
+ try {
+ // First, we'll run a query, retrieving all of the image names
+ ResultSet rs = stat.executeQuery("select imgname from images order by imgname");
+ if(rs!=null) {
+ list.removeAll();
+ while(rs.next())
+ list.addItem(rs.getString(1));
+ rs.close();
+ }
+ } catch(SQLException ex) {
+ label.setText(ex.toString()+" Have you initialised the database?");
+ }
+ }
+
+ /**
+ * This removes an image from the database
+ *
+ * Note: With postgresql, this is the only way of deleting a large object
+ * using Java.
+ */
+ public void removeImage()
+ {
+ try {
+ // Delete any large objects for the current name
+ ResultSet rs = stat.executeQuery("select imgoid from images where imgname='"+currentImage+"'");
+ if(rs!=null) {
+ // Even though there should only be one image, we still have to
+ // cycle through the ResultSet
+ while(rs.next()) {
+ System.out.println("Got oid "+rs.getInt(1));
+ lom.delete(rs.getInt(1));
+ System.out.println("Import complete");
+ }
+ }
+ rs.close();
+
+ // Finally delete any entries for that name
+ stat.executeUpdate("delete from images where imgname='"+currentImage+"'");
+
+ label.setText(currentImage+" deleted");
+ currentImage=null;
+ refreshList();
+ } catch(SQLException ex) {
+ label.setText(ex.toString());
+ }
+ }
+
+ /**
+ * This displays an image from the database.
+ *
+ * For images, this is the easiest method.
+ */
+ public void displayImage(String name)
+ {
+ try {
+ System.out.println("Selecting oid for "+name);
+ ResultSet rs = stat.executeQuery("select imgoid from images where imgname='"+name+"'");
+ if(rs!=null) {
+ // Even though there should only be one image, we still have to
+ // cycle through the ResultSet
+ while(rs.next()) {
+ System.out.println("Got oid "+rs.getInt(1));
+ canvas.setImage(canvas.getToolkit().createImage(rs.getBytes(1)));
+ System.out.println("Import complete");
+ label.setText(currentImage = name);
+ }
+ }
+ rs.close();
+ } catch(SQLException ex) {
+ label.setText(ex.toString());
+ }
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ displayImage(list.getItem(((Integer)e.getItem()).intValue()));
+ }
+
+ /**
+ * This is the command line instructions
+ */
+ public static void instructions()
+ {
+ System.err.println("java example.ImageViewer jdbc-url user password");
+ System.err.println("\nExamples:\n");
+ System.err.println("java -Djdbc.driver=postgresql.Driver example.ImageViewer jdbc:postgresql:test postgres password\n");
+
+ System.err.println("This example tests the binary large object api of the driver.\nBasically, it will allow you to store and view images held in the database.");
+ System.err.println("Note: If you are running this for the first time on a particular database,\nyou have to select \"Initialise\" in the \"PostgreSQL\" menu.\nThis will create a table used to store image names.");
+ }
+
+ /**
+ * This is the application entry point
+ */
+ public static void main(String args[])
+ {
+ if(args.length!=3) {
+ instructions();
+ System.exit(1);
+ }
+
+ try {
+ Frame frame = new Frame("PostgreSQL ImageViewer v6.3 rev 1");
+ frame.setLayout(new BorderLayout());
+ ImageViewer viewer = new ImageViewer(frame,args[0],args[1],args[2]);
+ frame.pack();
+ frame.setVisible(true);
+ } catch(Exception ex) {
+ System.err.println("Exception caught.\n"+ex);
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package example;
+
+import java.io.*;
+import java.sql.*;
+import java.text.*;
+
+/**
+ * This example tests the basic components of the JDBC driver, and shows
+ * how even the simplest of queries can be implemented.
+ *
+ * To use this example, you need a database to be in existence. This example
+ * will create a table called basic.
+ *
+ */
+
+public class basic
+{
+ Connection db; // The connection to the database
+ Statement st; // Our statement to run queries with
+
+ public basic(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
+ {
+ String url = args[0];
+ String usr = args[1];
+ String pwd = args[2];
+
+ // Load the driver
+ Class.forName("postgresql.Driver");
+
+ // Connect to database
+ System.out.println("Connecting to Database URL = " + url);
+ db = DriverManager.getConnection(url, usr, pwd);
+
+ System.out.println("Connected...Now creating a statement");
+ st = db.createStatement();
+
+ // Clean up the database (in case we failed earlier) then initialise
+ cleanup();
+
+ // Now run tests using JDBC methods
+ doexample();
+
+ // Clean up the database
+ cleanup();
+
+ // Finally close the database
+ System.out.println("Now closing the connection");
+ st.close();
+ db.close();
+
+ }
+
+ /**
+ * This drops the table (if it existed). No errors are reported.
+ */
+ public void cleanup()
+ {
+ try {
+ st.executeUpdate("drop table basic");
+ } catch(Exception ex) {
+ // We ignore any errors here
+ }
+ }
+
+ /**
+ * This performs the example
+ */
+ public void doexample() throws SQLException
+ {
+ System.out.println("\nRunning tests:");
+
+ // First we need a table to store data in
+ st.executeUpdate("create table basic (a int2, b int2)");
+
+ // Now insert some data, using the Statement
+ st.executeUpdate("insert into basic values (1,1)");
+ st.executeUpdate("insert into basic values (2,1)");
+ st.executeUpdate("insert into basic values (3,1)");
+
+ // For large inserts, a PreparedStatement is more efficient, because it
+ // supports the idea of precompiling the SQL statement, and to store
+ // directly, a Java object into any column. PostgreSQL doesnt support
+ // precompiling, but does support setting a column to the value of a
+ // Java object (like Date, String, etc).
+ //
+ // Also, this is the only way of writing dates in a datestyle independent
+ // manner. (DateStyles are PostgreSQL's way of handling different methods
+ // of representing dates in the Date data type.)
+ PreparedStatement ps = db.prepareStatement("insert into basic values (?,?)");
+ for(int i=2;i<5;i++) {
+ ps.setInt(1,4); // "column a" = 5
+ ps.setInt(2,i); // "column b" = i
+ ps.executeUpdate(); // executeUpdate because insert returns no data
+ }
+ ps.close(); // Always close when we are done with it
+
+ // Finally perform a query on the table
+ System.out.println("performing a query");
+ ResultSet rs = st.executeQuery("select a, b from basic");
+ if(rs!=null) {
+ // Now we run through the result set, printing out the result.
+ // Note, we must call .next() before attempting to read any results
+ while(rs.next()) {
+ int a = rs.getInt("a"); // This shows how to get the value by name
+ int b = rs.getInt(2); // This shows how to get the value by column
+ System.out.println(" a="+a+" b="+b);
+ }
+ rs.close(); // again, you must close the result when done
+ }
+
+ // Now run the query again, showing a more efficient way of getting the
+ // result if you don't know what column number a value is in
+ System.out.println("performing another query");
+ rs = st.executeQuery("select * from basic where b>1");
+ if(rs!=null) {
+ // First find out the column numbers.
+ //
+ // It's best to do this here, as calling the methods with the column
+ // numbers actually performs this call each time they are called. This
+ // really speeds things up on large queries.
+ //
+ int col_a = rs.findColumn("a");
+ int col_b = rs.findColumn("b");
+
+ // Now we run through the result set, printing out the result.
+ // Again, we must call .next() before attempting to read any results
+ while(rs.next()) {
+ int a = rs.getInt(col_a); // This shows how to get the value by name
+ int b = rs.getInt(col_b); // This shows how to get the value by column
+ System.out.println(" a="+a+" b="+b);
+ }
+ rs.close(); // again, you must close the result when done
+ }
+
+ // The last thing to do is to drop the table. This is done in the
+ // cleanup() method.
+ }
+
+ /**
+ * Display some instructions on how to run the example
+ */
+ public static void instructions()
+ {
+ System.out.println("\nThis example tests the basic components of the JDBC driver, demonstrating\nhow to build simple queries in java.\n");
+ System.out.println("Useage:\n java example.basic jdbc:postgresql:database user password [debug]\n\nThe debug field can be anything. It's presence will enable DriverManager's\ndebug trace. Unless you want to see screens of items, don't put anything in\nhere.");
+ System.exit(1);
+ }
+
+ /**
+ * This little lot starts the test
+ */
+ public static void main(String args[])
+ {
+ System.out.println("PostgreSQL basic test v6.3 rev 1\n");
+
+ if(args.length<3)
+ instructions();
+
+ // This line outputs debug information to stderr. To enable this, simply
+ // add an extra parameter to the command line
+ if(args.length>3)
+ DriverManager.setLogStream(System.err);
+
+ // Now run the tests
+ try {
+ basic test = new basic(args);
+ } catch(Exception ex) {
+ System.err.println("Exception caught.\n"+ex);
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package example;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.largeobject.*;
+
+/**
+ * This test attempts to create a blob in the database, then to read
+ * it back.
+ *
+ * Important note: You will notice we import the postgresql.largeobject
+ * package, but don't import the postgresql package. The reason for this is
+ * that importing postgresql can confuse javac (we have conflicting class names
+ * in postgresql.* and java.sql.*). This doesn't cause any problems, as long
+ * as no code imports postgresql.
+ *
+ * Under normal circumstances, code using any jdbc driver only needs to import
+ * java.sql, so this isn't a problem.
+ *
+ * It's only if you use the non jdbc facilities, do you have to take this into
+ * account.
+ *
+ */
+
+public class blobtest
+{
+ Connection db;
+ Statement s;
+ LargeObjectManager lobj;
+
+ public blobtest(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
+ {
+ String url = args[0];
+ String usr = args[1];
+ String pwd = args[2];
+
+ // Load the driver
+ Class.forName("postgresql.Driver");
+
+ // Connect to database
+ System.out.println("Connecting to Database URL = " + url);
+ db = DriverManager.getConnection(url, usr, pwd);
+ System.out.println("Connected...Now creating a statement");
+ s = db.createStatement();
+
+ // Now run tests using postgresql's own Large object api
+ // NOTE: The methods shown in this example are _NOT_ JDBC, but are
+ // an implementation of the calls found in libpq. Unless you need to
+ // use this functionality, look at the jdbc tests on how to access blobs.
+ ownapi();
+
+ // Now run tests using JDBC methods
+ //jdbcapi(db,s);
+
+ // Finally close the database
+ System.out.println("Now closing the connection");
+ s.close();
+ db.close();
+
+ }
+
+ /**
+ * Now this is an extension to JDBC, unique to postgresql. Here we fetch
+ * an PGlobj object, which provides us with access to postgresql's
+ * large object api.
+ */
+ public void ownapi() throws FileNotFoundException, IOException, SQLException
+ {
+ System.out.println("\n----------------------------------------------------------------------\nTesting postgresql large object api\n");
+
+ // Internally, the driver provides JDBC compliant methods to access large
+ // objects, however the unique methods available to postgresql makes things
+ System.out.println("Gaining access to large object api");
+ lobj = ((postgresql.Connection)db).getLargeObjectAPI();
+
+ int oid = ownapi_test1();
+ ownapi_test2(oid);
+ //ownapi_test3(oid);
+ System.out.println("\n\nOID="+oid);
+ }
+
+ private int ownapi_test1() throws FileNotFoundException, IOException, SQLException
+ {
+ System.out.println("Test 1 Creating a large object\n");
+
+ // Ok, test 1 is to create a large object. To do this, we use the create
+ // method.
+ System.out.println("Creating a large object");
+ int oid = lobj.create(LargeObjectManager.READ|LargeObjectManager.WRITE);
+ DriverManager.println("got large object oid="+oid);
+
+ LargeObject obj = lobj.open(oid,LargeObjectManager.WRITE);
+ DriverManager.println("got large object obj="+obj);
+
+ // Now open a test file - this class will do
+ System.out.println("Opening test source object");
+ FileInputStream fis = new FileInputStream("example/blobtest.java");
+
+ // copy the data
+ System.out.println("Copying file to large object");
+ byte buf[] = new byte[2048];
+ int s,tl=0;
+ while((s=fis.read(buf,0,2048))>0) {
+ System.out.println("Block size="+s+" offset="+tl);
+ //System.out.write(buf);
+ obj.write(buf,0,s);
+ tl+=s;
+ }
+ DriverManager.println("Copied "+tl+" bytes");
+
+ // Close the object
+ System.out.println("Closing object");
+ obj.close();
+
+ return oid;
+ }
+
+ private void ownapi_test2(int oid) throws FileNotFoundException, IOException, SQLException
+ {
+ System.out.println("Test 2 Reading a large object and save as a file\n");
+
+ // Now open the large object
+ System.out.println("Opening large object "+oid);
+ LargeObject obj = lobj.open(oid,LargeObjectManager.READ);
+ DriverManager.println("got obj="+obj);
+
+ // Now open a test file - this class will do
+ System.out.println("Opening test destination object");
+ FileOutputStream fos = new FileOutputStream("blob_testoutput");
+
+ // copy the data
+ System.out.println("Copying large object to file");
+ byte buf[] = new byte[512];
+ int s=obj.size();
+ int tl=0;
+ while(s>0) {
+ int rs = buf.length;
+ if(s<rs) rs=s;
+ obj.read(buf,0,rs);
+ fos.write(buf,0,rs);
+ tl+=rs;
+ s-=rs;
+ }
+ DriverManager.println("Copied "+tl+"/"+obj.size()+" bytes");
+
+ // Close the object
+ System.out.println("Closing object");
+ obj.close();
+ }
+
+ private void ownapi_test3(int oid) throws SQLException
+ {
+ System.out.println("Test 3 Deleting a large object\n");
+
+ // Now open the large object
+ System.out.println("Deleting large object "+oid);
+ lobj.unlink(oid);
+ }
+
+ //=========================================================================
+
+ public static void instructions()
+ {
+ System.err.println("java example.blobtest jdbc-url user password [debug]");
+ System.err.println("\nExamples:\n");
+ System.err.println("java -Djdbc.driver=postgresql.Driver example.blobtest jdbc:postgresql:test postgres password\nThis will run the tests on the database test on the local host.\n");
+ System.err.println("java -Djdbc.driver=postgresql.Driver example.blobtest jdbc:postgresql:test postgres password debug\nThis is the same as above, but will output debug information.\n");
+
+ System.err.println("This example tests the binary large object api of the driver.\nThis allows images or java objects to be stored in the database, and retrieved\nusing both postgresql's own api, and the standard JDBC api.");
+ }
+
+ public static void main(String args[])
+ {
+ System.out.println("PostgreSQL blobtest v6.3 rev 1\n");
+
+ if(args.length<3) {
+ instructions();
+ System.exit(1);
+ }
+
+ // This line outputs debug information to stderr. To enable this, simply
+ // add an extra parameter to the command line
+ if(args.length>3)
+ DriverManager.setLogStream(System.err);
+
+ // Now run the tests
+ try {
+ blobtest test = new blobtest(args);
+ } catch(Exception ex) {
+ System.err.println("Exception caught.\n"+ex);
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package example;
+
+import java.io.*;
+import java.sql.*;
+import java.text.*;
+
+/**
+ * This example tests the various date styles that are available to postgresql.
+ *
+ * To use this example, you need a database to be in existence. This example
+ * will create a table called datestyle.
+ *
+ */
+
+public class datestyle
+{
+ Connection db; // The connection to the database
+ Statement st; // Our statement to run queries with
+
+ // This is our standard to compare results with.
+ java.sql.Date standard;
+
+ // This is a list of the available date styles including variants.
+ // These have to match what the "set datestyle" statement accepts.
+ String styles[] = {
+ "postgres,european",
+ "postgres,us",
+ "iso", // iso has no variants - us/european has no affect
+ "sql,european",
+ "sql,us",
+ "german" // german has no variants - us/european has no affect
+ };
+
+ public datestyle(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
+ {
+ String url = args[0];
+ String usr = args[1];
+ String pwd = args[2];
+
+ // Load the driver
+ Class.forName("postgresql.Driver");
+
+ // Connect to database
+ System.out.println("Connecting to Database URL = " + url);
+ db = DriverManager.getConnection(url, usr, pwd);
+
+ System.out.println("Connected...Now creating a statement");
+ st = db.createStatement();
+
+ // Clean up the database (in case we failed earlier) then initialise
+ cleanup();
+ init();
+
+ // Now run tests using JDBC methods
+ doexample();
+
+ // Clean up the database
+ cleanup();
+
+ // Finally close the database
+ System.out.println("Now closing the connection");
+ st.close();
+ db.close();
+
+ }
+
+ /**
+ * This drops the table (if it existed). No errors are reported.
+ */
+ public void cleanup()
+ {
+ try {
+ st.executeUpdate("drop table datestyle");
+ } catch(Exception ex) {
+ // We ignore any errors here
+ }
+ }
+
+ /**
+ * This initialises the database for this example
+ */
+ public void init() throws SQLException
+ {
+ // Create a table holding a single date
+ st.executeUpdate("create table datestyle (dt date)");
+
+ // Now create our standard date for the test.
+ //
+ // NB: each component of the date should be different, otherwise the tests
+ // will not be valid.
+ //
+ // NB: January = 0 here
+ //
+ standard = new java.sql.Date(98,0,8);
+
+ // Now store the result.
+ //
+ // This is an example of how to set a date in a date style independent way.
+ // The only way of doing this is by using a PreparedStatement.
+ //
+ PreparedStatement ps = db.prepareStatement("insert into datestyle values (?)");
+ ps.setDate(1,standard);
+ ps.executeUpdate();
+ ps.close();
+
+ }
+
+ /**
+ * This performs the example
+ */
+ public void doexample() throws SQLException
+ {
+ System.out.println("\nRunning tests:");
+
+ for(int i=0;i<styles.length;i++) {
+ System.out.print("Test "+i+" - "+styles[i]);
+ System.out.flush();
+
+ // set the style
+ st.executeUpdate("set datestyle='"+styles[i]+"'");
+
+ // Now because the driver needs to know what the current style is,
+ // we have to run the following:
+ st.executeUpdate("show datestyle");
+ // This is a limitation, but there is no real way around this.
+
+ // Now we query the table.
+ ResultSet rs = st.executeQuery("select dt from datestyle");
+
+ // Throw an exception if there is no result (if the table is empty
+ // there should still be a result).
+ if(rs==null)
+ throw new SQLException("The test query returned no data");
+
+ while(rs.next()) {
+ // The JDBC spec states we should only read each column once.
+ // In the current implementation of the driver, this is not necessary.
+ // Here we use this fact to see what the query really returned.
+ if(standard.equals(rs.getDate(1)))
+ System.out.println(" passed, returned "+rs.getString(1));
+ else
+ System.out.println(" failed, returned "+rs.getString(1));
+ }
+ rs.close();
+ }
+ }
+
+ /**
+ * Display some instructions on how to run the example
+ */
+ public static void instructions()
+ {
+ System.out.println("\nThis example tests the drivers ability to handle dates correctly if the\nbackend is running any of the various date styles that it supports.\nIdealy this should work fine. If it doesn't, then there is something wrong\npossibly in postgresql.Connection or in the backend itself. If this does occur\nthen please email a bug report.\n");
+ System.out.println("Useage:\n java example.datestyle jdbc:postgresql:database user password [debug]\n\nThe debug field can be anything. It's presence will enable DriverManager's\ndebug trace. Unless you want to see screens of items, don't put anything in\nhere.");
+ System.exit(1);
+ }
+
+ /**
+ * This little lot starts the test
+ */
+ public static void main(String args[])
+ {
+ System.out.println("PostgreSQL datestyle test v6.3 rev 1\n");
+
+ if(args.length<3)
+ instructions();
+
+ // This line outputs debug information to stderr. To enable this, simply
+ // add an extra parameter to the command line
+ if(args.length>3)
+ DriverManager.setLogStream(System.err);
+
+ // Now run the tests
+ try {
+ datestyle test = new datestyle(args);
+ } catch(Exception ex) {
+ System.err.println("Exception caught.\n"+ex);
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package example;
+
+import java.io.*;
+import java.sql.*;
+import java.text.*;
+
+/**
+ * This example application demonstrates some of the drivers other features
+ * by implementing a simple psql replacement in Java.
+ *
+ */
+
+public class psql
+{
+ Connection db; // The connection to the database
+ Statement st; // Our statement to run queries with
+ DatabaseMetaData dbmd; // This defines the structure of the database
+
+ public psql(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
+ {
+ String url = args[0];
+ String usr = args[1];
+ String pwd = args[2];
+
+ // Load the driver
+ Class.forName("postgresql.Driver");
+
+ // Connect to database
+ System.out.println("Connecting to Database URL = " + url);
+ db = DriverManager.getConnection(url, usr, pwd);
+
+ dbmd = db.getMetaData();
+ st = db.createStatement();
+
+ // This prints the backend's version
+ System.out.println("Connected to "+dbmd.getDatabaseProductName()+" "+dbmd.getDatabaseProductVersion());
+
+ System.out.println();
+
+ // This provides us the means of reading from stdin
+ StreamTokenizer input = new StreamTokenizer(new InputStreamReader(System.in));
+ input.resetSyntax();
+ input.slashSlashComments(true); // allow // as a comment delimiter
+ input.eolIsSignificant(false); // treat eol's as spaces
+ input.wordChars(32,126);
+ input.whitespaceChars(59,59);
+ input.quoteChar(39);
+
+ // Now the main loop.
+ int tt=0,lineno=1;
+ while(tt!=StreamTokenizer.TT_EOF) {
+ System.out.print("["+lineno+"] ");
+ System.out.flush();
+
+ // Here, we trap SQLException so they don't terminate the application
+ try {
+ if((tt=input.nextToken())==StreamTokenizer.TT_WORD) {
+ processLine(input.sval);
+ lineno++;
+ }
+ } catch(SQLException ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+ System.out.println("Now closing the connection");
+ st.close();
+ db.close();
+
+ }
+
+ /**
+ * This processes a statement
+ */
+ public void processLine(String line) throws SQLException
+ {
+ if(line.startsWith("\\")) {
+ processSlashCommand(line);
+ return;
+ }
+
+ boolean type = st.execute(line);
+ boolean loop=true;
+ while(loop) {
+ if(type) {
+ // A ResultSet was returned
+ ResultSet rs=st.getResultSet();
+ displayResult(rs);
+ } else {
+ int count = st.getUpdateCount();
+
+ if(count==-1) {
+ // This indicates nothing left
+ loop=false;
+ } else {
+ // An update count was returned
+ System.out.println("Updated "+st.getUpdateCount()+" rows");
+ }
+ }
+
+ if(loop)
+ type = st.getMoreResults();
+ }
+ }
+
+ /**
+ * This displays a result set.
+ * Note: it closes the result once complete.
+ */
+ public void displayResult(ResultSet rs) throws SQLException
+ {
+ ResultSetMetaData rsmd = rs.getMetaData();
+
+ // Print the result column names
+ int cols = rsmd.getColumnCount();
+ for(int i=1;i<=cols;i++)
+ System.out.print(rsmd.getColumnLabel(i)+(i<cols?"\t":"\n"));
+
+ // now the results
+ while(rs.next()) {
+ for(int i=1;i<=cols;i++) {
+ Object o = rs.getObject(i);
+ if(rs.wasNull())
+ System.out.print("{null}"+(i<cols?"\t":"\n"));
+ else
+ System.out.print(rs.getObject(i).toString()+(i<cols?"\t":"\n"));
+ }
+ }
+
+ // finally close the result set
+ rs.close();
+ }
+
+ /**
+ * This process / commands (for now just /d)
+ */
+ public void processSlashCommand(String line) throws SQLException
+ {
+ if(line.startsWith("\\d")) {
+
+ if(line.startsWith("\\d ")) {
+ // Display details about a table
+ String table=line.substring(3);
+ displayResult(dbmd.getColumns(null,null,table,"%"));
+ } else {
+ String types[] = null;
+ if(line.equals("\\d"))
+ types=allUserTables;
+ else if(line.equals("\\di"))
+ types=usrIndices;
+ else if(line.equals("\\dt"))
+ types=usrTables;
+ else if(line.equals("\\ds"))
+ types=usrSequences;
+ else if(line.equals("\\dS"))
+ types=sysTables;
+ else
+ throw new SQLException("Unsupported \\d command: "+line);
+
+ // Display details about all system tables
+ //
+ // Note: the first two arguments are ignored. To keep to the spec,
+ // you must put null here
+ //
+ displayResult(dbmd.getTables(null,null,"%",types));
+ }
+ } else
+ throw new SQLException("Unsupported \\ command: "+line);
+ }
+
+ private static final String allUserTables[] = {"TABLE","INDEX","SEQUENCE"};
+ private static final String usrIndices[] = {"INDEX"};
+ private static final String usrTables[] = {"TABLE"};
+ private static final String usrSequences[] = {"SEQUENCE"};
+ private static final String sysTables[] = {"SYSTEM TABLE","SYSTEM INDEX"};
+
+ /**
+ * Display some instructions on how to run the example
+ */
+ public static void instructions()
+ {
+ System.out.println("\nThis example shows how some of the other JDBC features work within the\ndriver. It does this by implementing a very simple psql equivalent in java.\nNot everything that psql does is implemented.\n");
+ System.out.println("Useage:\n java example.psql jdbc:postgresql:database user password [debug]\n\nThe debug field can be anything. It's presence will enable DriverManager's\ndebug trace. Unless you want to see screens of items, don't put anything in\nhere.");
+ System.exit(1);
+ }
+
+ /**
+ * This little lot starts the test
+ */
+ public static void main(String args[])
+ {
+ System.out.println("PostgreSQL psql example v6.3 rev 1\n");
+
+ if(args.length<3)
+ instructions();
+
+ // This line outputs debug information to stderr. To enable this, simply
+ // add an extra parameter to the command line
+ if(args.length>3)
+ DriverManager.setLogStream(System.err);
+
+ // Now run the tests
+ try {
+ psql test = new psql(args);
+ } catch(Exception ex) {
+ System.err.println("Exception caught.\n"+ex);
+ ex.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package postgresql.fastpath;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This class implements the Fastpath api.
+ *
+ * <p>This is a means of executing functions imbeded in the postgresql backend
+ * from within a java application.
+ *
+ * <p>It is based around the file src/interfaces/libpq/fe-exec.c
+ *
+ *
+ * <p><b>Implementation notes:</b>
+ *
+ * <p><b><em>Network protocol:</em></b>
+ *
+ * <p>The code within the backend reads integers in reverse.
+ *
+ * <p>There is work in progress to convert all of the protocol to
+ * network order but it may not be there for v6.3
+ *
+ * <p>When fastpath switches, simply replace SendIntegerReverse() with
+ * SendInteger()
+ *
+ * @see postgresql.FastpathFastpathArg
+ * @see postgresql.LargeObject
+ */
+public class Fastpath
+{
+ // This maps the functions names to their id's (possible unique just
+ // to a connection).
+ protected Hashtable func = new Hashtable();
+
+ protected postgresql.Connection conn; // our connection
+ protected postgresql.PG_Stream stream; // the network stream
+
+ /**
+ * Initialises the fastpath system
+ *
+ * <p><b>Important Notice</b>
+ * <br>This is called from postgresql.Connection, and should not be called
+ * from client code.
+ *
+ * @param conn postgresql.Connection to attach to
+ * @param stream The network stream to the backend
+ */
+ public Fastpath(postgresql.Connection conn,postgresql.PG_Stream stream)
+ {
+ this.conn=conn;
+ this.stream=stream;
+ DriverManager.println("Fastpath initialised");
+ }
+
+ /**
+ * Send a function call to the PostgreSQL backend
+ *
+ * @param fnid Function id
+ * @param resulttype True if the result is an integer, false for other results
+ * @param args FastpathArguments to pass to fastpath
+ * @return null if no data, Integer if an integer result, or byte[] otherwise
+ * @exception SQLException if a database-access error occurs.
+ */
+ public Object fastpath(int fnid,boolean resulttype,FastpathArg[] args) throws SQLException
+ {
+ // send the function call
+ try {
+ // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
+ // that confuses the backend. The 0 terminates the command line.
+ stream.SendInteger(70,1);
+ stream.SendInteger(0,1);
+
+ stream.SendIntegerReverse(fnid,4);
+ stream.SendIntegerReverse(args.length,4);
+
+ for(int i=0;i<args.length;i++)
+ args[i].send(stream);
+
+ // This is needed, otherwise data can be lost
+ stream.flush();
+
+ } catch(IOException ioe) {
+ throw new SQLException("Failed to send fastpath call "+fnid+"\n"+ioe);
+ }
+
+ // Now handle the result
+
+ // We should get 'V' on sucess or 'E' on error. Anything else is treated
+ // as an error.
+ //int in = stream.ReceiveChar();
+ //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
+ //if(in!='V') {
+ //if(in=='E')
+ //throw new SQLException(stream.ReceiveString(4096));
+ //throw new SQLException("Fastpath: expected 'V' from backend, got "+((char)in));
+ //}
+
+ // Now loop, reading the results
+ Object result = null; // our result
+ while(true) {
+ int in = stream.ReceiveChar();
+ DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
+ switch(in)
+ {
+ case 'V':
+ break;
+
+ //------------------------------
+ // Function returned properly
+ //
+ case 'G':
+ int sz = stream.ReceiveInteger(4);
+ DriverManager.println("G: size="+sz); //debug
+
+ // Return an Integer if
+ if(resulttype)
+ result = new Integer(stream.ReceiveInteger(sz));
+ else {
+ byte buf[] = new byte[sz];
+ stream.Receive(buf,0,sz);
+ result = buf;
+ }
+ break;
+
+ //------------------------------
+ // Error message returned
+ case 'E':
+ throw new SQLException("Fastpath: "+stream.ReceiveString(4096));
+
+ //------------------------------
+ // Notice from backend
+ case 'N':
+ conn.addWarning(stream.ReceiveString(4096));
+ break;
+
+ //------------------------------
+ // End of results
+ //
+ // Here we simply return res, which would contain the result
+ // processed earlier. If no result, this already contains null
+ case '0':
+ DriverManager.println("returning "+result);
+ return result;
+
+ default:
+ throw new SQLException("Fastpath: protocol error. Got '"+((char)in)+"'");
+ }
+ }
+ }
+
+ /**
+ * Send a function call to the PostgreSQL backend by name.
+ *
+ * Note: the mapping for the procedure name to function id needs to exist,
+ * usually to an earlier call to addfunction().
+ *
+ * This is the prefered method to call, as function id's can/may change
+ * between versions of the backend.
+ *
+ * For an example of how this works, refer to postgresql.LargeObject
+ *
+ * @param name Function name
+ * @param resulttype True if the result is an integer, false for other
+ * results
+ * @param args FastpathArguments to pass to fastpath
+ * @return null if no data, Integer if an integer result, or byte[] otherwise
+ * @exception SQLException if name is unknown or if a database-access error
+ * occurs.
+ * @see postgresql.LargeObject
+ */
+ public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException
+ {
+ DriverManager.println("Fastpath: calling "+name);
+ return fastpath(getID(name),resulttype,args);
+ }
+
+ /**
+ * This convenience method assumes that the return value is an Integer
+ * @param name Function name
+ * @param args Function arguments
+ * @return integer result
+ * @exception SQLException if a database-access error occurs or no result
+ */
+ public int getInteger(String name,FastpathArg[] args) throws SQLException
+ {
+ Integer i = (Integer)fastpath(name,true,args);
+ if(i==null)
+ throw new SQLException("Fastpath:"+name+": no result returned, expected integer");
+ return i.intValue();
+ }
+
+ /**
+ * This convenience method assumes that the return value is an Integer
+ * @param name Function name
+ * @param args Function arguments
+ * @return byte[] array containing result
+ * @exception SQLException if a database-access error occurs or no result
+ */
+ public byte[] getData(String name,FastpathArg[] args) throws SQLException
+ {
+ return (byte[])fastpath(name,false,args);
+ }
+
+ /**
+ * This adds a function to our lookup table.
+ *
+ * <p>User code should use the addFunctions method, which is based upon a
+ * query, rather than hard coding the oid. The oid for a function is not
+ * guaranteed to remain static, even on different servers of the same
+ * version.
+ *
+ * @param name Function name
+ * @param fnid Function id
+ */
+ public void addFunction(String name,int fnid)
+ {
+ func.put(name,new Integer(fnid));
+ }
+
+ /**
+ * This takes a ResultSet containing two columns. Column 1 contains the
+ * function name, Column 2 the oid.
+ *
+ * <p>It reads the entire ResultSet, loading the values into the function
+ * table.
+ *
+ * <p><b>REMEMBER</b> to close() the resultset after calling this!!
+ *
+ * <p><b><em>Implementation note about function name lookups:</em></b>
+ *
+ * <p>PostgreSQL stores the function id's and their corresponding names in
+ * the pg_proc table. To speed things up locally, instead of querying each
+ * function from that table when required, a Hashtable is used. Also, only
+ * the function's required are entered into this table, keeping connection
+ * times as fast as possible.
+ *
+ * <p>The postgresql.LargeObject class performs a query upon it's startup,
+ * and passes the returned ResultSet to the addFunctions() method here.
+ *
+ * <p>Once this has been done, the LargeObject api refers to the functions by
+ * name.
+ *
+ * <p>Dont think that manually converting them to the oid's will work. Ok,
+ * they will for now, but they can change during development (there was some
+ * discussion about this for V7.0), so this is implemented to prevent any
+ * unwarranted headaches in the future.
+ *
+ * @param rs ResultSet
+ * @exception SQLException if a database-access error occurs.
+ * @see postgresql.LargeObjectManager
+ */
+ public void addFunctions(ResultSet rs) throws SQLException
+ {
+ while(rs.next()) {
+ func.put(rs.getString(1),new Integer(rs.getInt(2)));
+ }
+ }
+
+ /**
+ * This returns the function id associated by its name
+ *
+ * <p>If addFunction() or addFunctions() have not been called for this name,
+ * then an SQLException is thrown.
+ *
+ * @param name Function name to lookup
+ * @return Function ID for fastpath call
+ * @exception SQLException is function is unknown.
+ */
+ public int getID(String name) throws SQLException
+ {
+ Integer id = (Integer)func.get(name);
+
+ // may be we could add a lookup to the database here, and store the result
+ // in our lookup table, throwing the exception if that fails.
+ // We must, however, ensure that if we do, any existing ResultSet is
+ // unaffected, otherwise we could break user code.
+ //
+ // so, until we know we can do this (needs testing, on the TODO list)
+ // for now, we throw the exception and do no lookups.
+ if(id==null)
+ throw new SQLException("Fastpath: function "+name+" is unknown");
+
+ return id.intValue();
+ }
+}
+
--- /dev/null
+package postgresql.fastpath;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * Each fastpath call requires an array of arguments, the number and type
+ * dependent on the function being called.
+ *
+ * <p>This class implements methods needed to provide this capability.
+ *
+ * <p>For an example on how to use this, refer to the postgresql.largeobject
+ * package
+ *
+ * @see postgresql.fastpath.Fastpath
+ * @see postgresql.largeobject.LargeObjectManager
+ * @see postgresql.largeobject.LargeObject
+ */
+public class FastpathArg
+{
+ /**
+ * Type of argument, true=integer, false=byte[]
+ */
+ public boolean type;
+
+ /**
+ * Integer value if type=true
+ */
+ public int value;
+
+ /**
+ * Byte value if type=false;
+ */
+ public byte[] bytes;
+
+ /**
+ * Constructs an argument that consists of an integer value
+ * @param value int value to set
+ */
+ public FastpathArg(int value)
+ {
+ type=true;
+ this.value=value;
+ }
+
+ /**
+ * Constructs an argument that consists of an array of bytes
+ * @param bytes array to store
+ */
+ public FastpathArg(byte bytes[])
+ {
+ type=false;
+ this.bytes=bytes;
+ }
+
+ /**
+ * Constructs an argument that consists of part of a byte array
+ * @param buf source array
+ * @param off offset within array
+ * @param len length of data to include
+ */
+ public FastpathArg(byte buf[],int off,int len)
+ {
+ type=false;
+ bytes = new byte[len];
+ System.arraycopy(buf,off,bytes,0,len);
+ }
+
+ /**
+ * Constructs an argument that consists of a String.
+ * @param s String to store
+ */
+ public FastpathArg(String s)
+ {
+ this(s.getBytes());
+ }
+
+ /**
+ * This sends this argument down the network stream.
+ *
+ * <p>The stream sent consists of the length.int4 then the contents.
+ *
+ * <p><b>Note:</b> This is called from Fastpath, and cannot be called from
+ * client code.
+ *
+ * @param s output stream
+ * @exception IOException if something failed on the network stream
+ */
+ protected void send(postgresql.PG_Stream s) throws IOException
+ {
+ if(type) {
+ // argument is an integer
+ s.SendIntegerReverse(4,4); // size of an integer
+ s.SendIntegerReverse(value,4); // integer value of argument
+ } else {
+ // argument is a byte array
+ s.SendIntegerReverse(bytes.length,4); // size of array
+ s.Send(bytes);
+ }
+ }
+}
+
--- /dev/null
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This represents the box datatype within postgresql.
+ */
+public class PGbox extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ /**
+ * @param x1 first x coordinate
+ * @param y1 first y coordinate
+ * @param x2 second x coordinate
+ * @param y2 second y coordinate
+ */
+ public PGbox(double x1,double y1,double x2,double y2)
+ {
+ this();
+ this.point[0] = new PGpoint(x1,y1);
+ this.point[1] = new PGpoint(x2,y2);
+ }
+
+ /**
+ * @param p1 first point
+ * @param p2 second point
+ */
+ public PGbox(PGpoint p1,PGpoint p2)
+ {
+ this();
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * @param s Box definition in PostgreSQL syntax
+ * @exception SQLException if definition is invalid
+ */
+ public PGbox(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * Required constructor
+ */
+ public PGbox()
+ {
+ setType("box");
+ }
+
+ /**
+ * This method sets the value of this object. It should be overidden,
+ * but still called by subclasses.
+ *
+ * @param value a string representation of the value of the object
+ * @exception SQLException thrown if value is invalid for this type
+ */
+ public void setValue(String value) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(value,',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of box failed - "+value);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGbox) {
+ PGbox p = (PGbox)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGbox((PGpoint)point[0].clone(),(PGpoint)point[1].clone());
+ }
+
+ /**
+ * @return the PGbox in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return point[0].toString()+","+point[1].toString();
+ }
+}
--- /dev/null
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This represents postgresql's circle datatype, consisting of a point and
+ * a radius
+ */
+public class PGcircle extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * This is the centre point
+ */
+ public PGpoint center;
+
+ /**
+ * This is the radius
+ */
+ double radius;
+
+ /**
+ * @param x coordinate of centre
+ * @param y coordinate of centre
+ * @param r radius of circle
+ */
+ public PGcircle(double x,double y,double r)
+ {
+ this(new PGpoint(x,y),r);
+ }
+
+ /**
+ * @param c PGpoint describing the circle's centre
+ * @param r radius of circle
+ */
+ public PGcircle(PGpoint c,double r)
+ {
+ this();
+ this.center = c;
+ this.radius = r;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGcircle(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGcircle()
+ {
+ setType("circle");
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeAngle(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of circle failed - "+s);
+
+ try {
+ center = new PGpoint(t.getToken(0));
+ radius = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of circle failed - "+s+" - +"+e.toString());
+ }
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGcircle) {
+ PGcircle p = (PGcircle)obj;
+ return p.center.equals(center) && p.radius==radius;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGcircle((PGpoint)center.clone(),radius);
+ }
+
+ /**
+ * @return the PGcircle in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "<"+center+","+radius+">";
+ }
+}
--- /dev/null
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements a lseg (line segment) consisting of two points
+ */
+public class PGlseg extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ /**
+ * @param x1 coordinate for first point
+ * @param y1 coordinate for first point
+ * @param x2 coordinate for second point
+ * @param y2 coordinate for second point
+ */
+ public PGlseg(double x1,double y1,double x2,double y2)
+ {
+ this(new PGpoint(x1,y1),new PGpoint(x2,y2));
+ }
+
+ /**
+ * @param p1 first point
+ * @param p2 second point
+ */
+ public PGlseg(PGpoint p1,PGpoint p2)
+ {
+ this();
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGlseg(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * reuired by the driver
+ */
+ public PGlseg()
+ {
+ setType("lseg");
+ }
+
+ /**
+ * @param s Definition of the line segment in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeBox(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of lseg failed - "+s);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGlseg) {
+ PGlseg p = (PGlseg)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGlseg((PGpoint)point[0].clone(),(PGpoint)point[1].clone());
+ }
+
+ /**
+ * @return the PGlseg in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "["+point[0]+","+point[1]+"]";
+ }
+}
--- /dev/null
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements a path (a multiple segmented line, which may be closed)
+ */
+public class PGpath extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * True if the path is open, false if closed
+ */
+ public boolean open;
+
+ /**
+ * The points defining this path
+ */
+ public PGpoint points[];
+
+ /**
+ * @param points the PGpoints that define the path
+ * @param open True if the path is open, false if closed
+ */
+ public PGpath(PGpoint[] points,boolean open)
+ {
+ this();
+ this.points = points;
+ this.open = open;
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpath()
+ {
+ setType("path");
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGpath(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * @param s Definition of the path in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ // First test to see if were open
+ if(s.startsWith("[") && s.endsWith("]")) {
+ open = true;
+ s = PGtokenizer.removeBox(s);
+ } else if(s.startsWith("(") && s.endsWith(")")) {
+ open = false;
+ s = PGtokenizer.removePara(s);
+ } else
+ throw new SQLException("cannot tell if path is open or closed");
+
+ PGtokenizer t = new PGtokenizer(s,',');
+ int npoints = t.getSize();
+ points = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ points[p] = new PGpoint(t.getToken(p));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpath) {
+ PGpath p = (PGpath)obj;
+
+ if(p.points.length != points.length)
+ return false;
+
+ if(p.open != open)
+ return false;
+
+ for(int i=0;i<points.length;i++)
+ if(!points[i].equals(p.points[i]))
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGpoint ary[] = new PGpoint[points.length];
+ for(int i=0;i<points.length;i++)
+ ary[i]=(PGpoint)points[i].clone();
+ return new PGpath(ary,open);
+ }
+
+ /**
+ * This returns the polygon in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ StringBuffer b = new StringBuffer(open?"[":"(");
+
+ for(int p=0;p<points.length;p++) {
+ if(p>0) b.append(",");
+ b.append(points[p].toString());
+ }
+ b.append(open?"]":")");
+
+ return b.toString();
+ }
+
+ public boolean isOpen()
+ {
+ return open;
+ }
+
+ public boolean isClosed()
+ {
+ return !open;
+ }
+
+ public void closePath()
+ {
+ open = false;
+ }
+
+ public void openPath()
+ {
+ open = true;
+ }
+
+}
--- /dev/null
+package postgresql.geometric;
+
+import java.awt.Point;
+import java.io.*;
+import java.sql.*;
+
+import postgresql.util.*;
+
+/**
+ * This implements a version of java.awt.Point, except it uses double
+ * to represent the coordinates.
+ *
+ * <p>It maps to the point datatype in postgresql.
+ */
+public class PGpoint extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * The X coordinate of the point
+ */
+ public double x;
+
+ /**
+ * The Y coordinate of the point
+ */
+ public double y;
+
+ /**
+ * @param x coordinate
+ * @param y coordinate
+ */
+ public PGpoint(double x,double y)
+ {
+ this();
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * This is called mainly from the other geometric types, when a
+ * point is imbeded within their definition.
+ *
+ * @param value Definition of this point in PostgreSQL's syntax
+ */
+ public PGpoint(String value) throws SQLException
+ {
+ this();
+ setValue(value);
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpoint()
+ {
+ setType("point");
+ }
+
+ /**
+ * @param s Definition of this point in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ try {
+ x = Double.valueOf(t.getToken(0)).doubleValue();
+ y = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of point failed - "+e.toString());
+ }
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpoint) {
+ PGpoint p = (PGpoint)obj;
+ return x == p.x && y == p.y;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGpoint(x,y);
+ }
+
+ /**
+ * @return the PGpoint in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "("+x+","+y+")";
+ }
+
+ /**
+ * Translate the point with the supplied amount.
+ * @param x integer amount to add on the x axis
+ * @param y integer amount to add on the y axis
+ */
+ public void translate(int x,int y)
+ {
+ translate((double)x,(double)y);
+ }
+
+ /**
+ * Translate the point with the supplied amount.
+ * @param x double amount to add on the x axis
+ * @param y double amount to add on the y axis
+ */
+ public void translate(double x,double y)
+ {
+ this.x += x;
+ this.y += y;
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * @param x integer coordinate
+ * @param y integer coordinate
+ */
+ public void move(int x,int y)
+ {
+ setLocation(x,y);
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * @param x double coordinate
+ * @param y double coordinate
+ */
+ public void move(double x,double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * refer to java.awt.Point for description of this
+ * @param x integer coordinate
+ * @param y integer coordinate
+ * @see java.awt.Point
+ */
+ public void setLocation(int x,int y)
+ {
+ move((double)x,(double)y);
+ }
+
+ /**
+ * Moves the point to the supplied java.awt.Point
+ * refer to java.awt.Point for description of this
+ * @param p Point to move to
+ * @see java.awt.Point
+ */
+ public void setLocation(Point p)
+ {
+ setLocation(p.x,p.y);
+ }
+
+}
--- /dev/null
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements the polygon datatype within PostgreSQL.
+ */
+public class PGpolygon extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * The points defining the polygon
+ */
+ public PGpoint points[];
+
+ /**
+ * Creates a polygon using an array of PGpoints
+ *
+ * @param points the points defining the polygon
+ */
+ public PGpolygon(PGpoint[] points)
+ {
+ this();
+ this.points = points;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGpolygon(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpolygon()
+ {
+ setType("polygon");
+ }
+
+ /**
+ * @param s Definition of the polygon in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ int npoints = t.getSize();
+ points = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ points[p] = new PGpoint(t.getToken(p));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpolygon) {
+ PGpolygon p = (PGpolygon)obj;
+
+ if(p.points.length != points.length)
+ return false;
+
+ for(int i=0;i<points.length;i++)
+ if(!points[i].equals(p.points[i]))
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGpoint ary[] = new PGpoint[points.length];
+ for(int i=0;i<points.length;i++)
+ ary[i] = (PGpoint)points[i].clone();
+ return new PGpolygon(ary);
+ }
+
+ /**
+ * @return the PGpolygon in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("(");
+ for(int p=0;p<points.length;p++) {
+ if(p>0) b.append(",");
+ b.append(points[p].toString());
+ }
+ b.append(")");
+ return b.toString();
+ }
+}
--- /dev/null
+package postgresql.largeobject;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+
+import postgresql.fastpath.*;
+
+/**
+ * This class implements the large object interface to postgresql.
+ *
+ * <p>It provides the basic methods required to run the interface, plus
+ * a pair of methods that provide InputStream and OutputStream classes
+ * for this object.
+ *
+ * <p>Normally, client code would use the getAsciiStream, getBinaryStream,
+ * or getUnicodeStream methods in ResultSet, or setAsciiStream,
+ * setBinaryStream, or setUnicodeStream methods in PreparedStatement to
+ * access Large Objects.
+ *
+ * <p>However, sometimes lower level access to Large Objects are required,
+ * that are not supported by the JDBC specification.
+ *
+ * <p>Refer to postgresql.largeobject.LargeObjectManager on how to gain access
+ * to a Large Object, or how to create one.
+ *
+ * @see postgresql.largeobject.LargeObjectManager
+ * @see postgresql.ResultSet#getAsciiStream
+ * @see postgresql.ResultSet#getBinaryStream
+ * @see postgresql.ResultSet#getUnicodeStream
+ * @see postgresql.PreparedStatement#setAsciiStream
+ * @see postgresql.PreparedStatement#setBinaryStream
+ * @see postgresql.PreparedStatement#setUnicodeStream
+ * @see java.sql.ResultSet#getAsciiStream
+ * @see java.sql.ResultSet#getBinaryStream
+ * @see java.sql.ResultSet#getUnicodeStream
+ * @see java.sql.PreparedStatement#setAsciiStream
+ * @see java.sql.PreparedStatement#setBinaryStream
+ * @see java.sql.PreparedStatement#setUnicodeStream
+ *
+ */
+public class LargeObject
+{
+ /**
+ * Indicates a seek from the begining of a file
+ */
+ public static final int SEEK_SET = 0;
+
+ /**
+ * Indicates a seek from the current position
+ */
+ public static final int SEEK_CUR = 1;
+
+ /**
+ * Indicates a seek from the end of a file
+ */
+ public static final int SEEK_END = 2;
+
+ private Fastpath fp; // Fastpath API to use
+ private int oid; // OID of this object
+ private int fd; // the descriptor of the open large object
+
+ /**
+ * This opens a large object.
+ *
+ * <p>If the object does not exist, then an SQLException is thrown.
+ *
+ * @param fp FastPath API for the connection to use
+ * @param oid of the Large Object to open
+ * @param mode Mode of opening the large object
+ * (defined in LargeObjectManager)
+ * @exception SQLException if a database-access error occurs.
+ * @see postgresql.largeobject.LargeObjectManager
+ */
+ protected LargeObject(Fastpath fp,int oid,int mode) throws SQLException
+ {
+ this.fp = fp;
+ this.oid = oid;
+
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(oid);
+ args[1] = new FastpathArg(mode);
+ this.fd = fp.getInteger("lo_open",args);
+ }
+
+ /**
+ * @return the OID of this LargeObject
+ */
+ public int getOID()
+ {
+ return oid;
+ }
+
+ /**
+ * This method closes the object. You must not call methods in this
+ * object after this is called.
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void close() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(fd);
+ fp.fastpath("lo_close",false,args); // true here as we dont care!!
+ }
+
+ /**
+ * Reads some data from the object, and return as a byte[] array
+ *
+ * @param len number of bytes to read
+ * @return byte[] array containing data read
+ * @exception SQLException if a database-access error occurs.
+ */
+ public byte[] read(int len) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(len);
+ return fp.getData("loread",args);
+ }
+
+ /**
+ * Reads some data from the object into an existing array
+ *
+ * @param buf destination array
+ * @param off offset within array
+ * @param len number of bytes to read
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void read(byte buf[],int off,int len) throws SQLException
+ {
+ System.arraycopy(read(len),0,buf,off,len);
+ }
+
+ /**
+ * Writes an array to the object
+ *
+ * @param buf array to write
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void write(byte buf[]) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(buf);
+ fp.fastpath("lowrite",false,args);
+ }
+
+ /**
+ * Writes some data from an array to the object
+ *
+ * @param buf destination array
+ * @param off offset within array
+ * @param len number of bytes to write
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void write(byte buf[],int off,int len) throws SQLException
+ {
+ byte data[] = new byte[len];
+ System.arraycopy(buf,off,data,0,len);
+ write(data);
+ }
+
+ /**
+ * Sets the current position within the object.
+ *
+ * <p>This is similar to the fseek() call in the standard C library. It
+ * allows you to have random access to the large object.
+ *
+ * @param pos position within object
+ * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void seek(int pos,int ref) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[3];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(pos);
+ args[2] = new FastpathArg(ref);
+ fp.fastpath("lo_lseek",false,args);
+ }
+
+ /**
+ * Sets the current position within the object.
+ *
+ * <p>This is similar to the fseek() call in the standard C library. It
+ * allows you to have random access to the large object.
+ *
+ * @param pos position within object from begining
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void seek(int pos) throws SQLException
+ {
+ seek(pos,SEEK_SET);
+ }
+
+ /**
+ * @return the current position within the object
+ * @exception SQLException if a database-access error occurs.
+ */
+ public int tell() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(fd);
+ return fp.getInteger("lo_tell",args);
+ }
+
+ /**
+ * This method is inefficient, as the only way to find out the size of
+ * the object is to seek to the end, record the current position, then
+ * return to the original position.
+ *
+ * <p>A better method will be found in the future.
+ *
+ * @return the size of the large object
+ * @exception SQLException if a database-access error occurs.
+ */
+ public int size() throws SQLException
+ {
+ int cp = tell();
+ seek(0,SEEK_END);
+ int sz = tell();
+ seek(cp,SEEK_SET);
+ return sz;
+ }
+
+ /**
+ * Returns an InputStream from this object.
+ *
+ * <p>This InputStream can then be used in any method that requires an
+ * InputStream.
+ *
+ * @exception SQLException if a database-access error occurs.
+ */
+ public InputStream getInputStream() throws SQLException
+ {
+ throw new SQLException("LargeObject:getInputStream not implemented");
+ }
+
+ /**
+ * Returns an OutputStream to this object
+ *
+ * <p>This OutputStream can then be used in any method that requires an
+ * OutputStream.
+ *
+ * @exception SQLException if a database-access error occurs.
+ */
+ public OutputStream getOutputStream() throws SQLException
+ {
+ throw new SQLException("LargeObject:getOutputStream not implemented");
+ }
+}
--- /dev/null
+package postgresql.largeobject;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+
+import postgresql.fastpath.*;
+
+/**
+ * This class implements the large object interface to postgresql.
+ *
+ * <p>It provides methods that allow client code to create, open and delete
+ * large objects from the database. When opening an object, an instance of
+ * postgresql.largeobject.LargeObject is returned, and its methods then allow
+ * access to the object.
+ *
+ * <p>This class can only be created by postgresql.Connection
+ *
+ * <p>To get access to this class, use the following segment of code:
+ * <br><pre>
+ * import postgresql.largeobject.*;
+ *
+ * Connection conn;
+ * LargeObjectManager lobj;
+ *
+ * ... code that opens a connection ...
+ *
+ * lobj = ((postgresql.Connection)myconn).getLargeObjectAPI();
+ * </pre>
+ *
+ * <p>Normally, client code would use the getAsciiStream, getBinaryStream,
+ * or getUnicodeStream methods in ResultSet, or setAsciiStream,
+ * setBinaryStream, or setUnicodeStream methods in PreparedStatement to
+ * access Large Objects.
+ *
+ * <p>However, sometimes lower level access to Large Objects are required,
+ * that are not supported by the JDBC specification.
+ *
+ * <p>Refer to postgresql.largeobject.LargeObject on how to manipulate the
+ * contents of a Large Object.
+ *
+ * @see postgresql.largeobject.LargeObject
+ * @see postgresql.ResultSet#getAsciiStream
+ * @see postgresql.ResultSet#getBinaryStream
+ * @see postgresql.ResultSet#getUnicodeStream
+ * @see postgresql.PreparedStatement#setAsciiStream
+ * @see postgresql.PreparedStatement#setBinaryStream
+ * @see postgresql.PreparedStatement#setUnicodeStream
+ * @see java.sql.ResultSet#getAsciiStream
+ * @see java.sql.ResultSet#getBinaryStream
+ * @see java.sql.ResultSet#getUnicodeStream
+ * @see java.sql.PreparedStatement#setAsciiStream
+ * @see java.sql.PreparedStatement#setBinaryStream
+ * @see java.sql.PreparedStatement#setUnicodeStream
+ */
+public class LargeObjectManager
+{
+ // the fastpath api for this connection
+ private Fastpath fp;
+
+ /**
+ * This mode indicates we want to write to an object
+ */
+ public static final int WRITE = 0x00020000;
+
+ /**
+ * This mode indicates we want to read an object
+ */
+ public static final int READ = 0x00040000;
+
+ /**
+ * This mode is the default. It indicates we want read and write access to
+ * a large object
+ */
+ public static final int READWRITE = READ | WRITE;
+
+ /**
+ * This prevents us being created by mere mortals
+ */
+ private LargeObjectManager()
+ {
+ }
+
+ /**
+ * Constructs the LargeObject API.
+ *
+ * <p><b>Important Notice</b>
+ * <br>This method should only be called by postgresql.Connection
+ *
+ * <p>There should only be one LargeObjectManager per Connection. The
+ * postgresql.Connection class keeps track of the various extension API's
+ * and it's advised you use those to gain access, and not going direct.
+ */
+ public LargeObjectManager(postgresql.Connection conn) throws SQLException
+ {
+ // We need Fastpath to do anything
+ this.fp = conn.getFastpathAPI();
+
+ // Now get the function oid's for the api
+ //
+ // This is an example of Fastpath.addFunctions();
+ //
+ ResultSet res = (postgresql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" +
+ " where proname = 'lo_open'" +
+ " or proname = 'lo_close'" +
+ " or proname = 'lo_creat'" +
+ " or proname = 'lo_unlink'" +
+ " or proname = 'lo_lseek'" +
+ " or proname = 'lo_tell'" +
+ " or proname = 'loread'" +
+ " or proname = 'lowrite'");
+
+ if(res==null)
+ throw new SQLException("failed to initialise LargeObject API");
+
+ fp.addFunctions(res);
+ res.close();
+ DriverManager.println("Large Object initialised");
+ }
+
+ /**
+ * This opens an existing large object, based on its OID. This method
+ * assumes that READ and WRITE access is required (the default).
+ *
+ * @param oid of large object
+ * @return LargeObject instance providing access to the object
+ * @exception SQLException on error
+ */
+ public LargeObject open(int oid) throws SQLException
+ {
+ return new LargeObject(fp,oid,READWRITE);
+ }
+
+ /**
+ * This opens an existing large object, based on its OID
+ *
+ * @param oid of large object
+ * @param mode mode of open
+ * @return LargeObject instance providing access to the object
+ * @exception SQLException on error
+ */
+ public LargeObject open(int oid,int mode) throws SQLException
+ {
+ return new LargeObject(fp,oid,mode);
+ }
+
+ /**
+ * This creates a large object, returning its OID.
+ *
+ * <p>It defaults to READWRITE for the new object's attributes.
+ *
+ * @return oid of new object
+ * @exception SQLException on error
+ */
+ public int create() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(READWRITE);
+ return fp.getInteger("lo_creat",args);
+ }
+
+ /**
+ * This creates a large object, returning its OID
+ *
+ * @param mode a bitmask describing different attributes of the new object
+ * @return oid of new object
+ * @exception SQLException on error
+ */
+ public int create(int mode) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(mode);
+ return fp.getInteger("lo_creat",args);
+ }
+
+ /**
+ * This deletes a large object.
+ *
+ * @param oid describing object to delete
+ * @exception SQLException on error
+ */
+ public void delete(int oid) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(oid);
+ fp.fastpath("lo_unlink",false,args);
+ }
+
+ /**
+ * This deletes a large object.
+ *
+ * <p>It is identical to the delete method, and is supplied as the C API uses
+ * unlink.
+ *
+ * @param oid describing object to delete
+ * @exception SQLException on error
+ */
+ public void unlink(int oid) throws SQLException
+ {
+ delete(oid);
+ }
+
+}
--- /dev/null
+package postgresql.util;
+
+import java.io.*;
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+
+/**
+ * postgresql.PG_Object is a class used to describe unknown types
+ * An unknown type is any type that is unknown by JDBC Standards
+ *
+ * <p>As of PostgreSQL 6.3, this allows user code to add their own
+ * handlers via a call to postgresql.Connection. These handlers
+ * must extend this class.
+ */
+public class PGobject implements Serializable,Cloneable
+{
+ protected String type;
+ protected String value;
+
+ /**
+ * This is called by postgresql.Connection.getObject() to create the
+ * object.
+ */
+ public PGobject()
+ {
+ }
+
+ /**
+ * This method sets the type of this object.
+ *
+ * <p>It should not be extended by subclasses, hence its final
+ *
+ * @param type a string describing the type of the object
+ */
+ public final void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * This method sets the value of this object. It must be overidden.
+ *
+ * @param value a string representation of the value of the object
+ * @exception SQLException thrown if value is invalid for this type
+ */
+ public void setValue(String value) throws SQLException
+ {
+ this.value = value;
+ }
+
+ /**
+ * As this cannot change during the life of the object, it's final.
+ * @return the type name of this object
+ */
+ public final String getType()
+ {
+ return type;
+ }
+
+ /**
+ * This must be overidden, to return the value of the object, in the
+ * form required by postgresql.
+ * @return the value of this object
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This must be overidden to allow comparisons of objects
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGobject)
+ return ((PGobject)obj).getValue().equals(getValue());
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGobject obj = new PGobject();
+ obj.type=type;
+ obj.value=value;
+ return obj;
+ }
+
+ /**
+ * This is defined here, so user code need not overide it.
+ * @return the value of this object, in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return value;
+ }
+}
--- /dev/null
+package postgresql.util;
+
+import java.sql.*;
+import java.util.*;
+
+/**
+ * This class is used to tokenize the text output of postgres.
+ *
+ * <p>It's mainly used by the geometric classes, but is useful in parsing any
+ * output from custom data types output from postgresql.
+ *
+ * @see postgresql.geometric.PGbox
+ * @see postgresql.geometric.PGcircle
+ * @see postgresql.geometric.PGlseg
+ * @see postgresql.geometric.PGpath
+ * @see postgresql.geometric.PGpoint
+ * @see postgresql.geometric.PGpolygon
+ */
+public class PGtokenizer
+{
+ // Our tokens
+ protected Vector tokens;
+
+ /**
+ * Create a tokeniser.
+ *
+ * <p>We could have used StringTokenizer to do this, however, we needed to
+ * handle nesting of '(' ')' '[' ']' '<' and '>' as these are used
+ * by the geometric data types.
+ *
+ * @param string containing tokens
+ * @param delim single character to split the tokens
+ */
+ public PGtokenizer(String string,char delim)
+ {
+ tokenize(string,delim);
+ }
+
+ /**
+ * This resets this tokenizer with a new string and/or delimiter.
+ *
+ * @param string containing tokens
+ * @param delim single character to split the tokens
+ */
+ public int tokenize(String string,char delim)
+ {
+ tokens = new Vector();
+
+ // nest holds how many levels we are in the current token.
+ // if this is > 0 then we don't split a token when delim is matched.
+ //
+ // The Geometric datatypes use this, because often a type may have others
+ // (usualls PGpoint) imbedded within a token.
+ //
+ // Peter 1998 Jan 6 - Added < and > to the nesting rules
+ int nest=0,p,s;
+
+ for(p=0,s=0;p<string.length();p++) {
+ char c = string.charAt(p);
+
+ // increase nesting if an open character is found
+ if(c == '(' || c == '[' || c == '<')
+ nest++;
+
+ // decrease nesting if a close character is found
+ if(c == ')' || c == ']' || c == '>')
+ nest--;
+
+ if(nest==0 && c==delim) {
+ tokens.addElement(string.substring(s,p));
+ s=p+1; // +1 to skip the delimiter
+ }
+
+ }
+
+ // Don't forget the last token ;-)
+ if(s<string.length())
+ tokens.addElement(string.substring(s));
+
+ return tokens.size();
+ }
+
+ /**
+ * @return the number of tokens available
+ */
+ public int getSize()
+ {
+ return tokens.size();
+ }
+
+ /**
+ * @param n Token number ( 0 ... getSize()-1 )
+ * @return The token value
+ */
+ public String getToken(int n)
+ {
+ return (String)tokens.elementAt(n);
+ }
+
+ /**
+ * This returns a new tokenizer based on one of our tokens.
+ *
+ * The geometric datatypes use this to process nested tokens (usually
+ * PGpoint).
+ *
+ * @param n Token number ( 0 ... getSize()-1 )
+ * @param delim The delimiter to use
+ * @return A new instance of PGtokenizer based on the token
+ */
+ public PGtokenizer tokenizeToken(int n,char delim)
+ {
+ return new PGtokenizer(getToken(n),delim);
+ }
+
+ /**
+ * This removes the lead/trailing strings from a string
+ * @param s Source string
+ * @param l Leading string to remove
+ * @param t Trailing string to remove
+ * @return String without the lead/trailing strings
+ */
+ public static String remove(String s,String l,String t)
+ {
+ if(s.startsWith(l)) s = s.substring(l.length());
+ if(s.endsWith(t)) s = s.substring(0,s.length()-t.length());
+ return s;
+ }
+
+ /**
+ * This removes the lead/trailing strings from all tokens
+ * @param l Leading string to remove
+ * @param t Trailing string to remove
+ */
+ public void remove(String l,String t)
+ {
+ for(int i=0;i<tokens.size();i++) {
+ tokens.setElementAt(remove((String)tokens.elementAt(i),l,t),i);
+ }
+ }
+
+ /**
+ * Removes ( and ) from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the ( or )
+ */
+ public static String removePara(String s)
+ {
+ return remove(s,"(",")");
+ }
+
+ /**
+ * Removes ( and ) from the beginning and end of all tokens
+ * @return String without the ( or )
+ */
+ public void removePara()
+ {
+ remove("(",")");
+ }
+
+ /**
+ * Removes [ and ] from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the [ or ]
+ */
+ public static String removeBox(String s)
+ {
+ return remove(s,"[","]");
+ }
+
+ /**
+ * Removes [ and ] from the beginning and end of all tokens
+ * @return String without the [ or ]
+ */
+ public void removeBox()
+ {
+ remove("[","]");
+ }
+
+ /**
+ * Removes < and > from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the < or >
+ */
+ public static String removeAngle(String s)
+ {
+ return remove(s,"<",">");
+ }
+
+ /**
+ * Removes < and > from the beginning and end of all tokens
+ * @return String without the < or >
+ */
+ public void removeAngle()
+ {
+ remove("<",">");
+ }
+}