From: Bruce Momjian Date: Thu, 25 Oct 2001 06:00:05 +0000 (+0000) Subject: pgjindent jdbc files. First time jdbc files were formatted. X-Git-Tag: REL7_2_BETA1~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d2e27b0674e238a4f94f1c190a395a530746d5d3;p=postgresql pgjindent jdbc files. First time jdbc files were formatted. --- diff --git a/src/interfaces/jdbc/example/ImageViewer.java b/src/interfaces/jdbc/example/ImageViewer.java index 80581f7950..6097439bab 100644 --- a/src/interfaces/jdbc/example/ImageViewer.java +++ b/src/interfaces/jdbc/example/ImageViewer.java @@ -33,432 +33,486 @@ import org.postgresql.largeobject.*; 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 - { - // holds the image - private Image image; - - // holds the background buffer - private Image bkg; - - // the size of the buffer - private Dimension size; - - 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); - } - - /** - * Paints the image, using double buffering to prevent screen flicker - */ - public void paint(Graphics gr) - { - Dimension s = getSize(); - - if(size==null || bkg==null || !s.equals(size)) { - size = s; - bkg = createImage(size.width,size.height); - } - - // now set the background - Graphics g = bkg.getGraphics(); - g.setColor(Color.gray); - g.fillRect(0,0,s.width,s.height); - - // now paint the image over the background - if(image!=null) - g.drawImage(image,0,0,this); - - // dispose the graphics instance - g.dispose(); - - // paint the image onto the component - gr.drawImage(bkg,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("org.postgresql.Driver"); - - // Connect to database - db = DriverManager.getConnection(url, user, password); - - // Create a statement - stat = db.createStatement(); - - // Also, get the LargeObjectManager for this connection - lom = ((org.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 { - //db.setAutoCommit(true); - stat.executeUpdate("create table images (imgname name,imgoid oid)"); - label.setText("Initialised database"); - db.commit(); - } catch(SQLException ex) { - label.setText(ex.toString()); - } - - // This must run outside the previous try{} catch{} segment - //try { - //db.setAutoCommit(true); - //} 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, using a Thread to do this in the - * background. - */ - 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 start the true importer - Thread t = new importer(db,name,dir); - //t.setPriority(Thread.MAX_PRIORITY); - t.start(); - } - - /** - * This is an example of using a thread to import a file into a Large Object. - * It uses the Large Object extension, to write blocks of the file to the - * database. - */ - class importer extends Thread - { - String name,dir; - Connection db; - - public importer(Connection db,String name,String dir) { - this.db = db; - this.name = name; - this.dir = dir; - } - - public void run() { - - // Now the real import stuff - if(name!=null && dir!=null) { - Statement stat = null; - - try { - // fetch the large object manager - LargeObjectManager lom = ((org.postgresql.Connection)db).getLargeObjectAPI(); - - db.setAutoCommit(false); - - // A temporary buffer - this can be as large as you like - byte buf[] = new byte[2048]; - - // Open the file - FileInputStream fis = new FileInputStream(new File(dir,name)); - - // Now create the large object - int oid = lom.create(); - 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. - int s,t=0; - while((s=fis.read(buf,0,buf.length))>0) { - t+=s; - blob.write(buf,0,s); - } - - // Close the object - blob.close(); - - // Now store the entry into the table - - // As we are a different thread to the other window, we must use - // our own thread - stat = db.createStatement(); - stat.executeUpdate("insert into images values ('"+name+"',"+oid+")"); - db.commit(); - db.setAutoCommit(false); - - // Finally refresh the names list, and display the current image - ImageViewer.this.refreshList(); - ImageViewer.this.displayImage(name); - } catch(Exception ex) { - label.setText(ex.toString()); - } finally { - // ensure the statement is closed after us - try { - if(stat != null) - stat.close(); - } catch(SQLException se) { - System.err.println("closing of Statement failed"); - } + 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 + { + // holds the image + private Image image; + + // holds the background buffer + private Image bkg; + + // the size of the buffer + private Dimension size; + + 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); + } + + /** + * Paints the image, using double buffering to prevent screen flicker + */ + public void paint(Graphics gr) + { + Dimension s = getSize(); + + if (size == null || bkg == null || !s.equals(size)) + { + size = s; + bkg = createImage(size.width, size.height); + } + + // now set the background + Graphics g = bkg.getGraphics(); + g.setColor(Color.gray); + g.fillRect(0, 0, s.width, s.height); + + // now paint the image over the background + if (image != null) + g.drawImage(image, 0, 0, this); + + // dispose the graphics instance + g.dispose(); + + // paint the image onto the component + gr.drawImage(bkg, 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("org.postgresql.Driver"); + + // Connect to database + db = DriverManager.getConnection(url, user, password); + + // Create a statement + stat = db.createStatement(); + + // Also, get the LargeObjectManager for this connection + lom = ((org.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 + { + //db.setAutoCommit(true); + stat.executeUpdate("create table images (imgname name,imgoid oid)"); + label.setText("Initialised database"); + db.commit(); + } + catch (SQLException ex) + { + label.setText(ex.toString()); + } + + // This must run outside the previous try{} catch{} segment + //try { + //db.setAutoCommit(true); + //} catch(SQLException 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 - // - // Note: We don't need to worry about being in a transaction - // here, because we are not opening any blobs, only deleting - // them - // - 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()) { - lom.delete(rs.getInt(1)); + + /** + * 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); } - } - 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 { - // - // Now as we are opening and reading a large object we must - // turn on Transactions. This includes the ResultSet.getBytes() - // method when it's used on a field of type oid! - // - db.setAutoCommit(false); - - 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()) { - canvas.setImage(canvas.getToolkit().createImage(rs.getBytes(1))); - label.setText(currentImage = name); + + /** + * This imports an image into the database, using a Thread to do this in the + * background. + */ + 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 start the true importer + Thread t = new importer(db, name, dir); + //t.setPriority(Thread.MAX_PRIORITY); + t.start(); } - } - rs.close(); - } catch(SQLException ex) { - label.setText(ex.toString()); - } finally { - try { - db.setAutoCommit(true); - } catch(SQLException ex2) { + + /** + * This is an example of using a thread to import a file into a Large Object. + * It uses the Large Object extension, to write blocks of the file to the + * database. + */ + class importer extends Thread + { + String name, dir; + Connection db; + + public importer(Connection db, String name, String dir) + { + this.db = db; + this.name = name; + this.dir = dir; + } + + public void run() + { + + // Now the real import stuff + if (name != null && dir != null) + { + Statement stat = null; + + try + { + // fetch the large object manager + LargeObjectManager lom = ((org.postgresql.Connection)db).getLargeObjectAPI(); + + db.setAutoCommit(false); + + // A temporary buffer - this can be as large as you like + byte buf[] = new byte[2048]; + + // Open the file + FileInputStream fis = new FileInputStream(new File(dir, name)); + + // Now create the large object + int oid = lom.create(); + 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. + int s, t = 0; + while ((s = fis.read(buf, 0, buf.length)) > 0) + { + t += s; + blob.write(buf, 0, s); + } + + // Close the object + blob.close(); + + // Now store the entry into the table + + // As we are a different thread to the other window, we must use + // our own thread + stat = db.createStatement(); + stat.executeUpdate("insert into images values ('" + name + "'," + oid + ")"); + db.commit(); + db.setAutoCommit(false); + + // Finally refresh the names list, and display the current image + ImageViewer.this.refreshList(); + ImageViewer.this.displayImage(name); + } + catch (Exception ex) + { + label.setText(ex.toString()); + } + finally + { + // ensure the statement is closed after us + try + { + if (stat != null) + stat.close(); + } + catch (SQLException se) + { + System.err.println("closing of Statement failed"); + } + } + } + } + } + + /** + * 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 + // + // Note: We don't need to worry about being in a transaction + // here, because we are not opening any blobs, only deleting + // them + // + 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()) + { + lom.delete(rs.getInt(1)); + } + } + 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 + { + // + // Now as we are opening and reading a large object we must + // turn on Transactions. This includes the ResultSet.getBytes() + // method when it's used on a field of type oid! + // + db.setAutoCommit(false); + + 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()) + { + canvas.setImage(canvas.getToolkit().createImage(rs.getBytes(1))); + label.setText(currentImage = name); + } + } + rs.close(); + } + catch (SQLException ex) + { + label.setText(ex.toString()); + } + finally + { + try + { + db.setAutoCommit(true); + } + catch (SQLException ex2) + {} + } + } + + 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=org.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 v7.0 rev 1"); + frame.setLayout(new BorderLayout()); + ImageViewer viewer = new ImageViewer(frame, args[0], args[1], args[2]); + frame.pack(); + frame.setLocation(0, 50); + frame.setVisible(true); + } + catch (Exception ex) + { + System.err.println("Exception caught.\n" + ex); + ex.printStackTrace(); + } } - } - } - - 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=org.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 v7.0 rev 1"); - frame.setLayout(new BorderLayout()); - ImageViewer viewer = new ImageViewer(frame,args[0],args[1],args[2]); - frame.pack(); - frame.setLocation(0,50); - frame.setVisible(true); - } catch(Exception ex) { - System.err.println("Exception caught.\n"+ex); - ex.printStackTrace(); - } - } } diff --git a/src/interfaces/jdbc/example/Unicode.java b/src/interfaces/jdbc/example/Unicode.java index 64d7ce8c89..f5e56522f5 100644 --- a/src/interfaces/jdbc/example/Unicode.java +++ b/src/interfaces/jdbc/example/Unicode.java @@ -5,236 +5,274 @@ import java.sql.*; import java.util.*; /** - * Test inserting and extracting Unicode-encoded strings. + * Test inserting and extracting Unicode-encoded strings. * - * Synopsis: - * example.Unicode - * where must specify an existing database to which and - * give access and which has UNICODE as its encoding. - * (To create a database with UNICODE encoding, you need to compile - * postgres with "--enable-multibyte" and run createdb with the - * flag "-E UNICODE".) + * Synopsis: + * example.Unicode + * where must specify an existing database to which and + * give access and which has UNICODE as its encoding. + * (To create a database with UNICODE encoding, you need to compile + * postgres with "--enable-multibyte" and run createdb with the + * flag "-E UNICODE".) * - * This test only produces output on error. + * This test only produces output on error. * - * @author William Webber + * @author William Webber */ -public class Unicode { - - /** - * The url for the database to connect to. - */ - private String url; - - /** - * The user to connect as. - */ - private String user; - - /** - * The password to connect with. - */ - private String password; - - private static void usage() { - log("usage: example.Unicode "); - } - - private static void log(String message) { - System.err.println(message); - } - - private static void log(String message, Exception e) { - System.err.println(message); - e.printStackTrace(); - } - - - public Unicode(String url, String user, String password) { - this.url = url; - this.user = user; - this.password = password; - } - - /** - * Establish and return a connection to the database. - */ - private Connection getConnection() throws SQLException, - ClassNotFoundException { - Class.forName("org.postgresql.Driver"); - Properties info = new Properties(); - info.put("user", user); - info.put("password", password); - info.put("charSet", "utf-8"); - return DriverManager.getConnection(url, info); - } - - /** - * Get string representing a block of 256 consecutive unicode characters. - * We exclude the null character, "'", and "\". - */ - private String getSqlSafeUnicodeBlock(int blockNum) { - if (blockNum < 0 || blockNum > 255) - throw new IllegalArgumentException("blockNum must be from 0 to " - + "255: " + blockNum); - StringBuffer sb = new StringBuffer(256); - int blockFirst = blockNum * 256; - int blockLast = blockFirst + 256; - for (int i = blockFirst; i < blockLast; i++) { - char c = (char) i; - if (c == '\0' || c == '\'' || c == '\\') - continue; - sb.append(c); - } - return sb.toString(); - } - - /** - * Is the block a block of valid unicode values. - * d800 to db7f is the "unassigned high surrogate" range. - * db80 to dbff is the "private use" range. - * These should not be used in actual Unicode strings; - * at least, jdk1.2 will not convert them to utf-8. - */ - private boolean isValidUnicodeBlock(int blockNum) { - if (blockNum >= 0xd8 && blockNum <= 0xdb) - return false; - else - return true; - } - - /** - * Report incorrect block retrieval. - */ - private void reportRetrievalError(int blockNum, String block, - String retrieved) { - String message = "Block " + blockNum + " returned incorrectly: "; - int i = 0; - for (i = 0; i < block.length(); i++) { - if (i >= retrieved.length()) { - message += "too short"; - break; - } else if (retrieved.charAt(i) != block.charAt(i)) { - message += - "first changed character at position " + i + ", sent as 0x" - + Integer.toHexString((int) block.charAt(i)) - + ", retrieved as 0x" - + Integer.toHexString ((int) retrieved.charAt(i)); - break; - } - } - if (i >= block.length()) - message += "too long"; - log(message); - } - - /** - * Do the testing. - */ - public void runTest() { - Connection connection = null; - Statement statement = null; - int blockNum = 0; - final int CREATE = 0; - final int INSERT = 1; - final int SELECT = 2; - final int LIKE = 3; - int mode = CREATE; - try { - connection = getConnection(); - statement = connection.createStatement(); - statement.executeUpdate("CREATE TABLE test_unicode " - + "( blockNum INT PRIMARY KEY, " - + "block TEXT );"); - mode = INSERT; - for (blockNum = 0; blockNum < 256; blockNum++) { - if (isValidUnicodeBlock(blockNum)) { - String block = getSqlSafeUnicodeBlock(blockNum); - statement.executeUpdate - ("INSERT INTO test_unicode VALUES ( " + blockNum - + ", '" + block + "');"); - } - } - mode = SELECT; - for (blockNum = 0; blockNum < 256; blockNum++) { - if (isValidUnicodeBlock(blockNum)) { - String block = getSqlSafeUnicodeBlock(blockNum); - ResultSet rs = statement.executeQuery - ("SELECT block FROM test_unicode WHERE blockNum = " - + blockNum + ";"); - if (!rs.next()) - log("Could not retrieve block " + blockNum); - else { - String retrieved = rs.getString(1); - if (!retrieved.equals(block)) { - reportRetrievalError(blockNum, block, retrieved); - } - } - } - } - mode = LIKE; - for (blockNum = 0; blockNum < 256; blockNum++) { - if (isValidUnicodeBlock(blockNum)) { - String block = getSqlSafeUnicodeBlock(blockNum); - String likeString = "%" + - block.substring(2, block.length() - 3) + "%" ; - ResultSet rs = statement.executeQuery - ("SELECT blockNum FROM test_unicode WHERE block LIKE '" - + likeString + "';"); - if (!rs.next()) - log("Could get block " + blockNum + " using LIKE"); - } - } - } catch (SQLException sqle) { - switch (mode) { - case CREATE: - log("Exception creating database", sqle); - break; - case INSERT: - log("Exception inserting block " + blockNum, sqle); - break; - case SELECT: - log("Exception selecting block " + blockNum, sqle); - break; - case LIKE: - log("Exception doing LIKE on block " + blockNum, sqle); - break; - default: - log("Exception", sqle); - break; - } - } catch (ClassNotFoundException cnfe) { - log("Unable to load driver", cnfe); - return; - } - try { - if (statement != null) - statement.close(); - if (connection != null) - connection.close(); - } catch (SQLException sqle) { - log("Exception closing connections", sqle); - } - if (mode > CREATE) { - // If the backend gets what it regards as garbage on a connection, - // that connection may become unusable. To be safe, we create - // a fresh connection to delete the table. - try { - connection = getConnection(); - statement = connection.createStatement(); - statement.executeUpdate("DROP TABLE test_unicode;"); - } catch (Exception sqle) { - log("*** ERROR: unable to delete test table " - + "test_unicode; must be deleted manually", sqle); - } - } - } - - public static void main(String [] args) { - if (args.length != 3) { - usage(); - System.exit(1); - } - new Unicode(args[0], args[1], args[2]).runTest(); - } +public class Unicode +{ + + /** + * The url for the database to connect to. + */ + private String url; + + /** + * The user to connect as. + */ + private String user; + + /** + * The password to connect with. + */ + private String password; + + private static void usage() + { + log("usage: example.Unicode "); + } + + private static void log(String message) + { + System.err.println(message); + } + + private static void log(String message, Exception e) + { + System.err.println(message); + e.printStackTrace(); + } + + + public Unicode(String url, String user, String password) + { + this.url = url; + this.user = user; + this.password = password; + } + + /** + * Establish and return a connection to the database. + */ + private Connection getConnection() throws SQLException, + ClassNotFoundException + { + Class.forName("org.postgresql.Driver"); + Properties info = new Properties(); + info.put("user", user); + info.put("password", password); + info.put("charSet", "utf-8"); + return DriverManager.getConnection(url, info); + } + + /** + * Get string representing a block of 256 consecutive unicode characters. + * We exclude the null character, "'", and "\". + */ + private String getSqlSafeUnicodeBlock(int blockNum) + { + if (blockNum < 0 || blockNum > 255) + throw new IllegalArgumentException("blockNum must be from 0 to " + + "255: " + blockNum); + StringBuffer sb = new StringBuffer(256); + int blockFirst = blockNum * 256; + int blockLast = blockFirst + 256; + for (int i = blockFirst; i < blockLast; i++) + { + char c = (char) i; + if (c == '\0' || c == '\'' || c == '\\') + continue; + sb.append(c); + } + return sb.toString(); + } + + /** + * Is the block a block of valid unicode values. + * d800 to db7f is the "unassigned high surrogate" range. + * db80 to dbff is the "private use" range. + * These should not be used in actual Unicode strings; + * at least, jdk1.2 will not convert them to utf-8. + */ + private boolean isValidUnicodeBlock(int blockNum) + { + if (blockNum >= 0xd8 && blockNum <= 0xdb) + return false; + else + return true; + } + + /** + * Report incorrect block retrieval. + */ + private void reportRetrievalError(int blockNum, String block, + String retrieved) + { + String message = "Block " + blockNum + " returned incorrectly: "; + int i = 0; + for (i = 0; i < block.length(); i++) + { + if (i >= retrieved.length()) + { + message += "too short"; + break; + } + else if (retrieved.charAt(i) != block.charAt(i)) + { + message += + "first changed character at position " + i + ", sent as 0x" + + Integer.toHexString((int) block.charAt(i)) + + ", retrieved as 0x" + + Integer.toHexString ((int) retrieved.charAt(i)); + break; + } + } + if (i >= block.length()) + message += "too long"; + log(message); + } + + /** + * Do the testing. + */ + public void runTest() + { + Connection connection = null; + Statement statement = null; + int blockNum = 0; + final int CREATE = 0; + final int INSERT = 1; + final int SELECT = 2; + final int LIKE = 3; + int mode = CREATE; + try + { + connection = getConnection(); + statement = connection.createStatement(); + statement.executeUpdate("CREATE TABLE test_unicode " + + "( blockNum INT PRIMARY KEY, " + + "block TEXT );"); + mode = INSERT; + for (blockNum = 0; blockNum < 256; blockNum++) + { + if (isValidUnicodeBlock(blockNum)) + { + String block = getSqlSafeUnicodeBlock(blockNum); + statement.executeUpdate + ("INSERT INTO test_unicode VALUES ( " + blockNum + + ", '" + block + "');"); + } + } + mode = SELECT; + for (blockNum = 0; blockNum < 256; blockNum++) + { + if (isValidUnicodeBlock(blockNum)) + { + String block = getSqlSafeUnicodeBlock(blockNum); + ResultSet rs = statement.executeQuery + ("SELECT block FROM test_unicode WHERE blockNum = " + + blockNum + ";"); + if (!rs.next()) + log("Could not retrieve block " + blockNum); + else + { + String retrieved = rs.getString(1); + if (!retrieved.equals(block)) + { + reportRetrievalError(blockNum, block, retrieved); + } + } + } + } + mode = LIKE; + for (blockNum = 0; blockNum < 256; blockNum++) + { + if (isValidUnicodeBlock(blockNum)) + { + String block = getSqlSafeUnicodeBlock(blockNum); + String likeString = "%" + + block.substring(2, block.length() - 3) + "%" ; + ResultSet rs = statement.executeQuery + ("SELECT blockNum FROM test_unicode WHERE block LIKE '" + + likeString + "';"); + if (!rs.next()) + log("Could get block " + blockNum + " using LIKE"); + } + } + } + catch (SQLException sqle) + { + switch (mode) + { + case CREATE: + log("Exception creating database", sqle); + break; + case INSERT: + log("Exception inserting block " + blockNum, sqle); + break; + case SELECT: + log("Exception selecting block " + blockNum, sqle); + break; + case LIKE: + log("Exception doing LIKE on block " + blockNum, sqle); + break; + default: + log("Exception", sqle); + break; + } + } + catch (ClassNotFoundException cnfe) + { + log("Unable to load driver", cnfe); + return ; + } + try + { + if (statement != null) + statement.close(); + if (connection != null) + connection.close(); + } + catch (SQLException sqle) + { + log("Exception closing connections", sqle); + } + if (mode > CREATE) + { + // If the backend gets what it regards as garbage on a connection, + // that connection may become unusable. To be safe, we create + // a fresh connection to delete the table. + try + { + connection = getConnection(); + statement = connection.createStatement(); + statement.executeUpdate("DROP TABLE test_unicode;"); + } + catch (Exception sqle) + { + log("*** ERROR: unable to delete test table " + + "test_unicode; must be deleted manually", sqle); + } + } + } + + public static void main(String [] args) + { + if (args.length != 3) + { + usage(); + System.exit(1); + } + new Unicode(args[0], args[1], args[2]).runTest(); + } } diff --git a/src/interfaces/jdbc/example/basic.java b/src/interfaces/jdbc/example/basic.java index 2c989b4c89..8dd7d7baf7 100644 --- a/src/interfaces/jdbc/example/basic.java +++ b/src/interfaces/jdbc/example/basic.java @@ -6,7 +6,7 @@ import java.text.*; /** * - * $Id: basic.java,v 1.7 2001/01/31 09:23:45 peter Exp $ + * $Id: basic.java,v 1.8 2001/10/25 05:59:58 momjian Exp $ * * This example tests the basic components of the JDBC driver, and shows * how even the simplest of queries can be implemented. @@ -20,184 +20,198 @@ import java.text.*; 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("org.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(); - - //throw postgresql.Driver.notImplemented(); - } - - /** - * 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)"); - - // This shows how to get the oid of a just inserted row - // updated for 7.1 - st.executeUpdate("insert into basic values (4,1)"); - int insertedOID = ((org.postgresql.Statement)st).getInsertedOID(); - System.out.println("Inserted row with oid "+insertedOID); - - // Now change the value of b from 1 to 8 - st.executeUpdate("update basic set b=8"); - System.out.println("Updated "+st.getUpdateCount()+" rows"); - - // Now delete 2 rows - st.executeUpdate("delete from basic where a<3"); - System.out.println("deleted "+st.getUpdateCount()+" rows"); - - // 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 - } - - // Now test maxrows by setting it to 3 rows - st.setMaxRows(3); - System.out.println("performing a query limited to "+st.getMaxRows()); - rs = st.executeQuery("select a, b from basic"); - 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 - - // 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(); - } - } + 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("org.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(); + + //throw postgresql.Driver.notImplemented(); + } + + /** + * 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)"); + + // This shows how to get the oid of a just inserted row + // updated for 7.1 + st.executeUpdate("insert into basic values (4,1)"); + int insertedOID = ((org.postgresql.Statement)st).getInsertedOID(); + System.out.println("Inserted row with oid " + insertedOID); + + // Now change the value of b from 1 to 8 + st.executeUpdate("update basic set b=8"); + System.out.println("Updated " + st.getUpdateCount() + " rows"); + + // Now delete 2 rows + st.executeUpdate("delete from basic where a<3"); + System.out.println("deleted " + st.getUpdateCount() + " rows"); + + // 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 + } + + // Now test maxrows by setting it to 3 rows + + st.setMaxRows(3); + System.out.println("performing a query limited to " + st.getMaxRows()); + rs = st.executeQuery("select a, b from basic"); + 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 + + // 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(); + } + } } diff --git a/src/interfaces/jdbc/example/blobtest.java b/src/interfaces/jdbc/example/blobtest.java index e08ff5b6fe..654eaf92ac 100644 --- a/src/interfaces/jdbc/example/blobtest.java +++ b/src/interfaces/jdbc/example/blobtest.java @@ -24,224 +24,236 @@ import org.postgresql.largeobject.*; 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("org.postgresql.Driver"); - - // Connect to database - System.out.println("Connecting to Database URL = " + url); - db = DriverManager.getConnection(url, usr, pwd); - - // This is required for all LargeObject calls - System.out.println("Connected... First turn off autoCommit()"); - db.setAutoCommit(false); - - System.out.println("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----------------------------------------------------------------------\n"); - - // Internally, the driver provides JDBC compliant methods to access large - // objects, however the unique methods available to postgresql makes - // things a little easier. - System.out.println("Gaining access to large object api"); - lobj = ((org.postgresql.Connection)db).getLargeObjectAPI(); - - int oid = ownapi_test1(); - ownapi_test2(oid); - - // Now call the jdbc2api test - jdbc2api(oid); - - // finally delete the large object - 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 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); + } + + //======================================================================= + // This tests the Blob interface of the JDBC 2.0 specification + public void jdbc2api(int oid) throws SQLException, IOException + { + System.out.println("Testing JDBC2 Blob interface:"); + jdbc2api_cleanup(); + + System.out.println("Creating Blob on large object " + oid); + s.executeUpdate("create table basic (a oid)"); + + System.out.println("Inserting row"); + s.executeUpdate("insert into basic values (" + oid + ")"); + + System.out.println("Selecting row"); + ResultSet rs = s.executeQuery("select a from basic"); + if (rs != null) + { + while (rs.next()) + { + System.out.println("Fetching Blob"); + Blob b = rs.getBlob("a"); + System.out.println("Blob.length() = " + b.length()); + System.out.println("Characters 400-500:"); + System.out.write(b.getBytes(400l, 100)); + System.out.println(); + } + rs.close(); + } + + System.out.println("Cleaning up"); + jdbc2api_cleanup(); + } + + private void jdbc2api_cleanup() throws SQLException + { + db.setAutoCommit(true); + try + { + s.executeUpdate("drop table basic"); + } + catch (Exception ex) + { + // We ignore any errors here + } + db.setAutoCommit(false); + } + + //======================================================================= + + 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=org.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=org.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."); } - - System.out.println("Cleaning up"); - jdbc2api_cleanup(); - } - - private void jdbc2api_cleanup() throws SQLException - { - db.setAutoCommit(true); - try { - s.executeUpdate("drop table basic"); - } catch(Exception ex) { - // We ignore any errors here + + public static void main(String args[]) + { + System.out.println("PostgreSQL blobtest v7.0 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(); + } } - db.setAutoCommit(false); - } - - //======================================================================= - - 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=org.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=org.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 v7.0 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(); - } - } } diff --git a/src/interfaces/jdbc/example/corba/StockClient.java b/src/interfaces/jdbc/example/corba/StockClient.java index 7613f886fc..0a357b15ca 100644 --- a/src/interfaces/jdbc/example/corba/StockClient.java +++ b/src/interfaces/jdbc/example/corba/StockClient.java @@ -9,280 +9,340 @@ import org.omg.CosNaming.*; * * It has no GUI, just a text frontend to keep it simple. * - * $Id: StockClient.java,v 1.1 1999/01/25 21:22:03 scrappy Exp $ + * $Id: StockClient.java,v 1.2 2001/10/25 05:59:58 momjian Exp $ */ public class StockClient { - org.omg.CosNaming.NamingContext nameService; - - stock.StockDispenser dispenser; - stock.StockItem item; - - BufferedReader in; - - public StockClient(String[] args) { - try { - // We need this for our IO - in = new BufferedReader(new InputStreamReader(System.in)); - - // Initialize the orb - org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null); - - // Get a reference to the Naming Service - org.omg.CORBA.Object nameServiceObj = orb.resolve_initial_references("NameService"); - if(nameServiceObj==null) { - System.err.println("nameServiceObj == null"); - return; - } - - nameService = org.omg.CosNaming.NamingContextHelper.narrow(nameServiceObj); - if(nameService==null) { - System.err.println("nameService == null"); - return; - } - - // Resolve the dispenser - NameComponent[] dispName = { - new NameComponent("StockDispenser","Stock") - }; - dispenser = stock.StockDispenserHelper.narrow(nameService.resolve(dispName)); - if(dispenser==null) { - System.err.println("dispenser == null"); - return; - } - - // Now run the front end. - run(); - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); - System.exit(1); + org.omg.CosNaming.NamingContext nameService; + + stock.StockDispenser dispenser; + stock.StockItem item; + + BufferedReader in; + + public StockClient(String[] args) + { + try + { + // We need this for our IO + in = new BufferedReader(new InputStreamReader(System.in)); + + // Initialize the orb + org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null); + + // Get a reference to the Naming Service + org.omg.CORBA.Object nameServiceObj = orb.resolve_initial_references("NameService"); + if (nameServiceObj == null) + { + System.err.println("nameServiceObj == null"); + return ; + } + + nameService = org.omg.CosNaming.NamingContextHelper.narrow(nameServiceObj); + if (nameService == null) + { + System.err.println("nameService == null"); + return ; + } + + // Resolve the dispenser + NameComponent[] dispName = { + new NameComponent("StockDispenser", "Stock") + }; + dispenser = stock.StockDispenserHelper.narrow(nameService.resolve(dispName)); + if (dispenser == null) + { + System.err.println("dispenser == null"); + return ; + } + + // Now run the front end. + run(); + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + System.exit(1); + } } - } - - public static void main(String[] args) { - new StockClient(args); - } - - public void run() { - // First reserve a StockItem - try { - item = dispenser.reserveItem(); - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); - System.exit(1); + + public static void main(String[] args) + { + new StockClient(args); } - - mainMenu(); - - // finally free the StockItem - try { - dispenser.releaseItem(item); - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); - System.exit(1); + + public void run() + { + // First reserve a StockItem + try + { + item = dispenser.reserveItem(); + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + System.exit(1); + } + + mainMenu(); + + // finally free the StockItem + try + { + dispenser.releaseItem(item); + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + System.exit(1); + } } - } - - private void mainMenu() { - boolean run=true; - while(run) { - System.out.println("\nCORBA Stock System\n"); - System.out.println(" 1 Display stock item"); - System.out.println(" 2 Remove item from stock"); - System.out.println(" 3 Put item into stock"); - System.out.println(" 4 Order item"); - System.out.println(" 5 Display all items"); - System.out.println(" 0 Exit"); - int i = getMenu("Main",5); - switch(i) + + private void mainMenu() + { + boolean run = true; + while (run) { - case 0: - run=false; - break; - - case 1: - displayItem(); - break; - - case 2: - bookOut(); - break; - - case 3: - bookIn(); - break; - - case 4: - order(0); - break; - - case 5: - displayAll(); - break; + System.out.println("\nCORBA Stock System\n"); + System.out.println(" 1 Display stock item"); + System.out.println(" 2 Remove item from stock"); + System.out.println(" 3 Put item into stock"); + System.out.println(" 4 Order item"); + System.out.println(" 5 Display all items"); + System.out.println(" 0 Exit"); + int i = getMenu("Main", 5); + switch (i) + { + case 0: + run = false; + break; + + case 1: + displayItem(); + break; + + case 2: + bookOut(); + break; + + case 3: + bookIn(); + break; + + case 4: + order(0); + break; + + case 5: + displayAll(); + break; + } } } - } - - private void displayItem() { - try { - int id = getMenu("\nStockID to display",item.getLastID()); - if(id>0) { - item.fetchItem(id); - System.out.println("========================================"); - - String status = ""; - if(!item.isItemValid()) - status=" ** Superceded **"; - - int av = item.getAvailable(); - - System.out.println(" Stock ID: "+id+status+ - "\nItems Available: "+av+ - "\nItems on order: "+item.getOrdered()+ - "\n Description: "+item.getDescription()); - System.out.println("========================================"); - - if(av>0) - if(yn("Take this item out of stock?")) { - int rem=1; - if(av>1) - rem=getMenu("How many?",av); - if(rem>0) - item.removeStock(rem); - } - - } - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); + + private void displayItem() + { + try + { + int id = getMenu("\nStockID to display", item.getLastID()); + if (id > 0) + { + item.fetchItem(id); + System.out.println("========================================"); + + String status = ""; + if (!item.isItemValid()) + status = " ** Superceded **"; + + int av = item.getAvailable(); + + System.out.println(" Stock ID: " + id + status + + "\nItems Available: " + av + + "\nItems on order: " + item.getOrdered() + + "\n Description: " + item.getDescription()); + System.out.println("========================================"); + + if (av > 0) + if (yn("Take this item out of stock?")) + { + int rem = 1; + if (av > 1) + rem = getMenu("How many?", av); + if (rem > 0) + item.removeStock(rem); + } + + } + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + } } - } - - private void bookOut() { - try { - int id = getMenu("\nStockID to take out",item.getLastID()); - if(id>0) { - item.fetchItem(id); - int av = item.getAvailable(); - if(av>0) - if(yn("Take this item out of stock?")) { - int rem=1; - if(av>1) - rem=getMenu("How many?",av); - if(rem>0) - item.removeStock(rem); - } - else { - System.out.println("This item is not in stock."); - int order = item.getOrdered(); - if(order>0) - System.out.println("There are "+item.getOrdered()+" items on order."); - else { - if(item.isItemValid()) { - System.out.println("You will need to order some more "+item.getDescription()); - order(id); - } else - System.out.println("This item is now obsolete"); + + private void bookOut() + { + try + { + int id = getMenu("\nStockID to take out", item.getLastID()); + if (id > 0) + { + item.fetchItem(id); + int av = item.getAvailable(); + if (av > 0) + if (yn("Take this item out of stock?")) + { + int rem = 1; + if (av > 1) + rem = getMenu("How many?", av); + if (rem > 0) + item.removeStock(rem); + } + else + { + System.out.println("This item is not in stock."); + int order = item.getOrdered(); + if (order > 0) + System.out.println("There are " + item.getOrdered() + " items on order."); + else + { + if (item.isItemValid()) + { + System.out.println("You will need to order some more " + item.getDescription()); + order(id); + } + else + System.out.println("This item is now obsolete"); + } + } } - } - } else - System.out.println(item.getDescription()+"\nThis item is out of stock"); - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); + else + System.out.println(item.getDescription() + "\nThis item is out of stock"); + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + } } - } - - // book an item into stock - private void bookIn() { - try { - int id = getMenu("\nStockID to book in",item.getLastID()); - item.fetchItem(id); - System.out.println(item.getDescription()); - - if(item.getOrdered()>0) { - int am = getMenu("How many do you want to book in",item.getOrdered()); - if(am>0) - item.addNewStock(am); - } else - System.out.println("You don't have any of this item on ordered"); - - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); + + // book an item into stock + private void bookIn() + { + try + { + int id = getMenu("\nStockID to book in", item.getLastID()); + item.fetchItem(id); + System.out.println(item.getDescription()); + + if (item.getOrdered() > 0) + { + int am = getMenu("How many do you want to book in", item.getOrdered()); + if (am > 0) + item.addNewStock(am); + } + else + System.out.println("You don't have any of this item on ordered"); + + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + } } - } - - // Order an item - private void order(int id) { - try { - if(id==0) - id = getMenu("\nStockID to order",item.getLastID()); - item.fetchItem(id); - System.out.println(item.getDescription()); - int am = getMenu("How many do you want to order",999); - if(am>0) - item.orderStock(am); - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); + + // Order an item + private void order(int id) + { + try + { + if (id == 0) + id = getMenu("\nStockID to order", item.getLastID()); + item.fetchItem(id); + System.out.println(item.getDescription()); + int am = getMenu("How many do you want to order", 999); + if (am > 0) + item.orderStock(am); + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); + } } - } - - private void displayAll() { - try { - boolean cont=true; - int nr=item.getLastID(); - String header = "\nId\tAvail\tOrdered\tDescription"; - System.out.println(header); - for(int i=1;i<=nr && cont;i++) { - item.fetchItem(i); - System.out.println(""+i+"\t"+item.getAvailable()+"\t"+item.getOrdered()+"\t"+item.getDescription()); - if((i%20)==0) { - if((cont=yn("Continue?"))) + + private void displayAll() + { + try + { + boolean cont = true; + int nr = item.getLastID(); + String header = "\nId\tAvail\tOrdered\tDescription"; System.out.println(header); + for (int i = 1;i <= nr && cont;i++) + { + item.fetchItem(i); + System.out.println("" + i + "\t" + item.getAvailable() + "\t" + item.getOrdered() + "\t" + item.getDescription()); + if ((i % 20) == 0) + { + if ((cont = yn("Continue?"))) + System.out.println(header); + } + } + } + catch (Exception e) + { + System.out.println(e.toString()); + e.printStackTrace(); } - } - } catch(Exception e) { - System.out.println(e.toString()); - e.printStackTrace(); } - } - - private int getMenu(String title,int max) { - int v=-1; - while(v<0 || v>max) { - System.out.print(title); - System.out.print(" [0-"+max+"]: "); - System.out.flush(); - try { - v = Integer.parseInt(in.readLine()); - } catch(Exception nfe) { - v=-1; - } + + private int getMenu(String title, int max) + { + int v = -1; + while (v < 0 || v > max) + { + System.out.print(title); + System.out.print(" [0-" + max + "]: "); + System.out.flush(); + try + { + v = Integer.parseInt(in.readLine()); + } + catch (Exception nfe) + { + v = -1; + } + } + return v; } - return v; - } - - private boolean yn(String title) { - try { - while(true) { - System.out.print(title); - System.out.flush(); - String s = in.readLine(); - if(s.startsWith("y") || s.startsWith("Y")) - return true; - if(s.startsWith("n") || s.startsWith("N")) - return false; - } - } catch(Exception nfe) { - System.out.println(nfe.toString()); - nfe.printStackTrace(); - System.exit(1); + + private boolean yn(String title) + { + try + { + while (true) + { + System.out.print(title); + System.out.flush(); + String s = in.readLine(); + if (s.startsWith("y") || s.startsWith("Y")) + return true; + if (s.startsWith("n") || s.startsWith("N")) + return false; + } + } + catch (Exception nfe) + { + System.out.println(nfe.toString()); + nfe.printStackTrace(); + System.exit(1); + } + return false; } - return false; - } } diff --git a/src/interfaces/jdbc/example/corba/StockDB.java b/src/interfaces/jdbc/example/corba/StockDB.java index f2ef2463c8..88797902dd 100644 --- a/src/interfaces/jdbc/example/corba/StockDB.java +++ b/src/interfaces/jdbc/example/corba/StockDB.java @@ -13,105 +13,122 @@ import java.sql.*; * that an object could be changed by another client, and we need to ensure that * the returned data is live and accurate. * - * $Id: StockDB.java,v 1.2 2000/04/26 05:32:01 peter Exp $ + * $Id: StockDB.java,v 1.3 2001/10/25 05:59:58 momjian Exp $ */ public class StockDB { - Connection con; - Statement st; - - // the current stock number - int id = -1; - - public void connect(String url,String usr,String pwd) throws Exception { - Class.forName("org.postgresql.Driver"); - System.out.println("Connecting to "+url); - con = DriverManager.getConnection(url,usr,pwd); - st = con.createStatement(); - } - - public void closeConnection() throws Exception { - con.close(); - } - - public void fetchItem(int id) throws Exception { - this.id = id; - } - - public int newItem() throws Exception { - // tba - return -1; - } - - public String getDescription() throws SQLException { - ResultSet rs = st.executeQuery("select description from stock where id="+id); - if(rs!=null) { - rs.next(); - String s = rs.getString(1); - rs.close(); - return s; + Connection con; + Statement st; + + // the current stock number + int id = -1; + + public void connect(String url, String usr, String pwd) throws Exception + { + Class.forName("org.postgresql.Driver"); + System.out.println("Connecting to " + url); + con = DriverManager.getConnection(url, usr, pwd); + st = con.createStatement(); } - throw new SQLException("No ResultSet"); - } - - public int getAvailable() throws SQLException { - ResultSet rs = st.executeQuery("select avail from stock where id="+id); - if(rs!=null) { - rs.next(); - int v = rs.getInt(1); - rs.close(); - return v; + + public void closeConnection() throws Exception + { + con.close(); } - throw new SQLException("No ResultSet"); - } - - public int getOrdered() throws SQLException { - ResultSet rs = st.executeQuery("select ordered from stock where id="+id); - if(rs!=null) { - rs.next(); - int v = rs.getInt(1); - rs.close(); - return v; + + public void fetchItem(int id) throws Exception + { + this.id = id; } - throw new SQLException("No ResultSet"); - } - - public boolean isItemValid() throws SQLException { - ResultSet rs = st.executeQuery("select valid from stock where id="+id); - if(rs!=null) { - rs.next(); - boolean b = rs.getBoolean(1); - rs.close(); - return b; + + public int newItem() throws Exception + { + // tba + return -1; } - throw new SQLException("No ResultSet"); - } - - public void addNewStock(int amount) throws SQLException { - st.executeUpdate("update stock set avail=avail+"+amount+ - ", ordered=ordered-"+amount+ - " where id="+id+" and ordered>="+amount); - } - - public void removeStock(int amount) throws SQLException { - st.executeUpdate("update stock set avail=avail-"+amount+ - " where id="+id); - } - - public void orderStock(int amount) throws SQLException { - st.executeUpdate("update stock set ordered=ordered+"+amount+ - " where id="+id); - } - - public int getLastID() throws SQLException { - ResultSet rs = st.executeQuery("select max(id) from stock"); - if(rs!=null) { - rs.next(); - int v = rs.getInt(1); - rs.close(); - return v; + + public String getDescription() throws SQLException + { + ResultSet rs = st.executeQuery("select description from stock where id=" + id); + if (rs != null) + { + rs.next(); + String s = rs.getString(1); + rs.close(); + return s; + } + throw new SQLException("No ResultSet"); } - throw new SQLException("No ResultSet"); - } - + + public int getAvailable() throws SQLException + { + ResultSet rs = st.executeQuery("select avail from stock where id=" + id); + if (rs != null) + { + rs.next(); + int v = rs.getInt(1); + rs.close(); + return v; + } + throw new SQLException("No ResultSet"); + } + + public int getOrdered() throws SQLException + { + ResultSet rs = st.executeQuery("select ordered from stock where id=" + id); + if (rs != null) + { + rs.next(); + int v = rs.getInt(1); + rs.close(); + return v; + } + throw new SQLException("No ResultSet"); + } + + public boolean isItemValid() throws SQLException + { + ResultSet rs = st.executeQuery("select valid from stock where id=" + id); + if (rs != null) + { + rs.next(); + boolean b = rs.getBoolean(1); + rs.close(); + return b; + } + throw new SQLException("No ResultSet"); + } + + public void addNewStock(int amount) throws SQLException + { + st.executeUpdate("update stock set avail=avail+" + amount + + ", ordered=ordered-" + amount + + " where id=" + id + " and ordered>=" + amount); + } + + public void removeStock(int amount) throws SQLException + { + st.executeUpdate("update stock set avail=avail-" + amount + + " where id=" + id); + } + + public void orderStock(int amount) throws SQLException + { + st.executeUpdate("update stock set ordered=ordered+" + amount + + " where id=" + id); + } + + public int getLastID() throws SQLException + { + ResultSet rs = st.executeQuery("select max(id) from stock"); + if (rs != null) + { + rs.next(); + int v = rs.getInt(1); + rs.close(); + return v; + } + throw new SQLException("No ResultSet"); + } + } diff --git a/src/interfaces/jdbc/example/corba/StockDispenserImpl.java b/src/interfaces/jdbc/example/corba/StockDispenserImpl.java index 2d961a6dc7..9572d20ae7 100644 --- a/src/interfaces/jdbc/example/corba/StockDispenserImpl.java +++ b/src/interfaces/jdbc/example/corba/StockDispenserImpl.java @@ -5,79 +5,88 @@ import org.omg.CosNaming.*; /** * This class implements the server side of the example. * - * $Id: StockDispenserImpl.java,v 1.1 1999/01/25 21:22:03 scrappy Exp $ + * $Id: StockDispenserImpl.java,v 1.2 2001/10/25 05:59:58 momjian Exp $ */ public class StockDispenserImpl extends stock._StockDispenserImplBase { - private int maxObjects = 10; - private int numObjects = 0; - private StockItemStatus[] stock = new StockItemStatus[maxObjects]; - - public StockDispenserImpl(String[] args,String name,int num) - { - super(); - - try { - // get reference to orb - org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null); - - // prestart num objects - if(num>=maxObjects) - num=maxObjects; - numObjects = num; - for(int i=0;i= maxObjects) + num = maxObjects; + numObjects = num; + for (int i = 0;i < numObjects;i++) + { + stock[i] = new StockItemStatus(); + stock[i].ref = new StockItemImpl(args, "StockItem" + (i + 1)); + orb.connect(stock[i].ref); + } + } + catch (org.omg.CORBA.SystemException e) + { + e.printStackTrace(); + } } - } - - /** - * This method, defined in stock.idl, reserves a slot in the dispenser - */ - public stock.StockItem reserveItem() throws stock.StockException - { - for(int i=0;i3) - 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(); - } - } + 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("org.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(); + } + } } diff --git a/src/interfaces/jdbc/example/metadata.java b/src/interfaces/jdbc/example/metadata.java index 8d67412273..182e4533e0 100644 --- a/src/interfaces/jdbc/example/metadata.java +++ b/src/interfaces/jdbc/example/metadata.java @@ -15,260 +15,284 @@ import java.text.*; public class metadata { - Connection db; // The connection to the database - Statement st; // Our statement to run queries with - DatabaseMetaData dbmd; // This defines the structure of the database - - /** - * These are the available tests on DatabaseMetaData - */ - public void doDatabaseMetaData() throws SQLException { - if(doTest("getProcedures() - should show all available procedures")) - displayResult(dbmd.getProcedures(null,null,null)); - - if(doTest("getProcedures() with pattern - should show all circle procedures")) - displayResult(dbmd.getProcedures(null,null,"circle%")); - - if(doTest("getProcedureColumns() on circle procedures")) - displayResult(dbmd.getProcedureColumns(null,null,"circle%",null)); - - if(doTest("getTables()")) - displayResult(dbmd.getTables(null,null,null,null)); - - if(doTest("getColumns() - should show all tables, can take a while to run")) - displayResult(dbmd.getColumns(null,null,null,null)); - - if(doTest("getColumns() - should show the test_b table")) - displayResult(dbmd.getColumns(null,null,"test_b",null)); - - if(doTest("getColumnPrivileges() - should show all tables")) - displayResult(dbmd.getColumnPrivileges(null,null,null,null)); - - if(doTest("getPrimaryKeys()")) - displayResult(dbmd.getPrimaryKeys(null,null,null)); - - if(doTest("getTypeInfo()")) - displayResult(dbmd.getTypeInfo()); - - } - - /** - * These are the available tests on ResultSetMetaData - */ - public void doResultSetMetaData() throws SQLException { - - String sql = "select imagename,descr,source,cost from test_a,test_b,test_c where test_a.id=test_b.imageid and test_a.id=test_c.imageid"; - - System.out.println("Executing query for tests"); - ResultSet rs = st.executeQuery(sql); - ResultSetMetaData rsmd = rs.getMetaData(); - - if(doTest("isCurrency()")) - System.out.println("isCurrency on col 1 = "+rsmd.isCurrency(1)+" should be false\nisCurrency on col 4 = "+rsmd.isCurrency(4)+" should be true"); - - // Finally close the query. Now give the user a chance to display the - // ResultSet. - // - // NB: displayResult() actually closes the ResultSet. - if(doTest("Display query result")) { - System.out.println("Query: "+sql); - displayResult(rs); - } else - rs.close(); - } - - /** - * This creates some test data - */ - public void init() throws SQLException { - System.out.println("Creating some tables"); - cleanup(); - st.executeUpdate("create table test_a (imagename name,image oid,id int4)"); - st.executeUpdate("create table test_b (descr text,imageid int4,id int4)"); - st.executeUpdate("create table test_c (source text,cost money,imageid int4)"); - - System.out.println("Adding some data"); - st.executeUpdate("insert into test_a values ('test1',0,1)"); - st.executeUpdate("insert into test_b values ('A test description',1,2)"); - st.executeUpdate("insert into test_c values ('nowhere particular','$10.99',1)"); - } - - /** - * This removes the test data - */ - public void cleanup() throws SQLException { - try { - st.executeUpdate("drop table test_a"); - st.executeUpdate("drop table test_b"); - st.executeUpdate("drop table test_c"); - } catch(Exception ex) { - // We ignore any errors here - } - } - - public metadata(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException - { - String url = args[0]; - String usr = args[1]; - String pwd = args[2]; - - // Load the driver - Class.forName("org.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()); - - init(); - - System.out.println(); - - // Now the tests - if(doTest("Test DatabaseMetaData")) - doDatabaseMetaData(); - - if(doTest("Test ResultSetMetaData")) - doResultSetMetaData(); - - System.out.println("\nNow closing the connection"); - st.close(); - db.close(); - - cleanup(); - } - - /** - * This asks if the user requires to run a test. - */ - public boolean doTest(String s) { - System.out.println(); - System.out.print(s); - System.out.print(" Perform test? Y or N:"); - System.out.flush(); - char c = ' '; - try { - while(!(c=='n' || c=='y' || c=='N' || c=='Y')) { - c=(char)System.in.read(); - } - } catch(IOException ioe) { - return false; - } - - return c=='y' || c=='Y'; - } - - /** - * This displays a result set. - * Note: it closes the result once complete. - */ - public void displayResult(ResultSet rs) throws SQLException - { - ResultSetMetaData rsmd = rs.getMetaData(); - int count=0; - - // Print the result column names - int cols = rsmd.getColumnCount(); - for(int i=1;i<=cols;i++) - System.out.print(rsmd.getColumnLabel(i)+(i3) - DriverManager.setLogStream(System.err); - - // Now run the tests - try { - metadata test = new metadata(args); - } catch(Exception ex) { - System.err.println("Exception caught.\n"+ex); - ex.printStackTrace(); - } - } + Connection db; // The connection to the database + Statement st; // Our statement to run queries with + DatabaseMetaData dbmd; // This defines the structure of the database + + /** + * These are the available tests on DatabaseMetaData + */ + public void doDatabaseMetaData() throws SQLException + { + if (doTest("getProcedures() - should show all available procedures")) + displayResult(dbmd.getProcedures(null, null, null)); + + if (doTest("getProcedures() with pattern - should show all circle procedures")) + displayResult(dbmd.getProcedures(null, null, "circle%")); + + if (doTest("getProcedureColumns() on circle procedures")) + displayResult(dbmd.getProcedureColumns(null, null, "circle%", null)); + + if (doTest("getTables()")) + displayResult(dbmd.getTables(null, null, null, null)); + + if (doTest("getColumns() - should show all tables, can take a while to run")) + displayResult(dbmd.getColumns(null, null, null, null)); + + if (doTest("getColumns() - should show the test_b table")) + displayResult(dbmd.getColumns(null, null, "test_b", null)); + + if (doTest("getColumnPrivileges() - should show all tables")) + displayResult(dbmd.getColumnPrivileges(null, null, null, null)); + + if (doTest("getPrimaryKeys()")) + displayResult(dbmd.getPrimaryKeys(null, null, null)); + + if (doTest("getTypeInfo()")) + displayResult(dbmd.getTypeInfo()); + + } + + /** + * These are the available tests on ResultSetMetaData + */ + public void doResultSetMetaData() throws SQLException + { + + String sql = "select imagename,descr,source,cost from test_a,test_b,test_c where test_a.id=test_b.imageid and test_a.id=test_c.imageid"; + + System.out.println("Executing query for tests"); + ResultSet rs = st.executeQuery(sql); + ResultSetMetaData rsmd = rs.getMetaData(); + + if (doTest("isCurrency()")) + System.out.println("isCurrency on col 1 = " + rsmd.isCurrency(1) + " should be false\nisCurrency on col 4 = " + rsmd.isCurrency(4) + " should be true"); + + // Finally close the query. Now give the user a chance to display the + // ResultSet. + // + // NB: displayResult() actually closes the ResultSet. + if (doTest("Display query result")) + { + System.out.println("Query: " + sql); + displayResult(rs); + } + else + rs.close(); + } + + /** + * This creates some test data + */ + public void init() throws SQLException + { + System.out.println("Creating some tables"); + cleanup(); + st.executeUpdate("create table test_a (imagename name,image oid,id int4)"); + st.executeUpdate("create table test_b (descr text,imageid int4,id int4)"); + st.executeUpdate("create table test_c (source text,cost money,imageid int4)"); + + System.out.println("Adding some data"); + st.executeUpdate("insert into test_a values ('test1',0,1)"); + st.executeUpdate("insert into test_b values ('A test description',1,2)"); + st.executeUpdate("insert into test_c values ('nowhere particular','$10.99',1)"); + } + + /** + * This removes the test data + */ + public void cleanup() throws SQLException + { + try + { + st.executeUpdate("drop table test_a"); + st.executeUpdate("drop table test_b"); + st.executeUpdate("drop table test_c"); + } + catch (Exception ex) + { + // We ignore any errors here + } + } + + public metadata(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException + { + String url = args[0]; + String usr = args[1]; + String pwd = args[2]; + + // Load the driver + Class.forName("org.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()); + + init(); + + System.out.println(); + + // Now the tests + if (doTest("Test DatabaseMetaData")) + doDatabaseMetaData(); + + if (doTest("Test ResultSetMetaData")) + doResultSetMetaData(); + + System.out.println("\nNow closing the connection"); + st.close(); + db.close(); + + cleanup(); + } + + /** + * This asks if the user requires to run a test. + */ + public boolean doTest(String s) + { + System.out.println(); + System.out.print(s); + System.out.print(" Perform test? Y or N:"); + System.out.flush(); + char c = ' '; + try + { + while (!(c == 'n' || c == 'y' || c == 'N' || c == 'Y')) + { + c = (char)System.in.read(); + } + } + catch (IOException ioe) + { + return false; + } + + return c == 'y' || c == 'Y'; + } + + /** + * This displays a result set. + * Note: it closes the result once complete. + */ + public void displayResult(ResultSet rs) throws SQLException + { + ResultSetMetaData rsmd = rs.getMetaData(); + int count = 0; + + // 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()) + { + count++; + 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(o.toString() + (i < cols ? "\t" : "\n")); + } + } + + System.out.println("Result returned " + count + " rows."); + + // 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 is not really an example, but is used to test the various methods in\nthe DatabaseMetaData and ResultSetMetaData classes.\n"); + System.out.println("Useage:\n java example.metadata 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 debug 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 metdata tester v6.4 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 + { + metadata test = new metadata(args); + } + catch (Exception ex) + { + System.err.println("Exception caught.\n" + ex); + ex.printStackTrace(); + } + } } diff --git a/src/interfaces/jdbc/example/psql.java b/src/interfaces/jdbc/example/psql.java index 9e63ad8129..b3fd308cd7 100644 --- a/src/interfaces/jdbc/example/psql.java +++ b/src/interfaces/jdbc/example/psql.java @@ -1,213 +1,236 @@ -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 - boolean done = false; // Added by CWJ to permit \q command - - 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("org.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); *** CWJ: messes up literals in query string *** - - // Now the main loop. - int tt=0,lineno=1; - while(tt!=StreamTokenizer.TT_EOF && ! done) { // done added by CWJ to permit \q command - 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)+(i3) - 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(); - } - } -} +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 + boolean done = false; // Added by CWJ to permit \q command + + 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("org.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); *** CWJ: messes up literals in query string *** + + // Now the main loop. + int tt = 0, lineno = 1; + while (tt != StreamTokenizer.TT_EOF && ! done) + { // done added by CWJ to permit \q command + 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(o.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 if (line.equals("\\q")) // Added by CWJ to permit \q command + done = true; + 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(); + } + } +} diff --git a/src/interfaces/jdbc/example/threadsafe.java b/src/interfaces/jdbc/example/threadsafe.java index ea69143d8d..2931221f18 100644 --- a/src/interfaces/jdbc/example/threadsafe.java +++ b/src/interfaces/jdbc/example/threadsafe.java @@ -18,350 +18,388 @@ import org.postgresql.largeobject.*; public class threadsafe { - Connection db; // The connection to the database - Statement st; // Our statement to run queries with - - public threadsafe(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException - { - String url = args[0]; - String usr = args[1]; - String pwd = args[2]; - - // Load the driver - Class.forName("org.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(); - - // Because we use LargeObjects, we must use Transactions - db.setAutoCommit(false); - - // Now run tests using JDBC methods, then LargeObjects - 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 basic1"); - } catch(Exception ex) { - // We ignore any errors here - } - - try { - st.executeUpdate("drop table basic2"); - } catch(Exception ex) { - // We ignore any errors here - } - } - - /** - * This performs the example - */ - public void doexample() throws SQLException - { - System.out.println("\nThis test runs three Threads. Two simply insert data into a table, then\nthey perform a query. While they are running, a third thread is running,\nand it load data into, then reads from a Large Object.\n\nIf alls well, this should run without any errors. If so, we are Thread Safe.\nWhy test JDBC & LargeObject's? Because both will run over the network\nconnection, and if locking on the stream isn't done correctly, the backend\nwill get pretty confused!\n"); - - thread3 thread3=null; - - try { - - // create the two threads - Thread thread0 = Thread.currentThread(); - Thread thread1 = new thread1(db); - Thread thread2 = new thread2(db); - thread3 = new thread3(db); - - // now run, and wait for them - thread1.start(); - thread2.start(); - thread3.start(); - - // ok, I know this is bad, but it does the trick here as our main thread - // will yield as long as either of the children are still running - System.out.println("Waiting for threads to run"); - while(thread1.isAlive() || thread2.isAlive() || thread3.isAlive()) - thread0.yield(); - - } finally { - // clean up after thread3 (the finally ensures this is run even - // if an exception is thrown inside the try { } construct) - if(thread3 != null) - thread3.cleanup(); - } - - System.out.println("No Exceptions have been thrown. This is a good omen, as it means that we are\npretty much thread safe as we can get."); - } - - // This is the first thread. It's the same as the basic test - class thread1 extends Thread - { - Connection c; - Statement st; - - public thread1(Connection c) throws SQLException { - this.c = c; - st = c.createStatement(); - } - - public void run() { - try { - System.out.println("Thread 1 running..."); - - // First we need a table to store data in - st.executeUpdate("create table basic1 (a int2, b int2)"); - - // Now insert some data, using the Statement - st.executeUpdate("insert into basic1 values (1,1)"); - st.executeUpdate("insert into basic1 values (2,1)"); - st.executeUpdate("insert into basic1 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 basic1 values (?,?)"); - for(int i=2;i<2000;i++) { - ps.setInt(1,4); // "column a" = 5 - ps.setInt(2,i); // "column b" = i - ps.executeUpdate(); // executeUpdate because insert returns no data -// c.commit(); - if((i%50)==0) - DriverManager.println("Thread 1 done "+i+" inserts"); + Connection db; // The connection to the database + Statement st; // Our statement to run queries with + + public threadsafe(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException + { + String url = args[0]; + String usr = args[1]; + String pwd = args[2]; + + // Load the driver + Class.forName("org.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(); + + // Because we use LargeObjects, we must use Transactions + db.setAutoCommit(false); + + // Now run tests using JDBC methods, then LargeObjects + doexample(); + + // Clean up the database + cleanup(); + + // Finally close the database + System.out.println("Now closing the connection"); + st.close(); + db.close(); + } - ps.close(); // Always close when we are done with it - - // Finally perform a query on the table - DriverManager.println("Thread 1 performing a query"); - ResultSet rs = st.executeQuery("select a, b from basic1"); - int cnt=0; - 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); - cnt++; - } - rs.close(); // again, you must close the result when done + + /** + * This drops the table (if it existed). No errors are reported. + */ + public void cleanup() + { + try + { + st.executeUpdate("drop table basic1"); + } + catch (Exception ex) + { + // We ignore any errors here + } + + try + { + st.executeUpdate("drop table basic2"); + } + catch (Exception ex) + { + // We ignore any errors here + } } - DriverManager.println("Thread 1 read "+cnt+" rows"); - - // The last thing to do is to drop the table. This is done in the - // cleanup() method. - System.out.println("Thread 1 finished"); - } catch(SQLException se) { - System.err.println("Thread 1: "+se.toString()); - se.printStackTrace(); - System.exit(1); - } - } - } - - // This is the second thread. It's the similar to the basic test, and thread1 - // except it works on another table. - class thread2 extends Thread - { - Connection c; - Statement st; - - public thread2(Connection c) throws SQLException { - this.c = c; - st = c.createStatement(); - } - - public void run() { - try { - System.out.println("Thread 2 running..."); - - // First we need a table to store data in - st.executeUpdate("create table basic2 (a int2, b int2)"); - - // 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 basic2 values (?,?)"); - for(int i=2;i<2000;i++) { - ps.setInt(1,4); // "column a" = 5 - ps.setInt(2,i); // "column b" = i - ps.executeUpdate(); // executeUpdate because insert returns no data -// c.commit(); - if((i%50)==0) - DriverManager.println("Thread 2 done "+i+" inserts"); + + /** + * This performs the example + */ + public void doexample() throws SQLException + { + System.out.println("\nThis test runs three Threads. Two simply insert data into a table, then\nthey perform a query. While they are running, a third thread is running,\nand it load data into, then reads from a Large Object.\n\nIf alls well, this should run without any errors. If so, we are Thread Safe.\nWhy test JDBC & LargeObject's? Because both will run over the network\nconnection, and if locking on the stream isn't done correctly, the backend\nwill get pretty confused!\n"); + + thread3 thread3 = null; + + try + { + + // create the two threads + Thread thread0 = Thread.currentThread(); + Thread thread1 = new thread1(db); + Thread thread2 = new thread2(db); + thread3 = new thread3(db); + + // now run, and wait for them + thread1.start(); + thread2.start(); + thread3.start(); + + // ok, I know this is bad, but it does the trick here as our main thread + // will yield as long as either of the children are still running + System.out.println("Waiting for threads to run"); + while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive()) + thread0.yield(); + + } + finally + { + // clean up after thread3 (the finally ensures this is run even + // if an exception is thrown inside the try { } construct) + if (thread3 != null) + thread3.cleanup(); + } + + System.out.println("No Exceptions have been thrown. This is a good omen, as it means that we are\npretty much thread safe as we can get."); + } + + // This is the first thread. It's the same as the basic test + class thread1 extends Thread + { + Connection c; + Statement st; + + public thread1(Connection c) throws SQLException + { + this.c = c; + st = c.createStatement(); + } + + public void run() + { + try + { + System.out.println("Thread 1 running..."); + + // First we need a table to store data in + st.executeUpdate("create table basic1 (a int2, b int2)"); + + // Now insert some data, using the Statement + st.executeUpdate("insert into basic1 values (1,1)"); + st.executeUpdate("insert into basic1 values (2,1)"); + st.executeUpdate("insert into basic1 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 basic1 values (?,?)"); + for (int i = 2;i < 2000;i++) + { + ps.setInt(1, 4); // "column a" = 5 + ps.setInt(2, i); // "column b" = i + ps.executeUpdate(); // executeUpdate because insert returns no data + // c.commit(); + if ((i % 50) == 0) + DriverManager.println("Thread 1 done " + i + " inserts"); + } + ps.close(); // Always close when we are done with it + + // Finally perform a query on the table + DriverManager.println("Thread 1 performing a query"); + ResultSet rs = st.executeQuery("select a, b from basic1"); + int cnt = 0; + 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); + cnt++; + } + rs.close(); // again, you must close the result when done + } + DriverManager.println("Thread 1 read " + cnt + " rows"); + + // The last thing to do is to drop the table. This is done in the + // cleanup() method. + System.out.println("Thread 1 finished"); + } + catch (SQLException se) + { + System.err.println("Thread 1: " + se.toString()); + se.printStackTrace(); + System.exit(1); + } + } } - ps.close(); // Always close when we are done with it - - // Finally perform a query on the table - DriverManager.println("Thread 2 performing a query"); - ResultSet rs = st.executeQuery("select * from basic2 where b>1"); - int cnt=0; - 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); - cnt++; - } - rs.close(); // again, you must close the result when done + + // This is the second thread. It's the similar to the basic test, and thread1 + // except it works on another table. + class thread2 extends Thread + { + Connection c; + Statement st; + + public thread2(Connection c) throws SQLException + { + this.c = c; + st = c.createStatement(); + } + + public void run() + { + try + { + System.out.println("Thread 2 running..."); + + // First we need a table to store data in + st.executeUpdate("create table basic2 (a int2, b int2)"); + + // 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 basic2 values (?,?)"); + for (int i = 2;i < 2000;i++) + { + ps.setInt(1, 4); // "column a" = 5 + ps.setInt(2, i); // "column b" = i + ps.executeUpdate(); // executeUpdate because insert returns no data + // c.commit(); + if ((i % 50) == 0) + DriverManager.println("Thread 2 done " + i + " inserts"); + } + ps.close(); // Always close when we are done with it + + // Finally perform a query on the table + DriverManager.println("Thread 2 performing a query"); + ResultSet rs = st.executeQuery("select * from basic2 where b>1"); + int cnt = 0; + 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); + cnt++; + } + rs.close(); // again, you must close the result when done + } + DriverManager.println("Thread 2 read " + cnt + " rows"); + + // The last thing to do is to drop the table. This is done in the + // cleanup() method. + System.out.println("Thread 2 finished"); + } + catch (SQLException se) + { + System.err.println("Thread 2: " + se.toString()); + se.printStackTrace(); + System.exit(1); + } + } } - DriverManager.println("Thread 2 read "+cnt+" rows"); - - // The last thing to do is to drop the table. This is done in the - // cleanup() method. - System.out.println("Thread 2 finished"); - } catch(SQLException se) { - System.err.println("Thread 2: "+se.toString()); - se.printStackTrace(); - System.exit(1); - } - } - } - - // This is the third thread. It loads, then reads from a LargeObject, using - // our LargeObject api. - // - // The purpose of this is to test that FastPath will work in between normal - // JDBC queries. - class thread3 extends Thread - { - Connection c; - Statement st; - LargeObjectManager lom; - LargeObject lo; - int oid; - - public thread3(Connection c) throws SQLException { - this.c = c; - //st = c.createStatement(); - - // create a blob - lom = ((org.postgresql.Connection)c).getLargeObjectAPI(); - oid = lom.create(); - System.out.println("Thread 3 has created a blob of oid "+oid); - } - - public void run() { - try { - System.out.println("Thread 3 running..."); - - DriverManager.println("Thread 3: Loading data into blob "+oid); - lo = lom.open(oid); - FileInputStream fis = new FileInputStream("example/threadsafe.java"); - // keep the buffer size small, to allow the other thread a chance - byte buf[] = new byte[128]; - int rc,bc=1,bs=0; - while((rc=fis.read(buf))>0) { - DriverManager.println("Thread 3 read block "+bc+" "+bs+" bytes"); - lo.write(buf,0,rc); - bc++; - bs+=rc; + + // This is the third thread. It loads, then reads from a LargeObject, using + // our LargeObject api. + // + // The purpose of this is to test that FastPath will work in between normal + // JDBC queries. + class thread3 extends Thread + { + Connection c; + Statement st; + LargeObjectManager lom; + LargeObject lo; + int oid; + + public thread3(Connection c) throws SQLException + { + this.c = c; + //st = c.createStatement(); + + // create a blob + lom = ((org.postgresql.Connection)c).getLargeObjectAPI(); + oid = lom.create(); + System.out.println("Thread 3 has created a blob of oid " + oid); + } + + public void run() + { + try + { + System.out.println("Thread 3 running..."); + + DriverManager.println("Thread 3: Loading data into blob " + oid); + lo = lom.open(oid); + FileInputStream fis = new FileInputStream("example/threadsafe.java"); + // keep the buffer size small, to allow the other thread a chance + byte buf[] = new byte[128]; + int rc, bc = 1, bs = 0; + while ((rc = fis.read(buf)) > 0) + { + DriverManager.println("Thread 3 read block " + bc + " " + bs + " bytes"); + lo.write(buf, 0, rc); + bc++; + bs += rc; + } + lo.close(); + fis.close(); + + DriverManager.println("Thread 3: Reading blob " + oid); + lo = lom.open(oid); + bc = 0; + while (buf.length > 0) + { + buf = lo.read(buf.length); + if (buf.length > 0) + { + String s = new String(buf); + bc++; + DriverManager.println("Thread 3 block " + bc); + DriverManager.println("Block " + bc + " got " + s); + } + } + lo.close(); + + System.out.println("Thread 3 finished"); + } + catch (Exception se) + { + System.err.println("Thread 3: " + se.toString()); + se.printStackTrace(); + System.exit(1); + } + } + + public void cleanup() throws SQLException + { + if (lom != null && oid != 0) + { + System.out.println("Thread 3: Removing blob oid=" + oid); + lom.delete(oid); + } + } } - lo.close(); - fis.close(); - - DriverManager.println("Thread 3: Reading blob "+oid); - lo=lom.open(oid); - bc=0; - while(buf.length>0) { - buf=lo.read(buf.length); - if(buf.length>0) { - String s = new String(buf); - bc++; - DriverManager.println("Thread 3 block "+bc); - DriverManager.println("Block "+bc+" got "+s); - } + + /** + * Display some instructions on how to run the example + */ + public static void instructions() + { + System.out.println("\nThis tests the thread safety of the driver.\n\nThis is done in two parts, the first with standard JDBC calls, and the\nsecond mixing FastPath and LargeObject calls with queries.\n"); + System.out.println("Useage:\n java example.threadsafe 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 Thread Safety test v6.4 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 + { + threadsafe test = new threadsafe(args); + } + catch (Exception ex) + { + System.err.println("Exception caught.\n" + ex); + ex.printStackTrace(); + } } - lo.close(); - - System.out.println("Thread 3 finished"); - } catch(Exception se) { - System.err.println("Thread 3: "+se.toString()); - se.printStackTrace(); - System.exit(1); - } - } - - public void cleanup() throws SQLException { - if(lom!=null && oid!=0) { - System.out.println("Thread 3: Removing blob oid="+oid); - lom.delete(oid); - } - } - } - - /** - * Display some instructions on how to run the example - */ - public static void instructions() - { - System.out.println("\nThis tests the thread safety of the driver.\n\nThis is done in two parts, the first with standard JDBC calls, and the\nsecond mixing FastPath and LargeObject calls with queries.\n"); - System.out.println("Useage:\n java example.threadsafe 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 Thread Safety test v6.4 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 { - threadsafe test = new threadsafe(args); - } catch(Exception ex) { - System.err.println("Exception caught.\n"+ex); - ex.printStackTrace(); - } - } } diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java index 4f6aec722c..67edcb2be4 100644 --- a/src/interfaces/jdbc/org/postgresql/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/Connection.java @@ -11,7 +11,7 @@ import org.postgresql.util.*; import org.postgresql.core.*; /** - * $Id: Connection.java,v 1.31 2001/10/16 20:05:22 barry Exp $ + * $Id: Connection.java,v 1.32 2001/10/25 05:59:59 momjian Exp $ * * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -19,1062 +19,1133 @@ import org.postgresql.core.*; */ public abstract class Connection { - // This is the network stream associated with this connection - public PG_Stream pg_stream; - - private String PG_HOST; - private int PG_PORT; - private String PG_USER; - private String PG_PASSWORD; - private String PG_DATABASE; - private boolean PG_STATUS; - private String compatible; - - /** - * The encoding to use for this connection. - */ - private Encoding encoding = Encoding.defaultEncoding(); - - private String dbVersionNumber; - - public boolean CONNECTION_OK = true; - public boolean CONNECTION_BAD = false; - - public boolean autoCommit = true; - public boolean readOnly = false; - - public Driver this_driver; - private String this_url; - private String cursor = null; // The positioned update cursor name - - // These are new for v6.3, they determine the current protocol versions - // supported by this version of the driver. They are defined in - // src/include/libpq/pqcomm.h - protected static final int PG_PROTOCOL_LATEST_MAJOR = 2; - protected static final int PG_PROTOCOL_LATEST_MINOR = 0; - private static final int SM_DATABASE = 64; - private static final int SM_USER = 32; - private static final int SM_OPTIONS = 64; - private static final int SM_UNUSED = 64; - private static final int SM_TTY = 64; - - private static final int AUTH_REQ_OK = 0; - private static final int AUTH_REQ_KRB4 = 1; - private static final int AUTH_REQ_KRB5 = 2; - private static final int AUTH_REQ_PASSWORD = 3; - private static final int AUTH_REQ_CRYPT = 4; - - // New for 6.3, salt value for crypt authorisation - private String salt; - - // These are used to cache oids, PGTypes and SQLTypes - private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType - private static Hashtable pgTypeCache = new Hashtable(); // oid -> PGType - private static Hashtable typeOidCache = new Hashtable(); //PGType -> oid - - // Now handle notices as warnings, so things like "show" now work - public SQLWarning firstWarning = null; - - /** - * Cache of the current isolation level - */ - private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED; - - // The PID an cancellation key we get from the backend process - public int pid; - public int ckey; - - /** - * This is called by Class.forName() from within org.postgresql.Driver - */ - public Connection() - { - } - - /** - * This method actually opens the connection. It is called by Driver. - * - * @param host the hostname of the database back end - * @param port the port number of the postmaster process - * @param info a Properties[] thing of the user and password - * @param database the database to connect to - * @param u the URL of the connection - * @param d the Driver instantation of the connection - * @return a valid connection profile - * @exception SQLException if a database access error occurs - */ - protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException - { - // Throw an exception if the user or password properties are missing - // This occasionally occurs when the client uses the properties version - // of getConnection(), and is a common question on the email lists - if(info.getProperty("user")==null) - throw new PSQLException("postgresql.con.user"); - if(info.getProperty("password")==null) - throw new PSQLException("postgresql.con.pass"); - - this_driver = d; - this_url = url; - PG_DATABASE = database; - PG_PASSWORD = info.getProperty("password"); - PG_USER = info.getProperty("user"); - PG_PORT = port; - PG_HOST = host; - PG_STATUS = CONNECTION_BAD; - if(info.getProperty("compatible")==null) { - compatible = d.getMajorVersion() + "." + d.getMinorVersion(); - } else { - compatible = info.getProperty("compatible"); - } - - // Now make the initial connection - try - { - pg_stream = new PG_Stream(host, port); - } catch (ConnectException cex) { - // Added by Peter Mount - // ConnectException is thrown when the connection cannot be made. - // we trap this an return a more meaningful message for the end user - throw new PSQLException ("postgresql.con.refused"); - } catch (IOException e) { - throw new PSQLException ("postgresql.con.failed",e); - } - - // Now we need to construct and send a startup packet - try - { - // Ver 6.3 code - pg_stream.SendInteger(4+4+SM_DATABASE+SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY,4); - pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR,2); - pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR,2); - pg_stream.Send(database.getBytes(),SM_DATABASE); - - // This last send includes the unused fields - pg_stream.Send(PG_USER.getBytes(),SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY); - - // now flush the startup packets to the backend - pg_stream.flush(); - - // Now get the response from the backend, either an error message - // or an authentication request - int areq = -1; // must have a value here - do { - int beresp = pg_stream.ReceiveChar(); - switch(beresp) - { - case 'E': - // An error occured, so pass the error message to the - // user. - // - // The most common one to be thrown here is: - // "User authentication failed" - // - throw new SQLException(pg_stream.ReceiveString(encoding)); - - case 'R': - // Get the type of request - areq = pg_stream.ReceiveIntegerR(4); - - // Get the password salt if there is one - if(areq == AUTH_REQ_CRYPT) { - byte[] rst = new byte[2]; - rst[0] = (byte)pg_stream.ReceiveChar(); - rst[1] = (byte)pg_stream.ReceiveChar(); - salt = new String(rst,0,2); - DriverManager.println("Salt="+salt); - } - - // now send the auth packet - switch(areq) - { - case AUTH_REQ_OK: - break; - - case AUTH_REQ_KRB4: - DriverManager.println("postgresql: KRB4"); - throw new PSQLException("postgresql.con.kerb4"); - - case AUTH_REQ_KRB5: - DriverManager.println("postgresql: KRB5"); - throw new PSQLException("postgresql.con.kerb5"); - - case AUTH_REQ_PASSWORD: - DriverManager.println("postgresql: PASSWORD"); - pg_stream.SendInteger(5+PG_PASSWORD.length(),4); - pg_stream.Send(PG_PASSWORD.getBytes()); - pg_stream.SendInteger(0,1); - pg_stream.flush(); - break; - - case AUTH_REQ_CRYPT: - DriverManager.println("postgresql: CRYPT"); - String crypted = UnixCrypt.crypt(salt,PG_PASSWORD); - pg_stream.SendInteger(5+crypted.length(),4); - pg_stream.Send(crypted.getBytes()); - pg_stream.SendInteger(0,1); - pg_stream.flush(); - break; - - default: - throw new PSQLException("postgresql.con.auth",new Integer(areq)); - } - break; - - default: - throw new PSQLException("postgresql.con.authfail"); - } - } while(areq != AUTH_REQ_OK); - - } catch (IOException e) { - throw new PSQLException("postgresql.con.failed",e); - } - - - // As of protocol version 2.0, we should now receive the cancellation key and the pid - int beresp = pg_stream.ReceiveChar(); - switch(beresp) { - case 'K': - pid = pg_stream.ReceiveInteger(4); - ckey = pg_stream.ReceiveInteger(4); - break; - case 'E': - case 'N': - throw new SQLException(pg_stream.ReceiveString(encoding)); - default: - throw new PSQLException("postgresql.con.setup"); - } - - // Expect ReadyForQuery packet - beresp = pg_stream.ReceiveChar(); - switch(beresp) { - case 'Z': - break; - case 'E': - case 'N': - throw new SQLException(pg_stream.ReceiveString(encoding)); - default: - throw new PSQLException("postgresql.con.setup"); - } - - firstWarning = null; - - // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte, - // otherwise it's hardcoded to 'SQL_ASCII'. - // If the backend doesn't know about multibyte we can't assume anything about the encoding - // used, so we denote this with 'UNKNOWN'. - //Note: begining with 7.2 we should be using pg_client_encoding() which - //is new in 7.2. However it isn't easy to conditionally call this new - //function, since we don't yet have the information as to what server - //version we are talking to. Thus we will continue to call - //getdatabaseencoding() until we drop support for 7.1 and older versions - //or until someone comes up with a conditional way to run one or - //the other function depending on server version that doesn't require - //two round trips to the server per connection - - final String encodingQuery = - "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end"; - - // Set datestyle and fetch db encoding in a single call, to avoid making - // more than one round trip to the backend during connection startup. - - java.sql.ResultSet resultSet = - ExecSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";"); - - if (! resultSet.next()) { - throw new PSQLException("postgresql.con.failed", "failed getting backend encoding"); - } - String version = resultSet.getString(1); - dbVersionNumber = extractVersionNumber(version); - - String dbEncoding = resultSet.getString(2); - encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet")); - - // Initialise object handling - initObjectTypes(); - - // Mark the connection as ok, and cleanup - firstWarning = null; - PG_STATUS = CONNECTION_OK; - } - - // These methods used to be in the main Connection implementation. As they - // are common to all implementations (JDBC1 or 2), they are placed here. - // This should make it easy to maintain the two specifications. - - /** - * This adds a warning to the warning chain. - * @param msg message to add - */ - public void addWarning(String msg) - { - DriverManager.println(msg); - - // Add the warning to the chain - if(firstWarning!=null) - firstWarning.setNextWarning(new SQLWarning(msg)); - else - firstWarning = new SQLWarning(msg); - - // Now check for some specific messages - - // This is obsolete in 6.5, but I've left it in here so if we need to use this - // technique again, we'll know where to place it. - // - // This is generated by the SQL "show datestyle" - //if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) { - //// 13 is the length off "DateStyle is " - //msg = msg.substring(msg.indexOf("DateStyle is ")+13); - // - //for(int i=0;iNote: there does not seem to be any method currently - * in existance to return the update count. - * - * @param sql the SQL statement to be executed - * @return a ResultSet holding the results - * @exception SQLException if a database error occurs - */ - public java.sql.ResultSet ExecSQL(String sql) throws SQLException - { - return ExecSQL(sql,null); - } - - /** - * Send a query to the backend. Returns one of the ResultSet - * objects. - * - * Note: there does not seem to be any method currently - * in existance to return the update count. - * - * @param sql the SQL statement to be executed - * @param stat The Statement associated with this query (may be null) - * @return a ResultSet holding the results - * @exception SQLException if a database error occurs - */ - public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException - { - return new QueryExecutor(sql, stat, pg_stream, this).execute(); - } - - /** - * In SQL, a result table can be retrieved through a cursor that - * is named. The current row of a result can be updated or deleted - * using a positioned update/delete statement that references the - * cursor name. - * - * We support one cursor per connection. - * - * setCursorName sets the cursor name. - * - * @param cursor the cursor name - * @exception SQLException if a database access error occurs - */ - public void setCursorName(String cursor) throws SQLException - { - this.cursor = cursor; - } - - /** - * getCursorName gets the cursor name. - * - * @return the current cursor name - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return cursor; - } - - /** - * We are required to bring back certain information by - * the DatabaseMetaData class. These functions do that. - * - * Method getURL() brings back the URL (good job we saved it) - * - * @return the url - * @exception SQLException just in case... - */ - public String getURL() throws SQLException - { - return this_url; - } - - /** - * Method getUserName() brings back the User Name (again, we - * saved it) - * - * @return the user name - * @exception SQLException just in case... - */ - public String getUserName() throws SQLException - { - return PG_USER; - } - - /** - * Get the character encoding to use for this connection. - */ - public Encoding getEncoding() throws SQLException { - return encoding; - } - - /** - * This returns the Fastpath API for the current connection. - * - *

NOTE: This is not part of JDBC, but allows access to - * functions on the org.postgresql backend itself. - * - *

It is primarily used by the LargeObject API - * - *

The best way to use this is as follows: - * - *

-     * import org.postgresql.fastpath.*;
-     * ...
-     * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI();
-     * 
- * - *

where myconn is an open Connection to org.postgresql. - * - * @return Fastpath object allowing access to functions on the org.postgresql - * backend. - * @exception SQLException by Fastpath when initialising for first time - */ - public Fastpath getFastpathAPI() throws SQLException - { - if(fastpath==null) - fastpath = new Fastpath(this,pg_stream); - return fastpath; - } - - // This holds a reference to the Fastpath API if already open - private Fastpath fastpath = null; - - /** - * This returns the LargeObject API for the current connection. - * - *

NOTE: This is not part of JDBC, but allows access to - * functions on the org.postgresql backend itself. - * - *

The best way to use this is as follows: - * - *

-     * import org.postgresql.largeobject.*;
-     * ...
-     * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI();
-     * 
- * - *

where myconn is an open Connection to org.postgresql. - * - * @return LargeObject object that implements the API - * @exception SQLException by LargeObject when initialising for first time - */ - public LargeObjectManager getLargeObjectAPI() throws SQLException - { - if(largeobject==null) - largeobject = new LargeObjectManager(this); - return largeobject; - } - - // This holds a reference to the LargeObject API if already open - private LargeObjectManager largeobject = null; - - /** - * This method is used internally to return an object based around - * org.postgresql's more unique data types. - * - *

It uses an internal Hashtable to get the handling class. If the - * type is not supported, then an instance of org.postgresql.util.PGobject - * is returned. - * - * You can use the getValue() or setValue() methods to handle the returned - * object. Custom objects can have their own methods. - * - * In 6.4, this is extended to use the org.postgresql.util.Serialize class to - * allow the Serialization of Java Objects into the database without using - * Blobs. Refer to that class for details on how this new feature works. - * - * @return PGobject for this type, and set to value - * @exception SQLException if value is not correct for this type - * @see org.postgresql.util.Serialize - */ - public Object getObject(String type,String value) throws SQLException - { - try { - Object o = objectTypes.get(type); - - // If o is null, then the type is unknown, so check to see if type - // is an actual table name. If it does, see if a Class is known that - // can handle it - if(o == null) { - Serialize ser = new Serialize(this,type); - objectTypes.put(type,ser); - return ser.fetch(Integer.parseInt(value)); - } - - // If o is not null, and it is a String, then its a class name that - // extends PGobject. - // - // This is used to implement the org.postgresql unique types (like lseg, - // point, etc). - if(o instanceof String) { - // 6.3 style extending PG_Object - PGobject obj = null; - obj = (PGobject)(Class.forName((String)o).newInstance()); - obj.setType(type); - obj.setValue(value); - return (Object)obj; - } else { - // If it's an object, it should be an instance of our Serialize class - // If so, then call it's fetch method. - if(o instanceof Serialize) - return ((Serialize)o).fetch(Integer.parseInt(value)); - } - } catch(SQLException sx) { - // rethrow the exception. Done because we capture any others next - sx.fillInStackTrace(); - throw sx; - } catch(Exception ex) { - throw new PSQLException("postgresql.con.creobj",type,ex); - } - - // should never be reached - return null; - } - - /** - * This stores an object into the database. - * @param o Object to store - * @return OID of the new rectord - * @exception SQLException if value is not correct for this type - * @see org.postgresql.util.Serialize - */ - public int putObject(Object o) throws SQLException - { - try { - String type = o.getClass().getName(); - Object x = objectTypes.get(type); - - // If x is null, then the type is unknown, so check to see if type - // is an actual table name. If it does, see if a Class is known that - // can handle it - if(x == null) { - Serialize ser = new Serialize(this,type); - objectTypes.put(type,ser); - return ser.store(o); - } - - // If it's an object, it should be an instance of our Serialize class - // If so, then call it's fetch method. - if(x instanceof Serialize) - return ((Serialize)x).store(o); - - // Thow an exception because the type is unknown - throw new PSQLException("postgresql.con.strobj"); - - } catch(SQLException sx) { - // rethrow the exception. Done because we capture any others next - sx.fillInStackTrace(); - throw sx; - } catch(Exception ex) { - throw new PSQLException("postgresql.con.strobjex",ex); - } - } - - /** - * This allows client code to add a handler for one of org.postgresql's - * more unique data types. - * - *

NOTE: This is not part of JDBC, but an extension. - * - *

The best way to use this is as follows: - * - *

-     * ...
-     * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name");
-     * ...
-     * 
- * - *

where myconn is an open Connection to org.postgresql. - * - *

The handling class must extend org.postgresql.util.PGobject - * - * @see org.postgresql.util.PGobject - */ - public void addDataType(String type,String name) - { - objectTypes.put(type,name); - } - - // This holds the available types - private Hashtable objectTypes = new Hashtable(); - - // This array contains the types that are supported as standard. - // - // The first entry is the types name on the database, the second - // the full class name of the handling class. - // - private static final String defaultObjectTypes[][] = { - {"box", "org.postgresql.geometric.PGbox"}, - {"circle", "org.postgresql.geometric.PGcircle"}, - {"line", "org.postgresql.geometric.PGline"}, - {"lseg", "org.postgresql.geometric.PGlseg"}, - {"path", "org.postgresql.geometric.PGpath"}, - {"point", "org.postgresql.geometric.PGpoint"}, - {"polygon", "org.postgresql.geometric.PGpolygon"}, - {"money", "org.postgresql.util.PGmoney"} - }; - - // This initialises the objectTypes hashtable - private void initObjectTypes() - { - for(int i=0;iNote: A Connection is automatically closed when it is - * garbage collected. Certain fatal errors also result in a closed - * connection. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException { - if (pg_stream != null) { - try { - pg_stream.SendChar('X'); - pg_stream.flush(); - pg_stream.close(); - } catch (IOException e) {} - pg_stream = null; - } - } - - /** - * A driver may convert the JDBC sql grammar into its system's - * native SQL grammar prior to sending it; nativeSQL returns the - * native form of the statement that the driver would have sent. - * - * @param sql a SQL statement that may contain one or more '?' - * parameter placeholders - * @return the native form of this statement - * @exception SQLException if a database access error occurs - */ - public String nativeSQL(String sql) throws SQLException { - return sql; - } - - /** - * The first warning reported by calls on this Connection is - * returned. - * - * Note: Sebsequent warnings will be changed to this - * SQLWarning - * - * @return the first SQLWarning or null - * @exception SQLException if a database access error occurs - */ - public SQLWarning getWarnings() throws SQLException { - return firstWarning; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this connection. - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException { - firstWarning = null; - } - - - /** - * You can put a connection in read-only mode as a hunt to enable - * database optimizations - * - * Note: setReadOnly cannot be called while in the middle - * of a transaction - * - * @param readOnly - true enables read-only mode; false disables it - * @exception SQLException if a database access error occurs - */ - public void setReadOnly(boolean readOnly) throws SQLException { - this.readOnly = readOnly; - } - - /** - * Tests to see if the connection is in Read Only Mode. Note that - * we cannot really put the database in read only mode, but we pretend - * we can by returning the value of the readOnly flag - * - * @return true if the connection is read only - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException { - return readOnly; - } - - /** - * If a connection is in auto-commit mode, than all its SQL - * statements will be executed and committed as individual - * transactions. Otherwise, its SQL statements are grouped - * into transactions that are terminated by either commit() - * or rollback(). By default, new connections are in auto- - * commit mode. The commit occurs when the statement completes - * or the next execute occurs, whichever comes first. In the - * case of statements returning a ResultSet, the statement - * completes when the last row of the ResultSet has been retrieved - * or the ResultSet has been closed. In advanced cases, a single - * statement may return multiple results as well as output parameter - * values. Here the commit occurs when all results and output param - * values have been retrieved. - * - * @param autoCommit - true enables auto-commit; false disables it - * @exception SQLException if a database access error occurs - */ - public void setAutoCommit(boolean autoCommit) throws SQLException { - if (this.autoCommit == autoCommit) - return; - if (autoCommit) - ExecSQL("end"); - else { - if (haveMinimumServerVersion("7.1")){ - ExecSQL("begin;"+getIsolationLevelSQL()); - }else{ - ExecSQL("begin"); - ExecSQL(getIsolationLevelSQL()); - } - } - this.autoCommit = autoCommit; - } - - /** - * gets the current auto-commit state - * - * @return Current state of the auto-commit mode - * @exception SQLException (why?) - * @see setAutoCommit - */ - public boolean getAutoCommit() throws SQLException { - return this.autoCommit; - } - - /** - * The method commit() makes all changes made since the previous - * commit/rollback permanent and releases any database locks currently - * held by the Connection. This method should only be used when - * auto-commit has been disabled. (If autoCommit == true, then we - * just return anyhow) - * - * @exception SQLException if a database access error occurs - * @see setAutoCommit - */ - public void commit() throws SQLException { - if (autoCommit) - return; - if (haveMinimumServerVersion("7.1")){ - ExecSQL("commit;begin;"+getIsolationLevelSQL()); - }else{ - ExecSQL("commit"); - ExecSQL("begin"); - ExecSQL(getIsolationLevelSQL()); - } - } - - /** - * The method rollback() drops all changes made since the previous - * commit/rollback and releases any database locks currently held by - * the Connection. - * - * @exception SQLException if a database access error occurs - * @see commit - */ - public void rollback() throws SQLException { - if (autoCommit) - return; - if (haveMinimumServerVersion("7.1")){ - ExecSQL("rollback; begin;"+getIsolationLevelSQL()); - }else{ - ExecSQL("rollback"); - ExecSQL("begin"); - ExecSQL(getIsolationLevelSQL()); - } - } - - /** - * Get this Connection's current transaction isolation mode. - * - * @return the current TRANSACTION_* mode value - * @exception SQLException if a database access error occurs - */ - public int getTransactionIsolation() throws SQLException { - clearWarnings(); - ExecSQL("show xactisolevel"); - - SQLWarning warning = getWarnings(); - if (warning != null) { - String message = warning.getMessage(); - clearWarnings(); - if (message.indexOf("READ COMMITTED") != -1) - return java.sql.Connection.TRANSACTION_READ_COMMITTED; - else if (message.indexOf("READ UNCOMMITTED") != -1) - return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; - else if (message.indexOf("REPEATABLE READ") != -1) - return java.sql.Connection.TRANSACTION_REPEATABLE_READ; - else if (message.indexOf("SERIALIZABLE") != -1) - return java.sql.Connection.TRANSACTION_SERIALIZABLE; - } - return java.sql.Connection.TRANSACTION_READ_COMMITTED; - } - - /** - * You can call this method to try to change the transaction - * isolation level using one of the TRANSACTION_* values. - * - * Note: setTransactionIsolation cannot be called while - * in the middle of a transaction - * - * @param level one of the TRANSACTION_* isolation values with - * the exception of TRANSACTION_NONE; some databases may - * not support other values - * @exception SQLException if a database access error occurs - * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel - */ - public void setTransactionIsolation(int level) throws SQLException { - //In 7.1 and later versions of the server it is possible using - //the "set session" command to set this once for all future txns - //however in 7.0 and prior versions it is necessary to set it in - //each transaction, thus adding complexity below. - //When we decide to drop support for servers older than 7.1 - //this can be simplified - isolationLevel = level; - String isolationLevelSQL; - - if (!haveMinimumServerVersion("7.1")) { - isolationLevelSQL = getIsolationLevelSQL(); - } else { - isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL "; - switch(isolationLevel) { - case java.sql.Connection.TRANSACTION_READ_COMMITTED: - isolationLevelSQL += "READ COMMITTED"; - break; - case java.sql.Connection.TRANSACTION_SERIALIZABLE: - isolationLevelSQL += "SERIALIZABLE"; - break; - default: - throw new PSQLException("postgresql.con.isolevel", - new Integer(isolationLevel)); - } - } - ExecSQL(isolationLevelSQL); - } - - /** - * Helper method used by setTransactionIsolation(), commit(), rollback() - * and setAutoCommit(). This returns the SQL string needed to - * set the isolation level for a transaction. In 7.1 and later it - * is possible to set a default isolation level that applies to all - * future transactions, this method is only necesary for 7.0 and older - * servers, and should be removed when support for these older - * servers are dropped - */ - protected String getIsolationLevelSQL() throws SQLException { - //7.1 and higher servers have a default specified so - //no additional SQL is required to set the isolation level - if (haveMinimumServerVersion("7.1")) { - return ""; - } - StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL"); - - switch(isolationLevel) { - case java.sql.Connection.TRANSACTION_READ_COMMITTED: - sb.append(" READ COMMITTED"); - break; - - case java.sql.Connection.TRANSACTION_SERIALIZABLE: - sb.append(" SERIALIZABLE"); - break; - - default: - throw new PSQLException("postgresql.con.isolevel",new Integer(isolationLevel)); - } - return sb.toString(); - } - - /** - * A sub-space of this Connection's database may be selected by - * setting a catalog name. If the driver does not support catalogs, - * it will silently ignore this request - * - * @exception SQLException if a database access error occurs - */ - public void setCatalog(String catalog) throws SQLException - { - //no-op - } - - /** - * Return the connections current catalog name, or null if no - * catalog name is set, or we dont support catalogs. - * - * @return the current catalog name or null - * @exception SQLException if a database access error occurs - */ - public String getCatalog() throws SQLException - { - return PG_DATABASE; - } - - /** - * Overides finalize(). If called, it closes the connection. - * - * This was done at the request of Rachel Greenham - * who hit a problem where multiple - * clients didn't close the connection, and once a fortnight enough - * clients were open to kill the org.postgres server. - */ - public void finalize() throws Throwable - { - close(); - } - - private static String extractVersionNumber(String fullVersionString) - { - StringTokenizer versionParts = new StringTokenizer(fullVersionString); - versionParts.nextToken(); /* "PostgreSQL" */ - return versionParts.nextToken(); /* "X.Y.Z" */ - } - - /** - * Get server version number - */ - public String getDBVersionNumber() { - return dbVersionNumber; - } - - public boolean haveMinimumServerVersion(String ver) throws SQLException - { - return (getDBVersionNumber().compareTo(ver) >= 0); - } - - /** - * This method returns true if the compatible level set in the connection - * (which can be passed into the connection or specified in the URL) - * is at least the value passed to this method. This is used to toggle - * between different functionality as it changes across different releases - * of the jdbc driver code. The values here are versions of the jdbc client - * and not server versions. For example in 7.1 get/setBytes worked on - * LargeObject values, in 7.2 these methods were changed to work on bytea - * values. This change in functionality could be disabled by setting the - * "compatible" level to be 7.1, in which case the driver will revert to - * the 7.1 functionality. - */ - public boolean haveMinimumCompatibleVersion(String ver) throws SQLException - { - return (compatible.compareTo(ver) >= 0); - } - - - /** - * This returns the java.sql.Types type for a PG type oid - * - * @param oid PostgreSQL type oid - * @return the java.sql.Types type - * @exception SQLException if a database access error occurs - */ - public int getSQLType(int oid) throws SQLException - { - Integer sqlType = (Integer)typeOidCache.get(new Integer(oid)); - - // it's not in the cache, so perform a query, and add the result to the cache - if(sqlType==null) { - ResultSet result = (org.postgresql.ResultSet)ExecSQL("select typname from pg_type where oid = " + oid); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) - throw new PSQLException("postgresql.unexpected"); - result.next(); - String pgType = result.getString(1); - Integer iOid = new Integer(oid); - sqlType = new Integer(getSQLType(result.getString(1))); - sqlTypeCache.put(iOid,sqlType); - pgTypeCache.put(iOid,pgType); - result.close(); - } - - return sqlType.intValue(); - } - - /** - * This returns the java.sql.Types type for a PG type - * - * @param pgTypeName PostgreSQL type name - * @return the java.sql.Types type - */ - public abstract int getSQLType(String pgTypeName); - - /** - * This returns the oid for a given PG data type - * @param typeName PostgreSQL type name - * @return PostgreSQL oid value for a field of this type - */ - public int getOID(String typeName) throws SQLException - { - int oid = -1; - if(typeName != null) { - Integer oidValue = (Integer) typeOidCache.get(typeName); - if(oidValue != null) { - oid = oidValue.intValue(); - } else { - // it's not in the cache, so perform a query, and add the result to the cache - ResultSet result = (org.postgresql.ResultSet)ExecSQL("select oid from pg_type where typname='" - + typeName + "'"); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) - throw new PSQLException("postgresql.unexpected"); - result.next(); - oid = Integer.parseInt(result.getString(1)); - typeOidCache.put(typeName, new Integer(oid)); - result.close(); - } - } - return oid; - } - - /** - * We also need to get the PG type name as returned by the back end. - * - * @return the String representation of the type of this field - * @exception SQLException if a database access error occurs - */ - public String getPGType(int oid) throws SQLException - { - String pgType = (String) pgTypeCache.get(new Integer(oid)); - if(pgType == null) { - getSQLType(oid); - pgType = (String) pgTypeCache.get(new Integer(oid)); - } - return pgType; - } + // This is the network stream associated with this connection + public PG_Stream pg_stream; + + private String PG_HOST; + private int PG_PORT; + private String PG_USER; + private String PG_PASSWORD; + private String PG_DATABASE; + private boolean PG_STATUS; + private String compatible; + + /** + * The encoding to use for this connection. + */ + private Encoding encoding = Encoding.defaultEncoding(); + + private String dbVersionNumber; + + public boolean CONNECTION_OK = true; + public boolean CONNECTION_BAD = false; + + public boolean autoCommit = true; + public boolean readOnly = false; + + public Driver this_driver; + private String this_url; + private String cursor = null; // The positioned update cursor name + + // These are new for v6.3, they determine the current protocol versions + // supported by this version of the driver. They are defined in + // src/include/libpq/pqcomm.h + protected static final int PG_PROTOCOL_LATEST_MAJOR = 2; + protected static final int PG_PROTOCOL_LATEST_MINOR = 0; + private static final int SM_DATABASE = 64; + private static final int SM_USER = 32; + private static final int SM_OPTIONS = 64; + private static final int SM_UNUSED = 64; + private static final int SM_TTY = 64; + + private static final int AUTH_REQ_OK = 0; + private static final int AUTH_REQ_KRB4 = 1; + private static final int AUTH_REQ_KRB5 = 2; + private static final int AUTH_REQ_PASSWORD = 3; + private static final int AUTH_REQ_CRYPT = 4; + + // New for 6.3, salt value for crypt authorisation + private String salt; + + // These are used to cache oids, PGTypes and SQLTypes + private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType + private static Hashtable pgTypeCache = new Hashtable(); // oid -> PGType + private static Hashtable typeOidCache = new Hashtable(); //PGType -> oid + + // Now handle notices as warnings, so things like "show" now work + public SQLWarning firstWarning = null; + + /** + * Cache of the current isolation level + */ + private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED; + + // The PID an cancellation key we get from the backend process + public int pid; + public int ckey; + + /** + * This is called by Class.forName() from within org.postgresql.Driver + */ + public Connection() + {} + + /** + * This method actually opens the connection. It is called by Driver. + * + * @param host the hostname of the database back end + * @param port the port number of the postmaster process + * @param info a Properties[] thing of the user and password + * @param database the database to connect to + * @param u the URL of the connection + * @param d the Driver instantation of the connection + * @return a valid connection profile + * @exception SQLException if a database access error occurs + */ + protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException + { + // Throw an exception if the user or password properties are missing + // This occasionally occurs when the client uses the properties version + // of getConnection(), and is a common question on the email lists + if (info.getProperty("user") == null) + throw new PSQLException("postgresql.con.user"); + if (info.getProperty("password") == null) + throw new PSQLException("postgresql.con.pass"); + + this_driver = d; + this_url = url; + PG_DATABASE = database; + PG_PASSWORD = info.getProperty("password"); + PG_USER = info.getProperty("user"); + PG_PORT = port; + PG_HOST = host; + PG_STATUS = CONNECTION_BAD; + if (info.getProperty("compatible") == null) + { + compatible = d.getMajorVersion() + "." + d.getMinorVersion(); + } + else + { + compatible = info.getProperty("compatible"); + } + + // Now make the initial connection + try + { + pg_stream = new PG_Stream(host, port); + } + catch (ConnectException cex) + { + // Added by Peter Mount + // ConnectException is thrown when the connection cannot be made. + // we trap this an return a more meaningful message for the end user + throw new PSQLException ("postgresql.con.refused"); + } + catch (IOException e) + { + throw new PSQLException ("postgresql.con.failed", e); + } + + // Now we need to construct and send a startup packet + try + { + // Ver 6.3 code + pg_stream.SendInteger(4 + 4 + SM_DATABASE + SM_USER + SM_OPTIONS + SM_UNUSED + SM_TTY, 4); + pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR, 2); + pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR, 2); + pg_stream.Send(database.getBytes(), SM_DATABASE); + + // This last send includes the unused fields + pg_stream.Send(PG_USER.getBytes(), SM_USER + SM_OPTIONS + SM_UNUSED + SM_TTY); + + // now flush the startup packets to the backend + pg_stream.flush(); + + // Now get the response from the backend, either an error message + // or an authentication request + int areq = -1; // must have a value here + do + { + int beresp = pg_stream.ReceiveChar(); + switch (beresp) + { + case 'E': + // An error occured, so pass the error message to the + // user. + // + // The most common one to be thrown here is: + // "User authentication failed" + // + throw new SQLException(pg_stream.ReceiveString(encoding)); + + case 'R': + // Get the type of request + areq = pg_stream.ReceiveIntegerR(4); + + // Get the password salt if there is one + if (areq == AUTH_REQ_CRYPT) + { + byte[] rst = new byte[2]; + rst[0] = (byte)pg_stream.ReceiveChar(); + rst[1] = (byte)pg_stream.ReceiveChar(); + salt = new String(rst, 0, 2); + DriverManager.println("Salt=" + salt); + } + + // now send the auth packet + switch (areq) + { + case AUTH_REQ_OK: + break; + + case AUTH_REQ_KRB4: + DriverManager.println("postgresql: KRB4"); + throw new PSQLException("postgresql.con.kerb4"); + + case AUTH_REQ_KRB5: + DriverManager.println("postgresql: KRB5"); + throw new PSQLException("postgresql.con.kerb5"); + + case AUTH_REQ_PASSWORD: + DriverManager.println("postgresql: PASSWORD"); + pg_stream.SendInteger(5 + PG_PASSWORD.length(), 4); + pg_stream.Send(PG_PASSWORD.getBytes()); + pg_stream.SendInteger(0, 1); + pg_stream.flush(); + break; + + case AUTH_REQ_CRYPT: + DriverManager.println("postgresql: CRYPT"); + String crypted = UnixCrypt.crypt(salt, PG_PASSWORD); + pg_stream.SendInteger(5 + crypted.length(), 4); + pg_stream.Send(crypted.getBytes()); + pg_stream.SendInteger(0, 1); + pg_stream.flush(); + break; + + default: + throw new PSQLException("postgresql.con.auth", new Integer(areq)); + } + break; + + default: + throw new PSQLException("postgresql.con.authfail"); + } + } + while (areq != AUTH_REQ_OK); + + } + catch (IOException e) + { + throw new PSQLException("postgresql.con.failed", e); + } + + + // As of protocol version 2.0, we should now receive the cancellation key and the pid + int beresp = pg_stream.ReceiveChar(); + switch (beresp) + { + case 'K': + pid = pg_stream.ReceiveInteger(4); + ckey = pg_stream.ReceiveInteger(4); + break; + case 'E': + case 'N': + throw new SQLException(pg_stream.ReceiveString(encoding)); + default: + throw new PSQLException("postgresql.con.setup"); + } + + // Expect ReadyForQuery packet + beresp = pg_stream.ReceiveChar(); + switch (beresp) + { + case 'Z': + break; + case 'E': + case 'N': + throw new SQLException(pg_stream.ReceiveString(encoding)); + default: + throw new PSQLException("postgresql.con.setup"); + } + + firstWarning = null; + + // "pg_encoding_to_char(1)" will return 'EUC_JP' for a backend compiled with multibyte, + // otherwise it's hardcoded to 'SQL_ASCII'. + // If the backend doesn't know about multibyte we can't assume anything about the encoding + // used, so we denote this with 'UNKNOWN'. + //Note: begining with 7.2 we should be using pg_client_encoding() which + //is new in 7.2. However it isn't easy to conditionally call this new + //function, since we don't yet have the information as to what server + //version we are talking to. Thus we will continue to call + //getdatabaseencoding() until we drop support for 7.1 and older versions + //or until someone comes up with a conditional way to run one or + //the other function depending on server version that doesn't require + //two round trips to the server per connection + + final String encodingQuery = + "case when pg_encoding_to_char(1) = 'SQL_ASCII' then 'UNKNOWN' else getdatabaseencoding() end"; + + // Set datestyle and fetch db encoding in a single call, to avoid making + // more than one round trip to the backend during connection startup. + + java.sql.ResultSet resultSet = + ExecSQL("set datestyle to 'ISO'; select version(), " + encodingQuery + ";"); + + if (! resultSet.next()) + { + throw new PSQLException("postgresql.con.failed", "failed getting backend encoding"); + } + String version = resultSet.getString(1); + dbVersionNumber = extractVersionNumber(version); + + String dbEncoding = resultSet.getString(2); + encoding = Encoding.getEncoding(dbEncoding, info.getProperty("charSet")); + + // Initialise object handling + initObjectTypes(); + + // Mark the connection as ok, and cleanup + firstWarning = null; + PG_STATUS = CONNECTION_OK; + } + + // These methods used to be in the main Connection implementation. As they + // are common to all implementations (JDBC1 or 2), they are placed here. + // This should make it easy to maintain the two specifications. + + /** + * This adds a warning to the warning chain. + * @param msg message to add + */ + public void addWarning(String msg) + { + DriverManager.println(msg); + + // Add the warning to the chain + if (firstWarning != null) + firstWarning.setNextWarning(new SQLWarning(msg)); + else + firstWarning = new SQLWarning(msg); + + // Now check for some specific messages + + // This is obsolete in 6.5, but I've left it in here so if we need to use this + // technique again, we'll know where to place it. + // + // This is generated by the SQL "show datestyle" + //if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) { + //// 13 is the length off "DateStyle is " + //msg = msg.substring(msg.indexOf("DateStyle is ")+13); + // + //for(int i=0;iNote: there does not seem to be any method currently + * in existance to return the update count. + * + * @param sql the SQL statement to be executed + * @return a ResultSet holding the results + * @exception SQLException if a database error occurs + */ + public java.sql.ResultSet ExecSQL(String sql) throws SQLException + { + return ExecSQL(sql, null); + } + + /** + * Send a query to the backend. Returns one of the ResultSet + * objects. + * + * Note: there does not seem to be any method currently + * in existance to return the update count. + * + * @param sql the SQL statement to be executed + * @param stat The Statement associated with this query (may be null) + * @return a ResultSet holding the results + * @exception SQLException if a database error occurs + */ + public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException + { + return new QueryExecutor(sql, stat, pg_stream, this).execute(); + } + + /** + * In SQL, a result table can be retrieved through a cursor that + * is named. The current row of a result can be updated or deleted + * using a positioned update/delete statement that references the + * cursor name. + * + * We support one cursor per connection. + * + * setCursorName sets the cursor name. + * + * @param cursor the cursor name + * @exception SQLException if a database access error occurs + */ + public void setCursorName(String cursor) throws SQLException + { + this.cursor = cursor; + } + + /** + * getCursorName gets the cursor name. + * + * @return the current cursor name + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return cursor; + } + + /** + * We are required to bring back certain information by + * the DatabaseMetaData class. These functions do that. + * + * Method getURL() brings back the URL (good job we saved it) + * + * @return the url + * @exception SQLException just in case... + */ + public String getURL() throws SQLException + { + return this_url; + } + + /** + * Method getUserName() brings back the User Name (again, we + * saved it) + * + * @return the user name + * @exception SQLException just in case... + */ + public String getUserName() throws SQLException + { + return PG_USER; + } + + /** + * Get the character encoding to use for this connection. + */ + public Encoding getEncoding() throws SQLException + { + return encoding; + } + + /** + * This returns the Fastpath API for the current connection. + * + *

NOTE: This is not part of JDBC, but allows access to + * functions on the org.postgresql backend itself. + * + *

It is primarily used by the LargeObject API + * + *

The best way to use this is as follows: + * + *

+	 * import org.postgresql.fastpath.*;
+	 * ...
+	 * Fastpath fp = ((org.postgresql.Connection)myconn).getFastpathAPI();
+	 * 
+ * + *

where myconn is an open Connection to org.postgresql. + * + * @return Fastpath object allowing access to functions on the org.postgresql + * backend. + * @exception SQLException by Fastpath when initialising for first time + */ + public Fastpath getFastpathAPI() throws SQLException + { + if (fastpath == null) + fastpath = new Fastpath(this, pg_stream); + return fastpath; + } + + // This holds a reference to the Fastpath API if already open + private Fastpath fastpath = null; + + /** + * This returns the LargeObject API for the current connection. + * + *

NOTE: This is not part of JDBC, but allows access to + * functions on the org.postgresql backend itself. + * + *

The best way to use this is as follows: + * + *

+	 * import org.postgresql.largeobject.*;
+	 * ...
+	 * LargeObjectManager lo = ((org.postgresql.Connection)myconn).getLargeObjectAPI();
+	 * 
+ * + *

where myconn is an open Connection to org.postgresql. + * + * @return LargeObject object that implements the API + * @exception SQLException by LargeObject when initialising for first time + */ + public LargeObjectManager getLargeObjectAPI() throws SQLException + { + if (largeobject == null) + largeobject = new LargeObjectManager(this); + return largeobject; + } + + // This holds a reference to the LargeObject API if already open + private LargeObjectManager largeobject = null; + + /** + * This method is used internally to return an object based around + * org.postgresql's more unique data types. + * + *

It uses an internal Hashtable to get the handling class. If the + * type is not supported, then an instance of org.postgresql.util.PGobject + * is returned. + * + * You can use the getValue() or setValue() methods to handle the returned + * object. Custom objects can have their own methods. + * + * In 6.4, this is extended to use the org.postgresql.util.Serialize class to + * allow the Serialization of Java Objects into the database without using + * Blobs. Refer to that class for details on how this new feature works. + * + * @return PGobject for this type, and set to value + * @exception SQLException if value is not correct for this type + * @see org.postgresql.util.Serialize + */ + public Object getObject(String type, String value) throws SQLException + { + try + { + Object o = objectTypes.get(type); + + // If o is null, then the type is unknown, so check to see if type + // is an actual table name. If it does, see if a Class is known that + // can handle it + if (o == null) + { + Serialize ser = new Serialize(this, type); + objectTypes.put(type, ser); + return ser.fetch(Integer.parseInt(value)); + } + + // If o is not null, and it is a String, then its a class name that + // extends PGobject. + // + // This is used to implement the org.postgresql unique types (like lseg, + // point, etc). + if (o instanceof String) + { + // 6.3 style extending PG_Object + PGobject obj = null; + obj = (PGobject)(Class.forName((String)o).newInstance()); + obj.setType(type); + obj.setValue(value); + return (Object)obj; + } + else + { + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if (o instanceof Serialize) + return ((Serialize)o).fetch(Integer.parseInt(value)); + } + } + catch (SQLException sx) + { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); + throw sx; + } + catch (Exception ex) + { + throw new PSQLException("postgresql.con.creobj", type, ex); + } + + // should never be reached + return null; + } + + /** + * This stores an object into the database. + * @param o Object to store + * @return OID of the new rectord + * @exception SQLException if value is not correct for this type + * @see org.postgresql.util.Serialize + */ + public int putObject(Object o) throws SQLException + { + try + { + String type = o.getClass().getName(); + Object x = objectTypes.get(type); + + // If x is null, then the type is unknown, so check to see if type + // is an actual table name. If it does, see if a Class is known that + // can handle it + if (x == null) + { + Serialize ser = new Serialize(this, type); + objectTypes.put(type, ser); + return ser.store(o); + } + + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if (x instanceof Serialize) + return ((Serialize)x).store(o); + + // Thow an exception because the type is unknown + throw new PSQLException("postgresql.con.strobj"); + + } + catch (SQLException sx) + { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); + throw sx; + } + catch (Exception ex) + { + throw new PSQLException("postgresql.con.strobjex", ex); + } + } + + /** + * This allows client code to add a handler for one of org.postgresql's + * more unique data types. + * + *

NOTE: This is not part of JDBC, but an extension. + * + *

The best way to use this is as follows: + * + *

+	 * ...
+	 * ((org.postgresql.Connection)myconn).addDataType("mytype","my.class.name");
+	 * ...
+	 * 
+ * + *

where myconn is an open Connection to org.postgresql. + * + *

The handling class must extend org.postgresql.util.PGobject + * + * @see org.postgresql.util.PGobject + */ + public void addDataType(String type, String name) + { + objectTypes.put(type, name); + } + + // This holds the available types + private Hashtable objectTypes = new Hashtable(); + + // This array contains the types that are supported as standard. + // + // The first entry is the types name on the database, the second + // the full class name of the handling class. + // + private static final String defaultObjectTypes[][] = { + {"box", "org.postgresql.geometric.PGbox"}, + {"circle", "org.postgresql.geometric.PGcircle"}, + {"line", "org.postgresql.geometric.PGline"}, + {"lseg", "org.postgresql.geometric.PGlseg"}, + {"path", "org.postgresql.geometric.PGpath"}, + {"point", "org.postgresql.geometric.PGpoint"}, + {"polygon", "org.postgresql.geometric.PGpolygon"}, + {"money", "org.postgresql.util.PGmoney"} + }; + + // This initialises the objectTypes hashtable + private void initObjectTypes() + { + for (int i = 0;i < defaultObjectTypes.length;i++) + objectTypes.put(defaultObjectTypes[i][0], defaultObjectTypes[i][1]); + } + + // These are required by other common classes + public abstract java.sql.Statement createStatement() throws SQLException; + + /** + * This returns a resultset. It must be overridden, so that the correct + * version (from jdbc1 or jdbc2) are returned. + */ + public abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) throws SQLException; + + /** + * In some cases, it is desirable to immediately release a Connection's + * database and JDBC resources instead of waiting for them to be + * automatically released (cant think why off the top of my head) + * + * Note: A Connection is automatically closed when it is + * garbage collected. Certain fatal errors also result in a closed + * connection. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + if (pg_stream != null) + { + try + { + pg_stream.SendChar('X'); + pg_stream.flush(); + pg_stream.close(); + } + catch (IOException e) + {} + pg_stream = null; + } + } + + /** + * A driver may convert the JDBC sql grammar into its system's + * native SQL grammar prior to sending it; nativeSQL returns the + * native form of the statement that the driver would have sent. + * + * @param sql a SQL statement that may contain one or more '?' + * parameter placeholders + * @return the native form of this statement + * @exception SQLException if a database access error occurs + */ + public String nativeSQL(String sql) throws SQLException + { + return sql; + } + + /** + * The first warning reported by calls on this Connection is + * returned. + * + * Note: Sebsequent warnings will be changed to this + * SQLWarning + * + * @return the first SQLWarning or null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return firstWarning; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this connection. + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + firstWarning = null; + } + + + /** + * You can put a connection in read-only mode as a hunt to enable + * database optimizations + * + * Note: setReadOnly cannot be called while in the middle + * of a transaction + * + * @param readOnly - true enables read-only mode; false disables it + * @exception SQLException if a database access error occurs + */ + public void setReadOnly(boolean readOnly) throws SQLException + { + this.readOnly = readOnly; + } + + /** + * Tests to see if the connection is in Read Only Mode. Note that + * we cannot really put the database in read only mode, but we pretend + * we can by returning the value of the readOnly flag + * + * @return true if the connection is read only + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return readOnly; + } + + /** + * If a connection is in auto-commit mode, than all its SQL + * statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped + * into transactions that are terminated by either commit() + * or rollback(). By default, new connections are in auto- + * commit mode. The commit occurs when the statement completes + * or the next execute occurs, whichever comes first. In the + * case of statements returning a ResultSet, the statement + * completes when the last row of the ResultSet has been retrieved + * or the ResultSet has been closed. In advanced cases, a single + * statement may return multiple results as well as output parameter + * values. Here the commit occurs when all results and output param + * values have been retrieved. + * + * @param autoCommit - true enables auto-commit; false disables it + * @exception SQLException if a database access error occurs + */ + public void setAutoCommit(boolean autoCommit) throws SQLException + { + if (this.autoCommit == autoCommit) + return ; + if (autoCommit) + ExecSQL("end"); + else + { + if (haveMinimumServerVersion("7.1")) + { + ExecSQL("begin;" + getIsolationLevelSQL()); + } + else + { + ExecSQL("begin"); + ExecSQL(getIsolationLevelSQL()); + } + } + this.autoCommit = autoCommit; + } + + /** + * gets the current auto-commit state + * + * @return Current state of the auto-commit mode + * @exception SQLException (why?) + * @see setAutoCommit + */ + public boolean getAutoCommit() throws SQLException + { + return this.autoCommit; + } + + /** + * The method commit() makes all changes made since the previous + * commit/rollback permanent and releases any database locks currently + * held by the Connection. This method should only be used when + * auto-commit has been disabled. (If autoCommit == true, then we + * just return anyhow) + * + * @exception SQLException if a database access error occurs + * @see setAutoCommit + */ + public void commit() throws SQLException + { + if (autoCommit) + return ; + if (haveMinimumServerVersion("7.1")) + { + ExecSQL("commit;begin;" + getIsolationLevelSQL()); + } + else + { + ExecSQL("commit"); + ExecSQL("begin"); + ExecSQL(getIsolationLevelSQL()); + } + } + + /** + * The method rollback() drops all changes made since the previous + * commit/rollback and releases any database locks currently held by + * the Connection. + * + * @exception SQLException if a database access error occurs + * @see commit + */ + public void rollback() throws SQLException + { + if (autoCommit) + return ; + if (haveMinimumServerVersion("7.1")) + { + ExecSQL("rollback; begin;" + getIsolationLevelSQL()); + } + else + { + ExecSQL("rollback"); + ExecSQL("begin"); + ExecSQL(getIsolationLevelSQL()); + } + } + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_* mode value + * @exception SQLException if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException + { + clearWarnings(); + ExecSQL("show xactisolevel"); + + SQLWarning warning = getWarnings(); + if (warning != null) + { + String message = warning.getMessage(); + clearWarnings(); + if (message.indexOf("READ COMMITTED") != -1) + return java.sql.Connection.TRANSACTION_READ_COMMITTED; + else if (message.indexOf("READ UNCOMMITTED") != -1) + return java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; + else if (message.indexOf("REPEATABLE READ") != -1) + return java.sql.Connection.TRANSACTION_REPEATABLE_READ; + else if (message.indexOf("SERIALIZABLE") != -1) + return java.sql.Connection.TRANSACTION_SERIALIZABLE; + } + return java.sql.Connection.TRANSACTION_READ_COMMITTED; + } + + /** + * You can call this method to try to change the transaction + * isolation level using one of the TRANSACTION_* values. + * + * Note: setTransactionIsolation cannot be called while + * in the middle of a transaction + * + * @param level one of the TRANSACTION_* isolation values with + * the exception of TRANSACTION_NONE; some databases may + * not support other values + * @exception SQLException if a database access error occurs + * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel + */ + public void setTransactionIsolation(int level) throws SQLException + { + //In 7.1 and later versions of the server it is possible using + //the "set session" command to set this once for all future txns + //however in 7.0 and prior versions it is necessary to set it in + //each transaction, thus adding complexity below. + //When we decide to drop support for servers older than 7.1 + //this can be simplified + isolationLevel = level; + String isolationLevelSQL; + + if (!haveMinimumServerVersion("7.1")) + { + isolationLevelSQL = getIsolationLevelSQL(); + } + else + { + isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL "; + switch (isolationLevel) + { + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + isolationLevelSQL += "READ COMMITTED"; + break; + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + isolationLevelSQL += "SERIALIZABLE"; + break; + default: + throw new PSQLException("postgresql.con.isolevel", + new Integer(isolationLevel)); + } + } + ExecSQL(isolationLevelSQL); + } + + /** + * Helper method used by setTransactionIsolation(), commit(), rollback() + * and setAutoCommit(). This returns the SQL string needed to + * set the isolation level for a transaction. In 7.1 and later it + * is possible to set a default isolation level that applies to all + * future transactions, this method is only necesary for 7.0 and older + * servers, and should be removed when support for these older + * servers are dropped + */ + protected String getIsolationLevelSQL() throws SQLException + { + //7.1 and higher servers have a default specified so + //no additional SQL is required to set the isolation level + if (haveMinimumServerVersion("7.1")) + { + return ""; + } + StringBuffer sb = new StringBuffer("SET TRANSACTION ISOLATION LEVEL"); + + switch (isolationLevel) + { + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + sb.append(" READ COMMITTED"); + break; + + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + sb.append(" SERIALIZABLE"); + break; + + default: + throw new PSQLException("postgresql.con.isolevel", new Integer(isolationLevel)); + } + return sb.toString(); + } + + /** + * A sub-space of this Connection's database may be selected by + * setting a catalog name. If the driver does not support catalogs, + * it will silently ignore this request + * + * @exception SQLException if a database access error occurs + */ + public void setCatalog(String catalog) throws SQLException + { + //no-op + } + + /** + * Return the connections current catalog name, or null if no + * catalog name is set, or we dont support catalogs. + * + * @return the current catalog name or null + * @exception SQLException if a database access error occurs + */ + public String getCatalog() throws SQLException + { + return PG_DATABASE; + } + + /** + * Overides finalize(). If called, it closes the connection. + * + * This was done at the request of Rachel Greenham + * who hit a problem where multiple + * clients didn't close the connection, and once a fortnight enough + * clients were open to kill the org.postgres server. + */ + public void finalize() throws Throwable + { + close(); + } + + private static String extractVersionNumber(String fullVersionString) + { + StringTokenizer versionParts = new StringTokenizer(fullVersionString); + versionParts.nextToken(); /* "PostgreSQL" */ + return versionParts.nextToken(); /* "X.Y.Z" */ + } + + /** + * Get server version number + */ + public String getDBVersionNumber() + { + return dbVersionNumber; + } + + public boolean haveMinimumServerVersion(String ver) throws SQLException + { + return (getDBVersionNumber().compareTo(ver) >= 0); + } + + /** + * This method returns true if the compatible level set in the connection + * (which can be passed into the connection or specified in the URL) + * is at least the value passed to this method. This is used to toggle + * between different functionality as it changes across different releases + * of the jdbc driver code. The values here are versions of the jdbc client + * and not server versions. For example in 7.1 get/setBytes worked on + * LargeObject values, in 7.2 these methods were changed to work on bytea + * values. This change in functionality could be disabled by setting the + * "compatible" level to be 7.1, in which case the driver will revert to + * the 7.1 functionality. + */ + public boolean haveMinimumCompatibleVersion(String ver) throws SQLException + { + return (compatible.compareTo(ver) >= 0); + } + + + /** + * This returns the java.sql.Types type for a PG type oid + * + * @param oid PostgreSQL type oid + * @return the java.sql.Types type + * @exception SQLException if a database access error occurs + */ + public int getSQLType(int oid) throws SQLException + { + Integer sqlType = (Integer)typeOidCache.get(new Integer(oid)); + + // it's not in the cache, so perform a query, and add the result to the cache + if (sqlType == null) + { + ResultSet result = (org.postgresql.ResultSet)ExecSQL("select typname from pg_type where oid = " + oid); + if (result.getColumnCount() != 1 || result.getTupleCount() != 1) + throw new PSQLException("postgresql.unexpected"); + result.next(); + String pgType = result.getString(1); + Integer iOid = new Integer(oid); + sqlType = new Integer(getSQLType(result.getString(1))); + sqlTypeCache.put(iOid, sqlType); + pgTypeCache.put(iOid, pgType); + result.close(); + } + + return sqlType.intValue(); + } + + /** + * This returns the java.sql.Types type for a PG type + * + * @param pgTypeName PostgreSQL type name + * @return the java.sql.Types type + */ + public abstract int getSQLType(String pgTypeName); + + /** + * This returns the oid for a given PG data type + * @param typeName PostgreSQL type name + * @return PostgreSQL oid value for a field of this type + */ + public int getOID(String typeName) throws SQLException + { + int oid = -1; + if (typeName != null) + { + Integer oidValue = (Integer) typeOidCache.get(typeName); + if (oidValue != null) + { + oid = oidValue.intValue(); + } + else + { + // it's not in the cache, so perform a query, and add the result to the cache + ResultSet result = (org.postgresql.ResultSet)ExecSQL("select oid from pg_type where typname='" + + typeName + "'"); + if (result.getColumnCount() != 1 || result.getTupleCount() != 1) + throw new PSQLException("postgresql.unexpected"); + result.next(); + oid = Integer.parseInt(result.getString(1)); + typeOidCache.put(typeName, new Integer(oid)); + result.close(); + } + } + return oid; + } + + /** + * We also need to get the PG type name as returned by the back end. + * + * @return the String representation of the type of this field + * @exception SQLException if a database access error occurs + */ + public String getPGType(int oid) throws SQLException + { + String pgType = (String) pgTypeCache.get(new Integer(oid)); + if (pgType == null) + { + getSQLType(oid); + pgType = (String) pgTypeCache.get(new Integer(oid)); + } + return pgType; + } } diff --git a/src/interfaces/jdbc/org/postgresql/Field.java b/src/interfaces/jdbc/org/postgresql/Field.java index 1bbc272aa8..88507311c9 100644 --- a/src/interfaces/jdbc/org/postgresql/Field.java +++ b/src/interfaces/jdbc/org/postgresql/Field.java @@ -12,96 +12,96 @@ import org.postgresql.util.*; */ public class Field { - private int length; // Internal Length of this field - private int oid; // OID of the type - private int mod; // type modifier of this field - private String name; // Name of this field + private int length; // Internal Length of this field + private int oid; // OID of the type + private int mod; // type modifier of this field + private String name; // Name of this field - private Connection conn; // Connection Instantation + private Connection conn; // Connection Instantation - /** - * Construct a field based on the information fed to it. - * - * @param conn the connection this field came from - * @param name the name of the field - * @param oid the OID of the field - * @param len the length of the field - */ - public Field(Connection conn, String name, int oid, int length,int mod) - { - this.conn = conn; - this.name = name; - this.oid = oid; - this.length = length; - this.mod = mod; - } + /** + * Construct a field based on the information fed to it. + * + * @param conn the connection this field came from + * @param name the name of the field + * @param oid the OID of the field + * @param len the length of the field + */ + public Field(Connection conn, String name, int oid, int length, int mod) + { + this.conn = conn; + this.name = name; + this.oid = oid; + this.length = length; + this.mod = mod; + } - /** - * Constructor without mod parameter. - * - * @param conn the connection this field came from - * @param name the name of the field - * @param oid the OID of the field - * @param len the length of the field - */ - public Field(Connection conn, String name, int oid, int length) - { - this(conn,name,oid,length,0); - } + /** + * Constructor without mod parameter. + * + * @param conn the connection this field came from + * @param name the name of the field + * @param oid the OID of the field + * @param len the length of the field + */ + public Field(Connection conn, String name, int oid, int length) + { + this(conn, name, oid, length, 0); + } - /** - * @return the oid of this Field's data type - */ - public int getOID() - { - return oid; - } + /** + * @return the oid of this Field's data type + */ + public int getOID() + { + return oid; + } - /** - * @return the mod of this Field's data type - */ - public int getMod() - { - return mod; - } + /** + * @return the mod of this Field's data type + */ + public int getMod() + { + return mod; + } - /** - * @return the name of this Field's data type - */ - public String getName() - { - return name; - } + /** + * @return the name of this Field's data type + */ + public String getName() + { + return name; + } - /** - * @return the length of this Field's data type - */ - public int getLength() - { - return length; - } + /** + * @return the length of this Field's data type + */ + public int getLength() + { + return length; + } - /** - * We also need to get the PG type name as returned by the back end. - * - * @return the String representation of the PG type of this field - * @exception SQLException if a database access error occurs - */ - public String getPGType() throws SQLException - { - return conn.getPGType(oid); - } + /** + * We also need to get the PG type name as returned by the back end. + * + * @return the String representation of the PG type of this field + * @exception SQLException if a database access error occurs + */ + public String getPGType() throws SQLException + { + return conn.getPGType(oid); + } - /** - * We also need to get the java.sql.types type. - * - * @return the int representation of the java.sql.types type of this field - * @exception SQLException if a database access error occurs - */ - public int getSQLType() throws SQLException - { - return conn.getSQLType(oid); - } + /** + * We also need to get the java.sql.types type. + * + * @return the int representation of the java.sql.types type of this field + * @exception SQLException if a database access error occurs + */ + public int getSQLType() throws SQLException + { + return conn.getSQLType(oid); + } } diff --git a/src/interfaces/jdbc/org/postgresql/PG_Stream.java b/src/interfaces/jdbc/org/postgresql/PG_Stream.java index 71a090b0d4..049b4c5782 100644 --- a/src/interfaces/jdbc/org/postgresql/PG_Stream.java +++ b/src/interfaces/jdbc/org/postgresql/PG_Stream.java @@ -10,345 +10,366 @@ import org.postgresql.core.*; import org.postgresql.util.*; /** - * $Id: PG_Stream.java,v 1.13 2001/08/26 17:08:48 momjian Exp $ + * $Id: PG_Stream.java,v 1.14 2001/10/25 05:59:59 momjian Exp $ * * This class is used by Connection & PGlobj for communicating with the * backend. * * @see java.sql.Connection */ -// This class handles all the Streamed I/O for a org.postgresql connection +// This class handles all the Streamed I/O for a org.postgresql connection public class PG_Stream { - private Socket connection; - private InputStream pg_input; - private BufferedOutputStream pg_output; - private byte[] byte_buf = new byte[8*1024]; + private Socket connection; + private InputStream pg_input; + private BufferedOutputStream pg_output; + private byte[] byte_buf = new byte[8*1024]; - BytePoolDim1 bytePoolDim1 = new BytePoolDim1(); - BytePoolDim2 bytePoolDim2 = new BytePoolDim2(); + BytePoolDim1 bytePoolDim1 = new BytePoolDim1(); + BytePoolDim2 bytePoolDim2 = new BytePoolDim2(); - /** - * Constructor: Connect to the PostgreSQL back end and return - * a stream connection. - * - * @param host the hostname to connect to - * @param port the port number that the postmaster is sitting on - * @exception IOException if an IOException occurs below it. - */ - public PG_Stream(String host, int port) throws IOException - { - connection = new Socket(host, port); + /** + * Constructor: Connect to the PostgreSQL back end and return + * a stream connection. + * + * @param host the hostname to connect to + * @param port the port number that the postmaster is sitting on + * @exception IOException if an IOException occurs below it. + */ + public PG_Stream(String host, int port) throws IOException + { + connection = new Socket(host, port); - // Submitted by Jason Venner adds a 10x speed - // improvement on FreeBSD machines (caused by a bug in their TCP Stack) - connection.setTcpNoDelay(true); + // Submitted by Jason Venner adds a 10x speed + // improvement on FreeBSD machines (caused by a bug in their TCP Stack) + connection.setTcpNoDelay(true); - // Buffer sizes submitted by Sverre H Huseby - pg_input = new BufferedInputStream(connection.getInputStream(), 8192); - pg_output = new BufferedOutputStream(connection.getOutputStream(), 8192); - } + // Buffer sizes submitted by Sverre H Huseby + pg_input = new BufferedInputStream(connection.getInputStream(), 8192); + pg_output = new BufferedOutputStream(connection.getOutputStream(), 8192); + } - /** - * Sends a single character to the back end - * - * @param val the character to be sent - * @exception IOException if an I/O error occurs - */ - public void SendChar(int val) throws IOException - { - pg_output.write((byte)val); - } + /** + * Sends a single character to the back end + * + * @param val the character to be sent + * @exception IOException if an I/O error occurs + */ + public void SendChar(int val) throws IOException + { + pg_output.write((byte)val); + } - /** - * Sends an integer to the back end - * - * @param val the integer to be sent - * @param siz the length of the integer in bytes (size of structure) - * @exception IOException if an I/O error occurs - */ - public void SendInteger(int val, int siz) throws IOException - { - byte[] buf = bytePoolDim1.allocByte(siz); + /** + * Sends an integer to the back end + * + * @param val the integer to be sent + * @param siz the length of the integer in bytes (size of structure) + * @exception IOException if an I/O error occurs + */ + public void SendInteger(int val, int siz) throws IOException + { + byte[] buf = bytePoolDim1.allocByte(siz); - while (siz-- > 0) - { - buf[siz] = (byte)(val & 0xff); - val >>= 8; - } - Send(buf); - } + while (siz-- > 0) + { + buf[siz] = (byte)(val & 0xff); + val >>= 8; + } + Send(buf); + } - /** - * Send an array of bytes to the backend - * - * @param buf The array of bytes to be sent - * @exception IOException if an I/O error occurs - */ - public void Send(byte buf[]) throws IOException - { - pg_output.write(buf); - } + /** + * Send an array of bytes to the backend + * + * @param buf The array of bytes to be sent + * @exception IOException if an I/O error occurs + */ + public void Send(byte buf[]) throws IOException + { + pg_output.write(buf); + } - /** - * Send an exact array of bytes to the backend - if the length - * has not been reached, send nulls until it has. - * - * @param buf the array of bytes to be sent - * @param siz the number of bytes to be sent - * @exception IOException if an I/O error occurs - */ - public void Send(byte buf[], int siz) throws IOException - { - Send(buf,0,siz); - } + /** + * Send an exact array of bytes to the backend - if the length + * has not been reached, send nulls until it has. + * + * @param buf the array of bytes to be sent + * @param siz the number of bytes to be sent + * @exception IOException if an I/O error occurs + */ + public void Send(byte buf[], int siz) throws IOException + { + Send(buf, 0, siz); + } - /** - * Send an exact array of bytes to the backend - if the length - * has not been reached, send nulls until it has. - * - * @param buf the array of bytes to be sent - * @param off offset in the array to start sending from - * @param siz the number of bytes to be sent - * @exception IOException if an I/O error occurs - */ - public void Send(byte buf[], int off, int siz) throws IOException - { - int i; + /** + * Send an exact array of bytes to the backend - if the length + * has not been reached, send nulls until it has. + * + * @param buf the array of bytes to be sent + * @param off offset in the array to start sending from + * @param siz the number of bytes to be sent + * @exception IOException if an I/O error occurs + */ + public void Send(byte buf[], int off, int siz) throws IOException + { + int i; - pg_output.write(buf, off, ((buf.length-off) < siz ? (buf.length-off) : siz)); - if((buf.length-off) < siz) - { - for (i = buf.length-off ; i < siz ; ++i) - { - pg_output.write(0); - } - } - } + pg_output.write(buf, off, ((buf.length - off) < siz ? (buf.length - off) : siz)); + if ((buf.length - off) < siz) + { + for (i = buf.length - off ; i < siz ; ++i) + { + pg_output.write(0); + } + } + } - /** - * Receives a single character from the backend - * - * @return the character received - * @exception SQLException if an I/O Error returns - */ - public int ReceiveChar() throws SQLException - { - int c = 0; + /** + * Receives a single character from the backend + * + * @return the character received + * @exception SQLException if an I/O Error returns + */ + public int ReceiveChar() throws SQLException + { + int c = 0; - try - { - c = pg_input.read(); - if (c < 0) throw new PSQLException("postgresql.stream.eof"); - } catch (IOException e) { - throw new PSQLException("postgresql.stream.ioerror",e); - } - return c; - } + try + { + c = pg_input.read(); + if (c < 0) + throw new PSQLException("postgresql.stream.eof"); + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.ioerror", e); + } + return c; + } - /** - * Receives an integer from the backend - * - * @param siz length of the integer in bytes - * @return the integer received from the backend - * @exception SQLException if an I/O error occurs - */ - public int ReceiveInteger(int siz) throws SQLException - { - int n = 0; + /** + * Receives an integer from the backend + * + * @param siz length of the integer in bytes + * @return the integer received from the backend + * @exception SQLException if an I/O error occurs + */ + public int ReceiveInteger(int siz) throws SQLException + { + int n = 0; - try - { - for (int i = 0 ; i < siz ; i++) - { - int b = pg_input.read(); + try + { + for (int i = 0 ; i < siz ; i++) + { + int b = pg_input.read(); - if (b < 0) - throw new PSQLException("postgresql.stream.eof"); - n = n | (b << (8 * i)) ; - } - } catch (IOException e) { - throw new PSQLException("postgresql.stream.ioerror",e); - } - return n; - } + if (b < 0) + throw new PSQLException("postgresql.stream.eof"); + n = n | (b << (8 * i)) ; + } + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.ioerror", e); + } + return n; + } - /** - * Receives an integer from the backend - * - * @param siz length of the integer in bytes - * @return the integer received from the backend - * @exception SQLException if an I/O error occurs - */ - public int ReceiveIntegerR(int siz) throws SQLException - { - int n = 0; + /** + * Receives an integer from the backend + * + * @param siz length of the integer in bytes + * @return the integer received from the backend + * @exception SQLException if an I/O error occurs + */ + public int ReceiveIntegerR(int siz) throws SQLException + { + int n = 0; - try - { - for (int i = 0 ; i < siz ; i++) - { - int b = pg_input.read(); + try + { + for (int i = 0 ; i < siz ; i++) + { + int b = pg_input.read(); - if (b < 0) - throw new PSQLException("postgresql.stream.eof"); - n = b | (n << 8); - } - } catch (IOException e) { - throw new PSQLException("postgresql.stream.ioerror",e); - } - return n; - } + if (b < 0) + throw new PSQLException("postgresql.stream.eof"); + n = b | (n << 8); + } + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.ioerror", e); + } + return n; + } - /** - * Receives a null-terminated string from the backend. If we don't see a - * null, then we assume something has gone wrong. - * - * @param encoding the charset encoding to use. - * @return string from back end - * @exception SQLException if an I/O error occurs, or end of file - */ - public String ReceiveString(Encoding encoding) - throws SQLException - { - int s = 0; - byte[] rst = byte_buf; - try { - int buflen = rst.length; - boolean done = false; - while (!done) { - while (s < buflen) { - int c = pg_input.read(); - if (c < 0) - throw new PSQLException("postgresql.stream.eof"); - else if (c == 0) { - rst[s] = 0; - done = true; - break; - } else { - rst[s++] = (byte)c; - } - if (s >= buflen) { // Grow the buffer - buflen = (int)(buflen*2); // 100% bigger - byte[] newrst = new byte[buflen]; - System.arraycopy(rst, 0, newrst, 0, s); - rst = newrst; - } - } - } - } catch (IOException e) { - throw new PSQLException("postgresql.stream.ioerror",e); - } - return encoding.decode(rst, 0, s); - } + /** + * Receives a null-terminated string from the backend. If we don't see a + * null, then we assume something has gone wrong. + * + * @param encoding the charset encoding to use. + * @return string from back end + * @exception SQLException if an I/O error occurs, or end of file + */ + public String ReceiveString(Encoding encoding) + throws SQLException + { + int s = 0; + byte[] rst = byte_buf; + try + { + int buflen = rst.length; + boolean done = false; + while (!done) + { + while (s < buflen) + { + int c = pg_input.read(); + if (c < 0) + throw new PSQLException("postgresql.stream.eof"); + else if (c == 0) + { + rst[s] = 0; + done = true; + break; + } + else + { + rst[s++] = (byte)c; + } + if (s >= buflen) + { // Grow the buffer + buflen = (int)(buflen * 2); // 100% bigger + byte[] newrst = new byte[buflen]; + System.arraycopy(rst, 0, newrst, 0, s); + rst = newrst; + } + } + } + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.ioerror", e); + } + return encoding.decode(rst, 0, s); + } - /** - * Read a tuple from the back end. A tuple is a two dimensional - * array of bytes - * - * @param nf the number of fields expected - * @param bin true if the tuple is a binary tuple - * @return null if the current response has no more tuples, otherwise - * an array of strings - * @exception SQLException if a data I/O error occurs - */ - public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException - { - int i, bim = (nf + 7)/8; - byte[] bitmask = Receive(bim); - byte[][] answer = bytePoolDim2.allocByte(nf); + /** + * Read a tuple from the back end. A tuple is a two dimensional + * array of bytes + * + * @param nf the number of fields expected + * @param bin true if the tuple is a binary tuple + * @return null if the current response has no more tuples, otherwise + * an array of strings + * @exception SQLException if a data I/O error occurs + */ + public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException + { + int i, bim = (nf + 7) / 8; + byte[] bitmask = Receive(bim); + byte[][] answer = bytePoolDim2.allocByte(nf); - int whichbit = 0x80; - int whichbyte = 0; + int whichbit = 0x80; + int whichbyte = 0; - for (i = 0 ; i < nf ; ++i) - { - boolean isNull = ((bitmask[whichbyte] & whichbit) == 0); - whichbit >>= 1; - if (whichbit == 0) - { - ++whichbyte; - whichbit = 0x80; - } - if (isNull) - answer[i] = null; - else - { - int len = ReceiveIntegerR(4); - if (!bin) - len -= 4; - if (len < 0) - len = 0; - answer[i] = Receive(len); - } - } - return answer; - } + for (i = 0 ; i < nf ; ++i) + { + boolean isNull = ((bitmask[whichbyte] & whichbit) == 0); + whichbit >>= 1; + if (whichbit == 0) + { + ++whichbyte; + whichbit = 0x80; + } + if (isNull) + answer[i] = null; + else + { + int len = ReceiveIntegerR(4); + if (!bin) + len -= 4; + if (len < 0) + len = 0; + answer[i] = Receive(len); + } + } + return answer; + } - /** - * Reads in a given number of bytes from the backend - * - * @param siz number of bytes to read - * @return array of bytes received - * @exception SQLException if a data I/O error occurs - */ - private byte[] Receive(int siz) throws SQLException - { - byte[] answer = bytePoolDim1.allocByte(siz); - Receive(answer,0,siz); - return answer; - } + /** + * Reads in a given number of bytes from the backend + * + * @param siz number of bytes to read + * @return array of bytes received + * @exception SQLException if a data I/O error occurs + */ + private byte[] Receive(int siz) throws SQLException + { + byte[] answer = bytePoolDim1.allocByte(siz); + Receive(answer, 0, siz); + return answer; + } - /** - * Reads in a given number of bytes from the backend - * - * @param buf buffer to store result - * @param off offset in buffer - * @param siz number of bytes to read - * @exception SQLException if a data I/O error occurs - */ - public void Receive(byte[] b,int off,int siz) throws SQLException - { - int s = 0; + /** + * Reads in a given number of bytes from the backend + * + * @param buf buffer to store result + * @param off offset in buffer + * @param siz number of bytes to read + * @exception SQLException if a data I/O error occurs + */ + public void Receive(byte[] b, int off, int siz) throws SQLException + { + int s = 0; - try - { - while (s < siz) - { - int w = pg_input.read(b, off+s, siz - s); - if (w < 0) - throw new PSQLException("postgresql.stream.eof"); - s += w; - } - } catch (IOException e) { - throw new PSQLException("postgresql.stream.ioerror",e); - } - } + try + { + while (s < siz) + { + int w = pg_input.read(b, off + s, siz - s); + if (w < 0) + throw new PSQLException("postgresql.stream.eof"); + s += w; + } + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.ioerror", e); + } + } - /** - * This flushes any pending output to the backend. It is used primarily - * by the Fastpath code. - * @exception SQLException if an I/O error occurs - */ - public void flush() throws SQLException - { - try { - pg_output.flush(); - } catch (IOException e) { - throw new PSQLException("postgresql.stream.flush",e); - } - } + /** + * This flushes any pending output to the backend. It is used primarily + * by the Fastpath code. + * @exception SQLException if an I/O error occurs + */ + public void flush() throws SQLException + { + try + { + pg_output.flush(); + } + catch (IOException e) + { + throw new PSQLException("postgresql.stream.flush", e); + } + } - /** - * Closes the connection - * - * @exception IOException if a IO Error occurs - */ - public void close() throws IOException - { - pg_output.close(); - pg_input.close(); - connection.close(); - } + /** + * Closes the connection + * + * @exception IOException if a IO Error occurs + */ + public void close() throws IOException + { + pg_output.close(); + pg_input.close(); + connection.close(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/PostgresqlDataSource.java b/src/interfaces/jdbc/org/postgresql/PostgresqlDataSource.java index 62ee87ae9f..26dddfbf3d 100644 --- a/src/interfaces/jdbc/org/postgresql/PostgresqlDataSource.java +++ b/src/interfaces/jdbc/org/postgresql/PostgresqlDataSource.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: PostgresqlDataSource.java,v 1.2 2000/11/10 22:06:26 momjian Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: PostgresqlDataSource.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ +*/ package org.postgresql; @@ -79,13 +79,13 @@ import org.postgresql.xa.XADataSourceImpl; *

* The supported data source properties are: *

- * description         (optional)
- * databaseName        (required)
- * loginTimeout        (optional)
- * user                (optional)
- * password            (optional)
- * serverName          (optional)
- * portNumber          (optional)
+ * description		   (optional)
+ * databaseName		   (required)
+ * loginTimeout		   (optional)
+ * user				   (optional)
+ * password			   (optional)
+ * serverName		   (optional)
+ * portNumber		   (optional)
  * transactionTimeout  (optional for XA connections)
  * 
* This data source may be serialized and stored in a JNDI @@ -93,7 +93,7 @@ import org.postgresql.xa.XADataSourceImpl; * register it with JNDI: *
  * PostgresqlDataSource ds;
- * InitialContext       ctx;
+ * InitialContext		ctx;
  *
  * ds = new PostgresqlDataSource();
  * ds.setDatabaseName( "test" );
@@ -105,9 +105,9 @@ import org.postgresql.xa.XADataSourceImpl;
  * Example for obtaining the data source from JNDI and
  * opening a new connections:
  * 
- * InitialContext       ctx;
- * DataSource           ds;
- * 
+ * InitialContext		ctx;
+ * DataSource			ds;
+ *
  * ctx = new InitialContext();
  * ds = (DataSource) ctx.lookup( "/comp/jdbc/test" );
  * ds.getConnection();
@@ -121,482 +121,502 @@ import org.postgresql.xa.XADataSourceImpl;
  * @see Connection
  */
 public class PostgresqlDataSource
-    extends XADataSourceImpl
-    implements DataSource, Referenceable,
-	       ObjectFactory, Serializable
+			extends XADataSourceImpl
+			implements DataSource, Referenceable,
+			ObjectFactory, Serializable
 {
 
 
-    /**
-     * Holds the timeout for opening a new connection, specified
-     * in seconds. The default is obtained from the JDBC driver.
-     */
-    private int _loginTimeout;
+	/**
+	 * Holds the timeout for opening a new connection, specified
+	 * in seconds. The default is obtained from the JDBC driver.
+	 */
+	private int _loginTimeout;
 
 
-    /**
-     * Holds the user's account name.
-     */
-    private String _user;
+	/**
+	 * Holds the user's account name.
+	 */
+	private String _user;
 
 
-    /**
-     * Holds the database password.
-     */
-    private String _password;
+	/**
+	 * Holds the database password.
+	 */
+	private String _password;
 
 
-    /**
-     * Holds the name of the particular database on the server.
-     */
-    private String _databaseName;
+	/**
+	 * Holds the name of the particular database on the server.
+	 */
+	private String _databaseName;
 
 
-    /**
-     * Description of this datasource.
-     */
-    private String _description = "PostgreSQL DataSource";
+	/**
+	 * Description of this datasource.
+	 */
+	private String _description = "PostgreSQL DataSource";
 
 
-    /**
-     * Holds the database server name. If null, this is
-     * assumed to be the localhost.
-     */
-    private String _serverName;
+	/**
+	 * Holds the database server name. If null, this is
+	 * assumed to be the localhost.
+	 */
+	private String _serverName;
 
 
-    /**
-     * Holds the port number where a server is listening.
-     * The default value will open a connection with an
-     * unspecified port.
-     */
-    private int _portNumber = DEFAULT_PORT;
+	/**
+	 * Holds the port number where a server is listening.
+	 * The default value will open a connection with an
+	 * unspecified port.
+	 */
+	private int _portNumber = DEFAULT_PORT;
 
 
-    /**
-     * The default port number. Since we open the connection
-     * without specifying the port if it's the default one,
-     * this value can be meaningless.
-     */
-    private static final int DEFAULT_PORT = 0;
+	/**
+	 * The default port number. Since we open the connection
+	 * without specifying the port if it's the default one,
+	 * this value can be meaningless.
+	 */
+	private static final int DEFAULT_PORT = 0;
 
 
-    /**
-     * Holds the log writer to which all messages should be
-     * printed. The default writer is obtained from the driver
-     * manager, but it can be specified at the datasource level
-     * and will be passed to the driver. May be null.
-     */    
-    private transient PrintWriter _logWriter;
+	/**
+	 * Holds the log writer to which all messages should be
+	 * printed. The default writer is obtained from the driver
+	 * manager, but it can be specified at the datasource level
+	 * and will be passed to the driver. May be null.
+	 */
+	private transient PrintWriter _logWriter;
 
 
-    /**
-     * Each datasource maintains it's own driver, in case of
-     * driver-specific setup (e.g. pools, log writer).
-     */
-// FIXME
-//    private transient postgresql.Driver _driver;
-private transient org.postgresql.Driver _driver;
-//---------
+	/**
+	 * Each datasource maintains it's own driver, in case of
+	 * driver-specific setup (e.g. pools, log writer).
+	 */
+	// FIXME
+	//	  private transient postgresql.Driver _driver;
+	private transient org.postgresql.Driver _driver;
+	//---------
 
 
 
-    public PostgresqlDataSource()
-    {
-	_logWriter = DriverManager.getLogWriter();
-	_loginTimeout = DriverManager.getLoginTimeout();
-    }
+	public PostgresqlDataSource()
+	{
+		_logWriter = DriverManager.getLogWriter();
+		_loginTimeout = DriverManager.getLoginTimeout();
+	}
 
-    
-    public Connection getConnection()
-        throws SQLException
-    {
-	// Uses the username and password specified for the datasource.
-	return getConnection( _user, _password );
-    }
 
+	public Connection getConnection()
+	throws SQLException
+	{
+		// Uses the username and password specified for the datasource.
+		return getConnection( _user, _password );
+	}
 
-    public synchronized Connection getConnection( String user, String password )
-        throws SQLException
-    {
-	Connection conn;
-	Properties info;
-	String     url;
 
-	if ( _driver == null ) {
-	    try {
-		// Constructs a driver for use just by this data source
-		// which will produce TwoPhaseConnection-s. This driver
-		// is not registered with the driver manager.
-// FIXME
-//		_driver = new postgresql.Driver();
-_driver = new org.postgresql.Driver();
-//-----------
-
-//FIXME
-//		_driver.setLogWriter( _logWriter );
-// Method seems to be unavailable. Just commented it out.
-//----------
-	    } catch ( SQLException except ) {
-		if ( _logWriter != null )
-		    _logWriter.println( "DataSource: Failed to initialize JDBC driver: " + except );
-		throw except;
-	    }
+	public synchronized Connection getConnection( String user, String password )
+	throws SQLException
+	{
+		Connection conn;
+		Properties info;
+		String url;
+
+		if ( _driver == null )
+		{
+			try
+			{
+				// Constructs a driver for use just by this data source
+				// which will produce TwoPhaseConnection-s. This driver
+				// is not registered with the driver manager.
+				// FIXME
+				//		_driver = new postgresql.Driver();
+				_driver = new org.postgresql.Driver();
+				//-----------
+
+				//FIXME
+				//		_driver.setLogWriter( _logWriter );
+				// Method seems to be unavailable. Just commented it out.
+				//----------
+			}
+			catch ( SQLException except )
+			{
+				if ( _logWriter != null )
+					_logWriter.println( "DataSource: Failed to initialize JDBC driver: " + except );
+				throw except;
+			}
+		}
+
+		// Use info to supply properties that are not in the URL.
+		info = new Properties();
+		info.put( "loginTimeout", Integer.toString( _loginTimeout ) );
+
+		// DriverManager will do that and not rely on the URL alone.
+		if ( user == null )
+		{
+			user = _user;
+			password = _password;
+		}
+		if ( user == null || password == null )
+			throw new PSQLException( "postgresql.ds.userpswd" );
+		info.put( "user", user );
+		info.put( "password", password );
+
+		if ( _serverName != null )
+			info.put( "PGHOST", _serverName );
+		if ( _portNumber != DEFAULT_PORT )
+			info.put( "PGPORT", Integer.toString( _portNumber ) );
+		if ( _databaseName != null )
+			info.put( "PGDBNAME", _databaseName );
+
+		// Construct the URL suitable for this driver.
+		url = "jdbc:postgresql:";
+
+		// Attempt to establish a connection. Report a successful
+		// attempt or a failure.
+		try
+		{
+			conn = _driver.connect( url, info );
+			// FIXME
+			//		if ( ! ( conn instanceof postgresql.jdbc2.Connection ) ) {
+			if ( ! ( conn instanceof org.postgresql.jdbc2.Connection ) )
+			{
+				//--------
+				if ( _logWriter != null )
+					_logWriter.println( "DataSource: JDBC 1 connections not supported" );
+				throw new PSQLException( "postgresql.ds.onlyjdbc2" );
+			}
+		}
+		catch ( SQLException except )
+		{
+			if ( _logWriter != null )
+				_logWriter.println( "DataSource: getConnection failed " + except );
+			throw except;
+		}
+		if ( conn != null && _logWriter != null )
+			_logWriter.println( "DataSource: getConnection returning " + conn );
+		return conn;
 	}
 
-	// Use info to supply properties that are not in the URL.
-	info = new Properties();
-	info.put( "loginTimeout", Integer.toString( _loginTimeout ) );
 
-	// DriverManager will do that and not rely on the URL alone.
-	if ( user == null ) {
-	    user = _user;
-	    password = _password;
+	public PrintWriter getLogWriter()
+	{
+		return _logWriter;
 	}
-	if ( user == null || password == null )
-	    throw new PSQLException( "postgresql.ds.userpswd" );
-	info.put( "user", user );
-	info.put( "password", password );
-
-        if ( _serverName != null )
-            info.put( "PGHOST", _serverName );
-        if ( _portNumber != DEFAULT_PORT )
-            info.put( "PGPORT", Integer.toString( _portNumber ) );
-        if ( _databaseName != null )
-            info.put( "PGDBNAME", _databaseName );
-
-	// Construct the URL suitable for this driver.
-	url = "jdbc:postgresql:";
-
-	// Attempt to establish a connection. Report a successful
-	// attempt or a failure.
-	try {
-	    conn = _driver.connect( url, info );
-// FIXME
-//	    if ( ! ( conn instanceof postgresql.jdbc2.Connection ) ) {
-if ( ! ( conn instanceof org.postgresql.jdbc2.Connection ) ) {
-//--------
-		if ( _logWriter != null )
-		    _logWriter.println( "DataSource: JDBC 1 connections not supported" );
-		throw new PSQLException( "postgresql.ds.onlyjdbc2" );
-	    }
-	} catch ( SQLException except ) {
-	    if ( _logWriter != null )
-		_logWriter.println( "DataSource: getConnection failed " + except );
-	    throw except;
+
+
+	public synchronized void setLogWriter( PrintWriter writer )
+	{
+		// Once a log writer has been set, we cannot set it since some
+		// thread might be conditionally accessing it right now without
+		// synchronizing.
+		if ( writer != null )
+		{
+			if ( _driver != null )
+				// FIXME
+				//		_driver.setLogWriter( writer );
+				// Method seems to be unavailable. Commented it out.
+				//----------
+				_logWriter = writer;
+		}
 	}
-	if ( conn != null && _logWriter != null )
-	    _logWriter.println( "DataSource: getConnection returning " + conn );
-	return conn;
-    }
 
 
-    public PrintWriter getLogWriter()
-    {
-	return _logWriter;
-    }
+	public void setLoginTimeout( int seconds )
+	{
+		_loginTimeout = seconds;
+	}
 
 
-    public synchronized void setLogWriter( PrintWriter writer )
-    {
-	// Once a log writer has been set, we cannot set it since some
-	// thread might be conditionally accessing it right now without
-	// synchronizing.
-	if ( writer != null ) {
-	    if ( _driver != null )
-// FIXME
-//		_driver.setLogWriter( writer );
-// Method seems to be unavailable. Commented it out.
-//----------
-	    _logWriter = writer;
+	public synchronized int getLoginTimeout()
+	{
+		return _loginTimeout;
 	}
-    }
-
-
-    public void setLoginTimeout( int seconds )
-    {
-	_loginTimeout = seconds;
-    }
-
-
-    public synchronized int getLoginTimeout()
-    {
-	return _loginTimeout;
-    }
-
-
-    /**
-     * Sets the name of the particular database on the server.
-     * The standard name for this property is databaseName.
-     *
-     * @param databaseName The name of the particular database on the server
-     */
-    public synchronized void setDatabaseName( String databaseName )
-    {
-	if ( databaseName == null )
-	    throw new NullPointerException( "DataSource: Argument 'databaseName' is null" );
-	_databaseName = databaseName;
-    }
-
-
-    /**
-     * Returns the name of the particular database on the server.
-     * The standard name for this property is databaseName.
-     *
-     * @return The name of the particular database on the server
-     */
-    public String getDatabaseName()
-    {
-	return _databaseName;
-    }
-
-
-    /**
-     * Sets the description of this datasource.
-     * The standard name for this property is description.
-     *
-     * @param description The description of this datasource
-     */
-    public synchronized void setDescription( String description )
-    {
-	if ( description == null )
-	    throw new NullPointerException( "DataSource: Argument 'description' is null" );
-	_description = description;
-    }
-
-
-    /**
-     * Returns the description of this datasource.
-     * The standard name for this property is description.
-     *
-     * @return The description of this datasource
-     */
-    public String getDescription()
-    {
-	return _description;
-    }
-
-
-    /**
-     * Sets the database password.
-     * The standard name for this property is password.
-     *
-     * @param password The database password
-     */
-    public synchronized void setPassword( String password )
-    {
-	_password = password;
-    }
-
-
-    /**
-     * Returns the database password.
-     * The standard name for this property is password.
-     *
-     * @return The database password
-     */
-    public String getPassword()
-    {
-	return _password;
-    }
-
-
-    /**
-     * Sets the port number where a server is listening.
-     * The standard name for this property is portNumber.
-     *
-     * @param portNumber The port number where a server is listening
-     */
-    public synchronized void setPortNumber( int portNumber )
-    {
-	_portNumber = portNumber;
-    }
-
-
-    /**
-     * Returns the port number where a server is listening.
-     * The standard name for this property is portNumber.
-     *
-     * @return The port number where a server is listening
-     */
-    public int getPortNumber()
-    {
-	return _portNumber;
-    }
-
-
-    /**
-     * Sets the database server name.
-
-     * The standard name for this property is serverName.
-     *
-     * @param serverName The database server name
-     */
-    public synchronized void setServerName( String serverName )
-    {
-	_serverName = serverName;
-    }
-
-
-    /**
-     * Returns the database server name.
-     * The standard name for this property is serverName.
-     *
-     * @return The database server name
-     */
-    public String getServerName()
-    {
-	return _serverName;
-    }
-
-
-    /**
-     * Sets the user's account name.
-     * The standard name for this property is user.
-     *
-     * @param user The user's account name
-     */
-    public synchronized void setUser( String user )
-    {
-	_user = user;
-    }
-
-
-    /**
-     * Returns the user's account name.
-     * The standard name for this property is user.
-     *
-     * @return The user's account name
-     */
-    public String getUser()
-    {
-	return _user;
-    }
-
-
-    /**
-     * Returns true if this datasource and the other are equal.
-     * The two datasources are equal if and only if they will produce
-     * the exact same connections. Connection properties like database
-     * name, user name, etc are comapred. Setup properties like
-     * description, log writer, etc are not compared.
-     */
-    public synchronized boolean equals( Object other )
-    {
-	if ( other == this )
-	    return true;
-	if ( other == null || ! ( other instanceof PostgresqlDataSource ) )
-	    return false;
-
-	PostgresqlDataSource with;
-
-	with = (PostgresqlDataSource) other;
-	if ( _databaseName != null && _databaseName.equals( with._databaseName ) )
-	    if ( _portNumber == with._portNumber && 
-		 ( ( _serverName == null && with._serverName == null ) ||
-		   ( _serverName != null && _serverName.equals( with._serverName ) ) ) )
-		if ( ( _user == null && with._user == null ) ||
-		     ( _user != null && _password != null && _user.equals( with._user ) &&
-		       _password.equals( with._password ) ) )
-		    return true;
-	return false;
-    }
-
-
-    public String toString()
-    {
-	if ( _description != null )
-	    return _description;
-	else {
-	    String url;
-	    
-	    url = "jdbc:postgresql:";
-	    if ( _serverName != null ) {
-		if ( _portNumber == DEFAULT_PORT )
-		    url = url + "//" + _serverName + "/";
-		else
-		    url = url + "//" + _serverName + ":" + _portNumber + "/";
-	    } else if ( _portNumber != DEFAULT_PORT )
-		url = url + "//localhost:" + _portNumber + "/";
-	    if ( _databaseName != null )
-		url = url + _databaseName;
-	    return "DataSource " + url;
+
+
+	/**
+	 * Sets the name of the particular database on the server.
+	 * The standard name for this property is databaseName.
+	 *
+	 * @param databaseName The name of the particular database on the server
+	 */
+	public synchronized void setDatabaseName( String databaseName )
+	{
+		if ( databaseName == null )
+			throw new NullPointerException( "DataSource: Argument 'databaseName' is null" );
+		_databaseName = databaseName;
+	}
+
+
+	/**
+	 * Returns the name of the particular database on the server.
+	 * The standard name for this property is databaseName.
+	 *
+	 * @return The name of the particular database on the server
+	 */
+	public String getDatabaseName()
+	{
+		return _databaseName;
+	}
+
+
+	/**
+	 * Sets the description of this datasource.
+	 * The standard name for this property is description.
+	 *
+	 * @param description The description of this datasource
+	 */
+	public synchronized void setDescription( String description )
+	{
+		if ( description == null )
+			throw new NullPointerException( "DataSource: Argument 'description' is null" );
+		_description = description;
+	}
+
+
+	/**
+	 * Returns the description of this datasource.
+	 * The standard name for this property is description.
+	 *
+	 * @return The description of this datasource
+	 */
+	public String getDescription()
+	{
+		return _description;
+	}
+
+
+	/**
+	 * Sets the database password.
+	 * The standard name for this property is password.
+	 *
+	 * @param password The database password
+	 */
+	public synchronized void setPassword( String password )
+	{
+		_password = password;
+	}
+
+
+	/**
+	 * Returns the database password.
+	 * The standard name for this property is password.
+	 *
+	 * @return The database password
+	 */
+	public String getPassword()
+	{
+		return _password;
+	}
+
+
+	/**
+	 * Sets the port number where a server is listening.
+	 * The standard name for this property is portNumber.
+	 *
+	 * @param portNumber The port number where a server is listening
+	 */
+	public synchronized void setPortNumber( int portNumber )
+	{
+		_portNumber = portNumber;
+	}
+
+
+	/**
+	 * Returns the port number where a server is listening.
+	 * The standard name for this property is portNumber.
+	 *
+	 * @return The port number where a server is listening
+	 */
+	public int getPortNumber()
+	{
+		return _portNumber;
+	}
+
+
+	/**
+	 * Sets the database server name.
+
+	 * The standard name for this property is serverName.
+	 *
+	 * @param serverName The database server name
+	 */
+	public synchronized void setServerName( String serverName )
+	{
+		_serverName = serverName;
+	}
+
+
+	/**
+	 * Returns the database server name.
+	 * The standard name for this property is serverName.
+	 *
+	 * @return The database server name
+	 */
+	public String getServerName()
+	{
+		return _serverName;
+	}
+
+
+	/**
+	 * Sets the user's account name.
+	 * The standard name for this property is user.
+	 *
+	 * @param user The user's account name
+	 */
+	public synchronized void setUser( String user )
+	{
+		_user = user;
+	}
+
+
+	/**
+	 * Returns the user's account name.
+	 * The standard name for this property is user.
+	 *
+	 * @return The user's account name
+	 */
+	public String getUser()
+	{
+		return _user;
+	}
+
+
+	/**
+	 * Returns true if this datasource and the other are equal.
+	 * The two datasources are equal if and only if they will produce
+	 * the exact same connections. Connection properties like database
+	 * name, user name, etc are comapred. Setup properties like
+	 * description, log writer, etc are not compared.
+	 */
+	public synchronized boolean equals( Object other )
+	{
+		if ( other == this )
+			return true;
+		if ( other == null || ! ( other instanceof PostgresqlDataSource ) )
+			return false;
+
+		PostgresqlDataSource with;
+
+		with = (PostgresqlDataSource) other;
+		if ( _databaseName != null && _databaseName.equals( with._databaseName ) )
+			if ( _portNumber == with._portNumber &&
+					( ( _serverName == null && with._serverName == null ) ||
+					  ( _serverName != null && _serverName.equals( with._serverName ) ) ) )
+				if ( ( _user == null && with._user == null ) ||
+						( _user != null && _password != null && _user.equals( with._user ) &&
+						  _password.equals( with._password ) ) )
+					return true;
+		return false;
 	}
-    }
-
-
-    public synchronized Reference getReference()
-    {
-	Reference ref;
-
-	// We use same object as factory.
-	ref = new Reference( getClass().getName(), getClass().getName(), null );
-	// Mandatory properties
-	ref.add( new StringRefAddr( "description", _description ) );
-	ref.add( new StringRefAddr( "databaseName", _databaseName ) );
-	ref.add( new StringRefAddr( "loginTimeout", Integer.toString( _loginTimeout ) ) );
-	// Optional properties
-	if ( _user != null )
-	    ref.add( new StringRefAddr( "user", _user ) );
-	if ( _password != null )
-	    ref.add( new StringRefAddr( "password", _password ) );
-	if ( _serverName != null )
-	    ref.add( new StringRefAddr( "serverName", _serverName ) );
-	if ( _portNumber != DEFAULT_PORT )
-	    ref.add( new StringRefAddr( "portNumber", Integer.toString( _portNumber ) ) );
-	ref.add( new StringRefAddr( "transactionTimeout", Integer.toString( getTransactionTimeout() ) ) );
- 	return ref;
-    }
-
-
-    public Object getObjectInstance( Object refObj, Name name, Context nameCtx, Hashtable env )
-        throws NamingException
-    {
-	Reference ref;
-
-	// Can only reconstruct from a reference.
-	if ( refObj instanceof Reference ) {
-	    ref = (Reference) refObj;
-	    // Make sure reference is of datasource class.
-	    if ( ref.getClassName().equals( getClass().getName() ) ) {
-
-		PostgresqlDataSource ds;
-		RefAddr              addr;
-
-		try {
-		    ds = (PostgresqlDataSource) Class.forName( ref.getClassName() ).newInstance();
-		} catch ( Exception except ) {
-		    throw new NamingException( except.toString() );
+
+
+	public String toString()
+	{
+		if ( _description != null )
+			return _description;
+		else
+		{
+			String url;
+
+			url = "jdbc:postgresql:";
+			if ( _serverName != null )
+			{
+				if ( _portNumber == DEFAULT_PORT )
+					url = url + "//" + _serverName + "/";
+				else
+					url = url + "//" + _serverName + ":" + _portNumber + "/";
+			}
+			else if ( _portNumber != DEFAULT_PORT )
+				url = url + "//localhost:" + _portNumber + "/";
+			if ( _databaseName != null )
+				url = url + _databaseName;
+			return "DataSource " + url;
 		}
+	}
+
+
+	public synchronized Reference getReference()
+	{
+		Reference ref;
+
+		// We use same object as factory.
+		ref = new Reference( getClass().getName(), getClass().getName(), null );
 		// Mandatory properties
-		ds._description = (String) ref.get( "description" ).getContent();
-		ds._databaseName = (String) ref.get( "databaseName" ).getContent();
-		ds._loginTimeout = Integer.parseInt( (String) ref.get( "loginTimeout" ).getContent() );
+		ref.add( new StringRefAddr( "description", _description ) );
+		ref.add( new StringRefAddr( "databaseName", _databaseName ) );
+		ref.add( new StringRefAddr( "loginTimeout", Integer.toString( _loginTimeout ) ) );
 		// Optional properties
-		addr = ref.get( "user" );
-		if ( addr != null )
-		    ds._user = (String) addr.getContent();
-		addr = ref.get( "password" );
-		if ( addr != null )
-		    ds._password = (String) addr.getContent();
-		addr = ref.get( "serverName" );
-		if ( addr != null )
-		    ds._serverName = (String) addr.getContent();
-		addr = ref.get( "portNumber" );
-		if ( addr != null )
-		    ds._portNumber = Integer.parseInt( (String) addr.getContent() );
-		addr = ref.get( "transactionTimeout" );
-		if ( addr != null )
-		    setTransactionTimeout( Integer.parseInt( (String) addr.getContent() ) );
-		return ds;
-
-	    } else
-		throw new NamingException( "DataSource: Reference not constructed from class " + getClass().getName() );
-	} else if ( refObj instanceof Remote )
-	    return refObj;
-	else
-	    return null;
-    }
+		if ( _user != null )
+			ref.add( new StringRefAddr( "user", _user ) );
+		if ( _password != null )
+			ref.add( new StringRefAddr( "password", _password ) );
+		if ( _serverName != null )
+			ref.add( new StringRefAddr( "serverName", _serverName ) );
+		if ( _portNumber != DEFAULT_PORT )
+			ref.add( new StringRefAddr( "portNumber", Integer.toString( _portNumber ) ) );
+		ref.add( new StringRefAddr( "transactionTimeout", Integer.toString( getTransactionTimeout() ) ) );
+		return ref;
+	}
+
+
+	public Object getObjectInstance( Object refObj, Name name, Context nameCtx, Hashtable env )
+	throws NamingException
+	{
+		Reference ref;
+
+		// Can only reconstruct from a reference.
+		if ( refObj instanceof Reference )
+		{
+			ref = (Reference) refObj;
+			// Make sure reference is of datasource class.
+			if ( ref.getClassName().equals( getClass().getName() ) )
+			{
+
+				PostgresqlDataSource ds;
+				RefAddr addr;
+
+				try
+				{
+					ds = (PostgresqlDataSource) Class.forName( ref.getClassName() ).newInstance();
+				}
+				catch ( Exception except )
+				{
+					throw new NamingException( except.toString() );
+				}
+				// Mandatory properties
+				ds._description = (String) ref.get( "description" ).getContent();
+				ds._databaseName = (String) ref.get( "databaseName" ).getContent();
+				ds._loginTimeout = Integer.parseInt( (String) ref.get( "loginTimeout" ).getContent() );
+				// Optional properties
+				addr = ref.get( "user" );
+				if ( addr != null )
+					ds._user = (String) addr.getContent();
+				addr = ref.get( "password" );
+				if ( addr != null )
+					ds._password = (String) addr.getContent();
+				addr = ref.get( "serverName" );
+				if ( addr != null )
+					ds._serverName = (String) addr.getContent();
+				addr = ref.get( "portNumber" );
+				if ( addr != null )
+					ds._portNumber = Integer.parseInt( (String) addr.getContent() );
+				addr = ref.get( "transactionTimeout" );
+				if ( addr != null )
+					setTransactionTimeout( Integer.parseInt( (String) addr.getContent() ) );
+				return ds;
+
+			}
+			else
+				throw new NamingException( "DataSource: Reference not constructed from class " + getClass().getName() );
+		}
+		else if ( refObj instanceof Remote )
+			return refObj;
+		else
+			return null;
+	}
 
 
 }
diff --git a/src/interfaces/jdbc/org/postgresql/ResultSet.java b/src/interfaces/jdbc/org/postgresql/ResultSet.java
index 3274b1eb38..9febb88613 100644
--- a/src/interfaces/jdbc/org/postgresql/ResultSet.java
+++ b/src/interfaces/jdbc/org/postgresql/ResultSet.java
@@ -15,198 +15,201 @@ import org.postgresql.util.*;
  */
 public abstract class ResultSet
 {
-  protected Vector rows;			// The results
-  protected Field fields[];		// The field descriptions
-  protected String status;		// Status of the result
-  protected boolean binaryCursor = false; // is the data binary or Strings
-  protected int updateCount;		// How many rows did we get back?
-  protected int insertOID;		// The oid of an inserted row
-  protected int current_row;		// Our pointer to where we are at
-  protected byte[][] this_row;		// the current row result
-  protected Connection connection;	// the connection which we returned from
-  protected SQLWarning warnings = null;	// The warning chain
-  protected boolean wasNullFlag = false;	// the flag for wasNull()
-
-  //  We can chain multiple resultSets together - this points to
-  // next resultSet in the chain.
-  protected ResultSet next = null;
-
-  /**
-   * Create a new ResultSet - Note that we create ResultSets to
-   * represent the results of everything.
-   *
-   * @param fields an array of Field objects (basically, the
-   *	ResultSet MetaData)
-   * @param tuples Vector of the actual data
-   * @param status the status string returned from the back end
-   * @param updateCount the number of rows affected by the operation
-   * @param cursor the positioned update/delete cursor name
-   */
-  public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount,int insertOID, boolean binaryCursor)
-  {
-    this.connection = conn;
-    this.fields = fields;
-    this.rows = tuples;
-    this.status = status;
-    this.updateCount = updateCount;
-    this.insertOID = insertOID;
-    this.this_row = null;
-    this.current_row = -1;
-    this.binaryCursor = binaryCursor;
-  }
-
-
-  /**
-   * Create a new ResultSet - Note that we create ResultSets to
-   * represent the results of everything.
-   *
-   * @param fields an array of Field objects (basically, the
-   *	ResultSet MetaData)
-   * @param tuples Vector of the actual data
-   * @param status the status string returned from the back end
-   * @param updateCount the number of rows affected by the operation
-   * @param cursor the positioned update/delete cursor name
-   */
-   public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount)
-   {
-       this(conn,fields,tuples,status,updateCount,0,false);
-   }
-
-  /**
-   * We at times need to know if the resultSet we are working
-   * with is the result of an UPDATE, DELETE or INSERT (in which
-   * case, we only have a row count), or of a SELECT operation
-   * (in which case, we have multiple fields) - this routine
-   * tells us.
-   *
-   * @return true if we have tuples available
-   */
-  public boolean reallyResultSet()
-  {
-    return (fields != null);
-  }
-
-  /**
-   * Since ResultSets can be chained, we need some method of
-   * finding the next one in the chain.  The method getNext()
-   * returns the next one in the chain.
-   *
-   * @return the next ResultSet, or null if there are none
-   */
-  public java.sql.ResultSet getNext()
-  {
-    return (java.sql.ResultSet)next;
-  }
-
-  /**
-   * This following method allows us to add a ResultSet object
-   * to the end of the current chain.
-   *
-   * @param r the resultset to add to the end of the chain.
-   */
-  public void append(ResultSet r)
-  {
-    if (next == null)
-      next = r;
-    else
-      next.append(r);
-  }
-
-  /**
-   * If we are just a place holder for results, we still need
-   * to get an updateCount.  This method returns it.
-   *
-   * @return the updateCount
-   */
-  public int getResultCount()
-  {
-    return updateCount;
-  }
-
-  /**
-   * We also need to provide a couple of auxiliary functions for
-   * the implementation of the ResultMetaData functions.  In
-   * particular, we need to know the number of rows and the
-   * number of columns.  Rows are also known as Tuples
-   *
-   * @return the number of rows
-   */
-  public int getTupleCount()
-  {
-    return rows.size();
-  }
-
-  /**
-   * getColumnCount returns the number of columns
-   *
-   * @return the number of columns
-   */
-  public int getColumnCount()
-  {
-    return fields.length;
-  }
-
-   /**
-    * Returns the status message from the backend.

- * It is used internally by the driver. - * - * @return the status string from the backend - */ - public String getStatusString() - { - return status; - } - - /** - * returns the OID of a field.

- * It is used internally by the driver. - * - * @param field field id - * @return the oid of that field's type - */ - public int getColumnOID(int field) - { - return fields[field-1].getOID(); - } - - /** - * returns the OID of the last inserted row - */ - public int getInsertedOID() - { - return insertOID; - } - - /** - * This is part of the JDBC API, but is required by org.postgresql.Field - */ - public abstract void close() throws SQLException; - public abstract boolean next() throws SQLException; - public abstract String getString(int i) throws SQLException; - - /** - * This is used to fix get*() methods on Money fields. It should only be - * used by those methods! - * - * It converts ($##.##) to -##.## and $##.## to ##.## - */ - public String getFixedString(int col) throws SQLException { - String s = getString(col); - - // Handle SQL Null - wasNullFlag = (this_row[col - 1] == null); - if(wasNullFlag) - return null; - - // Handle Money - if(s.charAt(0)=='(') { - s="-"+org.postgresql.util.PGtokenizer.removePara(s).substring(1); - } - if(s.charAt(0)=='$') { - s=s.substring(1); - } - - return s; - } + protected Vector rows; // The results + protected Field fields[]; // The field descriptions + protected String status; // Status of the result + protected boolean binaryCursor = false; // is the data binary or Strings + protected int updateCount; // How many rows did we get back? + protected int insertOID; // The oid of an inserted row + protected int current_row; // Our pointer to where we are at + protected byte[][] this_row; // the current row result + protected Connection connection; // the connection which we returned from + protected SQLWarning warnings = null; // The warning chain + protected boolean wasNullFlag = false; // the flag for wasNull() + + // We can chain multiple resultSets together - this points to + // next resultSet in the chain. + protected ResultSet next = null; + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) + { + this.connection = conn; + this.fields = fields; + this.rows = tuples; + this.status = status; + this.updateCount = updateCount; + this.insertOID = insertOID; + this.this_row = null; + this.current_row = -1; + this.binaryCursor = binaryCursor; + } + + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + this(conn, fields, tuples, status, updateCount, 0, false); + } + + /** + * We at times need to know if the resultSet we are working + * with is the result of an UPDATE, DELETE or INSERT (in which + * case, we only have a row count), or of a SELECT operation + * (in which case, we have multiple fields) - this routine + * tells us. + * + * @return true if we have tuples available + */ + public boolean reallyResultSet() + { + return (fields != null); + } + + /** + * Since ResultSets can be chained, we need some method of + * finding the next one in the chain. The method getNext() + * returns the next one in the chain. + * + * @return the next ResultSet, or null if there are none + */ + public java.sql.ResultSet getNext() + { + return (java.sql.ResultSet)next; + } + + /** + * This following method allows us to add a ResultSet object + * to the end of the current chain. + * + * @param r the resultset to add to the end of the chain. + */ + public void append(ResultSet r) + { + if (next == null) + next = r; + else + next.append(r); + } + + /** + * If we are just a place holder for results, we still need + * to get an updateCount. This method returns it. + * + * @return the updateCount + */ + public int getResultCount() + { + return updateCount; + } + + /** + * We also need to provide a couple of auxiliary functions for + * the implementation of the ResultMetaData functions. In + * particular, we need to know the number of rows and the + * number of columns. Rows are also known as Tuples + * + * @return the number of rows + */ + public int getTupleCount() + { + return rows.size(); + } + + /** + * getColumnCount returns the number of columns + * + * @return the number of columns + */ + public int getColumnCount() + { + return fields.length; + } + + /** + * Returns the status message from the backend.

+ * It is used internally by the driver. + * + * @return the status string from the backend + */ + public String getStatusString() + { + return status; + } + + /** + * returns the OID of a field.

+ * It is used internally by the driver. + * + * @param field field id + * @return the oid of that field's type + */ + public int getColumnOID(int field) + { + return fields[field -1].getOID(); + } + + /** + * returns the OID of the last inserted row + */ + public int getInsertedOID() + { + return insertOID; + } + + /** + * This is part of the JDBC API, but is required by org.postgresql.Field + */ + public abstract void close() throws SQLException; + public abstract boolean next() throws SQLException; + public abstract String getString(int i) throws SQLException; + + /** + * This is used to fix get*() methods on Money fields. It should only be + * used by those methods! + * + * It converts ($##.##) to -##.## and $##.## to ##.## + */ + public String getFixedString(int col) throws SQLException + { + String s = getString(col); + + // Handle SQL Null + wasNullFlag = (this_row[col - 1] == null); + if (wasNullFlag) + return null; + + // Handle Money + if (s.charAt(0) == '(') + { + s = "-" + org.postgresql.util.PGtokenizer.removePara(s).substring(1); + } + if (s.charAt(0) == '$') + { + s = s.substring(1); + } + + return s; + } } diff --git a/src/interfaces/jdbc/org/postgresql/Statement.java b/src/interfaces/jdbc/org/postgresql/Statement.java index 6acdd8bfa5..2b201c8af4 100644 --- a/src/interfaces/jdbc/org/postgresql/Statement.java +++ b/src/interfaces/jdbc/org/postgresql/Statement.java @@ -23,284 +23,299 @@ import org.postgresql.util.PSQLException; * JDBC3. */ -public abstract class Statement { - - /** The warnings chain. */ - protected SQLWarning warnings = null; - - /** The current results */ - protected java.sql.ResultSet result = null; - - /** Maximum number of rows to return, 0 = unlimited */ - protected int maxrows = 0; - - /** Timeout (in seconds) for a query (not used) */ - protected int timeout = 0; - - protected boolean escapeProcessing = true; - - // Static variables for parsing SQL when escapeProcessing is true. - private static final short IN_SQLCODE = 0; - private static final short IN_STRING = 1; - private static final short BACKSLASH =2; - private static final short ESC_TIMEDATE = 3; - - public Statement() { - } - - /** - * Returns the status message from the current Result.

- * This is used internally by the driver. - * - * @return status message from backend - */ - public String getResultStatusString() { - if (result == null) - return null; - return ((org.postgresql.ResultSet) result).getStatusString(); - } - - /** - * The maxRows limit is set to limit the number of rows that - * any ResultSet can contain. If the limit is exceeded, the - * excess rows are silently dropped. - * - * @return the current maximum row limit; zero means unlimited - * @exception SQLException if a database access error occurs - */ - public int getMaxRows() throws SQLException { - return maxrows; - } - - /** - * Set the maximum number of rows - * - * @param max the new max rows limit; zero means unlimited - * @exception SQLException if a database access error occurs - * @see getMaxRows - */ - public void setMaxRows(int max) throws SQLException { - maxrows = max; - } - - /** - * If escape scanning is on (the default), the driver will do escape - * substitution before sending the SQL to the database. - * - * @param enable true to enable; false to disable - * @exception SQLException if a database access error occurs - */ - public void setEscapeProcessing(boolean enable) throws SQLException { - escapeProcessing = enable; - } - - /** - * The queryTimeout limit is the number of seconds the driver - * will wait for a Statement to execute. If the limit is - * exceeded, a SQLException is thrown. - * - * @return the current query timeout limit in seconds; 0 = unlimited - * @exception SQLException if a database access error occurs - */ - public int getQueryTimeout() throws SQLException { - return timeout; - } - - /** - * Sets the queryTimeout limit - * - * @param seconds - the new query timeout limit in seconds - * @exception SQLException if a database access error occurs - */ - public void setQueryTimeout(int seconds) throws SQLException { - timeout = seconds; - } - - /** - * The first warning reported by calls on this Statement is - * returned. A Statement's execute methods clear its SQLWarning - * chain. Subsequent Statement warnings will be chained to this - * SQLWarning. - * - *

The Warning chain is automatically cleared each time a statement - * is (re)executed. - * - *

Note: If you are processing a ResultSet then any warnings - * associated with ResultSet reads will be chained on the ResultSet - * object. - * - * @return the first SQLWarning on null - * @exception SQLException if a database access error occurs - */ - public SQLWarning getWarnings() throws SQLException { - return warnings; - } - - /** - * The maxFieldSize limit (in bytes) is the maximum amount of - * data returned for any column value; it only applies to - * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR - * columns. If the limit is exceeded, the excess data is silently - * discarded. - * - * @return the current max column size limit; zero means unlimited - * @exception SQLException if a database access error occurs - */ - public int getMaxFieldSize() throws SQLException { - return 8192; // We cannot change this - } - - /** - * Sets the maxFieldSize - NOT! - We throw an SQLException just - * to inform them to stop doing this. - * - * @param max the new max column size limit; zero means unlimited - * @exception SQLException if a database access error occurs - */ - public void setMaxFieldSize(int max) throws SQLException { - throw new PSQLException("postgresql.stat.maxfieldsize"); - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this Statement. - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException { - warnings = null; - } - - /** - * Cancel can be used by one thread to cancel a statement that - * is being executed by another thread. - *

- * Not implemented, this method is a no-op. - * - * @exception SQLException only because thats the spec. - */ - public void cancel() throws SQLException { - // FIXME: Cancel feature has been available since 6.4. Implement it here! - } - - /** - * New in 7.1: Returns the Last inserted oid. This should be used, rather - * than the old method using getResultSet, which for executeUpdate returns - * null. - * @return OID of last insert - */ - public int getInsertedOID() throws SQLException { - if (result == null) - return 0; - return ((org.postgresql.ResultSet) result).getInsertedOID(); - } - - /** - * getResultSet returns the current result as a ResultSet. It - * should only be called once per result. - * - * @return the current result set; null if there are no more - * @exception SQLException if a database access error occurs (why?) - */ - public java.sql.ResultSet getResultSet() throws SQLException { - if (result != null && ((org.postgresql.ResultSet) result).reallyResultSet()) - return result; - return null; - } - - /** - * In many cases, it is desirable to immediately release a - * Statement's database and JDBC resources instead of waiting - * for this to happen when it is automatically closed. The - * close method provides this immediate release. - * - *

Note: A Statement is automatically closed when it is - * garbage collected. When a Statement is closed, its current - * ResultSet, if one exists, is also closed. - * - * @exception SQLException if a database access error occurs (why?) - */ - public void close() throws SQLException { - // Force the ResultSet to close - java.sql.ResultSet rs = getResultSet(); - if(rs!=null) - rs.close(); - - // Disasociate it from us (For Garbage Collection) - result = null; - } - - /** - * Filter the SQL string of Java SQL Escape clauses. - * - * Currently implemented Escape clauses are those mentioned in 11.3 - * in the specification. Basically we look through the sql string for - * {d xxx}, {t xxx} or {ts xxx} in non-string sql code. When we find - * them, we just strip the escape part leaving only the xxx part. - * So, something like "select * from x where d={d '2001-10-09'}" would - * return "select * from x where d= '2001-10-09'". - */ - protected static String escapeSQL(String sql) - { - // Since escape codes can only appear in SQL CODE, we keep track - // of if we enter a string or not. - StringBuffer newsql = new StringBuffer(); - short state = IN_SQLCODE; - - int i = -1; - int len = sql.length(); - while(++i < len) - { - char c = sql.charAt(i); - switch(state) - { - case IN_SQLCODE: - if(c == '\'') // start of a string? - state = IN_STRING; - else if(c == '{') // start of an escape code? - if(i+1 < len) - { - char next = sql.charAt(i+1); - if(next == 'd') - { - state = ESC_TIMEDATE; - i++; - break; - } - else if(next == 't') - { - state = ESC_TIMEDATE; - i += (i+2 < len && sql.charAt(i+2) == 's') ? 2 : 1; - break; - } - } - newsql.append(c); - break; - - case IN_STRING: - if(c == '\'') // end of string? - state = IN_SQLCODE; - else if(c == '\\') // a backslash? - state = BACKSLASH; - - newsql.append(c); - break; - - case BACKSLASH: - state = IN_STRING; - - newsql.append(c); - break; - - case ESC_TIMEDATE: - if(c == '}') - state = IN_SQLCODE; // end of escape code. - else - newsql.append(c); - break; - } // end switch - } - - return newsql.toString(); - } +public abstract class Statement +{ + + /** The warnings chain. */ + protected SQLWarning warnings = null; + + /** The current results */ + protected java.sql.ResultSet result = null; + + /** Maximum number of rows to return, 0 = unlimited */ + protected int maxrows = 0; + + /** Timeout (in seconds) for a query (not used) */ + protected int timeout = 0; + + protected boolean escapeProcessing = true; + + // Static variables for parsing SQL when escapeProcessing is true. + private static final short IN_SQLCODE = 0; + private static final short IN_STRING = 1; + private static final short BACKSLASH = 2; + private static final short ESC_TIMEDATE = 3; + + public Statement() + {} + + /** + * Returns the status message from the current Result.

+ * This is used internally by the driver. + * + * @return status message from backend + */ + public String getResultStatusString() + { + if (result == null) + return null; + return ((org.postgresql.ResultSet) result).getStatusString(); + } + + /** + * The maxRows limit is set to limit the number of rows that + * any ResultSet can contain. If the limit is exceeded, the + * excess rows are silently dropped. + * + * @return the current maximum row limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxRows() throws SQLException + { + return maxrows; + } + + /** + * Set the maximum number of rows + * + * @param max the new max rows limit; zero means unlimited + * @exception SQLException if a database access error occurs + * @see getMaxRows + */ + public void setMaxRows(int max) throws SQLException + { + maxrows = max; + } + + /** + * If escape scanning is on (the default), the driver will do escape + * substitution before sending the SQL to the database. + * + * @param enable true to enable; false to disable + * @exception SQLException if a database access error occurs + */ + public void setEscapeProcessing(boolean enable) throws SQLException + { + escapeProcessing = enable; + } + + /** + * The queryTimeout limit is the number of seconds the driver + * will wait for a Statement to execute. If the limit is + * exceeded, a SQLException is thrown. + * + * @return the current query timeout limit in seconds; 0 = unlimited + * @exception SQLException if a database access error occurs + */ + public int getQueryTimeout() throws SQLException + { + return timeout; + } + + /** + * Sets the queryTimeout limit + * + * @param seconds - the new query timeout limit in seconds + * @exception SQLException if a database access error occurs + */ + public void setQueryTimeout(int seconds) throws SQLException + { + timeout = seconds; + } + + /** + * The first warning reported by calls on this Statement is + * returned. A Statement's execute methods clear its SQLWarning + * chain. Subsequent Statement warnings will be chained to this + * SQLWarning. + * + *

The Warning chain is automatically cleared each time a statement + * is (re)executed. + * + *

Note: If you are processing a ResultSet then any warnings + * associated with ResultSet reads will be chained on the ResultSet + * object. + * + * @return the first SQLWarning on null + * @exception SQLException if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * The maxFieldSize limit (in bytes) is the maximum amount of + * data returned for any column value; it only applies to + * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR + * columns. If the limit is exceeded, the excess data is silently + * discarded. + * + * @return the current max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public int getMaxFieldSize() throws SQLException + { + return 8192; // We cannot change this + } + + /** + * Sets the maxFieldSize - NOT! - We throw an SQLException just + * to inform them to stop doing this. + * + * @param max the new max column size limit; zero means unlimited + * @exception SQLException if a database access error occurs + */ + public void setMaxFieldSize(int max) throws SQLException + { + throw new PSQLException("postgresql.stat.maxfieldsize"); + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this Statement. + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Cancel can be used by one thread to cancel a statement that + * is being executed by another thread. + *

+ * Not implemented, this method is a no-op. + * + * @exception SQLException only because thats the spec. + */ + public void cancel() throws SQLException + { + // FIXME: Cancel feature has been available since 6.4. Implement it here! + } + + /** + * New in 7.1: Returns the Last inserted oid. This should be used, rather + * than the old method using getResultSet, which for executeUpdate returns + * null. + * @return OID of last insert + */ + public int getInsertedOID() throws SQLException + { + if (result == null) + return 0; + return ((org.postgresql.ResultSet) result).getInsertedOID(); + } + + /** + * getResultSet returns the current result as a ResultSet. It + * should only be called once per result. + * + * @return the current result set; null if there are no more + * @exception SQLException if a database access error occurs (why?) + */ + public java.sql.ResultSet getResultSet() throws SQLException + { + if (result != null && ((org.postgresql.ResultSet) result).reallyResultSet()) + return result; + return null; + } + + /** + * In many cases, it is desirable to immediately release a + * Statement's database and JDBC resources instead of waiting + * for this to happen when it is automatically closed. The + * close method provides this immediate release. + * + *

Note: A Statement is automatically closed when it is + * garbage collected. When a Statement is closed, its current + * ResultSet, if one exists, is also closed. + * + * @exception SQLException if a database access error occurs (why?) + */ + public void close() throws SQLException + { + // Force the ResultSet to close + java.sql.ResultSet rs = getResultSet(); + if (rs != null) + rs.close(); + + // Disasociate it from us (For Garbage Collection) + result = null; + } + + /** + * Filter the SQL string of Java SQL Escape clauses. + * + * Currently implemented Escape clauses are those mentioned in 11.3 + * in the specification. Basically we look through the sql string for + * {d xxx}, {t xxx} or {ts xxx} in non-string sql code. When we find + * them, we just strip the escape part leaving only the xxx part. + * So, something like "select * from x where d={d '2001-10-09'}" would + * return "select * from x where d= '2001-10-09'". + */ + protected static String escapeSQL(String sql) + { + // Since escape codes can only appear in SQL CODE, we keep track + // of if we enter a string or not. + StringBuffer newsql = new StringBuffer(); + short state = IN_SQLCODE; + + int i = -1; + int len = sql.length(); + while (++i < len) + { + char c = sql.charAt(i); + switch (state) + { + case IN_SQLCODE: + if (c == '\'') // start of a string? + state = IN_STRING; + else if (c == '{') // start of an escape code? + if (i + 1 < len) + { + char next = sql.charAt(i + 1); + if (next == 'd') + { + state = ESC_TIMEDATE; + i++; + break; + } + else if (next == 't') + { + state = ESC_TIMEDATE; + i += (i + 2 < len && sql.charAt(i + 2) == 's') ? 2 : 1; + break; + } + } + newsql.append(c); + break; + + case IN_STRING: + if (c == '\'') // end of string? + state = IN_SQLCODE; + else if (c == '\\') // a backslash? + state = BACKSLASH; + + newsql.append(c); + break; + + case BACKSLASH: + state = IN_STRING; + + newsql.append(c); + break; + + case ESC_TIMEDATE: + if (c == '}') + state = IN_SQLCODE; // end of escape code. + else + newsql.append(c); + break; + } // end switch + } + + return newsql.toString(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java index e78f46b4af..0f44104722 100644 --- a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java +++ b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java @@ -4,92 +4,98 @@ package org.postgresql.core; * A simple and efficient class to pool one dimensional byte arrays * of different sizes. */ -public class BytePoolDim1 { +public class BytePoolDim1 +{ - /** - * The maximum size of the array we manage. - */ - int maxsize = 256; - /** - * The pools not currently in use - */ - ObjectPool notusemap[] = new ObjectPool[maxsize+1]; - /** - * The pools currently in use - */ - ObjectPool inusemap[] = new ObjectPool[maxsize+1]; - /** - * - */ - byte binit[][] = new byte[maxsize+1][0]; + /** + * The maximum size of the array we manage. + */ + int maxsize = 256; + /** + * The pools not currently in use + */ + ObjectPool notusemap[] = new ObjectPool[maxsize + 1]; + /** + * The pools currently in use + */ + ObjectPool inusemap[] = new ObjectPool[maxsize + 1]; + /** + * + */ + byte binit[][] = new byte[maxsize + 1][0]; - /** - * Construct a new pool - */ - public BytePoolDim1(){ - for(int i = 0; i <= maxsize; i++){ - binit[i] = new byte[i]; - inusemap[i] = new SimpleObjectPool(); - notusemap[i] = new SimpleObjectPool(); + /** + * Construct a new pool + */ + public BytePoolDim1() + { + for (int i = 0; i <= maxsize; i++) + { + binit[i] = new byte[i]; + inusemap[i] = new SimpleObjectPool(); + notusemap[i] = new SimpleObjectPool(); + } } - } - /** - * Allocate a byte[] of a specified size and put it in the pool. If it's - * larger than maxsize then it is not pooled. - * @return the byte[] allocated - */ - public byte[] allocByte(int size) { - // for now until the bug can be removed - return new byte[size]; - /* - // Don't pool if >maxsize - if(size > maxsize){ - return new byte[size]; + /** + * Allocate a byte[] of a specified size and put it in the pool. If it's + * larger than maxsize then it is not pooled. + * @return the byte[] allocated + */ + public byte[] allocByte(int size) + { + // for now until the bug can be removed + return new byte[size]; + /* + // Don't pool if >maxsize + if(size > maxsize){ + return new byte[size]; } - ObjectPool not_usel = notusemap[size]; - ObjectPool in_usel = inusemap[size]; - byte b[] = null; + ObjectPool not_usel = notusemap[size]; + ObjectPool in_usel = inusemap[size]; + byte b[] = null; - // Fetch from the unused pool if available otherwise allocate a new - // now array - if(!not_usel.isEmpty()) { - Object o = not_usel.remove(); - b = (byte[]) o; + // Fetch from the unused pool if available otherwise allocate a new + // now array + if(!not_usel.isEmpty()) { + Object o = not_usel.remove(); + b = (byte[]) o; } else - b = new byte[size]; - in_usel.add(b); + b = new byte[size]; + in_usel.add(b); - return b; - */ - } + return b; + */ + } - /** - * Release an array - * @param b byte[] to release - */ - public void release(byte[] b) { - // If it's larger than maxsize then we don't touch it - if(b.length>maxsize) - return; + /** + * Release an array + * @param b byte[] to release + */ + public void release(byte[] b) + { + // If it's larger than maxsize then we don't touch it + if (b.length > maxsize) + return ; - ObjectPool not_usel = notusemap[b.length]; - ObjectPool in_usel = inusemap[b.length]; + ObjectPool not_usel = notusemap[b.length]; + ObjectPool in_usel = inusemap[b.length]; - in_usel.remove(b); - not_usel.add(b); - } + in_usel.remove(b); + not_usel.add(b); + } - /** - * Deallocate all - * @deprecated Real bad things happen if this is called! - */ - public void deallocate() { - //for(int i = 0; i <= maxsize; i++){ - // notusemap[i].addAll(inusemap[i]); - // inusemap[i].clear(); - //} - } + /** + * Deallocate all + * @deprecated Real bad things happen if this is called! + */ + public void deallocate() + { + //for(int i = 0; i <= maxsize; i++){ + // notusemap[i].addAll(inusemap[i]); + // inusemap[i].clear(); + //} + } } diff --git a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java index 0eb508171f..d330af40ea 100644 --- a/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java +++ b/src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java @@ -1,62 +1,69 @@ package org.postgresql.core; -public class BytePoolDim2 { - int maxsize = 32; - ObjectPool notusemap[] = new ObjectPool[maxsize+1]; - ObjectPool inusemap[] = new ObjectPool[maxsize+1]; - - public BytePoolDim2(){ - for(int i = 0; i <= maxsize; i++){ - inusemap[i] = new SimpleObjectPool(); - notusemap[i] = new SimpleObjectPool(); +public class BytePoolDim2 +{ + int maxsize = 32; + ObjectPool notusemap[] = new ObjectPool[maxsize + 1]; + ObjectPool inusemap[] = new ObjectPool[maxsize + 1]; + + public BytePoolDim2() + { + for (int i = 0; i <= maxsize; i++) + { + inusemap[i] = new SimpleObjectPool(); + notusemap[i] = new SimpleObjectPool(); + } } - } - - public byte[][] allocByte(int size){ - // For now until the bug can be removed - return new byte[size][0]; - /* - if(size > maxsize){ - return new byte[size][0]; + + public byte[][] allocByte(int size) + { + // For now until the bug can be removed + return new byte[size][0]; + /* + if(size > maxsize){ + return new byte[size][0]; } - ObjectPool not_usel = notusemap[size]; - ObjectPool in_usel = inusemap[size]; + ObjectPool not_usel = notusemap[size]; + ObjectPool in_usel = inusemap[size]; - byte b[][] = null; + byte b[][] = null; - if(!not_usel.isEmpty()) { - Object o = not_usel.remove(); - b = (byte[][]) o; + if(!not_usel.isEmpty()) { + Object o = not_usel.remove(); + b = (byte[][]) o; } else - b = new byte[size][0]; - in_usel.add(b); - return b; - */ - } - - public void release(byte[][] b){ - if(b.length > maxsize){ - return; + b = new byte[size][0]; + in_usel.add(b); + return b; + */ + } + + public void release(byte[][] b) + { + if (b.length > maxsize) + { + return ; + } + ObjectPool not_usel = notusemap[b.length]; + ObjectPool in_usel = inusemap[b.length]; + + in_usel.remove(b); + not_usel.add(b); + } + + /** + * Deallocate the object cache. + * PM 17/01/01: Commented out this code as it blows away any hope of + * multiple queries on the same connection. I'll redesign the allocation + * code to use some form of Statement context, so the buffers are per + * Statement and not per Connection/PG_Stream as it is now. + */ + public void deallocate() + { + //for(int i = 0; i <= maxsize; i++){ + // notusemap[i].addAll(inusemap[i]); + // inusemap[i].clear(); + //} } - ObjectPool not_usel = notusemap[b.length]; - ObjectPool in_usel = inusemap[b.length]; - - in_usel.remove(b); - not_usel.add(b); - } - - /** - * Deallocate the object cache. - * PM 17/01/01: Commented out this code as it blows away any hope of - * multiple queries on the same connection. I'll redesign the allocation - * code to use some form of Statement context, so the buffers are per - * Statement and not per Connection/PG_Stream as it is now. - */ - public void deallocate(){ - //for(int i = 0; i <= maxsize; i++){ - // notusemap[i].addAll(inusemap[i]); - // inusemap[i].clear(); - //} - } } diff --git a/src/interfaces/jdbc/org/postgresql/core/Encoding.java b/src/interfaces/jdbc/org/postgresql/core/Encoding.java index 1f03d8f1e1..11923aee2a 100644 --- a/src/interfaces/jdbc/org/postgresql/core/Encoding.java +++ b/src/interfaces/jdbc/org/postgresql/core/Encoding.java @@ -8,173 +8,213 @@ import org.postgresql.util.*; /** * Converts to and from the character encoding used by the backend. * - * $Id: Encoding.java,v 1.2 2001/10/16 20:07:17 barry Exp $ + * $Id: Encoding.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ */ -public class Encoding { - - private static final Encoding DEFAULT_ENCODING = new Encoding(null); - - /** - * Preferred JVM encodings for backend encodings. - */ - private static final Hashtable encodings = new Hashtable(); - - static { - //Note: this list should match the set of supported server - // encodings found in backend/util/mb/encnames.c - encodings.put("SQL_ASCII", new String[] { "ASCII", "us-ascii" }); - encodings.put("UNICODE", new String[] { "UTF-8", "UTF8" }); - encodings.put("LATIN1", new String[] { "ISO8859_1" }); - encodings.put("LATIN2", new String[] { "ISO8859_2" }); - encodings.put("LATIN3", new String[] { "ISO8859_3" }); - encodings.put("LATIN4", new String[] { "ISO8859_4" }); - encodings.put("ISO_8859_5", new String[] { "ISO8859_5" }); - encodings.put("ISO_8859_6", new String[] { "ISO8859_6" }); - encodings.put("ISO_8859_7", new String[] { "ISO8859_7" }); - encodings.put("ISO_8859_8", new String[] { "ISO8859_8" }); - encodings.put("LATIN5", new String[] { "ISO8859_9" }); - encodings.put("LATIN7", new String[] { "ISO8859_13" }); - encodings.put("LATIN9", new String[] { "ISO8859_15_FDIS" }); - encodings.put("EUC_JP", new String[] { "EUC_JP" }); - encodings.put("EUC_CN", new String[] { "EUC_CN" }); - encodings.put("EUC_KR", new String[] { "EUC_KR" }); - encodings.put("EUC_TW", new String[] { "EUC_TW" }); - encodings.put("SJIS", new String[] { "SJIS" }); - encodings.put("BIG5", new String[] { "Big5" }); - encodings.put("WIN1250", new String[] { "Cp1250" }); - encodings.put("WIN", new String[] { "Cp1251" }); - encodings.put("ALT", new String[] { "Cp866" }); - // We prefer KOI8-U, since it is a superset of KOI8-R. - encodings.put("KOI8", new String[] { "KOI8_U", "KOI8_R" }); - // If the database isn't encoding-aware then we can't have - // any preferred encodings. - encodings.put("UNKNOWN", new String[0]); - // The following encodings do not have a java equivalent - encodings.put("MULE_INTERNAL", new String[0]); - encodings.put("LATIN6", new String[0]); - encodings.put("LATIN8", new String[0]); - encodings.put("LATIN10", new String[0]); - } - - private final String encoding; - - private Encoding(String encoding) { - this.encoding = encoding; - } - - /** - * Get an Encoding for from the given database encoding and - * the encoding passed in by the user. - */ - public static Encoding getEncoding(String databaseEncoding, - String passedEncoding) - { - if (passedEncoding != null) { - if (isAvailable(passedEncoding)) { - return new Encoding(passedEncoding); - } else { +public class Encoding +{ + + private static final Encoding DEFAULT_ENCODING = new Encoding(null); + + /** + * Preferred JVM encodings for backend encodings. + */ + private static final Hashtable encodings = new Hashtable(); + + static { + //Note: this list should match the set of supported server + // encodings found in backend/util/mb/encnames.c + encodings.put("SQL_ASCII", new String[] { "ASCII", "us-ascii" }); + encodings.put("UNICODE", new String[] { "UTF-8", "UTF8" }); + encodings.put("LATIN1", new String[] { "ISO8859_1" }); + encodings.put("LATIN2", new String[] { "ISO8859_2" }); + encodings.put("LATIN3", new String[] { "ISO8859_3" }); + encodings.put("LATIN4", new String[] { "ISO8859_4" }); + encodings.put("ISO_8859_5", new String[] { "ISO8859_5" }); + encodings.put("ISO_8859_6", new String[] { "ISO8859_6" }); + encodings.put("ISO_8859_7", new String[] { "ISO8859_7" }); + encodings.put("ISO_8859_8", new String[] { "ISO8859_8" }); + encodings.put("LATIN5", new String[] { "ISO8859_9" }); + encodings.put("LATIN7", new String[] { "ISO8859_13" }); + encodings.put("LATIN9", new String[] { "ISO8859_15_FDIS" }); + encodings.put("EUC_JP", new String[] { "EUC_JP" }); + encodings.put("EUC_CN", new String[] { "EUC_CN" }); + encodings.put("EUC_KR", new String[] { "EUC_KR" }); + encodings.put("EUC_TW", new String[] { "EUC_TW" }); + encodings.put("SJIS", new String[] { "SJIS" }); + encodings.put("BIG5", new String[] { "Big5" }); + encodings.put("WIN1250", new String[] { "Cp1250" }); + encodings.put("WIN", new String[] { "Cp1251" }); + encodings.put("ALT", new String[] { "Cp866" }); + // We prefer KOI8-U, since it is a superset of KOI8-R. + encodings.put("KOI8", new String[] { "KOI8_U", "KOI8_R" }); + // If the database isn't encoding-aware then we can't have + // any preferred encodings. + encodings.put("UNKNOWN", new String[0]); + // The following encodings do not have a java equivalent + encodings.put("MULE_INTERNAL", new String[0]); + encodings.put("LATIN6", new String[0]); + encodings.put("LATIN8", new String[0]); + encodings.put("LATIN10", new String[0]); + } + + private final String encoding; + + private Encoding(String encoding) + { + this.encoding = encoding; + } + + /** + * Get an Encoding for from the given database encoding and + * the encoding passed in by the user. + */ + public static Encoding getEncoding(String databaseEncoding, + String passedEncoding) + { + if (passedEncoding != null) + { + if (isAvailable(passedEncoding)) + { + return new Encoding(passedEncoding); + } + else + { + return defaultEncoding(); + } + } + else + { + return encodingForDatabaseEncoding(databaseEncoding); + } + } + + /** + * Get an Encoding matching the given database encoding. + */ + private static Encoding encodingForDatabaseEncoding(String databaseEncoding) + { + // If the backend encoding is known and there is a suitable + // encoding in the JVM we use that. Otherwise we fall back + // to the default encoding of the JVM. + + if (encodings.containsKey(databaseEncoding)) + { + String[] candidates = (String[]) encodings.get(databaseEncoding); + for (int i = 0; i < candidates.length; i++) + { + if (isAvailable(candidates[i])) + { + return new Encoding(candidates[i]); + } + } + } return defaultEncoding(); - } - } else { - return encodingForDatabaseEncoding(databaseEncoding); } - } - - /** - * Get an Encoding matching the given database encoding. - */ - private static Encoding encodingForDatabaseEncoding(String databaseEncoding) { - // If the backend encoding is known and there is a suitable - // encoding in the JVM we use that. Otherwise we fall back - // to the default encoding of the JVM. - - if (encodings.containsKey(databaseEncoding)) { - String[] candidates = (String[]) encodings.get(databaseEncoding); - for (int i = 0; i < candidates.length; i++) { - if (isAvailable(candidates[i])) { - return new Encoding(candidates[i]); + + /** + * Name of the (JVM) encoding used. + */ + public String name() + { + return encoding; + } + + /** + * Encode a string to an array of bytes. + */ + public byte[] encode(String s) throws SQLException + { + try + { + if (encoding == null) + { + return s.getBytes(); + } + else + { + return s.getBytes(encoding); + } + } + catch (UnsupportedEncodingException e) + { + throw new PSQLException("postgresql.stream.encoding", e); + } + } + + /** + * Decode an array of bytes into a string. + */ + public String decode(byte[] encodedString, int offset, int length) throws SQLException + { + try + { + if (encoding == null) + { + return new String(encodedString, offset, length); + } + else + { + return new String(encodedString, offset, length, encoding); + } + } + catch (UnsupportedEncodingException e) + { + throw new PSQLException("postgresql.stream.encoding", e); } - } } - return defaultEncoding(); - } - - /** - * Name of the (JVM) encoding used. - */ - public String name() { - return encoding; - } - - /** - * Encode a string to an array of bytes. - */ - public byte[] encode(String s) throws SQLException { - try { - if (encoding == null) { - return s.getBytes(); - } else { - return s.getBytes(encoding); - } - } catch (UnsupportedEncodingException e) { - throw new PSQLException("postgresql.stream.encoding", e); + + /** + * Decode an array of bytes into a string. + */ + public String decode(byte[] encodedString) throws SQLException + { + return decode(encodedString, 0, encodedString.length); } - } - - /** - * Decode an array of bytes into a string. - */ - public String decode(byte[] encodedString, int offset, int length) throws SQLException { - try { - if (encoding == null) { - return new String(encodedString, offset, length); - } else { - return new String(encodedString, offset, length, encoding); - } - } catch (UnsupportedEncodingException e) { - throw new PSQLException("postgresql.stream.encoding", e); + + /** + * Get a Reader that decodes the given InputStream. + */ + public Reader getDecodingReader(InputStream in) throws SQLException + { + try + { + if (encoding == null) + { + return new InputStreamReader(in); + } + else + { + return new InputStreamReader(in, encoding); + } + } + catch (UnsupportedEncodingException e) + { + throw new PSQLException("postgresql.res.encoding", e); + } } - } - - /** - * Decode an array of bytes into a string. - */ - public String decode(byte[] encodedString) throws SQLException { - return decode(encodedString, 0, encodedString.length); - } - - /** - * Get a Reader that decodes the given InputStream. - */ - public Reader getDecodingReader(InputStream in) throws SQLException { - try { - if (encoding == null) { - return new InputStreamReader(in); - } else { - return new InputStreamReader(in, encoding); - } - } catch (UnsupportedEncodingException e) { - throw new PSQLException("postgresql.res.encoding", e); + + /** + * Get an Encoding using the default encoding for the JVM. + */ + public static Encoding defaultEncoding() + { + return DEFAULT_ENCODING; } - } - - /** - * Get an Encoding using the default encoding for the JVM. - */ - public static Encoding defaultEncoding() { - return DEFAULT_ENCODING; - } - - /** - * Test if an encoding is available in the JVM. - */ - private static boolean isAvailable(String encodingName) { - try { - "DUMMY".getBytes(encodingName); - return true; - } catch (UnsupportedEncodingException e) { - return false; + + /** + * Test if an encoding is available in the JVM. + */ + private static boolean isAvailable(String encodingName) + { + try + { + "DUMMY".getBytes(encodingName); + return true; + } + catch (UnsupportedEncodingException e) + { + return false; + } } - } } diff --git a/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java b/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java index 1e83d76be2..4c0996ba58 100644 --- a/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java +++ b/src/interfaces/jdbc/org/postgresql/core/MemoryPool.java @@ -3,16 +3,17 @@ package org.postgresql.core; /** * This interface defines the methods to access the memory pool classes. */ -public interface MemoryPool { - /** - * Allocate an array from the pool - * @return byte[] allocated - */ - public byte[] allocByte(int size); +public interface MemoryPool +{ + /** + * Allocate an array from the pool + * @return byte[] allocated + */ + public byte[] allocByte(int size); - /** - * Frees an object back to the pool - * @param o Object to release - */ - public void release(Object o); -} \ No newline at end of file + /** + * Frees an object back to the pool + * @param o Object to release + */ + public void release(Object o); +} diff --git a/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java b/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java index 5053c85c59..646f233e0c 100644 --- a/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java +++ b/src/interfaces/jdbc/org/postgresql/core/ObjectPool.java @@ -6,43 +6,44 @@ package org.postgresql.core; * other for jdk1.2+ */ -public interface ObjectPool { - /** - * Adds an object to the pool - * @param o Object to add - */ - public void add(Object o); - - /** - * Removes an object from the pool - * @param o Object to remove - */ - public void remove(Object o); - - /** - * Removes the top object from the pool - * @return Object from the top. - */ - public Object remove(); - - /** - * @return true if the pool is empty - */ - public boolean isEmpty(); - - /** - * @return the number of objects in the pool - */ - public int size(); - - /** - * Adds all objects in one pool to this one - * @param pool The pool to take the objects from - */ - public void addAll(ObjectPool pool); - - /** - * Clears the pool of all objects - */ - public void clear(); +public interface ObjectPool +{ + /** + * Adds an object to the pool + * @param o Object to add + */ + public void add(Object o); + + /** + * Removes an object from the pool + * @param o Object to remove + */ + public void remove(Object o); + + /** + * Removes the top object from the pool + * @return Object from the top. + */ + public Object remove(); + + /** + * @return true if the pool is empty + */ + public boolean isEmpty(); + + /** + * @return the number of objects in the pool + */ + public int size(); + + /** + * Adds all objects in one pool to this one + * @param pool The pool to take the objects from + */ + public void addAll(ObjectPool pool); + + /** + * Clears the pool of all objects + */ + public void clear(); } diff --git a/src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java b/src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java index 318daa84f4..1bb917d372 100644 --- a/src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java +++ b/src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java @@ -13,176 +13,195 @@ import org.postgresql.util.PSQLException; *

The lifetime of a QueryExecutor object is from sending the query * until the response has been received from the backend. * - * $Id: QueryExecutor.java,v 1.2 2001/10/09 20:47:35 barry Exp $ + * $Id: QueryExecutor.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ */ -public class QueryExecutor { - - private final String sql; - private final java.sql.Statement statement; - private final PG_Stream pg_stream; - private final org.postgresql.Connection connection; - - public QueryExecutor(String sql, - java.sql.Statement statement, - PG_Stream pg_stream, - org.postgresql.Connection connection) - throws SQLException - { - this.sql = sql; - this.statement = statement; - this.pg_stream = pg_stream; - this.connection = connection; - - if (statement != null) - maxRows = statement.getMaxRows(); - else - maxRows = 0; - } - - private Field[] fields = null; - private Vector tuples = new Vector(); - private boolean binaryCursor = false; - private String status = null; - private int update_count = 1; - private int insert_oid = 0; - private int maxRows; - - /** - * Execute a query on the backend. - */ - public java.sql.ResultSet execute() throws SQLException { - - int fqp = 0; - boolean hfr = false; - - synchronized(pg_stream) { - - sendQuery(sql); - - while (!hfr || fqp > 0) { - int c = pg_stream.ReceiveChar(); - - switch (c) - { - case 'A': // Asynchronous Notify - int pid = pg_stream.ReceiveInteger(4); - String msg = pg_stream.ReceiveString(connection.getEncoding()); - break; - case 'B': // Binary Data Transfer - receiveTuple(true); - break; - case 'C': // Command Status - receiveCommandStatus(); - - if (fields != null) - hfr = true; - else { - sendQuery(" "); - fqp++; - } - break; - case 'D': // Text Data Transfer - receiveTuple(false); - break; - case 'E': // Error Message - throw new SQLException(pg_stream.ReceiveString(connection.getEncoding())); - case 'I': // Empty Query - int t = pg_stream.ReceiveChar(); - if (t != 0) - throw new PSQLException("postgresql.con.garbled"); - - if (fqp > 0) - fqp--; - if (fqp == 0) - hfr = true; - break; - case 'N': // Error Notification - connection.addWarning(pg_stream.ReceiveString(connection.getEncoding())); - break; - case 'P': // Portal Name - String pname = pg_stream.ReceiveString(connection.getEncoding()); - break; - case 'T': // MetaData Field Description - receiveFields(); - break; - case 'Z': // backend ready for query, ignore for now :-) - break; - default: - throw new PSQLException("postgresql.con.type", - new Character((char) c)); - } - } - return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid, binaryCursor); - } - } - - /** - * Send a query to the backend. - */ - private void sendQuery(String query) throws SQLException { - try { - pg_stream.SendChar('Q'); - pg_stream.Send(connection.getEncoding().encode(query)); - pg_stream.SendChar(0); - pg_stream.flush(); - - } catch (IOException e) { - throw new PSQLException("postgresql.con.ioerror", e); - } - } - - /** - * Receive a tuple from the backend. - * - * @param isBinary set if the tuple should be treated as binary data - */ - private void receiveTuple(boolean isBinary) throws SQLException { - if (fields == null) - throw new PSQLException("postgresql.con.tuple"); - Object tuple = pg_stream.ReceiveTuple(fields.length, isBinary); - if (isBinary) binaryCursor = true; - if (maxRows == 0 || tuples.size() < maxRows) - tuples.addElement(tuple); - } - - /** - * Receive command status from the backend. - */ - private void receiveCommandStatus() throws SQLException { - - status = pg_stream.ReceiveString(connection.getEncoding()); - - try { - // Now handle the update count correctly. - if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE")) { - update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' '))); - } - if (status.startsWith("INSERT")) { - insert_oid = Integer.parseInt(status.substring(1 + status.indexOf(' '), - status.lastIndexOf(' '))); - } - } catch (NumberFormatException nfe) { - throw new PSQLException("postgresql.con.fathom", status); - } - } - - /** - * Receive the field descriptions from the back end. - */ - private void receiveFields() throws SQLException { - if (fields != null) - throw new PSQLException("postgresql.con.multres"); - - int size = pg_stream.ReceiveIntegerR(2); - fields = new Field[size]; - - for (int i = 0; i < fields.length; i++) { - String typeName = pg_stream.ReceiveString(connection.getEncoding()); - int typeOid = pg_stream.ReceiveIntegerR(4); - int typeLength = pg_stream.ReceiveIntegerR(2); - int typeModifier = pg_stream.ReceiveIntegerR(4); - fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier); - } - } +public class QueryExecutor +{ + + private final String sql; + private final java.sql.Statement statement; + private final PG_Stream pg_stream; + private final org.postgresql.Connection connection; + + public QueryExecutor(String sql, + java.sql.Statement statement, + PG_Stream pg_stream, + org.postgresql.Connection connection) + throws SQLException + { + this.sql = sql; + this.statement = statement; + this.pg_stream = pg_stream; + this.connection = connection; + + if (statement != null) + maxRows = statement.getMaxRows(); + else + maxRows = 0; + } + + private Field[] fields = null; + private Vector tuples = new Vector(); + private boolean binaryCursor = false; + private String status = null; + private int update_count = 1; + private int insert_oid = 0; + private int maxRows; + + /** + * Execute a query on the backend. + */ + public java.sql.ResultSet execute() throws SQLException + { + + int fqp = 0; + boolean hfr = false; + + synchronized (pg_stream) + { + + sendQuery(sql); + + while (!hfr || fqp > 0) + { + int c = pg_stream.ReceiveChar(); + + switch (c) + { + case 'A': // Asynchronous Notify + int pid = pg_stream.ReceiveInteger(4); + String msg = pg_stream.ReceiveString(connection.getEncoding()); + break; + case 'B': // Binary Data Transfer + receiveTuple(true); + break; + case 'C': // Command Status + receiveCommandStatus(); + + if (fields != null) + hfr = true; + else + { + sendQuery(" "); + fqp++; + } + break; + case 'D': // Text Data Transfer + receiveTuple(false); + break; + case 'E': // Error Message + throw new SQLException(pg_stream.ReceiveString(connection.getEncoding())); + case 'I': // Empty Query + int t = pg_stream.ReceiveChar(); + if (t != 0) + throw new PSQLException("postgresql.con.garbled"); + + if (fqp > 0) + fqp--; + if (fqp == 0) + hfr = true; + break; + case 'N': // Error Notification + connection.addWarning(pg_stream.ReceiveString(connection.getEncoding())); + break; + case 'P': // Portal Name + String pname = pg_stream.ReceiveString(connection.getEncoding()); + break; + case 'T': // MetaData Field Description + receiveFields(); + break; + case 'Z': // backend ready for query, ignore for now :-) + break; + default: + throw new PSQLException("postgresql.con.type", + new Character((char) c)); + } + } + return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid, binaryCursor); + } + } + + /** + * Send a query to the backend. + */ + private void sendQuery(String query) throws SQLException + { + try + { + pg_stream.SendChar('Q'); + pg_stream.Send(connection.getEncoding().encode(query)); + pg_stream.SendChar(0); + pg_stream.flush(); + + } + catch (IOException e) + { + throw new PSQLException("postgresql.con.ioerror", e); + } + } + + /** + * Receive a tuple from the backend. + * + * @param isBinary set if the tuple should be treated as binary data + */ + private void receiveTuple(boolean isBinary) throws SQLException + { + if (fields == null) + throw new PSQLException("postgresql.con.tuple"); + Object tuple = pg_stream.ReceiveTuple(fields.length, isBinary); + if (isBinary) + binaryCursor = true; + if (maxRows == 0 || tuples.size() < maxRows) + tuples.addElement(tuple); + } + + /** + * Receive command status from the backend. + */ + private void receiveCommandStatus() throws SQLException + { + + status = pg_stream.ReceiveString(connection.getEncoding()); + + try + { + // Now handle the update count correctly. + if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE")) + { + update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' '))); + } + if (status.startsWith("INSERT")) + { + insert_oid = Integer.parseInt(status.substring(1 + status.indexOf(' '), + status.lastIndexOf(' '))); + } + } + catch (NumberFormatException nfe) + { + throw new PSQLException("postgresql.con.fathom", status); + } + } + + /** + * Receive the field descriptions from the back end. + */ + private void receiveFields() throws SQLException + { + if (fields != null) + throw new PSQLException("postgresql.con.multres"); + + int size = pg_stream.ReceiveIntegerR(2); + fields = new Field[size]; + + for (int i = 0; i < fields.length; i++) + { + String typeName = pg_stream.ReceiveString(connection.getEncoding()); + int typeOid = pg_stream.ReceiveIntegerR(4); + int typeLength = pg_stream.ReceiveIntegerR(2); + int typeModifier = pg_stream.ReceiveIntegerR(4); + fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java b/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java index e9d5d9fc6d..dd5ffe85ca 100644 --- a/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java +++ b/src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java @@ -8,90 +8,99 @@ package org.postgresql.core; public class SimpleObjectPool implements ObjectPool { - // This was originally in PG_Stream but moved out to fix the major problem - // where more than one query (usually all the time) overwrote the results - // of another query. - int cursize = 0; - int maxsize = 16; - Object arr[] = new Object[maxsize]; + // This was originally in PG_Stream but moved out to fix the major problem + // where more than one query (usually all the time) overwrote the results + // of another query. + int cursize = 0; + int maxsize = 16; + Object arr[] = new Object[maxsize]; - /** - * Adds an object to the pool - * @param o Object to add - */ - public void add(Object o) - { - if(cursize >= maxsize){ - Object newarr[] = new Object[maxsize*2]; - System.arraycopy(arr, 0, newarr, 0, maxsize); - maxsize = maxsize * 2; - arr = newarr; + /** + * Adds an object to the pool + * @param o Object to add + */ + public void add(Object o) + { + if (cursize >= maxsize) + { + Object newarr[] = new Object[maxsize * 2]; + System.arraycopy(arr, 0, newarr, 0, maxsize); + maxsize = maxsize * 2; + arr = newarr; + } + arr[cursize++] = o; } - arr[cursize++] = o; - } - /** - * Removes the top object from the pool - * @return Object from the top. - */ - public Object remove(){ - return arr[--cursize]; - } + /** + * Removes the top object from the pool + * @return Object from the top. + */ + public Object remove() + { + return arr[--cursize]; + } - /** - * Removes the given object from the pool - * @param o Object to remove - */ - public void remove(Object o) { - int p=0; - while(p maxsize){ - Object newarr[] = new Object[totalsize*2]; - System.arraycopy(arr, 0, newarr, 0, cursize); - maxsize = maxsize = totalsize * 2; - arr = newarr; + int srcsize = pool.size(); + if (srcsize == 0) + return ; + int totalsize = srcsize + cursize; + if (totalsize > maxsize) + { + Object newarr[] = new Object[totalsize * 2]; + System.arraycopy(arr, 0, newarr, 0, cursize); + maxsize = maxsize = totalsize * 2; + arr = newarr; + } + System.arraycopy(pool.arr, 0, arr, cursize, srcsize); + cursize = totalsize; } - System.arraycopy(pool.arr, 0, arr, cursize, srcsize); - cursize = totalsize; - } - /** - * Clears the pool of all objects - */ - public void clear(){ - cursize = 0; - } + /** + * Clears the pool of all objects + */ + public void clear() + { + cursize = 0; + } } diff --git a/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java index a99bd0c0d1..77460b492c 100644 --- a/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java +++ b/src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java @@ -23,266 +23,273 @@ import org.postgresql.util.*; */ public class Fastpath { - // This maps the functions names to their id's (possible unique just - // to a connection). - protected Hashtable func = new Hashtable(); - - protected org.postgresql.Connection conn; // our connection - protected org.postgresql.PG_Stream stream; // the network stream - - /** - * Initialises the fastpath system - * - *

Important Notice - *
This is called from org.postgresql.Connection, and should not be called - * from client code. - * - * @param conn org.postgresql.Connection to attach to - * @param stream The network stream to the backend - */ - public Fastpath(org.postgresql.Connection conn,org.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 - { - // added Oct 7 1998 to give us thread safety - synchronized(stream) { - - // 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); + // This maps the functions names to their id's (possible unique just + // to a connection). + protected Hashtable func = new Hashtable(); - stream.SendInteger(fnid,4); - stream.SendInteger(args.length,4); - - for(int i=0;iImportant Notice + *
This is called from org.postgresql.Connection, and should not be called + * from client code. + * + * @param conn org.postgresql.Connection to attach to + * @param stream The network stream to the backend + */ + public Fastpath(org.postgresql.Connection conn, org.postgresql.PG_Stream stream) { - case 'V': - break; - - //------------------------------ - // Function returned properly - // - case 'G': - int sz = stream.ReceiveIntegerR(4); - //DriverManager.println("G: size="+sz); //debug - - // Return an Integer if - if(resulttype) - result = new Integer(stream.ReceiveIntegerR(sz)); - else { - byte buf[] = new byte[sz]; - stream.Receive(buf,0,sz); - result = buf; - } - break; - - //------------------------------ - // Error message returned - case 'E': - throw new PSQLException("postgresql.fp.error",stream.ReceiveString(conn.getEncoding())); - - //------------------------------ - // Notice from backend - case 'N': - conn.addWarning(stream.ReceiveString(conn.getEncoding())); - 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; - - case 'Z': - break; - - default: - throw new PSQLException("postgresql.fp.protocol",new Character((char)in)); + 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 + { + // added Oct 7 1998 to give us thread safety + synchronized (stream) + { + + // 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.SendInteger(fnid, 4); + stream.SendInteger(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 PSQLException("postgresql.fp.send", new Integer(fnid), 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(conn.getEncoding())); + //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.ReceiveIntegerR(4); + //DriverManager.println("G: size="+sz); //debug + + // Return an Integer if + if (resulttype) + result = new Integer(stream.ReceiveIntegerR(sz)); + else + { + byte buf[] = new byte[sz]; + stream.Receive(buf, 0, sz); + result = buf; + } + break; + + //------------------------------ + // Error message returned + case 'E': + throw new PSQLException("postgresql.fp.error", stream.ReceiveString(conn.getEncoding())); + + //------------------------------ + // Notice from backend + case 'N': + conn.addWarning(stream.ReceiveString(conn.getEncoding())); + 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; + + case 'Z': + break; + + default: + throw new PSQLException("postgresql.fp.protocol", new Character((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 org.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 org.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 PSQLException("postgresql.fp.expint", name); + 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. + * + *

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. + * + *

It reads the entire ResultSet, loading the values into the function + * table. + * + *

REMEMBER to close() the resultset after calling this!! + * + *

Implementation note about function name lookups: + * + *

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. + * + *

The org.postgresql.LargeObject class performs a query upon it's startup, + * and passes the returned ResultSet to the addFunctions() method here. + * + *

Once this has been done, the LargeObject api refers to the functions by + * name. + * + *

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 org.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 + * + *

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 PSQLException("postgresql.fp.unknown", name); + + return id.intValue(); } - } - } - } - - /** - * 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 org.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 org.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 PSQLException("postgresql.fp.expint",name); - 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. - * - *

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. - * - *

It reads the entire ResultSet, loading the values into the function - * table. - * - *

REMEMBER to close() the resultset after calling this!! - * - *

Implementation note about function name lookups: - * - *

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. - * - *

The org.postgresql.LargeObject class performs a query upon it's startup, - * and passes the returned ResultSet to the addFunctions() method here. - * - *

Once this has been done, the LargeObject api refers to the functions by - * name. - * - *

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 org.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 - * - *

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 PSQLException("postgresql.fp.unknown",name); - - return id.intValue(); - } } diff --git a/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java b/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java index 87b8475f64..7234df10d8 100644 --- a/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java +++ b/src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java @@ -22,85 +22,88 @@ import org.postgresql.util.*; */ 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. - * - *

The stream sent consists of the length.int4 then the contents. - * - *

Note: 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(org.postgresql.PG_Stream s) throws IOException - { - if(type) { - // argument is an integer - s.SendInteger(4,4); // size of an integer - s.SendInteger(value,4); // integer value of argument - } else { - // argument is a byte array - s.SendInteger(bytes.length,4); // size of array - s.Send(bytes); - } - } + /** + * 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. + * + *

The stream sent consists of the length.int4 then the contents. + * + *

Note: 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(org.postgresql.PG_Stream s) throws IOException + { + if (type) + { + // argument is an integer + s.SendInteger(4, 4); // size of an integer + s.SendInteger(value, 4); // integer value of argument + } + else + { + // argument is a byte array + s.SendInteger(bytes.length, 4); // size of array + s.Send(bytes); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGbox.java b/src/interfaces/jdbc/org/postgresql/geometric/PGbox.java index f092133ad4..baefd2ff10 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGbox.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGbox.java @@ -7,99 +7,100 @@ import org.postgresql.util.*; /** * This represents the box datatype within org.postgresql. */ -public class PGbox extends PGobject implements Serializable,Cloneable +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 PSQLException("postgresql.geo.box",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 org.postgresql - */ - public String getValue() - { - return point[0].toString()+","+point[1].toString(); - } + /** + * 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 PSQLException("postgresql.geo.box", 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 org.postgresql + */ + public String getValue() + { + return point[0].toString() + "," + point[1].toString(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGcircle.java b/src/interfaces/jdbc/org/postgresql/geometric/PGcircle.java index 9a1333e4e8..b716fa75a8 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGcircle.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGcircle.java @@ -8,101 +8,105 @@ import org.postgresql.util.*; * This represents org.postgresql's circle datatype, consisting of a point and * a radius */ -public class PGcircle extends PGobject implements Serializable,Cloneable +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 PSQLException("postgresql.geo.circle",s); - - try { - center = new PGpoint(t.getToken(0)); - radius = Double.valueOf(t.getToken(1)).doubleValue(); - } catch(NumberFormatException e) { - throw new PSQLException("postgresql.geo.circle",e); - } - } - - /** - * @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 org.postgresql - */ - public String getValue() - { - return "<"+center+","+radius+">"; - } + /** + * 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 PSQLException("postgresql.geo.circle", s); + + try + { + center = new PGpoint(t.getToken(0)); + radius = Double.valueOf(t.getToken(1)).doubleValue(); + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.geo.circle", e); + } + } + + /** + * @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 org.postgresql + */ + public String getValue() + { + return "<" + center + "," + radius + ">"; + } } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGline.java b/src/interfaces/jdbc/org/postgresql/geometric/PGline.java index 4901b6f2f1..cc2b05ecb1 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGline.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGline.java @@ -10,94 +10,95 @@ import org.postgresql.util.*; * Currently line is not yet implemented in the backend, but this class * ensures that when it's done were ready for it. */ -public class PGline extends PGobject implements Serializable,Cloneable +public class PGline 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 PGline(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 PGline(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 PGline(String s) throws SQLException - { - this(); - setValue(s); - } - - /** - * reuired by the driver - */ - public PGline() - { - setType("line"); - } - - /** - * @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 PSQLException("postgresql.geo.line",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 PGline) { - PGline p = (PGline)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 PGline((PGpoint)point[0].clone(),(PGpoint)point[1].clone()); - } - - /** - * @return the PGline in the syntax expected by org.postgresql - */ - public String getValue() - { - return "["+point[0]+","+point[1]+"]"; - } + /** + * 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 PGline(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 PGline(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 PGline(String s) throws SQLException + { + this(); + setValue(s); + } + + /** + * reuired by the driver + */ + public PGline() + { + setType("line"); + } + + /** + * @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 PSQLException("postgresql.geo.line", 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 PGline) + { + PGline p = (PGline)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 PGline((PGpoint)point[0].clone(), (PGpoint)point[1].clone()); + } + + /** + * @return the PGline in the syntax expected by org.postgresql + */ + public String getValue() + { + return "[" + point[0] + "," + point[1] + "]"; + } } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGlseg.java b/src/interfaces/jdbc/org/postgresql/geometric/PGlseg.java index ec0986963b..c63d980ffa 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGlseg.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGlseg.java @@ -7,94 +7,95 @@ import org.postgresql.util.*; /** * This implements a lseg (line segment) consisting of two points */ -public class PGlseg extends PGobject implements Serializable,Cloneable +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 PSQLException("postgresql.geo.lseg"); - - 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 org.postgresql - */ - public String getValue() - { - return "["+point[0]+","+point[1]+"]"; - } + /** + * 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 PSQLException("postgresql.geo.lseg"); + + 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 org.postgresql + */ + public String getValue() + { + return "[" + point[0] + "," + point[1] + "]"; + } } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGpath.java b/src/interfaces/jdbc/org/postgresql/geometric/PGpath.java index 2e5b4674fb..47ebfd82e3 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGpath.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGpath.java @@ -7,139 +7,146 @@ import org.postgresql.util.*; /** * This implements a path (a multiple segmented line, which may be closed) */ -public class PGpath extends PGobject implements Serializable,Cloneable +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 PSQLException("postgresql.geo.path"); - - PGtokenizer t = new PGtokenizer(s,','); - int npoints = t.getSize(); - points = new PGpoint[npoints]; - for(int p=0;p0) 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; - } - + /** + * 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 PSQLException("postgresql.geo.path"); + + 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 org.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; + } + } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGpoint.java b/src/interfaces/jdbc/org/postgresql/geometric/PGpoint.java index eeb71b2773..455e42aff4 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGpoint.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGpoint.java @@ -12,156 +12,160 @@ import org.postgresql.util.*; * *

It maps to the point datatype in org.postgresql. */ -public class PGpoint extends PGobject implements Serializable,Cloneable +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 PSQLException("postgresql.geo.point",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 org.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); - } - + /** + * 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 PSQLException("postgresql.geo.point", 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 org.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); + } + } diff --git a/src/interfaces/jdbc/org/postgresql/geometric/PGpolygon.java b/src/interfaces/jdbc/org/postgresql/geometric/PGpolygon.java index b805a1df30..12c45fd709 100644 --- a/src/interfaces/jdbc/org/postgresql/geometric/PGpolygon.java +++ b/src/interfaces/jdbc/org/postgresql/geometric/PGpolygon.java @@ -7,99 +7,102 @@ import org.postgresql.util.*; /** * This implements the polygon datatype within PostgreSQL. */ -public class PGpolygon extends PGobject implements Serializable,Cloneable +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;p0) b.append(","); - b.append(points[p].toString()); - } - b.append(")"); - return b.toString(); - } + /** + * 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 org.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(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java index be52766835..2947dfe82d 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java @@ -19,8 +19,8 @@ import java.math.*; * Parameters are refered to sequentially, by number. The first parameter * is 1. * - * {?= call [,, ...]} - * {call [,, ...]} + * {?= call [,, ...]} + * {call [,, ...]} * * *

IN parameter values are set using the set methods inherited from @@ -33,7 +33,7 @@ import java.math.*; * Statement. * *

For maximum portability, a call's ResultSets and update counts should - * be processed prior to getting the values of output parameters. + * be processed prior to getting the values of output parameters. * * @see Connection#prepareCall * @see ResultSet @@ -41,268 +41,282 @@ import java.math.*; public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement { - /** - * @exception SQLException on failure - */ - CallableStatement(Connection c,String q) throws SQLException - { - super(c,q); - } - - /** - * Before executing a stored procedure call you must explicitly - * call registerOutParameter to register the java.sql.Type of each - * out parameter. - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType SQL type code defined by java.sql.Types; for - * parameters of type Numeric or Decimal use the version of - * registerOutParameter that accepts a scale value - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { - } - - /** - * You must also specify the scale for numeric/decimal types: - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType, - int scale) throws SQLException - { - } - - // Old api? - //public boolean isNull(int parameterIndex) throws SQLException { - //return true; - //} - - /** - * An OUT parameter may have the value of SQL NULL; wasNull - * reports whether the last value read has this special value. - * - *

Note: You must first call getXXX on a parameter to read its - * value and then call wasNull() to see if the value was SQL NULL. - * @return true if the last parameter read was SQL NULL - * @exception SQLException if a database-access error occurs. - */ - public boolean wasNull() throws SQLException { - // check to see if the last access threw an exception - return false; // fake it for now - } - - // Old api? - //public String getChar(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a - * Java String. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public String getString(int parameterIndex) throws SQLException { - return null; - } - //public String getVarChar(int parameterIndex) throws SQLException { - // return null; - //} - - //public String getLongVarChar(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a BIT parameter as a Java boolean. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is false - * @exception SQLException if a database-access error occurs. - */ - public boolean getBoolean(int parameterIndex) throws SQLException { - return false; - } - - /** - * Get the value of a TINYINT parameter as a Java byte. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public byte getByte(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a SMALLINT parameter as a Java short. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public short getShort(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of an INTEGER parameter as a Java int. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ -public int getInt(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a BIGINT parameter as a Java long. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public long getLong(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a FLOAT parameter as a Java float. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public float getFloat(int parameterIndex) throws SQLException { - return (float) 0.0; - } - - /** - * Get the value of a DOUBLE parameter as a Java double. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public double getDouble(int parameterIndex) throws SQLException { - return 0.0; - } - - /** - * Get the value of a NUMERIC parameter as a java.math.BigDecimal - * object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public BigDecimal getBigDecimal(int parameterIndex, int scale) - throws SQLException { - return null; - } - - /** - * Get the value of a SQL BINARY or VARBINARY parameter as a Java - * byte[] - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public byte[] getBytes(int parameterIndex) throws SQLException { - return null; - } - - // New API (JPM) (getLongVarBinary) - //public byte[] getBinaryStream(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a SQL DATE parameter as a java.sql.Date object - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Date getDate(int parameterIndex) throws SQLException { - return null; - } - - /** - * Get the value of a SQL TIME parameter as a java.sql.Time object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Time getTime(int parameterIndex) throws SQLException { - return null; - } - - /** - * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Timestamp getTimestamp(int parameterIndex) - throws SQLException { - return null; - } - - //---------------------------------------------------------------------- - // Advanced features: - - // You can obtain a ParameterMetaData object to get information - // about the parameters to this CallableStatement. - //public DatabaseMetaData getMetaData() { - //return null; - //} - - // getObject returns a Java object for the parameter. - // See the JDBC spec's "Dynamic Programming" chapter for details. - /** - * Get the value of a parameter as a Java object. - * - *

This method returns a Java object whose type coresponds to the - * SQL type that was registered for this parameter using - * registerOutParameter. - * - *

Note that this method may be used to read datatabase-specific, - * abstract data types. This is done by specifying a targetSqlType - * of java.sql.types.OTHER, which allows the driver to return a - * database-specific Java type. - * - *

See the JDBC spec's "Dynamic Programming" chapter for details. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return A java.lang.Object holding the OUT parameter value. - * @exception SQLException if a database-access error occurs. - */ - public Object getObject(int parameterIndex) - throws SQLException { - return null; - } + /** + * @exception SQLException on failure + */ + CallableStatement(Connection c, String q) throws SQLException + { + super(c, q); + } + + /** + * Before executing a stored procedure call you must explicitly + * call registerOutParameter to register the java.sql.Type of each + * out parameter. + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType SQL type code defined by java.sql.Types; for + * parameters of type Numeric or Decimal use the version of + * registerOutParameter that accepts a scale value + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException + {} + + /** + * You must also specify the scale for numeric/decimal types: + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType, + int scale) throws SQLException + {} + + // Old api? + //public boolean isNull(int parameterIndex) throws SQLException { + //return true; + //} + + /** + * An OUT parameter may have the value of SQL NULL; wasNull + * reports whether the last value read has this special value. + * + *

Note: You must first call getXXX on a parameter to read its + * value and then call wasNull() to see if the value was SQL NULL. + * @return true if the last parameter read was SQL NULL + * @exception SQLException if a database-access error occurs. + */ + public boolean wasNull() throws SQLException + { + // check to see if the last access threw an exception + return false; // fake it for now + } + + // Old api? + //public String getChar(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a + * Java String. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public String getString(int parameterIndex) throws SQLException + { + return null; + } + //public String getVarChar(int parameterIndex) throws SQLException { + // return null; + //} + + //public String getLongVarChar(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a BIT parameter as a Java boolean. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is false + * @exception SQLException if a database-access error occurs. + */ + public boolean getBoolean(int parameterIndex) throws SQLException + { + return false; + } + + /** + * Get the value of a TINYINT parameter as a Java byte. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public byte getByte(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a SMALLINT parameter as a Java short. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public short getShort(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of an INTEGER parameter as a Java int. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public int getInt(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a BIGINT parameter as a Java long. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public long getLong(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a FLOAT parameter as a Java float. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public float getFloat(int parameterIndex) throws SQLException + { + return (float) 0.0; + } + + /** + * Get the value of a DOUBLE parameter as a Java double. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public double getDouble(int parameterIndex) throws SQLException + { + return 0.0; + } + + /** + * Get the value of a NUMERIC parameter as a java.math.BigDecimal + * object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public BigDecimal getBigDecimal(int parameterIndex, int scale) + throws SQLException + { + return null; + } + + /** + * Get the value of a SQL BINARY or VARBINARY parameter as a Java + * byte[] + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public byte[] getBytes(int parameterIndex) throws SQLException + { + return null; + } + + // New API (JPM) (getLongVarBinary) + //public byte[] getBinaryStream(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a SQL DATE parameter as a java.sql.Date object + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Date getDate(int parameterIndex) throws SQLException + { + return null; + } + + /** + * Get the value of a SQL TIME parameter as a java.sql.Time object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Time getTime(int parameterIndex) throws SQLException + { + return null; + } + + /** + * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(int parameterIndex) + throws SQLException + { + return null; + } + + //---------------------------------------------------------------------- + // Advanced features: + + // You can obtain a ParameterMetaData object to get information + // about the parameters to this CallableStatement. + //public DatabaseMetaData getMetaData() { + //return null; + //} + + // getObject returns a Java object for the parameter. + // See the JDBC spec's "Dynamic Programming" chapter for details. + /** + * Get the value of a parameter as a Java object. + * + *

This method returns a Java object whose type coresponds to the + * SQL type that was registered for this parameter using + * registerOutParameter. + * + *

Note that this method may be used to read datatabase-specific, + * abstract data types. This is done by specifying a targetSqlType + * of java.sql.types.OTHER, which allows the driver to return a + * database-specific Java type. + * + *

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ + public Object getObject(int parameterIndex) + throws SQLException + { + return null; + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java index dfdbb81827..e05bc5b625 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java @@ -17,9 +17,9 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.11 2001/10/09 20:47:35 barry Exp $ + * $Id: Connection.java,v 1.12 2001/10/25 05:59:59 momjian Exp $ * - * A Connection represents a session with a specific database. Within the + * A Connection represents a session with a specific database. Within the * context of a Connection, SQL statements are executed and results are * returned. * @@ -29,181 +29,183 @@ import org.postgresql.util.*; * with the getMetaData method. * *

Note: By default, the Connection automatically commits changes - * after executing each statement. If auto-commit has been disabled, an + * after executing each statement. If auto-commit has been disabled, an * explicit commit must be done or database changes will not be saved. * * @see java.sql.Connection */ public class Connection extends org.postgresql.Connection implements java.sql.Connection { - // This is a cache of the DatabaseMetaData instance for this connection - protected DatabaseMetaData metadata; - - /** - * SQL statements without parameters are normally executed using - * Statement objects. If the same SQL statement is executed many - * times, it is more efficient to use a PreparedStatement - * - * @return a new Statement object - * @exception SQLException passed through from the constructor - */ - public java.sql.Statement createStatement() throws SQLException - { - return new Statement(this); - } - - /** - * A SQL statement with or without IN parameters can be pre-compiled - * and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - * - * Note: This method is optimized for handling parametric - * SQL statements that benefit from precompilation if the drivers - * supports precompilation. PostgreSQL does not support precompilation. - * In this case, the statement is not sent to the database until the - * PreparedStatement is executed. This has no direct effect on users; - * however it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' IN - * parameter placeholders - * @return a new PreparedStatement object containing the pre-compiled - * statement. - * @exception SQLException if a database access error occurs. - */ - public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException - { - return new PreparedStatement(this, sql); - } - - /** - * A SQL stored procedure call statement is handled by creating a - * CallableStatement for it. The CallableStatement provides methods - * for setting up its IN and OUT parameters and methods for executing - * it. - * - * Note: This method is optimised for handling stored procedure - * call statements. Some drivers may send the call statement to the - * database when the prepareCall is done; others may wait until the - * CallableStatement is executed. This has no direct effect on users; - * however, it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' parameter - * placeholders. Typically this statement is a JDBC function call - * escape string. - * @return a new CallableStatement object containing the pre-compiled - * SQL statement - * @exception SQLException if a database access error occurs - */ - public java.sql.CallableStatement prepareCall(String sql) throws SQLException - { - throw new PSQLException("postgresql.con.call"); - // return new CallableStatement(this, sql); - } - - /** - * Tests to see if a Connection is closed - * - * @return the status of the connection - * @exception SQLException (why?) - */ - public boolean isClosed() throws SQLException - { - return (pg_stream == null); - } - - /** - * A connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is made - * available through a DatabaseMetaData object. - * - * @return a DatabaseMetaData object for this connection - * @exception SQLException if a database access error occurs - */ - public java.sql.DatabaseMetaData getMetaData() throws SQLException - { - if(metadata==null) - metadata = new DatabaseMetaData(this); - return metadata; - } - - /** - * This overides the method in org.postgresql.Connection and returns a - * ResultSet. - */ - public java.sql.ResultSet getResultSet(org.postgresql.Connection conn,java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount,int insertOID, boolean binaryCursor) throws SQLException - { - // in jdbc1 stat is ignored. - return new org.postgresql.jdbc1.ResultSet((org.postgresql.jdbc1.Connection)conn,fields,tuples,status,updateCount,insertOID,binaryCursor); - } - - - /* An implementation of the abstract method in the parent class. - * This implemetation uses the jdbc1Types array to support the jdbc1 - * datatypes. Basically jdbc1 and jdbc2 are the same, except that - * jdbc2 adds the Array types. - */ - public int getSQLType(String pgTypeName) - { - int sqlType = Types.OTHER; // default value - for(int i=0;iNote: This method is optimized for handling parametric + * SQL statements that benefit from precompilation if the drivers + * supports precompilation. PostgreSQL does not support precompilation. + * In this case, the statement is not sent to the database until the + * PreparedStatement is executed. This has no direct effect on users; + * however it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' IN + * parameter placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException + { + return new PreparedStatement(this, sql); + } + + /** + * A SQL stored procedure call statement is handled by creating a + * CallableStatement for it. The CallableStatement provides methods + * for setting up its IN and OUT parameters and methods for executing + * it. + * + * Note: This method is optimised for handling stored procedure + * call statements. Some drivers may send the call statement to the + * database when the prepareCall is done; others may wait until the + * CallableStatement is executed. This has no direct effect on users; + * however, it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' parameter + * placeholders. Typically this statement is a JDBC function call + * escape string. + * @return a new CallableStatement object containing the pre-compiled + * SQL statement + * @exception SQLException if a database access error occurs + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException + { + throw new PSQLException("postgresql.con.call"); + // return new CallableStatement(this, sql); + } + + /** + * Tests to see if a Connection is closed + * + * @return the status of the connection + * @exception SQLException (why?) + */ + public boolean isClosed() throws SQLException + { + return (pg_stream == null); + } + + /** + * A connection's database is able to provide information describing + * its tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made + * available through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException + { + if (metadata == null) + metadata = new DatabaseMetaData(this); + return metadata; + } + + /** + * This overides the method in org.postgresql.Connection and returns a + * ResultSet. + */ + public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) throws SQLException + { + // in jdbc1 stat is ignored. + return new org.postgresql.jdbc1.ResultSet((org.postgresql.jdbc1.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor); + } + + + /* An implementation of the abstract method in the parent class. + * This implemetation uses the jdbc1Types array to support the jdbc1 + * datatypes. Basically jdbc1 and jdbc2 are the same, except that + * jdbc2 adds the Array types. + */ + public int getSQLType(String pgTypeName) + { + int sqlType = Types.OTHER; // default value + for (int i = 0;i < jdbc1Types.length;i++) + { + if (pgTypeName.equals(jdbc1Types[i])) + { + sqlType = jdbc1Typei[i]; + break; + } + } + return sqlType; + } + + /** + * This table holds the org.postgresql names for the types supported. + * Any types that map to Types.OTHER (eg POINT) don't go into this table. + * They default automatically to Types.OTHER + * + * Note: This must be in the same order as below. + * + * Tip: keep these grouped together by the Types. value + */ + private static final String jdbc1Types[] = { + "int2", + "int4", "oid", + "int8", + "cash", "money", + "numeric", + "float4", + "float8", + "bpchar", "char", "char2", "char4", "char8", "char16", + "varchar", "text", "name", "filename", + "bytea", + "bool", + "date", + "time", + "abstime", "timestamp" + }; + + /** + * This table holds the JDBC type for each entry above. + * + * Note: This must be in the same order as above + * + * Tip: keep these grouped together by the Types. value + */ + private static final int jdbc1Typei[] = { + Types.SMALLINT, + Types.INTEGER, Types.INTEGER, + Types.BIGINT, + Types.DOUBLE, Types.DOUBLE, + Types.NUMERIC, + Types.REAL, + Types.DOUBLE, + Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, + Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, + Types.BINARY, + Types.BIT, + Types.DATE, + Types.TIME, + Types.TIMESTAMP, Types.TIMESTAMP + }; } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java index 3b24aebc0a..1029e7adcc 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java @@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException; /** * This class provides information about the database as a whole. * - * $Id: DatabaseMetaData.java,v 1.34 2001/10/24 04:31:48 barry Exp $ + * $Id: DatabaseMetaData.java,v 1.35 2001/10/25 05:59:59 momjian Exp $ * *

Many of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to @@ -37,2729 +37,2755 @@ import org.postgresql.util.PSQLException; */ public class DatabaseMetaData implements java.sql.DatabaseMetaData { - Connection connection; // The connection association - - // These define various OID's. Hopefully they will stay constant. - static final int iVarcharOid = 1043; // OID for varchar - static final int iBoolOid = 16; // OID for bool - static final int iInt2Oid = 21; // OID for int2 - static final int iInt4Oid = 23; // OID for int4 - static final int VARHDRSZ = 4; // length for int4 - - public DatabaseMetaData(Connection conn) - { - this.connection = conn; - } - - /** - * Can all the procedures returned by getProcedures be called - * by the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allProceduresAreCallable() throws SQLException - { - return true; // For now... - } - - /** - * Can all the tables returned by getTable be SELECTed by - * the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allTablesAreSelectable() throws SQLException - { - return true; // For now... - } - - /** - * What is the URL for this database? - * - * @return the url or null if it cannott be generated - * @exception SQLException if a database access error occurs - */ - public String getURL() throws SQLException - { - return connection.getURL(); - } - - /** - * What is our user name as known to the database? - * - * @return our database user name - * @exception SQLException if a database access error occurs - */ - public String getUserName() throws SQLException - { - return connection.getUserName(); - } - - /** - * Is the database in read-only mode? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - return connection.isReadOnly(); - } - - /** - * Are NULL values sorted high? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedHigh() throws SQLException - { - return connection.haveMinimumServerVersion("7.2"); - } - - /** - * Are NULL values sorted low? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedLow() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the start regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtStart() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the end regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtEnd() throws SQLException - { - return ! connection.haveMinimumServerVersion("7.2"); - } - - /** - * What is the name of this database product - we hope that it is - * PostgreSQL, so we return that explicitly. - * - * @return the database product name - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductName() throws SQLException - { - return "PostgreSQL"; - } - - /** - * What is the version of this database product. - * - * @return the database version - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductVersion() throws SQLException - { - return connection.getDBVersionNumber(); - } - - /** - * What is the name of this JDBC driver? If we don't know this - * we are doing something wrong! - * - * @return the JDBC driver name - * @exception SQLException why? - */ - public String getDriverName() throws SQLException - { - return "PostgreSQL Native Driver"; - } - - /** - * What is the version string of this JDBC driver? Again, this is - * static. - * - * @return the JDBC driver name. - * @exception SQLException why? - */ - public String getDriverVersion() throws SQLException - { - return connection.this_driver.getVersion(); - } - - /** - * What is this JDBC driver's major version number? - * - * @return the JDBC driver major version - */ - public int getDriverMajorVersion() - { - return connection.this_driver.getMajorVersion(); - } - - /** - * What is this JDBC driver's minor version number? - * - * @return the JDBC driver minor version - */ - public int getDriverMinorVersion() - { - return connection.this_driver.getMinorVersion(); - } - - /** - * Does the database store tables in a local file? No - it - * stores them in a file on the server. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFiles() throws SQLException - { - return false; - } - - /** - * Does the database use a file for each table? Well, not really, - * since it doesnt use local files. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFilePerTable() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers - * as case sensitive and as a result store them in mixed case? - * A JDBC-Compliant driver will always return false. - * - *

Predicament - what do they mean by "SQL identifiers" - if it - * means the names of the tables and columns, then the answers - * given below are correct - otherwise I don't know. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case sensitive and as a result store them in mixed case? A - * JDBC compliant driver will always return true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * What is the string used to quote SQL identifiers? This returns - * a space if identifier quoting isn't supported. A JDBC Compliant - * driver will always use a double quote character. - * - * @return the quoting string - * @exception SQLException if a database access error occurs - */ - public String getIdentifierQuoteString() throws SQLException - { - return "\""; - } - - /** - * Get a comma separated list of all a database's SQL keywords that - * are NOT also SQL92 keywords. - * - *

Within PostgreSQL, the keywords are found in - * src/backend/parser/keywords.c - * - *

For SQL Keywords, I took the list provided at - * - * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt - * which is for SQL3, not SQL-92, but it is close enough for - * this purpose. - * - * @return a comma separated list of keywords we use - * @exception SQLException if a database access error occurs - */ - public String getSQLKeywords() throws SQLException - { - return "abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"; - } - - public String getNumericFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getStringFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getSystemFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getTimeDateFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - /** - * This is the string that can be used to escape '_' and '%' in - * a search string pattern style catalog search parameters - * - * @return the string used to escape wildcard characters - * @exception SQLException if a database access error occurs - */ - public String getSearchStringEscape() throws SQLException - { - return "\\"; - } - - /** - * Get all the "extra" characters that can be used in unquoted - * identifier names (those beyond a-zA-Z0-9 and _) - * - *

From the file src/backend/parser/scan.l, an identifier is - * {letter}{letter_or_digit} which makes it just those listed - * above. - * - * @return a string containing the extra characters - * @exception SQLException if a database access error occurs - */ - public String getExtraNameCharacters() throws SQLException - { - return ""; - } - - /** - * Is "ALTER TABLE" with an add column supported? - * Yes for PostgreSQL 6.1 - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithAddColumn() throws SQLException - { - return true; - } - - /** - * Is "ALTER TABLE" with a drop column supported? - * Peter 10/10/2000 This was set to true, but 7.1devel doesn't support it! - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithDropColumn() throws SQLException - { - return false; - } - - /** - * Is column aliasing supported? - * - *

If so, the SQL AS clause can be used to provide names for - * computed columns or to provide alias names for columns as - * required. A JDBC Compliant driver always returns true. - * - *

e.g. - * - *

-   * select count(C) as C_COUNT from T group by C;
-   *
-   * 

- * should return a column named as C_COUNT instead of count(C) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsColumnAliasing() throws SQLException - { - return true; - } - - /** - * Are concatenations between NULL and non-NULL values NULL? A - * JDBC Compliant driver always returns true - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullPlusNonNullIsNull() throws SQLException - { - return true; - } - - public boolean supportsConvert() throws SQLException - { - // XXX-Not Implemented - return false; - } - - public boolean supportsConvert(int fromType, int toType) throws SQLException - { - // XXX-Not Implemented - return false; - } - - /** - * Are table correlation names supported? A JDBC Compliant - * driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsTableCorrelationNames() throws SQLException - { - return true; - } - - /** - * If table correlation names are supported, are they restricted to - * be different from the names of the tables? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsDifferentTableCorrelationNames() throws SQLException - { - return false; - } - - /** - * Are expressions in "ORDER BY" lists supported? - * - *
e.g. select * from t order by a + b; - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExpressionsInOrderBy() throws SQLException - { - return true; - } - - /** - * Can an "ORDER BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOrderByUnrelated() throws SQLException - { - return connection.haveMinimumServerVersion("6.4"); - } - - /** - * Is some form of "GROUP BY" clause supported? - * I checked it, and yes it is. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupBy() throws SQLException - { - return true; - } - - /** - * Can a "GROUP BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByUnrelated() throws SQLException - { - return connection.haveMinimumServerVersion("6.4"); - } - - /** - * Can a "GROUP BY" clause add columns not in the SELECT provided - * it specifies all the columns in the SELECT? Does anyone actually - * understand what they mean here? - * - * (I think this is a subset of the previous function. -- petere) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByBeyondSelect() throws SQLException - { - return supportsGroupByUnrelated(); - } - - /** - * Is the escape character in "LIKE" clauses supported? A - * JDBC compliant driver always returns true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLikeEscapeClause() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Are multiple ResultSets from a single execute supported? - * Well, I implemented it, but I dont think this is possible from - * the back ends point of view. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleResultSets() throws SQLException - { - return false; - } - - /** - * Can we have multiple transactions open at once (on different - * connections?) - * I guess we can have, since Im relying on it. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleTransactions() throws SQLException - { - return true; - } - - /** - * Can columns be defined as non-nullable. A JDBC Compliant driver - * always returns true. - * - *

This changed from false to true in v6.2 of the driver, as this - * support was added to the backend. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsNonNullableColumns() throws SQLException - { - return true; - } - - /** - * Does this driver support the minimum ODBC SQL grammar. This - * grammar is defined at: - * - *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm - * - *

In Appendix C. From this description, we seem to support the - * ODBC minimal (Level 0) grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMinimumSQLGrammar() throws SQLException - { - return true; - } - - /** - * Does this driver support the Core ODBC SQL grammar. We need - * SQL-92 conformance for this. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCoreSQLGrammar() throws SQLException - { - return false; - } - - /** - * Does this driver support the Extended (Level 2) ODBC SQL - * grammar. We don't conform to the Core (Level 1), so we can't - * conform to the Extended SQL Grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExtendedSQLGrammar() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 entry level SQL grammar? - * All JDBC Compliant drivers must return true. We currently - * report false until 'schema' support is added. Then this - * should be changed to return true, since we will be mostly - * compliant (probably more compliant than many other databases) - * And since this is a requirement for all JDBC drivers we - * need to get to the point where we can return true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92EntryLevelSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 intermediate level SQL - * grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92IntermediateSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 full SQL grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92FullSQL() throws SQLException - { - return false; - } - - /** - * Is the SQL Integrity Enhancement Facility supported? - * I haven't seen this mentioned anywhere, so I guess not - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsIntegrityEnhancementFacility() throws SQLException - { - return false; - } - - /** - * Is some form of outer join supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOuterJoins() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Are full nexted outer joins supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsFullOuterJoins() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Is there limited support for outer joins? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLimitedOuterJoins() throws SQLException - { - return supportsFullOuterJoins(); - } - - /** - * What is the database vendor's preferred term for "schema"? - * PostgreSQL doesn't have schemas, but when it does, we'll use the - * term "schema". - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getSchemaTerm() throws SQLException - { - return "schema"; - } - - /** - * What is the database vendor's preferred term for "procedure"? - * Traditionally, "function" has been used. - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getProcedureTerm() throws SQLException - { - return "function"; - } - - /** - * What is the database vendor's preferred term for "catalog"? - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getCatalogTerm() throws SQLException - { - return "database"; - } - - /** - * Does a catalog appear at the start of a qualified table name? - * (Otherwise it appears at the end). - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCatalogAtStart() throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * What is the Catalog separator. - * - * @return the catalog separator string - * @exception SQLException if a database access error occurs - */ - public String getCatalogSeparator() throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Can a schema name be used in a data manipulation statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a procedure call statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a schema be used in a table definition statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in an index definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a data manipulation statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a procedure call statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a table definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in an index definition? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * We support cursors for gets only it seems. I dont see a method - * to get a positioned delete. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedDelete() throws SQLException - { - return false; // For now... - } - - /** - * Is positioned UPDATE supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedUpdate() throws SQLException - { - return false; // For now... - } - - /** - * Is SELECT for UPDATE supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSelectForUpdate() throws SQLException - { - return connection.haveMinimumServerVersion("6.5"); - } - - /** - * Are stored procedure calls using the stored procedure escape - * syntax supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsStoredProcedures() throws SQLException - { - return false; - } - - /** - * Are subqueries in comparison expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInComparisons() throws SQLException - { - return true; - } - - /** - * Are subqueries in 'exists' expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInExists() throws SQLException - { - return true; - } - - /** - * Are subqueries in 'in' statements supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInIns() throws SQLException - { - return true; - } - - /** - * Are subqueries in quantified expressions supported? A JDBC - * Compliant driver always returns true. - * - * (No idea what this is, but we support a good deal of - * subquerying.) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInQuantifieds() throws SQLException - { - return true; - } - - /** - * Are correlated subqueries supported? A JDBC Compliant driver - * always returns true. - * - * (a.k.a. subselect in from?) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsCorrelatedSubqueries() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Is SQL UNION supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnion() throws SQLException - { - return true; // since 6.3 - } - - /** - * Is SQL UNION ALL supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnionAll() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * In PostgreSQL, Cursors are only open within transactions. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossCommit() throws SQLException - { - return false; - } - - /** - * Do we support open cursors across multiple transactions? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossRollback() throws SQLException - { - return false; - } - - /** - * Can statements remain open across commits? They may, but - * this driver cannot guarentee that. In further reflection. - * we are talking a Statement object here, so the answer is - * yes, since the Statement is only a vehicle to ExecSQL() - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossCommit() throws SQLException - { - return true; - } - - /** - * Can statements remain open across rollbacks? They may, but - * this driver cannot guarentee that. In further contemplation, - * we are talking a Statement object here, so the answer is yes, - * since the Statement is only a vehicle to ExecSQL() in Connection - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossRollback() throws SQLException - { - return true; - } - - /** - * How many hex characters can you have in an inline binary literal - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxBinaryLiteralLength() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum length for a character literal - * I suppose it is 8190 (8192 - 2 for the quotes) - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxCharLiteralLength() throws SQLException - { - return 0; // no limit - } - - /** - * Whats the limit on column name length. The description of - * pg_class would say '32' (length of pg_class.relname) - we - * should probably do a query for this....but.... - * - * @return the maximum column name length - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of columns in a "GROUP BY" clause? - * - * @return the max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInGroupBy() throws SQLException - { - return 0; // no limit - } - - /** - * What's the maximum number of columns allowed in an index? - * 6.0 only allowed one column, but 6.1 introduced multi-column - * indices, so, theoretically, its all of them. - * - * @return max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInIndex() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What's the maximum number of columns in an "ORDER BY clause? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInOrderBy() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum number of columns in a "SELECT" list? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInSelect() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum number of columns in a table? From the - * CREATE TABLE reference page... - * - *

"The new class is created as a heap with no initial data. A - * class can have no more than 1600 attributes (realistically, - * this is limited by the fact that tuple sizes must be less than - * 8192 bytes)..." - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInTable() throws SQLException - { - return 1600; - } - - /** - * How many active connection can we have at a time to this - * database? Well, since it depends on postmaster, which just - * does a listen() followed by an accept() and fork(), its - * basically very high. Unless the system runs out of processes, - * it can be 65535 (the number of aux. ports on a TCP/IP system). - * I will return 8192 since that is what even the largest system - * can realistically handle, - * - * @return the maximum number of connections - * @exception SQLException if a database access error occurs - */ - public int getMaxConnections() throws SQLException - { - return 8192; - } - - /** - * What is the maximum cursor name length (the same as all - * the other F***** identifiers!) - * - * @return max cursor name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxCursorNameLength() throws SQLException - { - return 32; - } - - /** - * Retrieves the maximum number of bytes for an index, including all - * of the parts of the index. - * - * @return max index length in bytes, which includes the composite - * of all the constituent parts of the index; a result of zero means - * that there is no limit or the limit is not known - * @exception SQLException if a database access error occurs - */ - public int getMaxIndexLength() throws SQLException - { - return 0; // no limit (larger than an int anyway) - } - - public int getMaxSchemaNameLength() throws SQLException - { - // XXX-Not Implemented - return 0; - } - - /** - * What is the maximum length of a procedure name? - * (length of pg_proc.proname used) - again, I really - * should do a query here to get it. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxProcedureNameLength() throws SQLException - { - return 32; - } - - public int getMaxCatalogNameLength() throws SQLException - { - // XXX-Not Implemented - return 0; - } - - /** - * What is the maximum length of a single row? - * - * @return max row size in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxRowSize() throws SQLException - { - if (connection.haveMinimumServerVersion("7.1")) - return 1073741824; // 1 GB - else - return 8192; // XXX could be altered - } - - /** - * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY - * blobs? We don't handle blobs yet - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean doesMaxRowSizeIncludeBlobs() throws SQLException - { - return false; - } - - /** - * What is the maximum length of a SQL statement? - * - * @return max length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxStatementLength() throws SQLException - { - if (connection.haveMinimumServerVersion("7.0")) - return 0; // actually whatever fits in size_t - else - return 16384; - } - - /** - * How many active statements can we have open at one time to - * this database? Basically, since each Statement downloads - * the results as the query is executed, we can have many. However, - * we can only really have one statement per connection going - * at once (since they are executed serially) - so we return - * one. - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxStatements() throws SQLException - { - return 1; - } - - /** - * What is the maximum length of a table name? This was found - * from pg_class.relname length - * - * @return max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxTableNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of tables that can be specified - * in a SELECT? - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxTablesInSelect() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum length of a user name? Well, we generally - * use UNIX like user names in PostgreSQL, so I think this would - * be 8. However, showing the schema for pg_user shows a length - * for username of 32. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxUserNameLength() throws SQLException - { - return 32; - } - - - /** - * What is the database's default transaction isolation level? We - * do not support this, so all transactions are SERIALIZABLE. - * - * @return the default isolation level - * @exception SQLException if a database access error occurs - * @see Connection - */ - public int getDefaultTransactionIsolation() throws SQLException - { - return Connection.TRANSACTION_READ_COMMITTED; - } - - /** - * Are transactions supported? If not, commit and rollback are noops - * and the isolation level is TRANSACTION_NONE. We do support - * transactions. - * - * @return true if transactions are supported - * @exception SQLException if a database access error occurs - */ - public boolean supportsTransactions() throws SQLException - { - return true; - } - - /** - * Does the database support the given transaction isolation level? - * We only support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED - * - * @param level the values are defined in java.sql.Connection - * @return true if so - * @exception SQLException if a database access error occurs - * @see Connection - */ - public boolean supportsTransactionIsolationLevel(int level) throws SQLException - { - if (level == Connection.TRANSACTION_SERIALIZABLE || - level == Connection.TRANSACTION_READ_COMMITTED) - return true; - else - return false; - } - - /** - * Are both data definition and data manipulation transactions - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException - { - return true; - } - - /** - * Are only data manipulation statements withing a transaction - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataManipulationTransactionsOnly() throws SQLException - { - return false; - } - - /** - * Does a data definition statement within a transaction force - * the transaction to commit? I think this means something like: - * - *

-   * CREATE TABLE T (A INT);
-   * INSERT INTO T (A) VALUES (2);
-   * BEGIN;
-   * UPDATE T SET A = A + 1;
-   * CREATE TABLE X (A INT);
-   * SELECT A FROM T INTO X;
-   * COMMIT;
-   * 

- * - * does the CREATE TABLE call cause a commit? The answer is no. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionCausesTransactionCommit() throws SQLException - { - return false; - } - - /** - * Is a data definition statement within a transaction ignored? - * It seems to be (from experiment in previous method) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionIgnoredInTransactions() throws SQLException - { - return true; - } - - /** - * Get a description of stored procedures available in a catalog - * - *

Only procedure descriptions matching the schema and procedure - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME - * - *

Each procedure description has the following columns: - *

    - *
  1. PROCEDURE_CAT String => procedure catalog (may be null) - *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) - *
  3. PROCEDURE_NAME String => procedure name - *
  4. Field 4 reserved (make it null) - *
  5. Field 5 reserved (make it null) - *
  6. Field 6 reserved (make it null) - *
  7. REMARKS String => explanatory comment on the procedure - *
  8. PROCEDURE_TYPE short => kind of procedure - *
      - *
    • procedureResultUnknown - May return a result - *
    • procedureNoResult - Does not return a result - *
    • procedureReturnsResult - Returns a result - *
    - *
- * - * @param catalog - a catalog name; "" retrieves those without a - * catalog; null means drop catalog name from criteria - * @param schemaParrern - a schema name pattern; "" retrieves those - * without a schema - we ignore this parameter - * @param procedureNamePattern - a procedure name pattern - * @return ResultSet - each row is a procedure description - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException - { - // the field descriptors for the new ResultSet - Field f[] = new Field[8]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); - f[3] = f[4] = f[5] = new Field(connection,"reserved",iVarcharOid,32); // null; // reserved, must be null for now - f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); - f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); - - // If the pattern is null, then set it to the default - if(procedureNamePattern==null) - procedureNamePattern="%"; - - r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname"); - - while (r.next()) - { - byte[][] tuple = new byte[8][0]; - - tuple[0] = null; // Catalog name - tuple[1] = null; // Schema name - tuple[2] = r.getBytes(1); // Procedure name - tuple[3] = tuple[4] = tuple[5] = null; // Reserved - tuple[6] = null; // Remarks - - if (r.getBoolean(2)) - tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); - else - tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); - - v.addElement(tuple); - } - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of a catalog's stored procedure parameters - * and result columns. - * - *

Only descriptions matching the schema, procedure and parameter - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME. Within this, the return value, if any, is - * first. Next are the parameter descriptions in call order. The - * column descriptions follow in column number order. - * - *

Each row in the ResultSet is a parameter description or column - * description with the following fields: - *

    - *
  1. PROCEDURE_CAT String => procedure catalog (may be null) - *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) - *
  3. PROCEDURE_NAME String => procedure name - *
  4. COLUMN_NAME String => column/parameter name - *
  5. COLUMN_TYPE Short => kind of column/parameter: - *
    • procedureColumnUnknown - nobody knows - *
    • procedureColumnIn - IN parameter - *
    • procedureColumnInOut - INOUT parameter - *
    • procedureColumnOut - OUT parameter - *
    • procedureColumnReturn - procedure return value - *
    • procedureColumnResult - result column in ResultSet - *
    - *
  6. DATA_TYPE short => SQL type from java.sql.Types - *
  7. TYPE_NAME String => SQL type name - *
  8. PRECISION int => precision - *
  9. LENGTH int => length in bytes of data - *
  10. SCALE short => scale - *
  11. RADIX short => radix - *
  12. NULLABLE short => can it contain NULL? - *
    • procedureNoNulls - does not allow NULL values - *
    • procedureNullable - allows NULL values - *
    • procedureNullableUnknown - nullability unknown - *
    • REMARKS String => comment describing parameter/column - *
- * @param catalog This is ignored in org.postgresql, advise this is set to null - * @param schemaPattern This is ignored in org.postgresql, advise this is set to null - * @param procedureNamePattern a procedure name pattern - * @param columnNamePattern a column name pattern - * @return each row is a stored procedure parameter or column description - * @exception SQLException if a database-access error occurs - * @see #getSearchStringEscape - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException - { - if(procedureNamePattern==null) - procedureNamePattern="%"; - - if(columnNamePattern==null) - columnNamePattern="%"; - - // for now, this returns an empty result set. - Field f[] = new Field[13]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[7] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[8] = new Field(connection, "LENGTH", iInt4Oid, 4); - f[9] = new Field(connection, "SCALE", iInt2Oid, 2); - f[10] = new Field(connection, "RADIX", iInt2Oid, 2); - f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[12] = new Field(connection, "REMARKS", iVarcharOid, 32); - - // add query loop here - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of tables available in a catalog. - * - *

Only table descriptions matching the catalog, schema, table - * name and type criteria are returned. They are ordered by - * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. - * - *

Each table description has the following columns: - * - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL - * TEMPORARY", "ALIAS", "SYNONYM". - *
  5. REMARKS String => explanatory comment on the table - *
- * - *

The valid values for the types parameter are: - * "TABLE", "INDEX", "SEQUENCE", "SYSTEM TABLE" and "SYSTEM INDEX" - * - * @param catalog a catalog name; For org.postgresql, this is ignored, and - * should be set to null - * @param schemaPattern a schema name pattern; For org.postgresql, this is ignored, and - * should be set to null - * @param tableNamePattern a table name pattern. For all tables this should be "%" - * @param types a list of table types to include; null returns - * all types - * @return each row is a table description - * @exception SQLException if a database-access error occurs. - */ - public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException - { - // Handle default value for types - if(types==null) - types = defaultTableTypes; - - if(tableNamePattern==null) - tableNamePattern="%"; - - // the field descriptors for the new ResultSet - Field f[] = new Field[5]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "TABLE_TYPE", iVarcharOid, 32); - f[4] = new Field(connection, "REMARKS", iVarcharOid, 32); - - // Now form the query - StringBuffer sql = new StringBuffer("select relname,oid,relkind from pg_class where ("); - - boolean notFirst=false; - for(int i=0;i - // Now take the pattern into account - sql.append(") and relname like '"); - sql.append(tableNamePattern.toLowerCase()); - sql.append("' order by relkind, relname"); - - // Now run the query - r = connection.ExecSQL(sql.toString()); - - while (r.next()) - { - byte[][] tuple = new byte[5][0]; - - // Fetch the description for the table (if any) - String getDescriptionStatement = - connection.haveMinimumServerVersion("7.2") ? - "select obj_description("+r.getInt(2)+",'pg_class')" : - "select description from pg_description where objoid=" + r.getInt(2); - - java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); - - byte remarks[] = null; - - if(((org.postgresql.ResultSet)dr).getTupleCount()==1) { - dr.next(); - remarks = dr.getBytes(1); - } - dr.close(); - - String relKind; - switch (r.getBytes(3)[0]) { - case (byte) 'r': - relKind = "TABLE"; - break; - case (byte) 'i': - relKind = "INDEX"; - break; - case (byte) 'S': - relKind = "SEQUENCE"; - break; - case (byte) 'v': - relKind = "VIEW"; - break; - default: - relKind = null; - } - - tuple[0] = null; // Catalog name - tuple[1] = null; // Schema name - tuple[2] = r.getBytes(1); // Table name - tuple[3] = (relKind==null) ? null : relKind.getBytes(); // Table type - tuple[4] = remarks; // Remarks - v.addElement(tuple); - } - r.close(); - return new ResultSet(connection, f, v, "OK", 1); - } - - // This array contains the valid values for the types argument - // in getTables(). - // - // Each supported type consists of it's name, and the sql where - // clause to retrieve that value. - // - // IMPORTANT: the query must be enclosed in ( ) - private static final String getTableTypes[][] = { - {"TABLE", "(relkind='r' and relhasrules='f' and relname !~ '^pg_' and relname !~ '^xinv')"}, - {"VIEW", "(relkind='v' and relname !~ '^pg_')"}, - {"INDEX", "(relkind='i' and relname !~ '^pg_')"}, - {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, - {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, - {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} - }; - - // These are the default tables, used when NULL is passed to getTables - // The choice of these provide the same behaviour as psql's \d - private static final String defaultTableTypes[] = { - "TABLE","VIEW","INDEX","SEQUENCE" - }; - - /** - * Get the schema names available in this database. The results - * are ordered by schema name. - * - *

The schema column is: - *

    - *
  1. TABLE_SCHEM String => schema name - *
- * - * @return ResultSet each row has a single String column that is a - * schema name - */ - public java.sql.ResultSet getSchemas() throws SQLException - { - // We don't use schemas, so we simply return a single schema name "". - // - Field f[] = new Field[1]; - Vector v = new Vector(); - byte[][] tuple = new byte[1][0]; - f[0] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32); - tuple[0] = "".getBytes(); - v.addElement(tuple); - return new ResultSet(connection,f,v,"OK",1); - } - - /** - * Get the catalog names available in this database. The results - * are ordered by catalog name. - * - *

The catalog column is: - *

    - *
  1. TABLE_CAT String => catalog name - *
- * - * @return ResultSet each row has a single String column that is a - * catalog name - */ - public java.sql.ResultSet getCatalogs() throws SQLException - { - return connection.createStatement().executeQuery("select datname as TABLE_CAT from pg_database;"); - } - - /** - * Get the table types available in this database. The results - * are ordered by table type. - * - *

The table type is: - *

    - *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", - * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". - *
- * - * @return ResultSet each row has a single String column that is a - * table type - */ - public java.sql.ResultSet getTableTypes() throws SQLException - { - Field f[] = new Field[1]; - Vector v = new Vector(); - f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32); - for(int i=0;iOnly column descriptions matching the catalog, schema, table - * and column name criteria are returned. They are ordered by - * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. - * - *

Each column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. DATA_TYPE short => SQL type from java.sql.Types - *
  6. TYPE_NAME String => Data source dependent type name - *
  7. COLUMN_SIZE int => column size. For char or date - * types this is the maximum number of characters, for numeric or - * decimal types this is precision. - *
  8. BUFFER_LENGTH is not used. - *
  9. DECIMAL_DIGITS int => the number of fractional digits - *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) - *
  11. NULLABLE int => is NULL allowed? - *
      - *
    • columnNoNulls - might not allow NULL values - *
    • columnNullable - definitely allows NULL values - *
    • columnNullableUnknown - nullability unknown - *
    - *
  12. REMARKS String => comment describing column (may be null) - *
  13. COLUMN_DEF String => default value (may be null) - *
  14. SQL_DATA_TYPE int => unused - *
  15. SQL_DATETIME_SUB int => unused - *
  16. CHAR_OCTET_LENGTH int => for char types the - * maximum number of bytes in the column - *
  17. ORDINAL_POSITION int => index of column in table - * (starting at 1) - *
  18. IS_NULLABLE String => "NO" means column definitely - * does not allow NULL values; "YES" means the column might - * allow NULL values. An empty string means nobody knows. - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schemaPattern a schema name pattern; "" retrieves those - * without a schema - * @param tableNamePattern a table name pattern - * @param columnNamePattern a column name pattern - * @return ResultSet each row is a column description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException - { - Vector v = new Vector(); // The new ResultSet tuple stuff - Field f[] = new Field[18]; // The field descriptors for the new ResultSet - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); - f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); - f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); - f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); - f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); - f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, 32); - f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4); - f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); - - StringBuffer sql = new StringBuffer(512); - - /* Build a >= 7.1 SQL statement to list all columns */ - sql.append("select " + - (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") + - " c.relname, " + - " a.attname, " + - " a.atttypid, " + - " a.attnum, " + - " a.attnotnull, " + - " a.attlen, " + - " a.atttypmod, " + - " d.adsrc, " + - " t.typname, " + - /* Use the new col_description in 7.2 or an additional outer join in 7.1 */ - (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") + - "from" + - " (" + - " (pg_class c inner join pg_attribute a on" + - " (" + - " a.attrelid=c.oid"); - - if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) { - sql.append(" and c.relname like \'" + tableNamePattern + "\'"); - } - - if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) { - sql.append(" and a.attname like \'" + columnNamePattern + "\'"); - } - - sql.append( - " and a.attnum > 0" + - " )" + - " ) inner join pg_type t on" + - " (" + - " t.oid = a.atttypid" + - " )" + - " )" + - " left outer join pg_attrdef d on" + - " (" + - " c.oid = d.adrelid" + - " and a.attnum = d.adnum" + - " ) "); - - if (!connection.haveMinimumServerVersion("7.2")) { - /* Only for 7.1 */ + Connection connection; // The connection association + + // These define various OID's. Hopefully they will stay constant. + static final int iVarcharOid = 1043; // OID for varchar + static final int iBoolOid = 16; // OID for bool + static final int iInt2Oid = 21; // OID for int2 + static final int iInt4Oid = 23; // OID for int4 + static final int VARHDRSZ = 4; // length for int4 + + public DatabaseMetaData(Connection conn) + { + this.connection = conn; + } + + /** + * Can all the procedures returned by getProcedures be called + * by the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allProceduresAreCallable() throws SQLException + { + return true; // For now... + } + + /** + * Can all the tables returned by getTable be SELECTed by + * the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allTablesAreSelectable() throws SQLException + { + return true; // For now... + } + + /** + * What is the URL for this database? + * + * @return the url or null if it cannott be generated + * @exception SQLException if a database access error occurs + */ + public String getURL() throws SQLException + { + return connection.getURL(); + } + + /** + * What is our user name as known to the database? + * + * @return our database user name + * @exception SQLException if a database access error occurs + */ + public String getUserName() throws SQLException + { + return connection.getUserName(); + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return connection.isReadOnly(); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedHigh() throws SQLException + { + return connection.haveMinimumServerVersion("7.2"); + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedLow() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtStart() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtEnd() throws SQLException + { + return ! connection.haveMinimumServerVersion("7.2"); + } + + /** + * What is the name of this database product - we hope that it is + * PostgreSQL, so we return that explicitly. + * + * @return the database product name + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductName() throws SQLException + { + return "PostgreSQL"; + } + + /** + * What is the version of this database product. + * + * @return the database version + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductVersion() throws SQLException + { + return connection.getDBVersionNumber(); + } + + /** + * What is the name of this JDBC driver? If we don't know this + * we are doing something wrong! + * + * @return the JDBC driver name + * @exception SQLException why? + */ + public String getDriverName() throws SQLException + { + return "PostgreSQL Native Driver"; + } + + /** + * What is the version string of this JDBC driver? Again, this is + * static. + * + * @return the JDBC driver name. + * @exception SQLException why? + */ + public String getDriverVersion() throws SQLException + { + return connection.this_driver.getVersion(); + } + + /** + * What is this JDBC driver's major version number? + * + * @return the JDBC driver major version + */ + public int getDriverMajorVersion() + { + return connection.this_driver.getMajorVersion(); + } + + /** + * What is this JDBC driver's minor version number? + * + * @return the JDBC driver minor version + */ + public int getDriverMinorVersion() + { + return connection.this_driver.getMinorVersion(); + } + + /** + * Does the database store tables in a local file? No - it + * stores them in a file on the server. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFiles() throws SQLException + { + return false; + } + + /** + * Does the database use a file for each table? Well, not really, + * since it doesnt use local files. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFilePerTable() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers + * as case sensitive and as a result store them in mixed case? + * A JDBC-Compliant driver will always return false. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case sensitive and as a result store them in mixed case? A + * JDBC compliant driver will always return true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * What is the string used to quote SQL identifiers? This returns + * a space if identifier quoting isn't supported. A JDBC Compliant + * driver will always use a double quote character. + * + * @return the quoting string + * @exception SQLException if a database access error occurs + */ + public String getIdentifierQuoteString() throws SQLException + { + return "\""; + } + + /** + * Get a comma separated list of all a database's SQL keywords that + * are NOT also SQL92 keywords. + * + *

Within PostgreSQL, the keywords are found in + * src/backend/parser/keywords.c + * + *

For SQL Keywords, I took the list provided at + * + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * which is for SQL3, not SQL-92, but it is close enough for + * this purpose. + * + * @return a comma separated list of keywords we use + * @exception SQLException if a database access error occurs + */ + public String getSQLKeywords() throws SQLException + { + return "abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"; + } + + public String getNumericFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getStringFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getSystemFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getTimeDateFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + /** + * This is the string that can be used to escape '_' and '%' in + * a search string pattern style catalog search parameters + * + * @return the string used to escape wildcard characters + * @exception SQLException if a database access error occurs + */ + public String getSearchStringEscape() throws SQLException + { + return "\\"; + } + + /** + * Get all the "extra" characters that can be used in unquoted + * identifier names (those beyond a-zA-Z0-9 and _) + * + *

From the file src/backend/parser/scan.l, an identifier is + * {letter}{letter_or_digit} which makes it just those listed + * above. + * + * @return a string containing the extra characters + * @exception SQLException if a database access error occurs + */ + public String getExtraNameCharacters() throws SQLException + { + return ""; + } + + /** + * Is "ALTER TABLE" with an add column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException + { + return true; + } + + /** + * Is "ALTER TABLE" with a drop column supported? + * Peter 10/10/2000 This was set to true, but 7.1devel doesn't support it! + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException + { + return false; + } + + /** + * Is column aliasing supported? + * + *

If so, the SQL AS clause can be used to provide names for + * computed columns or to provide alias names for columns as + * required. A JDBC Compliant driver always returns true. + * + *

e.g. + * + *

+	 * select count(C) as C_COUNT from T group by C;
+	 *
+	 * 

+ * should return a column named as C_COUNT instead of count(C) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsColumnAliasing() throws SQLException + { + return true; + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A + * JDBC Compliant driver always returns true + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullPlusNonNullIsNull() throws SQLException + { + return true; + } + + public boolean supportsConvert() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Are table correlation names supported? A JDBC Compliant + * driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsTableCorrelationNames() throws SQLException + { + return true; + } + + /** + * If table correlation names are supported, are they restricted to + * be different from the names of the tables? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsDifferentTableCorrelationNames() throws SQLException + { + return false; + } + + /** + * Are expressions in "ORDER BY" lists supported? + * + *
e.g. select * from t order by a + b; + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExpressionsInOrderBy() throws SQLException + { + return true; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOrderByUnrelated() throws SQLException + { + return connection.haveMinimumServerVersion("6.4"); + } + + /** + * Is some form of "GROUP BY" clause supported? + * I checked it, and yes it is. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupBy() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByUnrelated() throws SQLException + { + return connection.haveMinimumServerVersion("6.4"); + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided + * it specifies all the columns in the SELECT? Does anyone actually + * understand what they mean here? + * + * (I think this is a subset of the previous function. -- petere) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByBeyondSelect() throws SQLException + { + return supportsGroupByUnrelated(); + } + + /** + * Is the escape character in "LIKE" clauses supported? A + * JDBC compliant driver always returns true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLikeEscapeClause() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Are multiple ResultSets from a single execute supported? + * Well, I implemented it, but I dont think this is possible from + * the back ends point of view. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleResultSets() throws SQLException + { + return false; + } + + /** + * Can we have multiple transactions open at once (on different + * connections?) + * I guess we can have, since Im relying on it. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleTransactions() throws SQLException + { + return true; + } + + /** + * Can columns be defined as non-nullable. A JDBC Compliant driver + * always returns true. + * + *

This changed from false to true in v6.2 of the driver, as this + * support was added to the backend. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsNonNullableColumns() throws SQLException + { + return true; + } + + /** + * Does this driver support the minimum ODBC SQL grammar. This + * grammar is defined at: + * + *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + * + *

In Appendix C. From this description, we seem to support the + * ODBC minimal (Level 0) grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMinimumSQLGrammar() throws SQLException + { + return true; + } + + /** + * Does this driver support the Core ODBC SQL grammar. We need + * SQL-92 conformance for this. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCoreSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the Extended (Level 2) ODBC SQL + * grammar. We don't conform to the Core (Level 1), so we can't + * conform to the Extended SQL Grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExtendedSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 entry level SQL grammar? + * All JDBC Compliant drivers must return true. We currently + * report false until 'schema' support is added. Then this + * should be changed to return true, since we will be mostly + * compliant (probably more compliant than many other databases) + * And since this is a requirement for all JDBC drivers we + * need to get to the point where we can return true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 intermediate level SQL + * grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 full SQL grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92FullSQL() throws SQLException + { + return false; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * I haven't seen this mentioned anywhere, so I guess not + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException + { + return false; + } + + /** + * Is some form of outer join supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOuterJoins() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Are full nexted outer joins supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsFullOuterJoins() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Is there limited support for outer joins? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLimitedOuterJoins() throws SQLException + { + return supportsFullOuterJoins(); + } + + /** + * What is the database vendor's preferred term for "schema"? + * PostgreSQL doesn't have schemas, but when it does, we'll use the + * term "schema". + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getSchemaTerm() throws SQLException + { + return "schema"; + } + + /** + * What is the database vendor's preferred term for "procedure"? + * Traditionally, "function" has been used. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getProcedureTerm() throws SQLException + { + return "function"; + } + + /** + * What is the database vendor's preferred term for "catalog"? + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getCatalogTerm() throws SQLException + { + return "database"; + } + + /** + * Does a catalog appear at the start of a qualified table name? + * (Otherwise it appears at the end). + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCatalogAtStart() throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * What is the Catalog separator. + * + * @return the catalog separator string + * @exception SQLException if a database access error occurs + */ + public String getCatalogSeparator() throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Can a schema name be used in a data manipulation statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a schema be used in a table definition statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in an index definition? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * We support cursors for gets only it seems. I dont see a method + * to get a positioned delete. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedDelete() throws SQLException + { + return false; // For now... + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedUpdate() throws SQLException + { + return false; // For now... + } + + /** + * Is SELECT for UPDATE supported? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSelectForUpdate() throws SQLException + { + return connection.haveMinimumServerVersion("6.5"); + } + + /** + * Are stored procedure calls using the stored procedure escape + * syntax supported? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsStoredProcedures() throws SQLException + { + return false; + } + + /** + * Are subqueries in comparison expressions supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInComparisons() throws SQLException + { + return true; + } + + /** + * Are subqueries in 'exists' expressions supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInExists() throws SQLException + { + return true; + } + + /** + * Are subqueries in 'in' statements supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInIns() throws SQLException + { + return true; + } + + /** + * Are subqueries in quantified expressions supported? A JDBC + * Compliant driver always returns true. + * + * (No idea what this is, but we support a good deal of + * subquerying.) + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInQuantifieds() throws SQLException + { + return true; + } + + /** + * Are correlated subqueries supported? A JDBC Compliant driver + * always returns true. + * + * (a.k.a. subselect in from?) + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsCorrelatedSubqueries() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Is SQL UNION supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnion() throws SQLException + { + return true; // since 6.3 + } + + /** + * Is SQL UNION ALL supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnionAll() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * In PostgreSQL, Cursors are only open within transactions. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException + { + return false; + } + + /** + * Do we support open cursors across multiple transactions? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException + { + return false; + } + + /** + * Can statements remain open across commits? They may, but + * this driver cannot guarentee that. In further reflection. + * we are talking a Statement object here, so the answer is + * yes, since the Statement is only a vehicle to ExecSQL() + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException + { + return true; + } + + /** + * Can statements remain open across rollbacks? They may, but + * this driver cannot guarentee that. In further contemplation, + * we are talking a Statement object here, so the answer is yes, + * since the Statement is only a vehicle to ExecSQL() in Connection + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException + { + return true; + } + + /** + * How many hex characters can you have in an inline binary literal + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxBinaryLiteralLength() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum length for a character literal + * I suppose it is 8190 (8192 - 2 for the quotes) + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxCharLiteralLength() throws SQLException + { + return 0; // no limit + } + + /** + * Whats the limit on column name length. The description of + * pg_class would say '32' (length of pg_class.relname) - we + * should probably do a query for this....but.... + * + * @return the maximum column name length + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of columns in a "GROUP BY" clause? + * + * @return the max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInGroupBy() throws SQLException + { + return 0; // no limit + } + + /** + * What's the maximum number of columns allowed in an index? + * 6.0 only allowed one column, but 6.1 introduced multi-column + * indices, so, theoretically, its all of them. + * + * @return max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInIndex() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns in an "ORDER BY clause? + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInOrderBy() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum number of columns in a "SELECT" list? + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInSelect() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum number of columns in a table? From the + * CREATE TABLE reference page... + * + *

"The new class is created as a heap with no initial data. A + * class can have no more than 1600 attributes (realistically, + * this is limited by the fact that tuple sizes must be less than + * 8192 bytes)..." + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInTable() throws SQLException + { + return 1600; + } + + /** + * How many active connection can we have at a time to this + * database? Well, since it depends on postmaster, which just + * does a listen() followed by an accept() and fork(), its + * basically very high. Unless the system runs out of processes, + * it can be 65535 (the number of aux. ports on a TCP/IP system). + * I will return 8192 since that is what even the largest system + * can realistically handle, + * + * @return the maximum number of connections + * @exception SQLException if a database access error occurs + */ + public int getMaxConnections() throws SQLException + { + return 8192; + } + + /** + * What is the maximum cursor name length (the same as all + * the other F***** identifiers!) + * + * @return max cursor name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxCursorNameLength() throws SQLException + { + return 32; + } + + /** + * Retrieves the maximum number of bytes for an index, including all + * of the parts of the index. + * + * @return max index length in bytes, which includes the composite + * of all the constituent parts of the index; a result of zero means + * that there is no limit or the limit is not known + * @exception SQLException if a database access error occurs + */ + public int getMaxIndexLength() throws SQLException + { + return 0; // no limit (larger than an int anyway) + } + + public int getMaxSchemaNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a procedure name? + * (length of pg_proc.proname used) - again, I really + * should do a query here to get it. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxProcedureNameLength() throws SQLException + { + return 32; + } + + public int getMaxCatalogNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a single row? + * + * @return max row size in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxRowSize() throws SQLException + { + if (connection.haveMinimumServerVersion("7.1")) + return 1073741824; // 1 GB + else + return 8192; // XXX could be altered + } + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY + * blobs? We don't handle blobs yet + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException + { + return false; + } + + /** + * What is the maximum length of a SQL statement? + * + * @return max length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxStatementLength() throws SQLException + { + if (connection.haveMinimumServerVersion("7.0")) + return 0; // actually whatever fits in size_t + else + return 16384; + } + + /** + * How many active statements can we have open at one time to + * this database? Basically, since each Statement downloads + * the results as the query is executed, we can have many. However, + * we can only really have one statement per connection going + * at once (since they are executed serially) - so we return + * one. + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxStatements() throws SQLException + { + return 1; + } + + /** + * What is the maximum length of a table name? This was found + * from pg_class.relname length + * + * @return max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxTableNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of tables that can be specified + * in a SELECT? + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxTablesInSelect() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum length of a user name? Well, we generally + * use UNIX like user names in PostgreSQL, so I think this would + * be 8. However, showing the schema for pg_user shows a length + * for username of 32. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxUserNameLength() throws SQLException + { + return 32; + } + + + /** + * What is the database's default transaction isolation level? We + * do not support this, so all transactions are SERIALIZABLE. + * + * @return the default isolation level + * @exception SQLException if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException + { + return Connection.TRANSACTION_READ_COMMITTED; + } + + /** + * Are transactions supported? If not, commit and rollback are noops + * and the isolation level is TRANSACTION_NONE. We do support + * transactions. + * + * @return true if transactions are supported + * @exception SQLException if a database access error occurs + */ + public boolean supportsTransactions() throws SQLException + { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * We only support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED + * + * @param level the values are defined in java.sql.Connection + * @return true if so + * @exception SQLException if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException + { + if (level == Connection.TRANSACTION_SERIALIZABLE || + level == Connection.TRANSACTION_READ_COMMITTED) + return true; + else + return false; + } + + /** + * Are both data definition and data manipulation transactions + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException + { + return true; + } + + /** + * Are only data manipulation statements withing a transaction + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException + { + return false; + } + + /** + * Does a data definition statement within a transaction force + * the transaction to commit? I think this means something like: + * + *

+	 * CREATE TABLE T (A INT);
+	 * INSERT INTO T (A) VALUES (2);
+	 * BEGIN;
+	 * UPDATE T SET A = A + 1;
+	 * CREATE TABLE X (A INT);
+	 * SELECT A FROM T INTO X;
+	 * COMMIT;
+	 * 

+ * + * does the CREATE TABLE call cause a commit? The answer is no. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException + { + return false; + } + + /** + * Is a data definition statement within a transaction ignored? + * It seems to be (from experiment in previous method) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException + { + return true; + } + + /** + * Get a description of stored procedures available in a catalog + * + *

Only procedure descriptions matching the schema and procedure + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME + * + *

Each procedure description has the following columns: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. Field 4 reserved (make it null) + *
  5. Field 5 reserved (make it null) + *
  6. Field 6 reserved (make it null) + *
  7. REMARKS String => explanatory comment on the procedure + *
  8. PROCEDURE_TYPE short => kind of procedure + *
      + *
    • procedureResultUnknown - May return a result + *
    • procedureNoResult - Does not return a result + *
    • procedureReturnsResult - Returns a result + *
    + *
+ * + * @param catalog - a catalog name; "" retrieves those without a + * catalog; null means drop catalog name from criteria + * @param schemaParrern - a schema name pattern; "" retrieves those + * without a schema - we ignore this parameter + * @param procedureNamePattern - a procedure name pattern + * @return ResultSet - each row is a procedure description + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[8]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = f[4] = f[5] = new Field(connection, "reserved", iVarcharOid, 32); // null; // reserved, must be null for now + f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); + f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); + + // If the pattern is null, then set it to the default + if (procedureNamePattern == null) + procedureNamePattern = "%"; + + r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '" + procedureNamePattern.toLowerCase() + "' order by proname"); + + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Procedure name + tuple[3] = tuple[4] = tuple[5] = null; // Reserved + tuple[6] = null; // Remarks + + if (r.getBoolean(2)) + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); + else + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); + + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a catalog's stored procedure parameters + * and result columns. + * + *

Only descriptions matching the schema, procedure and parameter + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME. Within this, the return value, if any, is + * first. Next are the parameter descriptions in call order. The + * column descriptions follow in column number order. + * + *

Each row in the ResultSet is a parameter description or column + * description with the following fields: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. COLUMN_NAME String => column/parameter name + *
  5. COLUMN_TYPE Short => kind of column/parameter: + *
    • procedureColumnUnknown - nobody knows + *
    • procedureColumnIn - IN parameter + *
    • procedureColumnInOut - INOUT parameter + *
    • procedureColumnOut - OUT parameter + *
    • procedureColumnReturn - procedure return value + *
    • procedureColumnResult - result column in ResultSet + *
    + *
  6. DATA_TYPE short => SQL type from java.sql.Types + *
  7. TYPE_NAME String => SQL type name + *
  8. PRECISION int => precision + *
  9. LENGTH int => length in bytes of data + *
  10. SCALE short => scale + *
  11. RADIX short => radix + *
  12. NULLABLE short => can it contain NULL? + *
    • procedureNoNulls - does not allow NULL values + *
    • procedureNullable - allows NULL values + *
    • procedureNullableUnknown - nullability unknown + *
    • REMARKS String => comment describing parameter/column + *
+ * @param catalog This is ignored in org.postgresql, advise this is set to null + * @param schemaPattern This is ignored in org.postgresql, advise this is set to null + * @param procedureNamePattern a procedure name pattern + * @param columnNamePattern a column name pattern + * @return each row is a stored procedure parameter or column description + * @exception SQLException if a database-access error occurs + * @see #getSearchStringEscape + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException + { + if (procedureNamePattern == null) + procedureNamePattern = "%"; + + if (columnNamePattern == null) + columnNamePattern = "%"; + + // for now, this returns an empty result set. + Field f[] = new Field[13]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2); + f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[7] = new Field(connection, "PRECISION", iInt4Oid, 4); + f[8] = new Field(connection, "LENGTH", iInt4Oid, 4); + f[9] = new Field(connection, "SCALE", iInt2Oid, 2); + f[10] = new Field(connection, "RADIX", iInt2Oid, 2); + f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2); + f[12] = new Field(connection, "REMARKS", iVarcharOid, 32); + + // add query loop here + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of tables available in a catalog. + * + *

Only table descriptions matching the catalog, schema, table + * name and type criteria are returned. They are ordered by + * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. + * + *

Each table description has the following columns: + * + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL + * TEMPORARY", "ALIAS", "SYNONYM". + *
  5. REMARKS String => explanatory comment on the table + *
+ * + *

The valid values for the types parameter are: + * "TABLE", "INDEX", "SEQUENCE", "SYSTEM TABLE" and "SYSTEM INDEX" + * + * @param catalog a catalog name; For org.postgresql, this is ignored, and + * should be set to null + * @param schemaPattern a schema name pattern; For org.postgresql, this is ignored, and + * should be set to null + * @param tableNamePattern a table name pattern. For all tables this should be "%" + * @param types a list of table types to include; null returns + * all types + * @return each row is a table description + * @exception SQLException if a database-access error occurs. + */ + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException + { + // Handle default value for types + if (types == null) + types = defaultTableTypes; + + if (tableNamePattern == null) + tableNamePattern = "%"; + + // the field descriptors for the new ResultSet + Field f[] = new Field[5]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "TABLE_TYPE", iVarcharOid, 32); + f[4] = new Field(connection, "REMARKS", iVarcharOid, 32); + + // Now form the query + StringBuffer sql = new StringBuffer("select relname,oid,relkind from pg_class where ("); + + boolean notFirst = false; + for (int i = 0;i < types.length;i++) + { + for (int j = 0;j < getTableTypes.length;j++) + if (getTableTypes[j][0].equals(types[i])) + { + if (notFirst) + sql.append(" or "); + sql.append(getTableTypes[j][1]); + notFirst = true; + } + } + + // Added by Stefan Andreasen + // Now take the pattern into account + sql.append(") and relname like '"); + sql.append(tableNamePattern.toLowerCase()); + sql.append("' order by relkind, relname"); + + // Now run the query + r = connection.ExecSQL(sql.toString()); + + while (r.next()) + { + byte[][] tuple = new byte[5][0]; + + // Fetch the description for the table (if any) + String getDescriptionStatement = + connection.haveMinimumServerVersion("7.2") ? + "select obj_description(" + r.getInt(2) + ",'pg_class')" : + "select description from pg_description where objoid=" + r.getInt(2); + + java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); + + byte remarks[] = null; + + if (((org.postgresql.ResultSet)dr).getTupleCount() == 1) + { + dr.next(); + remarks = dr.getBytes(1); + } + dr.close(); + + String relKind; + switch (r.getBytes(3)[0]) + { + case (byte) 'r': + relKind = "TABLE"; + break; + case (byte) 'i': + relKind = "INDEX"; + break; + case (byte) 'S': + relKind = "SEQUENCE"; + break; + case (byte) 'v': + relKind = "VIEW"; + break; + default: + relKind = null; + } + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Table name + tuple[3] = (relKind == null) ? null : relKind.getBytes(); // Table type + tuple[4] = remarks; // Remarks + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + // This array contains the valid values for the types argument + // in getTables(). + // + // Each supported type consists of it's name, and the sql where + // clause to retrieve that value. + // + // IMPORTANT: the query must be enclosed in ( ) + private static final String getTableTypes[][] = { + {"TABLE", "(relkind='r' and relhasrules='f' and relname !~ '^pg_' and relname !~ '^xinv')"}, + {"VIEW", "(relkind='v' and relname !~ '^pg_')"}, + {"INDEX", "(relkind='i' and relname !~ '^pg_')"}, + {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, + {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, + {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} + }; + + // These are the default tables, used when NULL is passed to getTables + // The choice of these provide the same behaviour as psql's \d + private static final String defaultTableTypes[] = { + "TABLE", "VIEW", "INDEX", "SEQUENCE" + }; + + /** + * Get the schema names available in this database. The results + * are ordered by schema name. + * + *

The schema column is: + *

    + *
  1. TABLE_SCHEM String => schema name + *
+ * + * @return ResultSet each row has a single String column that is a + * schema name + */ + public java.sql.ResultSet getSchemas() throws SQLException + { + // We don't use schemas, so we simply return a single schema name "". + // + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get the catalog names available in this database. The results + * are ordered by catalog name. + * + *

The catalog column is: + *

    + *
  1. TABLE_CAT String => catalog name + *
+ * + * @return ResultSet each row has a single String column that is a + * catalog name + */ + public java.sql.ResultSet getCatalogs() throws SQLException + { + return connection.createStatement().executeQuery("select datname as TABLE_CAT from pg_database;"); + } + + /** + * Get the table types available in this database. The results + * are ordered by table type. + * + *

The table type is: + *

    + *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
+ * + * @return ResultSet each row has a single String column that is a + * table type + */ + public java.sql.ResultSet getTableTypes() throws SQLException + { + Field f[] = new Field[1]; + Vector v = new Vector(); + f[0] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32); + for (int i = 0;i < getTableTypes.length;i++) + { + byte[][] tuple = new byte[1][0]; + tuple[0] = getTableTypes[i][0].getBytes(); + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of table columns available in a catalog. + * + *

Only column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. DATA_TYPE short => SQL type from java.sql.Types + *
  6. TYPE_NAME String => Data source dependent type name + *
  7. COLUMN_SIZE int => column size. For char or date + * types this is the maximum number of characters, for numeric or + * decimal types this is precision. + *
  8. BUFFER_LENGTH is not used. + *
  9. DECIMAL_DIGITS int => the number of fractional digits + *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
  11. NULLABLE int => is NULL allowed? + *
      + *
    • columnNoNulls - might not allow NULL values + *
    • columnNullable - definitely allows NULL values + *
    • columnNullableUnknown - nullability unknown + *
    + *
  12. REMARKS String => comment describing column (may be null) + *
  13. COLUMN_DEF String => default value (may be null) + *
  14. SQL_DATA_TYPE int => unused + *
  15. SQL_DATETIME_SUB int => unused + *
  16. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
  17. ORDINAL_POSITION int => index of column in table + * (starting at 1) + *
  18. IS_NULLABLE String => "NO" means column definitely + * does not allow NULL values; "YES" means the column might + * allow NULL values. An empty string means nobody knows. + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException + { + Vector v = new Vector(); // The new ResultSet tuple stuff + Field f[] = new Field[18]; // The field descriptors for the new ResultSet + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); + f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); + f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); + f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); + f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); + f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); + f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); + f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); + f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); + f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, 32); + f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid, 4); + f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); + + StringBuffer sql = new StringBuffer(512); + + /* Build a >= 7.1 SQL statement to list all columns */ + sql.append("select " + + (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") + + " c.relname, " + + " a.attname, " + + " a.atttypid, " + + " a.attnum, " + + " a.attnotnull, " + + " a.attlen, " + + " a.atttypmod, " + + " d.adsrc, " + + " t.typname, " + + /* Use the new col_description in 7.2 or an additional outer join in 7.1 */ + (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") + + "from" + + " (" + + " (pg_class c inner join pg_attribute a on" + + " (" + + " a.attrelid=c.oid"); + + if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) + { + sql.append(" and c.relname like \'" + tableNamePattern + "\'"); + } + + if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) + { + sql.append(" and a.attname like \'" + columnNamePattern + "\'"); + } + sql.append( - " left outer join pg_description e on" + + " and a.attnum > 0" + + " )" + + " ) inner join pg_type t on" + + " (" + + " t.oid = a.atttypid" + + " )" + + " )" + + " left outer join pg_attrdef d on" + " (" + - " e.objoid = a.oid" + + " c.oid = d.adrelid" + + " and a.attnum = d.adnum" + " ) "); + + if (!connection.haveMinimumServerVersion("7.2")) + { + /* Only for 7.1 */ + sql.append( + " left outer join pg_description e on" + + " (" + + " e.objoid = a.oid" + + " ) "); + } + + sql.append("order by" + + " c.relname, a.attnum"); + + java.sql.ResultSet r = connection.ExecSQL(sql.toString()); + while (r.next()) + { + byte[][] tuple = new byte[18][0]; + + String nullFlag = r.getString(6); + String typname = r.getString(10); + + tuple[0] = null; // Catalog name, not supported + tuple[1] = null; // Schema name, not supported + tuple[2] = r.getBytes(2); // Table name + tuple[3] = r.getBytes(3); // Column name + tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type + tuple[5] = typname.getBytes(); // Type name + + // Column size + // Looking at the psql source, + // I think the length of a varchar as specified when the table was created + // should be extracted from atttypmod which contains this length + sizeof(int32) + if (typname.equals("bpchar") || typname.equals("varchar")) + { + int atttypmod = r.getInt(8); + tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); + } + else + { + tuple[6] = r.getBytes(7); + } + + tuple[7] = null; // Buffer length + tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? + tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal + tuple[10] = Integer.toString(nullFlag.equals("f") ? + java.sql.DatabaseMetaData.columnNullable : + java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable + tuple[11] = r.getBytes(11); // Description (if any) + tuple[12] = r.getBytes(9); // Column default + tuple[13] = null; // sql data type (unused) + tuple[14] = null; // sql datetime sub (unused) + tuple[15] = tuple[6]; // char octet length + tuple[16] = r.getBytes(5); // ordinal position + tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable + + v.addElement(tuple); + } + r.close(); + + return new ResultSet(connection, f, v, "OK", 1); } - sql.append("order by" + - " c.relname, a.attnum"); - - java.sql.ResultSet r = connection.ExecSQL(sql.toString()); - while (r.next()) { - byte[][] tuple = new byte[18][0]; - - String nullFlag = r.getString(6); - String typname = r.getString(10); - - tuple[0] = null; // Catalog name, not supported - tuple[1] = null; // Schema name, not supported - tuple[2] = r.getBytes(2); // Table name - tuple[3] = r.getBytes(3); // Column name - tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type - tuple[5] = typname.getBytes(); // Type name - - // Column size - // Looking at the psql source, - // I think the length of a varchar as specified when the table was created - // should be extracted from atttypmod which contains this length + sizeof(int32) - if (typname.equals("bpchar") || typname.equals("varchar")) { - int atttypmod = r.getInt(8); - tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); - } else { - tuple[6] = r.getBytes(7); + /** + * Get a description of the access rights for a table's columns. + * + *

Only privileges matching the column name criteria are + * returned. They are ordered by COLUMN_NAME and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException + { + Field f[] = new Field[8]; + Vector v = new Vector(); + + if (table == null) + table = "%"; + + if (columnNamePattern == null) + columnNamePattern = "%"; + else + columnNamePattern = columnNamePattern.toLowerCase(); + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "GRANTOR", iVarcharOid, 32); + f[5] = new Field(connection, "GRANTEE", iVarcharOid, 32); + f[6] = new Field(connection, "PRIVILEGE", iVarcharOid, 32); + f[7] = new Field(connection, "IS_GRANTABLE", iVarcharOid, 32); + + // This is taken direct from the psql source + java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '" + table.toLowerCase() + "' ORDER BY relname"); + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + tuple[0] = tuple[1] = "".getBytes(); + DriverManager.println("relname=\"" + r.getString(1) + "\" relacl=\"" + r.getString(2) + "\""); + + // For now, don't add to the result as relacl needs to be processed. + //v.addElement(tuple); } - tuple[7] = null; // Buffer length - tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? - tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal - tuple[10] = Integer.toString(nullFlag.equals("f") ? - java.sql.DatabaseMetaData.columnNullable : - java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable - tuple[11] = r.getBytes(11); // Description (if any) - tuple[12] = r.getBytes(9); // Column default - tuple[13] = null; // sql data type (unused) - tuple[14] = null; // sql datetime sub (unused) - tuple[15] = tuple[6]; // char octet length - tuple[16] = r.getBytes(5); // ordinal position - tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable + return new ResultSet(connection, f, v, "OK", 1); + } - v.addElement(tuple); + /** + * Get a description of the access rights for each table available + * in a catalog. + * + * This method is currently unimplemented. + * + *

Only privileges matching the schema and table name + * criteria are returned. They are ordered by TABLE_SCHEM, + * TABLE_NAME, and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @return ResultSet each row is a table privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of a table's optimal set of columns that + * uniquely identifies a row. They are ordered by SCOPE. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => actual scope of result + *
      + *
    • bestRowTemporary - very temporary, while using row + *
    • bestRowTransaction - valid for remainder of current transaction + *
    • bestRowSession - valid for remainder of current session + *
    + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => not used + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • bestRowUnknown - may or may not be pseudo column + *
    • bestRowNotPseudo - is NOT a pseudo column + *
    • bestRowPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param scope the scope of interest; use same values as SCOPE + * @param nullable include columns that are nullable? + * @return ResultSet each row is a column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[8]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); + f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); + f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); + f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); + f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a table's columns that are automatically + * updated when any value in a row is updated. They are + * unordered. + * + * This method is currently unimplemented. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => is not used + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => length of column value in bytes + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • versionColumnUnknown - may or may not be pseudo column + *
    • versionColumnNotPseudo - is NOT a pseudo column + *
    • versionColumnPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of a table's primary key columns. They + * are ordered by COLUMN_NAME. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. KEY_SEQ short => sequence number within primary key + *
  6. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException + { + return connection.createStatement().executeQuery("SELECT " + + "'' as TABLE_CAT," + + "'' AS TABLE_SCHEM," + + "bc.relname AS TABLE_NAME," + + "a.attname AS COLUMN_NAME," + + "a.attnum as KEY_SEQ," + + "ic.relname as PK_NAME " + + " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a" + + " WHERE bc.relkind = 'r' " + // -- not indices + " and upper(bc.relname) = upper('" + table + "')" + + " and i.indrelid = bc.oid" + + " and i.indexrelid = ic.oid" + + " and ic.oid = a.attrelid" + + " and i.indisprimary='t' " + + " ORDER BY table_name, pk_name, key_seq" + ); + } + + private void importLoop(Vector tuples, java.sql.ResultSet keyRelation) throws SQLException + { + String s, s2; + String origTable = null, primTable = new String(""), schema; + int i; + Vector v = new Vector(); + + s = keyRelation.getString(1); + s2 = s; + //System.out.println(s); + + for (i = 0;;i++) + { + s = s.substring(s.indexOf("\\000") + 4); + if (s.compareTo("") == 0) + { + //System.out.println(); + break; + } + s2 = s.substring(0, s.indexOf("\\000")); + switch (i) + { + case 0: + origTable = s2; + break; + case 1: + primTable = s2; + break; + case 2: + schema = s2; + break; + default: + v.addElement(s2); + } + } + + java.sql.ResultSet rstmp = connection.ExecSQL("select * from " + origTable + " where 1=0"); + java.sql.ResultSetMetaData origCols = rstmp.getMetaData(); + + String stmp; + // Vector tuples=new Vector(); + byte tuple[][]; + + // the foreign keys are only on even positions in the Vector. + for (i = 0;i < v.size();i += 2) + { + stmp = (String)v.elementAt(i); + + for (int j = 1;j <= origCols.getColumnCount();j++) + { + if (stmp.compareTo(origCols.getColumnName(j)) == 0) + { + tuple = new byte[14][0]; + + for (int k = 0;k < 14;k++) + tuple[k] = null; + + //PKTABLE_NAME + tuple[2] = primTable.getBytes(); + //PKTABLE_COLUMN + stmp = (String)v.elementAt(i + 1); + tuple[3] = stmp.getBytes(); + //FKTABLE_NAME + tuple[6] = origTable.getBytes(); + //FKCOLUMN_NAME + tuple[7] = origCols.getColumnName(j).getBytes(); + //KEY_SEQ + tuple[8] = Integer.toString(j).getBytes(); + + tuples.addElement(tuple); + + //System.out.println(origCols.getColumnName(j)+ + //": "+j+" -> "+primTable+": "+ + //(String)v.elementAt(i+1)); + break; + } + } + } + + //return tuples; + } + + /** + * Get a description of the primary key columns that are + * referenced by a table's foreign key columns (the primary keys + * imported by a table). They are ordered by PKTABLE_CAT, + * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + * + *

Each primary key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog + * being imported (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema + * being imported (may be null) + *
  3. PKTABLE_NAME String => primary key table name + * being imported + *
  4. PKCOLUMN_NAME String => primary key column name + * being imported + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + *
  8. FKCOLUMN_NAME String => foreign key column name + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key name (may be null) + *
  13. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException + { + // Added by Ola Sundell + // FIXME: error checking galore! + java.sql.ResultSet rsret; + Field f[] = new Field[14]; + byte tuple[][]; + + f[0] = new Field(connection, "PKTABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PKTABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PKTABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "PKCOLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "FKTABLE_CAT", iVarcharOid, 32); + f[5] = new Field(connection, "FKTABLE_SCHEM", iVarcharOid, 32); + f[6] = new Field(connection, "FKTABLE_NAME", iVarcharOid, 32); + f[7] = new Field(connection, "FKCOLUMN_NAME", iVarcharOid, 32); + f[8] = new Field(connection, "KEY_SEQ", iInt2Oid, 2); + f[9] = new Field(connection, "UPDATE_RULE", iInt2Oid, 2); + f[10] = new Field(connection, "DELETE_RULE", iInt2Oid, 2); + f[11] = new Field(connection, "FK_NAME", iVarcharOid, 32); + f[12] = new Field(connection, "PK_NAME", iVarcharOid, 32); + f[13] = new Field(connection, "DEFERRABILITY", iInt2Oid, 2); + + java.sql.ResultSet rs = connection.ExecSQL("select t.tgargs " + + "from pg_class as c, pg_trigger as t " + + "where c.relname like '" + table + "' and c.relfilenode=t.tgrelid"); + Vector tuples = new Vector(); + + while (rs.next()) + { + importLoop(tuples, rs); + } + + rsret = new ResultSet(connection, f, tuples, "OK", 1); + + return rsret; + } + + /** + * Get a description of a foreign key columns that reference a + * table's primary key columns (the foreign keys exported by a + * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + * + * This method is currently unimplemented. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of the foreign key columns in the foreign key + * table that reference the primary key columns of the primary key + * table (describe how one table imports another's key.) This + * should normally return a single foreign key/primary key pair + * (most tables only import a foreign key from a table once.) They + * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and + * KEY_SEQ. + * + * This method is currently unimplemented. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of all the standard SQL types supported by + * this database. They are ordered by DATA_TYPE and then by how + * closely the data type maps to the corresponding JDBC SQL type. + * + *

Each type description has the following columns: + *

    + *
  1. TYPE_NAME String => Type name + *
  2. DATA_TYPE short => SQL data type from java.sql.Types + *
  3. PRECISION int => maximum precision + *
  4. LITERAL_PREFIX String => prefix used to quote a literal + * (may be null) + *
  5. LITERAL_SUFFIX String => suffix used to quote a literal + (may be null) + *
  6. CREATE_PARAMS String => parameters used in creating + * the type (may be null) + *
  7. NULLABLE short => can you use NULL for this type? + *
      + *
    • typeNoNulls - does not allow NULL values + *
    • typeNullable - allows NULL values + *
    • typeNullableUnknown - nullability unknown + *
    + *
  8. CASE_SENSITIVE boolean=> is it case sensitive? + *
  9. SEARCHABLE short => can you use "WHERE" based on this type: + *
      + *
    • typePredNone - No support + *
    • typePredChar - Only supported with WHERE .. LIKE + *
    • typePredBasic - Supported except for WHERE .. LIKE + *
    • typeSearchable - Supported for all WHERE .. + *
    + *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? + *
  11. FIXED_PREC_SCALE boolean => can it be a money value? + *
  12. AUTO_INCREMENT boolean => can it be used for an + * auto-increment value? + *
  13. LOCAL_TYPE_NAME String => localized version of type name + * (may be null) + *
  14. MINIMUM_SCALE short => minimum scale supported + *
  15. MAXIMUM_SCALE short => maximum scale supported + *
  16. SQL_DATA_TYPE int => unused + *
  17. SQL_DATETIME_SUB int => unused + *
  18. NUM_PREC_RADIX int => usually 2 or 10 + *
+ * + * @return ResultSet each row is a SQL type description + */ + public java.sql.ResultSet getTypeInfo() throws SQLException + { + java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); + if (rs != null) + { + Field f[] = new Field[18]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[2] = new Field(connection, "PRECISION", iInt4Oid, 4); + f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, 32); + f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, 32); + f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, 32); + f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2); + f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1); + f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2); + f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1); + f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1); + f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1); + f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, 32); + f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2); + f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2); + f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); + f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); + f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); + + // cache some results, this will keep memory useage down, and speed + // things up a little. + byte b9[] = "9".getBytes(); + byte b10[] = "10".getBytes(); + byte bf[] = "f".getBytes(); + byte bnn[] = Integer.toString(typeNoNulls).getBytes(); + byte bts[] = Integer.toString(typeSearchable).getBytes(); + + while (rs.next()) + { + byte[][] tuple = new byte[18][]; + String typname = rs.getString(1); + tuple[0] = typname.getBytes(); + tuple[1] = Integer.toString(connection.getSQLType(typname)).getBytes(); + tuple[2] = b9; // for now + tuple[6] = bnn; // for now + tuple[7] = bf; // false for now - not case sensitive + tuple[8] = bts; + tuple[9] = bf; // false for now - it's signed + tuple[10] = bf; // false for now - must handle money + tuple[11] = bf; // false for now - handle autoincrement + // 12 - LOCAL_TYPE_NAME is null + // 13 & 14 ? + // 15 & 16 are unused so we return null + tuple[17] = b10; // everything is base 10 + v.addElement(tuple); + } + rs.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + throw new PSQLException("postgresql.metadata.unavailable"); + } + + /** + * Get a description of a table's indices and statistics. They are + * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + * + *

Each index column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. NON_UNIQUE boolean => Can index values be non-unique? + * false when TYPE is tableIndexStatistic + *
  5. INDEX_QUALIFIER String => index catalog (may be null); + * null when TYPE is tableIndexStatistic + *
  6. INDEX_NAME String => index name; null when TYPE is + * tableIndexStatistic + *
  7. TYPE short => index type: + *
      + *
    • tableIndexStatistic - this identifies table statistics that are + * returned in conjuction with a table's index descriptions + *
    • tableIndexClustered - this is a clustered index + *
    • tableIndexHashed - this is a hashed index + *
    • tableIndexOther - this is some other style of index + *
    + *
  8. ORDINAL_POSITION short => column sequence number + * within index; zero when TYPE is tableIndexStatistic + *
  9. COLUMN_NAME String => column name; null when TYPE is + * tableIndexStatistic + *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending + * "D" => descending, may be null if sort sequence is not supported; + * null when TYPE is tableIndexStatistic + *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then + * this is the number of rows in the table; otherwise it is the + * number of unique values in the index. + *
  12. PAGES int => When TYPE is tableIndexStatisic then + * this is the number of pages used for the table, otherwise it + * is the number of pages used for the current index. + *
  13. FILTER_CONDITION String => Filter condition, if any. + * (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those without a schema + * @param table a table name + * @param unique when true, return only indices for unique values; + * when false, return indices regardless of whether unique or not + * @param approximate when true, result is allowed to reflect approximate + * or out of data values; when false, results are requested to be + * accurate + * @return ResultSet each row is an index column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getIndexInfo(String catalog, String schema, String tableName, boolean unique, boolean approximate) throws SQLException + { + Field f[] = new Field[13]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "NON_UNIQUE", iBoolOid, 1); + f[4] = new Field(connection, "INDEX_QUALIFIER", iVarcharOid, 32); + f[5] = new Field(connection, "INDEX_NAME", iVarcharOid, 32); + f[6] = new Field(connection, "TYPE", iInt2Oid, 2); + f[7] = new Field(connection, "ORDINAL_POSITION", iInt2Oid, 2); + f[8] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[9] = new Field(connection, "ASC_OR_DESC", iVarcharOid, 32); + f[10] = new Field(connection, "CARDINALITY", iInt4Oid, 4); + f[11] = new Field(connection, "PAGES", iInt4Oid, 4); + f[12] = new Field(connection, "FILTER_CONDITION", iVarcharOid, 32); + + r = connection.ExecSQL("select " + + "c.relname, " + + "x.indisunique, " + + "i.relname, " + + "x.indisclustered, " + + "a.amname, " + + "x.indkey, " + + "c.reltuples, " + + "c.relpages, " + + "x.indexrelid " + + "FROM pg_index x, pg_class c, pg_class i, pg_am a " + + "WHERE ((c.relname = '" + tableName.toLowerCase() + "') " + + " AND (c.oid = x.indrelid) " + + " AND (i.oid = x.indexrelid) " + + " AND (i.relam = a.oid)) " + + "ORDER BY x.indisunique DESC, " + + " x.indisclustered, a.amname, i.relname"); + while (r.next()) + { + // indkey is an array of column ordinals (integers). In the JDBC + // interface, this has to be separated out into a separate + // tuple for each indexed column. Also, getArray() is not yet + // implemented for Postgres JDBC, so we parse by hand. + String columnOrdinalString = r.getString(6); + StringTokenizer stok = new StringTokenizer(columnOrdinalString); + int [] columnOrdinals = new int[stok.countTokens()]; + int o = 0; + while (stok.hasMoreTokens()) + { + columnOrdinals[o++] = Integer.parseInt(stok.nextToken()); + } + for (int i = 0; i < columnOrdinals.length; i++) + { + byte [] [] tuple = new byte [13] []; + tuple[0] = "".getBytes(); + tuple[1] = "".getBytes(); + tuple[2] = r.getBytes(1); + tuple[3] = r.getBoolean(2) ? "f".getBytes() : "t".getBytes(); + tuple[4] = null; + tuple[5] = r.getBytes(3); + tuple[6] = r.getBoolean(4) ? + Integer.toString(tableIndexClustered).getBytes() : + r.getString(5).equals("hash") ? + Integer.toString(tableIndexHashed).getBytes() : + Integer.toString(tableIndexOther).getBytes(); + tuple[7] = Integer.toString(i + 1).getBytes(); + java.sql.ResultSet columnNameRS = connection.ExecSQL("select a.attname FROM pg_attribute a WHERE (a.attnum = " + columnOrdinals[i] + ") AND (a.attrelid = " + r.getInt(9) + ")"); + columnNameRS.next(); + tuple[8] = columnNameRS.getBytes(1); + tuple[9] = null; // sort sequence ??? + tuple[10] = r.getBytes(7); // inexact + tuple[11] = r.getBytes(8); + tuple[12] = null; + v.addElement(tuple); + } + } + + return new ResultSet(connection, f, v, "OK", 1); } - r.close(); - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of the access rights for a table's columns. - * - *

Only privileges matching the column name criteria are - * returned. They are ordered by COLUMN_NAME and PRIVILEGE. - * - *

Each privilige description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. GRANTOR => grantor of access (may be null) - *
  6. GRANTEE String => grantee of access - *
  7. PRIVILEGE String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - *
  8. IS_GRANTABLE String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @param columnNamePattern a column name pattern - * @return ResultSet each row is a column privilege description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException - { - Field f[] = new Field[8]; - Vector v = new Vector(); - - if(table==null) - table="%"; - - if(columnNamePattern==null) - columnNamePattern="%"; - else - columnNamePattern=columnNamePattern.toLowerCase(); - - f[0] = new Field(connection,"TABLE_CAT",iVarcharOid,32); - f[1] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32); - f[2] = new Field(connection,"TABLE_NAME",iVarcharOid,32); - f[3] = new Field(connection,"COLUMN_NAME",iVarcharOid,32); - f[4] = new Field(connection,"GRANTOR",iVarcharOid,32); - f[5] = new Field(connection,"GRANTEE",iVarcharOid,32); - f[6] = new Field(connection,"PRIVILEGE",iVarcharOid,32); - f[7] = new Field(connection,"IS_GRANTABLE",iVarcharOid,32); - - // This is taken direct from the psql source - java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname"); - while(r.next()) { - byte[][] tuple = new byte[8][0]; - tuple[0] = tuple[1]= "".getBytes(); - DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\""); - - // For now, don't add to the result as relacl needs to be processed. - //v.addElement(tuple); - } - - return new ResultSet(connection,f,v,"OK",1); - } - - /** - * Get a description of the access rights for each table available - * in a catalog. - * - * This method is currently unimplemented. - * - *

Only privileges matching the schema and table name - * criteria are returned. They are ordered by TABLE_SCHEM, - * TABLE_NAME, and PRIVILEGE. - * - *

Each privilige description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. GRANTOR => grantor of access (may be null) - *
  6. GRANTEE String => grantee of access - *
  7. PRIVILEGE String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - *
  8. IS_GRANTABLE String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schemaPattern a schema name pattern; "" retrieves those - * without a schema - * @param tableNamePattern a table name pattern - * @return ResultSet each row is a table privilege description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of a table's optimal set of columns that - * uniquely identifies a row. They are ordered by SCOPE. - * - *

Each column description has the following columns: - *

    - *
  1. SCOPE short => actual scope of result - *
      - *
    • bestRowTemporary - very temporary, while using row - *
    • bestRowTransaction - valid for remainder of current transaction - *
    • bestRowSession - valid for remainder of current session - *
    - *
  2. COLUMN_NAME String => column name - *
  3. DATA_TYPE short => SQL data type from java.sql.Types - *
  4. TYPE_NAME String => Data source dependent type name - *
  5. COLUMN_SIZE int => precision - *
  6. BUFFER_LENGTH int => not used - *
  7. DECIMAL_DIGITS short => scale - *
  8. PSEUDO_COLUMN short => is this a pseudo column - * like an Oracle ROWID - *
      - *
    • bestRowUnknown - may or may not be pseudo column - *
    • bestRowNotPseudo - is NOT a pseudo column - *
    • bestRowPseudo - is a pseudo column - *
    - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @param scope the scope of interest; use same values as SCOPE - * @param nullable include columns that are nullable? - * @return ResultSet each row is a column description - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException - { - // for now, this returns an empty result set. - Field f[] = new Field[8]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); - f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); - f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); - f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of a table's columns that are automatically - * updated when any value in a row is updated. They are - * unordered. - * - * This method is currently unimplemented. - * - *

Each column description has the following columns: - *

    - *
  1. SCOPE short => is not used - *
  2. COLUMN_NAME String => column name - *
  3. DATA_TYPE short => SQL data type from java.sql.Types - *
  4. TYPE_NAME String => Data source dependent type name - *
  5. COLUMN_SIZE int => precision - *
  6. BUFFER_LENGTH int => length of column value in bytes - *
  7. DECIMAL_DIGITS short => scale - *
  8. PSEUDO_COLUMN short => is this a pseudo column - * like an Oracle ROWID - *
      - *
    • versionColumnUnknown - may or may not be pseudo column - *
    • versionColumnNotPseudo - is NOT a pseudo column - *
    • versionColumnPseudo - is a pseudo column - *
    - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @return ResultSet each row is a column description - */ - public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of a table's primary key columns. They - * are ordered by COLUMN_NAME. - * - *

Each column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. KEY_SEQ short => sequence number within primary key - *
  6. PK_NAME String => primary key name (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a primary key column description - */ - public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException - { - return connection.createStatement().executeQuery("SELECT " + - "'' as TABLE_CAT," + - "'' AS TABLE_SCHEM," + - "bc.relname AS TABLE_NAME," + - "a.attname AS COLUMN_NAME," + - "a.attnum as KEY_SEQ,"+ - "ic.relname as PK_NAME " + - " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a" + - " WHERE bc.relkind = 'r' " + // -- not indices - " and upper(bc.relname) = upper('"+table+"')" + - " and i.indrelid = bc.oid" + - " and i.indexrelid = ic.oid" + - " and ic.oid = a.attrelid" + - " and i.indisprimary='t' " + - " ORDER BY table_name, pk_name, key_seq" - ); - } - - private void importLoop(Vector tuples, java.sql.ResultSet keyRelation) throws SQLException { - String s,s2; - String origTable=null, primTable=new String(""), schema; - int i; - Vector v=new Vector(); - - s=keyRelation.getString(1); - s2=s; - //System.out.println(s); - - for (i=0;;i++) { - s=s.substring(s.indexOf("\\000")+4); - if (s.compareTo("")==0) { - //System.out.println(); - break; - } - s2=s.substring(0,s.indexOf("\\000")); - switch (i) { - case 0: - origTable=s2; - break; - case 1: - primTable=s2; - break; - case 2: - schema=s2; - break; - default: - v.addElement(s2); - } - } - - java.sql.ResultSet rstmp=connection.ExecSQL("select * from "+origTable+" where 1=0"); - java.sql.ResultSetMetaData origCols=rstmp.getMetaData(); - - String stmp; - // Vector tuples=new Vector(); - byte tuple[][]; - - // the foreign keys are only on even positions in the Vector. - for (i=0;i "+primTable+": "+ - //(String)v.elementAt(i+1)); - break; - } - } - } - - //return tuples; - } - - /** - * Get a description of the primary key columns that are - * referenced by a table's foreign key columns (the primary keys - * imported by a table). They are ordered by PKTABLE_CAT, - * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. - * - *

Each primary key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog - * being imported (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema - * being imported (may be null) - *
  3. PKTABLE_NAME String => primary key table name - * being imported - *
  4. PKCOLUMN_NAME String => primary key column name - * being imported - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - *
  8. FKCOLUMN_NAME String => foreign key column name - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key name (may be null) - *
  13. PK_NAME String => primary key name (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a primary key column description - * @see #getExportedKeys - */ - public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException - { - // Added by Ola Sundell - // FIXME: error checking galore! - java.sql.ResultSet rsret; - Field f[]=new Field[14]; - byte tuple[][]; - - f[0]=new Field(connection, "PKTABLE_CAT", iVarcharOid, 32); - f[1]=new Field(connection, "PKTABLE_SCHEM", iVarcharOid, 32); - f[2]=new Field(connection, "PKTABLE_NAME", iVarcharOid, 32); - f[3]=new Field(connection, "PKCOLUMN_NAME", iVarcharOid, 32); - f[4]=new Field(connection, "FKTABLE_CAT", iVarcharOid, 32); - f[5]=new Field(connection, "FKTABLE_SCHEM", iVarcharOid, 32); - f[6]=new Field(connection, "FKTABLE_NAME", iVarcharOid, 32); - f[7]=new Field(connection, "FKCOLUMN_NAME", iVarcharOid, 32); - f[8]=new Field(connection, "KEY_SEQ", iInt2Oid, 2); - f[9]=new Field(connection, "UPDATE_RULE", iInt2Oid, 2); - f[10]=new Field(connection, "DELETE_RULE", iInt2Oid, 2); - f[11]=new Field(connection, "FK_NAME", iVarcharOid, 32); - f[12]=new Field(connection, "PK_NAME", iVarcharOid, 32); - f[13]=new Field(connection, "DEFERRABILITY", iInt2Oid, 2); - - java.sql.ResultSet rs=connection.ExecSQL("select t.tgargs "+ - "from pg_class as c, pg_trigger as t "+ - "where c.relname like '"+table+"' and c.relfilenode=t.tgrelid"); - Vector tuples=new Vector(); - - while (rs.next()) { - importLoop(tuples,rs); - } - - rsret=new ResultSet(connection, f, tuples, "OK", 1); - - return rsret; - } - - /** - * Get a description of a foreign key columns that reference a - * table's primary key columns (the foreign keys exported by a - * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, - * FKTABLE_NAME, and KEY_SEQ. - * - * This method is currently unimplemented. - * - *

Each foreign key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) - *
  3. PKTABLE_NAME String => primary key table name - *
  4. PKCOLUMN_NAME String => primary key column name - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - * being exported (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - * being exported (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - * being exported - *
  8. FKCOLUMN_NAME String => foreign key column name - * being exported - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key identifier (may be null) - *
  13. PK_NAME String => primary key identifier (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a foreign key column description - * @see #getImportedKeys - */ - public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of the foreign key columns in the foreign key - * table that reference the primary key columns of the primary key - * table (describe how one table imports another's key.) This - * should normally return a single foreign key/primary key pair - * (most tables only import a foreign key from a table once.) They - * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and - * KEY_SEQ. - * - * This method is currently unimplemented. - * - *

Each foreign key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) - *
  3. PKTABLE_NAME String => primary key table name - *
  4. PKCOLUMN_NAME String => primary key column name - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - * being exported (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - * being exported (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - * being exported - *
  8. FKCOLUMN_NAME String => foreign key column name - * being exported - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key identifier (may be null) - *
  13. PK_NAME String => primary key identifier (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a foreign key column description - * @see #getImportedKeys - */ - public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of all the standard SQL types supported by - * this database. They are ordered by DATA_TYPE and then by how - * closely the data type maps to the corresponding JDBC SQL type. - * - *

Each type description has the following columns: - *

    - *
  1. TYPE_NAME String => Type name - *
  2. DATA_TYPE short => SQL data type from java.sql.Types - *
  3. PRECISION int => maximum precision - *
  4. LITERAL_PREFIX String => prefix used to quote a literal - * (may be null) - *
  5. LITERAL_SUFFIX String => suffix used to quote a literal - (may be null) - *
  6. CREATE_PARAMS String => parameters used in creating - * the type (may be null) - *
  7. NULLABLE short => can you use NULL for this type? - *
      - *
    • typeNoNulls - does not allow NULL values - *
    • typeNullable - allows NULL values - *
    • typeNullableUnknown - nullability unknown - *
    - *
  8. CASE_SENSITIVE boolean=> is it case sensitive? - *
  9. SEARCHABLE short => can you use "WHERE" based on this type: - *
      - *
    • typePredNone - No support - *
    • typePredChar - Only supported with WHERE .. LIKE - *
    • typePredBasic - Supported except for WHERE .. LIKE - *
    • typeSearchable - Supported for all WHERE .. - *
    - *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? - *
  11. FIXED_PREC_SCALE boolean => can it be a money value? - *
  12. AUTO_INCREMENT boolean => can it be used for an - * auto-increment value? - *
  13. LOCAL_TYPE_NAME String => localized version of type name - * (may be null) - *
  14. MINIMUM_SCALE short => minimum scale supported - *
  15. MAXIMUM_SCALE short => maximum scale supported - *
  16. SQL_DATA_TYPE int => unused - *
  17. SQL_DATETIME_SUB int => unused - *
  18. NUM_PREC_RADIX int => usually 2 or 10 - *
- * - * @return ResultSet each row is a SQL type description - */ - public java.sql.ResultSet getTypeInfo() throws SQLException - { - java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); - if(rs!=null) { - Field f[] = new Field[18]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[2] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, 32); - f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, 32); - f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, 32); - f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1); - f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2); - f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1); - f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1); - f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1); - f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, 32); - f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2); - f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2); - f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - - // cache some results, this will keep memory useage down, and speed - // things up a little. - byte b9[] = "9".getBytes(); - byte b10[] = "10".getBytes(); - byte bf[] = "f".getBytes(); - byte bnn[] = Integer.toString(typeNoNulls).getBytes(); - byte bts[] = Integer.toString(typeSearchable).getBytes(); - - while(rs.next()) { - byte[][] tuple = new byte[18][]; - String typname=rs.getString(1); - tuple[0] = typname.getBytes(); - tuple[1] = Integer.toString(connection.getSQLType(typname)).getBytes(); - tuple[2] = b9; // for now - tuple[6] = bnn; // for now - tuple[7] = bf; // false for now - not case sensitive - tuple[8] = bts; - tuple[9] = bf; // false for now - it's signed - tuple[10] = bf; // false for now - must handle money - tuple[11] = bf; // false for now - handle autoincrement - // 12 - LOCAL_TYPE_NAME is null - // 13 & 14 ? - // 15 & 16 are unused so we return null - tuple[17] = b10; // everything is base 10 - v.addElement(tuple); - } - rs.close(); - return new ResultSet(connection, f, v, "OK", 1); - } - - throw new PSQLException("postgresql.metadata.unavailable"); - } - - /** - * Get a description of a table's indices and statistics. They are - * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. - * - *

Each index column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. NON_UNIQUE boolean => Can index values be non-unique? - * false when TYPE is tableIndexStatistic - *
  5. INDEX_QUALIFIER String => index catalog (may be null); - * null when TYPE is tableIndexStatistic - *
  6. INDEX_NAME String => index name; null when TYPE is - * tableIndexStatistic - *
  7. TYPE short => index type: - *
      - *
    • tableIndexStatistic - this identifies table statistics that are - * returned in conjuction with a table's index descriptions - *
    • tableIndexClustered - this is a clustered index - *
    • tableIndexHashed - this is a hashed index - *
    • tableIndexOther - this is some other style of index - *
    - *
  8. ORDINAL_POSITION short => column sequence number - * within index; zero when TYPE is tableIndexStatistic - *
  9. COLUMN_NAME String => column name; null when TYPE is - * tableIndexStatistic - *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending - * "D" => descending, may be null if sort sequence is not supported; - * null when TYPE is tableIndexStatistic - *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then - * this is the number of rows in the table; otherwise it is the - * number of unique values in the index. - *
  12. PAGES int => When TYPE is tableIndexStatisic then - * this is the number of pages used for the table, otherwise it - * is the number of pages used for the current index. - *
  13. FILTER_CONDITION String => Filter condition, if any. - * (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those without a schema - * @param table a table name - * @param unique when true, return only indices for unique values; - * when false, return indices regardless of whether unique or not - * @param approximate when true, result is allowed to reflect approximate - * or out of data values; when false, results are requested to be - * accurate - * @return ResultSet each row is an index column description - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getIndexInfo(String catalog, String schema, String tableName, boolean unique, boolean approximate) throws SQLException - { - Field f[] = new Field[13]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "NON_UNIQUE", iBoolOid, 1); - f[4] = new Field(connection, "INDEX_QUALIFIER", iVarcharOid, 32); - f[5] = new Field(connection, "INDEX_NAME", iVarcharOid, 32); - f[6] = new Field(connection, "TYPE", iInt2Oid, 2); - f[7] = new Field(connection, "ORDINAL_POSITION", iInt2Oid, 2); - f[8] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[9] = new Field(connection, "ASC_OR_DESC", iVarcharOid, 32); - f[10] = new Field(connection, "CARDINALITY", iInt4Oid, 4); - f[11] = new Field(connection, "PAGES", iInt4Oid, 4); - f[12] = new Field(connection, "FILTER_CONDITION", iVarcharOid, 32); - - r = connection.ExecSQL("select " + - "c.relname, " + - "x.indisunique, " + - "i.relname, " + - "x.indisclustered, " + - "a.amname, " + - "x.indkey, " + - "c.reltuples, " + - "c.relpages, " + - "x.indexrelid " + - "FROM pg_index x, pg_class c, pg_class i, pg_am a " + - "WHERE ((c.relname = '" + tableName.toLowerCase() + "') " + - " AND (c.oid = x.indrelid) " + - " AND (i.oid = x.indexrelid) " + - " AND (i.relam = a.oid)) " + - "ORDER BY x.indisunique DESC, " + - " x.indisclustered, a.amname, i.relname"); - while (r.next()) { - // indkey is an array of column ordinals (integers). In the JDBC - // interface, this has to be separated out into a separate - // tuple for each indexed column. Also, getArray() is not yet - // implemented for Postgres JDBC, so we parse by hand. - String columnOrdinalString = r.getString(6); - StringTokenizer stok = new StringTokenizer(columnOrdinalString); - int [] columnOrdinals = new int[stok.countTokens()]; - int o = 0; - while (stok.hasMoreTokens()) { - columnOrdinals[o++] = Integer.parseInt(stok.nextToken()); - } - for (int i = 0; i < columnOrdinals.length; i++) { - byte [] [] tuple = new byte [13] []; - tuple[0] = "".getBytes(); - tuple[1] = "".getBytes(); - tuple[2] = r.getBytes(1); - tuple[3] = r.getBoolean(2) ? "f".getBytes() : "t".getBytes(); - tuple[4] = null; - tuple[5] = r.getBytes(3); - tuple[6] = r.getBoolean(4) ? - Integer.toString(tableIndexClustered).getBytes() : - r.getString(5).equals("hash") ? - Integer.toString(tableIndexHashed).getBytes() : - Integer.toString(tableIndexOther).getBytes(); - tuple[7] = Integer.toString(i + 1).getBytes(); - java.sql.ResultSet columnNameRS = connection.ExecSQL("select a.attname FROM pg_attribute a WHERE (a.attnum = " + columnOrdinals[i] + ") AND (a.attrelid = " + r.getInt(9) + ")"); - columnNameRS.next(); - tuple[8] = columnNameRS.getBytes(1); - tuple[9] = null; // sort sequence ??? - tuple[10] = r.getBytes(7); // inexact - tuple[11] = r.getBytes(8); - tuple[12] = null; - v.addElement(tuple); - } - } - - return new ResultSet(connection, f, v, "OK", 1); - } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java index 0c850e36ea..2fd10f4698 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java @@ -23,13 +23,13 @@ import org.postgresql.util.*; * parameter. For instance, if the IN parameter has SQL type Integer, then * setInt should be used. * - *

If arbitrary parameter type conversions are required, then the setObject + *

If arbitrary parameter type conversions are required, then the setObject * method should be used with a target SQL type. * * @see ResultSet * @see java.sql.PreparedStatement */ -public class PreparedStatement extends Statement implements java.sql.PreparedStatement +public class PreparedStatement extends Statement implements java.sql.PreparedStatement { String sql; String[] templateStrings; @@ -82,7 +82,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * A Prepared SQL query is executed and its ResultSet is returned * * @return a ResultSet that contains the data produced by the - * * query - never null + * * query - never null * @exception SQLException if a database access error occurs */ public java.sql.ResultSet executeQuery() throws SQLException @@ -93,12 +93,12 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta for (i = 0 ; i < inStrings.length ; ++i) { if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param",new Integer(i + 1)); + throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); s.append (templateStrings[i]); s.append (inStrings[i]); } s.append(templateStrings[inStrings.length]); - return super.executeQuery(s.toString()); // in Statement class + return super.executeQuery(s.toString()); // in Statement class } /** @@ -107,7 +107,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * be executed. * * @return either the row count for INSERT, UPDATE or DELETE; or - * * 0 for SQL statements that return nothing. + * * 0 for SQL statements that return nothing. * @exception SQLException if a database access error occurs */ public int executeUpdate() throws SQLException @@ -118,13 +118,13 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta for (i = 0 ; i < inStrings.length ; ++i) { if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param",new Integer(i + 1)); + throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); s.append (templateStrings[i]); s.append (inStrings[i]); } s.append(templateStrings[inStrings.length]); - return super.executeUpdate(s.toString()); // in Statement class - } + return super.executeUpdate(s.toString()); // in Statement class + } /** * Set a parameter to SQL NULL @@ -220,7 +220,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } /** - * Set a parameter to a Java double value. The driver converts this + * Set a parameter to a Java double value. The driver converts this * to a SQL DOUBLE value when it sends it to the database * * @param parameterIndex the first parameter is 1... @@ -247,7 +247,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } /** - * Set a parameter to a Java String value. The driver converts this + * Set a parameter to a Java String value. The driver converts this * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments * size relative to the driver's limits on VARCHARs) when it sends it * to the database. @@ -258,59 +258,66 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setString(int parameterIndex, String x) throws SQLException { - // if the passed string is null, then set this column to null - if(x==null) - setNull(parameterIndex,Types.OTHER); - else { - StringBuffer b = new StringBuffer(); - int i; - - b.append('\''); - for (i = 0 ; i < x.length() ; ++i) - { - char c = x.charAt(i); - if (c == '\\' || c == '\'') - b.append((char)'\\'); - b.append(c); - } - b.append('\''); - set(parameterIndex, b.toString()); - } + // if the passed string is null, then set this column to null + if (x == null) + setNull(parameterIndex, Types.OTHER); + else + { + StringBuffer b = new StringBuffer(); + int i; + + b.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + b.append((char)'\\'); + b.append(c); + } + b.append('\''); + set(parameterIndex, b.toString()); + } } - /** - * Set a parameter to a Java array of bytes. The driver converts this - * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's - * size relative to the driver's limits on VARBINARYs) when it sends - * it to the database. - * - *

Implementation note: - *
With org.postgresql, this creates a large object, and stores the - * objects oid in this column. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBytes(int parameterIndex, byte x[]) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports the bytea datatype for byte arrays - if(null == x){ - setNull(parameterIndex,Types.OTHER); - } else { - setString(parameterIndex, PGbytea.toPGString(x)); - } - } else { - //Version 7.1 and earlier support done as LargeObjects - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - lob.write(x); - lob.close(); - setInt(parameterIndex,oid); - } - } + /** + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + *

Implementation note: + *
With org.postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports the bytea datatype for byte arrays + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + setString(parameterIndex, PGbytea.toPGString(x)); + } + } + else + { + //Version 7.1 and earlier support done as LargeObjects + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex, oid); + } + } /** * Set a parameter to a java.sql.Date value. The driver converts this @@ -322,24 +329,27 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setDate(int parameterIndex, java.sql.Date x) throws SQLException { - if (null == x){ - setNull(parameterIndex,Types.OTHER); - }else{ - SimpleDateFormat df = new SimpleDateFormat("''yyyy-MM-dd''"); - set(parameterIndex, df.format(x)); - } - // The above is how the date should be handled. - // - // However, in JDK's prior to 1.1.6 (confirmed with the - // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems - // to format a date to the previous day. So the fix is to add a day - // before formatting. - // - // PS: 86400000 is one day - // - //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + SimpleDateFormat df = new SimpleDateFormat("''yyyy-MM-dd''"); + set(parameterIndex, df.format(x)); + } + // The above is how the date should be handled. + // + // However, in JDK's prior to 1.1.6 (confirmed with the + // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems + // to format a date to the previous day. So the fix is to add a day + // before formatting. + // + // PS: 86400000 is one day + // + //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); } - + /** * Set a parameter to a java.sql.Time value. The driver converts * this to a SQL TIME value when it sends it to the database. @@ -350,11 +360,14 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setTime(int parameterIndex, Time x) throws SQLException { - if (null == x){ - setNull(parameterIndex,Types.OTHER); - }else{ - set(parameterIndex, "'" + x.toString() + "'"); - } + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + set(parameterIndex, "'" + x.toString() + "'"); + } } /** @@ -366,16 +379,19 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * @exception SQLException if a database access error occurs */ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException - { - if (null == x){ - setNull(parameterIndex,Types.OTHER); - }else{ - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("GMT")); - StringBuffer strBuf = new StringBuffer("'"); - strBuf.append(df.format(x)).append('.').append(x.getNanos()/10000000).append("+00'"); - set(parameterIndex, strBuf.toString()); - } + { + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + StringBuffer strBuf = new StringBuffer("'"); + strBuf.append(df.format(x)).append('.').append(x.getNanos() / 10000000).append("+00'"); + set(parameterIndex, strBuf.toString()); + } } /** @@ -396,29 +412,37 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try { - InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars,0,length); - setString(parameterIndex, new String(l_chars,0,l_charsRead)); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual",l_uee); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - } else { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } } - } /** * When a very large Unicode value is input to a LONGVARCHAR parameter, @@ -437,35 +461,43 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try { - InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars,0,length); - setString(parameterIndex, new String(l_chars,0,l_charsRead)); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual",l_uee); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - } else { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } } - } /** * When a very large binary value is input to a LONGVARBINARY parameter, * it may be more practical to send it via a java.io.InputStream. * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. + * end-of-file. * *

Note: This stream object can either be a standard Java * stream object or your own subclass that implements the standard @@ -477,60 +509,73 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports BinaryStream for for the PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //setBytes() since there is no current way to stream the value to the server - byte[] l_bytes = new byte[length]; - int l_bytesRead; - try { - l_bytesRead = x.read(l_bytes,0,length); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - if (l_bytesRead == length) { - setBytes(parameterIndex, l_bytes); - } else { - //the stream contained less data than they said - byte[] l_bytes2 = new byte[l_bytesRead]; - System.arraycopy(l_bytes,0,l_bytes2,0,l_bytesRead); - setBytes(parameterIndex, l_bytes2); - } - } else { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARBINARY instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c=x.read(); - int p=0; - while(c>-1 && p -1 && p < length) + { + los.write(c); + c = x.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(parameterIndex, oid); + } } /** * In general, parameter values remain in force for repeated used of a * Statement. Setting a parameter value automatically clears its - * previous value. However, in coms cases, it is useful to immediately + * previous value. However, in coms cases, it is useful to immediately * release the resources used by the current parameter values; this * can be done by calling clearParameters * @@ -559,62 +604,66 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * @param x the object containing the input parameter value * @param targetSqlType The SQL type to be send to the database * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC - * * types this is the number of digits after the decimal. For - * * all other types this value will be ignored. + * * types this is the number of digits after the decimal. For + * * all other types this value will be ignored. * @exception SQLException if a database access error occurs */ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { - if (x == null){ - setNull(parameterIndex,Types.OTHER); - return; + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return ; } switch (targetSqlType) { - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - case Types.REAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.DECIMAL: - case Types.NUMERIC: - if (x instanceof Boolean) - set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); - else - set(parameterIndex, x.toString()); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - setString(parameterIndex, x.toString()); - break; - case Types.DATE: - setDate(parameterIndex, (java.sql.Date)x); - break; - case Types.TIME: - setTime(parameterIndex, (Time)x); - break; - case Types.TIMESTAMP: - setTimestamp(parameterIndex, (Timestamp)x); - break; - case Types.BIT: - if (x instanceof Boolean) { - set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); - } else { - throw new PSQLException("postgresql.prep.type"); - } - break; - case Types.BINARY: - case Types.VARBINARY: - setObject(parameterIndex,x); - break; - case Types.OTHER: - setString(parameterIndex, ((PGobject)x).getValue()); - break; - default: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + if (x instanceof Boolean) + set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); + else + set(parameterIndex, x.toString()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + setString(parameterIndex, x.toString()); + break; + case Types.DATE: + setDate(parameterIndex, (java.sql.Date)x); + break; + case Types.TIME: + setTime(parameterIndex, (Time)x); + break; + case Types.TIMESTAMP: + setTimestamp(parameterIndex, (Timestamp)x); + break; + case Types.BIT: + if (x instanceof Boolean) + { + set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); + } + else + { throw new PSQLException("postgresql.prep.type"); + } + break; + case Types.BINARY: + case Types.VARBINARY: + setObject(parameterIndex, x); + break; + case Types.OTHER: + setString(parameterIndex, ((PGobject)x).getValue()); + break; + default: + throw new PSQLException("postgresql.prep.type"); } } @@ -622,18 +671,19 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta { setObject(parameterIndex, x, targetSqlType, 0); } - - /** - * This stores an Object into a parameter. - *

New for 6.4, if the object is not recognised, but it is - * Serializable, then the object is serialised using the - * org.postgresql.util.Serialize class. - */ + + /** + * This stores an Object into a parameter. + *

New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * org.postgresql.util.Serialize class. + */ public void setObject(int parameterIndex, Object x) throws SQLException { - if (x == null){ - setNull(parameterIndex,Types.OTHER); - return; + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return ; } if (x instanceof String) setString(parameterIndex, (String)x); @@ -667,11 +717,11 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta /** * Some prepared statements return multiple results; the execute method - * handles these complex statements as well as the simpler form of + * handles these complex statements as well as the simpler form of * statements handled by executeQuery and executeUpdate * * @return true if the next result is a ResultSet; false if it is an - * * update count or there are no more results + * * update count or there are no more results * @exception SQLException if a database access error occurs */ public boolean execute() throws SQLException @@ -682,19 +732,20 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta for (i = 0 ; i < inStrings.length ; ++i) { if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param",new Integer(i + 1)); + throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); s.append (templateStrings[i]); s.append (inStrings[i]); } s.append(templateStrings[inStrings.length]); - return super.execute(s.toString()); // in Statement class + return super.execute(s.toString()); // in Statement class } /** * Returns the SQL statement with the current template values * substituted. */ - public String toString() { + public String toString() + { StringBuffer s = new StringBuffer(); int i; @@ -709,14 +760,14 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta s.append(templateStrings[inStrings.length]); return s.toString(); } - + // ************************************************************** - // END OF PUBLIC INTERFACE + // END OF PUBLIC INTERFACE // ************************************************************** - + /** * There are a lot of setXXX classes which all basically do - * the same thing. We need a method which actually does the + * the same thing. We need a method which actually does the * set for us. * * @param paramIndex the index into the inString diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java index fe01d15e29..bbde9d5bf9 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java @@ -21,13 +21,13 @@ import org.postgresql.core.Encoding; * Statement. The table rows are retrieved in sequence. Within a row its * column values can be accessed in any order. * - *

A ResultSet maintains a cursor pointing to its current row of data. + *

A ResultSet maintains a cursor pointing to its current row of data. * Initially the cursor is positioned before the first row. The 'next' * method moves the cursor to the next row. * *

The getXXX methods retrieve column values for the current row. You can * retrieve values either using the index number of the column, or by using - * the name of the column. In general using the column index will be more + * the name of the column. In general using the column index will be more * efficient. Columns are numbered from 1. * *

For maximum portability, ResultSet columns within each row should be read @@ -40,15 +40,15 @@ import org.postgresql.core.Encoding; * *

Column names used as input to getXXX methods are case insenstive. When * performing a getXXX using a column name, if several columns have the same - * name, then the value of the first matching column will be returned. The + * name, then the value of the first matching column will be returned. The * column name option is designed to be used when column names are used in the * SQL Query. For columns that are NOT explicitly named in the query, it is - * best to use column numbers. If column names were used there is no way for + * best to use column numbers. If column names were used there is no way for * the programmer to guarentee that they actually refer to the intended * columns. * - *

A ResultSet is automatically closed by the Statement that generated it - * when that Statement is closed, re-executed, or is used to retrieve the + *

A ResultSet is automatically closed by the Statement that generated it + * when that Statement is closed, re-executed, or is used to retrieve the * next result from a sequence of multiple results. * *

The number, types and properties of a ResultSet's columns are provided by @@ -57,859 +57,932 @@ import org.postgresql.core.Encoding; * @see ResultSetMetaData * @see java.sql.ResultSet */ -public class ResultSet extends org.postgresql.ResultSet implements java.sql.ResultSet +public class ResultSet extends org.postgresql.ResultSet implements java.sql.ResultSet { - /** - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) - { - super(conn,fields,tuples,status,updateCount,insertOID,binaryCursor); - } - - /** - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) - { - super(conn,fields,tuples,status,updateCount,0,false); - } - - /** - * A ResultSet is initially positioned before its first row, - * the first call to next makes the first row the current row; - * the second call makes the second row the current row, etc. - * - *

If an input stream from the previous row is open, it is - * implicitly closed. The ResultSet's warning chain is cleared - * when a new row is read - * - * @return true if the new current is valid; false if there are no - * more rows - * @exception SQLException if a database access error occurs - */ - public boolean next() throws SQLException - { - if (++current_row >= rows.size()) - return false; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - /** - * In some cases, it is desirable to immediately release a ResultSet - * database and JDBC resources instead of waiting for this to happen - * when it is automatically closed. The close method provides this - * immediate release. - * - *

Note: A ResultSet is automatically closed by the Statement - * the Statement that generated it when that Statement is closed, - * re-executed, or is used to retrieve the next result from a sequence - * of multiple results. A ResultSet is also automatically closed - * when it is garbage collected. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - //release resources held (memory for tuples) - rows.setSize(0); - } - - /** - * A column may have the value of SQL NULL; wasNull() reports whether - * the last column read had this special value. Note that you must - * first call getXXX on a column to try to read its value and then - * call wasNull() to find if the value was SQL NULL - * - * @return true if the last column read was SQL NULL - * @exception SQLException if a database access error occurred - */ - public boolean wasNull() throws SQLException - { - return wasNullFlag; - } - - /** - * Get the value of a column in the current row as a Java String - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, null for SQL NULL - * @exception SQLException if a database access error occurs - */ - public String getString(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - - wasNullFlag = (this_row[columnIndex - 1] == null); - if(wasNullFlag) - return null; - - Encoding encoding = connection.getEncoding(); - return encoding.decode(this_row[columnIndex - 1]); - } - - /** - * Get the value of a column in the current row as a Java boolean - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, false for SQL NULL - * @exception SQLException if a database access error occurs - */ - public boolean getBoolean(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - int c = s.charAt(0); - return ((c == 't') || (c == 'T') || (c == '1')); - } - return false; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public byte getByte(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Byte.parseByte(s); - } catch (NumberFormatException e) { - throw new PSQLException("postgresql.res.badbyte",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java short. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public short getShort(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Short.parseShort(s); - } catch (NumberFormatException e) { - throw new PSQLException("postgresql.res.badshort",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java int. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public int getInt(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badint",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java long. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public long getLong(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Long.parseLong(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badlong",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java float. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public float getFloat(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Float.valueOf(s).floatValue(); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badfloat",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java double. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public double getDouble(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Double.valueOf(s).doubleValue(); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.baddouble",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a - * java.math.BigDecimal object - * - * @param columnIndex the first column is 1, the second is 2... - * @param scale the number of digits to the right of the decimal - * @return the column value; if the value is SQL NULL, null - * @exception SQLException if a database access error occurs - */ - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException - { - String s = getFixedString(columnIndex); - BigDecimal val; - - if (s != null) - { - try - { - val = new BigDecimal(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badbigdec",s); - } - try - { - return val.setScale(scale); - } catch (ArithmeticException e) { - throw new PSQLException ("postgresql.res.badbigdec",s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte array. - * - *

In normal use, the bytes represent the raw values returned by the - * backend. However, if the column is an OID, then it is assumed to - * refer to a Large Object, and that object is returned as a byte array. - * - *

Be warned If the large object is huge, then you may run out - * of memory. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the result - * is null - * @exception SQLException if a database access error occurs - */ - public byte[] getBytes(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - - //If the data is already binary then just return it - if (binaryCursor) return this_row[columnIndex - 1]; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports the bytea datatype for byte arrays - return PGbytea.toBytes(getString(columnIndex)); - } else { - //Version 7.1 and earlier supports LargeObjects for byte arrays - wasNullFlag = (this_row[columnIndex - 1] == null); - // Handle OID's as BLOBS - if(!wasNullFlag) { - if( fields[columnIndex - 1].getOID() == 26) { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - byte buf[] = lob.read(lob.size()); - lob.close(); - return buf; - } - } - } - return null; - } - - /** - * Get the value of a column in the current row as a java.sql.Date - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public java.sql.Date getDate(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - if(s==null) - return null; - return java.sql.Date.valueOf(s); - } - - /** - * Get the value of a column in the current row as a java.sql.Time - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Time getTime(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - if (s.length() != 5 && s.length() != 8) - throw new NumberFormatException("Wrong Length!"); - int hr = Integer.parseInt(s.substring(0,2)); - int min = Integer.parseInt(s.substring(3,5)); - int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); - return new Time(hr, min, sec); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badtime",s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a - * java.sql.Timestamp object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Timestamp getTimestamp(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - if(s==null) - return null; - - boolean subsecond; - //if string contains a '.' we have fractional seconds - if (s.indexOf('.') == -1) { - subsecond = false; - } else { - subsecond = true; - } - - //here we are modifying the string from ISO format to a format java can understand - //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format - //and java expects three digits if fractional seconds are present instead of two for postgres - //so this code strips off timezone info and adds on the GMT+/-... - //as well as adds a third digit for partial seconds if necessary - StringBuffer strBuf = new StringBuffer(s); - - //we are looking to see if the backend has appended on a timezone. - //currently postgresql will return +/-HH:MM or +/-HH for timezone offset - //(i.e. -06, or +06:30, note the expectation of the leading zero for the - //hours, and the use of the : for delimiter between hours and minutes) - //if the backend ISO format changes in the future this code will - //need to be changed as well - char sub = strBuf.charAt(strBuf.length()-3); - if (sub == '+' || sub == '-') { - strBuf.setLength(strBuf.length()-3); - if (subsecond) { - strBuf.append('0').append("GMT").append(s.substring(s.length()-3, s.length())).append(":00"); - } else { - strBuf.append("GMT").append(s.substring(s.length()-3, s.length())).append(":00"); - } - } else if (sub == ':') { - //we may have found timezone info of format +/-HH:MM, or there is no - //timezone info at all and this is the : preceding the seconds - char sub2 = strBuf.charAt(strBuf.length()-5); - if (sub2 == '+' || sub2 == '-') { - //we have found timezone info of format +/-HH:MM - strBuf.setLength(strBuf.length()-5); - if (subsecond) { - strBuf.append('0').append("GMT").append(s.substring(s.length()-5)); - } else { - strBuf.append("GMT").append(s.substring(s.length()-5)); - } - } else if (subsecond) { - strBuf.append('0'); - } - } else if (subsecond) { - strBuf = strBuf.append('0'); - } - - s = strBuf.toString(); - - SimpleDateFormat df = null; - - if (s.length()>23 && subsecond) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz"); - } else if (s.length()>23 && !subsecond) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); - } else if (s.length()>10 && subsecond) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - } else if (s.length()>10 && !subsecond) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - } else { - df = new SimpleDateFormat("yyyy-MM-dd"); - } - - try { - return new Timestamp(df.parse(s).getTime()); - } catch(ParseException e) { - throw new PSQLException("postgresql.res.badtimestamp",new Integer(e.getErrorOffset()),s); - } - } - - /** - * A column value can be retrieved as a stream of ASCII characters - * and then read in chunks from the stream. This method is - * particular suitable for retrieving large LONGVARCHAR values. - * The JDBC driver will do any necessary conversion from the - * database format into ASCII. - * - *

Note: All the data in the returned stream must be read - * prior to getting the value of any other column. The next call - * to a get method implicitly closes the stream. Also, a stream - * may return 0 for available() whether there is data available - * or not. - * - *

We implement an ASCII stream as a Binary stream - we should really - * do the data conversion, but I cannot be bothered to implement this - * right now. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return a Java InputStream that delivers the database column - * value as a stream of one byte ASCII characters. If the - * value is SQL NULL then the result is null - * @exception SQLException if a database access error occurs - * @see getBinaryStream - */ - public InputStream getAsciiStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try { - return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual", l_uee); - } - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - /** - * A column value can also be retrieved as a stream of Unicode - * characters. We implement this as a binary stream. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getBinaryStream - */ - public InputStream getUnicodeStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try { - return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual", l_uee); - } - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - /** - * A column value can also be retrieved as a binary strea. This - * method is suitable for retrieving LONGVARBINARY values. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of bytes. If the value is SQL NULL, then the result - * is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getUnicodeStream - */ - public InputStream getBinaryStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports BinaryStream for all PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getBytes() since there is no current way to stream the value from the server - byte b[] = getBytes(columnIndex); - if (b != null) - return new ByteArrayInputStream(b); - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - if( fields[columnIndex - 1].getOID() == 26) { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - return lob.getInputStream(); - } - } - return null; - } - - /** - * The following routines simply convert the columnName into - * a columnIndex and then call the appropriate routine above. - * - * @param columnName is the SQL name of the column - * @return the column value - * @exception SQLException if a database access error occurs - */ - public String getString(String columnName) throws SQLException - { - return getString(findColumn(columnName)); - } - - public boolean getBoolean(String columnName) throws SQLException - { - return getBoolean(findColumn(columnName)); - } - - public byte getByte(String columnName) throws SQLException - { - - return getByte(findColumn(columnName)); - } - - public short getShort(String columnName) throws SQLException - { - return getShort(findColumn(columnName)); - } - - public int getInt(String columnName) throws SQLException - { - return getInt(findColumn(columnName)); - } - - public long getLong(String columnName) throws SQLException - { - return getLong(findColumn(columnName)); - } - - public float getFloat(String columnName) throws SQLException - { - return getFloat(findColumn(columnName)); - } - - public double getDouble(String columnName) throws SQLException - { - return getDouble(findColumn(columnName)); - } - - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException - { - return getBigDecimal(findColumn(columnName), scale); - } - - public byte[] getBytes(String columnName) throws SQLException - { - return getBytes(findColumn(columnName)); - } - - public java.sql.Date getDate(String columnName) throws SQLException - { - return getDate(findColumn(columnName)); - } - - public Time getTime(String columnName) throws SQLException - { - return getTime(findColumn(columnName)); - } - - public Timestamp getTimestamp(String columnName) throws SQLException - { - return getTimestamp(findColumn(columnName)); - } - - public InputStream getAsciiStream(String columnName) throws SQLException - { - return getAsciiStream(findColumn(columnName)); - } - - public InputStream getUnicodeStream(String columnName) throws SQLException - { - return getUnicodeStream(findColumn(columnName)); - } - - public InputStream getBinaryStream(String columnName) throws SQLException - { - return getBinaryStream(findColumn(columnName)); - } - - /** - * The first warning reported by calls on this ResultSet is - * returned. Subsequent ResultSet warnings will be chained - * to this SQLWarning. - * - *

The warning chain is automatically cleared each time a new - * row is read. - * - *

Note: This warning chain only covers warnings caused by - * ResultSet methods. Any warnings caused by statement methods - * (such as reading OUT parameters) will be chained on the - * Statement object. - * - * @return the first SQLWarning or null; - * @exception SQLException if a database access error occurs. - */ - public SQLWarning getWarnings() throws SQLException - { - return warnings; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this ResultSet - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - warnings = null; - } - - /** - * Get the name of the SQL cursor used by this ResultSet - * - *

In SQL, a result table is retrieved though a cursor that is - * named. The current row of a result can be updated or deleted - * using a positioned update/delete statement that references - * the cursor name. - * - *

JDBC supports this SQL feature by providing the name of the - * SQL cursor used by a ResultSet. The current row of a ResulSet - * is also the current row of this SQL cursor. - * - *

Note: If positioned update is not supported, a SQLException - * is thrown. - * - * @return the ResultSet's SQL cursor name. - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return connection.getCursorName(); - } - - /** - * The numbers, types and properties of a ResultSet's columns are - * provided by the getMetaData method - * - * @return a description of the ResultSet's columns - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - return new ResultSetMetaData(rows, fields); - } - - /** - * Get the value of a column in the current row as a Java object - * - *

This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - *

This method may also be used to read database specific abstract - * data types. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(int columnIndex) throws SQLException - { - Field field; - - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - field = fields[columnIndex - 1]; - - // some fields can be null, mainly from those returned by MetaData methods - if(field==null) { - wasNullFlag=true; - return null; - } - - switch (field.getSQLType()) - { - case Types.BIT: - return new Boolean(getBoolean(columnIndex)); - case Types.SMALLINT: - return new Integer(getInt(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal(columnIndex, ((field.getMod()-4) & 0xffff)); - case Types.REAL: - return new Float(getFloat(columnIndex)); - case Types.DOUBLE: - return new Double(getDouble(columnIndex)); - case Types.CHAR: - case Types.VARCHAR: - return getString(columnIndex); - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - case Types.BINARY: - case Types.VARBINARY: - return getBytes(columnIndex); - default: - String type = field.getPGType(); - // if the backend doesn't know the type then coerce to String - if (type.equals("unknown")){ - return getString(columnIndex); - }else{ - return connection.getObject(field.getPGType(), getString(columnIndex)); - } - } - } - - /** - * Get the value of a column in the current row as a Java object - * - *

This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - *

This method may also be used to read database specific abstract - * data types. - * - * @param columnName is the SQL name of the column - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(String columnName) throws SQLException - { - return getObject(findColumn(columnName)); - } - - /** - * Map a ResultSet column name to a ResultSet column index - * - * @param columnName the name of the column - * @return the column index - * @exception SQLException if a database access error occurs - */ - public int findColumn(String columnName) throws SQLException - { - int i; - - for (i = 0 ; i < fields.length; ++i) - if (fields[i].getName().equalsIgnoreCase(columnName)) - return (i+1); - throw new PSQLException ("postgresql.res.colname",columnName); - } + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) + { + super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); + } + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + super(conn, fields, tuples, status, updateCount, 0, false); + } + + /** + * A ResultSet is initially positioned before its first row, + * the first call to next makes the first row the current row; + * the second call makes the second row the current row, etc. + * + *

If an input stream from the previous row is open, it is + * implicitly closed. The ResultSet's warning chain is cleared + * when a new row is read + * + * @return true if the new current is valid; false if there are no + * more rows + * @exception SQLException if a database access error occurs + */ + public boolean next() throws SQLException + { + if (++current_row >= rows.size()) + return false; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + /** + * In some cases, it is desirable to immediately release a ResultSet + * database and JDBC resources instead of waiting for this to happen + * when it is automatically closed. The close method provides this + * immediate release. + * + *

Note: A ResultSet is automatically closed by the Statement + * the Statement that generated it when that Statement is closed, + * re-executed, or is used to retrieve the next result from a sequence + * of multiple results. A ResultSet is also automatically closed + * when it is garbage collected. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + //release resources held (memory for tuples) + rows.setSize(0); + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether + * the last column read had this special value. Note that you must + * first call getXXX on a column to try to read its value and then + * call wasNull() to find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * @exception SQLException if a database access error occurred + */ + public boolean wasNull() throws SQLException + { + return wasNullFlag; + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, null for SQL NULL + * @exception SQLException if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + Encoding encoding = connection.getEncoding(); + return encoding.decode(this_row[columnIndex - 1]); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, false for SQL NULL + * @exception SQLException if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + int c = s.charAt(0); + return ((c == 't') || (c == 'T') || (c == '1')); + } + return false; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Byte.parseByte(s); + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.res.badbyte", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Short.parseShort(s); + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.res.badshort", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Integer.parseInt(s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badint", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Long.parseLong(s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badlong", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Float.valueOf(s).floatValue(); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badfloat", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Double.valueOf(s).doubleValue(); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.baddouble", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.math.BigDecimal object + * + * @param columnIndex the first column is 1, the second is 2... + * @param scale the number of digits to the right of the decimal + * @return the column value; if the value is SQL NULL, null + * @exception SQLException if a database access error occurs + */ + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException + { + String s = getFixedString(columnIndex); + BigDecimal val; + + if (s != null) + { + try + { + val = new BigDecimal(s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badbigdec", s); + } + try + { + return val.setScale(scale); + } + catch (ArithmeticException e) + { + throw new PSQLException ("postgresql.res.badbigdec", s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

In normal use, the bytes represent the raw values returned by the + * backend. However, if the column is an OID, then it is assumed to + * refer to a Large Object, and that object is returned as a byte array. + * + *

Be warned If the large object is huge, then you may run out + * of memory. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the result + * is null + * @exception SQLException if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + + //If the data is already binary then just return it + if (binaryCursor) + return this_row[columnIndex - 1]; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports the bytea datatype for byte arrays + return PGbytea.toBytes(getString(columnIndex)); + } + else + { + //Version 7.1 and earlier supports LargeObjects for byte arrays + wasNullFlag = (this_row[columnIndex - 1] == null); + // Handle OID's as BLOBS + if (!wasNullFlag) + { + if ( fields[columnIndex - 1].getOID() == 26) + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + byte buf[] = lob.read(lob.size()); + lob.close(); + return buf; + } + } + } + return null; + } + + /** + * Get the value of a column in the current row as a java.sql.Date + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + if (s == null) + return null; + return java.sql.Date.valueOf(s); + } + + /** + * Get the value of a column in the current row as a java.sql.Time + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Time getTime(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + if (s.length() != 5 && s.length() != 8) + throw new NumberFormatException("Wrong Length!"); + int hr = Integer.parseInt(s.substring(0, 2)); + int min = Integer.parseInt(s.substring(3, 5)); + int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); + return new Time(hr, min, sec); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badtime", s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.sql.Timestamp object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + if (s == null) + return null; + + boolean subsecond; + //if string contains a '.' we have fractional seconds + if (s.indexOf('.') == -1) + { + subsecond = false; + } + else + { + subsecond = true; + } + + //here we are modifying the string from ISO format to a format java can understand + //java expects timezone info as 'GMT-08:00' instead of '-08' in postgres ISO format + //and java expects three digits if fractional seconds are present instead of two for postgres + //so this code strips off timezone info and adds on the GMT+/-... + //as well as adds a third digit for partial seconds if necessary + StringBuffer strBuf = new StringBuffer(s); + + //we are looking to see if the backend has appended on a timezone. + //currently postgresql will return +/-HH:MM or +/-HH for timezone offset + //(i.e. -06, or +06:30, note the expectation of the leading zero for the + //hours, and the use of the : for delimiter between hours and minutes) + //if the backend ISO format changes in the future this code will + //need to be changed as well + char sub = strBuf.charAt(strBuf.length() - 3); + if (sub == '+' || sub == '-') + { + strBuf.setLength(strBuf.length() - 3); + if (subsecond) + { + strBuf.append('0').append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00"); + } + else + { + strBuf.append("GMT").append(s.substring(s.length() - 3, s.length())).append(":00"); + } + } + else if (sub == ':') + { + //we may have found timezone info of format +/-HH:MM, or there is no + //timezone info at all and this is the : preceding the seconds + char sub2 = strBuf.charAt(strBuf.length() - 5); + if (sub2 == '+' || sub2 == '-') + { + //we have found timezone info of format +/-HH:MM + strBuf.setLength(strBuf.length() - 5); + if (subsecond) + { + strBuf.append('0').append("GMT").append(s.substring(s.length() - 5)); + } + else + { + strBuf.append("GMT").append(s.substring(s.length() - 5)); + } + } + else if (subsecond) + { + strBuf.append('0'); + } + } + else if (subsecond) + { + strBuf = strBuf.append('0'); + } + + s = strBuf.toString(); + + SimpleDateFormat df = null; + + if (s.length() > 23 && subsecond) + { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz"); + } + else if (s.length() > 23 && !subsecond) + { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); + } + else if (s.length() > 10 && subsecond) + { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + } + else if (s.length() > 10 && !subsecond) + { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + else + { + df = new SimpleDateFormat("yyyy-MM-dd"); + } + + try + { + return new Timestamp(df.parse(s).getTime()); + } + catch (ParseException e) + { + throw new PSQLException("postgresql.res.badtimestamp", new Integer(e.getErrorOffset()), s); + } + } + + /** + * A column value can be retrieved as a stream of ASCII characters + * and then read in chunks from the stream. This method is + * particular suitable for retrieving large LONGVARCHAR values. + * The JDBC driver will do any necessary conversion from the + * database format into ASCII. + * + *

Note: All the data in the returned stream must be read + * prior to getting the value of any other column. The next call + * to a get method implicitly closes the stream. Also, a stream + * may return 0 for available() whether there is data available + * or not. + * + *

We implement an ASCII stream as a Binary stream - we should really + * do the data conversion, but I cannot be bothered to implement this + * right now. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return a Java InputStream that delivers the database column + * value as a stream of one byte ASCII characters. If the + * value is SQL NULL then the result is null + * @exception SQLException if a database access error occurs + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try + { + return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + return getBinaryStream(columnIndex); + } + } + + /** + * A column value can also be retrieved as a stream of Unicode + * characters. We implement this as a binary stream. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getBinaryStream + */ + public InputStream getUnicodeStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try + { + return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + return getBinaryStream(columnIndex); + } + } + + /** + * A column value can also be retrieved as a binary strea. This + * method is suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of bytes. If the value is SQL NULL, then the result + * is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getUnicodeStream + */ + public InputStream getBinaryStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports BinaryStream for all PG bytea type + //As the spec/javadoc for this method indicate this is to be used for + //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate + //long binary datatype, but with toast the bytea datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getBytes() since there is no current way to stream the value from the server + byte b[] = getBytes(columnIndex); + if (b != null) + return new ByteArrayInputStream(b); + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + if ( fields[columnIndex - 1].getOID() == 26) + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + return lob.getInputStream(); + } + } + return null; + } + + /** + * The following routines simply convert the columnName into + * a columnIndex and then call the appropriate routine above. + * + * @param columnName is the SQL name of the column + * @return the column value + * @exception SQLException if a database access error occurs + */ + public String getString(String columnName) throws SQLException + { + return getString(findColumn(columnName)); + } + + public boolean getBoolean(String columnName) throws SQLException + { + return getBoolean(findColumn(columnName)); + } + + public byte getByte(String columnName) throws SQLException + { + + return getByte(findColumn(columnName)); + } + + public short getShort(String columnName) throws SQLException + { + return getShort(findColumn(columnName)); + } + + public int getInt(String columnName) throws SQLException + { + return getInt(findColumn(columnName)); + } + + public long getLong(String columnName) throws SQLException + { + return getLong(findColumn(columnName)); + } + + public float getFloat(String columnName) throws SQLException + { + return getFloat(findColumn(columnName)); + } + + public double getDouble(String columnName) throws SQLException + { + return getDouble(findColumn(columnName)); + } + + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException + { + return getBigDecimal(findColumn(columnName), scale); + } + + public byte[] getBytes(String columnName) throws SQLException + { + return getBytes(findColumn(columnName)); + } + + public java.sql.Date getDate(String columnName) throws SQLException + { + return getDate(findColumn(columnName)); + } + + public Time getTime(String columnName) throws SQLException + { + return getTime(findColumn(columnName)); + } + + public Timestamp getTimestamp(String columnName) throws SQLException + { + return getTimestamp(findColumn(columnName)); + } + + public InputStream getAsciiStream(String columnName) throws SQLException + { + return getAsciiStream(findColumn(columnName)); + } + + public InputStream getUnicodeStream(String columnName) throws SQLException + { + return getUnicodeStream(findColumn(columnName)); + } + + public InputStream getBinaryStream(String columnName) throws SQLException + { + return getBinaryStream(findColumn(columnName)); + } + + /** + * The first warning reported by calls on this ResultSet is + * returned. Subsequent ResultSet warnings will be chained + * to this SQLWarning. + * + *

The warning chain is automatically cleared each time a new + * row is read. + * + *

Note: This warning chain only covers warnings caused by + * ResultSet methods. Any warnings caused by statement methods + * (such as reading OUT parameters) will be chained on the + * Statement object. + * + * @return the first SQLWarning or null; + * @exception SQLException if a database access error occurs. + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this ResultSet + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + *

In SQL, a result table is retrieved though a cursor that is + * named. The current row of a result can be updated or deleted + * using a positioned update/delete statement that references + * the cursor name. + * + *

JDBC supports this SQL feature by providing the name of the + * SQL cursor used by a ResultSet. The current row of a ResulSet + * is also the current row of this SQL cursor. + * + *

Note: If positioned update is not supported, a SQLException + * is thrown. + * + * @return the ResultSet's SQL cursor name. + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return connection.getCursorName(); + } + + /** + * The numbers, types and properties of a ResultSet's columns are + * provided by the getMetaData method + * + * @return a description of the ResultSet's columns + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + return new ResultSetMetaData(rows, fields); + } + + /** + * Get the value of a column in the current row as a Java object + * + *

This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + *

This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException + { + Field field; + + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + field = fields[columnIndex - 1]; + + // some fields can be null, mainly from those returned by MetaData methods + if (field == null) + { + wasNullFlag = true; + return null; + } + + switch (field.getSQLType()) + { + case Types.BIT: + return new Boolean(getBoolean(columnIndex)); + case Types.SMALLINT: + return new Integer(getInt(columnIndex)); + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + case Types.BIGINT: + return new Long(getLong(columnIndex)); + case Types.NUMERIC: + return getBigDecimal(columnIndex, ((field.getMod() - 4) & 0xffff)); + case Types.REAL: + return new Float(getFloat(columnIndex)); + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + case Types.BINARY: + case Types.VARBINARY: + return getBytes(columnIndex); + default: + String type = field.getPGType(); + // if the backend doesn't know the type then coerce to String + if (type.equals("unknown")) + { + return getString(columnIndex); + } + else + { + return connection.getObject(field.getPGType(), getString(columnIndex)); + } + } + } + + /** + * Get the value of a column in the current row as a Java object + * + *

This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + *

This method may also be used to read database specific abstract + * data types. + * + * @param columnName is the SQL name of the column + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException + { + return getObject(findColumn(columnName)); + } + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName the name of the column + * @return the column index + * @exception SQLException if a database access error occurs + */ + public int findColumn(String columnName) throws SQLException + { + int i; + + for (i = 0 ; i < fields.length; ++i) + if (fields[i].getName().equalsIgnoreCase(columnName)) + return (i + 1); + throw new PSQLException ("postgresql.res.colname", columnName); + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSetMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSetMetaData.java index 91317bae4e..60efbf0f2f 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSetMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/ResultSetMetaData.java @@ -22,442 +22,455 @@ import java.sql.Types; * * @see java.sql.ResultSetMetaData */ -public class ResultSetMetaData implements java.sql.ResultSetMetaData +public class ResultSetMetaData implements java.sql.ResultSetMetaData { - Vector rows; - Field[] fields; - - /** - * Initialise for a result with a tuple set and - * a field descriptor set - * - * @param rows the Vector of rows returned by the ResultSet - * @param fields the array of field descriptors - */ - public ResultSetMetaData(Vector rows, Field[] fields) - { - this.rows = rows; - this.fields = fields; - } - - /** - * Whats the number of columns in the ResultSet? - * - * @return the number - * @exception SQLException if a database access error occurs - */ - public int getColumnCount() throws SQLException - { - return fields.length; - } - - /** - * Is the column automatically numbered (and thus read-only) - * I believe that PostgreSQL does not support this feature. - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isAutoIncrement(int column) throws SQLException - { - return false; - } - - /** - * Does a column's case matter? ASSUMPTION: Any field that is - * not obviously case insensitive is assumed to be case sensitive - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCaseSensitive(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; - default: - return true; - } - } - - /** - * Can the column be used in a WHERE clause? Basically for - * this, I split the functions into two types: recognised - * types (which are always useable), and OTHER types (which - * may or may not be useable). The OTHER types, for now, I - * will assume they are useable. We should really query the - * catalog to see if they are useable. - * - * @param column the first column is 1, the second is 2... - * @return true if they can be used in a WHERE clause - * @exception SQLException if a database access error occurs - */ - public boolean isSearchable(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - // This switch is pointless, I know - but it is a set-up - // for further expansion. - switch (sql_type) - { - case Types.OTHER: - return true; - default: - return true; - } - } - - /** - * Is the column a cash value? 6.1 introduced the cash/money - * type, which haven't been incorporated as of 970414, so I - * just check the type name for both 'cash' and 'money' - * - * @param column the first column is 1, the second is 2... - * @return true if its a cash column - * @exception SQLException if a database access error occurs - */ - public boolean isCurrency(int column) throws SQLException - { - String type_name = getField(column).getPGType(); - - return type_name.equals("cash") || type_name.equals("money"); - } - - /** - * Indicates the nullability of values in the designated column. - * - * @param column the first column is 1, the second is 2... - * @return one of the columnNullable values - * @exception SQLException if a database access error occurs - */ - public int isNullable(int column) throws SQLException - { - /* - * TODO This needs a real implementation, taking into account columns - * defined with NOT NULL or PRIMARY KEY, CHECK constraints, views, - * functions etc. + Vector rows; + Field[] fields; + + /** + * Initialise for a result with a tuple set and + * a field descriptor set + * + * @param rows the Vector of rows returned by the ResultSet + * @param fields the array of field descriptors + */ + public ResultSetMetaData(Vector rows, Field[] fields) + { + this.rows = rows; + this.fields = fields; + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * @exception SQLException if a database access error occurs + */ + public int getColumnCount() throws SQLException + { + return fields.length; + } + + /** + * Is the column automatically numbered (and thus read-only) + * I believe that PostgreSQL does not support this feature. + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException + { + return false; + } + + /** + * Does a column's case matter? ASSUMPTION: Any field that is + * not obviously case insensitive is assumed to be case sensitive + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + default: + return true; + } + } + + /** + * Can the column be used in a WHERE clause? Basically for + * this, I split the functions into two types: recognised + * types (which are always useable), and OTHER types (which + * may or may not be useable). The OTHER types, for now, I + * will assume they are useable. We should really query the + * catalog to see if they are useable. + * + * @param column the first column is 1, the second is 2... + * @return true if they can be used in a WHERE clause + * @exception SQLException if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + // This switch is pointless, I know - but it is a set-up + // for further expansion. + switch (sql_type) + { + case Types.OTHER: + return true; + default: + return true; + } + } + + /** + * Is the column a cash value? 6.1 introduced the cash/money + * type, which haven't been incorporated as of 970414, so I + * just check the type name for both 'cash' and 'money' + * + * @param column the first column is 1, the second is 2... + * @return true if its a cash column + * @exception SQLException if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException + { + String type_name = getField(column).getPGType(); + + return type_name.equals("cash") || type_name.equals("money"); + } + + /** + * Indicates the nullability of values in the designated column. + * + * @param column the first column is 1, the second is 2... + * @return one of the columnNullable values + * @exception SQLException if a database access error occurs + */ + public int isNullable(int column) throws SQLException + { + /* + * TODO This needs a real implementation, taking into account columns + * defined with NOT NULL or PRIMARY KEY, CHECK constraints, views, + * functions etc. + */ + return columnNullableUnknown; + } + + /** + * Is the column a signed number? In PostgreSQL, all numbers + * are signed, so this is trivial. However, strings are not + * signed (duh!) + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return true; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; // I don't know about these? + default: + return false; + } + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column the first column is 1, the second is 2, etc. + * @return the maximum width + * @exception SQLException if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException + { + Field f = getField(column); + String type_name = f.getPGType(); + int sql_type = f.getSQLType(); + int typmod = f.getMod(); + + // I looked at other JDBC implementations and couldn't find a consistent + // interpretation of the "display size" for numeric values, so this is our's + // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de + + // fixed length data types + if (type_name.equals( "int2" )) + return 6; // -32768 to +32768 (5 digits and a sign) + if (type_name.equals( "int4" ) + || type_name.equals( "oid" )) + return 11; // -2147483648 to +2147483647 + if (type_name.equals( "int8" )) + return 20; // -9223372036854775808 to +9223372036854775807 + if (type_name.equals( "money" )) + return 12; // MONEY = DECIMAL(9,2) + if (type_name.equals( "float4" )) + return 11; // i checked it out ans wasn't able to produce more than 11 digits + if (type_name.equals( "float8" )) + return 20; // dito, 20 + if (type_name.equals( "char" )) + return 1; + if (type_name.equals( "bool" )) + return 1; + if (type_name.equals( "date" )) + return 14; // "01/01/4713 BC" - "31/12/32767 AD" + if (type_name.equals( "time" )) + return 8; // 00:00:00-23:59:59 + if (type_name.equals( "timestamp" )) + return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 + + // variable length fields + typmod -= 4; + if (type_name.equals( "bpchar" ) + || type_name.equals( "varchar" )) + return typmod; // VARHDRSZ=sizeof(int32)=4 + if (type_name.equals( "numeric" )) + return ( (typmod >> 16) & 0xffff ) + + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) + + // if we don't know better + return f.getLength(); + } + + /** + * What is the suggested column title for use in printouts and + * displays? We suggest the ColumnName! + * + * @param column the first column is 1, the second is 2, etc. + * @return the column label + * @exception SQLException if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException + { + return getColumnName(column); + } + + /** + * What's a column's name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the column name + * @exception SQLException if a database access error occurs + */ + public String getColumnName(int column) throws SQLException + { + Field f = getField(column); + if (f != null) + return f.getName(); + return "field" + column; + } + + /** + * What is a column's table's schema? This relies on us knowing + * the table name....which I don't know how to do as yet. The + * JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column the first column is 1, the second is 2... + * @return the Schema + * @exception SQLException if a database access error occurs + */ + public String getSchemaName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's number of decimal digits. + * + * @param column the first column is 1, the second is 2... + * @return the precision + * @exception SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 5; + case Types.INTEGER: + return 10; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + case Types.NUMERIC: + Field f = getField(column); + if (f != null) + return ((0xFFFF0000)&f.getMod()) >> 16; + else + return 0; + default: + return 0; + } + } + + /** + * What is a column's number of digits to the right of the + * decimal point? + * + * @param column the first column is 1, the second is 2... + * @return the scale + * @exception SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 0; + case Types.INTEGER: + return 0; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + case Types.NUMERIC: + Field f = getField(column); + if (f != null) + return (((0x0000FFFF)&f.getMod()) - 4); + else + return 0; + default: + return 0; + } + } + + /** + * Whats a column's table's name? How do I find this out? Both + * getSchemaName() and getCatalogName() rely on knowing the table + * Name, so we need this before we can work on them. + * + * @param column the first column is 1, the second is 2... + * @return column name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getTableName(int column) throws SQLException + { + return ""; + } + + /** + * What's a column's table's catalog name? As with getSchemaName(), + * we can say that if getTableName() returns n/a, then we can too - + * otherwise, we need to work on it. + * + * @param column the first column is 1, the second is 2... + * @return catalog name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getCatalogName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column the first column is 1, the second is 2, etc. + * @return the java.sql.Type value + * @exception SQLException if a database access error occurs + * @see org.postgresql.Field#getSQLType + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException + { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the type name + * @exception SQLException if a database access error occurs + */ + public String getColumnTypeName(int column) throws SQLException + { + return getField(column).getPGType(); + } + + /** + * Is the column definitely not writable? In reality, we would + * have to check the GRANT/REVOKE stuff for this to be effective, + * and I haven't really looked into that yet, so this will get + * re-visited. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException + { + return false; + } + + /** + * Is it possible for a write on the column to succeed? Again, we + * would in reality have to check the GRANT/REVOKE stuff, which + * I haven't worked with as yet. However, if it isn't ReadOnly, then + * it is obviously writable. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException + { + return !isReadOnly(column); + } + + /** + * Will a write on this column definately succeed? Hmmm...this + * is a bad one, since the two preceding functions have not been + * really defined. I cannot tell is the short answer. I thus + * return isWritable() just to give us an idea. + * + * @param column the first column is 1, the second is 2, etc.. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException + { + return false; + } + + // ******************************************************** + // END OF PUBLIC INTERFACE + // ******************************************************** + + /** + * For several routines in this package, we need to convert + * a columnIndex into a Field[] descriptor. Rather than do + * the same code several times, here it is. + * + * @param columnIndex the first column is 1, the second is 2... + * @return the Field description + * @exception SQLException if a database access error occurs */ - return columnNullableUnknown; - } - - /** - * Is the column a signed number? In PostgreSQL, all numbers - * are signed, so this is trivial. However, strings are not - * signed (duh!) - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isSigned(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - return true; - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; // I don't know about these? - default: - return false; - } - } - - /** - * What is the column's normal maximum width in characters? - * - * @param column the first column is 1, the second is 2, etc. - * @return the maximum width - * @exception SQLException if a database access error occurs - */ - public int getColumnDisplaySize(int column) throws SQLException - { - Field f = getField(column); - String type_name = f.getPGType(); - int sql_type = f.getSQLType(); - int typmod = f.getMod(); - - // I looked at other JDBC implementations and couldn't find a consistent - // interpretation of the "display size" for numeric values, so this is our's - // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de - - // fixed length data types - if (type_name.equals( "int2" )) return 6; // -32768 to +32768 (5 digits and a sign) - if (type_name.equals( "int4" ) - || type_name.equals( "oid" )) return 11; // -2147483648 to +2147483647 - if (type_name.equals( "int8" )) return 20; // -9223372036854775808 to +9223372036854775807 - if (type_name.equals( "money" )) return 12; // MONEY = DECIMAL(9,2) - if (type_name.equals( "float4" )) return 11; // i checked it out ans wasn't able to produce more than 11 digits - if (type_name.equals( "float8" )) return 20; // dito, 20 - if (type_name.equals( "char" )) return 1; - if (type_name.equals( "bool" )) return 1; - if (type_name.equals( "date" )) return 14; // "01/01/4713 BC" - "31/12/32767 AD" - if (type_name.equals( "time" )) return 8; // 00:00:00-23:59:59 - if (type_name.equals( "timestamp" )) return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 - - // variable length fields - typmod -= 4; - if (type_name.equals( "bpchar" ) - || type_name.equals( "varchar" )) return typmod; // VARHDRSZ=sizeof(int32)=4 - if (type_name.equals( "numeric" )) return ( (typmod >>16) & 0xffff ) - + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) - - // if we don't know better - return f.getLength(); - } - - /** - * What is the suggested column title for use in printouts and - * displays? We suggest the ColumnName! - * - * @param column the first column is 1, the second is 2, etc. - * @return the column label - * @exception SQLException if a database access error occurs - */ - public String getColumnLabel(int column) throws SQLException - { - return getColumnName(column); - } - - /** - * What's a column's name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the column name - * @exception SQLException if a database access error occurs - */ - public String getColumnName(int column) throws SQLException - { - Field f = getField(column); - if(f!=null) - return f.getName(); - return "field"+column; - } - - /** - * What is a column's table's schema? This relies on us knowing - * the table name....which I don't know how to do as yet. The - * JDBC specification allows us to return "" if this is not - * applicable. - * - * @param column the first column is 1, the second is 2... - * @return the Schema - * @exception SQLException if a database access error occurs - */ - public String getSchemaName(int column) throws SQLException - { - return ""; - } - - /** - * What is a column's number of decimal digits. - * - * @param column the first column is 1, the second is 2... - * @return the precision - * @exception SQLException if a database access error occurs - */ - public int getPrecision(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 5; - case Types.INTEGER: - return 10; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - case Types.VARCHAR: - return 0; - case Types.NUMERIC: - Field f = getField(column); - if(f != null) - return ((0xFFFF0000)&f.getMod())>>16; - else - return 0; - default: - return 0; - } - } - - /** - * What is a column's number of digits to the right of the - * decimal point? - * - * @param column the first column is 1, the second is 2... - * @return the scale - * @exception SQLException if a database access error occurs - */ - public int getScale(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 0; - case Types.INTEGER: - return 0; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - case Types.VARCHAR: - return 0; - case Types.NUMERIC: - Field f = getField(column); - if(f != null) - return (((0x0000FFFF)&f.getMod())-4); - else - return 0; - default: - return 0; - } - } - - /** - * Whats a column's table's name? How do I find this out? Both - * getSchemaName() and getCatalogName() rely on knowing the table - * Name, so we need this before we can work on them. - * - * @param column the first column is 1, the second is 2... - * @return column name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - public String getTableName(int column) throws SQLException - { - return ""; - } - - /** - * What's a column's table's catalog name? As with getSchemaName(), - * we can say that if getTableName() returns n/a, then we can too - - * otherwise, we need to work on it. - * - * @param column the first column is 1, the second is 2... - * @return catalog name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - public String getCatalogName(int column) throws SQLException - { - return ""; - } - - /** - * What is a column's SQL Type? (java.sql.Type int) - * - * @param column the first column is 1, the second is 2, etc. - * @return the java.sql.Type value - * @exception SQLException if a database access error occurs - * @see org.postgresql.Field#getSQLType - * @see java.sql.Types - */ - public int getColumnType(int column) throws SQLException - { - return getField(column).getSQLType(); - } - - /** - * Whats is the column's data source specific type name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the type name - * @exception SQLException if a database access error occurs - */ - public String getColumnTypeName(int column) throws SQLException - { - return getField(column).getPGType(); - } - - /** - * Is the column definitely not writable? In reality, we would - * have to check the GRANT/REVOKE stuff for this to be effective, - * and I haven't really looked into that yet, so this will get - * re-visited. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly(int column) throws SQLException - { - return false; - } - - /** - * Is it possible for a write on the column to succeed? Again, we - * would in reality have to check the GRANT/REVOKE stuff, which - * I haven't worked with as yet. However, if it isn't ReadOnly, then - * it is obviously writable. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isWritable(int column) throws SQLException - { - return !isReadOnly(column); - } - - /** - * Will a write on this column definately succeed? Hmmm...this - * is a bad one, since the two preceding functions have not been - * really defined. I cannot tell is the short answer. I thus - * return isWritable() just to give us an idea. - * - * @param column the first column is 1, the second is 2, etc.. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isDefinitelyWritable(int column) throws SQLException - { - return false; - } - - // ******************************************************** - // END OF PUBLIC INTERFACE - // ******************************************************** - - /** - * For several routines in this package, we need to convert - * a columnIndex into a Field[] descriptor. Rather than do - * the same code several times, here it is. - * - * @param columnIndex the first column is 1, the second is 2... - * @return the Field description - * @exception SQLException if a database access error occurs - */ - private Field getField(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - return fields[columnIndex - 1]; - } + private Field getField(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + return fields[columnIndex - 1]; + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java index 436d815d68..c46f59d542 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java @@ -16,7 +16,7 @@ import org.postgresql.util.PSQLException; *

Only one ResultSet per Statement can be open at any point in time. * Therefore, if the reading of one ResultSet is interleaved with the * reading of another, each must have been generated by different - * Statements. All statement execute methods implicitly close a + * Statements. All statement execute methods implicitly close a * statement's current ResultSet if an open one exists. * * @see java.sql.Statement @@ -24,7 +24,7 @@ import org.postgresql.util.PSQLException; */ public class Statement extends org.postgresql.Statement implements java.sql.Statement { - private Connection connection; // The connection who created us + private Connection connection; // The connection who created us /** * Constructor for a Statement. It simply sets the connection @@ -73,7 +73,7 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat /** * setCursorName defines the SQL cursor name that will be used by - * subsequent execute methods. This name can then be used in SQL + * subsequent execute methods. This name can then be used in SQL * positioned update/delete statements to identify the current row * in the ResultSet generated by this statement. If a database * doesn't support positioned update/delete, this method is a @@ -81,10 +81,10 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat * *

Note: By definition, positioned update/delete execution * must be done by a different Statement than the one which - * generated the ResultSet being used for positioning. Also, cursor + * generated the ResultSet being used for positioning. Also, cursor * names must be unique within a Connection. * - *

We throw an additional constriction. There can only be one + *

We throw an additional constriction. There can only be one * cursor active at any one time. * * @param name the new cursor name @@ -98,20 +98,20 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat /** * Execute a SQL statement that may return multiple results. We * don't have to worry about this since we do not support multiple - * ResultSets. You can use getResultSet or getUpdateCount to + * ResultSets. You can use getResultSet or getUpdateCount to * retrieve the result. * * @param sql any SQL statement * @return true if the next result is a ResulSet, false if it is - * an update count or there are no more results + * an update count or there are no more results * @exception SQLException if a database access error occurs */ public boolean execute(String sql) throws SQLException { - if (escapeProcessing) - sql = escapeSQL(sql); - result = connection.ExecSQL(sql); - return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); + if (escapeProcessing) + sql = escapeSQL(sql); + result = connection.ExecSQL(sql); + return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); } /** @@ -124,8 +124,10 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat */ public int getUpdateCount() throws SQLException { - if (result == null) return -1; - if (((org.postgresql.ResultSet)result).reallyResultSet()) return -1; + if (result == null) + return -1; + if (((org.postgresql.ResultSet)result).reallyResultSet()) + return -1; return ((org.postgresql.ResultSet)result).getResultCount(); } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java index 16015b881b..e741f0f5b5 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java @@ -25,69 +25,78 @@ import org.postgresql.util.*; public class Array implements java.sql.Array { - private org.postgresql.Connection conn = null; - private org.postgresql.Field field = null; - private org.postgresql.jdbc2.ResultSet rs = null; - private int idx = 0; - private String rawString = null; + private org.postgresql.Connection conn = null; + private org.postgresql.Field field = null; + private org.postgresql.jdbc2.ResultSet rs = null; + private int idx = 0; + private String rawString = null; - /** - * Create a new Array - * - * @param conn a database connection - * @param idx 1-based index of the query field to load into this Array - * @param field the Field descriptor for the field to load into this Array - * @param rs the ResultSet from which to get the data for this Array - */ - public Array( org.postgresql.Connection conn, int idx, Field field, org.postgresql.jdbc2.ResultSet rs ) - throws SQLException - { + /** + * Create a new Array + * + * @param conn a database connection + * @param idx 1-based index of the query field to load into this Array + * @param field the Field descriptor for the field to load into this Array + * @param rs the ResultSet from which to get the data for this Array + */ + public Array( org.postgresql.Connection conn, int idx, Field field, org.postgresql.jdbc2.ResultSet rs ) + throws SQLException + { this.conn = conn; - this.field = field; + this.field = field; this.rs = rs; this.idx = idx; this.rawString = rs.getFixedString(idx); } - public Object getArray() throws SQLException { + public Object getArray() throws SQLException + { return getArray( 1, 0, null ); } - public Object getArray(long index, int count) throws SQLException { + public Object getArray(long index, int count) throws SQLException + { return getArray( index, count, null ); } - public Object getArray(Map map) throws SQLException { + public Object getArray(Map map) throws SQLException + { return getArray( 1, 0, map ); } - public Object getArray(long index, int count, Map map) throws SQLException { - if( map != null ) // For now maps aren't supported. - throw org.postgresql.Driver.notImplemented(); + public Object getArray(long index, int count, Map map) throws SQLException + { + if ( map != null ) // For now maps aren't supported. + throw org.postgresql.Driver.notImplemented(); - if (index < 1) - throw new PSQLException("postgresql.arr.range"); + if (index < 1) + throw new PSQLException("postgresql.arr.range"); Object retVal = null; ArrayList array = new ArrayList(); - if( rawString != null ) { + if ( rawString != null ) + { char[] chars = rawString.toCharArray(); StringBuffer sbuf = new StringBuffer(); boolean foundOpen = false; boolean insideString = false; - for( int i=0; i arrayContents.length ) - throw new PSQLException("postgresql.arr.range"); + if ( index + count > arrayContents.length ) + throw new PSQLException("postgresql.arr.range"); int i = 0; switch ( getBaseType() ) { - case Types.BIT: - retVal = new boolean[ count ]; - for( ; count > 0; count-- ) - ((boolean[])retVal)[i++] = ResultSet.toBoolean( arrayContents[(int)index++] ); - break; - case Types.SMALLINT: - case Types.INTEGER: - retVal = new int[ count ]; - for( ; count > 0; count-- ) - ((int[])retVal)[i++] = ResultSet.toInt( arrayContents[(int)index++] ); - break; - case Types.BIGINT: - retVal = new long[ count ]; - for( ; count > 0; count-- ) - ((long[])retVal)[i++] = ResultSet.toLong( arrayContents[(int)index++] ); - break; - case Types.NUMERIC: - retVal = new BigDecimal[ count ]; - for( ; count > 0; count-- ) - ((BigDecimal[])retVal)[i] = ResultSet.toBigDecimal( arrayContents[(int)index++], 0 ); - break; - case Types.REAL: - retVal = new float[ count ]; - for( ; count > 0; count-- ) - ((float[])retVal)[i++] = ResultSet.toFloat( arrayContents[(int)index++] ); - break; - case Types.DOUBLE: - retVal = new double[ count ]; - for( ; count > 0; count-- ) - ((double[])retVal)[i++] = ResultSet.toDouble( arrayContents[(int)index++] ); - break; - case Types.CHAR: - case Types.VARCHAR: - retVal = new String[ count ]; - for( ; count > 0; count-- ) - ((String[])retVal)[i++] = arrayContents[(int)index++]; - break; - case Types.DATE: - retVal = new java.sql.Date[ count ]; - for( ; count > 0; count-- ) - ((java.sql.Date[])retVal)[i++] = ResultSet.toDate( arrayContents[(int)index++] ); - break; - case Types.TIME: - retVal = new java.sql.Time[ count ]; - for( ; count > 0; count-- ) - ((java.sql.Time[])retVal)[i++] = ResultSet.toTime( arrayContents[(int)index++] ); - break; - case Types.TIMESTAMP: - retVal = new Timestamp[ count ]; - StringBuffer sbuf = null; - for( ; count > 0; count-- ) - ((java.sql.Timestamp[])retVal)[i++] = ResultSet.toTimestamp( arrayContents[(int)index], rs ); - break; + case Types.BIT: + retVal = new boolean[ count ]; + for ( ; count > 0; count-- ) + ((boolean[])retVal)[i++] = ResultSet.toBoolean( arrayContents[(int)index++] ); + break; + case Types.SMALLINT: + case Types.INTEGER: + retVal = new int[ count ]; + for ( ; count > 0; count-- ) + ((int[])retVal)[i++] = ResultSet.toInt( arrayContents[(int)index++] ); + break; + case Types.BIGINT: + retVal = new long[ count ]; + for ( ; count > 0; count-- ) + ((long[])retVal)[i++] = ResultSet.toLong( arrayContents[(int)index++] ); + break; + case Types.NUMERIC: + retVal = new BigDecimal[ count ]; + for ( ; count > 0; count-- ) + ((BigDecimal[])retVal)[i] = ResultSet.toBigDecimal( arrayContents[(int)index++], 0 ); + break; + case Types.REAL: + retVal = new float[ count ]; + for ( ; count > 0; count-- ) + ((float[])retVal)[i++] = ResultSet.toFloat( arrayContents[(int)index++] ); + break; + case Types.DOUBLE: + retVal = new double[ count ]; + for ( ; count > 0; count-- ) + ((double[])retVal)[i++] = ResultSet.toDouble( arrayContents[(int)index++] ); + break; + case Types.CHAR: + case Types.VARCHAR: + retVal = new String[ count ]; + for ( ; count > 0; count-- ) + ((String[])retVal)[i++] = arrayContents[(int)index++]; + break; + case Types.DATE: + retVal = new java.sql.Date[ count ]; + for ( ; count > 0; count-- ) + ((java.sql.Date[])retVal)[i++] = ResultSet.toDate( arrayContents[(int)index++] ); + break; + case Types.TIME: + retVal = new java.sql.Time[ count ]; + for ( ; count > 0; count-- ) + ((java.sql.Time[])retVal)[i++] = ResultSet.toTime( arrayContents[(int)index++] ); + break; + case Types.TIMESTAMP: + retVal = new Timestamp[ count ]; + StringBuffer sbuf = null; + for ( ; count > 0; count-- ) + ((java.sql.Timestamp[])retVal)[i++] = ResultSet.toTimestamp( arrayContents[(int)index], rs ); + break; // Other datatypes not currently supported. If you are really using other types ask // yourself if an array of non-trivial data types is really good database design. - default: - throw org.postgresql.Driver.notImplemented(); + default: + throw org.postgresql.Driver.notImplemented(); } return retVal; } - public int getBaseType() throws SQLException { - return conn.getSQLType(getBaseTypeName()); + public int getBaseType() throws SQLException + { + return conn.getSQLType(getBaseTypeName()); } - public String getBaseTypeName() throws SQLException { - String fType = field.getPGType(); - if( fType.charAt(0) == '_' ) + public String getBaseTypeName() throws SQLException + { + String fType = field.getPGType(); + if ( fType.charAt(0) == '_' ) fType = fType.substring(1); return fType; } - public java.sql.ResultSet getResultSet() throws SQLException { + public java.sql.ResultSet getResultSet() throws SQLException + { return getResultSet( 1, 0, null ); } - public java.sql.ResultSet getResultSet(long index, int count) throws SQLException { + public java.sql.ResultSet getResultSet(long index, int count) throws SQLException + { return getResultSet( index, count, null ); } - public java.sql.ResultSet getResultSet(Map map) throws SQLException { + public java.sql.ResultSet getResultSet(Map map) throws SQLException + { return getResultSet( 1, 0, map ); } - public java.sql.ResultSet getResultSet(long index, int count, java.util.Map map) throws SQLException { + public java.sql.ResultSet getResultSet(long index, int count, java.util.Map map) throws SQLException + { Object array = getArray( index, count, map ); Vector rows = new Vector(); Field[] fields = new Field[2]; - fields[0] = new Field(conn, "INDEX", conn.getOID("int2"), 2); + fields[0] = new Field(conn, "INDEX", conn.getOID("int2"), 2); switch ( getBaseType() ) { - case Types.BIT: - boolean[] booleanArray = (boolean[]) array; - fields[1] = new Field(conn, "VALUE", conn.getOID("bool"), 1); - for( int i=0; iNote: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType SQL type code defined by java.sql.Types; for - * parameters of type Numeric or Decimal use the version of - * registerOutParameter that accepts a scale value - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { - } - - /** - * You must also specify the scale for numeric/decimal types: - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType, - int scale) throws SQLException - { - } - - // Old api? - //public boolean isNull(int parameterIndex) throws SQLException { - //return true; - //} - - /** - * An OUT parameter may have the value of SQL NULL; wasNull - * reports whether the last value read has this special value. - * - *

Note: You must first call getXXX on a parameter to read its - * value and then call wasNull() to see if the value was SQL NULL. - * @return true if the last parameter read was SQL NULL - * @exception SQLException if a database-access error occurs. - */ - public boolean wasNull() throws SQLException { - // check to see if the last access threw an exception - return false; // fake it for now - } - - // Old api? - //public String getChar(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a - * Java String. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public String getString(int parameterIndex) throws SQLException { - return null; - } - //public String getVarChar(int parameterIndex) throws SQLException { - // return null; - //} - - //public String getLongVarChar(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a BIT parameter as a Java boolean. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is false - * @exception SQLException if a database-access error occurs. - */ - public boolean getBoolean(int parameterIndex) throws SQLException { - return false; - } - - /** - * Get the value of a TINYINT parameter as a Java byte. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public byte getByte(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a SMALLINT parameter as a Java short. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public short getShort(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of an INTEGER parameter as a Java int. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ -public int getInt(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a BIGINT parameter as a Java long. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public long getLong(int parameterIndex) throws SQLException { - return 0; - } - - /** - * Get the value of a FLOAT parameter as a Java float. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public float getFloat(int parameterIndex) throws SQLException { - return (float) 0.0; - } - - /** - * Get the value of a DOUBLE parameter as a Java double. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public double getDouble(int parameterIndex) throws SQLException { - return 0.0; - } - - /** - * Get the value of a NUMERIC parameter as a java.math.BigDecimal - * object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - * @deprecated in Java2.0 - */ - public BigDecimal getBigDecimal(int parameterIndex, int scale) - throws SQLException { - return null; - } - - /** - * Get the value of a SQL BINARY or VARBINARY parameter as a Java - * byte[] - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public byte[] getBytes(int parameterIndex) throws SQLException { - return null; - } - - // New API (JPM) (getLongVarBinary) - //public byte[] getBinaryStream(int parameterIndex) throws SQLException { - //return null; - //} - - /** - * Get the value of a SQL DATE parameter as a java.sql.Date object - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Date getDate(int parameterIndex) throws SQLException { - return null; - } - - /** - * Get the value of a SQL TIME parameter as a java.sql.Time object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Time getTime(int parameterIndex) throws SQLException { - return null; - } - - /** - * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Timestamp getTimestamp(int parameterIndex) - throws SQLException { - return null; - } - - //---------------------------------------------------------------------- - // Advanced features: - - // You can obtain a ParameterMetaData object to get information - // about the parameters to this CallableStatement. - //public DatabaseMetaData getMetaData() { - //return null; - //} - - // getObject returns a Java object for the parameter. - // See the JDBC spec's "Dynamic Programming" chapter for details. - /** - * Get the value of a parameter as a Java object. - * - *

This method returns a Java object whose type coresponds to the - * SQL type that was registered for this parameter using - * registerOutParameter. - * - *

Note that this method may be used to read datatabase-specific, - * abstract data types. This is done by specifying a targetSqlType - * of java.sql.types.OTHER, which allows the driver to return a - * database-specific Java type. - * - *

See the JDBC spec's "Dynamic Programming" chapter for details. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return A java.lang.Object holding the OUT parameter value. - * @exception SQLException if a database-access error occurs. - */ - public Object getObject(int parameterIndex) - throws SQLException { - return null; - } - - // ** JDBC 2 Extensions ** - - public java.sql.Array getArray(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public java.math.BigDecimal getBigDecimal(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Blob getBlob(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Clob getClob(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Object getObject(int i,java.util.Map map) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Ref getRef(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Time getTime(int i,java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public void registerOutParameter(int parameterIndex, int sqlType,String typeName) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } + /** + * @exception SQLException on failure + */ + public CallableStatement(Connection c, String q) throws SQLException + { + super(c, q); + } + + /** + * Before executing a stored procedure call you must explicitly + * call registerOutParameter to register the java.sql.Type of each + * out parameter. + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType SQL type code defined by java.sql.Types; for + * parameters of type Numeric or Decimal use the version of + * registerOutParameter that accepts a scale value + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException + {} + + /** + * You must also specify the scale for numeric/decimal types: + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType, + int scale) throws SQLException + {} + + // Old api? + //public boolean isNull(int parameterIndex) throws SQLException { + //return true; + //} + + /** + * An OUT parameter may have the value of SQL NULL; wasNull + * reports whether the last value read has this special value. + * + *

Note: You must first call getXXX on a parameter to read its + * value and then call wasNull() to see if the value was SQL NULL. + * @return true if the last parameter read was SQL NULL + * @exception SQLException if a database-access error occurs. + */ + public boolean wasNull() throws SQLException + { + // check to see if the last access threw an exception + return false; // fake it for now + } + + // Old api? + //public String getChar(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a + * Java String. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public String getString(int parameterIndex) throws SQLException + { + return null; + } + //public String getVarChar(int parameterIndex) throws SQLException { + // return null; + //} + + //public String getLongVarChar(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a BIT parameter as a Java boolean. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is false + * @exception SQLException if a database-access error occurs. + */ + public boolean getBoolean(int parameterIndex) throws SQLException + { + return false; + } + + /** + * Get the value of a TINYINT parameter as a Java byte. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public byte getByte(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a SMALLINT parameter as a Java short. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public short getShort(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of an INTEGER parameter as a Java int. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public int getInt(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a BIGINT parameter as a Java long. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public long getLong(int parameterIndex) throws SQLException + { + return 0; + } + + /** + * Get the value of a FLOAT parameter as a Java float. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public float getFloat(int parameterIndex) throws SQLException + { + return (float) 0.0; + } + + /** + * Get the value of a DOUBLE parameter as a Java double. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public double getDouble(int parameterIndex) throws SQLException + { + return 0.0; + } + + /** + * Get the value of a NUMERIC parameter as a java.math.BigDecimal + * object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + * @deprecated in Java2.0 + */ + public BigDecimal getBigDecimal(int parameterIndex, int scale) + throws SQLException + { + return null; + } + + /** + * Get the value of a SQL BINARY or VARBINARY parameter as a Java + * byte[] + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public byte[] getBytes(int parameterIndex) throws SQLException + { + return null; + } + + // New API (JPM) (getLongVarBinary) + //public byte[] getBinaryStream(int parameterIndex) throws SQLException { + //return null; + //} + + /** + * Get the value of a SQL DATE parameter as a java.sql.Date object + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Date getDate(int parameterIndex) throws SQLException + { + return null; + } + + /** + * Get the value of a SQL TIME parameter as a java.sql.Time object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Time getTime(int parameterIndex) throws SQLException + { + return null; + } + + /** + * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(int parameterIndex) + throws SQLException + { + return null; + } + + //---------------------------------------------------------------------- + // Advanced features: + + // You can obtain a ParameterMetaData object to get information + // about the parameters to this CallableStatement. + //public DatabaseMetaData getMetaData() { + //return null; + //} + + // getObject returns a Java object for the parameter. + // See the JDBC spec's "Dynamic Programming" chapter for details. + /** + * Get the value of a parameter as a Java object. + * + *

This method returns a Java object whose type coresponds to the + * SQL type that was registered for this parameter using + * registerOutParameter. + * + *

Note that this method may be used to read datatabase-specific, + * abstract data types. This is done by specifying a targetSqlType + * of java.sql.types.OTHER, which allows the driver to return a + * database-specific Java type. + * + *

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ + public Object getObject(int parameterIndex) + throws SQLException + { + return null; + } + + // ** JDBC 2 Extensions ** + + public java.sql.Array getArray(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public java.math.BigDecimal getBigDecimal(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Blob getBlob(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Clob getClob(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Object getObject(int i, java.util.Map map) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Ref getRef(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Time getTime(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java index 91aaa2798a..50a850d2c4 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java @@ -17,9 +17,9 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.13 2001/10/09 20:47:35 barry Exp $ + * $Id: Connection.java,v 1.14 2001/10/25 05:59:59 momjian Exp $ * - * A Connection represents a session with a specific database. Within the + * A Connection represents a session with a specific database. Within the * context of a Connection, SQL statements are executed and results are * returned. * @@ -29,306 +29,314 @@ import org.postgresql.util.*; * with the getMetaData method. * *

Note: By default, the Connection automatically commits changes - * after executing each statement. If auto-commit has been disabled, an + * after executing each statement. If auto-commit has been disabled, an * explicit commit must be done or database changes will not be saved. * * @see java.sql.Connection */ public class Connection extends org.postgresql.Connection implements java.sql.Connection { - // This is a cache of the DatabaseMetaData instance for this connection - protected DatabaseMetaData metadata; + // This is a cache of the DatabaseMetaData instance for this connection + protected DatabaseMetaData metadata; - /** - * The current type mappings - */ - protected java.util.Map typemap; + /** + * The current type mappings + */ + protected java.util.Map typemap; - /** - * SQL statements without parameters are normally executed using - * Statement objects. If the same SQL statement is executed many - * times, it is more efficient to use a PreparedStatement - * - * @return a new Statement object - * @exception SQLException passed through from the constructor - */ - public java.sql.Statement createStatement() throws SQLException - { - // The spec says default of TYPE_FORWARD_ONLY but everyone is used to - // using TYPE_SCROLL_INSENSITIVE - return createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY); - } + /** + * SQL statements without parameters are normally executed using + * Statement objects. If the same SQL statement is executed many + * times, it is more efficient to use a PreparedStatement + * + * @return a new Statement object + * @exception SQLException passed through from the constructor + */ + public java.sql.Statement createStatement() throws SQLException + { + // The spec says default of TYPE_FORWARD_ONLY but everyone is used to + // using TYPE_SCROLL_INSENSITIVE + return createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY); + } - /** - * SQL statements without parameters are normally executed using - * Statement objects. If the same SQL statement is executed many - * times, it is more efficient to use a PreparedStatement - * - * @param resultSetType to use - * @param resultSetCuncurrency to use - * @return a new Statement object - * @exception SQLException passed through from the constructor - */ - public java.sql.Statement createStatement(int resultSetType,int resultSetConcurrency) throws SQLException - { - Statement s = new Statement(this); - s.setResultSetType(resultSetType); - s.setResultSetConcurrency(resultSetConcurrency); - return s; - } + /** + * SQL statements without parameters are normally executed using + * Statement objects. If the same SQL statement is executed many + * times, it is more efficient to use a PreparedStatement + * + * @param resultSetType to use + * @param resultSetCuncurrency to use + * @return a new Statement object + * @exception SQLException passed through from the constructor + */ + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException + { + Statement s = new Statement(this); + s.setResultSetType(resultSetType); + s.setResultSetConcurrency(resultSetConcurrency); + return s; + } - /** - * A SQL statement with or without IN parameters can be pre-compiled - * and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - * - * Note: This method is optimized for handling parametric - * SQL statements that benefit from precompilation if the drivers - * supports precompilation. PostgreSQL does not support precompilation. - * In this case, the statement is not sent to the database until the - * PreparedStatement is executed. This has no direct effect on users; - * however it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' IN - * parameter placeholders - * @return a new PreparedStatement object containing the pre-compiled - * statement. - * @exception SQLException if a database access error occurs. - */ - public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException - { - return prepareStatement(sql,java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY); - } + /** + * A SQL statement with or without IN parameters can be pre-compiled + * and stored in a PreparedStatement object. This object can then + * be used to efficiently execute this statement multiple times. + * + * Note: This method is optimized for handling parametric + * SQL statements that benefit from precompilation if the drivers + * supports precompilation. PostgreSQL does not support precompilation. + * In this case, the statement is not sent to the database until the + * PreparedStatement is executed. This has no direct effect on users; + * however it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' IN + * parameter placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException + { + return prepareStatement(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY); + } - public java.sql.PreparedStatement prepareStatement(String sql,int resultSetType,int resultSetConcurrency) throws SQLException - { - PreparedStatement s = new PreparedStatement(this,sql); - s.setResultSetType(resultSetType); - s.setResultSetConcurrency(resultSetConcurrency); - return s; - } + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException + { + PreparedStatement s = new PreparedStatement(this, sql); + s.setResultSetType(resultSetType); + s.setResultSetConcurrency(resultSetConcurrency); + return s; + } - /** - * A SQL stored procedure call statement is handled by creating a - * CallableStatement for it. The CallableStatement provides methods - * for setting up its IN and OUT parameters and methods for executing - * it. - * - * Note: This method is optimised for handling stored procedure - * call statements. Some drivers may send the call statement to the - * database when the prepareCall is done; others may wait until the - * CallableStatement is executed. This has no direct effect on users; - * however, it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' parameter - * placeholders. Typically this statement is a JDBC function call - * escape string. - * @return a new CallableStatement object containing the pre-compiled - * SQL statement - * @exception SQLException if a database access error occurs - */ - public java.sql.CallableStatement prepareCall(String sql) throws SQLException - { - return prepareCall(sql,java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY); - } + /** + * A SQL stored procedure call statement is handled by creating a + * CallableStatement for it. The CallableStatement provides methods + * for setting up its IN and OUT parameters and methods for executing + * it. + * + * Note: This method is optimised for handling stored procedure + * call statements. Some drivers may send the call statement to the + * database when the prepareCall is done; others may wait until the + * CallableStatement is executed. This has no direct effect on users; + * however, it does affect which method throws certain SQLExceptions + * + * @param sql a SQL statement that may contain one or more '?' parameter + * placeholders. Typically this statement is a JDBC function call + * escape string. + * @return a new CallableStatement object containing the pre-compiled + * SQL statement + * @exception SQLException if a database access error occurs + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException + { + return prepareCall(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY); + } - public java.sql.CallableStatement prepareCall(String sql,int resultSetType,int resultSetConcurrency) throws SQLException - { - throw new PSQLException("postgresql.con.call"); - //CallableStatement s = new CallableStatement(this,sql); - //s.setResultSetType(resultSetType); - //s.setResultSetConcurrency(resultSetConcurrency); - //return s; - } + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException + { + throw new PSQLException("postgresql.con.call"); + //CallableStatement s = new CallableStatement(this,sql); + //s.setResultSetType(resultSetType); + //s.setResultSetConcurrency(resultSetConcurrency); + //return s; + } - /** - * Tests to see if a Connection is closed. - * - * Peter Feb 7 2000: Now I've discovered that this doesn't actually obey the - * specifications. Under JDBC2.1, this should only be valid _after_ close() - * has been called. It's result is not guraranteed to be valid before, and - * client code should not use it to see if a connection is open. The spec says - * that the client should monitor the SQLExceptions thrown when their queries - * fail because the connection is dead. - * - * I don't like this definition. As it doesn't hurt breaking it here, our - * isClosed() implementation does test the connection, so for PostgreSQL, you - * can rely on isClosed() returning a valid result. - * - * @return the status of the connection - * @exception SQLException (why?) - */ - public boolean isClosed() throws SQLException - { - // If the stream is gone, then close() was called - if(pg_stream == null) - return true; + /** + * Tests to see if a Connection is closed. + * + * Peter Feb 7 2000: Now I've discovered that this doesn't actually obey the + * specifications. Under JDBC2.1, this should only be valid _after_ close() + * has been called. It's result is not guraranteed to be valid before, and + * client code should not use it to see if a connection is open. The spec says + * that the client should monitor the SQLExceptions thrown when their queries + * fail because the connection is dead. + * + * I don't like this definition. As it doesn't hurt breaking it here, our + * isClosed() implementation does test the connection, so for PostgreSQL, you + * can rely on isClosed() returning a valid result. + * + * @return the status of the connection + * @exception SQLException (why?) + */ + public boolean isClosed() throws SQLException + { + // If the stream is gone, then close() was called + if (pg_stream == null) + return true; - // ok, test the connection - try { - // by sending an empty query. If we are dead, then an SQLException should - // be thrown - java.sql.ResultSet rs = ExecSQL(" "); - if(rs!=null) - rs.close(); + // ok, test the connection + try + { + // by sending an empty query. If we are dead, then an SQLException should + // be thrown + java.sql.ResultSet rs = ExecSQL(" "); + if (rs != null) + rs.close(); - // By now, we must be alive - return false; - } catch(SQLException se) { - // Why throw an SQLException as this may fail without throwing one, - // ie isClosed() is called incase the connection has died, and we don't - // want to find out by an Exception, so instead we return true, as its - // most likely why it was thrown in the first place. - return true; - } - } + // By now, we must be alive + return false; + } + catch (SQLException se) + { + // Why throw an SQLException as this may fail without throwing one, + // ie isClosed() is called incase the connection has died, and we don't + // want to find out by an Exception, so instead we return true, as its + // most likely why it was thrown in the first place. + return true; + } + } - /** - * A connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is made - * available through a DatabaseMetaData object. - * - * @return a DatabaseMetaData object for this connection - * @exception SQLException if a database access error occurs - */ - public java.sql.DatabaseMetaData getMetaData() throws SQLException - { - if(metadata==null) - metadata = new DatabaseMetaData(this); - return metadata; - } + /** + * A connection's database is able to provide information describing + * its tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made + * available through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException + { + if (metadata == null) + metadata = new DatabaseMetaData(this); + return metadata; + } - /** - * This overides the method in org.postgresql.Connection and returns a - * ResultSet. - */ - public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat,Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) throws SQLException - { - // In 7.1 we now test concurrency to see which class to return. If we are not working with a - // Statement then default to a normal ResultSet object. - if(stat!=null) { - if(stat.getResultSetConcurrency()==java.sql.ResultSet.CONCUR_UPDATABLE) - return new org.postgresql.jdbc2.UpdateableResultSet((org.postgresql.jdbc2.Connection)conn,fields,tuples,status,updateCount,insertOID,binaryCursor); - } + /** + * This overides the method in org.postgresql.Connection and returns a + * ResultSet. + */ + public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) throws SQLException + { + // In 7.1 we now test concurrency to see which class to return. If we are not working with a + // Statement then default to a normal ResultSet object. + if (stat != null) + { + if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE) + return new org.postgresql.jdbc2.UpdateableResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor); + } - return new org.postgresql.jdbc2.ResultSet((org.postgresql.jdbc2.Connection)conn,fields,tuples,status,updateCount,insertOID,binaryCursor); - } + return new org.postgresql.jdbc2.ResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor); + } - // ***************** - // JDBC 2 extensions - // ***************** + // ***************** + // JDBC 2 extensions + // ***************** - public java.util.Map getTypeMap() throws SQLException - { - // new in 7.1 - return typemap; - } + public java.util.Map getTypeMap() throws SQLException + { + // new in 7.1 + return typemap; + } - public void setTypeMap(java.util.Map map) throws SQLException - { - // new in 7.1 - typemap=map; - } + public void setTypeMap(java.util.Map map) throws SQLException + { + // new in 7.1 + typemap = map; + } - /** - * This overides the standard internal getObject method so that we can - * check the jdbc2 type map first - * - * @return PGobject for this type, and set to value - * @exception SQLException if value is not correct for this type - * @see org.postgresql.util.Serialize - */ - public Object getObject(String type,String value) throws SQLException - { - if(typemap!=null) { - SQLData d = (SQLData) typemap.get(type); - if(d!=null) { - // Handle the type (requires SQLInput & SQLOutput classes to be implemented) - throw org.postgresql.Driver.notImplemented(); - } - } + /** + * This overides the standard internal getObject method so that we can + * check the jdbc2 type map first + * + * @return PGobject for this type, and set to value + * @exception SQLException if value is not correct for this type + * @see org.postgresql.util.Serialize + */ + public Object getObject(String type, String value) throws SQLException + { + if (typemap != null) + { + SQLData d = (SQLData) typemap.get(type); + if (d != null) + { + // Handle the type (requires SQLInput & SQLOutput classes to be implemented) + throw org.postgresql.Driver.notImplemented(); + } + } - // Default to the original method - return super.getObject(type,value); - } + // Default to the original method + return super.getObject(type, value); + } - /* An implementation of the abstract method in the parent class. - * This implemetation uses the jdbc2Types array to support the jdbc2 - * datatypes. Basically jdbc1 and jdbc2 are the same, except that - * jdbc2 adds the Array types. - */ - public int getSQLType(String pgTypeName) - { - int sqlType = Types.OTHER; // default value - for(int i=0;iMany of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to @@ -37,2872 +37,2898 @@ import org.postgresql.util.PSQLException; */ public class DatabaseMetaData implements java.sql.DatabaseMetaData { - Connection connection; // The connection association - - // These define various OID's. Hopefully they will stay constant. - static final int iVarcharOid = 1043; // OID for varchar - static final int iBoolOid = 16; // OID for bool - static final int iInt2Oid = 21; // OID for int2 - static final int iInt4Oid = 23; // OID for int4 - static final int VARHDRSZ = 4; // length for int4 - - public DatabaseMetaData(Connection conn) - { - this.connection = conn; - } - - /** - * Can all the procedures returned by getProcedures be called - * by the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allProceduresAreCallable() throws SQLException - { - return true; // For now... - } - - /** - * Can all the tables returned by getTable be SELECTed by - * the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allTablesAreSelectable() throws SQLException - { - return true; // For now... - } - - /** - * What is the URL for this database? - * - * @return the url or null if it cannott be generated - * @exception SQLException if a database access error occurs - */ - public String getURL() throws SQLException - { - return connection.getURL(); - } - - /** - * What is our user name as known to the database? - * - * @return our database user name - * @exception SQLException if a database access error occurs - */ - public String getUserName() throws SQLException - { - return connection.getUserName(); - } - - /** - * Is the database in read-only mode? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - return connection.isReadOnly(); - } - - /** - * Are NULL values sorted high? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedHigh() throws SQLException - { - return connection.haveMinimumServerVersion("7.2"); - } - - /** - * Are NULL values sorted low? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedLow() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the start regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtStart() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the end regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtEnd() throws SQLException - { - return ! connection.haveMinimumServerVersion("7.2"); - } - - /** - * What is the name of this database product - we hope that it is - * PostgreSQL, so we return that explicitly. - * - * @return the database product name - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductName() throws SQLException - { - return "PostgreSQL"; - } - - /** - * What is the version of this database product. - * - * @return the database version - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductVersion() throws SQLException - { - return connection.getDBVersionNumber(); - } - - /** - * What is the name of this JDBC driver? If we don't know this - * we are doing something wrong! - * - * @return the JDBC driver name - * @exception SQLException why? - */ - public String getDriverName() throws SQLException - { - return "PostgreSQL Native Driver"; - } - - /** - * What is the version string of this JDBC driver? Again, this is - * static. - * - * @return the JDBC driver name. - * @exception SQLException why? - */ - public String getDriverVersion() throws SQLException - { - return connection.this_driver.getVersion(); - } - - /** - * What is this JDBC driver's major version number? - * - * @return the JDBC driver major version - */ - public int getDriverMajorVersion() - { - return connection.this_driver.getMajorVersion(); - } - - /** - * What is this JDBC driver's minor version number? - * - * @return the JDBC driver minor version - */ - public int getDriverMinorVersion() - { - return connection.this_driver.getMinorVersion(); - } - - /** - * Does the database store tables in a local file? No - it - * stores them in a file on the server. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFiles() throws SQLException - { - return false; - } - - /** - * Does the database use a file for each table? Well, not really, - * since it doesnt use local files. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFilePerTable() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers - * as case sensitive and as a result store them in mixed case? - * A JDBC-Compliant driver will always return false. - * - *

Predicament - what do they mean by "SQL identifiers" - if it - * means the names of the tables and columns, then the answers - * given below are correct - otherwise I don't know. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case sensitive and as a result store them in mixed case? A - * JDBC compliant driver will always return true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * What is the string used to quote SQL identifiers? This returns - * a space if identifier quoting isn't supported. A JDBC Compliant - * driver will always use a double quote character. - * - * @return the quoting string - * @exception SQLException if a database access error occurs - */ - public String getIdentifierQuoteString() throws SQLException - { - return "\""; - } - - /** - * Get a comma separated list of all a database's SQL keywords that - * are NOT also SQL92 keywords. - * - *

Within PostgreSQL, the keywords are found in - * src/backend/parser/keywords.c - * - *

For SQL Keywords, I took the list provided at - * - * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt - * which is for SQL3, not SQL-92, but it is close enough for - * this purpose. - * - * @return a comma separated list of keywords we use - * @exception SQLException if a database access error occurs - */ - public String getSQLKeywords() throws SQLException - { - return "abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"; - } - - public String getNumericFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getStringFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getSystemFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - public String getTimeDateFunctions() throws SQLException - { - // XXX-Not Implemented - return ""; - } - - /** - * This is the string that can be used to escape '_' and '%' in - * a search string pattern style catalog search parameters - * - * @return the string used to escape wildcard characters - * @exception SQLException if a database access error occurs - */ - public String getSearchStringEscape() throws SQLException - { - return "\\"; - } - - /** - * Get all the "extra" characters that can be used in unquoted - * identifier names (those beyond a-zA-Z0-9 and _) - * - *

From the file src/backend/parser/scan.l, an identifier is - * {letter}{letter_or_digit} which makes it just those listed - * above. - * - * @return a string containing the extra characters - * @exception SQLException if a database access error occurs - */ - public String getExtraNameCharacters() throws SQLException - { - return ""; - } - - /** - * Is "ALTER TABLE" with an add column supported? - * Yes for PostgreSQL 6.1 - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithAddColumn() throws SQLException - { - return true; - } - - /** - * Is "ALTER TABLE" with a drop column supported? - * Peter 10/10/2000 This was set to true, but 7.1devel doesn't support it! - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithDropColumn() throws SQLException - { - return false; - } - - /** - * Is column aliasing supported? - * - *

If so, the SQL AS clause can be used to provide names for - * computed columns or to provide alias names for columns as - * required. A JDBC Compliant driver always returns true. - * - *

e.g. - * - *

-   * select count(C) as C_COUNT from T group by C;
-   *
-   * 

- * should return a column named as C_COUNT instead of count(C) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsColumnAliasing() throws SQLException - { - return true; - } - - /** - * Are concatenations between NULL and non-NULL values NULL? A - * JDBC Compliant driver always returns true - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullPlusNonNullIsNull() throws SQLException - { - return true; - } - - public boolean supportsConvert() throws SQLException - { - // XXX-Not Implemented - return false; - } - - public boolean supportsConvert(int fromType, int toType) throws SQLException - { - // XXX-Not Implemented - return false; - } - - /** - * Are table correlation names supported? A JDBC Compliant - * driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsTableCorrelationNames() throws SQLException - { - return true; - } - - /** - * If table correlation names are supported, are they restricted to - * be different from the names of the tables? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsDifferentTableCorrelationNames() throws SQLException - { - return false; - } - - /** - * Are expressions in "ORDER BY" lists supported? - * - *
e.g. select * from t order by a + b; - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExpressionsInOrderBy() throws SQLException - { - return true; - } - - /** - * Can an "ORDER BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOrderByUnrelated() throws SQLException - { - return connection.haveMinimumServerVersion("6.4"); - } - - /** - * Is some form of "GROUP BY" clause supported? - * I checked it, and yes it is. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupBy() throws SQLException - { - return true; - } - - /** - * Can a "GROUP BY" clause use columns not in the SELECT? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByUnrelated() throws SQLException - { - return connection.haveMinimumServerVersion("6.4"); - } - - /** - * Can a "GROUP BY" clause add columns not in the SELECT provided - * it specifies all the columns in the SELECT? Does anyone actually - * understand what they mean here? - * - * (I think this is a subset of the previous function. -- petere) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByBeyondSelect() throws SQLException - { - return supportsGroupByUnrelated(); - } - - /** - * Is the escape character in "LIKE" clauses supported? A - * JDBC compliant driver always returns true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLikeEscapeClause() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Are multiple ResultSets from a single execute supported? - * Well, I implemented it, but I dont think this is possible from - * the back ends point of view. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleResultSets() throws SQLException - { - return false; - } - - /** - * Can we have multiple transactions open at once (on different - * connections?) - * I guess we can have, since Im relying on it. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleTransactions() throws SQLException - { - return true; - } - - /** - * Can columns be defined as non-nullable. A JDBC Compliant driver - * always returns true. - * - *

This changed from false to true in v6.2 of the driver, as this - * support was added to the backend. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsNonNullableColumns() throws SQLException - { - return true; - } - - /** - * Does this driver support the minimum ODBC SQL grammar. This - * grammar is defined at: - * - *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm - * - *

In Appendix C. From this description, we seem to support the - * ODBC minimal (Level 0) grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMinimumSQLGrammar() throws SQLException - { - return true; - } - - /** - * Does this driver support the Core ODBC SQL grammar. We need - * SQL-92 conformance for this. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCoreSQLGrammar() throws SQLException - { - return false; - } - - /** - * Does this driver support the Extended (Level 2) ODBC SQL - * grammar. We don't conform to the Core (Level 1), so we can't - * conform to the Extended SQL Grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExtendedSQLGrammar() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 entry level SQL grammar? - * All JDBC Compliant drivers must return true. We currently - * report false until 'schema' support is added. Then this - * should be changed to return true, since we will be mostly - * compliant (probably more compliant than many other databases) - * And since this is a requirement for all JDBC drivers we - * need to get to the point where we can return true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92EntryLevelSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 intermediate level SQL - * grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92IntermediateSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 full SQL grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92FullSQL() throws SQLException - { - return false; - } - - /** - * Is the SQL Integrity Enhancement Facility supported? - * I haven't seen this mentioned anywhere, so I guess not - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsIntegrityEnhancementFacility() throws SQLException - { - return false; - } - - /** - * Is some form of outer join supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOuterJoins() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Are full nexted outer joins supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsFullOuterJoins() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Is there limited support for outer joins? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLimitedOuterJoins() throws SQLException - { - return supportsFullOuterJoins(); - } - - /** - * What is the database vendor's preferred term for "schema"? - * PostgreSQL doesn't have schemas, but when it does, we'll use the - * term "schema". - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getSchemaTerm() throws SQLException - { - return "schema"; - } - - /** - * What is the database vendor's preferred term for "procedure"? - * Traditionally, "function" has been used. - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getProcedureTerm() throws SQLException - { - return "function"; - } - - /** - * What is the database vendor's preferred term for "catalog"? - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getCatalogTerm() throws SQLException - { - return "database"; - } - - /** - * Does a catalog appear at the start of a qualified table name? - * (Otherwise it appears at the end). - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCatalogAtStart() throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * What is the Catalog separator. - * - * @return the catalog separator string - * @exception SQLException if a database access error occurs - */ - public String getCatalogSeparator() throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Can a schema name be used in a data manipulation statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a procedure call statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a schema be used in a table definition statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in an index definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a data manipulation statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a procedure call statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a table definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in an index definition? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * We support cursors for gets only it seems. I dont see a method - * to get a positioned delete. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedDelete() throws SQLException - { - return false; // For now... - } - - /** - * Is positioned UPDATE supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedUpdate() throws SQLException - { - return false; // For now... - } - - /** - * Is SELECT for UPDATE supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSelectForUpdate() throws SQLException - { - return connection.haveMinimumServerVersion("6.5"); - } - - /** - * Are stored procedure calls using the stored procedure escape - * syntax supported? - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsStoredProcedures() throws SQLException - { - return false; - } - - /** - * Are subqueries in comparison expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInComparisons() throws SQLException - { - return true; - } - - /** - * Are subqueries in 'exists' expressions supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInExists() throws SQLException - { - return true; - } - - /** - * Are subqueries in 'in' statements supported? A JDBC - * Compliant driver always returns true. - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInIns() throws SQLException - { - return true; - } - - /** - * Are subqueries in quantified expressions supported? A JDBC - * Compliant driver always returns true. - * - * (No idea what this is, but we support a good deal of - * subquerying.) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsSubqueriesInQuantifieds() throws SQLException - { - return true; - } - - /** - * Are correlated subqueries supported? A JDBC Compliant driver - * always returns true. - * - * (a.k.a. subselect in from?) - * - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsCorrelatedSubqueries() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * Is SQL UNION supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnion() throws SQLException - { - return true; // since 6.3 - } - - /** - * Is SQL UNION ALL supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnionAll() throws SQLException - { - return connection.haveMinimumServerVersion("7.1"); - } - - /** - * In PostgreSQL, Cursors are only open within transactions. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossCommit() throws SQLException - { - return false; - } - - /** - * Do we support open cursors across multiple transactions? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossRollback() throws SQLException - { - return false; - } - - /** - * Can statements remain open across commits? They may, but - * this driver cannot guarentee that. In further reflection. - * we are talking a Statement object here, so the answer is - * yes, since the Statement is only a vehicle to ExecSQL() - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossCommit() throws SQLException - { - return true; - } - - /** - * Can statements remain open across rollbacks? They may, but - * this driver cannot guarentee that. In further contemplation, - * we are talking a Statement object here, so the answer is yes, - * since the Statement is only a vehicle to ExecSQL() in Connection - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossRollback() throws SQLException - { - return true; - } - - /** - * How many hex characters can you have in an inline binary literal - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxBinaryLiteralLength() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum length for a character literal - * I suppose it is 8190 (8192 - 2 for the quotes) - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxCharLiteralLength() throws SQLException - { - return 0; // no limit - } - - /** - * Whats the limit on column name length. The description of - * pg_class would say '32' (length of pg_class.relname) - we - * should probably do a query for this....but.... - * - * @return the maximum column name length - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of columns in a "GROUP BY" clause? - * - * @return the max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInGroupBy() throws SQLException - { - return 0; // no limit - } - - /** - * What's the maximum number of columns allowed in an index? - * 6.0 only allowed one column, but 6.1 introduced multi-column - * indices, so, theoretically, its all of them. - * - * @return max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInIndex() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What's the maximum number of columns in an "ORDER BY clause? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInOrderBy() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum number of columns in a "SELECT" list? - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInSelect() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum number of columns in a table? From the - * CREATE TABLE reference page... - * - *

"The new class is created as a heap with no initial data. A - * class can have no more than 1600 attributes (realistically, - * this is limited by the fact that tuple sizes must be less than - * 8192 bytes)..." - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInTable() throws SQLException - { - return 1600; - } - - /** - * How many active connection can we have at a time to this - * database? Well, since it depends on postmaster, which just - * does a listen() followed by an accept() and fork(), its - * basically very high. Unless the system runs out of processes, - * it can be 65535 (the number of aux. ports on a TCP/IP system). - * I will return 8192 since that is what even the largest system - * can realistically handle, - * - * @return the maximum number of connections - * @exception SQLException if a database access error occurs - */ - public int getMaxConnections() throws SQLException - { - return 8192; - } - - /** - * What is the maximum cursor name length (the same as all - * the other F***** identifiers!) - * - * @return max cursor name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxCursorNameLength() throws SQLException - { - return 32; - } - - /** - * Retrieves the maximum number of bytes for an index, including all - * of the parts of the index. - * - * @return max index length in bytes, which includes the composite - * of all the constituent parts of the index; a result of zero means - * that there is no limit or the limit is not known - * @exception SQLException if a database access error occurs - */ - public int getMaxIndexLength() throws SQLException - { - return 0; // no limit (larger than an int anyway) - } - - public int getMaxSchemaNameLength() throws SQLException - { - // XXX-Not Implemented - return 0; - } - - /** - * What is the maximum length of a procedure name? - * (length of pg_proc.proname used) - again, I really - * should do a query here to get it. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxProcedureNameLength() throws SQLException - { - return 32; - } - - public int getMaxCatalogNameLength() throws SQLException - { - // XXX-Not Implemented - return 0; - } - - /** - * What is the maximum length of a single row? - * - * @return max row size in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxRowSize() throws SQLException - { - if (connection.haveMinimumServerVersion("7.1")) - return 1073741824; // 1 GB - else - return 8192; // XXX could be altered - } - - /** - * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY - * blobs? We don't handle blobs yet - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean doesMaxRowSizeIncludeBlobs() throws SQLException - { - return false; - } - - /** - * What is the maximum length of a SQL statement? - * - * @return max length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxStatementLength() throws SQLException - { - if (connection.haveMinimumServerVersion("7.0")) - return 0; // actually whatever fits in size_t - else - return 16384; - } - - /** - * How many active statements can we have open at one time to - * this database? Basically, since each Statement downloads - * the results as the query is executed, we can have many. However, - * we can only really have one statement per connection going - * at once (since they are executed serially) - so we return - * one. - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxStatements() throws SQLException - { - return 1; - } - - /** - * What is the maximum length of a table name? This was found - * from pg_class.relname length - * - * @return max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxTableNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of tables that can be specified - * in a SELECT? - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxTablesInSelect() throws SQLException - { - return 0; // no limit - } - - /** - * What is the maximum length of a user name? Well, we generally - * use UNIX like user names in PostgreSQL, so I think this would - * be 8. However, showing the schema for pg_user shows a length - * for username of 32. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxUserNameLength() throws SQLException - { - return 32; - } - - - /** - * What is the database's default transaction isolation level? We - * do not support this, so all transactions are SERIALIZABLE. - * - * @return the default isolation level - * @exception SQLException if a database access error occurs - * @see Connection - */ - public int getDefaultTransactionIsolation() throws SQLException - { - return Connection.TRANSACTION_READ_COMMITTED; - } - - /** - * Are transactions supported? If not, commit and rollback are noops - * and the isolation level is TRANSACTION_NONE. We do support - * transactions. - * - * @return true if transactions are supported - * @exception SQLException if a database access error occurs - */ - public boolean supportsTransactions() throws SQLException - { - return true; - } - - /** - * Does the database support the given transaction isolation level? - * We only support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED - * - * @param level the values are defined in java.sql.Connection - * @return true if so - * @exception SQLException if a database access error occurs - * @see Connection - */ - public boolean supportsTransactionIsolationLevel(int level) throws SQLException - { - if (level == Connection.TRANSACTION_SERIALIZABLE || - level == Connection.TRANSACTION_READ_COMMITTED) - return true; - else - return false; - } - - /** - * Are both data definition and data manipulation transactions - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException - { - return true; - } - - /** - * Are only data manipulation statements withing a transaction - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataManipulationTransactionsOnly() throws SQLException - { - return false; - } - - /** - * Does a data definition statement within a transaction force - * the transaction to commit? I think this means something like: - * - *

-   * CREATE TABLE T (A INT);
-   * INSERT INTO T (A) VALUES (2);
-   * BEGIN;
-   * UPDATE T SET A = A + 1;
-   * CREATE TABLE X (A INT);
-   * SELECT A FROM T INTO X;
-   * COMMIT;
-   * 

- * - * does the CREATE TABLE call cause a commit? The answer is no. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionCausesTransactionCommit() throws SQLException - { - return false; - } - - /** - * Is a data definition statement within a transaction ignored? - * It seems to be (from experiment in previous method) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionIgnoredInTransactions() throws SQLException - { - return true; - } - - /** - * Get a description of stored procedures available in a catalog - * - *

Only procedure descriptions matching the schema and procedure - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME - * - *

Each procedure description has the following columns: - *

    - *
  1. PROCEDURE_CAT String => procedure catalog (may be null) - *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) - *
  3. PROCEDURE_NAME String => procedure name - *
  4. Field 4 reserved (make it null) - *
  5. Field 5 reserved (make it null) - *
  6. Field 6 reserved (make it null) - *
  7. REMARKS String => explanatory comment on the procedure - *
  8. PROCEDURE_TYPE short => kind of procedure - *
      - *
    • procedureResultUnknown - May return a result - *
    • procedureNoResult - Does not return a result - *
    • procedureReturnsResult - Returns a result - *
    - *
- * - * @param catalog - a catalog name; "" retrieves those without a - * catalog; null means drop catalog name from criteria - * @param schemaParrern - a schema name pattern; "" retrieves those - * without a schema - we ignore this parameter - * @param procedureNamePattern - a procedure name pattern - * @return ResultSet - each row is a procedure description - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException - { - // the field descriptors for the new ResultSet - Field f[] = new Field[8]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); - f[3] = f[4] = f[5] = new Field(connection,"reserved",iVarcharOid,32); // null; // reserved, must be null for now - f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); - f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); - - // If the pattern is null, then set it to the default - if(procedureNamePattern==null) - procedureNamePattern="%"; - - r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname"); - - while (r.next()) - { - byte[][] tuple = new byte[8][0]; - - tuple[0] = null; // Catalog name - tuple[1] = null; // Schema name - tuple[2] = r.getBytes(1); // Procedure name - tuple[3] = tuple[4] = tuple[5] = null; // Reserved - tuple[6] = null; - - if (r.getBoolean(2)) - tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); - else - tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); - - v.addElement(tuple); - } - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of a catalog's stored procedure parameters - * and result columns. - * - *

Only descriptions matching the schema, procedure and parameter - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME. Within this, the return value, if any, is - * first. Next are the parameter descriptions in call order. The - * column descriptions follow in column number order. - * - *

Each row in the ResultSet is a parameter description or column - * description with the following fields: - *

    - *
  1. PROCEDURE_CAT String => procedure catalog (may be null) - *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) - *
  3. PROCEDURE_NAME String => procedure name - *
  4. COLUMN_NAME String => column/parameter name - *
  5. COLUMN_TYPE Short => kind of column/parameter: - *
    • procedureColumnUnknown - nobody knows - *
    • procedureColumnIn - IN parameter - *
    • procedureColumnInOut - INOUT parameter - *
    • procedureColumnOut - OUT parameter - *
    • procedureColumnReturn - procedure return value - *
    • procedureColumnResult - result column in ResultSet - *
    - *
  6. DATA_TYPE short => SQL type from java.sql.Types - *
  7. TYPE_NAME String => SQL type name - *
  8. PRECISION int => precision - *
  9. LENGTH int => length in bytes of data - *
  10. SCALE short => scale - *
  11. RADIX short => radix - *
  12. NULLABLE short => can it contain NULL? - *
    • procedureNoNulls - does not allow NULL values - *
    • procedureNullable - allows NULL values - *
    • procedureNullableUnknown - nullability unknown - *
    • REMARKS String => comment describing parameter/column - *
- * @param catalog This is ignored in org.postgresql, advise this is set to null - * @param schemaPattern This is ignored in org.postgresql, advise this is set to null - * @param procedureNamePattern a procedure name pattern - * @param columnNamePattern a column name pattern - * @return each row is a stored procedure parameter or column description - * @exception SQLException if a database-access error occurs - * @see #getSearchStringEscape - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException - { - if(procedureNamePattern==null) - procedureNamePattern="%"; - - if(columnNamePattern==null) - columnNamePattern="%"; - - // for now, this returns an empty result set. - Field f[] = new Field[13]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[7] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[8] = new Field(connection, "LENGTH", iInt4Oid, 4); - f[9] = new Field(connection, "SCALE", iInt2Oid, 2); - f[10] = new Field(connection, "RADIX", iInt2Oid, 2); - f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[12] = new Field(connection, "REMARKS", iVarcharOid, 32); - - // add query loop here - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of tables available in a catalog. - * - *

Only table descriptions matching the catalog, schema, table - * name and type criteria are returned. They are ordered by - * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. - * - *

Each table description has the following columns: - * - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL - * TEMPORARY", "ALIAS", "SYNONYM". - *
  5. REMARKS String => explanatory comment on the table - *
- * - *

The valid values for the types parameter are: - * "TABLE", "INDEX", "SEQUENCE", "SYSTEM TABLE" and "SYSTEM INDEX" - * - * @param catalog a catalog name; For org.postgresql, this is ignored, and - * should be set to null - * @param schemaPattern a schema name pattern; For org.postgresql, this is ignored, and - * should be set to null - * @param tableNamePattern a table name pattern. For all tables this should be "%" - * @param types a list of table types to include; null returns - * all types - * @return each row is a table description - * @exception SQLException if a database-access error occurs. - */ - public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException - { - // Handle default value for types - if(types==null) - types = defaultTableTypes; - - if(tableNamePattern==null) - tableNamePattern="%"; - - // the field descriptors for the new ResultSet - Field f[] = new Field[5]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "TABLE_TYPE", iVarcharOid, 32); - f[4] = new Field(connection, "REMARKS", iVarcharOid, 32); - - // Now form the query - StringBuffer sql = new StringBuffer("select relname,oid,relkind from pg_class where ("); - - boolean notFirst=false; - for(int i=0;i - // Now take the pattern into account - sql.append(") and relname like '"); - sql.append(tableNamePattern.toLowerCase()); - sql.append("' order by relkind, relname"); - - // Now run the query - r = connection.ExecSQL(sql.toString()); - - while (r.next()) - { - byte[][] tuple = new byte[5][0]; - - // Fetch the description for the table (if any) - String getDescriptionStatement = - connection.haveMinimumServerVersion("7.2") ? - "select obj_description("+r.getInt(2)+",'pg_class')" : - "select description from pg_description where objoid=" + r.getInt(2); - - java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); - - byte remarks[] = null; - - if(((org.postgresql.ResultSet)dr).getTupleCount()==1) { - dr.next(); - remarks = dr.getBytes(1); - } - dr.close(); - - String relKind; - switch (r.getBytes(3)[0]) { - case (byte) 'r': - relKind = "TABLE"; - break; - case (byte) 'i': - relKind = "INDEX"; - break; - case (byte) 'S': - relKind = "SEQUENCE"; - break; - case (byte) 'v': - relKind = "VIEW"; - break; - default: - relKind = null; - } - - tuple[0] = null; // Catalog name - tuple[1] = null; // Schema name - tuple[2] = r.getBytes(1); // Table name - tuple[3] = (relKind==null) ? null : relKind.getBytes(); // Table type - tuple[4] = remarks; // Remarks - v.addElement(tuple); - } - r.close(); - return new ResultSet(connection, f, v, "OK", 1); - } - - // This array contains the valid values for the types argument - // in getTables(). - // - // Each supported type consists of it's name, and the sql where - // clause to retrieve that value. - // - // IMPORTANT: the query must be enclosed in ( ) - private static final String getTableTypes[][] = { - {"TABLE", "(relkind='r' and relhasrules='f' and relname !~ '^pg_' and relname !~ '^xinv')"}, - {"VIEW", "(relkind='v' and relname !~ '^pg_')"}, - {"INDEX", "(relkind='i' and relname !~ '^pg_')"}, - {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, - {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, - {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} - }; - - // These are the default tables, used when NULL is passed to getTables - // The choice of these provide the same behaviour as psql's \d - private static final String defaultTableTypes[] = { - "TABLE","VIEW","INDEX","SEQUENCE" - }; - - /** - * Get the schema names available in this database. The results - * are ordered by schema name. - * - *

The schema column is: - *

    - *
  1. TABLE_SCHEM String => schema name - *
- * - * @return ResultSet each row has a single String column that is a - * schema name - */ - public java.sql.ResultSet getSchemas() throws SQLException - { - // We don't use schemas, so we simply return a single schema name "". - // - Field f[] = new Field[1]; - Vector v = new Vector(); - byte[][] tuple = new byte[1][0]; - f[0] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32); - tuple[0] = "".getBytes(); - v.addElement(tuple); - return new ResultSet(connection,f,v,"OK",1); - } - - /** - * Get the catalog names available in this database. The results - * are ordered by catalog name. - * - *

The catalog column is: - *

    - *
  1. TABLE_CAT String => catalog name - *
- * - * @return ResultSet each row has a single String column that is a - * catalog name - */ - public java.sql.ResultSet getCatalogs() throws SQLException - { - return connection.createStatement().executeQuery("select datname as TABLE_CAT from pg_database;"); - } - - /** - * Get the table types available in this database. The results - * are ordered by table type. - * - *

The table type is: - *

    - *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", - * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", - * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". - *
- * - * @return ResultSet each row has a single String column that is a - * table type - */ - public java.sql.ResultSet getTableTypes() throws SQLException - { - Field f[] = new Field[1]; - Vector v = new Vector(); - f[0] = new Field(connection,new String("TABLE_TYPE"),iVarcharOid,32); - for(int i=0;iOnly column descriptions matching the catalog, schema, table - * and column name criteria are returned. They are ordered by - * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. - * - *

Each column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. DATA_TYPE short => SQL type from java.sql.Types - *
  6. TYPE_NAME String => Data source dependent type name - *
  7. COLUMN_SIZE int => column size. For char or date - * types this is the maximum number of characters, for numeric or - * decimal types this is precision. - *
  8. BUFFER_LENGTH is not used. - *
  9. DECIMAL_DIGITS int => the number of fractional digits - *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) - *
  11. NULLABLE int => is NULL allowed? - *
      - *
    • columnNoNulls - might not allow NULL values - *
    • columnNullable - definitely allows NULL values - *
    • columnNullableUnknown - nullability unknown - *
    - *
  12. REMARKS String => comment describing column (may be null) - *
  13. COLUMN_DEF String => default value (may be null) - *
  14. SQL_DATA_TYPE int => unused - *
  15. SQL_DATETIME_SUB int => unused - *
  16. CHAR_OCTET_LENGTH int => for char types the - * maximum number of bytes in the column - *
  17. ORDINAL_POSITION int => index of column in table - * (starting at 1) - *
  18. IS_NULLABLE String => "NO" means column definitely - * does not allow NULL values; "YES" means the column might - * allow NULL values. An empty string means nobody knows. - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schemaPattern a schema name pattern; "" retrieves those - * without a schema - * @param tableNamePattern a table name pattern - * @param columnNamePattern a column name pattern - * @return ResultSet each row is a column description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException - { - Vector v = new Vector(); // The new ResultSet tuple stuff - Field f[] = new Field[18]; // The field descriptors for the new ResultSet - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); - f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); - f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); - f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); - f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); - f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, 32); - f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4); - f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); - - StringBuffer sql = new StringBuffer(512); - - /* Build a >= 7.1 SQL statement to list all columns */ - sql.append("select " + - (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") + - " c.relname, " + - " a.attname, " + - " a.atttypid, " + - " a.attnum, " + - " a.attnotnull, " + - " a.attlen, " + - " a.atttypmod, " + - " d.adsrc, " + - " t.typname, " + - /* Use the new col_description in 7.2 or an additional outer join in 7.1 */ - (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") + - "from" + - " (" + - " (pg_class c inner join pg_attribute a on" + - " (" + - " a.attrelid=c.oid"); - - if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) { - sql.append(" and c.relname like \'" + tableNamePattern + "\'"); - } - - if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) { - sql.append(" and a.attname like \'" + columnNamePattern + "\'"); - } - - sql.append( - " and a.attnum > 0" + - " )" + - " ) inner join pg_type t on" + - " (" + - " t.oid = a.atttypid" + - " )" + - " )" + - " left outer join pg_attrdef d on" + - " (" + - " c.oid = d.adrelid" + - " and a.attnum = d.adnum" + - " ) "); - - if (!connection.haveMinimumServerVersion("7.2")) { - /* Only for 7.1 */ + Connection connection; // The connection association + + // These define various OID's. Hopefully they will stay constant. + static final int iVarcharOid = 1043; // OID for varchar + static final int iBoolOid = 16; // OID for bool + static final int iInt2Oid = 21; // OID for int2 + static final int iInt4Oid = 23; // OID for int4 + static final int VARHDRSZ = 4; // length for int4 + + public DatabaseMetaData(Connection conn) + { + this.connection = conn; + } + + /** + * Can all the procedures returned by getProcedures be called + * by the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allProceduresAreCallable() throws SQLException + { + return true; // For now... + } + + /** + * Can all the tables returned by getTable be SELECTed by + * the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allTablesAreSelectable() throws SQLException + { + return true; // For now... + } + + /** + * What is the URL for this database? + * + * @return the url or null if it cannott be generated + * @exception SQLException if a database access error occurs + */ + public String getURL() throws SQLException + { + return connection.getURL(); + } + + /** + * What is our user name as known to the database? + * + * @return our database user name + * @exception SQLException if a database access error occurs + */ + public String getUserName() throws SQLException + { + return connection.getUserName(); + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return connection.isReadOnly(); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedHigh() throws SQLException + { + return connection.haveMinimumServerVersion("7.2"); + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedLow() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtStart() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtEnd() throws SQLException + { + return ! connection.haveMinimumServerVersion("7.2"); + } + + /** + * What is the name of this database product - we hope that it is + * PostgreSQL, so we return that explicitly. + * + * @return the database product name + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductName() throws SQLException + { + return "PostgreSQL"; + } + + /** + * What is the version of this database product. + * + * @return the database version + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductVersion() throws SQLException + { + return connection.getDBVersionNumber(); + } + + /** + * What is the name of this JDBC driver? If we don't know this + * we are doing something wrong! + * + * @return the JDBC driver name + * @exception SQLException why? + */ + public String getDriverName() throws SQLException + { + return "PostgreSQL Native Driver"; + } + + /** + * What is the version string of this JDBC driver? Again, this is + * static. + * + * @return the JDBC driver name. + * @exception SQLException why? + */ + public String getDriverVersion() throws SQLException + { + return connection.this_driver.getVersion(); + } + + /** + * What is this JDBC driver's major version number? + * + * @return the JDBC driver major version + */ + public int getDriverMajorVersion() + { + return connection.this_driver.getMajorVersion(); + } + + /** + * What is this JDBC driver's minor version number? + * + * @return the JDBC driver minor version + */ + public int getDriverMinorVersion() + { + return connection.this_driver.getMinorVersion(); + } + + /** + * Does the database store tables in a local file? No - it + * stores them in a file on the server. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFiles() throws SQLException + { + return false; + } + + /** + * Does the database use a file for each table? Well, not really, + * since it doesnt use local files. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFilePerTable() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers + * as case sensitive and as a result store them in mixed case? + * A JDBC-Compliant driver will always return false. + * + *

Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case sensitive and as a result store them in mixed case? A + * JDBC compliant driver will always return true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * What is the string used to quote SQL identifiers? This returns + * a space if identifier quoting isn't supported. A JDBC Compliant + * driver will always use a double quote character. + * + * @return the quoting string + * @exception SQLException if a database access error occurs + */ + public String getIdentifierQuoteString() throws SQLException + { + return "\""; + } + + /** + * Get a comma separated list of all a database's SQL keywords that + * are NOT also SQL92 keywords. + * + *

Within PostgreSQL, the keywords are found in + * src/backend/parser/keywords.c + * + *

For SQL Keywords, I took the list provided at + * + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * which is for SQL3, not SQL-92, but it is close enough for + * this purpose. + * + * @return a comma separated list of keywords we use + * @exception SQLException if a database access error occurs + */ + public String getSQLKeywords() throws SQLException + { + return "abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"; + } + + public String getNumericFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getStringFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getSystemFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getTimeDateFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + /** + * This is the string that can be used to escape '_' and '%' in + * a search string pattern style catalog search parameters + * + * @return the string used to escape wildcard characters + * @exception SQLException if a database access error occurs + */ + public String getSearchStringEscape() throws SQLException + { + return "\\"; + } + + /** + * Get all the "extra" characters that can be used in unquoted + * identifier names (those beyond a-zA-Z0-9 and _) + * + *

From the file src/backend/parser/scan.l, an identifier is + * {letter}{letter_or_digit} which makes it just those listed + * above. + * + * @return a string containing the extra characters + * @exception SQLException if a database access error occurs + */ + public String getExtraNameCharacters() throws SQLException + { + return ""; + } + + /** + * Is "ALTER TABLE" with an add column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException + { + return true; + } + + /** + * Is "ALTER TABLE" with a drop column supported? + * Peter 10/10/2000 This was set to true, but 7.1devel doesn't support it! + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException + { + return false; + } + + /** + * Is column aliasing supported? + * + *

If so, the SQL AS clause can be used to provide names for + * computed columns or to provide alias names for columns as + * required. A JDBC Compliant driver always returns true. + * + *

e.g. + * + *

+	 * select count(C) as C_COUNT from T group by C;
+	 *
+	 * 

+ * should return a column named as C_COUNT instead of count(C) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsColumnAliasing() throws SQLException + { + return true; + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A + * JDBC Compliant driver always returns true + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullPlusNonNullIsNull() throws SQLException + { + return true; + } + + public boolean supportsConvert() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Are table correlation names supported? A JDBC Compliant + * driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsTableCorrelationNames() throws SQLException + { + return true; + } + + /** + * If table correlation names are supported, are they restricted to + * be different from the names of the tables? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsDifferentTableCorrelationNames() throws SQLException + { + return false; + } + + /** + * Are expressions in "ORDER BY" lists supported? + * + *
e.g. select * from t order by a + b; + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExpressionsInOrderBy() throws SQLException + { + return true; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOrderByUnrelated() throws SQLException + { + return connection.haveMinimumServerVersion("6.4"); + } + + /** + * Is some form of "GROUP BY" clause supported? + * I checked it, and yes it is. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupBy() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByUnrelated() throws SQLException + { + return connection.haveMinimumServerVersion("6.4"); + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided + * it specifies all the columns in the SELECT? Does anyone actually + * understand what they mean here? + * + * (I think this is a subset of the previous function. -- petere) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByBeyondSelect() throws SQLException + { + return supportsGroupByUnrelated(); + } + + /** + * Is the escape character in "LIKE" clauses supported? A + * JDBC compliant driver always returns true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLikeEscapeClause() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Are multiple ResultSets from a single execute supported? + * Well, I implemented it, but I dont think this is possible from + * the back ends point of view. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleResultSets() throws SQLException + { + return false; + } + + /** + * Can we have multiple transactions open at once (on different + * connections?) + * I guess we can have, since Im relying on it. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleTransactions() throws SQLException + { + return true; + } + + /** + * Can columns be defined as non-nullable. A JDBC Compliant driver + * always returns true. + * + *

This changed from false to true in v6.2 of the driver, as this + * support was added to the backend. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsNonNullableColumns() throws SQLException + { + return true; + } + + /** + * Does this driver support the minimum ODBC SQL grammar. This + * grammar is defined at: + * + *

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + * + *

In Appendix C. From this description, we seem to support the + * ODBC minimal (Level 0) grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMinimumSQLGrammar() throws SQLException + { + return true; + } + + /** + * Does this driver support the Core ODBC SQL grammar. We need + * SQL-92 conformance for this. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCoreSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the Extended (Level 2) ODBC SQL + * grammar. We don't conform to the Core (Level 1), so we can't + * conform to the Extended SQL Grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExtendedSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 entry level SQL grammar? + * All JDBC Compliant drivers must return true. We currently + * report false until 'schema' support is added. Then this + * should be changed to return true, since we will be mostly + * compliant (probably more compliant than many other databases) + * And since this is a requirement for all JDBC drivers we + * need to get to the point where we can return true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 intermediate level SQL + * grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 full SQL grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92FullSQL() throws SQLException + { + return false; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * I haven't seen this mentioned anywhere, so I guess not + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException + { + return false; + } + + /** + * Is some form of outer join supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOuterJoins() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Are full nexted outer joins supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsFullOuterJoins() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Is there limited support for outer joins? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLimitedOuterJoins() throws SQLException + { + return supportsFullOuterJoins(); + } + + /** + * What is the database vendor's preferred term for "schema"? + * PostgreSQL doesn't have schemas, but when it does, we'll use the + * term "schema". + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getSchemaTerm() throws SQLException + { + return "schema"; + } + + /** + * What is the database vendor's preferred term for "procedure"? + * Traditionally, "function" has been used. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getProcedureTerm() throws SQLException + { + return "function"; + } + + /** + * What is the database vendor's preferred term for "catalog"? + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getCatalogTerm() throws SQLException + { + return "database"; + } + + /** + * Does a catalog appear at the start of a qualified table name? + * (Otherwise it appears at the end). + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCatalogAtStart() throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * What is the Catalog separator. + * + * @return the catalog separator string + * @exception SQLException if a database access error occurs + */ + public String getCatalogSeparator() throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Can a schema name be used in a data manipulation statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a schema be used in a table definition statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in an index definition? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * We support cursors for gets only it seems. I dont see a method + * to get a positioned delete. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedDelete() throws SQLException + { + return false; // For now... + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedUpdate() throws SQLException + { + return false; // For now... + } + + /** + * Is SELECT for UPDATE supported? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSelectForUpdate() throws SQLException + { + return connection.haveMinimumServerVersion("6.5"); + } + + /** + * Are stored procedure calls using the stored procedure escape + * syntax supported? + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsStoredProcedures() throws SQLException + { + return false; + } + + /** + * Are subqueries in comparison expressions supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInComparisons() throws SQLException + { + return true; + } + + /** + * Are subqueries in 'exists' expressions supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInExists() throws SQLException + { + return true; + } + + /** + * Are subqueries in 'in' statements supported? A JDBC + * Compliant driver always returns true. + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInIns() throws SQLException + { + return true; + } + + /** + * Are subqueries in quantified expressions supported? A JDBC + * Compliant driver always returns true. + * + * (No idea what this is, but we support a good deal of + * subquerying.) + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsSubqueriesInQuantifieds() throws SQLException + { + return true; + } + + /** + * Are correlated subqueries supported? A JDBC Compliant driver + * always returns true. + * + * (a.k.a. subselect in from?) + * + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsCorrelatedSubqueries() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * Is SQL UNION supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnion() throws SQLException + { + return true; // since 6.3 + } + + /** + * Is SQL UNION ALL supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnionAll() throws SQLException + { + return connection.haveMinimumServerVersion("7.1"); + } + + /** + * In PostgreSQL, Cursors are only open within transactions. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException + { + return false; + } + + /** + * Do we support open cursors across multiple transactions? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException + { + return false; + } + + /** + * Can statements remain open across commits? They may, but + * this driver cannot guarentee that. In further reflection. + * we are talking a Statement object here, so the answer is + * yes, since the Statement is only a vehicle to ExecSQL() + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException + { + return true; + } + + /** + * Can statements remain open across rollbacks? They may, but + * this driver cannot guarentee that. In further contemplation, + * we are talking a Statement object here, so the answer is yes, + * since the Statement is only a vehicle to ExecSQL() in Connection + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException + { + return true; + } + + /** + * How many hex characters can you have in an inline binary literal + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxBinaryLiteralLength() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum length for a character literal + * I suppose it is 8190 (8192 - 2 for the quotes) + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxCharLiteralLength() throws SQLException + { + return 0; // no limit + } + + /** + * Whats the limit on column name length. The description of + * pg_class would say '32' (length of pg_class.relname) - we + * should probably do a query for this....but.... + * + * @return the maximum column name length + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of columns in a "GROUP BY" clause? + * + * @return the max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInGroupBy() throws SQLException + { + return 0; // no limit + } + + /** + * What's the maximum number of columns allowed in an index? + * 6.0 only allowed one column, but 6.1 introduced multi-column + * indices, so, theoretically, its all of them. + * + * @return max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInIndex() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns in an "ORDER BY clause? + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInOrderBy() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum number of columns in a "SELECT" list? + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInSelect() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum number of columns in a table? From the + * CREATE TABLE reference page... + * + *

"The new class is created as a heap with no initial data. A + * class can have no more than 1600 attributes (realistically, + * this is limited by the fact that tuple sizes must be less than + * 8192 bytes)..." + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInTable() throws SQLException + { + return 1600; + } + + /** + * How many active connection can we have at a time to this + * database? Well, since it depends on postmaster, which just + * does a listen() followed by an accept() and fork(), its + * basically very high. Unless the system runs out of processes, + * it can be 65535 (the number of aux. ports on a TCP/IP system). + * I will return 8192 since that is what even the largest system + * can realistically handle, + * + * @return the maximum number of connections + * @exception SQLException if a database access error occurs + */ + public int getMaxConnections() throws SQLException + { + return 8192; + } + + /** + * What is the maximum cursor name length (the same as all + * the other F***** identifiers!) + * + * @return max cursor name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxCursorNameLength() throws SQLException + { + return 32; + } + + /** + * Retrieves the maximum number of bytes for an index, including all + * of the parts of the index. + * + * @return max index length in bytes, which includes the composite + * of all the constituent parts of the index; a result of zero means + * that there is no limit or the limit is not known + * @exception SQLException if a database access error occurs + */ + public int getMaxIndexLength() throws SQLException + { + return 0; // no limit (larger than an int anyway) + } + + public int getMaxSchemaNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a procedure name? + * (length of pg_proc.proname used) - again, I really + * should do a query here to get it. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxProcedureNameLength() throws SQLException + { + return 32; + } + + public int getMaxCatalogNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a single row? + * + * @return max row size in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxRowSize() throws SQLException + { + if (connection.haveMinimumServerVersion("7.1")) + return 1073741824; // 1 GB + else + return 8192; // XXX could be altered + } + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY + * blobs? We don't handle blobs yet + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException + { + return false; + } + + /** + * What is the maximum length of a SQL statement? + * + * @return max length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxStatementLength() throws SQLException + { + if (connection.haveMinimumServerVersion("7.0")) + return 0; // actually whatever fits in size_t + else + return 16384; + } + + /** + * How many active statements can we have open at one time to + * this database? Basically, since each Statement downloads + * the results as the query is executed, we can have many. However, + * we can only really have one statement per connection going + * at once (since they are executed serially) - so we return + * one. + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxStatements() throws SQLException + { + return 1; + } + + /** + * What is the maximum length of a table name? This was found + * from pg_class.relname length + * + * @return max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxTableNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of tables that can be specified + * in a SELECT? + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxTablesInSelect() throws SQLException + { + return 0; // no limit + } + + /** + * What is the maximum length of a user name? Well, we generally + * use UNIX like user names in PostgreSQL, so I think this would + * be 8. However, showing the schema for pg_user shows a length + * for username of 32. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxUserNameLength() throws SQLException + { + return 32; + } + + + /** + * What is the database's default transaction isolation level? We + * do not support this, so all transactions are SERIALIZABLE. + * + * @return the default isolation level + * @exception SQLException if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException + { + return Connection.TRANSACTION_READ_COMMITTED; + } + + /** + * Are transactions supported? If not, commit and rollback are noops + * and the isolation level is TRANSACTION_NONE. We do support + * transactions. + * + * @return true if transactions are supported + * @exception SQLException if a database access error occurs + */ + public boolean supportsTransactions() throws SQLException + { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * We only support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED + * + * @param level the values are defined in java.sql.Connection + * @return true if so + * @exception SQLException if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException + { + if (level == Connection.TRANSACTION_SERIALIZABLE || + level == Connection.TRANSACTION_READ_COMMITTED) + return true; + else + return false; + } + + /** + * Are both data definition and data manipulation transactions + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException + { + return true; + } + + /** + * Are only data manipulation statements withing a transaction + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException + { + return false; + } + + /** + * Does a data definition statement within a transaction force + * the transaction to commit? I think this means something like: + * + *

+	 * CREATE TABLE T (A INT);
+	 * INSERT INTO T (A) VALUES (2);
+	 * BEGIN;
+	 * UPDATE T SET A = A + 1;
+	 * CREATE TABLE X (A INT);
+	 * SELECT A FROM T INTO X;
+	 * COMMIT;
+	 * 

+ * + * does the CREATE TABLE call cause a commit? The answer is no. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException + { + return false; + } + + /** + * Is a data definition statement within a transaction ignored? + * It seems to be (from experiment in previous method) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException + { + return true; + } + + /** + * Get a description of stored procedures available in a catalog + * + *

Only procedure descriptions matching the schema and procedure + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME + * + *

Each procedure description has the following columns: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. Field 4 reserved (make it null) + *
  5. Field 5 reserved (make it null) + *
  6. Field 6 reserved (make it null) + *
  7. REMARKS String => explanatory comment on the procedure + *
  8. PROCEDURE_TYPE short => kind of procedure + *
      + *
    • procedureResultUnknown - May return a result + *
    • procedureNoResult - Does not return a result + *
    • procedureReturnsResult - Returns a result + *
    + *
+ * + * @param catalog - a catalog name; "" retrieves those without a + * catalog; null means drop catalog name from criteria + * @param schemaParrern - a schema name pattern; "" retrieves those + * without a schema - we ignore this parameter + * @param procedureNamePattern - a procedure name pattern + * @return ResultSet - each row is a procedure description + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + Field f[] = new Field[8]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = f[4] = f[5] = new Field(connection, "reserved", iVarcharOid, 32); // null; // reserved, must be null for now + f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); + f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); + + // If the pattern is null, then set it to the default + if (procedureNamePattern == null) + procedureNamePattern = "%"; + + r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '" + procedureNamePattern.toLowerCase() + "' order by proname"); + + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Procedure name + tuple[3] = tuple[4] = tuple[5] = null; // Reserved + tuple[6] = null; + + if (r.getBoolean(2)) + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); + else + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); + + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a catalog's stored procedure parameters + * and result columns. + * + *

Only descriptions matching the schema, procedure and parameter + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME. Within this, the return value, if any, is + * first. Next are the parameter descriptions in call order. The + * column descriptions follow in column number order. + * + *

Each row in the ResultSet is a parameter description or column + * description with the following fields: + *

    + *
  1. PROCEDURE_CAT String => procedure catalog (may be null) + *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
  3. PROCEDURE_NAME String => procedure name + *
  4. COLUMN_NAME String => column/parameter name + *
  5. COLUMN_TYPE Short => kind of column/parameter: + *
    • procedureColumnUnknown - nobody knows + *
    • procedureColumnIn - IN parameter + *
    • procedureColumnInOut - INOUT parameter + *
    • procedureColumnOut - OUT parameter + *
    • procedureColumnReturn - procedure return value + *
    • procedureColumnResult - result column in ResultSet + *
    + *
  6. DATA_TYPE short => SQL type from java.sql.Types + *
  7. TYPE_NAME String => SQL type name + *
  8. PRECISION int => precision + *
  9. LENGTH int => length in bytes of data + *
  10. SCALE short => scale + *
  11. RADIX short => radix + *
  12. NULLABLE short => can it contain NULL? + *
    • procedureNoNulls - does not allow NULL values + *
    • procedureNullable - allows NULL values + *
    • procedureNullableUnknown - nullability unknown + *
    • REMARKS String => comment describing parameter/column + *
+ * @param catalog This is ignored in org.postgresql, advise this is set to null + * @param schemaPattern This is ignored in org.postgresql, advise this is set to null + * @param procedureNamePattern a procedure name pattern + * @param columnNamePattern a column name pattern + * @return each row is a stored procedure parameter or column description + * @exception SQLException if a database-access error occurs + * @see #getSearchStringEscape + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException + { + if (procedureNamePattern == null) + procedureNamePattern = "%"; + + if (columnNamePattern == null) + columnNamePattern = "%"; + + // for now, this returns an empty result set. + Field f[] = new Field[13]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "COLUMN_TYPE", iInt2Oid, 2); + f[5] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[6] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[7] = new Field(connection, "PRECISION", iInt4Oid, 4); + f[8] = new Field(connection, "LENGTH", iInt4Oid, 4); + f[9] = new Field(connection, "SCALE", iInt2Oid, 2); + f[10] = new Field(connection, "RADIX", iInt2Oid, 2); + f[11] = new Field(connection, "NULLABLE", iInt2Oid, 2); + f[12] = new Field(connection, "REMARKS", iVarcharOid, 32); + + // add query loop here + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of tables available in a catalog. + * + *

Only table descriptions matching the catalog, schema, table + * name and type criteria are returned. They are ordered by + * TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. + * + *

Each table description has the following columns: + * + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL + * TEMPORARY", "ALIAS", "SYNONYM". + *
  5. REMARKS String => explanatory comment on the table + *
+ * + *

The valid values for the types parameter are: + * "TABLE", "INDEX", "SEQUENCE", "SYSTEM TABLE" and "SYSTEM INDEX" + * + * @param catalog a catalog name; For org.postgresql, this is ignored, and + * should be set to null + * @param schemaPattern a schema name pattern; For org.postgresql, this is ignored, and + * should be set to null + * @param tableNamePattern a table name pattern. For all tables this should be "%" + * @param types a list of table types to include; null returns + * all types + * @return each row is a table description + * @exception SQLException if a database-access error occurs. + */ + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException + { + // Handle default value for types + if (types == null) + types = defaultTableTypes; + + if (tableNamePattern == null) + tableNamePattern = "%"; + + // the field descriptors for the new ResultSet + Field f[] = new Field[5]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "TABLE_TYPE", iVarcharOid, 32); + f[4] = new Field(connection, "REMARKS", iVarcharOid, 32); + + // Now form the query + StringBuffer sql = new StringBuffer("select relname,oid,relkind from pg_class where ("); + + boolean notFirst = false; + for (int i = 0;i < types.length;i++) + { + for (int j = 0;j < getTableTypes.length;j++) + if (getTableTypes[j][0].equals(types[i])) + { + if (notFirst) + sql.append(" or "); + sql.append(getTableTypes[j][1]); + notFirst = true; + } + } + + // Added by Stefan Andreasen + // Now take the pattern into account + sql.append(") and relname like '"); + sql.append(tableNamePattern.toLowerCase()); + sql.append("' order by relkind, relname"); + + // Now run the query + r = connection.ExecSQL(sql.toString()); + + while (r.next()) + { + byte[][] tuple = new byte[5][0]; + + // Fetch the description for the table (if any) + String getDescriptionStatement = + connection.haveMinimumServerVersion("7.2") ? + "select obj_description(" + r.getInt(2) + ",'pg_class')" : + "select description from pg_description where objoid=" + r.getInt(2); + + java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); + + byte remarks[] = null; + + if (((org.postgresql.ResultSet)dr).getTupleCount() == 1) + { + dr.next(); + remarks = dr.getBytes(1); + } + dr.close(); + + String relKind; + switch (r.getBytes(3)[0]) + { + case (byte) 'r': + relKind = "TABLE"; + break; + case (byte) 'i': + relKind = "INDEX"; + break; + case (byte) 'S': + relKind = "SEQUENCE"; + break; + case (byte) 'v': + relKind = "VIEW"; + break; + default: + relKind = null; + } + + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Table name + tuple[3] = (relKind == null) ? null : relKind.getBytes(); // Table type + tuple[4] = remarks; // Remarks + v.addElement(tuple); + } + r.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + // This array contains the valid values for the types argument + // in getTables(). + // + // Each supported type consists of it's name, and the sql where + // clause to retrieve that value. + // + // IMPORTANT: the query must be enclosed in ( ) + private static final String getTableTypes[][] = { + {"TABLE", "(relkind='r' and relhasrules='f' and relname !~ '^pg_' and relname !~ '^xinv')"}, + {"VIEW", "(relkind='v' and relname !~ '^pg_')"}, + {"INDEX", "(relkind='i' and relname !~ '^pg_')"}, + {"SEQUENCE", "(relkind='S' and relname !~ '^pg_')"}, + {"SYSTEM TABLE", "(relkind='r' and relname ~ '^pg_')"}, + {"SYSTEM INDEX", "(relkind='i' and relname ~ '^pg_')"} + }; + + // These are the default tables, used when NULL is passed to getTables + // The choice of these provide the same behaviour as psql's \d + private static final String defaultTableTypes[] = { + "TABLE", "VIEW", "INDEX", "SEQUENCE" + }; + + /** + * Get the schema names available in this database. The results + * are ordered by schema name. + * + *

The schema column is: + *

    + *
  1. TABLE_SCHEM String => schema name + *
+ * + * @return ResultSet each row has a single String column that is a + * schema name + */ + public java.sql.ResultSet getSchemas() throws SQLException + { + // We don't use schemas, so we simply return a single schema name "". + // + Field f[] = new Field[1]; + Vector v = new Vector(); + byte[][] tuple = new byte[1][0]; + f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + tuple[0] = "".getBytes(); + v.addElement(tuple); + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get the catalog names available in this database. The results + * are ordered by catalog name. + * + *

The catalog column is: + *

    + *
  1. TABLE_CAT String => catalog name + *
+ * + * @return ResultSet each row has a single String column that is a + * catalog name + */ + public java.sql.ResultSet getCatalogs() throws SQLException + { + return connection.createStatement().executeQuery("select datname as TABLE_CAT from pg_database;"); + } + + /** + * Get the table types available in this database. The results + * are ordered by table type. + * + *

The table type is: + *

    + *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
+ * + * @return ResultSet each row has a single String column that is a + * table type + */ + public java.sql.ResultSet getTableTypes() throws SQLException + { + Field f[] = new Field[1]; + Vector v = new Vector(); + f[0] = new Field(connection, new String("TABLE_TYPE"), iVarcharOid, 32); + for (int i = 0;i < getTableTypes.length;i++) + { + byte[][] tuple = new byte[2][0]; + tuple[0] = getTableTypes[i][0].getBytes(); + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of table columns available in a catalog. + * + *

Only column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. DATA_TYPE short => SQL type from java.sql.Types + *
  6. TYPE_NAME String => Data source dependent type name + *
  7. COLUMN_SIZE int => column size. For char or date + * types this is the maximum number of characters, for numeric or + * decimal types this is precision. + *
  8. BUFFER_LENGTH is not used. + *
  9. DECIMAL_DIGITS int => the number of fractional digits + *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
  11. NULLABLE int => is NULL allowed? + *
      + *
    • columnNoNulls - might not allow NULL values + *
    • columnNullable - definitely allows NULL values + *
    • columnNullableUnknown - nullability unknown + *
    + *
  12. REMARKS String => comment describing column (may be null) + *
  13. COLUMN_DEF String => default value (may be null) + *
  14. SQL_DATA_TYPE int => unused + *
  15. SQL_DATETIME_SUB int => unused + *
  16. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
  17. ORDINAL_POSITION int => index of column in table + * (starting at 1) + *
  18. IS_NULLABLE String => "NO" means column definitely + * does not allow NULL values; "YES" means the column might + * allow NULL values. An empty string means nobody knows. + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException + { + Vector v = new Vector(); // The new ResultSet tuple stuff + Field f[] = new Field[18]; // The field descriptors for the new ResultSet + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); + f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); + f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); + f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); + f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); + f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); + f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); + f[13] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); + f[14] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); + f[15] = new Field(connection, "CHAR_OCTET_LENGTH", iVarcharOid, 32); + f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid, 4); + f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); + + StringBuffer sql = new StringBuffer(512); + + /* Build a >= 7.1 SQL statement to list all columns */ + sql.append("select " + + (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") + + " c.relname, " + + " a.attname, " + + " a.atttypid, " + + " a.attnum, " + + " a.attnotnull, " + + " a.attlen, " + + " a.atttypmod, " + + " d.adsrc, " + + " t.typname, " + + /* Use the new col_description in 7.2 or an additional outer join in 7.1 */ + (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") + + "from" + + " (" + + " (pg_class c inner join pg_attribute a on" + + " (" + + " a.attrelid=c.oid"); + + if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) + { + sql.append(" and c.relname like \'" + tableNamePattern + "\'"); + } + + if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) + { + sql.append(" and a.attname like \'" + columnNamePattern + "\'"); + } + sql.append( - " left outer join pg_description e on" + + " and a.attnum > 0" + + " )" + + " ) inner join pg_type t on" + + " (" + + " t.oid = a.atttypid" + + " )" + + " )" + + " left outer join pg_attrdef d on" + " (" + - " e.objoid = a.oid" + + " c.oid = d.adrelid" + + " and a.attnum = d.adnum" + " ) "); + + if (!connection.haveMinimumServerVersion("7.2")) + { + /* Only for 7.1 */ + sql.append( + " left outer join pg_description e on" + + " (" + + " e.objoid = a.oid" + + " ) "); + } + + sql.append("order by" + + " c.relname, a.attnum"); + + java.sql.ResultSet r = connection.ExecSQL(sql.toString()); + while (r.next()) + { + byte[][] tuple = new byte[18][0]; + + String nullFlag = r.getString(6); + String typname = r.getString(10); + + tuple[0] = null; // Catalog name, not supported + tuple[1] = null; // Schema name, not supported + tuple[2] = r.getBytes(2); // Table name + tuple[3] = r.getBytes(3); // Column name + tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type + tuple[5] = typname.getBytes(); // Type name + + // Column size + // Looking at the psql source, + // I think the length of a varchar as specified when the table was created + // should be extracted from atttypmod which contains this length + sizeof(int32) + if (typname.equals("bpchar") || typname.equals("varchar")) + { + int atttypmod = r.getInt(8); + tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); + } + else + { + tuple[6] = r.getBytes(7); + } + + tuple[7] = null; // Buffer length + // Decimal digits = scale + // From the source (see e.g. backend/utils/adt/numeric.c, + // function numeric()) the scale and precision can be calculated + // from the typmod value. mark@plasticsoftware.com.au + if (typname.equals("numeric") || typname.equals("decimal")) + { + int attypmod = r.getInt(8); + tuple[8] = + Integer.toString((attypmod & 0xffff) - VARHDRSZ).getBytes(); + } + else + tuple[8] = "0".getBytes(); + tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal + tuple[10] = Integer.toString(nullFlag.equals("f") ? + java.sql.DatabaseMetaData.columnNullable : + java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable + tuple[11] = r.getBytes(11); // Description (if any) + tuple[12] = r.getBytes(9); // Column default + tuple[13] = null; // sql data type (unused) + tuple[14] = null; // sql datetime sub (unused) + tuple[15] = tuple[6]; // char octet length + tuple[16] = r.getBytes(5); // ordinal position + tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable + + v.addElement(tuple); + } + r.close(); + + return new ResultSet(connection, f, v, "OK", 1); } - sql.append("order by" + - " c.relname, a.attnum"); - - java.sql.ResultSet r = connection.ExecSQL(sql.toString()); - while (r.next()) { - byte[][] tuple = new byte[18][0]; - - String nullFlag = r.getString(6); - String typname = r.getString(10); - - tuple[0] = null; // Catalog name, not supported - tuple[1] = null; // Schema name, not supported - tuple[2] = r.getBytes(2); // Table name - tuple[3] = r.getBytes(3); // Column name - tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type - tuple[5] = typname.getBytes(); // Type name - - // Column size - // Looking at the psql source, - // I think the length of a varchar as specified when the table was created - // should be extracted from atttypmod which contains this length + sizeof(int32) - if (typname.equals("bpchar") || typname.equals("varchar")) { - int atttypmod = r.getInt(8); - tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); - } else { - tuple[6] = r.getBytes(7); + /** + * Get a description of the access rights for a table's columns. + * + *

Only privileges matching the column name criteria are + * returned. They are ordered by COLUMN_NAME and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException + { + Field f[] = new Field[8]; + Vector v = new Vector(); + + if (table == null) + table = "%"; + + if (columnNamePattern == null) + columnNamePattern = "%"; + else + columnNamePattern = columnNamePattern.toLowerCase(); + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "GRANTOR", iVarcharOid, 32); + f[5] = new Field(connection, "GRANTEE", iVarcharOid, 32); + f[6] = new Field(connection, "PRIVILEGE", iVarcharOid, 32); + f[7] = new Field(connection, "IS_GRANTABLE", iVarcharOid, 32); + + // This is taken direct from the psql source + java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '" + table.toLowerCase() + "' ORDER BY relname"); + while (r.next()) + { + byte[][] tuple = new byte[8][0]; + tuple[0] = tuple[1] = "".getBytes(); + DriverManager.println("relname=\"" + r.getString(1) + "\" relacl=\"" + r.getString(2) + "\""); + + // For now, don't add to the result as relacl needs to be processed. + //v.addElement(tuple); } - tuple[7] = null; // Buffer length - // Decimal digits = scale - // From the source (see e.g. backend/utils/adt/numeric.c, - // function numeric()) the scale and precision can be calculated - // from the typmod value. mark@plasticsoftware.com.au - if (typname.equals("numeric") || typname.equals("decimal")) - { - int attypmod = r.getInt(8); - tuple[8] = - Integer.toString((attypmod & 0xffff) - VARHDRSZ).getBytes(); - } - else - tuple[8] = "0".getBytes(); - tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal - tuple[10] = Integer.toString(nullFlag.equals("f") ? - java.sql.DatabaseMetaData.columnNullable : - java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable - tuple[11] = r.getBytes(11); // Description (if any) - tuple[12] = r.getBytes(9); // Column default - tuple[13] = null; // sql data type (unused) - tuple[14] = null; // sql datetime sub (unused) - tuple[15] = tuple[6]; // char octet length - tuple[16] = r.getBytes(5); // ordinal position - tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable + return new ResultSet(connection, f, v, "OK", 1); + } - v.addElement(tuple); + /** + * Get a description of the access rights for each table available + * in a catalog. + * + * This method is currently unimplemented. + * + *

Only privileges matching the schema and table name + * criteria are returned. They are ordered by TABLE_SCHEM, + * TABLE_NAME, and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @return ResultSet each row is a table privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of a table's optimal set of columns that + * uniquely identifies a row. They are ordered by SCOPE. + * + * This method is currently not implemented. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => actual scope of result + *
      + *
    • bestRowTemporary - very temporary, while using row + *
    • bestRowTransaction - valid for remainder of current transaction + *
    • bestRowSession - valid for remainder of current session + *
    + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => not used + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • bestRowUnknown - may or may not be pseudo column + *
    • bestRowNotPseudo - is NOT a pseudo column + *
    • bestRowPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param scope the scope of interest; use same values as SCOPE + * @param nullable include columns that are nullable? + * @return ResultSet each row is a column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException + { + // for now, this returns an empty result set. + Field f[] = new Field[8]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); + f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); + f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); + f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); + f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); + + return new ResultSet(connection, f, v, "OK", 1); + } + + /** + * Get a description of a table's columns that are automatically + * updated when any value in a row is updated. They are + * unordered. + * + * This method is currently unimplemented. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => is not used + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => length of column value in bytes + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • versionColumnUnknown - may or may not be pseudo column + *
    • versionColumnNotPseudo - is NOT a pseudo column + *
    • versionColumnPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of a table's primary key columns. They + * are ordered by COLUMN_NAME. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. KEY_SEQ short => sequence number within primary key + *
  6. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException + { + return connection.createStatement().executeQuery("SELECT " + + "'' as TABLE_CAT," + + "'' AS TABLE_SCHEM," + + "bc.relname AS TABLE_NAME," + + "a.attname AS COLUMN_NAME," + + "a.attnum as KEY_SEQ," + + "ic.relname as PK_NAME " + + " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a" + + " WHERE bc.relkind = 'r' " + // -- not indices + " and upper(bc.relname) = upper('" + table + "')" + + " and i.indrelid = bc.oid" + + " and i.indexrelid = ic.oid" + + " and ic.oid = a.attrelid" + + " and i.indisprimary='t' " + + " ORDER BY table_name, pk_name, key_seq" + ); + } + + private Vector importLoop(java.sql.ResultSet keyRelation) throws SQLException + { + String s, s2; + String origTable = null, primTable = new String(""), schema; + int i; + Vector v; + + s = keyRelation.getString(1); + s2 = s; + // System.out.println(s); + v = new Vector(); + for (i = 0;;i++) + { + s = s.substring(s.indexOf("\\000") + 4); + if (s.compareTo("") == 0) + { + //System.out.println(); + break; + } + s2 = s.substring(0, s.indexOf("\\000")); + switch (i) + { + case 0: + origTable = s2; + break; + case 1: + primTable = s2; + break; + case 2: + schema = s2; + break; + default: + v.add(s2); + } + } + + java.sql.ResultSet rstmp = connection.ExecSQL("select * from " + origTable + " where 1=0"); + java.sql.ResultSetMetaData origCols = rstmp.getMetaData(); + + String stmp; + Vector tuples = new Vector(); + byte tuple[][]; + + // the foreign keys are only on even positions in the Vector. + for (i = 0;i < v.size();i += 2) + { + stmp = (String)v.elementAt(i); + + for (int j = 1;j <= origCols.getColumnCount();j++) + { + if (stmp.compareTo(origCols.getColumnName(j)) == 0) + { + tuple = new byte[14][0]; + + for (int k = 0;k < 14;k++) + tuple[k] = null; + + //PKTABLE_NAME + tuple[2] = primTable.getBytes(); + //PKTABLE_COLUMN + stmp = (String)v.elementAt(i + 1); + tuple[3] = stmp.getBytes(); + //FKTABLE_NAME + tuple[6] = origTable.getBytes(); + //FKCOLUMN_NAME + tuple[7] = origCols.getColumnName(j).getBytes(); + //KEY_SEQ + tuple[8] = Integer.toString(j).getBytes(); + + tuples.add(tuple); + /* + System.out.println(origCols.getColumnName(j)+ + ": "+j+" -> "+primTable+": "+ + (String)v.elementAt(i+1)); + */ + break; + } + } + } + + return tuples; + } + + /** + * Get a description of the primary key columns that are + * referenced by a table's foreign key columns (the primary keys + * imported by a table). They are ordered by PKTABLE_CAT, + * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + * + *

Each primary key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog + * being imported (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema + * being imported (may be null) + *
  3. PKTABLE_NAME String => primary key table name + * being imported + *
  4. PKCOLUMN_NAME String => primary key column name + * being imported + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + *
  8. FKCOLUMN_NAME String => foreign key column name + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key name (may be null) + *
  13. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException + { + // Added by Ola Sundell + // FIXME: error checking galore! + java.sql.ResultSet rsret; + Field f[] = new Field[14]; + byte tuple[][]; + + f[0] = new Field(connection, "PKTABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PKTABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PKTABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "PKCOLUMN_NAME", iVarcharOid, 32); + f[4] = new Field(connection, "FKTABLE_CAT", iVarcharOid, 32); + f[5] = new Field(connection, "FKTABLE_SCHEM", iVarcharOid, 32); + f[6] = new Field(connection, "FKTABLE_NAME", iVarcharOid, 32); + f[7] = new Field(connection, "FKCOLUMN_NAME", iVarcharOid, 32); + f[8] = new Field(connection, "KEY_SEQ", iInt2Oid, 2); + f[9] = new Field(connection, "UPDATE_RULE", iInt2Oid, 2); + f[10] = new Field(connection, "DELETE_RULE", iInt2Oid, 2); + f[11] = new Field(connection, "FK_NAME", iVarcharOid, 32); + f[12] = new Field(connection, "PK_NAME", iVarcharOid, 32); + f[13] = new Field(connection, "DEFERRABILITY", iInt2Oid, 2); + + java.sql.ResultSet rs = connection.ExecSQL("select t.tgargs " + + "from pg_class as c, pg_trigger as t " + + "where c.relname like '" + table + "' and c.relfilenode=t.tgrelid"); + Vector tuples = new Vector(); + + while (rs.next()) + { + tuples.addAll(importLoop(rs)); + } + + rsret = new ResultSet(connection, f, tuples, "OK", 1); + + return rsret; + } + + /** + * Get a description of a foreign key columns that reference a + * table's primary key columns (the foreign keys exported by a + * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + * + * This method is currently unimplemented. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of the foreign key columns in the foreign key + * table that reference the primary key columns of the primary key + * table (describe how one table imports another's key.) This + * should normally return a single foreign key/primary key pair + * (most tables only import a foreign key from a table once.) They + * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and + * KEY_SEQ. + * + * This method is currently unimplemented. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * Get a description of all the standard SQL types supported by + * this database. They are ordered by DATA_TYPE and then by how + * closely the data type maps to the corresponding JDBC SQL type. + * + *

Each type description has the following columns: + *

    + *
  1. TYPE_NAME String => Type name + *
  2. DATA_TYPE short => SQL data type from java.sql.Types + *
  3. PRECISION int => maximum precision + *
  4. LITERAL_PREFIX String => prefix used to quote a literal + * (may be null) + *
  5. LITERAL_SUFFIX String => suffix used to quote a literal + (may be null) + *
  6. CREATE_PARAMS String => parameters used in creating + * the type (may be null) + *
  7. NULLABLE short => can you use NULL for this type? + *
      + *
    • typeNoNulls - does not allow NULL values + *
    • typeNullable - allows NULL values + *
    • typeNullableUnknown - nullability unknown + *
    + *
  8. CASE_SENSITIVE boolean=> is it case sensitive? + *
  9. SEARCHABLE short => can you use "WHERE" based on this type: + *
      + *
    • typePredNone - No support + *
    • typePredChar - Only supported with WHERE .. LIKE + *
    • typePredBasic - Supported except for WHERE .. LIKE + *
    • typeSearchable - Supported for all WHERE .. + *
    + *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? + *
  11. FIXED_PREC_SCALE boolean => can it be a money value? + *
  12. AUTO_INCREMENT boolean => can it be used for an + * auto-increment value? + *
  13. LOCAL_TYPE_NAME String => localized version of type name + * (may be null) + *
  14. MINIMUM_SCALE short => minimum scale supported + *
  15. MAXIMUM_SCALE short => maximum scale supported + *
  16. SQL_DATA_TYPE int => unused + *
  17. SQL_DATETIME_SUB int => unused + *
  18. NUM_PREC_RADIX int => usually 2 or 10 + *
+ * + * @return ResultSet each row is a SQL type description + */ + public java.sql.ResultSet getTypeInfo() throws SQLException + { + java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); + if (rs != null) + { + Field f[] = new Field[18]; + ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); + f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); + f[2] = new Field(connection, "PRECISION", iInt4Oid, 4); + f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, 32); + f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, 32); + f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, 32); + f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2); + f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1); + f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2); + f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1); + f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1); + f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1); + f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, 32); + f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2); + f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2); + f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); + f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); + f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); + + // cache some results, this will keep memory useage down, and speed + // things up a little. + byte b9[] = "9".getBytes(); + byte b10[] = "10".getBytes(); + byte bf[] = "f".getBytes(); + byte bnn[] = Integer.toString(typeNoNulls).getBytes(); + byte bts[] = Integer.toString(typeSearchable).getBytes(); + + while (rs.next()) + { + byte[][] tuple = new byte[18][]; + String typname = rs.getString(1); + tuple[0] = typname.getBytes(); + tuple[1] = Integer.toString(connection.getSQLType(typname)).getBytes(); + tuple[2] = b9; // for now + tuple[6] = bnn; // for now + tuple[7] = bf; // false for now - not case sensitive + tuple[8] = bts; + tuple[9] = bf; // false for now - it's signed + tuple[10] = bf; // false for now - must handle money + tuple[11] = bf; // false for now - handle autoincrement + // 12 - LOCAL_TYPE_NAME is null + // 13 & 14 ? + // 15 & 16 are unused so we return null + tuple[17] = b10; // everything is base 10 + v.addElement(tuple); + } + rs.close(); + return new ResultSet(connection, f, v, "OK", 1); + } + + throw new PSQLException("postgresql.metadata.unavailable"); + } + + /** + * Get a description of a table's indices and statistics. They are + * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + * + *

Each index column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. NON_UNIQUE boolean => Can index values be non-unique? + * false when TYPE is tableIndexStatistic + *
  5. INDEX_QUALIFIER String => index catalog (may be null); + * null when TYPE is tableIndexStatistic + *
  6. INDEX_NAME String => index name; null when TYPE is + * tableIndexStatistic + *
  7. TYPE short => index type: + *
      + *
    • tableIndexStatistic - this identifies table statistics that are + * returned in conjuction with a table's index descriptions + *
    • tableIndexClustered - this is a clustered index + *
    • tableIndexHashed - this is a hashed index + *
    • tableIndexOther - this is some other style of index + *
    + *
  8. ORDINAL_POSITION short => column sequence number + * within index; zero when TYPE is tableIndexStatistic + *
  9. COLUMN_NAME String => column name; null when TYPE is + * tableIndexStatistic + *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending + * "D" => descending, may be null if sort sequence is not supported; + * null when TYPE is tableIndexStatistic + *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then + * this is the number of rows in the table; otherwise it is the + * number of unique values in the index. + *
  12. PAGES int => When TYPE is tableIndexStatisic then + * this is the number of pages used for the table, otherwise it + * is the number of pages used for the current index. + *
  13. FILTER_CONDITION String => Filter condition, if any. + * (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those without a schema + * @param table a table name + * @param unique when true, return only indices for unique values; + * when false, return indices regardless of whether unique or not + * @param approximate when true, result is allowed to reflect approximate + * or out of data values; when false, results are requested to be + * accurate + * @return ResultSet each row is an index column description + */ + // Implementation note: This is required for Borland's JBuilder to work + public java.sql.ResultSet getIndexInfo(String catalog, String schema, String tableName, boolean unique, boolean approximate) throws SQLException + { + Field f[] = new Field[13]; + java.sql.ResultSet r; // ResultSet for the SQL query that we need to do + Vector v = new Vector(); // The new ResultSet tuple stuff + + f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); + f[3] = new Field(connection, "NON_UNIQUE", iBoolOid, 1); + f[4] = new Field(connection, "INDEX_QUALIFIER", iVarcharOid, 32); + f[5] = new Field(connection, "INDEX_NAME", iVarcharOid, 32); + f[6] = new Field(connection, "TYPE", iInt2Oid, 2); + f[7] = new Field(connection, "ORDINAL_POSITION", iInt2Oid, 2); + f[8] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); + f[9] = new Field(connection, "ASC_OR_DESC", iVarcharOid, 32); + f[10] = new Field(connection, "CARDINALITY", iInt4Oid, 4); + f[11] = new Field(connection, "PAGES", iInt4Oid, 4); + f[12] = new Field(connection, "FILTER_CONDITION", iVarcharOid, 32); + + r = connection.ExecSQL("select " + + "c.relname, " + + "x.indisunique, " + + "i.relname, " + + "x.indisclustered, " + + "a.amname, " + + "x.indkey, " + + "c.reltuples, " + + "c.relpages, " + + "x.indexrelid " + + "FROM pg_index x, pg_class c, pg_class i, pg_am a " + + "WHERE ((c.relname = '" + tableName.toLowerCase() + "') " + + " AND (c.oid = x.indrelid) " + + " AND (i.oid = x.indexrelid) " + + " AND (i.relam = a.oid)) " + + "ORDER BY x.indisunique DESC, " + + " x.indisclustered, a.amname, i.relname"); + while (r.next()) + { + // indkey is an array of column ordinals (integers). In the JDBC + // interface, this has to be separated out into a separate + // tuple for each indexed column. Also, getArray() is not yet + // implemented for Postgres JDBC, so we parse by hand. + String columnOrdinalString = r.getString(6); + StringTokenizer stok = new StringTokenizer(columnOrdinalString); + int [] columnOrdinals = new int[stok.countTokens()]; + int o = 0; + while (stok.hasMoreTokens()) + { + columnOrdinals[o++] = Integer.parseInt(stok.nextToken()); + } + for (int i = 0; i < columnOrdinals.length; i++) + { + byte [] [] tuple = new byte [13] []; + tuple[0] = "".getBytes(); + tuple[1] = "".getBytes(); + tuple[2] = r.getBytes(1); + tuple[3] = r.getBoolean(2) ? "f".getBytes() : "t".getBytes(); + tuple[4] = null; + tuple[5] = r.getBytes(3); + tuple[6] = r.getBoolean(4) ? + Integer.toString(tableIndexClustered).getBytes() : + r.getString(5).equals("hash") ? + Integer.toString(tableIndexHashed).getBytes() : + Integer.toString(tableIndexOther).getBytes(); + tuple[7] = Integer.toString(i + 1).getBytes(); + java.sql.ResultSet columnNameRS = connection.ExecSQL("select a.attname FROM pg_attribute a WHERE (a.attnum = " + columnOrdinals[i] + ") AND (a.attrelid = " + r.getInt(9) + ")"); + columnNameRS.next(); + tuple[8] = columnNameRS.getBytes(1); + tuple[9] = null; // sort sequence ??? + tuple[10] = r.getBytes(7); // inexact + tuple[11] = r.getBytes(8); + tuple[12] = null; + v.addElement(tuple); + } + } + + return new ResultSet(connection, f, v, "OK", 1); + } + + + // ** JDBC 2 Extensions ** + + /** + * Does the database support the given result set type? + * + * @param type - defined in java.sql.ResultSet + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsResultSetType(int type) throws SQLException + { + // The only type we don't support + return type != java.sql.ResultSet.TYPE_SCROLL_SENSITIVE; + } + + + /** + * Does the database support the concurrency type in combination + * with the given result set type? + * + * @param type - defined in java.sql.ResultSet + * @param concurrency - type defined in java.sql.ResultSet + * @return true if so; false otherwise + * @exception SQLException - if a database access error occurs + */ + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException + { + // These combinations are not supported! + if (type == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE) + return false; + + // We don't yet support Updateable ResultSets + if (concurrency == java.sql.ResultSet.CONCUR_UPDATABLE) + return false; + + // Everything else we do + return true; + } + + + /* lots of unsupported stuff... */ + public boolean ownUpdatesAreVisible(int type) throws SQLException + { + return false; + } + + public boolean ownDeletesAreVisible(int type) throws SQLException + { + return false; + } + + public boolean ownInsertsAreVisible(int type) throws SQLException + { + return false; + } + + public boolean othersUpdatesAreVisible(int type) throws SQLException + { + return false; + } + + public boolean othersDeletesAreVisible(int i) throws SQLException + { + return false; + } + + public boolean othersInsertsAreVisible(int type) throws SQLException + { + return false; + } + + public boolean updatesAreDetected(int type) throws SQLException + { + return false; + } + + public boolean deletesAreDetected(int i) throws SQLException + { + return false; + } + + public boolean insertsAreDetected(int type) throws SQLException + { + return false; + } + + /** + * Indicates whether the driver supports batch updates. + */ + public boolean supportsBatchUpdates() throws SQLException + { + return true; + } + + /** + * Return user defined types in a schema + */ + public java.sql.ResultSet getUDTs(String catalog, + String schemaPattern, + String typeNamePattern, + int[] types + ) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + + /** + * Retrieves the connection that produced this metadata object. + * + * @return the connection that produced this metadata object + */ + public java.sql.Connection getConnection() throws SQLException + { + return (java.sql.Connection)connection; + } + + /* I don't find these in the spec!?! */ + + public boolean rowChangesAreDetected(int type) throws SQLException + { + return false; + } + + public boolean rowChangesAreVisible(int type) throws SQLException + { + return false; } - r.close(); - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of the access rights for a table's columns. - * - *

Only privileges matching the column name criteria are - * returned. They are ordered by COLUMN_NAME and PRIVILEGE. - * - *

Each privilige description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. GRANTOR => grantor of access (may be null) - *
  6. GRANTEE String => grantee of access - *
  7. PRIVILEGE String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - *
  8. IS_GRANTABLE String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @param columnNamePattern a column name pattern - * @return ResultSet each row is a column privilege description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException - { - Field f[] = new Field[8]; - Vector v = new Vector(); - - if(table==null) - table="%"; - - if(columnNamePattern==null) - columnNamePattern="%"; - else - columnNamePattern=columnNamePattern.toLowerCase(); - - f[0] = new Field(connection,"TABLE_CAT",iVarcharOid,32); - f[1] = new Field(connection,"TABLE_SCHEM",iVarcharOid,32); - f[2] = new Field(connection,"TABLE_NAME",iVarcharOid,32); - f[3] = new Field(connection,"COLUMN_NAME",iVarcharOid,32); - f[4] = new Field(connection,"GRANTOR",iVarcharOid,32); - f[5] = new Field(connection,"GRANTEE",iVarcharOid,32); - f[6] = new Field(connection,"PRIVILEGE",iVarcharOid,32); - f[7] = new Field(connection,"IS_GRANTABLE",iVarcharOid,32); - - // This is taken direct from the psql source - java.sql.ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname"); - while(r.next()) { - byte[][] tuple = new byte[8][0]; - tuple[0] = tuple[1]= "".getBytes(); - DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\""); - - // For now, don't add to the result as relacl needs to be processed. - //v.addElement(tuple); - } - - return new ResultSet(connection,f,v,"OK",1); - } - - /** - * Get a description of the access rights for each table available - * in a catalog. - * - * This method is currently unimplemented. - * - *

Only privileges matching the schema and table name - * criteria are returned. They are ordered by TABLE_SCHEM, - * TABLE_NAME, and PRIVILEGE. - * - *

Each privilige description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. GRANTOR => grantor of access (may be null) - *
  6. GRANTEE String => grantee of access - *
  7. PRIVILEGE String => name of access (SELECT, - * INSERT, UPDATE, REFRENCES, ...) - *
  8. IS_GRANTABLE String => "YES" if grantee is permitted - * to grant to others; "NO" if not; null if unknown - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schemaPattern a schema name pattern; "" retrieves those - * without a schema - * @param tableNamePattern a table name pattern - * @return ResultSet each row is a table privilege description - * @see #getSearchStringEscape - */ - public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of a table's optimal set of columns that - * uniquely identifies a row. They are ordered by SCOPE. - * - * This method is currently not implemented. - * - *

Each column description has the following columns: - *

    - *
  1. SCOPE short => actual scope of result - *
      - *
    • bestRowTemporary - very temporary, while using row - *
    • bestRowTransaction - valid for remainder of current transaction - *
    • bestRowSession - valid for remainder of current session - *
    - *
  2. COLUMN_NAME String => column name - *
  3. DATA_TYPE short => SQL data type from java.sql.Types - *
  4. TYPE_NAME String => Data source dependent type name - *
  5. COLUMN_SIZE int => precision - *
  6. BUFFER_LENGTH int => not used - *
  7. DECIMAL_DIGITS short => scale - *
  8. PSEUDO_COLUMN short => is this a pseudo column - * like an Oracle ROWID - *
      - *
    • bestRowUnknown - may or may not be pseudo column - *
    • bestRowNotPseudo - is NOT a pseudo column - *
    • bestRowPseudo - is a pseudo column - *
    - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @param scope the scope of interest; use same values as SCOPE - * @param nullable include columns that are nullable? - * @return ResultSet each row is a column description - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException - { - // for now, this returns an empty result set. - Field f[] = new Field[8]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "SCOPE", iInt2Oid, 2); - f[1] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[2] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[3] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[4] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); - f[5] = new Field(connection, "BUFFER_LENGTH", iInt4Oid, 4); - f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); - f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - - return new ResultSet(connection, f, v, "OK", 1); - } - - /** - * Get a description of a table's columns that are automatically - * updated when any value in a row is updated. They are - * unordered. - * - * This method is currently unimplemented. - * - *

Each column description has the following columns: - *

    - *
  1. SCOPE short => is not used - *
  2. COLUMN_NAME String => column name - *
  3. DATA_TYPE short => SQL data type from java.sql.Types - *
  4. TYPE_NAME String => Data source dependent type name - *
  5. COLUMN_SIZE int => precision - *
  6. BUFFER_LENGTH int => length of column value in bytes - *
  7. DECIMAL_DIGITS short => scale - *
  8. PSEUDO_COLUMN short => is this a pseudo column - * like an Oracle ROWID - *
      - *
    • versionColumnUnknown - may or may not be pseudo column - *
    • versionColumnNotPseudo - is NOT a pseudo column - *
    • versionColumnPseudo - is a pseudo column - *
    - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name; "" retrieves those without a schema - * @param table a table name - * @return ResultSet each row is a column description - */ - public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of a table's primary key columns. They - * are ordered by COLUMN_NAME. - * - *

Each column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. COLUMN_NAME String => column name - *
  5. KEY_SEQ short => sequence number within primary key - *
  6. PK_NAME String => primary key name (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a primary key column description - */ - public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException - { - return connection.createStatement().executeQuery("SELECT " + - "'' as TABLE_CAT," + - "'' AS TABLE_SCHEM," + - "bc.relname AS TABLE_NAME," + - "a.attname AS COLUMN_NAME," + - "a.attnum as KEY_SEQ,"+ - "ic.relname as PK_NAME " + - " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a" + - " WHERE bc.relkind = 'r' " + // -- not indices - " and upper(bc.relname) = upper('"+table+"')" + - " and i.indrelid = bc.oid" + - " and i.indexrelid = ic.oid" + - " and ic.oid = a.attrelid" + - " and i.indisprimary='t' " + - " ORDER BY table_name, pk_name, key_seq" - ); - } - - private Vector importLoop(java.sql.ResultSet keyRelation) throws SQLException { - String s,s2; - String origTable=null, primTable=new String(""), schema; - int i; - Vector v; - - s=keyRelation.getString(1); - s2=s; - // System.out.println(s); - v=new Vector(); - for (i=0;;i++) { - s=s.substring(s.indexOf("\\000")+4); - if (s.compareTo("")==0) { - //System.out.println(); - break; - } - s2=s.substring(0,s.indexOf("\\000")); - switch (i) { - case 0: - origTable=s2; - break; - case 1: - primTable=s2; - break; - case 2: - schema=s2; - break; - default: - v.add(s2); - } - } - - java.sql.ResultSet rstmp=connection.ExecSQL("select * from "+origTable+" where 1=0"); - java.sql.ResultSetMetaData origCols=rstmp.getMetaData(); - - String stmp; - Vector tuples=new Vector(); - byte tuple[][]; - - // the foreign keys are only on even positions in the Vector. - for (i=0;i "+primTable+": "+ - (String)v.elementAt(i+1)); -*/ - break; - } - } - } - - return tuples; - } - - /** - * Get a description of the primary key columns that are - * referenced by a table's foreign key columns (the primary keys - * imported by a table). They are ordered by PKTABLE_CAT, - * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. - * - *

Each primary key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog - * being imported (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema - * being imported (may be null) - *
  3. PKTABLE_NAME String => primary key table name - * being imported - *
  4. PKCOLUMN_NAME String => primary key column name - * being imported - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - *
  8. FKCOLUMN_NAME String => foreign key column name - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key name (may be null) - *
  13. PK_NAME String => primary key name (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a primary key column description - * @see #getExportedKeys - */ - public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException - { - // Added by Ola Sundell - // FIXME: error checking galore! - java.sql.ResultSet rsret; - Field f[]=new Field[14]; - byte tuple[][]; - - f[0]=new Field(connection, "PKTABLE_CAT", iVarcharOid, 32); - f[1]=new Field(connection, "PKTABLE_SCHEM", iVarcharOid, 32); - f[2]=new Field(connection, "PKTABLE_NAME", iVarcharOid, 32); - f[3]=new Field(connection, "PKCOLUMN_NAME", iVarcharOid, 32); - f[4]=new Field(connection, "FKTABLE_CAT", iVarcharOid, 32); - f[5]=new Field(connection, "FKTABLE_SCHEM", iVarcharOid, 32); - f[6]=new Field(connection, "FKTABLE_NAME", iVarcharOid, 32); - f[7]=new Field(connection, "FKCOLUMN_NAME", iVarcharOid, 32); - f[8]=new Field(connection, "KEY_SEQ", iInt2Oid, 2); - f[9]=new Field(connection, "UPDATE_RULE", iInt2Oid, 2); - f[10]=new Field(connection, "DELETE_RULE", iInt2Oid, 2); - f[11]=new Field(connection, "FK_NAME", iVarcharOid, 32); - f[12]=new Field(connection, "PK_NAME", iVarcharOid, 32); - f[13]=new Field(connection, "DEFERRABILITY", iInt2Oid, 2); - - java.sql.ResultSet rs=connection.ExecSQL("select t.tgargs "+ - "from pg_class as c, pg_trigger as t "+ - "where c.relname like '"+table+"' and c.relfilenode=t.tgrelid"); - Vector tuples=new Vector(); - - while (rs.next()) { - tuples.addAll(importLoop(rs)); - } - - rsret=new ResultSet(connection, f, tuples, "OK", 1); - - return rsret; - } - - /** - * Get a description of a foreign key columns that reference a - * table's primary key columns (the foreign keys exported by a - * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, - * FKTABLE_NAME, and KEY_SEQ. - * - * This method is currently unimplemented. - * - *

Each foreign key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) - *
  3. PKTABLE_NAME String => primary key table name - *
  4. PKCOLUMN_NAME String => primary key column name - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - * being exported (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - * being exported (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - * being exported - *
  8. FKCOLUMN_NAME String => foreign key column name - * being exported - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key identifier (may be null) - *
  13. PK_NAME String => primary key identifier (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a foreign key column description - * @see #getImportedKeys - */ - public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of the foreign key columns in the foreign key - * table that reference the primary key columns of the primary key - * table (describe how one table imports another's key.) This - * should normally return a single foreign key/primary key pair - * (most tables only import a foreign key from a table once.) They - * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and - * KEY_SEQ. - * - * This method is currently unimplemented. - * - *

Each foreign key column description has the following columns: - *

    - *
  1. PKTABLE_CAT String => primary key table catalog (may be null) - *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) - *
  3. PKTABLE_NAME String => primary key table name - *
  4. PKCOLUMN_NAME String => primary key column name - *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) - * being exported (may be null) - *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) - * being exported (may be null) - *
  7. FKTABLE_NAME String => foreign key table name - * being exported - *
  8. FKCOLUMN_NAME String => foreign key column name - * being exported - *
  9. KEY_SEQ short => sequence number within foreign key - *
  10. UPDATE_RULE short => What happens to - * foreign key when primary is updated: - *
      - *
    • importedKeyCascade - change imported key to agree - * with primary key update - *
    • importedKeyRestrict - do not allow update of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been updated - *
    - *
  11. DELETE_RULE short => What happens to - * the foreign key when primary is deleted. - *
      - *
    • importedKeyCascade - delete rows that import a deleted key - *
    • importedKeyRestrict - do not allow delete of primary - * key if it has been imported - *
    • importedKeySetNull - change imported key to NULL if - * its primary key has been deleted - *
    - *
  12. FK_NAME String => foreign key identifier (may be null) - *
  13. PK_NAME String => primary key identifier (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those - * without a schema - * @param table a table name - * @return ResultSet each row is a foreign key column description - * @see #getImportedKeys - */ - public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /** - * Get a description of all the standard SQL types supported by - * this database. They are ordered by DATA_TYPE and then by how - * closely the data type maps to the corresponding JDBC SQL type. - * - *

Each type description has the following columns: - *

    - *
  1. TYPE_NAME String => Type name - *
  2. DATA_TYPE short => SQL data type from java.sql.Types - *
  3. PRECISION int => maximum precision - *
  4. LITERAL_PREFIX String => prefix used to quote a literal - * (may be null) - *
  5. LITERAL_SUFFIX String => suffix used to quote a literal - (may be null) - *
  6. CREATE_PARAMS String => parameters used in creating - * the type (may be null) - *
  7. NULLABLE short => can you use NULL for this type? - *
      - *
    • typeNoNulls - does not allow NULL values - *
    • typeNullable - allows NULL values - *
    • typeNullableUnknown - nullability unknown - *
    - *
  8. CASE_SENSITIVE boolean=> is it case sensitive? - *
  9. SEARCHABLE short => can you use "WHERE" based on this type: - *
      - *
    • typePredNone - No support - *
    • typePredChar - Only supported with WHERE .. LIKE - *
    • typePredBasic - Supported except for WHERE .. LIKE - *
    • typeSearchable - Supported for all WHERE .. - *
    - *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? - *
  11. FIXED_PREC_SCALE boolean => can it be a money value? - *
  12. AUTO_INCREMENT boolean => can it be used for an - * auto-increment value? - *
  13. LOCAL_TYPE_NAME String => localized version of type name - * (may be null) - *
  14. MINIMUM_SCALE short => minimum scale supported - *
  15. MAXIMUM_SCALE short => maximum scale supported - *
  16. SQL_DATA_TYPE int => unused - *
  17. SQL_DATETIME_SUB int => unused - *
  18. NUM_PREC_RADIX int => usually 2 or 10 - *
- * - * @return ResultSet each row is a SQL type description - */ - public java.sql.ResultSet getTypeInfo() throws SQLException - { - java.sql.ResultSet rs = connection.ExecSQL("select typname from pg_type"); - if(rs!=null) { - Field f[] = new Field[18]; - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); - f[1] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); - f[2] = new Field(connection, "PRECISION", iInt4Oid, 4); - f[3] = new Field(connection, "LITERAL_PREFIX", iVarcharOid, 32); - f[4] = new Field(connection, "LITERAL_SUFFIX", iVarcharOid, 32); - f[5] = new Field(connection, "CREATE_PARAMS", iVarcharOid, 32); - f[6] = new Field(connection, "NULLABLE", iInt2Oid, 2); - f[7] = new Field(connection, "CASE_SENSITIVE", iBoolOid, 1); - f[8] = new Field(connection, "SEARCHABLE", iInt2Oid, 2); - f[9] = new Field(connection, "UNSIGNED_ATTRIBUTE", iBoolOid, 1); - f[10] = new Field(connection, "FIXED_PREC_SCALE", iBoolOid, 1); - f[11] = new Field(connection, "AUTO_INCREMENT", iBoolOid, 1); - f[12] = new Field(connection, "LOCAL_TYPE_NAME", iVarcharOid, 32); - f[13] = new Field(connection, "MINIMUM_SCALE", iInt2Oid, 2); - f[14] = new Field(connection, "MAXIMUM_SCALE", iInt2Oid, 2); - f[15] = new Field(connection, "SQL_DATA_TYPE", iInt4Oid, 4); - f[16] = new Field(connection, "SQL_DATETIME_SUB", iInt4Oid, 4); - f[17] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); - - // cache some results, this will keep memory useage down, and speed - // things up a little. - byte b9[] = "9".getBytes(); - byte b10[] = "10".getBytes(); - byte bf[] = "f".getBytes(); - byte bnn[] = Integer.toString(typeNoNulls).getBytes(); - byte bts[] = Integer.toString(typeSearchable).getBytes(); - - while(rs.next()) { - byte[][] tuple = new byte[18][]; - String typname=rs.getString(1); - tuple[0] = typname.getBytes(); - tuple[1] = Integer.toString(connection.getSQLType(typname)).getBytes(); - tuple[2] = b9; // for now - tuple[6] = bnn; // for now - tuple[7] = bf; // false for now - not case sensitive - tuple[8] = bts; - tuple[9] = bf; // false for now - it's signed - tuple[10] = bf; // false for now - must handle money - tuple[11] = bf; // false for now - handle autoincrement - // 12 - LOCAL_TYPE_NAME is null - // 13 & 14 ? - // 15 & 16 are unused so we return null - tuple[17] = b10; // everything is base 10 - v.addElement(tuple); - } - rs.close(); - return new ResultSet(connection, f, v, "OK", 1); - } - - throw new PSQLException("postgresql.metadata.unavailable"); - } - - /** - * Get a description of a table's indices and statistics. They are - * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. - * - *

Each index column description has the following columns: - *

    - *
  1. TABLE_CAT String => table catalog (may be null) - *
  2. TABLE_SCHEM String => table schema (may be null) - *
  3. TABLE_NAME String => table name - *
  4. NON_UNIQUE boolean => Can index values be non-unique? - * false when TYPE is tableIndexStatistic - *
  5. INDEX_QUALIFIER String => index catalog (may be null); - * null when TYPE is tableIndexStatistic - *
  6. INDEX_NAME String => index name; null when TYPE is - * tableIndexStatistic - *
  7. TYPE short => index type: - *
      - *
    • tableIndexStatistic - this identifies table statistics that are - * returned in conjuction with a table's index descriptions - *
    • tableIndexClustered - this is a clustered index - *
    • tableIndexHashed - this is a hashed index - *
    • tableIndexOther - this is some other style of index - *
    - *
  8. ORDINAL_POSITION short => column sequence number - * within index; zero when TYPE is tableIndexStatistic - *
  9. COLUMN_NAME String => column name; null when TYPE is - * tableIndexStatistic - *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending - * "D" => descending, may be null if sort sequence is not supported; - * null when TYPE is tableIndexStatistic - *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then - * this is the number of rows in the table; otherwise it is the - * number of unique values in the index. - *
  12. PAGES int => When TYPE is tableIndexStatisic then - * this is the number of pages used for the table, otherwise it - * is the number of pages used for the current index. - *
  13. FILTER_CONDITION String => Filter condition, if any. - * (may be null) - *
- * - * @param catalog a catalog name; "" retrieves those without a catalog - * @param schema a schema name pattern; "" retrieves those without a schema - * @param table a table name - * @param unique when true, return only indices for unique values; - * when false, return indices regardless of whether unique or not - * @param approximate when true, result is allowed to reflect approximate - * or out of data values; when false, results are requested to be - * accurate - * @return ResultSet each row is an index column description - */ - // Implementation note: This is required for Borland's JBuilder to work - public java.sql.ResultSet getIndexInfo(String catalog, String schema, String tableName, boolean unique, boolean approximate) throws SQLException - { - Field f[] = new Field[13]; - java.sql.ResultSet r; // ResultSet for the SQL query that we need to do - Vector v = new Vector(); // The new ResultSet tuple stuff - - f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); - f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); - f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); - f[3] = new Field(connection, "NON_UNIQUE", iBoolOid, 1); - f[4] = new Field(connection, "INDEX_QUALIFIER", iVarcharOid, 32); - f[5] = new Field(connection, "INDEX_NAME", iVarcharOid, 32); - f[6] = new Field(connection, "TYPE", iInt2Oid, 2); - f[7] = new Field(connection, "ORDINAL_POSITION", iInt2Oid, 2); - f[8] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); - f[9] = new Field(connection, "ASC_OR_DESC", iVarcharOid, 32); - f[10] = new Field(connection, "CARDINALITY", iInt4Oid, 4); - f[11] = new Field(connection, "PAGES", iInt4Oid, 4); - f[12] = new Field(connection, "FILTER_CONDITION", iVarcharOid, 32); - - r = connection.ExecSQL("select " + - "c.relname, " + - "x.indisunique, " + - "i.relname, " + - "x.indisclustered, " + - "a.amname, " + - "x.indkey, " + - "c.reltuples, " + - "c.relpages, " + - "x.indexrelid " + - "FROM pg_index x, pg_class c, pg_class i, pg_am a " + - "WHERE ((c.relname = '" + tableName.toLowerCase() + "') " + - " AND (c.oid = x.indrelid) " + - " AND (i.oid = x.indexrelid) " + - " AND (i.relam = a.oid)) " + - "ORDER BY x.indisunique DESC, " + - " x.indisclustered, a.amname, i.relname"); - while (r.next()) { - // indkey is an array of column ordinals (integers). In the JDBC - // interface, this has to be separated out into a separate - // tuple for each indexed column. Also, getArray() is not yet - // implemented for Postgres JDBC, so we parse by hand. - String columnOrdinalString = r.getString(6); - StringTokenizer stok = new StringTokenizer(columnOrdinalString); - int [] columnOrdinals = new int[stok.countTokens()]; - int o = 0; - while (stok.hasMoreTokens()) { - columnOrdinals[o++] = Integer.parseInt(stok.nextToken()); - } - for (int i = 0; i < columnOrdinals.length; i++) { - byte [] [] tuple = new byte [13] []; - tuple[0] = "".getBytes(); - tuple[1] = "".getBytes(); - tuple[2] = r.getBytes(1); - tuple[3] = r.getBoolean(2) ? "f".getBytes() : "t".getBytes(); - tuple[4] = null; - tuple[5] = r.getBytes(3); - tuple[6] = r.getBoolean(4) ? - Integer.toString(tableIndexClustered).getBytes() : - r.getString(5).equals("hash") ? - Integer.toString(tableIndexHashed).getBytes() : - Integer.toString(tableIndexOther).getBytes(); - tuple[7] = Integer.toString(i + 1).getBytes(); - java.sql.ResultSet columnNameRS = connection.ExecSQL("select a.attname FROM pg_attribute a WHERE (a.attnum = " + columnOrdinals[i] + ") AND (a.attrelid = " + r.getInt(9) + ")"); - columnNameRS.next(); - tuple[8] = columnNameRS.getBytes(1); - tuple[9] = null; // sort sequence ??? - tuple[10] = r.getBytes(7); // inexact - tuple[11] = r.getBytes(8); - tuple[12] = null; - v.addElement(tuple); - } - } - - return new ResultSet(connection, f, v, "OK", 1); - } - - - // ** JDBC 2 Extensions ** - - /** - * Does the database support the given result set type? - * - * @param type - defined in java.sql.ResultSet - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsResultSetType(int type) throws SQLException - { - // The only type we don't support - return type != java.sql.ResultSet.TYPE_SCROLL_SENSITIVE; - } - - - /** - * Does the database support the concurrency type in combination - * with the given result set type? - * - * @param type - defined in java.sql.ResultSet - * @param concurrency - type defined in java.sql.ResultSet - * @return true if so; false otherwise - * @exception SQLException - if a database access error occurs - */ - public boolean supportsResultSetConcurrency(int type,int concurrency) throws SQLException - { - // These combinations are not supported! - if(type == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE) - return false; - - // We don't yet support Updateable ResultSets - if(concurrency == java.sql.ResultSet.CONCUR_UPDATABLE) - return false; - - // Everything else we do - return true; - } - - - /* lots of unsupported stuff... */ - public boolean ownUpdatesAreVisible(int type) throws SQLException - { - return false; - } - - public boolean ownDeletesAreVisible(int type) throws SQLException - { - return false; - } - - public boolean ownInsertsAreVisible(int type) throws SQLException - { - return false; - } - - public boolean othersUpdatesAreVisible(int type) throws SQLException - { - return false; - } - - public boolean othersDeletesAreVisible(int i) throws SQLException - { - return false; - } - - public boolean othersInsertsAreVisible(int type) throws SQLException - { - return false; - } - - public boolean updatesAreDetected(int type) throws SQLException - { - return false; - } - - public boolean deletesAreDetected(int i) throws SQLException - { - return false; - } - - public boolean insertsAreDetected(int type) throws SQLException - { - return false; - } - - /** - * Indicates whether the driver supports batch updates. - */ - public boolean supportsBatchUpdates() throws SQLException - { - return true; - } - - /** - * Return user defined types in a schema - */ - public java.sql.ResultSet getUDTs(String catalog, - String schemaPattern, - String typeNamePattern, - int[] types - ) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - - /** - * Retrieves the connection that produced this metadata object. - * - * @return the connection that produced this metadata object - */ - public java.sql.Connection getConnection() throws SQLException - { - return (java.sql.Connection)connection; - } - - /* I don't find these in the spec!?! */ - - public boolean rowChangesAreDetected(int type) throws SQLException - { - return false; - } - - public boolean rowChangesAreVisible(int type) throws SQLException - { - return false; - } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/PBatchUpdateException.java b/src/interfaces/jdbc/org/postgresql/jdbc2/PBatchUpdateException.java index 2f9d8ee402..f001f4ce28 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/PBatchUpdateException.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/PBatchUpdateException.java @@ -4,26 +4,29 @@ import org.postgresql.util.*; import java.sql.*; /** - * This class extends java.sql.BatchUpdateException, and provides our + * This class extends java.sql.BatchUpdateException, and provides our * internationalisation handling. */ -class PBatchUpdateException extends java.sql.BatchUpdateException { +class PBatchUpdateException extends java.sql.BatchUpdateException +{ private String message; public PBatchUpdateException( - String error, Object arg1, Object arg2, int[] updateCounts ) { + String error, Object arg1, Object arg2, int[] updateCounts ) + { super(updateCounts); Object[] argv = new Object[2]; argv[0] = arg1; argv[1] = arg2; - translate(error,argv); + translate(error, argv); } - private void translate(String error, Object[] args) { - message = MessageTranslator.translate(error,args); + private void translate(String error, Object[] args) + { + message = MessageTranslator.translate(error, args); } // Overides Throwable @@ -32,7 +35,7 @@ class PBatchUpdateException extends java.sql.BatchUpdateException { return message; } - // Overides Throwable + // Overides Throwable public String getMessage() { return message; @@ -41,6 +44,6 @@ class PBatchUpdateException extends java.sql.BatchUpdateException { // Overides Object public String toString() { - return message; + return message; } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java index 16b07ab5b6..d7db0669c6 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java @@ -36,13 +36,13 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta String[] inStrings; Connection connection; - // Some performance caches - private StringBuffer sbuf = new StringBuffer(); + // Some performance caches + private StringBuffer sbuf = new StringBuffer(); - // We use ThreadLocal for SimpleDateFormat's because they are not that - // thread safe, so each calling thread has its own object. - private static ThreadLocal tl_df = new ThreadLocal(); // setDate() SimpleDateFormat - private static ThreadLocal tl_tsdf = new ThreadLocal(); // setTimestamp() SimpleDateFormat + // We use ThreadLocal for SimpleDateFormat's because they are not that + // thread safe, so each calling thread has its own object. + private static ThreadLocal tl_df = new ThreadLocal(); // setDate() SimpleDateFormat + private static ThreadLocal tl_tsdf = new ThreadLocal(); // setTimestamp() SimpleDateFormat /** * Constructor for the PreparedStatement class. @@ -64,7 +64,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta this.sql = sql; this.connection = connection; - + for (i = 0; i < sql.length(); ++i) { int c = sql.charAt(i); @@ -91,12 +91,12 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * A Prepared SQL query is executed and its ResultSet is returned * * @return a ResultSet that contains the data produced by the - * * query - never null + * * query - never null * @exception SQLException if a database access error occurs */ public java.sql.ResultSet executeQuery() throws SQLException { - return super.executeQuery(compileQuery()); // in Statement class + return super.executeQuery(compileQuery()); // in Statement class } /** @@ -105,33 +105,34 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * be executed. * * @return either the row count for INSERT, UPDATE or DELETE; or - * * 0 for SQL statements that return nothing. + * * 0 for SQL statements that return nothing. * @exception SQLException if a database access error occurs */ public int executeUpdate() throws SQLException { - return super.executeUpdate(compileQuery()); // in Statement class + return super.executeUpdate(compileQuery()); // in Statement class } - /** - * Helper - this compiles the SQL query from the various parameters - * This is identical to toString() except it throws an exception if a - * parameter is unused. - */ - private synchronized String compileQuery() throws SQLException - { - sbuf.setLength(0); + /** + * Helper - this compiles the SQL query from the various parameters + * This is identical to toString() except it throws an exception if a + * parameter is unused. + */ + private synchronized String compileQuery() + throws SQLException + { + sbuf.setLength(0); int i; for (i = 0 ; i < inStrings.length ; ++i) { if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param",new Integer(i + 1)); + throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); sbuf.append (templateStrings[i]).append (inStrings[i]); } sbuf.append(templateStrings[inStrings.length]); - return sbuf.toString(); - } + return sbuf.toString(); + } /** * Set a parameter to SQL NULL @@ -227,7 +228,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } /** - * Set a parameter to a Java double value. The driver converts this + * Set a parameter to a Java double value. The driver converts this * to a SQL DOUBLE value when it sends it to the database * * @param parameterIndex the first parameter is 1... @@ -254,7 +255,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } /** - * Set a parameter to a Java String value. The driver converts this + * Set a parameter to a Java String value. The driver converts this * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments * size relative to the driver's limits on VARCHARs) when it sends it * to the database. @@ -265,63 +266,71 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setString(int parameterIndex, String x) throws SQLException { - // if the passed string is null, then set this column to null - if(x==null) - setNull(parameterIndex,Types.OTHER); - else { - // use the shared buffer object. Should never clash but this makes - // us thread safe! - synchronized(sbuf) { - sbuf.setLength(0); - int i; - - sbuf.append('\''); - for (i = 0 ; i < x.length() ; ++i) - { - char c = x.charAt(i); - if (c == '\\' || c == '\'') - sbuf.append((char)'\\'); - sbuf.append(c); - } - sbuf.append('\''); - set(parameterIndex, sbuf.toString()); - } - } - } - - /** - * Set a parameter to a Java array of bytes. The driver converts this - * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's - * size relative to the driver's limits on VARBINARYs) when it sends - * it to the database. - * - *

Implementation note: - *
With org.postgresql, this creates a large object, and stores the - * objects oid in this column. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBytes(int parameterIndex, byte x[]) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports the bytea datatype for byte arrays - if(null == x){ - setNull(parameterIndex,Types.OTHER); - } else { - setString(parameterIndex, PGbytea.toPGString(x)); - } - } else { - //Version 7.1 and earlier support done as LargeObjects - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - lob.write(x); - lob.close(); - setInt(parameterIndex,oid); - } - } + // if the passed string is null, then set this column to null + if (x == null) + setNull(parameterIndex, Types.OTHER); + else + { + // use the shared buffer object. Should never clash but this makes + // us thread safe! + synchronized (sbuf) + { + sbuf.setLength(0); + int i; + + sbuf.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + sbuf.append((char)'\\'); + sbuf.append(c); + } + sbuf.append('\''); + set(parameterIndex, sbuf.toString()); + } + } + } + + /** + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + *

Implementation note: + *
With org.postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports the bytea datatype for byte arrays + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + setString(parameterIndex, PGbytea.toPGString(x)); + } + } + else + { + //Version 7.1 and earlier support done as LargeObjects + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex, oid); + } + } /** * Set a parameter to a java.sql.Date value. The driver converts this @@ -333,26 +342,30 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setDate(int parameterIndex, java.sql.Date x) throws SQLException { - if(null == x){ - setNull(parameterIndex,Types.OTHER); - } else { - SimpleDateFormat df = (SimpleDateFormat) tl_df.get(); - if(df==null) { - df = new SimpleDateFormat("''yyyy-MM-dd''"); - tl_df.set(df); - } - set(parameterIndex, df.format(x)); - } - // The above is how the date should be handled. - // - // However, in JDK's prior to 1.1.6 (confirmed with the - // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems - // to format a date to the previous day. So the fix is to add a day - // before formatting. - // - // PS: 86400000 is one day - // - //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + SimpleDateFormat df = (SimpleDateFormat) tl_df.get(); + if (df == null) + { + df = new SimpleDateFormat("''yyyy-MM-dd''"); + tl_df.set(df); + } + set(parameterIndex, df.format(x)); + } + // The above is how the date should be handled. + // + // However, in JDK's prior to 1.1.6 (confirmed with the + // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems + // to format a date to the previous day. So the fix is to add a day + // before formatting. + // + // PS: 86400000 is one day + // + //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); } /** @@ -365,11 +378,14 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setTime(int parameterIndex, Time x) throws SQLException { - if (null == x){ - setNull(parameterIndex,Types.OTHER); - } else { - set(parameterIndex, "'" + x.toString() + "'"); - } + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + set(parameterIndex, "'" + x.toString() + "'"); + } } /** @@ -381,28 +397,33 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * @exception SQLException if a database access error occurs */ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException - { - if (null == x){ - setNull(parameterIndex,Types.OTHER); - } else { - SimpleDateFormat df = (SimpleDateFormat) tl_tsdf.get(); - if(df==null) { - df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("GMT")); - tl_tsdf.set(df); - } - - // Use the shared StringBuffer - synchronized(sbuf) { - sbuf.setLength(0); - sbuf.append("'").append(df.format(x)).append('.').append(x.getNanos()/10000000).append("+00'"); - set(parameterIndex, sbuf.toString()); - } - - // The above works, but so does the following. I'm leaving the above in, but this seems - // to be identical. Pays to read the docs ;-) - //set(parameterIndex,"'"+x.toString()+"'"); - } + { + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + SimpleDateFormat df = (SimpleDateFormat) tl_tsdf.get(); + if (df == null) + { + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + tl_tsdf.set(df); + } + + // Use the shared StringBuffer + synchronized (sbuf) + { + sbuf.setLength(0); + sbuf.append("'").append(df.format(x)).append('.').append(x.getNanos() / 10000000).append("+00'"); + set(parameterIndex, sbuf.toString()); + } + + // The above works, but so does the following. I'm leaving the above in, but this seems + // to be identical. Pays to read the docs ;-) + //set(parameterIndex,"'"+x.toString()+"'"); + } } /** @@ -423,29 +444,37 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try { - InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars,0,length); - setString(parameterIndex, new String(l_chars,0,l_charsRead)); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual",l_uee); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - } else { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } + } /** * When a very large Unicode value is input to a LONGVARCHAR parameter, @@ -467,29 +496,37 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try { - InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars,0,length); - setString(parameterIndex, new String(l_chars,0,l_charsRead)); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual",l_uee); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - } else { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } + } /** * When a very large binary value is input to a LONGVARBINARY parameter, @@ -507,60 +544,73 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports BinaryStream for for the PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //setBytes() since there is no current way to stream the value to the server - byte[] l_bytes = new byte[length]; - int l_bytesRead; - try { - l_bytesRead = x.read(l_bytes,0,length); - } catch (IOException l_ioe) { - throw new PSQLException("postgresql.unusual",l_ioe); - } - if (l_bytesRead == length) { - setBytes(parameterIndex, l_bytes); - } else { - //the stream contained less data than they said - byte[] l_bytes2 = new byte[l_bytesRead]; - System.arraycopy(l_bytes,0,l_bytes2,0,l_bytesRead); - setBytes(parameterIndex, l_bytes2); - } - } else { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARBINARY instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c=x.read(); - int p=0; - while(c>-1 && p -1 && p < length) + { + los.write(c); + c = x.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(parameterIndex, oid); + } + } /** * In general, parameter values remain in force for repeated used of a * Statement. Setting a parameter value automatically clears its - * previous value. However, in coms cases, it is useful to immediately + * previous value. However, in coms cases, it is useful to immediately * release the resources used by the current parameter values; this * can be done by calling clearParameters * @@ -595,56 +645,60 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { - if (x == null){ - setNull(parameterIndex,Types.OTHER); - return; + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return ; } switch (targetSqlType) { - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - case Types.REAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.DECIMAL: - case Types.NUMERIC: - if (x instanceof Boolean) - set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); - else - set(parameterIndex, x.toString()); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - setString(parameterIndex, x.toString()); - break; - case Types.DATE: - setDate(parameterIndex, (java.sql.Date)x); - break; - case Types.TIME: - setTime(parameterIndex, (Time)x); - break; - case Types.TIMESTAMP: - setTimestamp(parameterIndex, (Timestamp)x); - break; - case Types.BIT: - if (x instanceof Boolean) { - set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); - } else { - throw new PSQLException("postgresql.prep.type"); - } - break; - case Types.BINARY: - case Types.VARBINARY: - setObject(parameterIndex,x); - break; - case Types.OTHER: - setString(parameterIndex, ((PGobject)x).getValue()); - break; - default: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + if (x instanceof Boolean) + set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); + else + set(parameterIndex, x.toString()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + setString(parameterIndex, x.toString()); + break; + case Types.DATE: + setDate(parameterIndex, (java.sql.Date)x); + break; + case Types.TIME: + setTime(parameterIndex, (Time)x); + break; + case Types.TIMESTAMP: + setTimestamp(parameterIndex, (Timestamp)x); + break; + case Types.BIT: + if (x instanceof Boolean) + { + set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); + } + else + { throw new PSQLException("postgresql.prep.type"); + } + break; + case Types.BINARY: + case Types.VARBINARY: + setObject(parameterIndex, x); + break; + case Types.OTHER: + setString(parameterIndex, ((PGobject)x).getValue()); + break; + default: + throw new PSQLException("postgresql.prep.type"); } } @@ -653,17 +707,18 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta setObject(parameterIndex, x, targetSqlType, 0); } - /** - * This stores an Object into a parameter. - *

New for 6.4, if the object is not recognised, but it is - * Serializable, then the object is serialised using the - * org.postgresql.util.Serialize class. - */ + /** + * This stores an Object into a parameter. + *

New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * org.postgresql.util.Serialize class. + */ public void setObject(int parameterIndex, Object x) throws SQLException { - if (x == null){ - setNull(parameterIndex,Types.OTHER); - return; + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return ; } if (x instanceof String) setString(parameterIndex, (String)x); @@ -707,31 +762,33 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public boolean execute() throws SQLException { - return super.execute(compileQuery()); // in Statement class + return super.execute(compileQuery()); // in Statement class } /** * Returns the SQL statement with the current template values * substituted. - * NB: This is identical to compileQuery() except instead of throwing - * SQLException if a parameter is null, it places ? instead. + * NB: This is identical to compileQuery() except instead of throwing + * SQLException if a parameter is null, it places ? instead. */ - public String toString() { - synchronized(sbuf) { - sbuf.setLength(0); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) + public String toString() + { + synchronized (sbuf) { - if (inStrings[i] == null) - sbuf.append( '?' ); - else - sbuf.append (templateStrings[i]); - sbuf.append (inStrings[i]); + sbuf.setLength(0); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + sbuf.append( '?' ); + else + sbuf.append (templateStrings[i]); + sbuf.append (inStrings[i]); + } + sbuf.append(templateStrings[inStrings.length]); + return sbuf.toString(); } - sbuf.append(templateStrings[inStrings.length]); - return sbuf.toString(); - } } // ************************************************************** @@ -740,7 +797,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta /** * There are a lot of setXXX classes which all basically do - * the same thing. We need a method which actually does the + * the same thing. We need a method which actually does the * set for us. * * @param paramIndex the index into the inString @@ -777,197 +834,218 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } - // ** JDBC 2 Extensions ** - - /** - * This parses the query and adds it to the current batch - */ - public void addBatch() throws SQLException - { - super.addBatch(compileQuery()); - } - - /** - * Not sure what this one does, so I'm saying this returns the MetaData for - * the last ResultSet returned! - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - java.sql.ResultSet rs = getResultSet(); - if(rs!=null) - return rs.getMetaData(); - - // Does anyone really know what this method does? - return null; - } - - public void setArray(int i, java.sql.Array x) throws SQLException - { - setString(i, x.toString()); - } - - /** - * Sets a Blob - */ - public void setBlob(int i,Blob x) throws SQLException - { - InputStream l_inStream = x.getBinaryStream(); - int l_length = (int) x.length(); - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c=l_inStream.read(); - int p=0; - while(c>-1 && p-1 && p-1 && p -1 && p < l_length) + { + los.write(c); + c = l_inStream.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + + /** + * This is similar to setBinaryStream except it uses a Reader instead of + * InputStream. + */ + public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports CharacterStream for for the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all the text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + char[] l_chars = new char[length]; + int l_charsRead; + try + { + l_charsRead = x.read(l_chars, 0, length); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + setString(i, new String(l_chars, 0, l_charsRead)); + } + else + { + //Version 7.1 only supported streams for LargeObjects + //but the jdbc spec indicates that streams should be + //available for LONGVARCHAR instead + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = x.read(); + int p = 0; + while (c > -1 && p < length) + { + los.write(c); + c = x.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + } + + /** + * New in 7.1 + */ + public void setClob(int i, Clob x) throws SQLException + { + InputStream l_inStream = x.getAsciiStream(); + int l_length = (int) x.length(); + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = l_inStream.read(); + int p = 0; + while (c > -1 && p < l_length) + { + los.write(c); + c = l_inStream.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + + /** + * At least this works as in PostgreSQL null represents anything null ;-) + * + * New in 7,1 + */ + public void setNull(int i, int t, String s) throws SQLException + { + setNull(i, t); + } + + public void setRef(int i, Ref x) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /** + * New in 7,1 + */ + public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setDate(i, d); + else + { + cal.setTime(d); + setDate(i, new java.sql.Date(cal.getTime().getTime())); + } + } + + /** + * New in 7,1 + */ + public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setTime(i, t); + else + { + cal.setTime(t); + setTime(i, new java.sql.Time(cal.getTime().getTime())); + } + } + + /** + * New in 7,1 + */ + public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setTimestamp(i, t); + else + { + cal.setTime(t); + setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime())); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java index 3d39f6461a..cd941d2179 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java @@ -27,7 +27,7 @@ import org.postgresql.core.Encoding; * *

The getXXX methods retrieve column values for the current row. You can * retrieve values either using the index number of the column, or by using - * the name of the column. In general using the column index will be more + * the name of the column. In general using the column index will be more * efficient. Columns are numbered from 1. * *

For maximum portability, ResultSet columns within each row should be read @@ -40,10 +40,10 @@ import org.postgresql.core.Encoding; * *

Column names used as input to getXXX methods are case insenstive. When * performing a getXXX using a column name, if several columns have the same - * name, then the value of the first matching column will be returned. The + * name, then the value of the first matching column will be returned. The * column name option is designed to be used when column names are used in the * SQL Query. For columns that are NOT explicitly named in the query, it is - * best to use column numbers. If column names were used there is no way for + * best to use column numbers. If column names were used there is no way for * the programmer to guarentee that they actually refer to the intended * columns. * @@ -59,1344 +59,1381 @@ import org.postgresql.core.Encoding; */ public class ResultSet extends org.postgresql.ResultSet implements java.sql.ResultSet { - protected org.postgresql.jdbc2.Statement statement; - - private StringBuffer sbuf = null; - - /** - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount,int insertOID, boolean binaryCursor) - { - super(conn,fields,tuples,status,updateCount,insertOID,binaryCursor); - } - - /** - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) - { - super(conn,fields,tuples,status,updateCount,0,false); - } - - /** - * A ResultSet is initially positioned before its first row, - * the first call to next makes the first row the current row; - * the second call makes the second row the current row, etc. - * - *

If an input stream from the previous row is open, it is - * implicitly closed. The ResultSet's warning chain is cleared - * when a new row is read - * - * @return true if the new current is valid; false if there are no - * more rows - * @exception SQLException if a database access error occurs - */ - public boolean next() throws SQLException - { - if (++current_row >= rows.size()) - return false; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - /** - * In some cases, it is desirable to immediately release a ResultSet - * database and JDBC resources instead of waiting for this to happen - * when it is automatically closed. The close method provides this - * immediate release. - * - *

Note: A ResultSet is automatically closed by the Statement - * the Statement that generated it when that Statement is closed, - * re-executed, or is used to retrieve the next result from a sequence - * of multiple results. A ResultSet is also automatically closed - * when it is garbage collected. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - //release resources held (memory for tuples) - if(rows!=null) { - rows=null; - } - } - - /** - * A column may have the value of SQL NULL; wasNull() reports whether - * the last column read had this special value. Note that you must - * first call getXXX on a column to try to read its value and then - * call wasNull() to find if the value was SQL NULL - * - * @return true if the last column read was SQL NULL - * @exception SQLException if a database access error occurred - */ - public boolean wasNull() throws SQLException - { - return wasNullFlag; - } - - /** - * Get the value of a column in the current row as a Java String - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, null for SQL NULL - * @exception SQLException if a database access error occurs - */ - public String getString(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - - wasNullFlag = (this_row[columnIndex - 1] == null); - if(wasNullFlag) - return null; - - Encoding encoding = connection.getEncoding(); - return encoding.decode(this_row[columnIndex - 1]); - } - - /** - * Get the value of a column in the current row as a Java boolean - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, false for SQL NULL - * @exception SQLException if a database access error occurs - */ - public boolean getBoolean(int columnIndex) throws SQLException - { - return toBoolean( getString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a Java byte. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public byte getByte(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Byte.parseByte(s); - } catch (NumberFormatException e) { - throw new PSQLException("postgresql.res.badbyte",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java short. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public short getShort(int columnIndex) throws SQLException - { - String s = getFixedString(columnIndex); - - if (s != null) - { - try - { - return Short.parseShort(s); - } catch (NumberFormatException e) { - throw new PSQLException("postgresql.res.badshort",s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java int. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public int getInt(int columnIndex) throws SQLException - { - return toInt( getFixedString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a Java long. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public long getLong(int columnIndex) throws SQLException - { - return toLong( getFixedString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a Java float. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public float getFloat(int columnIndex) throws SQLException - { - return toFloat( getFixedString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a Java double. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public double getDouble(int columnIndex) throws SQLException - { - return toDouble( getFixedString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a - * java.math.BigDecimal object - * - * @param columnIndex the first column is 1, the second is 2... - * @param scale the number of digits to the right of the decimal - * @return the column value; if the value is SQL NULL, null - * @exception SQLException if a database access error occurs - * @deprecated - */ - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException - { - return toBigDecimal( getFixedString(columnIndex), scale ); - } - - /** - * Get the value of a column in the current row as a Java byte array. - * - *

In normal use, the bytes represent the raw values returned by the - * backend. However, if the column is an OID, then it is assumed to - * refer to a Large Object, and that object is returned as a byte array. - * - *

Be warned If the large object is huge, then you may run out - * of memory. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the result - * is null - * @exception SQLException if a database access error occurs - */ - public byte[] getBytes(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - - //If the data is already binary then just return it - if (binaryCursor) return this_row[columnIndex - 1]; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports the bytea datatype for byte arrays - return PGbytea.toBytes(getString(columnIndex)); - } else { - //Version 7.1 and earlier supports LargeObjects for byte arrays - wasNullFlag = (this_row[columnIndex - 1] == null); - // Handle OID's as BLOBS - if(!wasNullFlag) { - if( fields[columnIndex - 1].getOID() == 26) { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - byte buf[] = lob.read(lob.size()); - lob.close(); - return buf; - } - } - } - return null; - } - - /** - * Get the value of a column in the current row as a java.sql.Date - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public java.sql.Date getDate(int columnIndex) throws SQLException - { - return toDate( getString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a java.sql.Time - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Time getTime(int columnIndex) throws SQLException - { - return toTime( getString(columnIndex) ); - } - - /** - * Get the value of a column in the current row as a - * java.sql.Timestamp object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Timestamp getTimestamp(int columnIndex) throws SQLException - { - return toTimestamp( getString(columnIndex), this ); - } - - /** - * A column value can be retrieved as a stream of ASCII characters - * and then read in chunks from the stream. This method is - * particular suitable for retrieving large LONGVARCHAR values. - * The JDBC driver will do any necessary conversion from the - * database format into ASCII. - * - *

Note: All the data in the returned stream must be read - * prior to getting the value of any other column. The next call - * to a get method implicitly closes the stream. Also, a stream - * may return 0 for available() whether there is data available - * or not. - * - *

We implement an ASCII stream as a Binary stream - we should really - * do the data conversion, but I cannot be bothered to implement this - * right now. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return a Java InputStream that delivers the database column - * value as a stream of one byte ASCII characters. If the - * value is SQL NULL then the result is null - * @exception SQLException if a database access error occurs - * @see getBinaryStream - */ - public InputStream getAsciiStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try { - return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual", l_uee); - } - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - /** - * A column value can also be retrieved as a stream of Unicode - * characters. We implement this as a binary stream. - * - * ** DEPRECATED IN JDBC 2 ** - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getBinaryStream - * @deprecated in JDBC2.0 - */ - public InputStream getUnicodeStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - try { - return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); - } catch (UnsupportedEncodingException l_uee) { - throw new PSQLException("postgresql.unusual", l_uee); - } - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - return getBinaryStream(columnIndex); - } - } - - /** - * A column value can also be retrieved as a binary strea. This - * method is suitable for retrieving LONGVARBINARY values. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of bytes. If the value is SQL NULL, then the result - * is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getUnicodeStream - */ - public InputStream getBinaryStream(int columnIndex) throws SQLException - { - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports BinaryStream for all PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getBytes() since there is no current way to stream the value from the server - byte b[] = getBytes(columnIndex); - if (b != null) - return new ByteArrayInputStream(b); - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - if( fields[columnIndex - 1].getOID() == 26) { - LargeObjectManager lom = connection.getLargeObjectAPI(); - LargeObject lob = lom.open(getInt(columnIndex)); - return lob.getInputStream(); - } - } - return null; - } - - /** - * The following routines simply convert the columnName into - * a columnIndex and then call the appropriate routine above. - * - * @param columnName is the SQL name of the column - * @return the column value - * @exception SQLException if a database access error occurs - */ - public String getString(String columnName) throws SQLException - { - return getString(findColumn(columnName)); - } - - public boolean getBoolean(String columnName) throws SQLException - { - return getBoolean(findColumn(columnName)); - } - - public byte getByte(String columnName) throws SQLException - { - - return getByte(findColumn(columnName)); - } - - public short getShort(String columnName) throws SQLException - { - return getShort(findColumn(columnName)); - } - - public int getInt(String columnName) throws SQLException - { - return getInt(findColumn(columnName)); - } - - public long getLong(String columnName) throws SQLException - { - return getLong(findColumn(columnName)); - } - - public float getFloat(String columnName) throws SQLException - { - return getFloat(findColumn(columnName)); - } - - public double getDouble(String columnName) throws SQLException - { - return getDouble(findColumn(columnName)); - } - - /** - * @deprecated - */ - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException - { - return getBigDecimal(findColumn(columnName), scale); - } - - public byte[] getBytes(String columnName) throws SQLException - { - return getBytes(findColumn(columnName)); - } - - public java.sql.Date getDate(String columnName) throws SQLException - { - return getDate(findColumn(columnName)); - } - - public Time getTime(String columnName) throws SQLException - { - return getTime(findColumn(columnName)); - } - - public Timestamp getTimestamp(String columnName) throws SQLException - { - return getTimestamp(findColumn(columnName)); - } - - public InputStream getAsciiStream(String columnName) throws SQLException - { - return getAsciiStream(findColumn(columnName)); - } - - /** - * - * ** DEPRECATED IN JDBC 2 ** - * - * @deprecated - */ - public InputStream getUnicodeStream(String columnName) throws SQLException - { - return getUnicodeStream(findColumn(columnName)); - } - - public InputStream getBinaryStream(String columnName) throws SQLException - { - return getBinaryStream(findColumn(columnName)); - } - - /** - * The first warning reported by calls on this ResultSet is - * returned. Subsequent ResultSet warnings will be chained - * to this SQLWarning. - * - *

The warning chain is automatically cleared each time a new - * row is read. - * - *

Note: This warning chain only covers warnings caused by - * ResultSet methods. Any warnings caused by statement methods - * (such as reading OUT parameters) will be chained on the - * Statement object. - * - * @return the first SQLWarning or null; - * @exception SQLException if a database access error occurs. - */ - public SQLWarning getWarnings() throws SQLException - { - return warnings; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this ResultSet - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - warnings = null; - } - - /** - * Get the name of the SQL cursor used by this ResultSet - * - *

In SQL, a result table is retrieved though a cursor that is - * named. The current row of a result can be updated or deleted - * using a positioned update/delete statement that references - * the cursor name. - * - *

JDBC supports this SQL feature by providing the name of the - * SQL cursor used by a ResultSet. The current row of a ResulSet - * is also the current row of this SQL cursor. - * - *

Note: If positioned update is not supported, a SQLException - * is thrown. - * - * @return the ResultSet's SQL cursor name. - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return connection.getCursorName(); - } - - /** - * The numbers, types and properties of a ResultSet's columns are - * provided by the getMetaData method - * - * @return a description of the ResultSet's columns - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - return new ResultSetMetaData(rows, fields); - } - - /** - * Get the value of a column in the current row as a Java object - * - *

This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - *

This method may also be used to read database specific abstract - * data types. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(int columnIndex) throws SQLException - { - Field field; - - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - - wasNullFlag = (this_row[columnIndex - 1] == null); - if(wasNullFlag) - return null; - - field = fields[columnIndex - 1]; - - // some fields can be null, mainly from those returned by MetaData methods - if(field==null) { - wasNullFlag=true; - return null; - } - - switch (field.getSQLType()) - { - case Types.BIT: - return new Boolean(getBoolean(columnIndex)); - case Types.SMALLINT: - return new Integer(getInt(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal - (columnIndex, (field.getMod()==-1)?-1:((field.getMod()-4) & 0xffff)); - case Types.REAL: - return new Float(getFloat(columnIndex)); - case Types.DOUBLE: - return new Double(getDouble(columnIndex)); - case Types.CHAR: - case Types.VARCHAR: - return getString(columnIndex); - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - case Types.BINARY: - case Types.VARBINARY: - return getBytes(columnIndex); - default: - String type = field.getPGType(); - // if the backend doesn't know the type then coerce to String - if (type.equals("unknown")){ - return getString(columnIndex); - }else{ - return connection.getObject(field.getPGType(), getString(columnIndex)); - } - } - } - - /** - * Get the value of a column in the current row as a Java object - * - *

This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - *

This method may also be used to read database specific abstract - * data types. - * - * @param columnName is the SQL name of the column - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(String columnName) throws SQLException - { - return getObject(findColumn(columnName)); - } - - /** - * Map a ResultSet column name to a ResultSet column index - * - * @param columnName the name of the column - * @return the column index - * @exception SQLException if a database access error occurs - */ - public int findColumn(String columnName) throws SQLException - { - int i; - - final int flen = fields.length; - for (i = 0 ; i < flen; ++i) - if (fields[i].getName().equalsIgnoreCase(columnName)) - return (i+1); - throw new PSQLException ("postgresql.res.colname",columnName); - } - - // ** JDBC 2 Extensions ** - - public boolean absolute(int index) throws SQLException - { - // index is 1-based, but internally we use 0-based indices - int internalIndex; - - if (index==0) - throw new SQLException("Cannot move to index of 0"); - - final int rows_size = rows.size(); - - //if index<0, count from the end of the result set, but check - //to be sure that it is not beyond the first index - if (index<0) - if (index >= -rows_size) - internalIndex = rows_size+index; - else { - beforeFirst(); - return false; - } - - //must be the case that index>0, - //find the correct place, assuming that - //the index is not too large - if (index <= rows_size) - internalIndex = index-1; - else { - afterLast(); - return false; - } - - current_row=internalIndex; - this_row = (byte [][])rows.elementAt(internalIndex); - return true; - } - - public void afterLast() throws SQLException - { - final int rows_size = rows.size(); - if (rows_size > 0) - current_row = rows_size; - } - - public void beforeFirst() throws SQLException - { - if (rows.size() > 0) - current_row = -1; - } - - public void cancelRowUpdates() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void deleteRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean first() throws SQLException - { - if (rows.size() <= 0) - return false; - current_row = 0; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - public java.sql.Array getArray(String colName) throws SQLException - { - return getArray(findColumn(colName)); - } - - public java.sql.Array getArray(int i) throws SQLException - { - wasNullFlag = (this_row[i - 1] == null); - if(wasNullFlag) - return null; - - if (i < 1 || i > fields.length) - throw new PSQLException("postgresql.res.colrange"); - return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i-1], this ); - } - - public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException - { - return getBigDecimal(columnIndex,-1); - } - - public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException - { - return getBigDecimal(findColumn(columnName)); - } - - public Blob getBlob(String columnName) throws SQLException - { - return getBlob(findColumn(columnName)); - } - - public Blob getBlob(int i) throws SQLException - { - return new org.postgresql.largeobject.PGblob(connection,getInt(i)); - } - - public java.io.Reader getCharacterStream(String columnName) throws SQLException - { - return getCharacterStream(findColumn(columnName)); - } - - public java.io.Reader getCharacterStream(int i) throws SQLException - { - wasNullFlag = (this_row[i - 1] == null); - if (wasNullFlag) - return null; - - if (connection.haveMinimumCompatibleVersion("7.2")) { - //Version 7.2 supports AsciiStream for all the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - return new CharArrayReader(getString(i).toCharArray()); - } else { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - Encoding encoding = connection.getEncoding(); - InputStream input = getBinaryStream(i); - return encoding.getDecodingReader(input); - } - } - - /** - * New in 7.1 - */ - public Clob getClob(String columnName) throws SQLException - { - return getClob(findColumn(columnName)); - } - - /** - * New in 7.1 - */ - public Clob getClob(int i) throws SQLException - { - return new org.postgresql.largeobject.PGclob(connection,getInt(i)); - } - - public int getConcurrency() throws SQLException - { - // New in 7.1 - The standard ResultSet class will now return - // CONCUR_READ_ONLY. A sub-class will overide this if the query was - // updateable. - return CONCUR_READ_ONLY; - } - - public java.sql.Date getDate(int i,java.util.Calendar cal) throws SQLException - { - // new in 7.1: If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getDate()? - // for now... - return getDate(i); - } - - public Time getTime(int i,java.util.Calendar cal) throws SQLException - { - // new in 7.1: If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getTime()? - // for now... - return getTime(i); - } - - public Timestamp getTimestamp(int i,java.util.Calendar cal) throws SQLException - { - // new in 7.1: If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getDate()? - // for now... - return getTimestamp(i); - } - - public java.sql.Date getDate(String c,java.util.Calendar cal) throws SQLException - { - return getDate(findColumn(c),cal); - } - - public Time getTime(String c,java.util.Calendar cal) throws SQLException - { - return getTime(findColumn(c),cal); - } - - public Timestamp getTimestamp(String c,java.util.Calendar cal) throws SQLException - { - return getTimestamp(findColumn(c),cal); - } - - public int getFetchDirection() throws SQLException - { - // new in 7.1: PostgreSQL normally sends rows first->last - return FETCH_FORWARD; - } - - public int getFetchSize() throws SQLException - { - // new in 7.1: In this implementation we return the entire result set, so - // here return the number of rows we have. Sub-classes can return a proper - // value - return rows.size(); - } - - public Object getObject(String columnName,java.util.Map map) throws SQLException - { - return getObject(findColumn(columnName),map); - } - - /** - * This checks against map for the type of column i, and if found returns - * an object based on that mapping. The class must implement the SQLData - * interface. - */ - public Object getObject(int i,java.util.Map map) throws SQLException - { - /* In preparation - SQLInput s = new PSQLInput(this,i); - String t = getTypeName(i); - SQLData o = (SQLData) map.get(t); - // If the type is not in the map, then pass to the existing code - if(o==null) - return getObject(i); - o.readSQL(s,t); - return o; - */throw org.postgresql.Driver.notImplemented(); - } - - public Ref getRef(String columnName) throws SQLException - { - return getRef(findColumn(columnName)); - } - - public Ref getRef(int i) throws SQLException - { - // new in 7.1: The backend doesn't yet have SQL3 REF types - throw new PSQLException("postgresql.psqlnotimp"); - } - - public int getRow() throws SQLException - { - return current_row + 1; - } - - // This one needs some thought, as not all ResultSets come from a statement - public java.sql.Statement getStatement() throws SQLException - { - return statement; - } - - public int getType() throws SQLException - { - // New in 7.1. This implementation allows scrolling but is not able to - // see any changes. Sub-classes may overide this to return a more - // meaningful result. - return TYPE_SCROLL_INSENSITIVE; - } - - public void insertRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean isAfterLast() throws SQLException - { - final int rows_size = rows.size(); - return (current_row >= rows_size && rows_size > 0); - } - - public boolean isBeforeFirst() throws SQLException - { - return (current_row < 0 && rows.size() > 0); - } - - public boolean isFirst() throws SQLException - { - return (current_row == 0 && rows.size() >= 0); - } - - public boolean isLast() throws SQLException - { - final int rows_size = rows.size(); - return (current_row == rows_size -1 && rows_size > 0); - } - - public boolean last() throws SQLException - { - final int rows_size = rows.size(); - if (rows_size <= 0) - return false; - current_row = rows_size - 1; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - public void moveToCurrentRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void moveToInsertRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean previous() throws SQLException - { - if (--current_row < 0) - return false; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - public void refreshRow() throws SQLException - { - throw new PSQLException("postgresql.notsensitive"); - } - - // Peter: Implemented in 7.0 - public boolean relative(int rows) throws SQLException - { - //have to add 1 since absolute expects a 1-based index - return absolute(current_row+1+rows); - } - - public boolean rowDeleted() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public boolean rowInserted() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public boolean rowUpdated() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public void setFetchDirection(int direction) throws SQLException - { - // In 7.1, the backend doesn't yet support this - throw new PSQLException("postgresql.psqlnotimp"); - } - - public void setFetchSize(int rows) throws SQLException - { - // Sub-classes should implement this as part of their cursor support - throw org.postgresql.Driver.notImplemented(); - } - - public void updateAsciiStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateAsciiStream(String columnName, - java.io.InputStream x, - int length - ) throws SQLException - { - updateAsciiStream(findColumn(columnName),x,length); - } - - public void updateBigDecimal(int columnIndex, - java.math.BigDecimal x - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBigDecimal(String columnName, - java.math.BigDecimal x - ) throws SQLException - { - updateBigDecimal(findColumn(columnName),x); - } - - public void updateBinaryStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBinaryStream(String columnName, - java.io.InputStream x, - int length - ) throws SQLException - { - updateBinaryStream(findColumn(columnName),x,length); - } - - public void updateBoolean(int columnIndex,boolean x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBoolean(String columnName,boolean x) throws SQLException - { - updateBoolean(findColumn(columnName),x); - } - - public void updateByte(int columnIndex,byte x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateByte(String columnName,byte x) throws SQLException - { - updateByte(findColumn(columnName),x); - } - - public void updateBytes(String columnName,byte[] x) throws SQLException - { - updateBytes(findColumn(columnName),x); - } - - public void updateBytes(int columnIndex,byte[] x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateCharacterStream(int columnIndex, - java.io.Reader x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateCharacterStream(String columnName, - java.io.Reader x, - int length - ) throws SQLException - { - updateCharacterStream(findColumn(columnName),x,length); - } - - public void updateDate(int columnIndex,java.sql.Date x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateDate(String columnName,java.sql.Date x) throws SQLException - { - updateDate(findColumn(columnName),x); - } - - public void updateDouble(int columnIndex,double x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateDouble(String columnName,double x) throws SQLException - { - updateDouble(findColumn(columnName),x); - } - - public void updateFloat(int columnIndex,float x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateFloat(String columnName,float x) throws SQLException - { - updateFloat(findColumn(columnName),x); - } - - public void updateInt(int columnIndex,int x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateInt(String columnName,int x) throws SQLException - { - updateInt(findColumn(columnName),x); - } - - public void updateLong(int columnIndex,long x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateLong(String columnName,long x) throws SQLException - { - updateLong(findColumn(columnName),x); - } - - public void updateNull(int columnIndex) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateNull(String columnName) throws SQLException - { - updateNull(findColumn(columnName)); - } - - public void updateObject(int columnIndex,Object x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateObject(String columnName,Object x) throws SQLException - { - updateObject(findColumn(columnName),x); - } - - public void updateObject(int columnIndex,Object x,int scale) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateObject(String columnName,Object x,int scale) throws SQLException - { - updateObject(findColumn(columnName),x,scale); - } - - public void updateRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateShort(int columnIndex,short x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateShort(String columnName,short x) throws SQLException - { - updateShort(findColumn(columnName),x); - } - - public void updateString(int columnIndex,String x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateString(String columnName,String x) throws SQLException - { - updateString(findColumn(columnName),x); - } - - public void updateTime(int columnIndex,Time x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateTime(String columnName,Time x) throws SQLException - { - updateTime(findColumn(columnName),x); - } - - public void updateTimestamp(int columnIndex,Timestamp x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateTimestamp(String columnName,Timestamp x) throws SQLException - { - updateTimestamp(findColumn(columnName),x); - } - - // helper method. Throws an SQLException when an update is not possible - public void notUpdateable() throws SQLException - { - throw new PSQLException("postgresql.noupdate"); - } - - /** - * This is called by Statement to register itself with this statement. - * It's used currently by getStatement() but may also with the new core - * package. - */ - public void setStatement(org.postgresql.jdbc2.Statement statement) { - this.statement=statement; - } + protected org.postgresql.jdbc2.Statement statement; + + private StringBuffer sbuf = null; + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount, int insertOID, boolean binaryCursor) + { + super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); + } + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + super(conn, fields, tuples, status, updateCount, 0, false); + } + + /** + * A ResultSet is initially positioned before its first row, + * the first call to next makes the first row the current row; + * the second call makes the second row the current row, etc. + * + *

If an input stream from the previous row is open, it is + * implicitly closed. The ResultSet's warning chain is cleared + * when a new row is read + * + * @return true if the new current is valid; false if there are no + * more rows + * @exception SQLException if a database access error occurs + */ + public boolean next() throws SQLException + { + if (++current_row >= rows.size()) + return false; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + /** + * In some cases, it is desirable to immediately release a ResultSet + * database and JDBC resources instead of waiting for this to happen + * when it is automatically closed. The close method provides this + * immediate release. + * + *

Note: A ResultSet is automatically closed by the Statement + * the Statement that generated it when that Statement is closed, + * re-executed, or is used to retrieve the next result from a sequence + * of multiple results. A ResultSet is also automatically closed + * when it is garbage collected. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + //release resources held (memory for tuples) + if (rows != null) + { + rows = null; + } + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether + * the last column read had this special value. Note that you must + * first call getXXX on a column to try to read its value and then + * call wasNull() to find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * @exception SQLException if a database access error occurred + */ + public boolean wasNull() throws SQLException + { + return wasNullFlag; + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, null for SQL NULL + * @exception SQLException if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + Encoding encoding = connection.getEncoding(); + return encoding.decode(this_row[columnIndex - 1]); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, false for SQL NULL + * @exception SQLException if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException + { + return toBoolean( getString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Byte.parseByte(s); + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.res.badbyte", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException + { + String s = getFixedString(columnIndex); + + if (s != null) + { + try + { + return Short.parseShort(s); + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.res.badshort", s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException + { + return toInt( getFixedString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException + { + return toLong( getFixedString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException + { + return toFloat( getFixedString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException + { + return toDouble( getFixedString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a + * java.math.BigDecimal object + * + * @param columnIndex the first column is 1, the second is 2... + * @param scale the number of digits to the right of the decimal + * @return the column value; if the value is SQL NULL, null + * @exception SQLException if a database access error occurs + * @deprecated + */ + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException + { + return toBigDecimal( getFixedString(columnIndex), scale ); + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

In normal use, the bytes represent the raw values returned by the + * backend. However, if the column is an OID, then it is assumed to + * refer to a Large Object, and that object is returned as a byte array. + * + *

Be warned If the large object is huge, then you may run out + * of memory. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the result + * is null + * @exception SQLException if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + + //If the data is already binary then just return it + if (binaryCursor) + return this_row[columnIndex - 1]; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports the bytea datatype for byte arrays + return PGbytea.toBytes(getString(columnIndex)); + } + else + { + //Version 7.1 and earlier supports LargeObjects for byte arrays + wasNullFlag = (this_row[columnIndex - 1] == null); + // Handle OID's as BLOBS + if (!wasNullFlag) + { + if ( fields[columnIndex - 1].getOID() == 26) + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + byte buf[] = lob.read(lob.size()); + lob.close(); + return buf; + } + } + } + return null; + } + + /** + * Get the value of a column in the current row as a java.sql.Date + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws SQLException + { + return toDate( getString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a java.sql.Time + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Time getTime(int columnIndex) throws SQLException + { + return toTime( getString(columnIndex) ); + } + + /** + * Get the value of a column in the current row as a + * java.sql.Timestamp object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws SQLException + { + return toTimestamp( getString(columnIndex), this ); + } + + /** + * A column value can be retrieved as a stream of ASCII characters + * and then read in chunks from the stream. This method is + * particular suitable for retrieving large LONGVARCHAR values. + * The JDBC driver will do any necessary conversion from the + * database format into ASCII. + * + *

Note: All the data in the returned stream must be read + * prior to getting the value of any other column. The next call + * to a get method implicitly closes the stream. Also, a stream + * may return 0 for available() whether there is data available + * or not. + * + *

We implement an ASCII stream as a Binary stream - we should really + * do the data conversion, but I cannot be bothered to implement this + * right now. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return a Java InputStream that delivers the database column + * value as a stream of one byte ASCII characters. If the + * value is SQL NULL then the result is null + * @exception SQLException if a database access error occurs + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try + { + return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + return getBinaryStream(columnIndex); + } + } + + /** + * A column value can also be retrieved as a stream of Unicode + * characters. We implement this as a binary stream. + * + * ** DEPRECATED IN JDBC 2 ** + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getBinaryStream + * @deprecated in JDBC2.0 + */ + public InputStream getUnicodeStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try + { + return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + return getBinaryStream(columnIndex); + } + } + + /** + * A column value can also be retrieved as a binary strea. This + * method is suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of bytes. If the value is SQL NULL, then the result + * is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getUnicodeStream + */ + public InputStream getBinaryStream(int columnIndex) throws SQLException + { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports BinaryStream for all PG bytea type + //As the spec/javadoc for this method indicate this is to be used for + //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate + //long binary datatype, but with toast the bytea datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getBytes() since there is no current way to stream the value from the server + byte b[] = getBytes(columnIndex); + if (b != null) + return new ByteArrayInputStream(b); + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + if ( fields[columnIndex - 1].getOID() == 26) + { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + return lob.getInputStream(); + } + } + return null; + } + + /** + * The following routines simply convert the columnName into + * a columnIndex and then call the appropriate routine above. + * + * @param columnName is the SQL name of the column + * @return the column value + * @exception SQLException if a database access error occurs + */ + public String getString(String columnName) throws SQLException + { + return getString(findColumn(columnName)); + } + + public boolean getBoolean(String columnName) throws SQLException + { + return getBoolean(findColumn(columnName)); + } + + public byte getByte(String columnName) throws SQLException + { + + return getByte(findColumn(columnName)); + } + + public short getShort(String columnName) throws SQLException + { + return getShort(findColumn(columnName)); + } + + public int getInt(String columnName) throws SQLException + { + return getInt(findColumn(columnName)); + } + + public long getLong(String columnName) throws SQLException + { + return getLong(findColumn(columnName)); + } + + public float getFloat(String columnName) throws SQLException + { + return getFloat(findColumn(columnName)); + } + + public double getDouble(String columnName) throws SQLException + { + return getDouble(findColumn(columnName)); + } + + /** + * @deprecated + */ + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException + { + return getBigDecimal(findColumn(columnName), scale); + } + + public byte[] getBytes(String columnName) throws SQLException + { + return getBytes(findColumn(columnName)); + } + + public java.sql.Date getDate(String columnName) throws SQLException + { + return getDate(findColumn(columnName)); + } + + public Time getTime(String columnName) throws SQLException + { + return getTime(findColumn(columnName)); + } + + public Timestamp getTimestamp(String columnName) throws SQLException + { + return getTimestamp(findColumn(columnName)); + } + + public InputStream getAsciiStream(String columnName) throws SQLException + { + return getAsciiStream(findColumn(columnName)); + } + + /** + * + * ** DEPRECATED IN JDBC 2 ** + * + * @deprecated + */ + public InputStream getUnicodeStream(String columnName) throws SQLException + { + return getUnicodeStream(findColumn(columnName)); + } + + public InputStream getBinaryStream(String columnName) throws SQLException + { + return getBinaryStream(findColumn(columnName)); + } + + /** + * The first warning reported by calls on this ResultSet is + * returned. Subsequent ResultSet warnings will be chained + * to this SQLWarning. + * + *

The warning chain is automatically cleared each time a new + * row is read. + * + *

Note: This warning chain only covers warnings caused by + * ResultSet methods. Any warnings caused by statement methods + * (such as reading OUT parameters) will be chained on the + * Statement object. + * + * @return the first SQLWarning or null; + * @exception SQLException if a database access error occurs. + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this ResultSet + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + *

In SQL, a result table is retrieved though a cursor that is + * named. The current row of a result can be updated or deleted + * using a positioned update/delete statement that references + * the cursor name. + * + *

JDBC supports this SQL feature by providing the name of the + * SQL cursor used by a ResultSet. The current row of a ResulSet + * is also the current row of this SQL cursor. + * + *

Note: If positioned update is not supported, a SQLException + * is thrown. + * + * @return the ResultSet's SQL cursor name. + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return connection.getCursorName(); + } + + /** + * The numbers, types and properties of a ResultSet's columns are + * provided by the getMetaData method + * + * @return a description of the ResultSet's columns + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + return new ResultSetMetaData(rows, fields); + } + + /** + * Get the value of a column in the current row as a Java object + * + *

This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + *

This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException + { + Field field; + + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + field = fields[columnIndex - 1]; + + // some fields can be null, mainly from those returned by MetaData methods + if (field == null) + { + wasNullFlag = true; + return null; + } + + switch (field.getSQLType()) + { + case Types.BIT: + return new Boolean(getBoolean(columnIndex)); + case Types.SMALLINT: + return new Integer(getInt(columnIndex)); + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + case Types.BIGINT: + return new Long(getLong(columnIndex)); + case Types.NUMERIC: + return getBigDecimal + (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff)); + case Types.REAL: + return new Float(getFloat(columnIndex)); + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + case Types.BINARY: + case Types.VARBINARY: + return getBytes(columnIndex); + default: + String type = field.getPGType(); + // if the backend doesn't know the type then coerce to String + if (type.equals("unknown")) + { + return getString(columnIndex); + } + else + { + return connection.getObject(field.getPGType(), getString(columnIndex)); + } + } + } + + /** + * Get the value of a column in the current row as a Java object + * + *

This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + *

This method may also be used to read database specific abstract + * data types. + * + * @param columnName is the SQL name of the column + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException + { + return getObject(findColumn(columnName)); + } + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName the name of the column + * @return the column index + * @exception SQLException if a database access error occurs + */ + public int findColumn(String columnName) throws SQLException + { + int i; + + final int flen = fields.length; + for (i = 0 ; i < flen; ++i) + if (fields[i].getName().equalsIgnoreCase(columnName)) + return (i + 1); + throw new PSQLException ("postgresql.res.colname", columnName); + } + + // ** JDBC 2 Extensions ** + + public boolean absolute(int index) throws SQLException + { + // index is 1-based, but internally we use 0-based indices + int internalIndex; + + if (index == 0) + throw new SQLException("Cannot move to index of 0"); + + final int rows_size = rows.size(); + + //if index<0, count from the end of the result set, but check + //to be sure that it is not beyond the first index + if (index < 0) + if (index >= -rows_size) + internalIndex = rows_size + index; + else + { + beforeFirst(); + return false; + } + + //must be the case that index>0, + //find the correct place, assuming that + //the index is not too large + if (index <= rows_size) + internalIndex = index - 1; + else + { + afterLast(); + return false; + } + + current_row = internalIndex; + this_row = (byte [][])rows.elementAt(internalIndex); + return true; + } + + public void afterLast() throws SQLException + { + final int rows_size = rows.size(); + if (rows_size > 0) + current_row = rows_size; + } + + public void beforeFirst() throws SQLException + { + if (rows.size() > 0) + current_row = -1; + } + + public void cancelRowUpdates() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void deleteRow() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public boolean first() throws SQLException + { + if (rows.size() <= 0) + return false; + current_row = 0; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + public java.sql.Array getArray(String colName) throws SQLException + { + return getArray(findColumn(colName)); + } + + public java.sql.Array getArray(int i) throws SQLException + { + wasNullFlag = (this_row[i - 1] == null); + if (wasNullFlag) + return null; + + if (i < 1 || i > fields.length) + throw new PSQLException("postgresql.res.colrange"); + return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], this ); + } + + public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException + { + return getBigDecimal(columnIndex, -1); + } + + public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException + { + return getBigDecimal(findColumn(columnName)); + } + + public Blob getBlob(String columnName) throws SQLException + { + return getBlob(findColumn(columnName)); + } + + public Blob getBlob(int i) throws SQLException + { + return new org.postgresql.largeobject.PGblob(connection, getInt(i)); + } + + public java.io.Reader getCharacterStream(String columnName) throws SQLException + { + return getCharacterStream(findColumn(columnName)); + } + + public java.io.Reader getCharacterStream(int i) throws SQLException + { + wasNullFlag = (this_row[i - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + return new CharArrayReader(getString(i).toCharArray()); + } + else + { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + Encoding encoding = connection.getEncoding(); + InputStream input = getBinaryStream(i); + return encoding.getDecodingReader(input); + } + } + + /** + * New in 7.1 + */ + public Clob getClob(String columnName) throws SQLException + { + return getClob(findColumn(columnName)); + } + + /** + * New in 7.1 + */ + public Clob getClob(int i) throws SQLException + { + return new org.postgresql.largeobject.PGclob(connection, getInt(i)); + } + + public int getConcurrency() throws SQLException + { + // New in 7.1 - The standard ResultSet class will now return + // CONCUR_READ_ONLY. A sub-class will overide this if the query was + // updateable. + return CONCUR_READ_ONLY; + } + + public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException + { + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getDate(i); + } + + public Time getTime(int i, java.util.Calendar cal) throws SQLException + { + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getTime()? + // for now... + return getTime(i); + } + + public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException + { + // new in 7.1: If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getTimestamp(i); + } + + public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException + { + return getDate(findColumn(c), cal); + } + + public Time getTime(String c, java.util.Calendar cal) throws SQLException + { + return getTime(findColumn(c), cal); + } + + public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException + { + return getTimestamp(findColumn(c), cal); + } + + public int getFetchDirection() throws SQLException + { + // new in 7.1: PostgreSQL normally sends rows first->last + return FETCH_FORWARD; + } + + public int getFetchSize() throws SQLException + { + // new in 7.1: In this implementation we return the entire result set, so + // here return the number of rows we have. Sub-classes can return a proper + // value + return rows.size(); + } + + public Object getObject(String columnName, java.util.Map map) throws SQLException + { + return getObject(findColumn(columnName), map); + } + + /** + * This checks against map for the type of column i, and if found returns + * an object based on that mapping. The class must implement the SQLData + * interface. + */ + public Object getObject(int i, java.util.Map map) throws SQLException + { + /* In preparation + SQLInput s = new PSQLInput(this,i); + String t = getTypeName(i); + SQLData o = (SQLData) map.get(t); + // If the type is not in the map, then pass to the existing code + if(o==null) + return getObject(i); + o.readSQL(s,t); + return o; + */throw org.postgresql.Driver.notImplemented(); + } + + public Ref getRef(String columnName) throws SQLException + { + return getRef(findColumn(columnName)); + } + + public Ref getRef(int i) throws SQLException + { + // new in 7.1: The backend doesn't yet have SQL3 REF types + throw new PSQLException("postgresql.psqlnotimp"); + } + + public int getRow() throws SQLException + { + return current_row + 1; + } + + // This one needs some thought, as not all ResultSets come from a statement + public java.sql.Statement getStatement() throws SQLException + { + return statement; + } + + public int getType() throws SQLException + { + // New in 7.1. This implementation allows scrolling but is not able to + // see any changes. Sub-classes may overide this to return a more + // meaningful result. + return TYPE_SCROLL_INSENSITIVE; + } + + public void insertRow() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public boolean isAfterLast() throws SQLException + { + final int rows_size = rows.size(); + return (current_row >= rows_size && rows_size > 0); + } + + public boolean isBeforeFirst() throws SQLException + { + return (current_row < 0 && rows.size() > 0); + } + + public boolean isFirst() throws SQLException + { + return (current_row == 0 && rows.size() >= 0); + } + + public boolean isLast() throws SQLException + { + final int rows_size = rows.size(); + return (current_row == rows_size - 1 && rows_size > 0); + } + + public boolean last() throws SQLException + { + final int rows_size = rows.size(); + if (rows_size <= 0) + return false; + current_row = rows_size - 1; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + public void moveToCurrentRow() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void moveToInsertRow() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public boolean previous() throws SQLException + { + if (--current_row < 0) + return false; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + public void refreshRow() throws SQLException + { + throw new PSQLException("postgresql.notsensitive"); + } + + // Peter: Implemented in 7.0 + public boolean relative(int rows) throws SQLException + { + //have to add 1 since absolute expects a 1-based index + return absolute(current_row + 1 + rows); + } + + public boolean rowDeleted() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + return false; // javac complains about not returning a value! + } + + public boolean rowInserted() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + return false; // javac complains about not returning a value! + } + + public boolean rowUpdated() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + return false; // javac complains about not returning a value! + } + + public void setFetchDirection(int direction) throws SQLException + { + // In 7.1, the backend doesn't yet support this + throw new PSQLException("postgresql.psqlnotimp"); + } + + public void setFetchSize(int rows) throws SQLException + { + // Sub-classes should implement this as part of their cursor support + throw org.postgresql.Driver.notImplemented(); + } + + public void updateAsciiStream(int columnIndex, + java.io.InputStream x, + int length + ) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateAsciiStream(String columnName, + java.io.InputStream x, + int length + ) throws SQLException + { + updateAsciiStream(findColumn(columnName), x, length); + } + + public void updateBigDecimal(int columnIndex, + java.math.BigDecimal x + ) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateBigDecimal(String columnName, + java.math.BigDecimal x + ) throws SQLException + { + updateBigDecimal(findColumn(columnName), x); + } + + public void updateBinaryStream(int columnIndex, + java.io.InputStream x, + int length + ) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateBinaryStream(String columnName, + java.io.InputStream x, + int length + ) throws SQLException + { + updateBinaryStream(findColumn(columnName), x, length); + } + + public void updateBoolean(int columnIndex, boolean x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateBoolean(String columnName, boolean x) throws SQLException + { + updateBoolean(findColumn(columnName), x); + } + + public void updateByte(int columnIndex, byte x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateByte(String columnName, byte x) throws SQLException + { + updateByte(findColumn(columnName), x); + } + + public void updateBytes(String columnName, byte[] x) throws SQLException + { + updateBytes(findColumn(columnName), x); + } + + public void updateBytes(int columnIndex, byte[] x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateCharacterStream(int columnIndex, + java.io.Reader x, + int length + ) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateCharacterStream(String columnName, + java.io.Reader x, + int length + ) throws SQLException + { + updateCharacterStream(findColumn(columnName), x, length); + } + + public void updateDate(int columnIndex, java.sql.Date x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateDate(String columnName, java.sql.Date x) throws SQLException + { + updateDate(findColumn(columnName), x); + } + + public void updateDouble(int columnIndex, double x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateDouble(String columnName, double x) throws SQLException + { + updateDouble(findColumn(columnName), x); + } + + public void updateFloat(int columnIndex, float x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateFloat(String columnName, float x) throws SQLException + { + updateFloat(findColumn(columnName), x); + } + + public void updateInt(int columnIndex, int x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateInt(String columnName, int x) throws SQLException + { + updateInt(findColumn(columnName), x); + } + + public void updateLong(int columnIndex, long x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateLong(String columnName, long x) throws SQLException + { + updateLong(findColumn(columnName), x); + } + + public void updateNull(int columnIndex) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateNull(String columnName) throws SQLException + { + updateNull(findColumn(columnName)); + } + + public void updateObject(int columnIndex, Object x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateObject(String columnName, Object x) throws SQLException + { + updateObject(findColumn(columnName), x); + } + + public void updateObject(int columnIndex, Object x, int scale) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateObject(String columnName, Object x, int scale) throws SQLException + { + updateObject(findColumn(columnName), x, scale); + } + + public void updateRow() throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateShort(int columnIndex, short x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateShort(String columnName, short x) throws SQLException + { + updateShort(findColumn(columnName), x); + } + + public void updateString(int columnIndex, String x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateString(String columnName, String x) throws SQLException + { + updateString(findColumn(columnName), x); + } + + public void updateTime(int columnIndex, Time x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateTime(String columnName, Time x) throws SQLException + { + updateTime(findColumn(columnName), x); + } + + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException + { + // only sub-classes implement CONCUR_UPDATEABLE + notUpdateable(); + } + + public void updateTimestamp(String columnName, Timestamp x) throws SQLException + { + updateTimestamp(findColumn(columnName), x); + } + + // helper method. Throws an SQLException when an update is not possible + public void notUpdateable() throws SQLException + { + throw new PSQLException("postgresql.noupdate"); + } + + /** + * This is called by Statement to register itself with this statement. + * It's used currently by getStatement() but may also with the new core + * package. + */ + public void setStatement(org.postgresql.jdbc2.Statement statement) + { + this.statement = statement; + } //----------------- Formatting Methods ------------------- @@ -1417,8 +1454,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu try { return Integer.parseInt(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badint",s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badint", s); } } return 0; // SQL NULL @@ -1431,8 +1470,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu try { return Long.parseLong(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badlong",s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badlong", s); } } return 0; // SQL NULL @@ -1446,15 +1487,20 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu try { val = new BigDecimal(s); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badbigdec",s); } - if (scale==-1) return val; + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badbigdec", s); + } + if (scale == -1) + return val; try { return val.setScale(scale); - } catch (ArithmeticException e) { - throw new PSQLException ("postgresql.res.badbigdec",s); + } + catch (ArithmeticException e) + { + throw new PSQLException ("postgresql.res.badbigdec", s); } } return null; // SQL NULL @@ -1467,8 +1513,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu try { return Float.valueOf(s).floatValue(); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.badfloat",s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.badfloat", s); } } return 0; // SQL NULL @@ -1481,8 +1529,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu try { return Double.valueOf(s).doubleValue(); - } catch (NumberFormatException e) { - throw new PSQLException ("postgresql.res.baddouble",s); + } + catch (NumberFormatException e) + { + throw new PSQLException ("postgresql.res.baddouble", s); } } return 0; // SQL NULL @@ -1490,28 +1540,31 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public static java.sql.Date toDate(String s) throws SQLException { - if(s==null) + if (s == null) return null; return java.sql.Date.valueOf(s); } public static Time toTime(String s) throws SQLException { - if(s==null) + if (s == null) return null; // SQL NULL return java.sql.Time.valueOf(s); } public static Timestamp toTimestamp(String s, ResultSet resultSet) throws SQLException { - if(s==null) + if (s == null) return null; boolean subsecond; //if string contains a '.' we have fractional seconds - if (s.indexOf('.') == -1) { + if (s.indexOf('.') == -1) + { subsecond = false; - } else { + } + else + { subsecond = true; } @@ -1520,49 +1573,68 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu //and java expects three digits if fractional seconds are present instead of two for postgres //so this code strips off timezone info and adds on the GMT+/-... //as well as adds a third digit for partial seconds if necessary - synchronized(resultSet) { + synchronized (resultSet) + { // We must be synchronized here incase more theads access the ResultSet // bad practice but possible. Anyhow this is to protect sbuf and // SimpleDateFormat objects // First time? - if(resultSet.sbuf==null) + if (resultSet.sbuf == null) resultSet.sbuf = new StringBuffer(); resultSet.sbuf.setLength(0); resultSet.sbuf.append(s); - char sub = resultSet.sbuf.charAt(resultSet.sbuf.length()-3); - if (sub == '+' || sub == '-') { - resultSet.sbuf.setLength(resultSet.sbuf.length()-3); - if (subsecond) { - resultSet.sbuf.append('0').append("GMT").append(s.substring(s.length()-3)).append(":00"); - } else { - resultSet.sbuf.append("GMT").append(s.substring(s.length()-3)).append(":00"); + char sub = resultSet.sbuf.charAt(resultSet.sbuf.length() - 3); + if (sub == '+' || sub == '-') + { + resultSet.sbuf.setLength(resultSet.sbuf.length() - 3); + if (subsecond) + { + resultSet.sbuf.append('0').append("GMT").append(s.substring(s.length() - 3)).append(":00"); } - } else if (subsecond) { + else + { + resultSet.sbuf.append("GMT").append(s.substring(s.length() - 3)).append(":00"); + } + } + else if (subsecond) + { resultSet.sbuf.append('0'); } // could optimize this a tad to remove too many object creations... SimpleDateFormat df = null; - if (resultSet.sbuf.length()>23 && subsecond) { + if (resultSet.sbuf.length() > 23 && subsecond) + { df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSzzzzzzzzz"); - } else if (resultSet.sbuf.length()>23 && !subsecond) { + } + else if (resultSet.sbuf.length() > 23 && !subsecond) + { df = new SimpleDateFormat("yyyy-MM-dd HH:mm:sszzzzzzzzz"); - } else if (resultSet.sbuf.length()>10 && subsecond) { + } + else if (resultSet.sbuf.length() > 10 && subsecond) + { df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - } else if (resultSet.sbuf.length()>10 && !subsecond) { + } + else if (resultSet.sbuf.length() > 10 && !subsecond) + { df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - } else { + } + else + { df = new SimpleDateFormat("yyyy-MM-dd"); } - try { + try + { return new Timestamp(df.parse(resultSet.sbuf.toString()).getTime()); - } catch(ParseException e) { - throw new PSQLException("postgresql.res.badtimestamp",new Integer(e.getErrorOffset()),s); + } + catch (ParseException e) + { + throw new PSQLException("postgresql.res.badtimestamp", new Integer(e.getErrorOffset()), s); } } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSetMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSetMetaData.java index 49ba9c72ed..5d6abf814a 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSetMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/ResultSetMetaData.java @@ -17,451 +17,464 @@ import org.postgresql.util.*; * * @see java.sql.ResultSetMetaData */ -public class ResultSetMetaData implements java.sql.ResultSetMetaData +public class ResultSetMetaData implements java.sql.ResultSetMetaData { - Vector rows; - Field[] fields; - - /** - * Initialise for a result with a tuple set and - * a field descriptor set - * - * @param rows the Vector of rows returned by the ResultSet - * @param fields the array of field descriptors - */ - public ResultSetMetaData(Vector rows, Field[] fields) - { - this.rows = rows; - this.fields = fields; - } - - /** - * Whats the number of columns in the ResultSet? - * - * @return the number - * @exception SQLException if a database access error occurs - */ - public int getColumnCount() throws SQLException - { - return fields.length; - } - - /** - * Is the column automatically numbered (and thus read-only) - * I believe that PostgreSQL does not support this feature. - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isAutoIncrement(int column) throws SQLException - { - return false; - } - - /** - * Does a column's case matter? ASSUMPTION: Any field that is - * not obviously case insensitive is assumed to be case sensitive - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCaseSensitive(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; - default: - return true; - } - } - - /** - * Can the column be used in a WHERE clause? Basically for - * this, I split the functions into two types: recognised - * types (which are always useable), and OTHER types (which - * may or may not be useable). The OTHER types, for now, I - * will assume they are useable. We should really query the - * catalog to see if they are useable. - * - * @param column the first column is 1, the second is 2... - * @return true if they can be used in a WHERE clause - * @exception SQLException if a database access error occurs - */ - public boolean isSearchable(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - // This switch is pointless, I know - but it is a set-up - // for further expansion. - switch (sql_type) - { - case Types.OTHER: - return true; - default: - return true; - } - } - - /** - * Is the column a cash value? 6.1 introduced the cash/money - * type, which haven't been incorporated as of 970414, so I - * just check the type name for both 'cash' and 'money' - * - * @param column the first column is 1, the second is 2... - * @return true if its a cash column - * @exception SQLException if a database access error occurs - */ - public boolean isCurrency(int column) throws SQLException - { - String type_name = getField(column).getPGType(); - - return type_name.equals("cash") || type_name.equals("money"); - } - - /** - * Indicates the nullability of values in the designated column. - * - * @param column the first column is 1, the second is 2... - * @return one of the columnNullable values - * @exception SQLException if a database access error occurs - */ - public int isNullable(int column) throws SQLException - { - /* - * TODO This needs a real implementation, taking into account columns - * defined with NOT NULL or PRIMARY KEY, CHECK constraints, views, - * functions etc. + Vector rows; + Field[] fields; + + /** + * Initialise for a result with a tuple set and + * a field descriptor set + * + * @param rows the Vector of rows returned by the ResultSet + * @param fields the array of field descriptors + */ + public ResultSetMetaData(Vector rows, Field[] fields) + { + this.rows = rows; + this.fields = fields; + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * @exception SQLException if a database access error occurs + */ + public int getColumnCount() throws SQLException + { + return fields.length; + } + + /** + * Is the column automatically numbered (and thus read-only) + * I believe that PostgreSQL does not support this feature. + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException + { + return false; + } + + /** + * Does a column's case matter? ASSUMPTION: Any field that is + * not obviously case insensitive is assumed to be case sensitive + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + default: + return true; + } + } + + /** + * Can the column be used in a WHERE clause? Basically for + * this, I split the functions into two types: recognised + * types (which are always useable), and OTHER types (which + * may or may not be useable). The OTHER types, for now, I + * will assume they are useable. We should really query the + * catalog to see if they are useable. + * + * @param column the first column is 1, the second is 2... + * @return true if they can be used in a WHERE clause + * @exception SQLException if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + // This switch is pointless, I know - but it is a set-up + // for further expansion. + switch (sql_type) + { + case Types.OTHER: + return true; + default: + return true; + } + } + + /** + * Is the column a cash value? 6.1 introduced the cash/money + * type, which haven't been incorporated as of 970414, so I + * just check the type name for both 'cash' and 'money' + * + * @param column the first column is 1, the second is 2... + * @return true if its a cash column + * @exception SQLException if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException + { + String type_name = getField(column).getPGType(); + + return type_name.equals("cash") || type_name.equals("money"); + } + + /** + * Indicates the nullability of values in the designated column. + * + * @param column the first column is 1, the second is 2... + * @return one of the columnNullable values + * @exception SQLException if a database access error occurs + */ + public int isNullable(int column) throws SQLException + { + /* + * TODO This needs a real implementation, taking into account columns + * defined with NOT NULL or PRIMARY KEY, CHECK constraints, views, + * functions etc. + */ + return columnNullableUnknown; + } + + /** + * Is the column a signed number? In PostgreSQL, all numbers + * are signed, so this is trivial. However, strings are not + * signed (duh!) + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return true; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; // I don't know about these? + default: + return false; + } + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column the first column is 1, the second is 2, etc. + * @return the maximum width + * @exception SQLException if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException + { + Field f = getField(column); + String type_name = f.getPGType(); + int sql_type = f.getSQLType(); + int typmod = f.getMod(); + + // I looked at other JDBC implementations and couldn't find a consistent + // interpretation of the "display size" for numeric values, so this is our's + // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de + + // fixed length data types + if (type_name.equals( "int2" )) + return 6; // -32768 to +32768 (5 digits and a sign) + if (type_name.equals( "int4" ) + || type_name.equals( "oid" )) + return 11; // -2147483648 to +2147483647 + if (type_name.equals( "int8" )) + return 20; // -9223372036854775808 to +9223372036854775807 + if (type_name.equals( "money" )) + return 12; // MONEY = DECIMAL(9,2) + if (type_name.equals( "float4" )) + return 11; // i checked it out ans wasn't able to produce more than 11 digits + if (type_name.equals( "float8" )) + return 20; // dito, 20 + if (type_name.equals( "char" )) + return 1; + if (type_name.equals( "bool" )) + return 1; + if (type_name.equals( "date" )) + return 14; // "01/01/4713 BC" - "31/12/32767 AD" + if (type_name.equals( "time" )) + return 8; // 00:00:00-23:59:59 + if (type_name.equals( "timestamp" )) + return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 + + // variable length fields + typmod -= 4; + if (type_name.equals( "bpchar" ) + || type_name.equals( "varchar" )) + return typmod; // VARHDRSZ=sizeof(int32)=4 + if (type_name.equals( "numeric" )) + return ( (typmod >> 16) & 0xffff ) + + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) + + // if we don't know better + return f.getLength(); + } + + /** + * What is the suggested column title for use in printouts and + * displays? We suggest the ColumnName! + * + * @param column the first column is 1, the second is 2, etc. + * @return the column label + * @exception SQLException if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException + { + return getColumnName(column); + } + + /** + * What's a column's name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the column name + * @exception SQLException if a database access error occurs */ - return columnNullableUnknown; - } - - /** - * Is the column a signed number? In PostgreSQL, all numbers - * are signed, so this is trivial. However, strings are not - * signed (duh!) - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isSigned(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - return true; - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; // I don't know about these? - default: - return false; - } - } - - /** - * What is the column's normal maximum width in characters? - * - * @param column the first column is 1, the second is 2, etc. - * @return the maximum width - * @exception SQLException if a database access error occurs - */ - public int getColumnDisplaySize(int column) throws SQLException - { - Field f = getField(column); - String type_name = f.getPGType(); - int sql_type = f.getSQLType(); - int typmod = f.getMod(); - - // I looked at other JDBC implementations and couldn't find a consistent - // interpretation of the "display size" for numeric values, so this is our's - // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de - - // fixed length data types - if (type_name.equals( "int2" )) return 6; // -32768 to +32768 (5 digits and a sign) - if (type_name.equals( "int4" ) - || type_name.equals( "oid" )) return 11; // -2147483648 to +2147483647 - if (type_name.equals( "int8" )) return 20; // -9223372036854775808 to +9223372036854775807 - if (type_name.equals( "money" )) return 12; // MONEY = DECIMAL(9,2) - if (type_name.equals( "float4" )) return 11; // i checked it out ans wasn't able to produce more than 11 digits - if (type_name.equals( "float8" )) return 20; // dito, 20 - if (type_name.equals( "char" )) return 1; - if (type_name.equals( "bool" )) return 1; - if (type_name.equals( "date" )) return 14; // "01/01/4713 BC" - "31/12/32767 AD" - if (type_name.equals( "time" )) return 8; // 00:00:00-23:59:59 - if (type_name.equals( "timestamp" )) return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 - - // variable length fields - typmod -= 4; - if (type_name.equals( "bpchar" ) - || type_name.equals( "varchar" )) return typmod; // VARHDRSZ=sizeof(int32)=4 - if (type_name.equals( "numeric" )) return ( (typmod >>16) & 0xffff ) - + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) - - // if we don't know better - return f.getLength(); - } - - /** - * What is the suggested column title for use in printouts and - * displays? We suggest the ColumnName! - * - * @param column the first column is 1, the second is 2, etc. - * @return the column label - * @exception SQLException if a database access error occurs - */ - public String getColumnLabel(int column) throws SQLException - { - return getColumnName(column); - } - - /** - * What's a column's name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the column name - * @exception SQLException if a database access error occurs - */ - public String getColumnName(int column) throws SQLException - { - Field f = getField(column); - if(f!=null) - return f.getName(); - return "field"+column; - } - - /** - * What is a column's table's schema? This relies on us knowing - * the table name....which I don't know how to do as yet. The - * JDBC specification allows us to return "" if this is not - * applicable. - * - * @param column the first column is 1, the second is 2... - * @return the Schema - * @exception SQLException if a database access error occurs - */ - public String getSchemaName(int column) throws SQLException - { - return ""; - } - - /** - * What is a column's number of decimal digits. - * - * @param column the first column is 1, the second is 2... - * @return the precision - * @exception SQLException if a database access error occurs - */ - public int getPrecision(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 5; - case Types.INTEGER: - return 10; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - case Types.VARCHAR: - return 0; - case Types.NUMERIC: - Field f = getField(column); - if(f != null) - return ((0xFFFF0000)&f.getMod())>>16; - else - return 0; - default: - return 0; - } - } - - /** - * What is a column's number of digits to the right of the - * decimal point? - * - * @param column the first column is 1, the second is 2... - * @return the scale - * @exception SQLException if a database access error occurs - */ - public int getScale(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 0; - case Types.INTEGER: - return 0; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - case Types.VARCHAR: - return 0; - case Types.NUMERIC: - Field f = getField(column); - if(f != null) - return (((0x0000FFFF)&f.getMod())-4); - else - return 0; - default: - return 0; - } - } - - /** - * Whats a column's table's name? How do I find this out? Both - * getSchemaName() and getCatalogName() rely on knowing the table - * Name, so we need this before we can work on them. - * - * @param column the first column is 1, the second is 2... - * @return column name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - public String getTableName(int column) throws SQLException - { - return ""; - } - - /** - * What's a column's table's catalog name? As with getSchemaName(), - * we can say that if getTableName() returns n/a, then we can too - - * otherwise, we need to work on it. - * - * @param column the first column is 1, the second is 2... - * @return catalog name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - public String getCatalogName(int column) throws SQLException - { - return ""; - } - - /** - * What is a column's SQL Type? (java.sql.Type int) - * - * @param column the first column is 1, the second is 2, etc. - * @return the java.sql.Type value - * @exception SQLException if a database access error occurs - * @see org.postgresql.Field#getSQLType - * @see java.sql.Types - */ - public int getColumnType(int column) throws SQLException - { - return getField(column).getSQLType(); - } - - /** - * Whats is the column's data source specific type name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the type name - * @exception SQLException if a database access error occurs - */ - public String getColumnTypeName(int column) throws SQLException - { - return getField(column).getPGType(); - } - - /** - * Is the column definitely not writable? In reality, we would - * have to check the GRANT/REVOKE stuff for this to be effective, - * and I haven't really looked into that yet, so this will get - * re-visited. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly(int column) throws SQLException - { - return false; - } - - /** - * Is it possible for a write on the column to succeed? Again, we - * would in reality have to check the GRANT/REVOKE stuff, which - * I haven't worked with as yet. However, if it isn't ReadOnly, then - * it is obviously writable. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isWritable(int column) throws SQLException - { - return !isReadOnly(column); - } - - /** - * Will a write on this column definately succeed? Hmmm...this - * is a bad one, since the two preceding functions have not been - * really defined. I cannot tell is the short answer. I thus - * return isWritable() just to give us an idea. - * - * @param column the first column is 1, the second is 2, etc.. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isDefinitelyWritable(int column) throws SQLException - { - return false; - } - - // ******************************************************** - // END OF PUBLIC INTERFACE - // ******************************************************** - - /** - * For several routines in this package, we need to convert - * a columnIndex into a Field[] descriptor. Rather than do - * the same code several times, here it is. - * - * @param columnIndex the first column is 1, the second is 2... - * @return the Field description - * @exception SQLException if a database access error occurs - */ - private Field getField(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new PSQLException("postgresql.res.colrange"); - return fields[columnIndex - 1]; - } - - // ** JDBC 2 Extensions ** - - // This can hook into our PG_Object mechanism - public String getColumnClassName(int column) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - + public String getColumnName(int column) throws SQLException + { + Field f = getField(column); + if (f != null) + return f.getName(); + return "field" + column; + } + + /** + * What is a column's table's schema? This relies on us knowing + * the table name....which I don't know how to do as yet. The + * JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column the first column is 1, the second is 2... + * @return the Schema + * @exception SQLException if a database access error occurs + */ + public String getSchemaName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's number of decimal digits. + * + * @param column the first column is 1, the second is 2... + * @return the precision + * @exception SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 5; + case Types.INTEGER: + return 10; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + case Types.NUMERIC: + Field f = getField(column); + if (f != null) + return ((0xFFFF0000)&f.getMod()) >> 16; + else + return 0; + default: + return 0; + } + } + + /** + * What is a column's number of digits to the right of the + * decimal point? + * + * @param column the first column is 1, the second is 2... + * @return the scale + * @exception SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 0; + case Types.INTEGER: + return 0; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + case Types.VARCHAR: + return 0; + case Types.NUMERIC: + Field f = getField(column); + if (f != null) + return (((0x0000FFFF)&f.getMod()) - 4); + else + return 0; + default: + return 0; + } + } + + /** + * Whats a column's table's name? How do I find this out? Both + * getSchemaName() and getCatalogName() rely on knowing the table + * Name, so we need this before we can work on them. + * + * @param column the first column is 1, the second is 2... + * @return column name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getTableName(int column) throws SQLException + { + return ""; + } + + /** + * What's a column's table's catalog name? As with getSchemaName(), + * we can say that if getTableName() returns n/a, then we can too - + * otherwise, we need to work on it. + * + * @param column the first column is 1, the second is 2... + * @return catalog name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getCatalogName(int column) throws SQLException + { + return ""; + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column the first column is 1, the second is 2, etc. + * @return the java.sql.Type value + * @exception SQLException if a database access error occurs + * @see org.postgresql.Field#getSQLType + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException + { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the type name + * @exception SQLException if a database access error occurs + */ + public String getColumnTypeName(int column) throws SQLException + { + return getField(column).getPGType(); + } + + /** + * Is the column definitely not writable? In reality, we would + * have to check the GRANT/REVOKE stuff for this to be effective, + * and I haven't really looked into that yet, so this will get + * re-visited. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException + { + return false; + } + + /** + * Is it possible for a write on the column to succeed? Again, we + * would in reality have to check the GRANT/REVOKE stuff, which + * I haven't worked with as yet. However, if it isn't ReadOnly, then + * it is obviously writable. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException + { + return !isReadOnly(column); + } + + /** + * Will a write on this column definately succeed? Hmmm...this + * is a bad one, since the two preceding functions have not been + * really defined. I cannot tell is the short answer. I thus + * return isWritable() just to give us an idea. + * + * @param column the first column is 1, the second is 2, etc.. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException + { + return false; + } + + // ******************************************************** + // END OF PUBLIC INTERFACE + // ******************************************************** + + /** + * For several routines in this package, we need to convert + * a columnIndex into a Field[] descriptor. Rather than do + * the same code several times, here it is. + * + * @param columnIndex the first column is 1, the second is 2... + * @return the Field description + * @exception SQLException if a database access error occurs + */ + private Field getField(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new PSQLException("postgresql.res.colrange"); + return fields[columnIndex - 1]; + } + + // ** JDBC 2 Extensions ** + + // This can hook into our PG_Object mechanism + public String getColumnClassName(int column) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Statement.java index 13dccefd39..232f9d41d1 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Statement.java @@ -16,7 +16,7 @@ import org.postgresql.util.*; *

Only one ResultSet per Statement can be open at any point in time. * Therefore, if the reading of one ResultSet is interleaved with the * reading of another, each must have been generated by different - * Statements. All statement execute methods implicitly close a + * Statements. All statement execute methods implicitly close a * statement's current ResultSet if an open one exists. * * @see java.sql.Statement @@ -24,10 +24,10 @@ import org.postgresql.util.*; */ public class Statement extends org.postgresql.Statement implements java.sql.Statement { - private Connection connection; // The connection who created us - private Vector batch=null; - private int resultsettype; // the resultset type to return - private int concurrency; // is it updateable or not? + private Connection connection; // The connection who created us + private Vector batch = null; + private int resultsettype; // the resultset type to return + private int concurrency; // is it updateable or not? /** * Constructor for a Statement. It simply sets the connection @@ -38,8 +38,8 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat public Statement (Connection c) { connection = c; - resultsettype = java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; - concurrency = java.sql.ResultSet.CONCUR_READ_ONLY; + resultsettype = java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + concurrency = java.sql.ResultSet.CONCUR_READ_ONLY; } /** @@ -51,12 +51,12 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat */ public java.sql.ResultSet executeQuery(String sql) throws SQLException { - this.execute(sql); - while (result != null && !((org.postgresql.ResultSet)result).reallyResultSet()) - result = ((org.postgresql.ResultSet)result).getNext(); - if (result == null) - throw new PSQLException("postgresql.stat.noresult"); - return result; + this.execute(sql); + while (result != null && !((org.postgresql.ResultSet)result).reallyResultSet()) + result = ((org.postgresql.ResultSet)result).getNext(); + if (result == null) + throw new PSQLException("postgresql.stat.noresult"); + return result; } /** @@ -78,7 +78,7 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat /** * setCursorName defines the SQL cursor name that will be used by - * subsequent execute methods. This name can then be used in SQL + * subsequent execute methods. This name can then be used in SQL * positioned update/delete statements to identify the current row * in the ResultSet generated by this statement. If a database * doesn't support positioned update/delete, this method is a @@ -86,10 +86,10 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat * *

Note: By definition, positioned update/delete execution * must be done by a different Statement than the one which - * generated the ResultSet being used for positioning. Also, cursor + * generated the ResultSet being used for positioning. Also, cursor * names must be unique within a Connection. * - *

We throw an additional constriction. There can only be one + *

We throw an additional constriction. There can only be one * cursor active at any one time. * * @param name the new cursor name @@ -103,36 +103,37 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat /** * Execute a SQL statement that may return multiple results. We * don't have to worry about this since we do not support multiple - * ResultSets. You can use getResultSet or getUpdateCount to + * ResultSets. You can use getResultSet or getUpdateCount to * retrieve the result. * * @param sql any SQL statement * @return true if the next result is a ResulSet, false if it is - * an update count or there are no more results + * an update count or there are no more results * @exception SQLException if a database access error occurs */ - public boolean execute(String sql) throws SQLException - { - if (escapeProcessing) - sql = escapeSQL(sql); + public boolean execute(String sql) throws SQLException + { + if (escapeProcessing) + sql = escapeSQL(sql); - // New in 7.1, if we have a previous resultset then force it to close - // This brings us nearer to compliance, and helps memory management. - // Internal stuff will call ExecSQL directly, bypassing this. - if(result!=null) { - java.sql.ResultSet rs = getResultSet(); - if(rs!=null) - rs.close(); - } + // New in 7.1, if we have a previous resultset then force it to close + // This brings us nearer to compliance, and helps memory management. + // Internal stuff will call ExecSQL directly, bypassing this. + if (result != null) + { + java.sql.ResultSet rs = getResultSet(); + if (rs != null) + rs.close(); + } - // New in 7.1, pass Statement so that ExecSQL can customise to it - result = connection.ExecSQL(sql,this); + // New in 7.1, pass Statement so that ExecSQL can customise to it + result = connection.ExecSQL(sql, this); - // New in 7.1, required for ResultSet.getStatement() to work - ((org.postgresql.jdbc2.ResultSet)result).setStatement(this); + // New in 7.1, required for ResultSet.getStatement() to work + ((org.postgresql.jdbc2.ResultSet)result).setStatement(this); - return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); - } + return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); + } /** * getUpdateCount returns the current result as an update count, @@ -144,8 +145,10 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat */ public int getUpdateCount() throws SQLException { - if (result == null) return -1; - if (((org.postgresql.ResultSet)result).reallyResultSet()) return -1; + if (result == null) + return -1; + if (((org.postgresql.ResultSet)result).reallyResultSet()) + return -1; return ((org.postgresql.ResultSet)result).getResultCount(); } @@ -162,98 +165,103 @@ public class Statement extends org.postgresql.Statement implements java.sql.Stat return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet()); } - // ** JDBC 2 Extensions ** + // ** JDBC 2 Extensions ** - public void addBatch(String sql) throws SQLException - { - if(batch==null) - batch=new Vector(); - batch.addElement(sql); - } + public void addBatch(String sql) throws SQLException + { + if (batch == null) + batch = new Vector(); + batch.addElement(sql); + } - public void clearBatch() throws SQLException - { - if(batch!=null) - batch.removeAllElements(); - } + public void clearBatch() throws SQLException + { + if (batch != null) + batch.removeAllElements(); + } - public int[] executeBatch() throws SQLException - { - if(batch==null) - batch=new Vector(); - int size=batch.size(); - int[] result=new int[size]; - int i=0; - try { - for(i=0;i= buffer.length) { - buffer=lo.read(bsize); - bpos=0; - } - - // Handle EOF - if(bpos >= buffer.length) { - return -1; - } - - int ret = (buffer[bpos] & 0x7F); - if ((buffer[bpos] &0x80) == 0x80) { - ret |= 0x80; - } - - bpos++; - - return ret; - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } - - - /** - * Closes this input stream and releases any system resources associated - * with the stream. - * - *

The close method of InputStream does - * nothing. - * - * @exception IOException if an I/O error occurs. - */ - public void close() throws IOException { - try { - lo.close(); - lo=null; - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } - - /** - * Marks the current position in this input stream. A subsequent call to - * the reset method repositions this stream at the last marked - * position so that subsequent reads re-read the same bytes. - * - *

The readlimit arguments tells this input stream to - * allow that many bytes to be read before the mark position gets - * invalidated. - * - *

The general contract of mark is that, if the method - * markSupported returns true, the stream somehow - * remembers all the bytes read after the call to mark and - * stands ready to supply those same bytes again if and whenever the method - * reset is called. However, the stream is not required to - * remember any data at all if more than readlimit bytes are - * read from the stream before reset is called. - * - *

The mark method of InputStream does - * nothing. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - * @see java.io.InputStream#reset() - */ - public synchronized void mark(int readlimit) { - try { - mpos=lo.tell(); - } catch(SQLException se) { - //throw new IOException(se.toString()); - } - } - - /** - * Repositions this stream to the position at the time the - * mark method was last called on this input stream. - * NB: If mark is not called we move to the begining. - * @see java.io.InputStream#mark(int) - * @see java.io.IOException - */ - public synchronized void reset() throws IOException { - try { - lo.seek(mpos); - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } - - /** - * Tests if this input stream supports the mark and - * reset methods. The markSupported method of - * InputStream returns false. - * - * @return true if this true type supports the mark and reset - * method; false otherwise. - * @see java.io.InputStream#mark(int) - * @see java.io.InputStream#reset() - */ - public boolean markSupported() { - return true; - } +public class BlobInputStream extends InputStream +{ + /** + * The parent LargeObject + */ + private LargeObject lo; + + /** + * Buffer used to improve performance + */ + private byte[] buffer; + + /** + * Position within buffer + */ + private int bpos; + + /** + * The buffer size + */ + private int bsize; + + /** + * The mark position + */ + private int mpos = 0; + + /** + * @param lo LargeObject to read from + */ + public BlobInputStream(LargeObject lo) + { + this(lo, 1024); + } + + /** + * @param lo LargeObject to read from + * @param bsize buffer size + */ + public BlobInputStream(LargeObject lo, int bsize) + { + this.lo = lo; + buffer = null; + bpos = 0; + this.bsize = bsize; + } + + /** + * The minimum required to implement input stream + */ + public int read() throws java.io.IOException + { + try + { + if (buffer == null || bpos >= buffer.length) + { + buffer = lo.read(bsize); + bpos = 0; + } + + // Handle EOF + if (bpos >= buffer.length) + { + return -1; + } + + int ret = (buffer[bpos] & 0x7F); + if ((buffer[bpos] &0x80) == 0x80) + { + ret |= 0x80; + } + + bpos++; + + return ret; + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } + + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * + *

The close method of InputStream does + * nothing. + * + * @exception IOException if an I/O error occurs. + */ + public void close() throws IOException + { + try + { + lo.close(); + lo = null; + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } + + /** + * Marks the current position in this input stream. A subsequent call to + * the reset method repositions this stream at the last marked + * position so that subsequent reads re-read the same bytes. + * + *

The readlimit arguments tells this input stream to + * allow that many bytes to be read before the mark position gets + * invalidated. + * + *

The general contract of mark is that, if the method + * markSupported returns true, the stream somehow + * remembers all the bytes read after the call to mark and + * stands ready to supply those same bytes again if and whenever the method + * reset is called. However, the stream is not required to + * remember any data at all if more than readlimit bytes are + * read from the stream before reset is called. + * + *

The mark method of InputStream does + * nothing. + * + * @param readlimit the maximum limit of bytes that can be read before + * the mark position becomes invalid. + * @see java.io.InputStream#reset() + */ + public synchronized void mark(int readlimit) + { + try + { + mpos = lo.tell(); + } + catch (SQLException se) + { + //throw new IOException(se.toString()); + } + } + + /** + * Repositions this stream to the position at the time the + * mark method was last called on this input stream. + * NB: If mark is not called we move to the begining. + * @see java.io.InputStream#mark(int) + * @see java.io.IOException + */ + public synchronized void reset() + throws IOException + { + try + { + lo.seek(mpos); + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } + + /** + * Tests if this input stream supports the mark and + * reset methods. The markSupported method of + * InputStream returns false. + * + * @return true if this true type supports the mark and reset + * method; false otherwise. + * @see java.io.InputStream#mark(int) + * @see java.io.InputStream#reset() + */ + public boolean markSupported() + { + return true; + } } diff --git a/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java b/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java index 3cba1c3702..763e2bbba1 100644 --- a/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java +++ b/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java @@ -7,97 +7,113 @@ import java.sql.SQLException; /** * This implements a basic output stream that writes to a LargeObject */ -public class BlobOutputStream extends OutputStream { - /** - * The parent LargeObject - */ - private LargeObject lo; +public class BlobOutputStream extends OutputStream +{ + /** + * The parent LargeObject + */ + private LargeObject lo; - /** - * Buffer - */ - private byte buf[]; + /** + * Buffer + */ + private byte buf[]; - /** - * Size of the buffer (default 1K) - */ - private int bsize; + /** + * Size of the buffer (default 1K) + */ + private int bsize; - /** - * Position within the buffer - */ - private int bpos; + /** + * Position within the buffer + */ + private int bpos; - /** - * Create an OutputStream to a large object - * @param lo LargeObject - */ - public BlobOutputStream(LargeObject lo) { - this(lo,1024); - } + /** + * Create an OutputStream to a large object + * @param lo LargeObject + */ + public BlobOutputStream(LargeObject lo) + { + this(lo, 1024); + } - /** - * Create an OutputStream to a large object - * @param lo LargeObject - * @param bsize The size of the buffer used to improve performance - */ - public BlobOutputStream(LargeObject lo,int bsize) { - this.lo=lo; - this.bsize=bsize; - buf=new byte[bsize]; - bpos=0; - } + /** + * Create an OutputStream to a large object + * @param lo LargeObject + * @param bsize The size of the buffer used to improve performance + */ + public BlobOutputStream(LargeObject lo, int bsize) + { + this.lo = lo; + this.bsize = bsize; + buf = new byte[bsize]; + bpos = 0; + } - public void write(int b) throws java.io.IOException { - try { - if(bpos>=bsize) { - lo.write(buf); - bpos=0; - } - buf[bpos++]=(byte)b; - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } + public void write(int b) throws java.io.IOException + { + try + { + if (bpos >= bsize) + { + lo.write(buf); + bpos = 0; + } + buf[bpos++] = (byte)b; + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } - /** - * Flushes this output stream and forces any buffered output bytes - * to be written out. The general contract of flush is - * that calling it is an indication that, if any bytes previously - * written have been buffered by the implementation of the output - * stream, such bytes should immediately be written to their - * intended destination. - * - * @exception IOException if an I/O error occurs. - */ - public void flush() throws IOException { - try { - if(bpos>0) - lo.write(buf,0,bpos); - bpos=0; - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } + /** + * Flushes this output stream and forces any buffered output bytes + * to be written out. The general contract of flush is + * that calling it is an indication that, if any bytes previously + * written have been buffered by the implementation of the output + * stream, such bytes should immediately be written to their + * intended destination. + * + * @exception IOException if an I/O error occurs. + */ + public void flush() throws IOException + { + try + { + if (bpos > 0) + lo.write(buf, 0, bpos); + bpos = 0; + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } - /** - * Closes this output stream and releases any system resources - * associated with this stream. The general contract of close - * is that it closes the output stream. A closed stream cannot perform - * output operations and cannot be reopened. - *

- * The close method of OutputStream does nothing. - * - * @exception IOException if an I/O error occurs. - */ - public void close() throws IOException { - try { - flush(); - lo.close(); - lo=null; - } catch(SQLException se) { - throw new IOException(se.toString()); - } - } + /** + * Closes this output stream and releases any system resources + * associated with this stream. The general contract of close + * is that it closes the output stream. A closed stream cannot perform + * output operations and cannot be reopened. + *

+ * The close method of OutputStream does nothing. + * + * @exception IOException if an I/O error occurs. + */ + public void close() throws IOException + { + try + { + flush(); + lo.close(); + lo = null; + } + catch (SQLException se) + { + throw new IOException(se.toString()); + } + } -} \ No newline at end of file +} diff --git a/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java b/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java index 677d853bb5..d701c6122b 100644 --- a/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java +++ b/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java @@ -43,271 +43,278 @@ import org.postgresql.fastpath.*; */ public class LargeObject { - /** - * Indicates a seek from the begining of a file - */ - public static final int SEEK_SET = 0; + /** + * 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 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; + /** + * 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 + private Fastpath fp; // Fastpath API to use + private int oid; // OID of this object + private int fd; // the descriptor of the open large object - private BlobOutputStream os; // The current output stream + private BlobOutputStream os; // The current output stream - private boolean closed=false; // true when we are closed + private boolean closed = false; // true when we are closed - /** - * This opens a large object. - * - *

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 org.postgresql.largeobject.LargeObjectManager - */ - protected LargeObject(Fastpath fp,int oid,int mode) throws SQLException - { - this.fp = fp; - this.oid = oid; + /** + * This opens a large object. + * + *

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 org.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); - } + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(oid); + args[1] = new FastpathArg(mode); + this.fd = fp.getInteger("lo_open", args); + } - /* Release large object resources during garbage cleanup */ - protected void finalize() throws SQLException - { - close(); - } + /* Release large object resources during garbage cleanup */ + protected void finalize() throws SQLException + { + close(); + } - /** - * @return the OID of this LargeObject - */ - public int getOID() - { - return oid; - } + /** + * @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 - { - if(!closed) { - // flush any open output streams - if(os!=null) { - try { - // we can't call os.close() otherwise we go into an infinite loop! - os.flush(); - } catch(IOException ioe) { - throw new SQLException(ioe.getMessage()); - } finally { - os=null; - } - } + /** + * 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 + { + if (!closed) + { + // flush any open output streams + if (os != null) + { + try + { + // we can't call os.close() otherwise we go into an infinite loop! + os.flush(); + } + catch (IOException ioe) + { + throw new SQLException(ioe.getMessage()); + } + finally + { + os = null; + } + } - // finally close - FastpathArg args[] = new FastpathArg[1]; - args[0] = new FastpathArg(fd); - fp.fastpath("lo_close",false,args); // true here as we dont care!! - closed=true; - } - } + // finally close + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + fp.fastpath("lo_close", false, args); // true here as we dont care!! + closed = true; + } + } - /** - * 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 - { - // This is the original method, where the entire block (len bytes) - // is retrieved in one go. - 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, 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 + { + // This is the original method, where the entire block (len bytes) + // is retrieved in one go. + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(len); + return fp.getData("loread", args); - // This version allows us to break this down into 4k blocks - //if(len<=4048) { - //// handle as before, return the whole block in one go - //FastpathArg args[] = new FastpathArg[2]; - //args[0] = new FastpathArg(fd); - //args[1] = new FastpathArg(len); - //return fp.getData("loread",args); - //} else { - //// return in 4k blocks - //byte[] buf=new byte[len]; - //int off=0; - //while(len>0) { - //int bs=4048; - //len-=bs; - //if(len<0) { - //bs+=len; - //len=0; - //} - //read(buf,off,bs); - //off+=bs; - //} - //return buf; - //} - } + // This version allows us to break this down into 4k blocks + //if(len<=4048) { + //// handle as before, return the whole block in one go + //FastpathArg args[] = new FastpathArg[2]; + //args[0] = new FastpathArg(fd); + //args[1] = new FastpathArg(len); + //return fp.getData("loread",args); + //} else { + //// return in 4k blocks + //byte[] buf=new byte[len]; + //int off=0; + //while(len>0) { + //int bs=4048; + //len-=bs; + //if(len<0) { + //bs+=len; + //len=0; + //} + //read(buf,off,bs); + //off+=bs; + //} + //return buf; + //} + } - /** - * 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 - * @return the number of bytes actually read - * @exception SQLException if a database-access error occurs. - */ - public int read(byte buf[],int off,int len) throws SQLException - { - byte b[] = read(len); - if(b.lengthThis 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. + * + *

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. - * - *

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); - } + /** + * Sets the current position within the object. + * + *

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); - } + /** + * @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. - * - *

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; - } + /** + * 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. + * + *

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. - * - *

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 - { - return new BlobInputStream(this); - } + /** + * Returns an InputStream from this object. + * + *

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 + { + return new BlobInputStream(this); + } - /** - * Returns an OutputStream to this object - * - *

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 - { - if(os==null) - os = new BlobOutputStream(this); - return os; - } + /** + * Returns an OutputStream to this object + * + *

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 + { + if (os == null) + os = new BlobOutputStream(this); + return os; + } } diff --git a/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java b/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java index 6f79711c70..03445c01b7 100644 --- a/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java +++ b/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java @@ -32,7 +32,7 @@ import org.postgresql.util.*; *

* *

Normally, client code would use the getAsciiStream, getBinaryStream, - * or getUnicodeStream methods in ResultSet, or setAsciiStream, + * or getUnicodeStream methods in ResultSet, or setAsciiStream, * setBinaryStream, or setUnicodeStream methods in PreparedStatement to * access Large Objects. * @@ -58,149 +58,148 @@ import org.postgresql.util.*; */ 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. - * - *

Important Notice - *
This method should only be called by org.postgresql.Connection - * - *

There should only be one LargeObjectManager per Connection. The - * org.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(org.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(); - // - java.sql.ResultSet res = (java.sql.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 PSQLException("postgresql.lo.init"); - - 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. - * - *

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. - * - *

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); - } - + // 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. + * + *

Important Notice + *
This method should only be called by org.postgresql.Connection + * + *

There should only be one LargeObjectManager per Connection. The + * org.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(org.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(); + // + java.sql.ResultSet res = (java.sql.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 PSQLException("postgresql.lo.init"); + + 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. + * + *

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. + * + *

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); + } + } diff --git a/src/interfaces/jdbc/org/postgresql/largeobject/PGblob.java b/src/interfaces/jdbc/org/postgresql/largeobject/PGblob.java index 1fbc84d87c..2959668bac 100644 --- a/src/interfaces/jdbc/org/postgresql/largeobject/PGblob.java +++ b/src/interfaces/jdbc/org/postgresql/largeobject/PGblob.java @@ -20,47 +20,53 @@ import org.postgresql.largeobject.*; * This implements the Blob interface, which is basically another way to * access a LargeObject. * - * $Id: PGblob.java,v 1.1 2000/04/17 20:07:52 peter Exp $ + * $Id: PGblob.java,v 1.2 2001/10/25 05:59:59 momjian Exp $ * */ public class PGblob implements java.sql.Blob { - private org.postgresql.Connection conn; - private int oid; - private LargeObject lo; - - public PGblob(org.postgresql.Connection conn,int oid) throws SQLException { - this.conn=conn; - this.oid=oid; - LargeObjectManager lom = conn.getLargeObjectAPI(); - this.lo = lom.open(oid); - } - - public long length() throws SQLException { - return lo.size(); - } - - public InputStream getBinaryStream() throws SQLException { - return lo.getInputStream(); - } - - public byte[] getBytes(long pos,int length) throws SQLException { - lo.seek((int)pos,LargeObject.SEEK_SET); - return lo.read(length); - } - - /* - * For now, this is not implemented. - */ - public long position(byte[] pattern,long start) throws SQLException { - throw org.postgresql.Driver.notImplemented(); - } - - /* - * This should be simply passing the byte value of the pattern Blob - */ - public long position(Blob pattern,long start) throws SQLException { - return position(pattern.getBytes(0,(int)pattern.length()),start); - } - + private org.postgresql.Connection conn; + private int oid; + private LargeObject lo; + + public PGblob(org.postgresql.Connection conn, int oid) throws SQLException + { + this.conn = conn; + this.oid = oid; + LargeObjectManager lom = conn.getLargeObjectAPI(); + this.lo = lom.open(oid); + } + + public long length() throws SQLException + { + return lo.size(); + } + + public InputStream getBinaryStream() throws SQLException + { + return lo.getInputStream(); + } + + public byte[] getBytes(long pos, int length) throws SQLException + { + lo.seek((int)pos, LargeObject.SEEK_SET); + return lo.read(length); + } + + /* + * For now, this is not implemented. + */ + public long position(byte[] pattern, long start) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + /* + * This should be simply passing the byte value of the pattern Blob + */ + public long position(Blob pattern, long start) throws SQLException + { + return position(pattern.getBytes(0, (int)pattern.length()), start); + } + } diff --git a/src/interfaces/jdbc/org/postgresql/largeobject/PGclob.java b/src/interfaces/jdbc/org/postgresql/largeobject/PGclob.java index ee3fff86d6..805ca01890 100644 --- a/src/interfaces/jdbc/org/postgresql/largeobject/PGclob.java +++ b/src/interfaces/jdbc/org/postgresql/largeobject/PGclob.java @@ -20,51 +20,58 @@ import org.postgresql.largeobject.*; * This implements the Blob interface, which is basically another way to * access a LargeObject. * - * $Id: PGclob.java,v 1.1 2001/02/16 16:45:01 peter Exp $ + * $Id: PGclob.java,v 1.2 2001/10/25 05:59:59 momjian Exp $ * */ public class PGclob implements java.sql.Clob { - private org.postgresql.Connection conn; - private int oid; - private LargeObject lo; + private org.postgresql.Connection conn; + private int oid; + private LargeObject lo; - public PGclob(org.postgresql.Connection conn,int oid) throws SQLException { - this.conn=conn; - this.oid=oid; - LargeObjectManager lom = conn.getLargeObjectAPI(); - this.lo = lom.open(oid); - } + public PGclob(org.postgresql.Connection conn, int oid) throws SQLException + { + this.conn = conn; + this.oid = oid; + LargeObjectManager lom = conn.getLargeObjectAPI(); + this.lo = lom.open(oid); + } - public long length() throws SQLException { - return lo.size(); - } + public long length() throws SQLException + { + return lo.size(); + } - public InputStream getAsciiStream() throws SQLException { - return lo.getInputStream(); - } + public InputStream getAsciiStream() throws SQLException + { + return lo.getInputStream(); + } - public Reader getCharacterStream() throws SQLException { - return new InputStreamReader(lo.getInputStream()); - } + public Reader getCharacterStream() throws SQLException + { + return new InputStreamReader(lo.getInputStream()); + } - public String getSubString(long i,int j) throws SQLException { - lo.seek((int)i-1); - return new String(lo.read(j)); - } + public String getSubString(long i, int j) throws SQLException + { + lo.seek((int)i - 1); + return new String(lo.read(j)); + } - /* - * For now, this is not implemented. - */ - public long position(String pattern,long start) throws SQLException { - throw org.postgresql.Driver.notImplemented(); - } + /* + * For now, this is not implemented. + */ + public long position(String pattern, long start) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } - /* - * This should be simply passing the byte value of the pattern Blob - */ - public long position(Clob pattern,long start) throws SQLException { - throw org.postgresql.Driver.notImplemented(); - } + /* + * This should be simply passing the byte value of the pattern Blob + */ + public long position(Clob pattern, long start) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/JDBC2Tests.java b/src/interfaces/jdbc/org/postgresql/test/JDBC2Tests.java index 76f8e368c5..1f299ceb16 100644 --- a/src/interfaces/jdbc/org/postgresql/test/JDBC2Tests.java +++ b/src/interfaces/jdbc/org/postgresql/test/JDBC2Tests.java @@ -9,38 +9,48 @@ import java.sql.*; /** * Executes all known tests for JDBC2 and includes some utility methods. */ -public class JDBC2Tests extends TestSuite { +public class JDBC2Tests extends TestSuite +{ /** * Returns the Test database JDBC URL */ - public static String getURL() { + public static String getURL() + { return System.getProperty("database"); } /** * Returns the Postgresql username */ - public static String getUser() { + public static String getUser() + { return System.getProperty("username"); } /** * Returns the user's password */ - public static String getPassword() { + public static String getPassword() + { return System.getProperty("password"); } /** * Helper - opens a connection. */ - public static java.sql.Connection openDB() { - try { + public static java.sql.Connection openDB() + { + try + { Class.forName("org.postgresql.Driver"); - return java.sql.DriverManager.getConnection(JDBC2Tests.getURL(),JDBC2Tests.getUser(),JDBC2Tests.getPassword()); - } catch(ClassNotFoundException ex) { + return java.sql.DriverManager.getConnection(JDBC2Tests.getURL(), JDBC2Tests.getUser(), JDBC2Tests.getPassword()); + } + catch (ClassNotFoundException ex) + { TestCase.fail(ex.getMessage()); - } catch(SQLException ex) { + } + catch (SQLException ex) + { TestCase.fail(ex.getMessage()); } return null; @@ -50,11 +60,15 @@ public class JDBC2Tests extends TestSuite { * Helper - closes an open connection. This rewrites SQLException to a failed * assertion. It's static so other classes can use it. */ - public static void closeDB(Connection con) { - try { + public static void closeDB(Connection con) + { + try + { if (con != null) con.close(); - } catch (SQLException ex) { + } + catch (SQLException ex) + { TestCase.fail(ex.getMessage()); } } @@ -64,19 +78,26 @@ public class JDBC2Tests extends TestSuite { */ public static void createTable(Connection con, String table, - String columns) { - try { + String columns) + { + try + { Statement st = con.createStatement(); - try { + try + { // Drop the table dropTable(con, table); // Now create the table st.executeUpdate("create table " + table + " (" + columns + ")"); - } finally { + } + finally + { st.close(); } - } catch(SQLException ex) { + } + catch (SQLException ex) + { TestCase.fail(ex.getMessage()); } } @@ -84,15 +105,22 @@ public class JDBC2Tests extends TestSuite { /** * Helper - drops a table */ - public static void dropTable(Connection con, String table) { - try { + public static void dropTable(Connection con, String table) + { + try + { Statement stmt = con.createStatement(); - try { + try + { stmt.executeUpdate("DROP TABLE " + table); - } catch (SQLException ex) { + } + catch (SQLException ex) + { // ignore } - } catch (SQLException ex) { + } + catch (SQLException ex) + { TestCase.fail(ex.getMessage()); } } @@ -100,47 +128,53 @@ public class JDBC2Tests extends TestSuite { /** * Helper - generates INSERT SQL - very simple */ - public static String insertSQL(String table, String values) { + public static String insertSQL(String table, String values) + { return insertSQL(table, null, values); } - - public static String insertSQL(String table, String columns, String values) { + + public static String insertSQL(String table, String columns, String values) + { String s = "INSERT INTO " + table; - + if (columns != null) s = s + " (" + columns + ")"; - + return s + " VALUES (" + values + ")"; } /** * Helper - generates SELECT SQL - very simple */ - public static String selectSQL(String table, String columns) { + public static String selectSQL(String table, String columns) + { return selectSQL(table, columns, null, null); } - public static String selectSQL(String table, String columns, String where) { + public static String selectSQL(String table, String columns, String where) + { return selectSQL(table, columns, where, null); } - - public static String selectSQL(String table, String columns, String where, String other) { + + public static String selectSQL(String table, String columns, String where, String other) + { String s = "SELECT " + columns + " FROM " + table; - + if (where != null) s = s + " WHERE " + where; if (other != null) s = s + " " + other; - + return s; } - + /** * Helper to prefix a number with leading zeros - ugly but it works... * @param v value to prefix * @param l number of digits (0-10) */ - public static String fix(int v, int l) { + public static String fix(int v, int l) + { String s = "0000000000".substring(0, l) + Integer.toString(v); return s.substring(s.length() - l); } @@ -148,8 +182,9 @@ public class JDBC2Tests extends TestSuite { /** * The main entry point for JUnit */ - public static TestSuite suite() { - TestSuite suite= new TestSuite(); + public static TestSuite suite() + { + TestSuite suite = new TestSuite(); // // Add one line per class in our test cases. These should be in order of diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/ANTTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/ANTTest.java index b9b3524c20..6f61d73eab 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/ANTTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/ANTTest.java @@ -2,25 +2,28 @@ package org.postgresql.test.jdbc2; import junit.framework.TestCase; -public class ANTTest extends TestCase { - public ANTTest(String name) { - super(name); - } +public class ANTTest extends TestCase +{ + public ANTTest(String name) + { + super(name); + } - /** - * This tests the acceptsURL() method with a couple of good and badly formed - * jdbc urls - */ - public void testANT() { - String url=System.getProperty("database"); - String usr=System.getProperty("username"); - String psw=System.getProperty("password"); + /** + * This tests the acceptsURL() method with a couple of good and badly formed + * jdbc urls + */ + public void testANT() + { + String url = System.getProperty("database"); + String usr = System.getProperty("username"); + String psw = System.getProperty("password"); - assertNotNull(url); - assertNotNull(usr); - assertNotNull(psw); + assertNotNull(url); + assertNotNull(usr); + assertNotNull(psw); - assertTrue(! url.equals("")); - assertTrue(! usr.equals("")); - } + assertTrue(! url.equals("")); + assertTrue(! usr.equals("")); + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/BatchExecuteTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/BatchExecuteTest.java index d66de43dc3..50684926cd 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/BatchExecuteTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/BatchExecuteTest.java @@ -12,24 +12,27 @@ import java.sql.*; /** * Test case for Statement.batchExecute() */ -public class BatchExecuteTest extends TestCase { +public class BatchExecuteTest extends TestCase +{ private Connection con; - public BatchExecuteTest(String name) { + public BatchExecuteTest(String name) + { super(name); } // Set up the fixture for this testcase: a connection to a database with // a table for this test. - protected void setUp() throws Exception { + protected void setUp() throws Exception + { con = JDBC2Tests.openDB(); Statement stmt = con.createStatement(); // Drop the test table if it already exists for some reason. It is // not an error if it doesn't exist. JDBC2Tests.createTable(con, "testbatch", "pk INTEGER, col1 INTEGER"); - + stmt.executeUpdate("INSERT INTO testbatch VALUES (1, 0)"); // Generally recommended with batch updates. By default we run all @@ -38,19 +41,22 @@ public class BatchExecuteTest extends TestCase { } // Tear down the fixture for this test case. - protected void tearDown() throws Exception { + protected void tearDown() throws Exception + { con.setAutoCommit(true); JDBC2Tests.dropTable(con, "testbatch"); JDBC2Tests.closeDB(con); } - public void testSupportsBatchUpdates() throws Exception { + public void testSupportsBatchUpdates() throws Exception + { DatabaseMetaData dbmd = con.getMetaData(); assertTrue(dbmd.supportsBatchUpdates()); } - private void assertCol1HasValue(int expected) throws Exception { + private void assertCol1HasValue(int expected) throws Exception + { Statement getCol1 = con.createStatement(); ResultSet rs = @@ -67,21 +73,23 @@ public class BatchExecuteTest extends TestCase { getCol1.close(); } - public void testExecuteEmptyBatch() throws Exception { + public void testExecuteEmptyBatch() throws Exception + { Statement stmt = con.createStatement(); int[] updateCount = stmt.executeBatch(); - assertEquals(0,updateCount.length); + assertEquals(0, updateCount.length); stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1"); stmt.clearBatch(); updateCount = stmt.executeBatch(); - assertEquals(0,updateCount.length); + assertEquals(0, updateCount.length); stmt.close(); } - public void testClearBatch() throws Exception { + public void testClearBatch() throws Exception + { Statement stmt = con.createStatement(); - + stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1"); assertCol1HasValue(0); stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1"); @@ -98,44 +106,51 @@ public class BatchExecuteTest extends TestCase { stmt.close(); } - public void testSelectThrowsException() throws Exception { + public void testSelectThrowsException() throws Exception + { Statement stmt = con.createStatement(); - + stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1"); stmt.addBatch("SELECT col1 FROM testbatch WHERE pk = 1"); stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1"); - try { + try + { stmt.executeBatch(); fail("Should raise a BatchUpdateException because of the SELECT"); - } catch (BatchUpdateException e) { + } + catch (BatchUpdateException e) + { int [] updateCounts = e.getUpdateCounts(); - assertEquals(1,updateCounts.length); - assertEquals(1,updateCounts[0]); - } catch (SQLException e) { + assertEquals(1, updateCounts.length); + assertEquals(1, updateCounts[0]); + } + catch (SQLException e) + { fail( "Should throw a BatchUpdateException instead of " + - "a generic SQLException: " + e); + "a generic SQLException: " + e); } stmt.close(); } - public void testPreparedStatement() throws Exception { + public void testPreparedStatement() throws Exception + { PreparedStatement pstmt = con.prepareStatement( - "UPDATE testbatch SET col1 = col1 + ? WHERE PK = ?" ); + "UPDATE testbatch SET col1 = col1 + ? WHERE PK = ?" ); // Note that the first parameter changes for every statement in the // batch, whereas the second parameter remains constant. - pstmt.setInt(1,1); - pstmt.setInt(2,1); + pstmt.setInt(1, 1); + pstmt.setInt(2, 1); pstmt.addBatch(); assertCol1HasValue(0); - pstmt.setInt(1,2); + pstmt.setInt(1, 2); pstmt.addBatch(); assertCol1HasValue(0); - pstmt.setInt(1,4); + pstmt.setInt(1, 4); pstmt.addBatch(); assertCol1HasValue(0); @@ -153,9 +168,10 @@ public class BatchExecuteTest extends TestCase { /** */ - public void testTransactionalBehaviour() throws Exception { + public void testTransactionalBehaviour() throws Exception + { Statement stmt = con.createStatement(); - + stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1"); stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1"); stmt.executeBatch(); @@ -170,9 +186,9 @@ public class BatchExecuteTest extends TestCase { assertCol1HasValue(0); int[] updateCounts = stmt.executeBatch(); - assertEquals(2,updateCounts.length); - assertEquals(1,updateCounts[0]); - assertEquals(1,updateCounts[1]); + assertEquals(2, updateCounts.length); + assertEquals(1, updateCounts[0]); + assertEquals(1, updateCounts[1]); assertCol1HasValue(12); con.commit(); diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/BlobTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/BlobTest.java index 93e25abe2e..981e6c7dd5 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/BlobTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/BlobTest.java @@ -8,39 +8,45 @@ import java.sql.*; import org.postgresql.largeobject.*; /** - * $Id: BlobTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: BlobTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ * * Some simple tests based on problems reported by users. Hopefully these will * help prevent previous problems from re-occuring ;-) * */ -public class BlobTest extends TestCase { +public class BlobTest extends TestCase +{ - private Connection con; + private Connection con; - private static final int LOOP = 0; // LargeObject API using loop + private static final int LOOP = 0; // LargeObject API using loop private static final int NATIVE_STREAM = 1; // LargeObject API using OutputStream - private static final int JDBC_STREAM = 2; // JDBC API using OutputStream + private static final int JDBC_STREAM = 2; // JDBC API using OutputStream - public BlobTest(String name) { + public BlobTest(String name) + { super(name); - } + } - protected void setUp() throws Exception { + protected void setUp() throws Exception + { con = JDBC2Tests.openDB(); JDBC2Tests.createTable(con, "testblob", "id name,lo oid"); } - protected void tearDown() throws Exception { + protected void tearDown() throws Exception + { JDBC2Tests.dropTable(con, "testblob"); JDBC2Tests.closeDB(con); } - + /** * Tests one method of uploading a blob to the database */ - public void testUploadBlob_LOOP() { - try { + public void testUploadBlob_LOOP() + { + try + { con.setAutoCommit(false); assertTrue(!con.getAutoCommit()); @@ -51,7 +57,9 @@ public class BlobTest extends TestCase { assertTrue(compareBlobs()); con.setAutoCommit(true); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -59,8 +67,10 @@ public class BlobTest extends TestCase { /** * Tests one method of uploading a blob to the database */ - public void testUploadBlob_NATIVE() { - try { + public void testUploadBlob_NATIVE() + { + try + { con.setAutoCommit(false); assertTrue(!con.getAutoCommit()); @@ -71,7 +81,9 @@ public class BlobTest extends TestCase { assertTrue(compareBlobs()); con.setAutoCommit(true); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -81,7 +93,8 @@ public class BlobTest extends TestCase { * because it always works, and we can use it as a base to test the new * methods. */ - private int uploadFile(String file, int method) throws Exception { + private int uploadFile(String file, int method) throws Exception + { LargeObjectManager lom = ((org.postgresql.Connection)con).getLargeObjectAPI(); FileInputStream fis = new FileInputStream(file); @@ -89,40 +102,42 @@ public class BlobTest extends TestCase { int oid = lom.create(LargeObjectManager.READWRITE); LargeObject blob = lom.open(oid); - int s,t; + int s, t; byte buf[]; OutputStream os; - switch(method) + switch (method) { - case LOOP: - buf = new byte[2048]; - t=0; - while((s=fis.read(buf,0,buf.length))>0) { - t+=s; - blob.write(buf,0,s); - } - break; - - case NATIVE_STREAM: - os = blob.getOutputStream(); - s= fis.read(); - while(s>-1) { - os.write(s); - s=fis.read(); - } - os.close(); - break; - - case JDBC_STREAM: - File f = new File(file); - PreparedStatement ps = con.prepareStatement(JDBC2Tests.insertSQL("testblob", "?")); - ps.setBinaryStream(1,fis,(int) f.length()); - ps.execute(); - break; - - default: - assertTrue("Unknown method in uploadFile",false); + case LOOP: + buf = new byte[2048]; + t = 0; + while ((s = fis.read(buf, 0, buf.length)) > 0) + { + t += s; + blob.write(buf, 0, s); + } + break; + + case NATIVE_STREAM: + os = blob.getOutputStream(); + s = fis.read(); + while (s > -1) + { + os.write(s); + s = fis.read(); + } + os.close(); + break; + + case JDBC_STREAM: + File f = new File(file); + PreparedStatement ps = con.prepareStatement(JDBC2Tests.insertSQL("testblob", "?")); + ps.setBinaryStream(1, fis, (int) f.length()); + ps.execute(); + break; + + default: + assertTrue("Unknown method in uploadFile", false); } blob.close(); @@ -130,7 +145,7 @@ public class BlobTest extends TestCase { // Insert into the table Statement st = con.createStatement(); - st.executeUpdate(JDBC2Tests.insertSQL("testblob", "id,lo","'"+file+"',"+oid)); + st.executeUpdate(JDBC2Tests.insertSQL("testblob", "id,lo", "'" + file + "'," + oid)); con.commit(); st.close(); @@ -141,8 +156,9 @@ public class BlobTest extends TestCase { * Helper - compares the blobs in a table with a local file. Note this alone * tests the InputStream methods! */ - private boolean compareBlobs() throws Exception { - boolean result=true; + private boolean compareBlobs() throws Exception + { + boolean result = true; LargeObjectManager lom = ((org.postgresql.Connection)con).getLargeObjectAPI(); @@ -150,7 +166,8 @@ public class BlobTest extends TestCase { ResultSet rs = st.executeQuery(JDBC2Tests.selectSQL("testblob", "id,lo")); assertNotNull(rs); - while(rs.next()) { + while (rs.next()) + { String file = rs.getString(1); int oid = rs.getInt(2); @@ -158,19 +175,20 @@ public class BlobTest extends TestCase { LargeObject blob = lom.open(oid); InputStream bis = blob.getInputStream(); - int f=fis.read(); - int b=bis.read(); - int c=0; - while(f>=0 && b>=0 & result) { - result=(f==b); - f=fis.read(); - b=bis.read(); + int f = fis.read(); + int b = bis.read(); + int c = 0; + while (f >= 0 && b >= 0 & result) + { + result = (f == b); + f = fis.read(); + b = bis.read(); c++; } - result=result && f==-1 && b==-1; + result = result && f == -1 && b == -1; - if(!result) - System.out.println("\nBlob compare failed at "+c+" of "+blob.size()); + if (!result) + System.out.println("\nBlob compare failed at " + c + " of " + blob.size()); blob.close(); fis.close(); diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java index 90136a506e..d8a9e482b1 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java @@ -10,194 +10,219 @@ import java.sql.*; * * PS: Do you know how difficult it is to type on a train? ;-) * - * $Id: ConnectionTest.java,v 1.5 2001/09/23 04:11:14 momjian Exp $ + * $Id: ConnectionTest.java,v 1.6 2001/10/25 05:59:59 momjian Exp $ */ -public class ConnectionTest extends TestCase { - - /** - * Constructor - */ - public ConnectionTest(String name) { - super(name); - } - - // Set up the fixture for this testcase: the tables for this test. - protected void setUp() throws Exception { - Connection con = JDBC2Tests.openDB(); - - JDBC2Tests.createTable(con, "test_a", "imagename name,image oid,id int4"); - JDBC2Tests.createTable(con, "test_c", "source text,cost money,imageid int4"); - - JDBC2Tests.closeDB(con); - } - - // Tear down the fixture for this test case. - protected void tearDown() throws Exception { - Connection con = JDBC2Tests.openDB(); - - JDBC2Tests.dropTable(con, "test_a"); - JDBC2Tests.dropTable(con, "test_c"); - - JDBC2Tests.closeDB(con); - } - - /** - * Tests the two forms of createStatement() - */ - public void testCreateStatement() { - try { - java.sql.Connection conn = JDBC2Tests.openDB(); - - // A standard Statement - java.sql.Statement stat = conn.createStatement(); - assertNotNull(stat); - stat.close(); - - // Ask for Updateable ResultSets - stat = conn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_UPDATABLE); - assertNotNull(stat); - stat.close(); - - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } - - /** - * Tests the two forms of prepareStatement() - */ - public void testPrepareStatement() { - try { - java.sql.Connection conn = JDBC2Tests.openDB(); - - String sql = "select source,cost,imageid from test_c"; - - // A standard Statement - java.sql.PreparedStatement stat = conn.prepareStatement(sql); - assertNotNull(stat); - stat.close(); - - // Ask for Updateable ResultSets - stat = conn.prepareStatement(sql,java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_UPDATABLE); - assertNotNull(stat); - stat.close(); - - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } - - /** - * Put the test for createPrepareCall here - */ - public void testPrepareCall() { - } - - /** - * Test nativeSQL - */ - public void testNativeSQL() { - // For now do nothing as it returns itself - } - - /** - * Test autoCommit (both get & set) - */ - public void testTransactions() { - try { - java.sql.Connection con = JDBC2Tests.openDB(); - java.sql.Statement st; - java.sql.ResultSet rs; - - // Turn it off - con.setAutoCommit(false); - assertTrue(!con.getAutoCommit()); - - // Turn it back on - con.setAutoCommit(true); - assertTrue(con.getAutoCommit()); - - // Now test commit - st = con.createStatement(); - st.executeUpdate("insert into test_a (imagename,image,id) values ('comttest',1234,5678)"); - - con.setAutoCommit(false); - - // Now update image to 9876 and commit - st.executeUpdate("update test_a set image=9876 where id=5678"); - con.commit(); - rs = st.executeQuery("select image from test_a where id=5678"); - assertTrue(rs.next()); - assertEquals(9876, rs.getInt(1)); - rs.close(); - - // Now try to change it but rollback - st.executeUpdate("update test_a set image=1111 where id=5678"); - con.rollback(); - rs = st.executeQuery("select image from test_a where id=5678"); - assertTrue(rs.next()); - assertEquals(9876, rs.getInt(1)); // Should not change! - rs.close(); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } - - /** - * Simple test to see if isClosed works. - */ - public void testIsClosed() { - try { - Connection con = JDBC2Tests.openDB(); - - // Should not say closed - assertTrue(!con.isClosed()); - - JDBC2Tests.closeDB(con); - - // Should now say closed - assertTrue(con.isClosed()); - - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } - - /** - * Test the warnings system - */ - public void testWarnings() { - try { - Connection con = JDBC2Tests.openDB(); - - String testStr = "This Is OuR TeSt message"; - - // The connection must be ours! - assertTrue(con instanceof org.postgresql.Connection); - - // Clear any existing warnings - con.clearWarnings(); - - // Set the test warning - ((org.postgresql.Connection)con).addWarning(testStr); - - // Retrieve it - SQLWarning warning = con.getWarnings(); - assertNotNull(warning); - assertEquals(testStr, warning.getMessage()); - - // Finally test clearWarnings() this time there must be something to delete - con.clearWarnings(); - assertTrue(con.getWarnings()==null); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } +public class ConnectionTest extends TestCase +{ + + /** + * Constructor + */ + public ConnectionTest(String name) + { + super(name); + } + + // Set up the fixture for this testcase: the tables for this test. + protected void setUp() throws Exception + { + Connection con = JDBC2Tests.openDB(); + + JDBC2Tests.createTable(con, "test_a", "imagename name,image oid,id int4"); + JDBC2Tests.createTable(con, "test_c", "source text,cost money,imageid int4"); + + JDBC2Tests.closeDB(con); + } + + // Tear down the fixture for this test case. + protected void tearDown() throws Exception + { + Connection con = JDBC2Tests.openDB(); + + JDBC2Tests.dropTable(con, "test_a"); + JDBC2Tests.dropTable(con, "test_c"); + + JDBC2Tests.closeDB(con); + } + + /** + * Tests the two forms of createStatement() + */ + public void testCreateStatement() + { + try + { + java.sql.Connection conn = JDBC2Tests.openDB(); + + // A standard Statement + java.sql.Statement stat = conn.createStatement(); + assertNotNull(stat); + stat.close(); + + // Ask for Updateable ResultSets + stat = conn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_UPDATABLE); + assertNotNull(stat); + stat.close(); + + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } + + /** + * Tests the two forms of prepareStatement() + */ + public void testPrepareStatement() + { + try + { + java.sql.Connection conn = JDBC2Tests.openDB(); + + String sql = "select source,cost,imageid from test_c"; + + // A standard Statement + java.sql.PreparedStatement stat = conn.prepareStatement(sql); + assertNotNull(stat); + stat.close(); + + // Ask for Updateable ResultSets + stat = conn.prepareStatement(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_UPDATABLE); + assertNotNull(stat); + stat.close(); + + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } + + /** + * Put the test for createPrepareCall here + */ + public void testPrepareCall() + {} + + /** + * Test nativeSQL + */ + public void testNativeSQL() + { + // For now do nothing as it returns itself + } + + /** + * Test autoCommit (both get & set) + */ + public void testTransactions() + { + try + { + java.sql.Connection con = JDBC2Tests.openDB(); + java.sql.Statement st; + java.sql.ResultSet rs; + + // Turn it off + con.setAutoCommit(false); + assertTrue(!con.getAutoCommit()); + + // Turn it back on + con.setAutoCommit(true); + assertTrue(con.getAutoCommit()); + + // Now test commit + st = con.createStatement(); + st.executeUpdate("insert into test_a (imagename,image,id) values ('comttest',1234,5678)"); + + con.setAutoCommit(false); + + // Now update image to 9876 and commit + st.executeUpdate("update test_a set image=9876 where id=5678"); + con.commit(); + rs = st.executeQuery("select image from test_a where id=5678"); + assertTrue(rs.next()); + assertEquals(9876, rs.getInt(1)); + rs.close(); + + // Now try to change it but rollback + st.executeUpdate("update test_a set image=1111 where id=5678"); + con.rollback(); + rs = st.executeQuery("select image from test_a where id=5678"); + assertTrue(rs.next()); + assertEquals(9876, rs.getInt(1)); // Should not change! + rs.close(); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } + + /** + * Simple test to see if isClosed works. + */ + public void testIsClosed() + { + try + { + Connection con = JDBC2Tests.openDB(); + + // Should not say closed + assertTrue(!con.isClosed()); + + JDBC2Tests.closeDB(con); + + // Should now say closed + assertTrue(con.isClosed()); + + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } + + /** + * Test the warnings system + */ + public void testWarnings() + { + try + { + Connection con = JDBC2Tests.openDB(); + + String testStr = "This Is OuR TeSt message"; + + // The connection must be ours! + assertTrue(con instanceof org.postgresql.Connection); + + // Clear any existing warnings + con.clearWarnings(); + + // Set the test warning + ((org.postgresql.Connection)con).addWarning(testStr); + + // Retrieve it + SQLWarning warning = con.getWarnings(); + assertNotNull(warning); + assertEquals(testStr, warning.getMessage()); + + // Finally test clearWarnings() this time there must be something to delete + con.clearWarnings(); + assertTrue(con.getWarnings() == null); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } /** * Transaction Isolation Levels @@ -210,14 +235,14 @@ public class ConnectionTest extends TestCase { // PostgreSQL defaults to READ COMMITTED assertEquals(Connection.TRANSACTION_READ_COMMITTED, - con.getTransactionIsolation()); + con.getTransactionIsolation()); // Begin a transaction con.setAutoCommit(false); // The isolation level should not have changed assertEquals(Connection.TRANSACTION_READ_COMMITTED, - con.getTransactionIsolation()); + con.getTransactionIsolation()); // Now change the default for future transactions con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); @@ -227,14 +252,14 @@ public class ConnectionTest extends TestCase { // transaction did not change. It affects only future transactions. // This behaviour is recommended by the JDBC spec. assertEquals(Connection.TRANSACTION_READ_COMMITTED, - con.getTransactionIsolation()); + con.getTransactionIsolation()); // Begin a new transaction con.commit(); // Now we should see the new isolation level assertEquals(Connection.TRANSACTION_SERIALIZABLE, - con.getTransactionIsolation()); + con.getTransactionIsolation()); // Repeat the steps above with the transition back to // READ COMMITTED. @@ -277,35 +302,39 @@ public class ConnectionTest extends TestCase { con.getTransactionIsolation()); JDBC2Tests.closeDB(con); - } - catch ( SQLException ex ) + } + catch ( SQLException ex ) { fail( ex.getMessage() ); } } - /** - * JDBC2 Type mappings - */ - public void testTypeMaps() { - try { - Connection con = JDBC2Tests.openDB(); - - // preserve the current map - java.util.Map oldmap = con.getTypeMap(); - - // now change it for an empty one - java.util.Map newmap = new java.util.HashMap(); - con.setTypeMap(newmap); - assertEquals(newmap, con.getTypeMap()); - - // restore the old one - con.setTypeMap(oldmap); - assertEquals(oldmap, con.getTypeMap()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - assertTrue(ex.getMessage(),false); - } - } + /** + * JDBC2 Type mappings + */ + public void testTypeMaps() + { + try + { + Connection con = JDBC2Tests.openDB(); + + // preserve the current map + java.util.Map oldmap = con.getTypeMap(); + + // now change it for an empty one + java.util.Map newmap = new java.util.HashMap(); + con.setTypeMap(newmap); + assertEquals(newmap, con.getTypeMap()); + + // restore the old one + con.setTypeMap(oldmap); + assertEquals(oldmap, con.getTypeMap()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + assertTrue(ex.getMessage(), false); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java index ad643fe82a..310aad3ca8 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java @@ -9,273 +9,323 @@ import java.sql.*; * * PS: Do you know how difficult it is to type on a train? ;-) * - * $Id: DatabaseMetaDataTest.java,v 1.2 2001/09/10 15:07:58 momjian Exp $ + * $Id: DatabaseMetaDataTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ */ -public class DatabaseMetaDataTest extends TestCase { - - /** - * Constructor - */ - public DatabaseMetaDataTest(String name) { - super(name); - } - - /** - * The spec says this may return null, but we always do! - */ - public void testGetMetaData() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - /** - * Test default capabilities - */ - public void testCapabilities() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(dbmd.allProceduresAreCallable()); - assertTrue(dbmd.allTablesAreSelectable()); // not true all the time - - // This should always be false for postgresql (at least for 7.x) - assertTrue(!dbmd.isReadOnly()); - - // does the backend support this yet? The protocol does... - assertTrue(!dbmd.supportsMultipleResultSets()); - - // yes, as multiple backends can have transactions open - assertTrue(dbmd.supportsMultipleTransactions()); - - assertTrue(dbmd.supportsMinimumSQLGrammar()); - assertTrue(!dbmd.supportsCoreSQLGrammar()); - assertTrue(!dbmd.supportsExtendedSQLGrammar()); - assertTrue(!dbmd.supportsANSI92EntryLevelSQL()); - assertTrue(!dbmd.supportsANSI92IntermediateSQL()); - assertTrue(!dbmd.supportsANSI92FullSQL()); - - assertTrue(!dbmd.supportsIntegrityEnhancementFacility()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - - public void testJoins() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(dbmd.supportsOuterJoins()); - assertTrue(dbmd.supportsFullOuterJoins()); - assertTrue(dbmd.supportsLimitedOuterJoins()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testCursors() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(!dbmd.supportsPositionedDelete()); - assertTrue(!dbmd.supportsPositionedUpdate()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testNulls() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - // We need to type cast the connection to get access to the - // PostgreSQL-specific method haveMinimumServerVersion(). - // This is not available through the java.sql.Connection interface. - assertTrue( con instanceof org.postgresql.Connection ); - - assertTrue(!dbmd.nullsAreSortedAtStart()); - assertTrue( dbmd.nullsAreSortedAtEnd() != - ((org.postgresql.Connection)con).haveMinimumServerVersion("7.2")); - assertTrue( dbmd.nullsAreSortedHigh() == - ((org.postgresql.Connection)con).haveMinimumServerVersion("7.2")); - assertTrue(!dbmd.nullsAreSortedLow()); - - assertTrue(dbmd.nullPlusNonNullIsNull()); - - assertTrue(dbmd.supportsNonNullableColumns()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testLocalFiles() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(!dbmd.usesLocalFilePerTable()); - assertTrue(!dbmd.usesLocalFiles()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testIdentifiers() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(!dbmd.supportsMixedCaseIdentifiers()); // always false - assertTrue(dbmd.supportsMixedCaseQuotedIdentifiers()); // always true - - assertTrue(!dbmd.storesUpperCaseIdentifiers()); // always false - assertTrue(dbmd.storesLowerCaseIdentifiers()); // always true - assertTrue(!dbmd.storesUpperCaseQuotedIdentifiers()); // always false - assertTrue(!dbmd.storesLowerCaseQuotedIdentifiers()); // always false - assertTrue(!dbmd.storesMixedCaseQuotedIdentifiers()); // always false - - assertTrue(dbmd.getIdentifierQuoteString().equals("\"")); - - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testTables() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - // we can add columns - assertTrue(dbmd.supportsAlterTableWithAddColumn()); - - // we can't drop columns (yet) - assertTrue(!dbmd.supportsAlterTableWithDropColumn()); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testSelect() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - // yes we can?: SELECT col a FROM a; - assertTrue(dbmd.supportsColumnAliasing()); - - // yes we can have expressions in ORDERBY - assertTrue(dbmd.supportsExpressionsInOrderBy()); - - // Yes, an ORDER BY clause can contain columns that are not in the - // SELECT clause. - assertTrue(dbmd.supportsOrderByUnrelated()); - - assertTrue(dbmd.supportsGroupBy()); - assertTrue(dbmd.supportsGroupByUnrelated()); - assertTrue(dbmd.supportsGroupByBeyondSelect()); // needs checking - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testDBParams() { - try { - Connection con = JDBC2Tests.openDB(); - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(dbmd.getURL().equals(JDBC2Tests.getURL())); - assertTrue(dbmd.getUserName().equals(JDBC2Tests.getUser())); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testDbProductDetails() { - try { - Connection con = JDBC2Tests.openDB(); - assertTrue(con instanceof org.postgresql.Connection); - org.postgresql.Connection pc = (org.postgresql.Connection) con; - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(dbmd.getDatabaseProductName().equals("PostgreSQL")); - assertTrue(dbmd.getDatabaseProductVersion().startsWith(Integer.toString(pc.this_driver.getMajorVersion())+"."+Integer.toString(pc.this_driver.getMinorVersion()))); - assertTrue(dbmd.getDriverName().equals("PostgreSQL Native Driver")); - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } - - public void testDriverVersioning() { - try { - Connection con = JDBC2Tests.openDB(); - assertTrue(con instanceof org.postgresql.Connection); - org.postgresql.Connection pc = (org.postgresql.Connection) con; - - DatabaseMetaData dbmd = con.getMetaData(); - assertNotNull(dbmd); - - assertTrue(dbmd.getDriverVersion().equals(pc.this_driver.getVersion())); - assertTrue(dbmd.getDriverMajorVersion()==pc.this_driver.getMajorVersion()); - assertTrue(dbmd.getDriverMinorVersion()==pc.this_driver.getMinorVersion()); - - - JDBC2Tests.closeDB(con); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } +public class DatabaseMetaDataTest extends TestCase +{ + + /** + * Constructor + */ + public DatabaseMetaDataTest(String name) + { + super(name); + } + + /** + * The spec says this may return null, but we always do! + */ + public void testGetMetaData() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + /** + * Test default capabilities + */ + public void testCapabilities() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(dbmd.allProceduresAreCallable()); + assertTrue(dbmd.allTablesAreSelectable()); // not true all the time + + // This should always be false for postgresql (at least for 7.x) + assertTrue(!dbmd.isReadOnly()); + + // does the backend support this yet? The protocol does... + assertTrue(!dbmd.supportsMultipleResultSets()); + + // yes, as multiple backends can have transactions open + assertTrue(dbmd.supportsMultipleTransactions()); + + assertTrue(dbmd.supportsMinimumSQLGrammar()); + assertTrue(!dbmd.supportsCoreSQLGrammar()); + assertTrue(!dbmd.supportsExtendedSQLGrammar()); + assertTrue(!dbmd.supportsANSI92EntryLevelSQL()); + assertTrue(!dbmd.supportsANSI92IntermediateSQL()); + assertTrue(!dbmd.supportsANSI92FullSQL()); + + assertTrue(!dbmd.supportsIntegrityEnhancementFacility()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + + public void testJoins() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(dbmd.supportsOuterJoins()); + assertTrue(dbmd.supportsFullOuterJoins()); + assertTrue(dbmd.supportsLimitedOuterJoins()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testCursors() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(!dbmd.supportsPositionedDelete()); + assertTrue(!dbmd.supportsPositionedUpdate()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testNulls() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + // We need to type cast the connection to get access to the + // PostgreSQL-specific method haveMinimumServerVersion(). + // This is not available through the java.sql.Connection interface. + assertTrue( con instanceof org.postgresql.Connection ); + + assertTrue(!dbmd.nullsAreSortedAtStart()); + assertTrue( dbmd.nullsAreSortedAtEnd() != + ((org.postgresql.Connection)con).haveMinimumServerVersion("7.2")); + assertTrue( dbmd.nullsAreSortedHigh() == + ((org.postgresql.Connection)con).haveMinimumServerVersion("7.2")); + assertTrue(!dbmd.nullsAreSortedLow()); + + assertTrue(dbmd.nullPlusNonNullIsNull()); + + assertTrue(dbmd.supportsNonNullableColumns()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testLocalFiles() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(!dbmd.usesLocalFilePerTable()); + assertTrue(!dbmd.usesLocalFiles()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testIdentifiers() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(!dbmd.supportsMixedCaseIdentifiers()); // always false + assertTrue(dbmd.supportsMixedCaseQuotedIdentifiers()); // always true + + assertTrue(!dbmd.storesUpperCaseIdentifiers()); // always false + assertTrue(dbmd.storesLowerCaseIdentifiers()); // always true + assertTrue(!dbmd.storesUpperCaseQuotedIdentifiers()); // always false + assertTrue(!dbmd.storesLowerCaseQuotedIdentifiers()); // always false + assertTrue(!dbmd.storesMixedCaseQuotedIdentifiers()); // always false + + assertTrue(dbmd.getIdentifierQuoteString().equals("\"")); + + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testTables() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + // we can add columns + assertTrue(dbmd.supportsAlterTableWithAddColumn()); + + // we can't drop columns (yet) + assertTrue(!dbmd.supportsAlterTableWithDropColumn()); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testSelect() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + // yes we can?: SELECT col a FROM a; + assertTrue(dbmd.supportsColumnAliasing()); + + // yes we can have expressions in ORDERBY + assertTrue(dbmd.supportsExpressionsInOrderBy()); + + // Yes, an ORDER BY clause can contain columns that are not in the + // SELECT clause. + assertTrue(dbmd.supportsOrderByUnrelated()); + + assertTrue(dbmd.supportsGroupBy()); + assertTrue(dbmd.supportsGroupByUnrelated()); + assertTrue(dbmd.supportsGroupByBeyondSelect()); // needs checking + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testDBParams() + { + try + { + Connection con = JDBC2Tests.openDB(); + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(dbmd.getURL().equals(JDBC2Tests.getURL())); + assertTrue(dbmd.getUserName().equals(JDBC2Tests.getUser())); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testDbProductDetails() + { + try + { + Connection con = JDBC2Tests.openDB(); + assertTrue(con instanceof org.postgresql.Connection); + org.postgresql.Connection pc = (org.postgresql.Connection) con; + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(dbmd.getDatabaseProductName().equals("PostgreSQL")); + assertTrue(dbmd.getDatabaseProductVersion().startsWith(Integer.toString(pc.this_driver.getMajorVersion()) + "." + Integer.toString(pc.this_driver.getMinorVersion()))); + assertTrue(dbmd.getDriverName().equals("PostgreSQL Native Driver")); + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } + + public void testDriverVersioning() + { + try + { + Connection con = JDBC2Tests.openDB(); + assertTrue(con instanceof org.postgresql.Connection); + org.postgresql.Connection pc = (org.postgresql.Connection) con; + + DatabaseMetaData dbmd = con.getMetaData(); + assertNotNull(dbmd); + + assertTrue(dbmd.getDriverVersion().equals(pc.this_driver.getVersion())); + assertTrue(dbmd.getDriverMajorVersion() == pc.this_driver.getMajorVersion()); + assertTrue(dbmd.getDriverMinorVersion() == pc.this_driver.getMinorVersion()); + + + JDBC2Tests.closeDB(con); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DateTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DateTest.java index 48d5ba5d21..3c42c7173e 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DateTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DateTest.java @@ -5,37 +5,43 @@ import junit.framework.TestCase; import java.sql.*; /** - * $Id: DateTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: DateTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ * * Some simple tests based on problems reported by users. Hopefully these will * help prevent previous problems from re-occuring ;-) * */ -public class DateTest extends TestCase { +public class DateTest extends TestCase +{ private Connection con; - - public DateTest(String name) { + + public DateTest(String name) + { super(name); } - protected void setUp() throws Exception { + protected void setUp() throws Exception + { con = JDBC2Tests.openDB(); JDBC2Tests.createTable(con, "testdate", "dt date"); } - protected void tearDown() throws Exception { + protected void tearDown() throws Exception + { JDBC2Tests.dropTable(con, "testdate"); JDBC2Tests.closeDB(con); } - + /** * Tests the time methods in ResultSet */ - public void testGetDate() { - try { + public void testGetDate() + { + try + { Statement stmt = con.createStatement(); - + assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testdate", "'1950-02-07'"))); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testdate", "'1970-06-02'"))); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testdate", "'1999-08-11'"))); @@ -46,7 +52,9 @@ public class DateTest extends TestCase { assertEquals(4, stmt.executeUpdate("DELETE FROM " + "testdate")); stmt.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -54,8 +62,10 @@ public class DateTest extends TestCase { /** * Tests the time methods in PreparedStatement */ - public void testSetDate() { - try { + public void testSetDate() + { + try + { Statement stmt = con.createStatement(); PreparedStatement ps = con.prepareStatement(JDBC2Tests.insertSQL("testdate", "?")); @@ -78,7 +88,9 @@ public class DateTest extends TestCase { assertEquals(4, stmt.executeUpdate("DELETE FROM testdate")); stmt.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -86,7 +98,8 @@ public class DateTest extends TestCase { /** * Helper for the date tests. It tests what should be in the db */ - private void dateTest() throws SQLException { + private void dateTest() throws SQLException + { Statement st = con.createStatement(); ResultSet rs; java.sql.Date d; @@ -97,7 +110,7 @@ public class DateTest extends TestCase { assertTrue(rs.next()); d = rs.getDate(1); assertNotNull(d); - assertEquals(d, makeDate(1950, 2, 7)); + assertEquals(d, makeDate(1950, 2, 7)); assertTrue(rs.next()); d = rs.getDate(1); @@ -108,7 +121,7 @@ public class DateTest extends TestCase { d = rs.getDate(1); assertNotNull(d); assertEquals(d, makeDate(1999, 8, 11)); - + assertTrue(rs.next()); d = rs.getDate(1); assertNotNull(d); @@ -120,7 +133,8 @@ public class DateTest extends TestCase { st.close(); } - private java.sql.Date makeDate(int y, int m, int d) { + private java.sql.Date makeDate(int y, int m, int d) + { return java.sql.Date.valueOf(JDBC2Tests.fix(y, 4) + "-" + JDBC2Tests.fix(m, 2) + "-" + JDBC2Tests.fix(d, 2)); diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DriverTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DriverTest.java index cdd5f93cfa..5b2b408cc2 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DriverTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DriverTest.java @@ -5,68 +5,80 @@ import junit.framework.TestCase; import java.sql.*; /** - * $Id: DriverTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: DriverTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ * * Tests the dynamically created class org.postgresql.Driver * */ -public class DriverTest extends TestCase { +public class DriverTest extends TestCase +{ - public DriverTest(String name) { - super(name); - } + public DriverTest(String name) + { + super(name); + } - /** - * This tests the acceptsURL() method with a couple of good and badly formed - * jdbc urls - */ - public void testAcceptsURL() { - try { + /** + * This tests the acceptsURL() method with a couple of good and badly formed + * jdbc urls + */ + public void testAcceptsURL() + { + try + { - // Load the driver (note clients should never do it this way!) - org.postgresql.Driver drv = new org.postgresql.Driver(); - assertNotNull(drv); + // Load the driver (note clients should never do it this way!) + org.postgresql.Driver drv = new org.postgresql.Driver(); + assertNotNull(drv); - // These are always correct - assertTrue(drv.acceptsURL("jdbc:postgresql:test")); - assertTrue(drv.acceptsURL("jdbc:postgresql://localhost/test")); - assertTrue(drv.acceptsURL("jdbc:postgresql://localhost:5432/test")); - assertTrue(drv.acceptsURL("jdbc:postgresql://127.0.0.1/anydbname")); - assertTrue(drv.acceptsURL("jdbc:postgresql://127.0.0.1:5433/hidden")); + // These are always correct + assertTrue(drv.acceptsURL("jdbc:postgresql:test")); + assertTrue(drv.acceptsURL("jdbc:postgresql://localhost/test")); + assertTrue(drv.acceptsURL("jdbc:postgresql://localhost:5432/test")); + assertTrue(drv.acceptsURL("jdbc:postgresql://127.0.0.1/anydbname")); + assertTrue(drv.acceptsURL("jdbc:postgresql://127.0.0.1:5433/hidden")); - // Badly formatted url's - assertTrue(!drv.acceptsURL("jdbc:postgres:test")); - assertTrue(!drv.acceptsURL("postgresql:test")); + // Badly formatted url's + assertTrue(!drv.acceptsURL("jdbc:postgres:test")); + assertTrue(!drv.acceptsURL("postgresql:test")); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } - /** - * Tests parseURL (internal) - */ - /** - * Tests the connect method by connecting to the test database - */ - public void testConnect() { - Connection con=null; - try { - Class.forName("org.postgresql.Driver"); + /** + * Tests parseURL (internal) + */ + /** + * Tests the connect method by connecting to the test database + */ + public void testConnect() + { + Connection con = null; + try + { + Class.forName("org.postgresql.Driver"); - // Test with the url, username & password - con = DriverManager.getConnection(JDBC2Tests.getURL(),JDBC2Tests.getUser(),JDBC2Tests.getPassword()); - assertNotNull(con); - con.close(); + // Test with the url, username & password + con = DriverManager.getConnection(JDBC2Tests.getURL(), JDBC2Tests.getUser(), JDBC2Tests.getPassword()); + assertNotNull(con); + con.close(); - // Test with the username in the url - con = DriverManager.getConnection(JDBC2Tests.getURL()+"?user="+JDBC2Tests.getUser()+"&password="+JDBC2Tests.getPassword()); - assertNotNull(con); - con.close(); - } catch(ClassNotFoundException ex) { - fail(ex.getMessage()); - } catch(SQLException ex) { - fail(ex.getMessage()); - } - } + // Test with the username in the url + con = DriverManager.getConnection(JDBC2Tests.getURL() + "?user=" + JDBC2Tests.getUser() + "&password=" + JDBC2Tests.getPassword()); + assertNotNull(con); + con.close(); + } + catch (ClassNotFoundException ex) + { + fail(ex.getMessage()); + } + catch (SQLException ex) + { + fail(ex.getMessage()); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/EncodingTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/EncodingTest.java index f1e893cc99..dbf2aefbe6 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/EncodingTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/EncodingTest.java @@ -8,50 +8,55 @@ import java.io.*; /** * Tests for the Encoding class. * - * $Id: EncodingTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: EncodingTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ */ -public class EncodingTest extends TestCase { - - public EncodingTest(String name) { - super(name); - } - - public void testCreation() throws Exception { - Encoding encoding; - encoding = Encoding.getEncoding("UNICODE", null); - assertEquals("UTF", encoding.name().substring(0, 3).toUpperCase()); - encoding = Encoding.getEncoding("SQL_ASCII", null); - assertTrue(encoding.name().toUpperCase().indexOf("ASCII") != -1); - assertEquals("When encoding is unknown the default encoding should be used", - Encoding.defaultEncoding(), - Encoding.getEncoding("UNKNOWN", null)); - encoding = Encoding.getEncoding("SQL_ASCII", "utf-8"); - assertTrue("Encoding passed in by the user should be preferred", - encoding.name().toUpperCase().indexOf("UTF") != -1); - } - - public void testTransformations() throws Exception { - Encoding encoding = Encoding.getEncoding("UNICODE", null); - assertEquals("ab", encoding.decode(new byte[] { 97, 98 })); - - assertEquals(2, encoding.encode("ab").length); - assertEquals(97, encoding.encode("a")[0]); - assertEquals(98, encoding.encode("b")[0]); - - encoding = Encoding.defaultEncoding(); - assertEquals("a".getBytes()[0], encoding.encode("a")[0]); - assertEquals(new String(new byte[] { 97 }), - encoding.decode(new byte[] { 97 })); - } - - public void testReader() throws Exception { - Encoding encoding = Encoding.getEncoding("SQL_ASCII", null); - InputStream stream = new ByteArrayInputStream(new byte[] { 97, 98 }); - Reader reader = encoding.getDecodingReader(stream); - assertEquals(97, reader.read()); - assertEquals(98, reader.read()); - assertEquals(-1, reader.read()); - } +public class EncodingTest extends TestCase +{ + + public EncodingTest(String name) + { + super(name); + } + + public void testCreation() throws Exception + { + Encoding encoding; + encoding = Encoding.getEncoding("UNICODE", null); + assertEquals("UTF", encoding.name().substring(0, 3).toUpperCase()); + encoding = Encoding.getEncoding("SQL_ASCII", null); + assertTrue(encoding.name().toUpperCase().indexOf("ASCII") != -1); + assertEquals("When encoding is unknown the default encoding should be used", + Encoding.defaultEncoding(), + Encoding.getEncoding("UNKNOWN", null)); + encoding = Encoding.getEncoding("SQL_ASCII", "utf-8"); + assertTrue("Encoding passed in by the user should be preferred", + encoding.name().toUpperCase().indexOf("UTF") != -1); + } + + public void testTransformations() throws Exception + { + Encoding encoding = Encoding.getEncoding("UNICODE", null); + assertEquals("ab", encoding.decode(new byte[] { 97, 98 })); + + assertEquals(2, encoding.encode("ab").length); + assertEquals(97, encoding.encode("a")[0]); + assertEquals(98, encoding.encode("b")[0]); + + encoding = Encoding.defaultEncoding(); + assertEquals("a".getBytes()[0], encoding.encode("a")[0]); + assertEquals(new String(new byte[] { 97 }), + encoding.decode(new byte[] { 97 })); + } + + public void testReader() throws Exception + { + Encoding encoding = Encoding.getEncoding("SQL_ASCII", null); + InputStream stream = new ByteArrayInputStream(new byte[] { 97, 98 }); + Reader reader = encoding.getDecodingReader(stream); + assertEquals(97, reader.read()); + assertEquals(98, reader.read()); + assertEquals( -1, reader.read()); + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/JBuilderTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/JBuilderTest.java index 94282706c6..8cd7af8951 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/JBuilderTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/JBuilderTest.java @@ -6,56 +6,65 @@ import java.sql.*; import java.math.BigDecimal; /** - * $Id: JBuilderTest.java,v 1.3 2001/09/23 04:11:14 momjian Exp $ + * $Id: JBuilderTest.java,v 1.4 2001/10/25 05:59:59 momjian Exp $ * * Some simple tests to check that the required components needed for JBuilder * stay working * */ -public class JBuilderTest extends TestCase { - - public JBuilderTest(String name) { - super(name); - } - - // Set up the fixture for this testcase: the tables for this test. - protected void setUp() throws Exception { - Connection con = JDBC2Tests.openDB(); - - JDBC2Tests.createTable( con, "test_c", - "source text,cost money,imageid int4" ); - - JDBC2Tests.closeDB(con); - } - - // Tear down the fixture for this test case. - protected void tearDown() throws Exception { - Connection con = JDBC2Tests.openDB(); - JDBC2Tests.dropTable(con, "test_c"); - JDBC2Tests.closeDB(con); - } - - /** - * This tests that Money types work. JDBCExplorer barfs if this fails. - */ - public void testMoney() { - try { - Connection con = JDBC2Tests.openDB(); - - Statement st=con.createStatement(); - ResultSet rs=st.executeQuery("select cost from test_c"); - assertNotNull(rs); - - while(rs.next()){ - double bd = rs.getDouble(1); - } - - rs.close(); - st.close(); - - JDBC2Tests.closeDB(con); - } catch(Exception ex) { - fail(ex.getMessage()); - } - } +public class JBuilderTest extends TestCase +{ + + public JBuilderTest(String name) + { + super(name); + } + + // Set up the fixture for this testcase: the tables for this test. + protected void setUp() throws Exception + { + Connection con = JDBC2Tests.openDB(); + + JDBC2Tests.createTable( con, "test_c", + "source text,cost money,imageid int4" ); + + JDBC2Tests.closeDB(con); + } + + // Tear down the fixture for this test case. + protected void tearDown() throws Exception + { + Connection con = JDBC2Tests.openDB(); + JDBC2Tests.dropTable(con, "test_c"); + JDBC2Tests.closeDB(con); + } + + /** + * This tests that Money types work. JDBCExplorer barfs if this fails. + */ + public void testMoney() + { + try + { + Connection con = JDBC2Tests.openDB(); + + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery("select cost from test_c"); + assertNotNull(rs); + + while (rs.next()) + { + double bd = rs.getDouble(1); + } + + rs.close(); + st.close(); + + JDBC2Tests.closeDB(con); + } + catch (Exception ex) + { + fail(ex.getMessage()); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/MiscTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/MiscTest.java index 1e4e34fc84..2e4a7e6848 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/MiscTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/MiscTest.java @@ -5,43 +5,50 @@ import junit.framework.TestCase; import java.sql.*; /** - * $Id: MiscTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: MiscTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ * * Some simple tests based on problems reported by users. Hopefully these will * help prevent previous problems from re-occuring ;-) * */ -public class MiscTest extends TestCase { - - public MiscTest(String name) { - super(name); - } - - /** - * Some versions of the driver would return rs as a null? - * - * Sasha was having this problem. - * - * Added Feb 13 2001 - */ - public void testDatabaseSelectNullBug() { - try { - Connection con = JDBC2Tests.openDB(); - - Statement st=con.createStatement(); - ResultSet rs=st.executeQuery("select datname from pg_database"); - assertNotNull(rs); - - while(rs.next()){ - String s = rs.getString(1); - } - - rs.close(); - st.close(); - - JDBC2Tests.closeDB(con); - } catch(Exception ex) { - fail(ex.getMessage()); - } - } +public class MiscTest extends TestCase +{ + + public MiscTest(String name) + { + super(name); + } + + /** + * Some versions of the driver would return rs as a null? + * + * Sasha was having this problem. + * + * Added Feb 13 2001 + */ + public void testDatabaseSelectNullBug() + { + try + { + Connection con = JDBC2Tests.openDB(); + + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery("select datname from pg_database"); + assertNotNull(rs); + + while (rs.next()) + { + String s = rs.getString(1); + } + + rs.close(); + st.close(); + + JDBC2Tests.closeDB(con); + } + catch (Exception ex) + { + fail(ex.getMessage()); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimeTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimeTest.java index f4cbb3c3da..03f484dad8 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimeTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimeTest.java @@ -5,37 +5,43 @@ import junit.framework.TestCase; import java.sql.*; /** - * $Id: TimeTest.java,v 1.2 2001/09/23 04:11:14 momjian Exp $ + * $Id: TimeTest.java,v 1.3 2001/10/25 05:59:59 momjian Exp $ * * Some simple tests based on problems reported by users. Hopefully these will * help prevent previous problems from re-occuring ;-) * */ -public class TimeTest extends TestCase { +public class TimeTest extends TestCase +{ private Connection con; - - public TimeTest(String name) { + + public TimeTest(String name) + { super(name); - } + } - protected void setUp() throws Exception { + protected void setUp() throws Exception + { con = JDBC2Tests.openDB(); JDBC2Tests.createTable(con, "testtime", "tm time"); - } + } - protected void tearDown() throws Exception { + protected void tearDown() throws Exception + { JDBC2Tests.dropTable(con, "testtime"); JDBC2Tests.closeDB(con); - } - - /** - * Tests the time methods in ResultSet - */ - public void testGetTime() { - try { + } + + /** + * Tests the time methods in ResultSet + */ + public void testGetTime() + { + try + { Statement stmt = con.createStatement(); - + assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testtime", "'01:02:03'"))); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testtime", "'23:59:59'"))); @@ -44,16 +50,20 @@ public class TimeTest extends TestCase { assertEquals(2, stmt.executeUpdate("DELETE FROM testtime")); stmt.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } - } - - /** - * Tests the time methods in PreparedStatement - */ - public void testSetTime() { - try { + } + + /** + * Tests the time methods in PreparedStatement + */ + public void testSetTime() + { + try + { PreparedStatement ps = con.prepareStatement(JDBC2Tests.insertSQL("testtime", "?")); Statement stmt = con.createStatement(); @@ -69,15 +79,18 @@ public class TimeTest extends TestCase { assertEquals(2, stmt.executeUpdate("DELETE FROM testtime")); stmt.close(); ps.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } - } + } - /** - * Helper for the TimeTests. It tests what should be in the db - */ - private void timeTest() throws SQLException { + /** + * Helper for the TimeTests. It tests what should be in the db + */ + private void timeTest() throws SQLException + { Statement st = con.createStatement(); ResultSet rs; java.sql.Time t; @@ -98,11 +111,12 @@ public class TimeTest extends TestCase { assertTrue(! rs.next()); rs.close(); - } + } - private java.sql.Time makeTime(int h, int m, int s) { + private java.sql.Time makeTime(int h, int m, int s) + { return java.sql.Time.valueOf(JDBC2Tests.fix(h, 2) + ":" + JDBC2Tests.fix(m, 2) + ":" + JDBC2Tests.fix(s, 2)); - } + } } diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java index 37be09a250..4b98ee5c21 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java @@ -5,7 +5,7 @@ import junit.framework.TestCase; import java.sql.*; /** - * $Id: TimestampTest.java,v 1.4 2001/09/29 03:11:11 momjian Exp $ + * $Id: TimestampTest.java,v 1.5 2001/10/25 05:59:59 momjian Exp $ * * This has been the most controversial pair of methods since 6.5 was released! * @@ -13,22 +13,26 @@ import java.sql.*; * MUST PASS this TestCase!!! * */ -public class TimestampTest extends TestCase { +public class TimestampTest extends TestCase +{ private Connection con; - public TimestampTest(String name) { + public TimestampTest(String name) + { super(name); } - protected void setUp() throws Exception { + protected void setUp() throws Exception + { con = JDBC2Tests.openDB(); Statement stmt = con.createStatement(); - + JDBC2Tests.createTable(con, "testtimestamp", "ts timestamp"); } - protected void tearDown() throws Exception { + protected void tearDown() throws Exception + { JDBC2Tests.dropTable(con, "testtimestamp"); JDBC2Tests.closeDB(con); } @@ -36,19 +40,21 @@ public class TimestampTest extends TestCase { /** * Tests the time methods in ResultSet */ - public void testGetTimestamp() { - try { + public void testGetTimestamp() + { + try + { Statement stmt = con.createStatement(); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testtimestamp", - "'1950-02-07 15:00:00'"))); + "'1950-02-07 15:00:00'"))); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testtimestamp", "'" + - getTimestamp(1970, 6, 2, 8, 13, 0, 0).toString() + - "'"))); + getTimestamp(1970, 6, 2, 8, 13, 0, 0).toString() + + "'"))); assertEquals(1, stmt.executeUpdate(JDBC2Tests.insertSQL("testtimestamp", - "'1970-06-02 08:13:00'"))); + "'1970-06-02 08:13:00'"))); // Fall through helper timestampTest(); @@ -56,7 +62,9 @@ public class TimestampTest extends TestCase { assertEquals(3, stmt.executeUpdate("DELETE FROM testtimestamp")); stmt.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -64,8 +72,10 @@ public class TimestampTest extends TestCase { /** * Tests the time methods in PreparedStatement */ - public void testSetTimestamp() { - try { + public void testSetTimestamp() + { + try + { Statement stmt = con.createStatement(); PreparedStatement pstmt = con.prepareStatement(JDBC2Tests.insertSQL("testtimestamp", "?")); @@ -85,7 +95,9 @@ public class TimestampTest extends TestCase { pstmt.close(); stmt.close(); - } catch(Exception ex) { + } + catch (Exception ex) + { fail(ex.getMessage()); } } @@ -93,7 +105,8 @@ public class TimestampTest extends TestCase { /** * Helper for the TimeTests. It tests what should be in the db */ - private void timestampTest() throws SQLException { + private void timestampTest() throws SQLException + { Statement stmt = con.createStatement(); ResultSet rs; java.sql.Timestamp t; @@ -115,20 +128,21 @@ public class TimestampTest extends TestCase { t = rs.getTimestamp(1); assertNotNull(t); assertTrue(t.equals(getTimestamp(1970, 6, 2, 8, 13, 0, 0))); - + assertTrue(! rs.next()); // end of table. Fail if more entries exist. rs.close(); stmt.close(); } - private java.sql.Timestamp getTimestamp(int y, int m, int d, int h, int mn, int se, int f) { - return java.sql.Timestamp.valueOf(JDBC2Tests.fix(y, 4) + "-" + - JDBC2Tests.fix(m, 2) + "-" + - JDBC2Tests.fix(d, 2) + " " + - JDBC2Tests.fix(h, 2) + ":" + + private java.sql.Timestamp getTimestamp(int y, int m, int d, int h, int mn, int se, int f) + { + return java.sql.Timestamp.valueOf(JDBC2Tests.fix(y, 4) + "-" + + JDBC2Tests.fix(m, 2) + "-" + + JDBC2Tests.fix(d, 2) + " " + + JDBC2Tests.fix(h, 2) + ":" + JDBC2Tests.fix(mn, 2) + ":" + JDBC2Tests.fix(se, 2) + "." + - JDBC2Tests.fix(f, 9)); + JDBC2Tests.fix(f, 9)); } } diff --git a/src/interfaces/jdbc/org/postgresql/util/MessageTranslator.java b/src/interfaces/jdbc/org/postgresql/util/MessageTranslator.java index 97fd32a869..d9f4810082 100644 --- a/src/interfaces/jdbc/org/postgresql/util/MessageTranslator.java +++ b/src/interfaces/jdbc/org/postgresql/util/MessageTranslator.java @@ -6,58 +6,74 @@ import java.text.*; /** * A singleton class to translate JDBC driver messages in SQLException's. */ -public class MessageTranslator { +public class MessageTranslator +{ // The singleton instance. private static MessageTranslator instance = null; private ResourceBundle bundle; - private MessageTranslator() { - try { + private MessageTranslator() + { + try + { bundle = ResourceBundle.getBundle("org.postgresql.errors"); - } catch(MissingResourceException e) { + } + catch (MissingResourceException e) + { // translation files have not been installed. bundle = null; - } + } } // Synchronized, otherwise multiple threads may perform the test and // assign to the singleton instance simultaneously. - private synchronized final static MessageTranslator getInstance() { - if (instance == null) { + private synchronized final static MessageTranslator getInstance() + { + if (instance == null) + { instance = new MessageTranslator(); } return instance; } - public final static String translate(String id, Object[] args) { + public final static String translate(String id, Object[] args) + { MessageTranslator translator = MessageTranslator.getInstance(); return translator._translate(id, args); } - private final String _translate(String id, Object[] args) { + private final String _translate(String id, Object[] args) + { String message; - if (bundle != null && id != null) { + if (bundle != null && id != null) + { // Now look up a localized message. If one is not found, then use // the supplied message instead. - try { + try + { message = bundle.getString(id); - } catch(MissingResourceException e) { + } + catch (MissingResourceException e) + { message = id; } - } else { + } + else + { message = id; } // Expand any arguments - if (args != null && message != null) { - message = MessageFormat.format(message,args); + if (args != null && message != null) + { + message = MessageFormat.format(message, args); } return message; - } + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/PGbytea.java b/src/interfaces/jdbc/org/postgresql/util/PGbytea.java index e994fce5bc..65db41d7ff 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PGbytea.java +++ b/src/interfaces/jdbc/org/postgresql/util/PGbytea.java @@ -5,82 +5,98 @@ import java.sql.*; /** * Converts to and from the postgresql bytea datatype used by the backend. * - * $Id: PGbytea.java,v 1.1 2001/09/10 15:07:05 momjian Exp $ + * $Id: PGbytea.java,v 1.2 2001/10/25 06:00:00 momjian Exp $ */ -public class PGbytea { +public class PGbytea +{ - /** - * Converts a PG bytea string (i.e. the text representation - * of the bytea data type) into a java byte[] - */ - public static byte[] toBytes(String s) throws SQLException { - if(s==null) - return null; - int slength = s.length(); - byte[] buf = new byte[slength]; - int bufpos = 0; - int thebyte; - char nextchar; - char secondchar; - for (int i = 0; i < slength; i++) { - nextchar = s.charAt(i); - if (nextchar == '\\') { - secondchar = s.charAt(++i); - if (secondchar == '\\') { - //escaped \ - buf[bufpos++] = (byte)'\\'; - } else { - thebyte = (secondchar-48)*64 + (s.charAt(++i)-48)*8 + (s.charAt(++i)-48); - if (thebyte > 127) - thebyte -= 256; - buf[bufpos++] = (byte)thebyte; - } - } else { - buf[bufpos++] = (byte)nextchar; - } - } - byte[] l_return = new byte[bufpos]; - System.arraycopy(buf,0,l_return,0,bufpos); - return l_return; - } + /** + * Converts a PG bytea string (i.e. the text representation + * of the bytea data type) into a java byte[] + */ + public static byte[] toBytes(String s) throws SQLException + { + if (s == null) + return null; + int slength = s.length(); + byte[] buf = new byte[slength]; + int bufpos = 0; + int thebyte; + char nextchar; + char secondchar; + for (int i = 0; i < slength; i++) + { + nextchar = s.charAt(i); + if (nextchar == '\\') + { + secondchar = s.charAt(++i); + if (secondchar == '\\') + { + //escaped \ + buf[bufpos++] = (byte)'\\'; + } + else + { + thebyte = (secondchar - 48) * 64 + (s.charAt(++i) - 48) * 8 + (s.charAt(++i) - 48); + if (thebyte > 127) + thebyte -= 256; + buf[bufpos++] = (byte)thebyte; + } + } + else + { + buf[bufpos++] = (byte)nextchar; + } + } + byte[] l_return = new byte[bufpos]; + System.arraycopy(buf, 0, l_return, 0, bufpos); + return l_return; + } - /** - * Converts a java byte[] into a PG bytea string (i.e. the text - * representation of the bytea data type) - */ - public static String toPGString(byte[] p_buf) throws SQLException - { - if(p_buf==null) - return null; - StringBuffer l_strbuf = new StringBuffer(); - for (int i = 0; i < p_buf.length; i++) { - int l_int = (int)p_buf[i]; - if (l_int < 0) { - l_int = 256 + l_int; - } - //we escape the same non-printable characters as the backend - //we must escape all 8bit characters otherwise when convering - //from java unicode to the db character set we may end up with - //question marks if the character set is SQL_ASCII - if (l_int < 040 || l_int > 0176) { - //escape charcter with the form \000, but need two \\ because of - //the parser - l_strbuf.append("\\"); - l_strbuf.append((char)(((l_int >> 6) & 0x3)+48)); - l_strbuf.append((char)(((l_int >> 3) & 0x7)+48)); - l_strbuf.append((char)((l_int & 0x07)+48)); - } else if (p_buf[i] == (byte)'\\') { - //escape the backslash character as \\, but need four \\\\ because - //of the parser - l_strbuf.append("\\\\"); - } else { - //other characters are left alone - l_strbuf.append((char)p_buf[i]); - } - } - return l_strbuf.toString(); - } + /** + * Converts a java byte[] into a PG bytea string (i.e. the text + * representation of the bytea data type) + */ + public static String toPGString(byte[] p_buf) throws SQLException + { + if (p_buf == null) + return null; + StringBuffer l_strbuf = new StringBuffer(); + for (int i = 0; i < p_buf.length; i++) + { + int l_int = (int)p_buf[i]; + if (l_int < 0) + { + l_int = 256 + l_int; + } + //we escape the same non-printable characters as the backend + //we must escape all 8bit characters otherwise when convering + //from java unicode to the db character set we may end up with + //question marks if the character set is SQL_ASCII + if (l_int < 040 || l_int > 0176) + { + //escape charcter with the form \000, but need two \\ because of + //the parser + l_strbuf.append("\\"); + l_strbuf.append((char)(((l_int >> 6) & 0x3) + 48)); + l_strbuf.append((char)(((l_int >> 3) & 0x7) + 48)); + l_strbuf.append((char)((l_int & 0x07) + 48)); + } + else if (p_buf[i] == (byte)'\\') + { + //escape the backslash character as \\, but need four \\\\ because + //of the parser + l_strbuf.append("\\\\"); + } + else + { + //other characters are left alone + l_strbuf.append((char)p_buf[i]); + } + } + return l_strbuf.toString(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/PGmoney.java b/src/interfaces/jdbc/org/postgresql/util/PGmoney.java index ef71e15666..4b999d98ce 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PGmoney.java +++ b/src/interfaces/jdbc/org/postgresql/util/PGmoney.java @@ -6,102 +6,110 @@ import java.sql.*; /** * This implements a class that handles the PostgreSQL money and cash types */ -public class PGmoney extends PGobject implements Serializable,Cloneable +public class PGmoney extends PGobject implements Serializable, Cloneable { - /** - * The value of the field - */ - public double val; + /** + * The value of the field + */ + public double val; - /** - * @param value of field - */ - public PGmoney(double value) { - this(); - val = value; - } + /** + * @param value of field + */ + public PGmoney(double value) + { + this(); + val = value; + } - /** - * 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 PGmoney(String value) throws SQLException - { - this(); - setValue(value); - } + /** + * 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 PGmoney(String value) throws SQLException + { + this(); + setValue(value); + } - /** - * Required by the driver - */ - public PGmoney() - { - setType("money"); - } + /** + * Required by the driver + */ + public PGmoney() + { + setType("money"); + } - /** - * @param s Definition of this point in PostgreSQL's syntax - * @exception SQLException on conversion failure - */ - public void setValue(String s) throws SQLException - { - try { - String s1; - boolean negative; + /** + * @param s Definition of this point in PostgreSQL's syntax + * @exception SQLException on conversion failure + */ + public void setValue(String s) throws SQLException + { + try + { + String s1; + boolean negative; - negative = (s.charAt(0) == '(') ; + negative = (s.charAt(0) == '(') ; - // Remove any () (for negative) & currency symbol - s1 = PGtokenizer.removePara(s).substring(1); + // Remove any () (for negative) & currency symbol + s1 = PGtokenizer.removePara(s).substring(1); - // Strip out any , in currency - int pos = s1.indexOf(','); - while (pos != -1) { - s1 = s1.substring(0,pos) + s1.substring(pos +1); - pos = s1.indexOf(','); - } + // Strip out any , in currency + int pos = s1.indexOf(','); + while (pos != -1) + { + s1 = s1.substring(0, pos) + s1.substring(pos + 1); + pos = s1.indexOf(','); + } - val = Double.valueOf(s1).doubleValue(); - val = negative ? -val : val; + val = Double.valueOf(s1).doubleValue(); + val = negative ? -val : val; - } catch(NumberFormatException e) { - throw new PSQLException("postgresql.money",e); - } - } + } + catch (NumberFormatException e) + { + throw new PSQLException("postgresql.money", e); + } + } - /** - * @param obj Object to compare with - * @return true if the two boxes are identical - */ - public boolean equals(Object obj) - { - if(obj instanceof PGmoney) { - PGmoney p = (PGmoney)obj; - return val == p.val; - } - return false; - } + /** + * @param obj Object to compare with + * @return true if the two boxes are identical + */ + public boolean equals(Object obj) + { + if (obj instanceof PGmoney) + { + PGmoney p = (PGmoney)obj; + return val == p.val; + } + return false; + } - /** - * This must be overidden to allow the object to be cloned - */ - public Object clone() - { - return new PGmoney(val); - } + /** + * This must be overidden to allow the object to be cloned + */ + public Object clone() + { + return new PGmoney(val); + } - /** - * @return the PGpoint in the syntax expected by org.postgresql - */ - public String getValue() - { - if (val < 0) { - return "-$" + (-val); - } - else { - return "$"+val; - } - } + /** + * @return the PGpoint in the syntax expected by org.postgresql + */ + public String getValue() + { + if (val < 0) + { + return "-$" + ( -val); + } + else + { + return "$" + val; + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/PGobject.java b/src/interfaces/jdbc/org/postgresql/util/PGobject.java index c04d0b450e..28df4940c8 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PGobject.java +++ b/src/interfaces/jdbc/org/postgresql/util/PGobject.java @@ -6,97 +6,96 @@ import java.sql.*; import java.util.*; /** - * org.postgresql.PG_Object is a class used to describe unknown types + * org.postgresql.PG_Object is a class used to describe unknown types * An unknown type is any type that is unknown by JDBC Standards * *

As of PostgreSQL 6.3, this allows user code to add their own * handlers via a call to org.postgresql.Connection. These handlers * must extend this class. */ -public class PGobject implements Serializable,Cloneable +public class PGobject implements Serializable, Cloneable { - protected String type; - protected String value; - - /** - * This is called by org.postgresql.Connection.getObject() to create the - * object. - */ - public PGobject() - { - } - - /** - * This method sets the type of this object. - * - *

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 org.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 org.postgresql - */ - public String toString() - { - return getValue(); - } + protected String type; + protected String value; + + /** + * This is called by org.postgresql.Connection.getObject() to create the + * object. + */ + public PGobject() + {} + + /** + * This method sets the type of this object. + * + *

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 org.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 org.postgresql + */ + public String toString() + { + return getValue(); + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/PGtokenizer.java b/src/interfaces/jdbc/org/postgresql/util/PGtokenizer.java index b9d1bb68ff..c8605061eb 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PGtokenizer.java +++ b/src/interfaces/jdbc/org/postgresql/util/PGtokenizer.java @@ -18,180 +18,186 @@ import java.util.*; */ public class PGtokenizer { - // Our tokens - protected Vector tokens; - - /** - * Create a tokeniser. - * - *

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') - 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"); - } - - /** - * Removes < and > from the beginning and end of all tokens - * @return String without the < or > - */ - public void removeAngle() - { - remove("<",">"); - } + // Our tokens + protected Vector tokens; + + /** + * Create a tokeniser. + * + *

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("<", ">"); + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java index d5c8cefa7d..99f6f310d7 100644 --- a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java +++ b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java @@ -8,104 +8,109 @@ import java.sql.*; */ public class PSQLException extends SQLException { - private String message; + private String message; - /** - * This provides the same functionality to SQLException - * @param error Error string - */ - public PSQLException(String error) { - super(); - translate(error,null); - } + /** + * This provides the same functionality to SQLException + * @param error Error string + */ + public PSQLException(String error) + { + super(); + translate(error, null); + } - /** - * A more generic entry point. - * @param error Error string or standard message id - * @param args Array of arguments - */ - public PSQLException(String error,Object[] args) - { - //super(); - translate(error,args); - } + /** + * A more generic entry point. + * @param error Error string or standard message id + * @param args Array of arguments + */ + public PSQLException(String error, Object[] args) + { + //super(); + translate(error, args); + } - /** - * Helper version for 1 arg - */ - public PSQLException(String error,Object arg) - { - super(); - Object[] argv = new Object[1]; - argv[0] = arg; - translate(error,argv); - } + /** + * Helper version for 1 arg + */ + public PSQLException(String error, Object arg) + { + super(); + Object[] argv = new Object[1]; + argv[0] = arg; + translate(error, argv); + } - /** - * Helper version for 1 arg. This is used for debug purposes only with - * some unusual Exception's. It allows the originiating Exceptions stack - * trace to be returned. - */ - public PSQLException(String error,Exception ex) - { - super(); + /** + * Helper version for 1 arg. This is used for debug purposes only with + * some unusual Exception's. It allows the originiating Exceptions stack + * trace to be returned. + */ + public PSQLException(String error, Exception ex) + { + super(); - Object[] argv = new Object[1]; + Object[] argv = new Object[1]; - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - PrintWriter pw = new PrintWriter(baos); - pw.println("Exception: "+ex.toString()+"\nStack Trace:\n"); - ex.printStackTrace(pw); - pw.println("End of Stack Trace"); - pw.flush(); - argv[0] = baos.toString(); - pw.close(); - baos.close(); - } catch(Exception ioe) { - argv[0] = ex.toString()+"\nIO Error on stack trace generation! "+ioe.toString(); - } + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter pw = new PrintWriter(baos); + pw.println("Exception: " + ex.toString() + "\nStack Trace:\n"); + ex.printStackTrace(pw); + pw.println("End of Stack Trace"); + pw.flush(); + argv[0] = baos.toString(); + pw.close(); + baos.close(); + } + catch (Exception ioe) + { + argv[0] = ex.toString() + "\nIO Error on stack trace generation! " + ioe.toString(); + } - translate(error,argv); - } + translate(error, argv); + } - /** - * Helper version for 2 args - */ - public PSQLException(String error,Object arg1,Object arg2) - { - super(); - Object[] argv = new Object[2]; - argv[0] = arg1; - argv[1] = arg2; - translate(error,argv); - } + /** + * Helper version for 2 args + */ + public PSQLException(String error, Object arg1, Object arg2) + { + super(); + Object[] argv = new Object[2]; + argv[0] = arg1; + argv[1] = arg2; + translate(error, argv); + } - private void translate(String error, Object[] args) { - message = MessageTranslator.translate(error,args); + private void translate(String error, Object[] args) + { + message = MessageTranslator.translate(error, args); } - /** - * Overides Throwable - */ - public String getLocalizedMessage() - { - return message; - } + /** + * Overides Throwable + */ + public String getLocalizedMessage() + { + return message; + } - /** - * Overides Throwable - */ - public String getMessage() - { - return message; - } + /** + * Overides Throwable + */ + public String getMessage() + { + return message; + } - /** - * Overides Object - */ - public String toString() - { - return message; - } + /** + * Overides Object + */ + public String toString() + { + return message; + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/Serialize.java b/src/interfaces/jdbc/org/postgresql/util/Serialize.java index 2e80102ecb..7b26f06f4f 100644 --- a/src/interfaces/jdbc/org/postgresql/util/Serialize.java +++ b/src/interfaces/jdbc/org/postgresql/util/Serialize.java @@ -19,11 +19,11 @@ import java.sql.*; * a table to be used as a data type. However, Postgres support of * this feature is incomplete. The basic ability to create and use * a table as a field type in another table exists:
- * CREATE TABLE myclass( var1 TEXT, var2 INTEGER );
- * CREATE TABLE othertable( field1 TEXT, field2 myclass );
- * INSERT INTO myclass VALUES ('Hello', 1);
- * INSERT INTO othertable VALUES ('World', xxxx::myclass);
- * where xxxx is the OID of a row in myclass
+ * CREATE TABLE myclass( var1 TEXT, var2 INTEGER );
+ * CREATE TABLE othertable( field1 TEXT, field2 myclass );
+ * INSERT INTO myclass VALUES ('Hello', 1);
+ * INSERT INTO othertable VALUES ('World', xxxx::myclass);
+ * where xxxx is the OID of a row in myclass
* This lets othertable reference a myclass instance but * the support to actually make any use of the myclass data type * is not there. For instance, you cannot compare the myclass field @@ -33,13 +33,13 @@ import java.sql.*; * syntax appears to work.

* * Queries like:
- * SELECT othertable.field2.var1 FROM othertable;
+ * SELECT othertable.field2.var1 FROM othertable;
* will not work but were suggested in the original Postgres * design documents.

* Because support is incomplete for table data types, tables * such as othertable that hold java instances should also * hold an oid field for the same java instance:
- * CREATE othertable( field1 TEXT, field2 myclass, myclassOID oid);
+ * CREATE othertable( field1 TEXT, field2 myclass, myclassOID oid);
* This oid-type field would be set with setInt() immediately after * setting the myclass-type field with setObject(). The order of these * set calls matters since the oid is not available until after @@ -107,230 +107,271 @@ import java.sql.*; */ public class Serialize { - // This is the connection that the instance refers to - protected org.postgresql.Connection conn; - - // This is the table name - protected String tableName; - - // This is the class name - protected String className; - - // This is the Class for this serialzed object - protected Class ourClass; - - /** - * This creates an instance that can be used to serialize or deserialize - * a Java object from a PostgreSQL table. - */ - public Serialize(org.postgresql.Connection c,String type) throws SQLException - { - try { - conn = c; - DriverManager.println("Serialize: initializing instance for type: " + type); - tableName = toPostgreSQL(type); - className = type; - ourClass = Class.forName(className); - } catch(ClassNotFoundException cnfe) { - DriverManager.println("Serialize: " + className + " java class not found"); - throw new PSQLException("postgresql.serial.noclass",type); - } - - // Second check, the type must be a table - boolean status = false; - ResultSet rs = conn.ExecSQL("select typname from pg_type,pg_class where typname=relname and typname='" + tableName + "'"); - if(rs!=null) { - if(rs.next()) { - status = true; - DriverManager.println("Serialize: " + tableName + " table found"); + // This is the connection that the instance refers to + protected org.postgresql.Connection conn; + + // This is the table name + protected String tableName; + + // This is the class name + protected String className; + + // This is the Class for this serialzed object + protected Class ourClass; + + /** + * This creates an instance that can be used to serialize or deserialize + * a Java object from a PostgreSQL table. + */ + public Serialize(org.postgresql.Connection c, String type) throws SQLException + { + try + { + conn = c; + DriverManager.println("Serialize: initializing instance for type: " + type); + tableName = toPostgreSQL(type); + className = type; + ourClass = Class.forName(className); + } + catch (ClassNotFoundException cnfe) + { + DriverManager.println("Serialize: " + className + " java class not found"); + throw new PSQLException("postgresql.serial.noclass", type); + } + + // Second check, the type must be a table + boolean status = false; + ResultSet rs = conn.ExecSQL("select typname from pg_type,pg_class where typname=relname and typname='" + tableName + "'"); + if (rs != null) + { + if (rs.next()) + { + status = true; + DriverManager.println("Serialize: " + tableName + " table found"); + } + rs.close(); + } + // This should never occur, as org.postgresql has it's own internal checks + if (!status) + { + DriverManager.println("Serialize: " + tableName + " table not found"); + throw new PSQLException("postgresql.serial.table", type); } - rs.close(); + // Finally cache the fields within the table } - // This should never occur, as org.postgresql has it's own internal checks - if(!status) { - DriverManager.println("Serialize: " + tableName + " table not found"); - throw new PSQLException("postgresql.serial.table",type); + + /** + * Constructor when Object is passed in + */ + public Serialize(org.postgresql.Connection c, Object o) throws SQLException + { + this(c, o.getClass().getName()); } - // Finally cache the fields within the table - } - - /** - * Constructor when Object is passed in - */ - public Serialize(org.postgresql.Connection c,Object o) throws SQLException - { - this(c, o.getClass().getName()); - } - - /** - * Constructor when Class is passed in - */ - public Serialize(org.postgresql.Connection c, Class cls) throws SQLException - { - this(c, cls.getName()); - } - - /** - * This fetches an object from a table, given it's OID - * @param oid The oid of the object - * @return Object relating to oid - * @exception SQLException on error - */ + + /** + * Constructor when Class is passed in + */ + public Serialize(org.postgresql.Connection c, Class cls) throws SQLException + { + this(c, cls.getName()); + } + + /** + * This fetches an object from a table, given it's OID + * @param oid The oid of the object + * @return Object relating to oid + * @exception SQLException on error + */ public Object fetch(int oid) throws SQLException { - try { + try + { DriverManager.println("Serialize.fetch: " + "attempting to instantiate object of type: " + ourClass.getName() ); - Object obj = ourClass.newInstance(); + Object obj = ourClass.newInstance(); DriverManager.println("Serialize.fetch: " + "instantiated object of type: " + ourClass.getName() ); - // NB: we use java.lang.reflect here to prevent confusion with - // the org.postgresql.Field - - // used getFields to get only public fields. We have no way to set values - // for other declarations. Maybe look for setFieldName() methods? - java.lang.reflect.Field f[] = ourClass.getFields(); - boolean hasOID=false; - int oidFIELD=-1; + // NB: we use java.lang.reflect here to prevent confusion with + // the org.postgresql.Field - StringBuffer sb = new StringBuffer("select"); - char sep=' '; - // build a select for the fields. Look for the oid field to use in the where - for(int i=0;i - * - * If the object has an int called OID, and it is > 0, then - * that value is used for the OID, and the table will be updated. - * If the value of OID is 0, then a new row will be created, and the - * value of OID will be set in the object. This enables an object's - * value in the database to be updateable. - * - * If the object has no int called OID, then the object is stored. However - * if the object is later retrieved, amended and stored again, it's new - * state will be appended to the table, and will not overwrite the old - * entries. - * - * @param o Object to store (must implement Serializable) - * @return oid of stored object - * @exception SQLException on error - */ + /** + * This stores an object into a table, returning it's OID.

+ * + * If the object has an int called OID, and it is > 0, then + * that value is used for the OID, and the table will be updated. + * If the value of OID is 0, then a new row will be created, and the + * value of OID will be set in the object. This enables an object's + * value in the database to be updateable. + * + * If the object has no int called OID, then the object is stored. However + * if the object is later retrieved, amended and stored again, it's new + * state will be appended to the table, and will not overwrite the old + * entries. + * + * @param o Object to store (must implement Serializable) + * @return oid of stored object + * @exception SQLException on error + */ public int store(Object o) throws SQLException { - try { - // NB: we use java.lang.reflect here to prevent confusion with + try + { + // NB: we use java.lang.reflect here to prevent confusion with // the org.postgresql.Field // don't save private fields since we would not be able to fetch them java.lang.reflect.Field f[] = ourClass.getFields(); - boolean hasOID=false; - int oidFIELD=-1; - boolean update=false; + boolean hasOID = false; + int oidFIELD = -1; + boolean update = false; // Find out if we have an oid value - for(int i=0;i 0; } } - StringBuffer sb = new StringBuffer(update?"update "+tableName+" set":"insert into "+tableName+" "); - char sep=update?' ':'('; - for(int i=0;i -1) { - StringBuffer buf = new StringBuffer(); - StringTokenizer tok = new StringTokenizer(s, "'"); - // handle quote as 1St charater - if (idx > 0) buf.append(tok.nextToken()); - - while(tok.hasMoreTokens()) - buf.append("''").append(tok.nextToken()); - - s = buf.toString(); - } - - // if the string has backslashes in it escape them them as \\ - if ((idx = s.indexOf("\\")) > -1) { - StringBuffer buf = new StringBuffer(); - StringTokenizer tok = new StringTokenizer(s, "\\"); - if (idx > 0) buf.append(tok.nextToken()); - - while(tok.hasMoreTokens()) - buf.append("\\\\").append(tok.nextToken()); - - s = buf.toString(); - } - - return s; - } - - /** - * This method is not used by the driver, but it creates a table, given - * a Serializable Java Object. It should be used before serializing any - * objects. - * @param c Connection to database - * @param o Object to base table on - * @exception SQLException on error - */ - public static void create(org.postgresql.Connection con,Object o) throws SQLException - { - create(con,o.getClass()); - } - - /** - * This method is not used by the driver, but it creates a table, given - * a Serializable Java Object. It should be used before serializing any - * objects. - * @param c Connection to database - * @param o Class to base table on - * @exception SQLException on error - */ - public static void create(org.postgresql.Connection con,Class c) throws SQLException + if (s == null) + return ""; + + // if the string has single quotes in it escape them as '' + if ((idx = s.indexOf("'")) > -1) + { + StringBuffer buf = new StringBuffer(); + StringTokenizer tok = new StringTokenizer(s, "'"); + // handle quote as 1St charater + if (idx > 0) + buf.append(tok.nextToken()); + + while (tok.hasMoreTokens()) + buf.append("''").append(tok.nextToken()); + + s = buf.toString(); + } + + // if the string has backslashes in it escape them them as \\ + if ((idx = s.indexOf("\\")) > -1) + { + StringBuffer buf = new StringBuffer(); + StringTokenizer tok = new StringTokenizer(s, "\\"); + if (idx > 0) + buf.append(tok.nextToken()); + + while (tok.hasMoreTokens()) + buf.append("\\\\").append(tok.nextToken()); + + s = buf.toString(); + } + + return s; + } + + /** + * This method is not used by the driver, but it creates a table, given + * a Serializable Java Object. It should be used before serializing any + * objects. + * @param c Connection to database + * @param o Object to base table on + * @exception SQLException on error + */ + public static void create(org.postgresql.Connection con, Object o) throws SQLException { - if(c.isInterface()) throw new PSQLException("postgresql.serial.interface"); + create(con, o.getClass()); + } + + /** + * This method is not used by the driver, but it creates a table, given + * a Serializable Java Object. It should be used before serializing any + * objects. + * @param c Connection to database + * @param o Class to base table on + * @exception SQLException on error + */ + public static void create(org.postgresql.Connection con, Class c) throws SQLException + { + if (c.isInterface()) + throw new PSQLException("postgresql.serial.interface"); // See if the table exists String tableName = toPostgreSQL(c.getName()); - ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '"+tableName+"'"); - if( rs.next() ) { - DriverManager.println("Serialize.create: table "+tableName+" exists, skipping"); + ResultSet rs = con.ExecSQL("select relname from pg_class where relname = '" + tableName + "'"); + if ( rs.next() ) + { + DriverManager.println("Serialize.create: table " + tableName + " exists, skipping"); rs.close(); - return; + return ; } // else table not found, so create it DriverManager.println("Serialize.create: table " + tableName + " not found, creating" ); - // No entries returned, so the table doesn't exist + // No entries returned, so the table doesn't exist - StringBuffer sb = new StringBuffer("create table "); - sb.append(tableName); - char sep='('; + StringBuffer sb = new StringBuffer("create table "); + sb.append(tableName); + char sep = '('; // java.lang.reflect.Field[] fields = c.getDeclaredFields(); // Only store public fields, another limitation! - java.lang.reflect.Field[] fields = c.getFields(); - for(int i=0;i - * - * Because of this, a Class name may not have _ in the name.

- * Another limitation, is that the entire class name (including packages) - * cannot be longer than 32 characters (a limit forced by PostgreSQL). - * - * @param name Class name - * @return PostgreSQL table name - * @exception SQLException on error - */ - public static String toPostgreSQL(String name) throws SQLException - { - name = name.toLowerCase(); - - if(name.indexOf("_")>-1) - throw new PSQLException("postgresql.serial.underscore"); - - // Postgres table names can only be 32 character long. - // Reserve 1 char, so allow only up to 31 chars. - // If the full class name with package is too long - // then just use the class name. If the class name is - // too long throw an exception. - // - if( name.length() > 31 ) { - name = name.substring(name.lastIndexOf(".") + 1); - if( name.length() >31 ) - throw new PSQLException("postgresql.serial.namelength",name,new Integer(name.length())); - } - return name.replace('.','_'); - } - - - /** - * This converts a org.postgresql table to a Java Class name, by replacing _ with - * .

- * - * @param name PostgreSQL table name - * @return Class name - * @exception SQLException on error - */ - public static String toClassName(String name) throws SQLException - { - name = name.toLowerCase(); - return name.replace('_','.'); - } + // This is used to translate between Java primitives and PostgreSQL types. + private static final String tp[][] = { + // {"boolean", "int1"}, + {"boolean", "bool"}, + {"double", "float8"}, + {"float", "float4"}, + {"int", "int4"}, + // {"long", "int4"}, + {"long", "int8"}, + {"short", "int2"}, + {"java.lang.String", "text"}, + {"java.lang.Integer", "int4"}, + {"java.lang.Float", "float4"}, + {"java.lang.Double", "float8"}, + {"java.lang.Short", "int2"}, + {"char", "char"}, + {"byte", "int2"} + }; + + /** + * This converts a Java Class name to a org.postgresql table, by replacing . with + * _

+ * + * Because of this, a Class name may not have _ in the name.

+ * Another limitation, is that the entire class name (including packages) + * cannot be longer than 32 characters (a limit forced by PostgreSQL). + * + * @param name Class name + * @return PostgreSQL table name + * @exception SQLException on error + */ + public static String toPostgreSQL(String name) throws SQLException + { + name = name.toLowerCase(); + + if (name.indexOf("_") > -1) + throw new PSQLException("postgresql.serial.underscore"); + + // Postgres table names can only be 32 character long. + // Reserve 1 char, so allow only up to 31 chars. + // If the full class name with package is too long + // then just use the class name. If the class name is + // too long throw an exception. + // + if ( name.length() > 31 ) + { + name = name.substring(name.lastIndexOf(".") + 1); + if ( name.length() > 31 ) + throw new PSQLException("postgresql.serial.namelength", name, new Integer(name.length())); + } + return name.replace('.', '_'); + } + + + /** + * This converts a org.postgresql table to a Java Class name, by replacing _ with + * .

+ * + * @param name PostgreSQL table name + * @return Class name + * @exception SQLException on error + */ + public static String toClassName(String name) throws SQLException + { + name = name.toLowerCase(); + return name.replace('_', '.'); + } } diff --git a/src/interfaces/jdbc/org/postgresql/util/UnixCrypt.java b/src/interfaces/jdbc/org/postgresql/util/UnixCrypt.java index 36c640c4b4..e06698ca25 100644 --- a/src/interfaces/jdbc/org/postgresql/util/UnixCrypt.java +++ b/src/interfaces/jdbc/org/postgresql/util/UnixCrypt.java @@ -14,662 +14,671 @@ package org.postgresql.util; */ public class UnixCrypt extends Object { - // - // Null constructor - can't instantiate class - private UnixCrypt() - { - } - - private static final char[] saltChars = - ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./".toCharArray()); - - private static final int ITERATIONS = 16; - - private static final int con_salt[] = - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, - 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, - 0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, - 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, - 0x3D, 0x3E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - - private static final boolean shifts2[] = - { - false, false, true, true, true, true, true, true, - false, true, true, true, true, true, true, false - }; - - private static final int skb[][] = - { - { - /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ - 0x00000000, 0x00000010, 0x20000000, 0x20000010, - 0x00010000, 0x00010010, 0x20010000, 0x20010010, - 0x00000800, 0x00000810, 0x20000800, 0x20000810, - 0x00010800, 0x00010810, 0x20010800, 0x20010810, - 0x00000020, 0x00000030, 0x20000020, 0x20000030, - 0x00010020, 0x00010030, 0x20010020, 0x20010030, - 0x00000820, 0x00000830, 0x20000820, 0x20000830, - 0x00010820, 0x00010830, 0x20010820, 0x20010830, - 0x00080000, 0x00080010, 0x20080000, 0x20080010, - 0x00090000, 0x00090010, 0x20090000, 0x20090010, - 0x00080800, 0x00080810, 0x20080800, 0x20080810, - 0x00090800, 0x00090810, 0x20090800, 0x20090810, - 0x00080020, 0x00080030, 0x20080020, 0x20080030, - 0x00090020, 0x00090030, 0x20090020, 0x20090030, - 0x00080820, 0x00080830, 0x20080820, 0x20080830, - 0x00090820, 0x00090830, 0x20090820, 0x20090830, - }, - { - /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ - 0x00000000, 0x02000000, 0x00002000, 0x02002000, - 0x00200000, 0x02200000, 0x00202000, 0x02202000, - 0x00000004, 0x02000004, 0x00002004, 0x02002004, - 0x00200004, 0x02200004, 0x00202004, 0x02202004, - 0x00000400, 0x02000400, 0x00002400, 0x02002400, - 0x00200400, 0x02200400, 0x00202400, 0x02202400, - 0x00000404, 0x02000404, 0x00002404, 0x02002404, - 0x00200404, 0x02200404, 0x00202404, 0x02202404, - 0x10000000, 0x12000000, 0x10002000, 0x12002000, - 0x10200000, 0x12200000, 0x10202000, 0x12202000, - 0x10000004, 0x12000004, 0x10002004, 0x12002004, - 0x10200004, 0x12200004, 0x10202004, 0x12202004, - 0x10000400, 0x12000400, 0x10002400, 0x12002400, - 0x10200400, 0x12200400, 0x10202400, 0x12202400, - 0x10000404, 0x12000404, 0x10002404, 0x12002404, - 0x10200404, 0x12200404, 0x10202404, 0x12202404, - }, - { - /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ - 0x00000000, 0x00000001, 0x00040000, 0x00040001, - 0x01000000, 0x01000001, 0x01040000, 0x01040001, - 0x00000002, 0x00000003, 0x00040002, 0x00040003, - 0x01000002, 0x01000003, 0x01040002, 0x01040003, - 0x00000200, 0x00000201, 0x00040200, 0x00040201, - 0x01000200, 0x01000201, 0x01040200, 0x01040201, - 0x00000202, 0x00000203, 0x00040202, 0x00040203, - 0x01000202, 0x01000203, 0x01040202, 0x01040203, - 0x08000000, 0x08000001, 0x08040000, 0x08040001, - 0x09000000, 0x09000001, 0x09040000, 0x09040001, - 0x08000002, 0x08000003, 0x08040002, 0x08040003, - 0x09000002, 0x09000003, 0x09040002, 0x09040003, - 0x08000200, 0x08000201, 0x08040200, 0x08040201, - 0x09000200, 0x09000201, 0x09040200, 0x09040201, - 0x08000202, 0x08000203, 0x08040202, 0x08040203, - 0x09000202, 0x09000203, 0x09040202, 0x09040203, - }, - { - /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ - 0x00000000, 0x00100000, 0x00000100, 0x00100100, - 0x00000008, 0x00100008, 0x00000108, 0x00100108, - 0x00001000, 0x00101000, 0x00001100, 0x00101100, - 0x00001008, 0x00101008, 0x00001108, 0x00101108, - 0x04000000, 0x04100000, 0x04000100, 0x04100100, - 0x04000008, 0x04100008, 0x04000108, 0x04100108, - 0x04001000, 0x04101000, 0x04001100, 0x04101100, - 0x04001008, 0x04101008, 0x04001108, 0x04101108, - 0x00020000, 0x00120000, 0x00020100, 0x00120100, - 0x00020008, 0x00120008, 0x00020108, 0x00120108, - 0x00021000, 0x00121000, 0x00021100, 0x00121100, - 0x00021008, 0x00121008, 0x00021108, 0x00121108, - 0x04020000, 0x04120000, 0x04020100, 0x04120100, - 0x04020008, 0x04120008, 0x04020108, 0x04120108, - 0x04021000, 0x04121000, 0x04021100, 0x04121100, - 0x04021008, 0x04121008, 0x04021108, 0x04121108, - }, - { - /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ - 0x00000000, 0x10000000, 0x00010000, 0x10010000, - 0x00000004, 0x10000004, 0x00010004, 0x10010004, - 0x20000000, 0x30000000, 0x20010000, 0x30010000, - 0x20000004, 0x30000004, 0x20010004, 0x30010004, - 0x00100000, 0x10100000, 0x00110000, 0x10110000, - 0x00100004, 0x10100004, 0x00110004, 0x10110004, - 0x20100000, 0x30100000, 0x20110000, 0x30110000, - 0x20100004, 0x30100004, 0x20110004, 0x30110004, - 0x00001000, 0x10001000, 0x00011000, 0x10011000, - 0x00001004, 0x10001004, 0x00011004, 0x10011004, - 0x20001000, 0x30001000, 0x20011000, 0x30011000, - 0x20001004, 0x30001004, 0x20011004, 0x30011004, - 0x00101000, 0x10101000, 0x00111000, 0x10111000, - 0x00101004, 0x10101004, 0x00111004, 0x10111004, - 0x20101000, 0x30101000, 0x20111000, 0x30111000, - 0x20101004, 0x30101004, 0x20111004, 0x30111004, - }, - { - /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ - 0x00000000, 0x08000000, 0x00000008, 0x08000008, - 0x00000400, 0x08000400, 0x00000408, 0x08000408, - 0x00020000, 0x08020000, 0x00020008, 0x08020008, - 0x00020400, 0x08020400, 0x00020408, 0x08020408, - 0x00000001, 0x08000001, 0x00000009, 0x08000009, - 0x00000401, 0x08000401, 0x00000409, 0x08000409, - 0x00020001, 0x08020001, 0x00020009, 0x08020009, - 0x00020401, 0x08020401, 0x00020409, 0x08020409, - 0x02000000, 0x0A000000, 0x02000008, 0x0A000008, - 0x02000400, 0x0A000400, 0x02000408, 0x0A000408, - 0x02020000, 0x0A020000, 0x02020008, 0x0A020008, - 0x02020400, 0x0A020400, 0x02020408, 0x0A020408, - 0x02000001, 0x0A000001, 0x02000009, 0x0A000009, - 0x02000401, 0x0A000401, 0x02000409, 0x0A000409, - 0x02020001, 0x0A020001, 0x02020009, 0x0A020009, - 0x02020401, 0x0A020401, 0x02020409, 0x0A020409, - }, - { - /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ - 0x00000000, 0x00000100, 0x00080000, 0x00080100, - 0x01000000, 0x01000100, 0x01080000, 0x01080100, - 0x00000010, 0x00000110, 0x00080010, 0x00080110, - 0x01000010, 0x01000110, 0x01080010, 0x01080110, - 0x00200000, 0x00200100, 0x00280000, 0x00280100, - 0x01200000, 0x01200100, 0x01280000, 0x01280100, - 0x00200010, 0x00200110, 0x00280010, 0x00280110, - 0x01200010, 0x01200110, 0x01280010, 0x01280110, - 0x00000200, 0x00000300, 0x00080200, 0x00080300, - 0x01000200, 0x01000300, 0x01080200, 0x01080300, - 0x00000210, 0x00000310, 0x00080210, 0x00080310, - 0x01000210, 0x01000310, 0x01080210, 0x01080310, - 0x00200200, 0x00200300, 0x00280200, 0x00280300, - 0x01200200, 0x01200300, 0x01280200, 0x01280300, - 0x00200210, 0x00200310, 0x00280210, 0x00280310, - 0x01200210, 0x01200310, 0x01280210, 0x01280310, - }, - { - /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ - 0x00000000, 0x04000000, 0x00040000, 0x04040000, - 0x00000002, 0x04000002, 0x00040002, 0x04040002, - 0x00002000, 0x04002000, 0x00042000, 0x04042000, - 0x00002002, 0x04002002, 0x00042002, 0x04042002, - 0x00000020, 0x04000020, 0x00040020, 0x04040020, - 0x00000022, 0x04000022, 0x00040022, 0x04040022, - 0x00002020, 0x04002020, 0x00042020, 0x04042020, - 0x00002022, 0x04002022, 0x00042022, 0x04042022, - 0x00000800, 0x04000800, 0x00040800, 0x04040800, - 0x00000802, 0x04000802, 0x00040802, 0x04040802, - 0x00002800, 0x04002800, 0x00042800, 0x04042800, - 0x00002802, 0x04002802, 0x00042802, 0x04042802, - 0x00000820, 0x04000820, 0x00040820, 0x04040820, - 0x00000822, 0x04000822, 0x00040822, 0x04040822, - 0x00002820, 0x04002820, 0x00042820, 0x04042820, - 0x00002822, 0x04002822, 0x00042822, 0x04042822, - }, - }; - - private static final int SPtrans[][] = - { - { - /* nibble 0 */ - 0x00820200, 0x00020000, 0x80800000, 0x80820200, - 0x00800000, 0x80020200, 0x80020000, 0x80800000, - 0x80020200, 0x00820200, 0x00820000, 0x80000200, - 0x80800200, 0x00800000, 0x00000000, 0x80020000, - 0x00020000, 0x80000000, 0x00800200, 0x00020200, - 0x80820200, 0x00820000, 0x80000200, 0x00800200, - 0x80000000, 0x00000200, 0x00020200, 0x80820000, - 0x00000200, 0x80800200, 0x80820000, 0x00000000, - 0x00000000, 0x80820200, 0x00800200, 0x80020000, - 0x00820200, 0x00020000, 0x80000200, 0x00800200, - 0x80820000, 0x00000200, 0x00020200, 0x80800000, - 0x80020200, 0x80000000, 0x80800000, 0x00820000, - 0x80820200, 0x00020200, 0x00820000, 0x80800200, - 0x00800000, 0x80000200, 0x80020000, 0x00000000, - 0x00020000, 0x00800000, 0x80800200, 0x00820200, - 0x80000000, 0x80820000, 0x00000200, 0x80020200, - }, - { - /* nibble 1 */ - 0x10042004, 0x00000000, 0x00042000, 0x10040000, - 0x10000004, 0x00002004, 0x10002000, 0x00042000, - 0x00002000, 0x10040004, 0x00000004, 0x10002000, - 0x00040004, 0x10042000, 0x10040000, 0x00000004, - 0x00040000, 0x10002004, 0x10040004, 0x00002000, - 0x00042004, 0x10000000, 0x00000000, 0x00040004, - 0x10002004, 0x00042004, 0x10042000, 0x10000004, - 0x10000000, 0x00040000, 0x00002004, 0x10042004, - 0x00040004, 0x10042000, 0x10002000, 0x00042004, - 0x10042004, 0x00040004, 0x10000004, 0x00000000, - 0x10000000, 0x00002004, 0x00040000, 0x10040004, - 0x00002000, 0x10000000, 0x00042004, 0x10002004, - 0x10042000, 0x00002000, 0x00000000, 0x10000004, - 0x00000004, 0x10042004, 0x00042000, 0x10040000, - 0x10040004, 0x00040000, 0x00002004, 0x10002000, - 0x10002004, 0x00000004, 0x10040000, 0x00042000, - }, - { - /* nibble 2 */ - 0x41000000, 0x01010040, 0x00000040, 0x41000040, - 0x40010000, 0x01000000, 0x41000040, 0x00010040, - 0x01000040, 0x00010000, 0x01010000, 0x40000000, - 0x41010040, 0x40000040, 0x40000000, 0x41010000, - 0x00000000, 0x40010000, 0x01010040, 0x00000040, - 0x40000040, 0x41010040, 0x00010000, 0x41000000, - 0x41010000, 0x01000040, 0x40010040, 0x01010000, - 0x00010040, 0x00000000, 0x01000000, 0x40010040, - 0x01010040, 0x00000040, 0x40000000, 0x00010000, - 0x40000040, 0x40010000, 0x01010000, 0x41000040, - 0x00000000, 0x01010040, 0x00010040, 0x41010000, - 0x40010000, 0x01000000, 0x41010040, 0x40000000, - 0x40010040, 0x41000000, 0x01000000, 0x41010040, - 0x00010000, 0x01000040, 0x41000040, 0x00010040, - 0x01000040, 0x00000000, 0x41010000, 0x40000040, - 0x41000000, 0x40010040, 0x00000040, 0x01010000, - }, - { - /* nibble 3 */ - 0x00100402, 0x04000400, 0x00000002, 0x04100402, - 0x00000000, 0x04100000, 0x04000402, 0x00100002, - 0x04100400, 0x04000002, 0x04000000, 0x00000402, - 0x04000002, 0x00100402, 0x00100000, 0x04000000, - 0x04100002, 0x00100400, 0x00000400, 0x00000002, - 0x00100400, 0x04000402, 0x04100000, 0x00000400, - 0x00000402, 0x00000000, 0x00100002, 0x04100400, - 0x04000400, 0x04100002, 0x04100402, 0x00100000, - 0x04100002, 0x00000402, 0x00100000, 0x04000002, - 0x00100400, 0x04000400, 0x00000002, 0x04100000, - 0x04000402, 0x00000000, 0x00000400, 0x00100002, - 0x00000000, 0x04100002, 0x04100400, 0x00000400, - 0x04000000, 0x04100402, 0x00100402, 0x00100000, - 0x04100402, 0x00000002, 0x04000400, 0x00100402, - 0x00100002, 0x00100400, 0x04100000, 0x04000402, - 0x00000402, 0x04000000, 0x04000002, 0x04100400, - }, - { - /* nibble 4 */ - 0x02000000, 0x00004000, 0x00000100, 0x02004108, - 0x02004008, 0x02000100, 0x00004108, 0x02004000, - 0x00004000, 0x00000008, 0x02000008, 0x00004100, - 0x02000108, 0x02004008, 0x02004100, 0x00000000, - 0x00004100, 0x02000000, 0x00004008, 0x00000108, - 0x02000100, 0x00004108, 0x00000000, 0x02000008, - 0x00000008, 0x02000108, 0x02004108, 0x00004008, - 0x02004000, 0x00000100, 0x00000108, 0x02004100, - 0x02004100, 0x02000108, 0x00004008, 0x02004000, - 0x00004000, 0x00000008, 0x02000008, 0x02000100, - 0x02000000, 0x00004100, 0x02004108, 0x00000000, - 0x00004108, 0x02000000, 0x00000100, 0x00004008, - 0x02000108, 0x00000100, 0x00000000, 0x02004108, - 0x02004008, 0x02004100, 0x00000108, 0x00004000, - 0x00004100, 0x02004008, 0x02000100, 0x00000108, - 0x00000008, 0x00004108, 0x02004000, 0x02000008, - }, - { - /* nibble 5 */ - 0x20000010, 0x00080010, 0x00000000, 0x20080800, - 0x00080010, 0x00000800, 0x20000810, 0x00080000, - 0x00000810, 0x20080810, 0x00080800, 0x20000000, - 0x20000800, 0x20000010, 0x20080000, 0x00080810, - 0x00080000, 0x20000810, 0x20080010, 0x00000000, - 0x00000800, 0x00000010, 0x20080800, 0x20080010, - 0x20080810, 0x20080000, 0x20000000, 0x00000810, - 0x00000010, 0x00080800, 0x00080810, 0x20000800, - 0x00000810, 0x20000000, 0x20000800, 0x00080810, - 0x20080800, 0x00080010, 0x00000000, 0x20000800, - 0x20000000, 0x00000800, 0x20080010, 0x00080000, - 0x00080010, 0x20080810, 0x00080800, 0x00000010, - 0x20080810, 0x00080800, 0x00080000, 0x20000810, - 0x20000010, 0x20080000, 0x00080810, 0x00000000, - 0x00000800, 0x20000010, 0x20000810, 0x20080800, - 0x20080000, 0x00000810, 0x00000010, 0x20080010, - }, - { - /* nibble 6 */ - 0x00001000, 0x00000080, 0x00400080, 0x00400001, - 0x00401081, 0x00001001, 0x00001080, 0x00000000, - 0x00400000, 0x00400081, 0x00000081, 0x00401000, - 0x00000001, 0x00401080, 0x00401000, 0x00000081, - 0x00400081, 0x00001000, 0x00001001, 0x00401081, - 0x00000000, 0x00400080, 0x00400001, 0x00001080, - 0x00401001, 0x00001081, 0x00401080, 0x00000001, - 0x00001081, 0x00401001, 0x00000080, 0x00400000, - 0x00001081, 0x00401000, 0x00401001, 0x00000081, - 0x00001000, 0x00000080, 0x00400000, 0x00401001, - 0x00400081, 0x00001081, 0x00001080, 0x00000000, - 0x00000080, 0x00400001, 0x00000001, 0x00400080, - 0x00000000, 0x00400081, 0x00400080, 0x00001080, - 0x00000081, 0x00001000, 0x00401081, 0x00400000, - 0x00401080, 0x00000001, 0x00001001, 0x00401081, - 0x00400001, 0x00401080, 0x00401000, 0x00001001, - }, - { - /* nibble 7 */ - 0x08200020, 0x08208000, 0x00008020, 0x00000000, - 0x08008000, 0x00200020, 0x08200000, 0x08208020, - 0x00000020, 0x08000000, 0x00208000, 0x00008020, - 0x00208020, 0x08008020, 0x08000020, 0x08200000, - 0x00008000, 0x00208020, 0x00200020, 0x08008000, - 0x08208020, 0x08000020, 0x00000000, 0x00208000, - 0x08000000, 0x00200000, 0x08008020, 0x08200020, - 0x00200000, 0x00008000, 0x08208000, 0x00000020, - 0x00200000, 0x00008000, 0x08000020, 0x08208020, - 0x00008020, 0x08000000, 0x00000000, 0x00208000, - 0x08200020, 0x08008020, 0x08008000, 0x00200020, - 0x08208000, 0x00000020, 0x00200020, 0x08008000, - 0x08208020, 0x00200000, 0x08200000, 0x08000020, - 0x00208000, 0x00008020, 0x08008020, 0x08200000, - 0x00000020, 0x08208000, 0x00208020, 0x00000000, - 0x08000000, 0x08200020, 0x00008000, 0x00208020 - } - }; - - private static final int cov_2char[] = - { - 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, - 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A - }; - - private static final int byteToUnsigned(byte b) - { - int value = (int)b; - - return(value >= 0 ? value : value + 256); - } - - private static int fourBytesToInt(byte b[], int offset) - { - int value; - - value = byteToUnsigned(b[offset++]); - value |= (byteToUnsigned(b[offset++]) << 8); - value |= (byteToUnsigned(b[offset++]) << 16); - value |= (byteToUnsigned(b[offset++]) << 24); - - return(value); - } - - private static final void intToFourBytes(int iValue, byte b[], int offset) - { - b[offset++] = (byte)((iValue) & 0xff); - b[offset++] = (byte)((iValue >>> 8 ) & 0xff); - b[offset++] = (byte)((iValue >>> 16) & 0xff); - b[offset++] = (byte)((iValue >>> 24) & 0xff); - } - - private static final void PERM_OP(int a, int b, int n, int m, int results[]) - { - int t; - - t = ((a >>> n) ^ b) & m; - a ^= t << n; - b ^= t; - - results[0] = a; - results[1] = b; - } - - private static final int HPERM_OP(int a, int n, int m) - { - int t; - - t = ((a << (16 - n)) ^ a) & m; - a = a ^ t ^ (t >>> (16 - n)); - - return(a); - } - - private static int [] des_set_key(byte key[]) - { - int schedule[] = new int[ITERATIONS * 2]; - - int c = fourBytesToInt(key, 0); - int d = fourBytesToInt(key, 4); - - int results[] = new int[2]; - - PERM_OP(d, c, 4, 0x0f0f0f0f, results); - d = results[0]; c = results[1]; - - c = HPERM_OP(c, -2, 0xcccc0000); - d = HPERM_OP(d, -2, 0xcccc0000); - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - PERM_OP(c, d, 8, 0x00ff00ff, results); - c = results[0]; d = results[1]; - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) | - ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4)); - c &= 0x0fffffff; - - int s, t; - int j = 0; - - for(int i = 0; i < ITERATIONS; i ++) - { - if(shifts2[i]) - { - c = (c >>> 2) | (c << 26); - d = (d >>> 2) | (d << 26); - } - else - { - c = (c >>> 1) | (c << 27); - d = (d >>> 1) | (d << 27); - } - - c &= 0x0fffffff; - d &= 0x0fffffff; - - s = skb[0][ (c ) & 0x3f ]| - skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)]| - skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]| - skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) | - ((c >>> 22) & 0x38)]; - - t = skb[4][ (d ) & 0x3f ]| - skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)]| - skb[6][ (d >>>15) & 0x3f ]| - skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)]; - - schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff; - s = ((s >>> 16) | (t & 0xffff0000)); - - s = (s << 4) | (s >>> 28); - schedule[j++] = s & 0xffffffff; - } - return(schedule); - } - - private static final int D_ENCRYPT - ( - int L, int R, int S, int E0, int E1, int s[] - ) - { - int t, u, v; - - v = R ^ (R >>> 16); - u = v & E0; - v = v & E1; - u = (u ^ (u << 16)) ^ R ^ s[S]; - t = (v ^ (v << 16)) ^ R ^ s[S + 1]; - t = (t >>> 4) | (t << 28); - - L ^= SPtrans[1][(t ) & 0x3f] | - SPtrans[3][(t >>> 8) & 0x3f] | - SPtrans[5][(t >>> 16) & 0x3f] | - SPtrans[7][(t >>> 24) & 0x3f] | - SPtrans[0][(u ) & 0x3f] | - SPtrans[2][(u >>> 8) & 0x3f] | - SPtrans[4][(u >>> 16) & 0x3f] | - SPtrans[6][(u >>> 24) & 0x3f]; - - return(L); - } - - private static final int [] body(int schedule[], int Eswap0, int Eswap1) - { - int left = 0; - int right = 0; - int t = 0; - - for(int j = 0; j < 25; j ++) - { - for(int i = 0; i < ITERATIONS * 2; i += 4) - { - left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule); - right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule); - } - t = left; - left = right; - right = t; - } - - t = right; - - right = (left >>> 1) | (left << 31); - left = (t >>> 1) | (t << 31); - - left &= 0xffffffff; - right &= 0xffffffff; - - int results[] = new int[2]; - - PERM_OP(right, left, 1, 0x55555555, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 8, 0x00ff00ff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 2, 0x33333333, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 16, 0x0000ffff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 4, 0x0f0f0f0f, results); - right = results[0]; left = results[1]; - - int out[] = new int[2]; - - out[0] = left; out[1] = right; - - return(out); - } - - /** - *

Encrypt a password given the cleartext password and a "salt".

- * @param salt A two-character string representing the salt used to - * iterate the encryption engine in lots of different ways. If you - * are generating a new encryption then this value should be - * randomised. - * @param original The password to be encrypted. - * @return A string consisting of the 2-character salt followed by the - * encrypted password. - */ - public static final String crypt(String salt, String original) - { - while(salt.length() < 2) - salt += "A"; - - StringBuffer buffer = new StringBuffer(" "); - - char charZero = salt.charAt(0); - char charOne = salt.charAt(1); - - buffer.setCharAt(0, charZero); - buffer.setCharAt(1, charOne); - - int Eswap0 = con_salt[(int)charZero]; - int Eswap1 = con_salt[(int)charOne] << 4; - - byte key[] = new byte[8]; - - for(int i = 0; i < key.length; i ++) - key[i] = (byte)0; - - for(int i = 0; i < key.length && i < original.length(); i ++) - { - int iChar = (int)original.charAt(i); - - key[i] = (byte)(iChar << 1); - } - - int schedule[] = des_set_key(key); - int out[] = body(schedule, Eswap0, Eswap1); - - byte b[] = new byte[9]; - - intToFourBytes(out[0], b, 0); - intToFourBytes(out[1], b, 4); - b[8] = 0; - - for(int i = 2, y = 0, u = 0x80; i < 13; i ++) - { - for(int j = 0, c = 0; j < 6; j ++) - { - c <<= 1; - - if(((int)b[y] & u) != 0) - c |= 1; - - u >>>= 1; - - if(u == 0) - { - y++; - u = 0x80; - } - buffer.setCharAt(i, (char)cov_2char[c]); - } - } - return(buffer.toString()); - } - - /** - *

Encrypt a password given the cleartext password. This method - * generates a random salt using the 'java.util.Random' class.

- * @param original The password to be encrypted. - * @return A string consisting of the 2-character salt followed by the - * encrypted password. - */ - public static final String crypt(String original) - { - java.util.Random randomGenerator = new java.util.Random(); - int numSaltChars = saltChars.length; - String salt; - - salt = (new StringBuffer()).append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars]).append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars]).toString(); - - return crypt(salt, original); - } - - /** - *

Check that enteredPassword encrypts to - * encryptedPassword.

- * @param encryptedPassword The encryptedPassword. The first - * two characters are assumed to be the salt. This string would - * be the same as one found in a Unix /etc/passwd file. - * @param enteredPassword The password as entered by the user (or - * otherwise aquired). - * @return true if the password should be considered correct. - */ - public final static boolean matches(String encryptedPassword, String enteredPassword) - { - String salt = encryptedPassword.substring(0, 3); - String newCrypt = crypt(salt, enteredPassword); - - return newCrypt.equals(encryptedPassword); - } + // + // Null constructor - can't instantiate class + private UnixCrypt() + {} + + private static final char[] saltChars = + ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./".toCharArray()); + + private static final int ITERATIONS = 16; + + private static final int con_salt[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, + 0x3D, 0x3E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + private static final boolean shifts2[] = + { + false, false, true, true, true, true, true, true, + false, true, true, true, true, true, true, false + }; + + private static final int skb[][] = + { + { + /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000, 0x00000010, 0x20000000, 0x20000010, + 0x00010000, 0x00010010, 0x20010000, 0x20010010, + 0x00000800, 0x00000810, 0x20000800, 0x20000810, + 0x00010800, 0x00010810, 0x20010800, 0x20010810, + 0x00000020, 0x00000030, 0x20000020, 0x20000030, + 0x00010020, 0x00010030, 0x20010020, 0x20010030, + 0x00000820, 0x00000830, 0x20000820, 0x20000830, + 0x00010820, 0x00010830, 0x20010820, 0x20010830, + 0x00080000, 0x00080010, 0x20080000, 0x20080010, + 0x00090000, 0x00090010, 0x20090000, 0x20090010, + 0x00080800, 0x00080810, 0x20080800, 0x20080810, + 0x00090800, 0x00090810, 0x20090800, 0x20090810, + 0x00080020, 0x00080030, 0x20080020, 0x20080030, + 0x00090020, 0x00090030, 0x20090020, 0x20090030, + 0x00080820, 0x00080830, 0x20080820, 0x20080830, + 0x00090820, 0x00090830, 0x20090820, 0x20090830, + }, + { + /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ + 0x00000000, 0x02000000, 0x00002000, 0x02002000, + 0x00200000, 0x02200000, 0x00202000, 0x02202000, + 0x00000004, 0x02000004, 0x00002004, 0x02002004, + 0x00200004, 0x02200004, 0x00202004, 0x02202004, + 0x00000400, 0x02000400, 0x00002400, 0x02002400, + 0x00200400, 0x02200400, 0x00202400, 0x02202400, + 0x00000404, 0x02000404, 0x00002404, 0x02002404, + 0x00200404, 0x02200404, 0x00202404, 0x02202404, + 0x10000000, 0x12000000, 0x10002000, 0x12002000, + 0x10200000, 0x12200000, 0x10202000, 0x12202000, + 0x10000004, 0x12000004, 0x10002004, 0x12002004, + 0x10200004, 0x12200004, 0x10202004, 0x12202004, + 0x10000400, 0x12000400, 0x10002400, 0x12002400, + 0x10200400, 0x12200400, 0x10202400, 0x12202400, + 0x10000404, 0x12000404, 0x10002404, 0x12002404, + 0x10200404, 0x12200404, 0x10202404, 0x12202404, + }, + { + /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ + 0x00000000, 0x00000001, 0x00040000, 0x00040001, + 0x01000000, 0x01000001, 0x01040000, 0x01040001, + 0x00000002, 0x00000003, 0x00040002, 0x00040003, + 0x01000002, 0x01000003, 0x01040002, 0x01040003, + 0x00000200, 0x00000201, 0x00040200, 0x00040201, + 0x01000200, 0x01000201, 0x01040200, 0x01040201, + 0x00000202, 0x00000203, 0x00040202, 0x00040203, + 0x01000202, 0x01000203, 0x01040202, 0x01040203, + 0x08000000, 0x08000001, 0x08040000, 0x08040001, + 0x09000000, 0x09000001, 0x09040000, 0x09040001, + 0x08000002, 0x08000003, 0x08040002, 0x08040003, + 0x09000002, 0x09000003, 0x09040002, 0x09040003, + 0x08000200, 0x08000201, 0x08040200, 0x08040201, + 0x09000200, 0x09000201, 0x09040200, 0x09040201, + 0x08000202, 0x08000203, 0x08040202, 0x08040203, + 0x09000202, 0x09000203, 0x09040202, 0x09040203, + }, + { + /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ + 0x00000000, 0x00100000, 0x00000100, 0x00100100, + 0x00000008, 0x00100008, 0x00000108, 0x00100108, + 0x00001000, 0x00101000, 0x00001100, 0x00101100, + 0x00001008, 0x00101008, 0x00001108, 0x00101108, + 0x04000000, 0x04100000, 0x04000100, 0x04100100, + 0x04000008, 0x04100008, 0x04000108, 0x04100108, + 0x04001000, 0x04101000, 0x04001100, 0x04101100, + 0x04001008, 0x04101008, 0x04001108, 0x04101108, + 0x00020000, 0x00120000, 0x00020100, 0x00120100, + 0x00020008, 0x00120008, 0x00020108, 0x00120108, + 0x00021000, 0x00121000, 0x00021100, 0x00121100, + 0x00021008, 0x00121008, 0x00021108, 0x00121108, + 0x04020000, 0x04120000, 0x04020100, 0x04120100, + 0x04020008, 0x04120008, 0x04020108, 0x04120108, + 0x04021000, 0x04121000, 0x04021100, 0x04121100, + 0x04021008, 0x04121008, 0x04021108, 0x04121108, + }, + { + /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ + 0x00000000, 0x10000000, 0x00010000, 0x10010000, + 0x00000004, 0x10000004, 0x00010004, 0x10010004, + 0x20000000, 0x30000000, 0x20010000, 0x30010000, + 0x20000004, 0x30000004, 0x20010004, 0x30010004, + 0x00100000, 0x10100000, 0x00110000, 0x10110000, + 0x00100004, 0x10100004, 0x00110004, 0x10110004, + 0x20100000, 0x30100000, 0x20110000, 0x30110000, + 0x20100004, 0x30100004, 0x20110004, 0x30110004, + 0x00001000, 0x10001000, 0x00011000, 0x10011000, + 0x00001004, 0x10001004, 0x00011004, 0x10011004, + 0x20001000, 0x30001000, 0x20011000, 0x30011000, + 0x20001004, 0x30001004, 0x20011004, 0x30011004, + 0x00101000, 0x10101000, 0x00111000, 0x10111000, + 0x00101004, 0x10101004, 0x00111004, 0x10111004, + 0x20101000, 0x30101000, 0x20111000, 0x30111000, + 0x20101004, 0x30101004, 0x20111004, 0x30111004, + }, + { + /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ + 0x00000000, 0x08000000, 0x00000008, 0x08000008, + 0x00000400, 0x08000400, 0x00000408, 0x08000408, + 0x00020000, 0x08020000, 0x00020008, 0x08020008, + 0x00020400, 0x08020400, 0x00020408, 0x08020408, + 0x00000001, 0x08000001, 0x00000009, 0x08000009, + 0x00000401, 0x08000401, 0x00000409, 0x08000409, + 0x00020001, 0x08020001, 0x00020009, 0x08020009, + 0x00020401, 0x08020401, 0x00020409, 0x08020409, + 0x02000000, 0x0A000000, 0x02000008, 0x0A000008, + 0x02000400, 0x0A000400, 0x02000408, 0x0A000408, + 0x02020000, 0x0A020000, 0x02020008, 0x0A020008, + 0x02020400, 0x0A020400, 0x02020408, 0x0A020408, + 0x02000001, 0x0A000001, 0x02000009, 0x0A000009, + 0x02000401, 0x0A000401, 0x02000409, 0x0A000409, + 0x02020001, 0x0A020001, 0x02020009, 0x0A020009, + 0x02020401, 0x0A020401, 0x02020409, 0x0A020409, + }, + { + /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ + 0x00000000, 0x00000100, 0x00080000, 0x00080100, + 0x01000000, 0x01000100, 0x01080000, 0x01080100, + 0x00000010, 0x00000110, 0x00080010, 0x00080110, + 0x01000010, 0x01000110, 0x01080010, 0x01080110, + 0x00200000, 0x00200100, 0x00280000, 0x00280100, + 0x01200000, 0x01200100, 0x01280000, 0x01280100, + 0x00200010, 0x00200110, 0x00280010, 0x00280110, + 0x01200010, 0x01200110, 0x01280010, 0x01280110, + 0x00000200, 0x00000300, 0x00080200, 0x00080300, + 0x01000200, 0x01000300, 0x01080200, 0x01080300, + 0x00000210, 0x00000310, 0x00080210, 0x00080310, + 0x01000210, 0x01000310, 0x01080210, 0x01080310, + 0x00200200, 0x00200300, 0x00280200, 0x00280300, + 0x01200200, 0x01200300, 0x01280200, 0x01280300, + 0x00200210, 0x00200310, 0x00280210, 0x00280310, + 0x01200210, 0x01200310, 0x01280210, 0x01280310, + }, + { + /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ + 0x00000000, 0x04000000, 0x00040000, 0x04040000, + 0x00000002, 0x04000002, 0x00040002, 0x04040002, + 0x00002000, 0x04002000, 0x00042000, 0x04042000, + 0x00002002, 0x04002002, 0x00042002, 0x04042002, + 0x00000020, 0x04000020, 0x00040020, 0x04040020, + 0x00000022, 0x04000022, 0x00040022, 0x04040022, + 0x00002020, 0x04002020, 0x00042020, 0x04042020, + 0x00002022, 0x04002022, 0x00042022, 0x04042022, + 0x00000800, 0x04000800, 0x00040800, 0x04040800, + 0x00000802, 0x04000802, 0x00040802, 0x04040802, + 0x00002800, 0x04002800, 0x00042800, 0x04042800, + 0x00002802, 0x04002802, 0x00042802, 0x04042802, + 0x00000820, 0x04000820, 0x00040820, 0x04040820, + 0x00000822, 0x04000822, 0x00040822, 0x04040822, + 0x00002820, 0x04002820, 0x00042820, 0x04042820, + 0x00002822, 0x04002822, 0x00042822, 0x04042822, + }, + }; + + private static final int SPtrans[][] = + { + { + /* nibble 0 */ + 0x00820200, 0x00020000, 0x80800000, 0x80820200, + 0x00800000, 0x80020200, 0x80020000, 0x80800000, + 0x80020200, 0x00820200, 0x00820000, 0x80000200, + 0x80800200, 0x00800000, 0x00000000, 0x80020000, + 0x00020000, 0x80000000, 0x00800200, 0x00020200, + 0x80820200, 0x00820000, 0x80000200, 0x00800200, + 0x80000000, 0x00000200, 0x00020200, 0x80820000, + 0x00000200, 0x80800200, 0x80820000, 0x00000000, + 0x00000000, 0x80820200, 0x00800200, 0x80020000, + 0x00820200, 0x00020000, 0x80000200, 0x00800200, + 0x80820000, 0x00000200, 0x00020200, 0x80800000, + 0x80020200, 0x80000000, 0x80800000, 0x00820000, + 0x80820200, 0x00020200, 0x00820000, 0x80800200, + 0x00800000, 0x80000200, 0x80020000, 0x00000000, + 0x00020000, 0x00800000, 0x80800200, 0x00820200, + 0x80000000, 0x80820000, 0x00000200, 0x80020200, + }, + { + /* nibble 1 */ + 0x10042004, 0x00000000, 0x00042000, 0x10040000, + 0x10000004, 0x00002004, 0x10002000, 0x00042000, + 0x00002000, 0x10040004, 0x00000004, 0x10002000, + 0x00040004, 0x10042000, 0x10040000, 0x00000004, + 0x00040000, 0x10002004, 0x10040004, 0x00002000, + 0x00042004, 0x10000000, 0x00000000, 0x00040004, + 0x10002004, 0x00042004, 0x10042000, 0x10000004, + 0x10000000, 0x00040000, 0x00002004, 0x10042004, + 0x00040004, 0x10042000, 0x10002000, 0x00042004, + 0x10042004, 0x00040004, 0x10000004, 0x00000000, + 0x10000000, 0x00002004, 0x00040000, 0x10040004, + 0x00002000, 0x10000000, 0x00042004, 0x10002004, + 0x10042000, 0x00002000, 0x00000000, 0x10000004, + 0x00000004, 0x10042004, 0x00042000, 0x10040000, + 0x10040004, 0x00040000, 0x00002004, 0x10002000, + 0x10002004, 0x00000004, 0x10040000, 0x00042000, + }, + { + /* nibble 2 */ + 0x41000000, 0x01010040, 0x00000040, 0x41000040, + 0x40010000, 0x01000000, 0x41000040, 0x00010040, + 0x01000040, 0x00010000, 0x01010000, 0x40000000, + 0x41010040, 0x40000040, 0x40000000, 0x41010000, + 0x00000000, 0x40010000, 0x01010040, 0x00000040, + 0x40000040, 0x41010040, 0x00010000, 0x41000000, + 0x41010000, 0x01000040, 0x40010040, 0x01010000, + 0x00010040, 0x00000000, 0x01000000, 0x40010040, + 0x01010040, 0x00000040, 0x40000000, 0x00010000, + 0x40000040, 0x40010000, 0x01010000, 0x41000040, + 0x00000000, 0x01010040, 0x00010040, 0x41010000, + 0x40010000, 0x01000000, 0x41010040, 0x40000000, + 0x40010040, 0x41000000, 0x01000000, 0x41010040, + 0x00010000, 0x01000040, 0x41000040, 0x00010040, + 0x01000040, 0x00000000, 0x41010000, 0x40000040, + 0x41000000, 0x40010040, 0x00000040, 0x01010000, + }, + { + /* nibble 3 */ + 0x00100402, 0x04000400, 0x00000002, 0x04100402, + 0x00000000, 0x04100000, 0x04000402, 0x00100002, + 0x04100400, 0x04000002, 0x04000000, 0x00000402, + 0x04000002, 0x00100402, 0x00100000, 0x04000000, + 0x04100002, 0x00100400, 0x00000400, 0x00000002, + 0x00100400, 0x04000402, 0x04100000, 0x00000400, + 0x00000402, 0x00000000, 0x00100002, 0x04100400, + 0x04000400, 0x04100002, 0x04100402, 0x00100000, + 0x04100002, 0x00000402, 0x00100000, 0x04000002, + 0x00100400, 0x04000400, 0x00000002, 0x04100000, + 0x04000402, 0x00000000, 0x00000400, 0x00100002, + 0x00000000, 0x04100002, 0x04100400, 0x00000400, + 0x04000000, 0x04100402, 0x00100402, 0x00100000, + 0x04100402, 0x00000002, 0x04000400, 0x00100402, + 0x00100002, 0x00100400, 0x04100000, 0x04000402, + 0x00000402, 0x04000000, 0x04000002, 0x04100400, + }, + { + /* nibble 4 */ + 0x02000000, 0x00004000, 0x00000100, 0x02004108, + 0x02004008, 0x02000100, 0x00004108, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x00004100, + 0x02000108, 0x02004008, 0x02004100, 0x00000000, + 0x00004100, 0x02000000, 0x00004008, 0x00000108, + 0x02000100, 0x00004108, 0x00000000, 0x02000008, + 0x00000008, 0x02000108, 0x02004108, 0x00004008, + 0x02004000, 0x00000100, 0x00000108, 0x02004100, + 0x02004100, 0x02000108, 0x00004008, 0x02004000, + 0x00004000, 0x00000008, 0x02000008, 0x02000100, + 0x02000000, 0x00004100, 0x02004108, 0x00000000, + 0x00004108, 0x02000000, 0x00000100, 0x00004008, + 0x02000108, 0x00000100, 0x00000000, 0x02004108, + 0x02004008, 0x02004100, 0x00000108, 0x00004000, + 0x00004100, 0x02004008, 0x02000100, 0x00000108, + 0x00000008, 0x00004108, 0x02004000, 0x02000008, + }, + { + /* nibble 5 */ + 0x20000010, 0x00080010, 0x00000000, 0x20080800, + 0x00080010, 0x00000800, 0x20000810, 0x00080000, + 0x00000810, 0x20080810, 0x00080800, 0x20000000, + 0x20000800, 0x20000010, 0x20080000, 0x00080810, + 0x00080000, 0x20000810, 0x20080010, 0x00000000, + 0x00000800, 0x00000010, 0x20080800, 0x20080010, + 0x20080810, 0x20080000, 0x20000000, 0x00000810, + 0x00000010, 0x00080800, 0x00080810, 0x20000800, + 0x00000810, 0x20000000, 0x20000800, 0x00080810, + 0x20080800, 0x00080010, 0x00000000, 0x20000800, + 0x20000000, 0x00000800, 0x20080010, 0x00080000, + 0x00080010, 0x20080810, 0x00080800, 0x00000010, + 0x20080810, 0x00080800, 0x00080000, 0x20000810, + 0x20000010, 0x20080000, 0x00080810, 0x00000000, + 0x00000800, 0x20000010, 0x20000810, 0x20080800, + 0x20080000, 0x00000810, 0x00000010, 0x20080010, + }, + { + /* nibble 6 */ + 0x00001000, 0x00000080, 0x00400080, 0x00400001, + 0x00401081, 0x00001001, 0x00001080, 0x00000000, + 0x00400000, 0x00400081, 0x00000081, 0x00401000, + 0x00000001, 0x00401080, 0x00401000, 0x00000081, + 0x00400081, 0x00001000, 0x00001001, 0x00401081, + 0x00000000, 0x00400080, 0x00400001, 0x00001080, + 0x00401001, 0x00001081, 0x00401080, 0x00000001, + 0x00001081, 0x00401001, 0x00000080, 0x00400000, + 0x00001081, 0x00401000, 0x00401001, 0x00000081, + 0x00001000, 0x00000080, 0x00400000, 0x00401001, + 0x00400081, 0x00001081, 0x00001080, 0x00000000, + 0x00000080, 0x00400001, 0x00000001, 0x00400080, + 0x00000000, 0x00400081, 0x00400080, 0x00001080, + 0x00000081, 0x00001000, 0x00401081, 0x00400000, + 0x00401080, 0x00000001, 0x00001001, 0x00401081, + 0x00400001, 0x00401080, 0x00401000, 0x00001001, + }, + { + /* nibble 7 */ + 0x08200020, 0x08208000, 0x00008020, 0x00000000, + 0x08008000, 0x00200020, 0x08200000, 0x08208020, + 0x00000020, 0x08000000, 0x00208000, 0x00008020, + 0x00208020, 0x08008020, 0x08000020, 0x08200000, + 0x00008000, 0x00208020, 0x00200020, 0x08008000, + 0x08208020, 0x08000020, 0x00000000, 0x00208000, + 0x08000000, 0x00200000, 0x08008020, 0x08200020, + 0x00200000, 0x00008000, 0x08208000, 0x00000020, + 0x00200000, 0x00008000, 0x08000020, 0x08208020, + 0x00008020, 0x08000000, 0x00000000, 0x00208000, + 0x08200020, 0x08008020, 0x08008000, 0x00200020, + 0x08208000, 0x00000020, 0x00200020, 0x08008000, + 0x08208020, 0x00200000, 0x08200000, 0x08000020, + 0x00208000, 0x00008020, 0x08008020, 0x08200000, + 0x00000020, 0x08208000, 0x00208020, 0x00000000, + 0x08000000, 0x08200020, 0x00008000, 0x00208020 + } + }; + + private static final int cov_2char[] = + { + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A + }; + + private static final int byteToUnsigned(byte b) + { + int value = (int)b; + + return (value >= 0 ? value : value + 256); + } + + private static int fourBytesToInt(byte b[], int offset) + { + int value; + + value = byteToUnsigned(b[offset++]); + value |= (byteToUnsigned(b[offset++]) << 8); + value |= (byteToUnsigned(b[offset++]) << 16); + value |= (byteToUnsigned(b[offset++]) << 24); + + return (value); + } + + private static final void intToFourBytes(int iValue, byte b[], int offset) + { + b[offset++] = (byte)((iValue) & 0xff); + b[offset++] = (byte)((iValue >>> 8 ) & 0xff); + b[offset++] = (byte)((iValue >>> 16) & 0xff); + b[offset++] = (byte)((iValue >>> 24) & 0xff); + } + + private static final void PERM_OP(int a, int b, int n, int m, int results[]) + { + int t; + + t = ((a >>> n) ^ b) & m; + a ^= t << n; + b ^= t; + + results[0] = a; + results[1] = b; + } + + private static final int HPERM_OP(int a, int n, int m) + { + int t; + + t = ((a << (16 - n)) ^ a) & m; + a = a ^ t ^ (t >>> (16 - n)); + + return (a); + } + + private static int [] des_set_key(byte key[]) + { + int schedule[] = new int[ITERATIONS * 2]; + + int c = fourBytesToInt(key, 0); + int d = fourBytesToInt(key, 4); + + int results[] = new int[2]; + + PERM_OP(d, c, 4, 0x0f0f0f0f, results); + d = results[0]; + c = results[1]; + + c = HPERM_OP(c, -2, 0xcccc0000); + d = HPERM_OP(d, -2, 0xcccc0000); + + PERM_OP(d, c, 1, 0x55555555, results); + d = results[0]; + c = results[1]; + + PERM_OP(c, d, 8, 0x00ff00ff, results); + c = results[0]; + d = results[1]; + + PERM_OP(d, c, 1, 0x55555555, results); + d = results[0]; + c = results[1]; + + d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) | + ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4)); + c &= 0x0fffffff; + + int s, t; + int j = 0; + + for (int i = 0; i < ITERATIONS; i ++) + { + if (shifts2[i]) + { + c = (c >>> 2) | (c << 26); + d = (d >>> 2) | (d << 26); + } + else + { + c = (c >>> 1) | (c << 27); + d = (d >>> 1) | (d << 27); + } + + c &= 0x0fffffff; + d &= 0x0fffffff; + + s = skb[0][ (c ) & 0x3f ] | + skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)] | + skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)] | + skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) | + ((c >>> 22) & 0x38)]; + + t = skb[4][ (d ) & 0x3f ] | + skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)] | + skb[6][ (d >>> 15) & 0x3f ] | + skb[7][((d >>> 21) & 0x0f) | ((d >>> 22) & 0x30)]; + + schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff; + s = ((s >>> 16) | (t & 0xffff0000)); + + s = (s << 4) | (s >>> 28); + schedule[j++] = s & 0xffffffff; + } + return (schedule); + } + + private static final int D_ENCRYPT + ( + int L, int R, int S, int E0, int E1, int s[] + ) + { + int t, u, v; + + v = R ^ (R >>> 16); + u = v & E0; + v = v & E1; + u = (u ^ (u << 16)) ^ R ^ s[S]; + t = (v ^ (v << 16)) ^ R ^ s[S + 1]; + t = (t >>> 4) | (t << 28); + + L ^= SPtrans[1][(t ) & 0x3f] | + SPtrans[3][(t >>> 8) & 0x3f] | + SPtrans[5][(t >>> 16) & 0x3f] | + SPtrans[7][(t >>> 24) & 0x3f] | + SPtrans[0][(u ) & 0x3f] | + SPtrans[2][(u >>> 8) & 0x3f] | + SPtrans[4][(u >>> 16) & 0x3f] | + SPtrans[6][(u >>> 24) & 0x3f]; + + return (L); + } + + private static final int [] body(int schedule[], int Eswap0, int Eswap1) + { + int left = 0; + int right = 0; + int t = 0; + + for (int j = 0; j < 25; j ++) + { + for (int i = 0; i < ITERATIONS * 2; i += 4) + { + left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule); + right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule); + } + t = left; + left = right; + right = t; + } + + t = right; + + right = (left >>> 1) | (left << 31); + left = (t >>> 1) | (t << 31); + + left &= 0xffffffff; + right &= 0xffffffff; + + int results[] = new int[2]; + + PERM_OP(right, left, 1, 0x55555555, results); + right = results[0]; + left = results[1]; + + PERM_OP(left, right, 8, 0x00ff00ff, results); + left = results[0]; + right = results[1]; + + PERM_OP(right, left, 2, 0x33333333, results); + right = results[0]; + left = results[1]; + + PERM_OP(left, right, 16, 0x0000ffff, results); + left = results[0]; + right = results[1]; + + PERM_OP(right, left, 4, 0x0f0f0f0f, results); + right = results[0]; + left = results[1]; + + int out[] = new int[2]; + + out[0] = left; + out[1] = right; + + return (out); + } + + /** + *

Encrypt a password given the cleartext password and a "salt".

+ * @param salt A two-character string representing the salt used to + * iterate the encryption engine in lots of different ways. If you + * are generating a new encryption then this value should be + * randomised. + * @param original The password to be encrypted. + * @return A string consisting of the 2-character salt followed by the + * encrypted password. + */ + public static final String crypt(String salt, String original) + { + while (salt.length() < 2) + salt += "A"; + + StringBuffer buffer = new StringBuffer(" "); + + char charZero = salt.charAt(0); + char charOne = salt.charAt(1); + + buffer.setCharAt(0, charZero); + buffer.setCharAt(1, charOne); + + int Eswap0 = con_salt[(int)charZero]; + int Eswap1 = con_salt[(int)charOne] << 4; + + byte key[] = new byte[8]; + + for (int i = 0; i < key.length; i ++) + key[i] = (byte)0; + + for (int i = 0; i < key.length && i < original.length(); i ++) + { + int iChar = (int)original.charAt(i); + + key[i] = (byte)(iChar << 1); + } + + int schedule[] = des_set_key(key); + int out[] = body(schedule, Eswap0, Eswap1); + + byte b[] = new byte[9]; + + intToFourBytes(out[0], b, 0); + intToFourBytes(out[1], b, 4); + b[8] = 0; + + for (int i = 2, y = 0, u = 0x80; i < 13; i ++) + { + for (int j = 0, c = 0; j < 6; j ++) + { + c <<= 1; + + if (((int)b[y] & u) != 0) + c |= 1; + + u >>>= 1; + + if (u == 0) + { + y++; + u = 0x80; + } + buffer.setCharAt(i, (char)cov_2char[c]); + } + } + return (buffer.toString()); + } + + /** + *

Encrypt a password given the cleartext password. This method + * generates a random salt using the 'java.util.Random' class.

+ * @param original The password to be encrypted. + * @return A string consisting of the 2-character salt followed by the + * encrypted password. + */ + public static final String crypt(String original) + { + java.util.Random randomGenerator = new java.util.Random(); + int numSaltChars = saltChars.length; + String salt; + + salt = (new StringBuffer()).append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars]).append(saltChars[Math.abs(randomGenerator.nextInt()) % numSaltChars]).toString(); + + return crypt(salt, original); + } + + /** + *

Check that enteredPassword encrypts to + * encryptedPassword.

+ * @param encryptedPassword The encryptedPassword. The first + * two characters are assumed to be the salt. This string would + * be the same as one found in a Unix /etc/passwd file. + * @param enteredPassword The password as entered by the user (or + * otherwise aquired). + * @return true if the password should be considered correct. + */ + public final static boolean matches(String encryptedPassword, String enteredPassword) + { + String salt = encryptedPassword.substring(0, 3); + String newCrypt = crypt(salt, enteredPassword); + + return newCrypt.equals(encryptedPassword); + } } diff --git a/src/interfaces/jdbc/org/postgresql/xa/ClientConnection.java b/src/interfaces/jdbc/org/postgresql/xa/ClientConnection.java index 3f06b09b4c..b6fd5c5a9e 100644 --- a/src/interfaces/jdbc/org/postgresql/xa/ClientConnection.java +++ b/src/interfaces/jdbc/org/postgresql/xa/ClientConnection.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: ClientConnection.java,v 1.1 2000/04/17 20:07:55 peter Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: ClientConnection.java,v 1.2 2001/10/25 06:00:05 momjian Exp $ +*/ package org.postgresql.xa; @@ -68,425 +68,497 @@ import java.sql.*; * @see Connection */ final class ClientConnection - implements Connection + implements Connection { - /** - * The pooled XA connection that created this client connection - * and should be used to report closure and fatal errors. - */ - private XAConnectionImpl _xaConn; - - - /** - * This identifier was handed on to use when we were created by - * {@link XAConnection}. If since then the XA connection was asked - * to create another connection or was closed, our identifier will - * no longer be valid and any call to {@link - * XAConnection#getUnderlying} will throw an exception. Previously, - * the XA connection would hold a reference to use and tell us to - * terminate, but that prevented ClientConnection from being - * finalized. - */ - private int _clientId; - - - - - /** - * Construct a new client connection to provide access to the - * underlying JDBC connection (underlying) on behalf of - * an XA/pooled connection (xaConn). The pooled connection - * is required to notify of connection closure and fatal errors. - * - * @param xaConn The XA/pooled connection that created this - * client connection - * @param clientId A unique identifier handed to us by - * {@link XAConnection} - * @param underlying The underlying JDBC connection - */ - ClientConnection( XAConnectionImpl xaConn, int clientId ) - { - _xaConn = xaConn; - _clientId = clientId; - } - - - public Statement createStatement() - throws SQLException - { - try { - return getUnderlying().createStatement(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + /** + * The pooled XA connection that created this client connection + * and should be used to report closure and fatal errors. + */ + private XAConnectionImpl _xaConn; + + + /** + * This identifier was handed on to use when we were created by + * {@link XAConnection}. If since then the XA connection was asked + * to create another connection or was closed, our identifier will + * no longer be valid and any call to {@link + * XAConnection#getUnderlying} will throw an exception. Previously, + * the XA connection would hold a reference to use and tell us to + * terminate, but that prevented ClientConnection from being + * finalized. + */ + private int _clientId; + + + + + /** + * Construct a new client connection to provide access to the + * underlying JDBC connection (underlying) on behalf of + * an XA/pooled connection (xaConn). The pooled connection + * is required to notify of connection closure and fatal errors. + * + * @param xaConn The XA/pooled connection that created this + * client connection + * @param clientId A unique identifier handed to us by + * {@link XAConnection} + * @param underlying The underlying JDBC connection + */ + ClientConnection( XAConnectionImpl xaConn, int clientId ) + { + _xaConn = xaConn; + _clientId = clientId; } - } - public Statement createStatement( int resultSetType, int resultSetConcurrency ) - throws SQLException - { - try { - return getUnderlying().createStatement( resultSetType, resultSetConcurrency ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public Statement createStatement() + throws SQLException + { + try + { + return getUnderlying().createStatement(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public PreparedStatement prepareStatement( String sql ) - throws SQLException - { - try { - return getUnderlying().prepareStatement( sql ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public Statement createStatement( int resultSetType, int resultSetConcurrency ) + throws SQLException + { + try + { + return getUnderlying().createStatement( resultSetType, resultSetConcurrency ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency ) - throws SQLException - { - try { - return getUnderlying().prepareStatement( sql, resultSetType, resultSetConcurrency ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public PreparedStatement prepareStatement( String sql ) + throws SQLException + { + try + { + return getUnderlying().prepareStatement( sql ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public CallableStatement prepareCall( String sql ) - throws SQLException - { - try { - return getUnderlying().prepareCall( sql ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency ) + throws SQLException + { + try + { + return getUnderlying().prepareStatement( sql, resultSetType, resultSetConcurrency ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency ) - throws SQLException - { - try { - return getUnderlying().prepareCall( sql, resultSetType, resultSetConcurrency ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public CallableStatement prepareCall( String sql ) + throws SQLException + { + try + { + return getUnderlying().prepareCall( sql ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public String nativeSQL( String sql ) - throws SQLException - { - try { - return getUnderlying().nativeSQL( sql ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency ) + throws SQLException + { + try + { + return getUnderlying().prepareCall( sql, resultSetType, resultSetConcurrency ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public DatabaseMetaData getMetaData() - throws SQLException - { - try { - return getUnderlying().getMetaData(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public String nativeSQL( String sql ) + throws SQLException + { + try + { + return getUnderlying().nativeSQL( sql ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public void setCatalog( String catalog ) - throws SQLException - { - try { - getUnderlying().setCatalog( catalog ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public DatabaseMetaData getMetaData() + throws SQLException + { + try + { + return getUnderlying().getMetaData(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public String getCatalog() - throws SQLException - { - try { - return getUnderlying().getCatalog(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public void setCatalog( String catalog ) + throws SQLException + { + try + { + getUnderlying().setCatalog( catalog ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public SQLWarning getWarnings() - throws SQLException - { - try { - return getUnderlying().getWarnings(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public String getCatalog() + throws SQLException + { + try + { + return getUnderlying().getCatalog(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public void clearWarnings() - throws SQLException - { - try { - getUnderlying().clearWarnings(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public SQLWarning getWarnings() + throws SQLException + { + try + { + return getUnderlying().getWarnings(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public Map getTypeMap() - throws SQLException - { - try { - return getUnderlying().getTypeMap(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public void clearWarnings() + throws SQLException + { + try + { + getUnderlying().clearWarnings(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public void setTypeMap( Map map ) - throws SQLException - { - try { - getUnderlying().setTypeMap( map ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public Map getTypeMap() + throws SQLException + { + try + { + return getUnderlying().getTypeMap(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - - - public void setAutoCommit( boolean autoCommit ) - throws SQLException - { - // Cannot set auto-commit inside a transaction. - if ( _xaConn.insideGlobalTx() ) - throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); - try { - getUnderlying().setAutoCommit( autoCommit ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + + + public void setTypeMap( Map map ) + throws SQLException + { + try + { + getUnderlying().setTypeMap( map ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public boolean getAutoCommit() - throws SQLException - { - try { - return getUnderlying().getAutoCommit(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public void setAutoCommit( boolean autoCommit ) + throws SQLException + { + // Cannot set auto-commit inside a transaction. + if ( _xaConn.insideGlobalTx() ) + throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); + try + { + getUnderlying().setAutoCommit( autoCommit ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - - - public void commit() - throws SQLException - { - // Cannot commit directly if we're inside a global transaction. - if ( _xaConn.insideGlobalTx() ) - throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); - // Cannot commit a read-only transaction. - if ( isReadOnly() ) - throw new SQLException( "Cannot commit/rollback a read-only transaction" ); - - // This only occurs if not inside a local transaction. - try { - getUnderlying().commit(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + + + public boolean getAutoCommit() + throws SQLException + { + try + { + return getUnderlying().getAutoCommit(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } + public void commit() + throws SQLException + { + // Cannot commit directly if we're inside a global transaction. + if ( _xaConn.insideGlobalTx() ) + throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); + // Cannot commit a read-only transaction. + if ( isReadOnly() ) + throw new SQLException( "Cannot commit/rollback a read-only transaction" ); + + // This only occurs if not inside a local transaction. + try + { + getUnderlying().commit(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } + } + - public void rollback() - throws SQLException - { - // Cannot commit directly if we're inside a global transaction. - if ( _xaConn.insideGlobalTx() ) - throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); - // This only occurs if not inside a local transaction. - try { - getUnderlying().rollback(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public void rollback() + throws SQLException + { + // Cannot commit directly if we're inside a global transaction. + if ( _xaConn.insideGlobalTx() ) + throw new SQLException( "Cannot commit/rollback a connection managed by the transaction manager" ); + + // This only occurs if not inside a local transaction. + try + { + getUnderlying().rollback(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public void setReadOnly( boolean readOnly ) - throws SQLException - { - try { - getUnderlying().setReadOnly( readOnly ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public void setReadOnly( boolean readOnly ) + throws SQLException + { + try + { + getUnderlying().setReadOnly( readOnly ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public boolean isReadOnly() - throws SQLException - { - try { - return getUnderlying().isReadOnly(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public boolean isReadOnly() + throws SQLException + { + try + { + return getUnderlying().isReadOnly(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - - - public void setTransactionIsolation( int level ) - throws SQLException - { - try { - getUnderlying().setTransactionIsolation( level ); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + + + public void setTransactionIsolation( int level ) + throws SQLException + { + try + { + getUnderlying().setTransactionIsolation( level ); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public int getTransactionIsolation() - throws SQLException - { - try { - return getUnderlying().getTransactionIsolation(); - } catch ( SQLException except ) { - notifyError( except ); - throw except; + public int getTransactionIsolation() + throws SQLException + { + try + { + return getUnderlying().getTransactionIsolation(); + } + catch ( SQLException except ) + { + notifyError( except ); + throw except; + } } - } - public synchronized void close() + public synchronized void close() throws SQLException - { - if ( _xaConn == null ) - return; - - // Notify the XA connection that we are no longer going - // to be used. Whether the underlying connection is released, - // held until the transaction terminates, etc is not - // a concern of us. - _xaConn.notifyClose( _clientId ); - _xaConn = null; - } - - - public synchronized boolean isClosed() - { - // Simple way of determining if this connection is closed. - // The actual connection is never closed, it is pooled. - return ( _xaConn == null ); - } - - - /** - * Called by {@link XAConnectionImpl} to terminate this connection - * by dissociating it from the underlying JDBC connection. - * The application would call {@link #close} but {@link - * XAConnectionImpl} cannot, since pooled connection requirements - * will cause an inifinite loop. This method should not attempt - * to notify either a closure or fatal error, but rather throw an - * exception if it fails. - */ - /* Deprecated: see XAConnection._clientId - void terminate() - { + { + if ( _xaConn == null ) + return ; + + // Notify the XA connection that we are no longer going + // to be used. Whether the underlying connection is released, + // held until the transaction terminates, etc is not + // a concern of us. + _xaConn.notifyClose( _clientId ); + _xaConn = null; + } + + + public synchronized boolean isClosed() + { + // Simple way of determining if this connection is closed. + // The actual connection is never closed, it is pooled. + return ( _xaConn == null ); + } + + + /** + * Called by {@link XAConnectionImpl} to terminate this connection + * by dissociating it from the underlying JDBC connection. + * The application would call {@link #close} but {@link + * XAConnectionImpl} cannot, since pooled connection requirements + * will cause an inifinite loop. This method should not attempt + * to notify either a closure or fatal error, but rather throw an + * exception if it fails. + */ + /* Deprecated: see XAConnection._clientId + void terminate() +{ _xaConn = null; - } - */ +} + */ - protected void finalize() + protected void finalize() throws Throwable - { - close(); - } + { + close(); + } - public String toString() - { - try { - return getUnderlying().toString(); - } catch ( SQLException except ) { - return "XAConnection: Connection closed"; + public String toString() + { + try + { + return getUnderlying().toString(); + } + catch ( SQLException except ) + { + return "XAConnection: Connection closed"; + } } - } - - - /** - * Called when an exception is thrown by the underlying connection - * to determine whether the exception is critical or not. If the - * exception is critical, notifies the XA connection to forget - * about this connection. - * - * @param except The exception thrown by the underlying - * connection - */ - void notifyError( SQLException except ) - { - if ( _xaConn != null ) - _xaConn.notifyError( _clientId, except ); - } - - - /** - * Called to retrieve the underlying JDBC connection. Actual JDBC - * operations are performed against it. Throws an SQLException if - * this connection has been closed. - */ - Connection getUnderlying() - throws SQLException - { - if ( _xaConn == null ) - throw new SQLException( "This connection has been closed" ); - // Must pass the client identifier so XAConnection can determine - // whether we are still valid. If it tells us we're no longer - // valid, we have little to do. - try { - return _xaConn.getUnderlying( _clientId ); - } catch ( SQLException except ) { - _xaConn = null; - throw except; + + + /** + * Called when an exception is thrown by the underlying connection + * to determine whether the exception is critical or not. If the + * exception is critical, notifies the XA connection to forget + * about this connection. + * + * @param except The exception thrown by the underlying + * connection + */ + void notifyError( SQLException except ) + { + if ( _xaConn != null ) + _xaConn.notifyError( _clientId, except ); + } + + + /** + * Called to retrieve the underlying JDBC connection. Actual JDBC + * operations are performed against it. Throws an SQLException if + * this connection has been closed. + */ + Connection getUnderlying() + throws SQLException + { + if ( _xaConn == null ) + throw new SQLException( "This connection has been closed" ); + // Must pass the client identifier so XAConnection can determine + // whether we are still valid. If it tells us we're no longer + // valid, we have little to do. + try + { + return _xaConn.getUnderlying( _clientId ); + } + catch ( SQLException except ) + { + _xaConn = null; + throw except; + } } - } } diff --git a/src/interfaces/jdbc/org/postgresql/xa/TwoPhaseConnection.java b/src/interfaces/jdbc/org/postgresql/xa/TwoPhaseConnection.java index f80da15ed0..8927943378 100644 --- a/src/interfaces/jdbc/org/postgresql/xa/TwoPhaseConnection.java +++ b/src/interfaces/jdbc/org/postgresql/xa/TwoPhaseConnection.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: TwoPhaseConnection.java,v 1.1 2000/04/17 20:07:55 peter Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: TwoPhaseConnection.java,v 1.2 2001/10/25 06:00:05 momjian Exp $ +*/ package org.postgresql.xa; @@ -79,38 +79,38 @@ public interface TwoPhaseConnection { - /** - * Enables or disables transaction demarcation through SQL commit - * and rollback. When the connection falls under control of - * {@link XAConnection}, SQL commit/rollback commands will be - * disabled to prevent direct transaction demarcation. - * - * @param flag True to enable SQL transactions (the default) - */ - public void enableSQLTransactions( boolean flag ); + /** + * Enables or disables transaction demarcation through SQL commit + * and rollback. When the connection falls under control of + * {@link XAConnection}, SQL commit/rollback commands will be + * disabled to prevent direct transaction demarcation. + * + * @param flag True to enable SQL transactions (the default) + */ + public void enableSQLTransactions( boolean flag ); - /** - * Called to prepare the transaction for commit. Returns true if - * the transaction is prepared, false if the transaction is - * read-only. If the transaction has been marked for rollback, - * throws a {@link RollbackException}. - * - * @return True if can commit, false if read-only - * @throws SQLException If transaction has been marked for - * rollback or cannot commit for any other reason - */ - public boolean prepare() + /** + * Called to prepare the transaction for commit. Returns true if + * the transaction is prepared, false if the transaction is + * read-only. If the transaction has been marked for rollback, + * throws a {@link RollbackException}. + * + * @return True if can commit, false if read-only + * @throws SQLException If transaction has been marked for + * rollback or cannot commit for any other reason + */ + public boolean prepare() throws SQLException; - /** - * Returns true if the error issued by this connection is a - * critical error and the connection should be terminated. - * - * @param except The exception thrown by this connection - */ - public boolean isCriticalError( SQLException except ); + /** + * Returns true if the error issued by this connection is a + * critical error and the connection should be terminated. + * + * @param except The exception thrown by this connection + */ + public boolean isCriticalError( SQLException except ); } diff --git a/src/interfaces/jdbc/org/postgresql/xa/TxConnection.java b/src/interfaces/jdbc/org/postgresql/xa/TxConnection.java index 5cf8836eb7..6d9597af4e 100644 --- a/src/interfaces/jdbc/org/postgresql/xa/TxConnection.java +++ b/src/interfaces/jdbc/org/postgresql/xa/TxConnection.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: TxConnection.java,v 1.1 2000/04/17 20:07:56 peter Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: TxConnection.java,v 1.2 2001/10/25 06:00:05 momjian Exp $ +*/ package org.postgresql.xa; @@ -66,64 +66,64 @@ import javax.transaction.xa.Xid; */ final class TxConnection { - - - /** - * The Xid of the transactions. Connections that are not - * associated with a transaction are not represented here. - */ - Xid xid; - - - /** - * Holds the underlying JDBC connection for as long as this - * connection is useable. If the connection has been rolled back, - * timed out or had any other error, this variable will null - * and the connection is considered failed. - */ - Connection conn; - - - - /** - * Indicates the clock time (in ms) when the transaction should - * time out. The transaction times out when - * System.currentTimeMillis() > timeout. - */ - long timeout; - - - /** - * Indicates the clock time (in ms) when the transaction started. - */ - long started; - - - /** - * Reference counter indicates how many XA connections share this - * underlying connection and transaction. Always one or more. - */ - int count; - - - /** - * True if the transaction has failed due to time out. - */ - boolean timedOut; - - - /** - * True if the transaction has already been prepared. - */ - boolean prepared; - - - /** - * True if the transaction has been prepared and found out to be - * read-only. Read-only transactions do not require commit/rollback. - */ - boolean readOnly; - - + + + /** + * The Xid of the transactions. Connections that are not + * associated with a transaction are not represented here. + */ + Xid xid; + + + /** + * Holds the underlying JDBC connection for as long as this + * connection is useable. If the connection has been rolled back, + * timed out or had any other error, this variable will null + * and the connection is considered failed. + */ + Connection conn; + + + + /** + * Indicates the clock time (in ms) when the transaction should + * time out. The transaction times out when + * System.currentTimeMillis() > timeout. + */ + long timeout; + + + /** + * Indicates the clock time (in ms) when the transaction started. + */ + long started; + + + /** + * Reference counter indicates how many XA connections share this + * underlying connection and transaction. Always one or more. + */ + int count; + + + /** + * True if the transaction has failed due to time out. + */ + boolean timedOut; + + + /** + * True if the transaction has already been prepared. + */ + boolean prepared; + + + /** + * True if the transaction has been prepared and found out to be + * read-only. Read-only transactions do not require commit/rollback. + */ + boolean readOnly; + + } diff --git a/src/interfaces/jdbc/org/postgresql/xa/XAConnectionImpl.java b/src/interfaces/jdbc/org/postgresql/xa/XAConnectionImpl.java index 896472d696..ea51c90dce 100644 --- a/src/interfaces/jdbc/org/postgresql/xa/XAConnectionImpl.java +++ b/src/interfaces/jdbc/org/postgresql/xa/XAConnectionImpl.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: XAConnectionImpl.java,v 1.1 2000/04/17 20:07:56 peter Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: XAConnectionImpl.java,v 1.2 2001/10/25 06:00:05 momjian Exp $ +*/ package org.postgresql.xa; @@ -83,770 +83,872 @@ import javax.transaction.xa.XAException; * @see TxConnection */ public final class XAConnectionImpl - implements XAConnection, XAResource + implements XAConnection, XAResource { - /** - * This is the underlying JDBC connection represented - * by this pooled connection. This variable may initially be null, - * in which case {@link #getUnderlying} will return a new - * connection and set this variable. This variable is mutually - * exclusive with {@link #_txConn} and is always null for - * connections inside a transaction. - */ - Connection _underlying; - - - /** - * If this connection is part of a global transaction, this - * object identifies the transaction. The transaction's - * underlying JDBC connection is exposed through this object and - * {@link #_underlying} is null. If this connection is closed, - * then the connection has been timedout. Commit/rollback will - * always set this variable to null. - */ - private TxConnection _txConn; - - - /** - * The client connection last handed to the application. If the - * application calls {@link #getConnection} again, we should hand - * out a new client connection and render the previous one closed. - */ - // No longer in use, see _clientId - //private ClientConnection _clientConn; - - - /** - * An event listener can be registered and notified when the - * client connection has been closed by the application or a - * fatal error rendered it unuseable. - */ - private ConnectionEventListener _listener; - - - /** - * The resource manager is used to share connections within the - * same transaction. - */ - private XADataSourceImpl _resManager; - - - /** - * This is an identifier we hand to the client connection when we - * create it. When the client connection asks for the underlying - * connection, we compare the identifiers. If since that point we - * created a new client connection, we regard an old client - * connection as discarded and do not hand it the underlying - * connection. - *

- * Previously, when a new client connection was created, we used - * a reference to the old one to terminate it. This proved to - * not work well, since the client connection could never be - * finalized. - */ - private int _clientId = 1; - - - /** - * Construct a new XA/pooled connection with the underlying JDBC - * connection suitable for this driver only. This is a one to one - * mapping between this connection and the underlying connection. - * The underlying connection is only provided for pooled - * connections. XA connections are suspect of being enlisted with - * a global transaction which might already bear an underlying - * connection. If not, one will be created later on. - */ - XAConnectionImpl( XADataSourceImpl resManager, - Connection underlying ) - { - _underlying = underlying; - _resManager = resManager; - } - - - public synchronized void close() - throws SQLException - { - // This is our indication that this connection has been - // closed programmatically. - if ( _resManager == null ) - throw new SQLException( "This connection has been closed" ); - - // The client connection is no longer useable. - /* Deprecated: see _clientId - if ( _clientConn != null ) - _clientConn.terminate(); - */ - _clientId = -1; - - // The underlying connection is closed and this connection - // is no longer useable. This method can be called any number - // of times (e.g. we use it in finalizer). We do not handle - // transactions, we just kill the connection. - try { - if ( _underlying != null ) { - _underlying.commit(); - _underlying.close(); - } else if ( _txConn != null ) { - try { - end( _txConn.xid, TMSUCCESS ); - } catch ( XAException except ) { } - } - } finally { - _resManager = null; - _underlying = null; - _txConn = null; - _listener = null; + /** + * This is the underlying JDBC connection represented + * by this pooled connection. This variable may initially be null, + * in which case {@link #getUnderlying} will return a new + * connection and set this variable. This variable is mutually + * exclusive with {@link #_txConn} and is always null for + * connections inside a transaction. + */ + Connection _underlying; + + + /** + * If this connection is part of a global transaction, this + * object identifies the transaction. The transaction's + * underlying JDBC connection is exposed through this object and + * {@link #_underlying} is null. If this connection is closed, + * then the connection has been timedout. Commit/rollback will + * always set this variable to null. + */ + private TxConnection _txConn; + + + /** + * The client connection last handed to the application. If the + * application calls {@link #getConnection} again, we should hand + * out a new client connection and render the previous one closed. + */ + // No longer in use, see _clientId + //private ClientConnection _clientConn; + + + /** + * An event listener can be registered and notified when the + * client connection has been closed by the application or a + * fatal error rendered it unuseable. + */ + private ConnectionEventListener _listener; + + + /** + * The resource manager is used to share connections within the + * same transaction. + */ + private XADataSourceImpl _resManager; + + + /** + * This is an identifier we hand to the client connection when we + * create it. When the client connection asks for the underlying + * connection, we compare the identifiers. If since that point we + * created a new client connection, we regard an old client + * connection as discarded and do not hand it the underlying + * connection. + *

+ * Previously, when a new client connection was created, we used + * a reference to the old one to terminate it. This proved to + * not work well, since the client connection could never be + * finalized. + */ + private int _clientId = 1; + + + /** + * Construct a new XA/pooled connection with the underlying JDBC + * connection suitable for this driver only. This is a one to one + * mapping between this connection and the underlying connection. + * The underlying connection is only provided for pooled + * connections. XA connections are suspect of being enlisted with + * a global transaction which might already bear an underlying + * connection. If not, one will be created later on. + */ + XAConnectionImpl( XADataSourceImpl resManager, + Connection underlying ) + { + _underlying = underlying; + _resManager = resManager; } - } - - - public XAResource getXAResource() - { - // The connection acts as it's own resource manager - return this; - } - - - public synchronized void addConnectionEventListener( ConnectionEventListener listener ) - { - if ( listener == null ) - throw new NullPointerException( "XAConnection: Argument 'listener' is null" ); - if ( _listener != null ) - throw new IllegalStateException( "XAConnection: Only one listener supported per connection" ); - _listener = listener; - } - - - public synchronized void removeConnectionEventListener( ConnectionEventListener listener ) - { - if ( listener == null ) - throw new NullPointerException( "XAConnection: Argument 'listener' is null" ); - if ( _listener == null || _listener != listener ) - throw new IllegalStateException( "XAConnection: Listener never registered with this pooled connection" ); - _listener = null; - } - - - public synchronized java.sql.Connection getConnection() - throws SQLException - { - // If this pooled connection has been closed, throw an exception. - if ( _resManager == null ) - throw new SQLException( "This connection has been closed" ); - - // If getConnection() was called before and the underlying - // connection was not closed, we take it away from the previous - // recieved as per the PooledConnection design. - /* Deprecated: see _clientId - if ( _clientConn != null ) - _clientConn.terminate(); - */ - - // If we are handling an underlying connection, we commit the - // old transaction and are ready to work for a new one. - // If we are part of a global transaction we hope that end/ - // start were called properly, but we're not longer in that - // transaction. - if ( _underlying != null ) { - try { - _underlying.commit(); - } catch ( SQLException except ) { - ConnectionEvent event; - if ( _listener != null ) { - event = new ConnectionEvent( this, except ); - _listener.connectionErrorOccurred( event ); + + public synchronized void close() + throws SQLException + { + // This is our indication that this connection has been + // closed programmatically. + if ( _resManager == null ) + throw new SQLException( "This connection has been closed" ); + + // The client connection is no longer useable. + /* Deprecated: see _clientId + if ( _clientConn != null ) + _clientConn.terminate(); + */ + _clientId = -1; + + // The underlying connection is closed and this connection + // is no longer useable. This method can be called any number + // of times (e.g. we use it in finalizer). We do not handle + // transactions, we just kill the connection. + try + { + if ( _underlying != null ) + { + _underlying.commit(); + _underlying.close(); + } + else if ( _txConn != null ) + { + try + { + end( _txConn.xid, TMSUCCESS ); + } + catch ( XAException except ) + { } + } + } + finally + { + _resManager = null; + _underlying = null; + _txConn = null; + _listener = null; } - } } - // Create a new ClientConnection which will be returned to the - // application. The ClientConnection cannot be closed directly - // and cannot manage it's own transactions. - /* Deprecated: see _clientId - _clientConn = new ClientConnection( this ); - return _clientConn; - */ - return new ClientConnection( this, ++_clientId ); - } - - - /** - * Called by {@link ClientConnection} to notify that the application - * has attempted to close the connection. After this call, the client - * connection is no longer useable and this pooled connection can be - * reused. The event listener is notified immediately. - * - * @param clientId The {@link ClientConnection} identifier - */ - synchronized void notifyClose( int clientId ) - { - ConnectionEvent event; - - // ClientConnection has been closed, we dissociated it from - // the underlying connection and notify any listener that this - // pooled connection can be reused. - /* Deprecated: see clientId - _clientConn.terminate(); - _clientConn = null; - */ - // We have to expect being called by a ClientConnection that we - // no longer regard as valid. That's acceptable, we just ignore. - if ( clientId != _clientId ) - return; - - // If we are handling an underlying connection, we commit the - // old transaction and are ready to work for a new one. - // If we are part of a global transaction we hope that end/ - // start were called properly. - if ( _underlying != null ) { - try { - _underlying.commit(); - } catch ( SQLException except ) { - if ( _listener != null ) { - event = new ConnectionEvent( this, except ); - _listener.connectionErrorOccurred( event ); - } - return; - } + + public XAResource getXAResource() + { + // The connection acts as it's own resource manager + return this; } - // Notify the listener. - if ( _listener != null ) { - event = new ConnectionEvent( this ); - _listener.connectionClosed( event ); + + + public synchronized void addConnectionEventListener( ConnectionEventListener listener ) + { + if ( listener == null ) + throw new NullPointerException( "XAConnection: Argument 'listener' is null" ); + if ( _listener != null ) + throw new IllegalStateException( "XAConnection: Only one listener supported per connection" ); + _listener = listener; } - } - - - /** - * Called by {@link ClientConnection} to notify that an error - * occured with the underlying connection. If the error is - * critical, the underlying connection is closed and the listener - * is notified. - * - * @param clientId The {@link ClientConnection} identifier - * @param except The exception raised by the underlying connection - */ - synchronized void notifyError( int clientId, SQLException except ) - { - ConnectionEvent event; - - if ( clientId != _clientId ) - return; - - // If the connection is not two-phase commit we cannot determine - // whether the error is critical, we just return. If the connection - // is two phase commit, but the error is not critical, we return. - if ( _underlying != null ) { - if ( ! ( _underlying instanceof TwoPhaseConnection ) || - ! ( (TwoPhaseConnection) _underlying ).isCriticalError( except ) ) - return; - if ( _txConn.conn == null || - ! ( _txConn.conn instanceof TwoPhaseConnection ) || - ! ( (TwoPhaseConnection) _txConn.conn ).isCriticalError( except ) ) - return; + + + public synchronized void removeConnectionEventListener( ConnectionEventListener listener ) + { + if ( listener == null ) + throw new NullPointerException( "XAConnection: Argument 'listener' is null" ); + if ( _listener == null || _listener != listener ) + throw new IllegalStateException( "XAConnection: Listener never registered with this pooled connection" ); + _listener = null; } - // The client connection is no longer useable, the underlying - // connection (if used) is closed, the TxConnection (if used) - // is rolledback and this connection dies (but close() may - // still be called). - ++_clientId; - if ( _underlying != null ) { - try { - _underlying.close(); - } catch ( SQLException e2 ) { - // Ignore that, we know there's an error. - } - _underlying = null; - } else if ( _txConn != null ) { - try { - end( _txConn.xid, TMFAIL ); - } catch ( XAException e2 ) { - // Ignore that, we know there's an error. - } - _txConn = null; - } - - // Notify the listener. - if ( _listener != null ) { - event = new ConnectionEvent( this, except ); - _listener.connectionErrorOccurred( event ); + + public synchronized java.sql.Connection getConnection() + throws SQLException + { + // If this pooled connection has been closed, throw an exception. + if ( _resManager == null ) + throw new SQLException( "This connection has been closed" ); + + // If getConnection() was called before and the underlying + // connection was not closed, we take it away from the previous + // recieved as per the PooledConnection design. + /* Deprecated: see _clientId + if ( _clientConn != null ) + _clientConn.terminate(); + */ + + // If we are handling an underlying connection, we commit the + // old transaction and are ready to work for a new one. + // If we are part of a global transaction we hope that end/ + // start were called properly, but we're not longer in that + // transaction. + if ( _underlying != null ) + { + try + { + _underlying.commit(); + } + catch ( SQLException except ) + { + ConnectionEvent event; + + if ( _listener != null ) + { + event = new ConnectionEvent( this, except ); + _listener.connectionErrorOccurred( event ); + } + } + } + + // Create a new ClientConnection which will be returned to the + // application. The ClientConnection cannot be closed directly + // and cannot manage it's own transactions. + /* Deprecated: see _clientId + _clientConn = new ClientConnection( this ); + return _clientConn; + */ + return new ClientConnection( this, ++_clientId ); } - } - protected void finalize() - throws Throwable - { - // We are no longer referenced by anyone (including the - // connection pool). Time to close down. - close(); - } - - - public String toString() - { - if ( _underlying != null ) - return "XAConnection: " + _underlying; - else - return "XAConnection: unused"; - } - - - public synchronized void start( Xid xid, int flags ) - throws XAException - { - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - if ( _txConn != null ) - throw new XAException( XAException.XAER_OUTSIDE ); - - synchronized ( _resManager ) { - if ( flags == TMNOFLAGS ) { - // Starting a new transaction. First, make sure it is - // not shared with any other connection (need to join - // for that). - if ( _resManager.getTxConnection( xid ) != null ) - throw new XAException( XAException.XAER_DUPID ); - - // Create a new TxConnection to describe this - // connection in the context of a transaction and - // register it with the resource manager so it can - // be shared. - try { - _txConn = new TxConnection(); - if ( _underlying != null ) { - _txConn.conn = _underlying; + /** + * Called by {@link ClientConnection} to notify that the application + * has attempted to close the connection. After this call, the client + * connection is no longer useable and this pooled connection can be + * reused. The event listener is notified immediately. + * + * @param clientId The {@link ClientConnection} identifier + */ + synchronized void notifyClose( int clientId ) + { + ConnectionEvent event; + + // ClientConnection has been closed, we dissociated it from + // the underlying connection and notify any listener that this + // pooled connection can be reused. + /* Deprecated: see clientId + _clientConn.terminate(); + _clientConn = null; + */ + // We have to expect being called by a ClientConnection that we + // no longer regard as valid. That's acceptable, we just ignore. + if ( clientId != _clientId ) + return ; + + // If we are handling an underlying connection, we commit the + // old transaction and are ready to work for a new one. + // If we are part of a global transaction we hope that end/ + // start were called properly. + if ( _underlying != null ) + { + try + { + _underlying.commit(); + } + catch ( SQLException except ) + { + if ( _listener != null ) + { + event = new ConnectionEvent( this, except ); + _listener.connectionErrorOccurred( event ); + } + return ; + } + } + // Notify the listener. + if ( _listener != null ) + { + event = new ConnectionEvent( this ); + _listener.connectionClosed( event ); + } + } + + + /** + * Called by {@link ClientConnection} to notify that an error + * occured with the underlying connection. If the error is + * critical, the underlying connection is closed and the listener + * is notified. + * + * @param clientId The {@link ClientConnection} identifier + * @param except The exception raised by the underlying connection + */ + synchronized void notifyError( int clientId, SQLException except ) + { + ConnectionEvent event; + + if ( clientId != _clientId ) + return ; + + // If the connection is not two-phase commit we cannot determine + // whether the error is critical, we just return. If the connection + // is two phase commit, but the error is not critical, we return. + if ( _underlying != null ) + { + if ( ! ( _underlying instanceof TwoPhaseConnection ) || + ! ( (TwoPhaseConnection) _underlying ).isCriticalError( except ) ) + return ; + if ( _txConn.conn == null || + ! ( _txConn.conn instanceof TwoPhaseConnection ) || + ! ( (TwoPhaseConnection) _txConn.conn ).isCriticalError( except ) ) + return ; + } + + // The client connection is no longer useable, the underlying + // connection (if used) is closed, the TxConnection (if used) + // is rolledback and this connection dies (but close() may + // still be called). + ++_clientId; + if ( _underlying != null ) + { + try + { + _underlying.close(); + } + catch ( SQLException e2 ) + { + // Ignore that, we know there's an error. + } _underlying = null; - } else - _txConn.conn = _resManager.newConnection(); - _txConn.xid = xid; - _txConn.count = 1; - _txConn.started = System.currentTimeMillis(); - _txConn.timeout = _txConn.started + ( _resManager.getTransactionTimeout() * 1000 ); - _resManager.setTxConnection( xid, _txConn ); - } catch ( SQLException except ) { - // If error occured at this point, we can only - // report it as resource manager error. - if ( _resManager.getLogWriter() != null ) - _resManager.getLogWriter().println( "XAConnection: failed to begin a transaction: " + except ); - throw new XAException( XAException.XAER_RMERR ); + } + else if ( _txConn != null ) + { + try + { + end( _txConn.xid, TMFAIL ); + } + catch ( XAException e2 ) + { + // Ignore that, we know there's an error. + } + _txConn = null; } - try { - _txConn.conn.setAutoCommit( false ); - try { - if ( _resManager.isolationLevel() != Connection.TRANSACTION_NONE ) - _txConn.conn.setTransactionIsolation( _resManager.isolationLevel() ); - } catch ( SQLException e ) { - // The underlying driver might not support this - // isolation level that we use by default. - } - if ( _txConn.conn instanceof TwoPhaseConnection ) - ( (TwoPhaseConnection) _txConn.conn ).enableSQLTransactions( false ); - } catch ( SQLException except ) { - // If error occured at this point, we can only - // report it as resource manager error. - if ( _resManager.getLogWriter() != null ) - _resManager.getLogWriter().println( "XAConnection: failed to begin a transaction: " + except ); - throw new XAException( XAException.XAER_RMERR ); + // Notify the listener. + if ( _listener != null ) + { + event = new ConnectionEvent( this, except ); + _listener.connectionErrorOccurred( event ); } - } else if ( flags == TMJOIN || flags == TMRESUME ) { - // We are joining another transaction with an - // existing TxConnection. - _txConn = _resManager.getTxConnection( xid ); - if ( _txConn == null ) - throw new XAException( XAException.XAER_INVAL ); - - // Update the number of XAConnections sharing this - // transaction connection. - if ( flags == TMJOIN && _txConn.count == 0 ) - throw new XAException( XAException.XAER_PROTO ); - ++_txConn.count; - - // If we already have an underlying connection (as we can - // expect to), we should release that underlying connection - // and make it available to the resource manager. - if ( _underlying != null ) { - _resManager.releaseConnection( _underlying ); - _underlying = null; + } + + + protected void finalize() + throws Throwable + { + // We are no longer referenced by anyone (including the + // connection pool). Time to close down. + close(); + } + + + public String toString() + { + if ( _underlying != null ) + return "XAConnection: " + _underlying; + else + return "XAConnection: unused"; + } + + + public synchronized void start( Xid xid, int flags ) + throws XAException + { + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + if ( _txConn != null ) + throw new XAException( XAException.XAER_OUTSIDE ); + + synchronized ( _resManager ) + { + if ( flags == TMNOFLAGS ) + { + // Starting a new transaction. First, make sure it is + // not shared with any other connection (need to join + // for that). + if ( _resManager.getTxConnection( xid ) != null ) + throw new XAException( XAException.XAER_DUPID ); + + // Create a new TxConnection to describe this + // connection in the context of a transaction and + // register it with the resource manager so it can + // be shared. + try + { + _txConn = new TxConnection(); + if ( _underlying != null ) + { + _txConn.conn = _underlying; + _underlying = null; + } + else + _txConn.conn = _resManager.newConnection(); + _txConn.xid = xid; + _txConn.count = 1; + _txConn.started = System.currentTimeMillis(); + _txConn.timeout = _txConn.started + ( _resManager.getTransactionTimeout() * 1000 ); + _resManager.setTxConnection( xid, _txConn ); + } + catch ( SQLException except ) + { + // If error occured at this point, we can only + // report it as resource manager error. + if ( _resManager.getLogWriter() != null ) + _resManager.getLogWriter().println( "XAConnection: failed to begin a transaction: " + except ); + throw new XAException( XAException.XAER_RMERR ); + } + + try + { + _txConn.conn.setAutoCommit( false ); + try + { + if ( _resManager.isolationLevel() != Connection.TRANSACTION_NONE ) + _txConn.conn.setTransactionIsolation( _resManager.isolationLevel() ); + } + catch ( SQLException e ) + { + // The underlying driver might not support this + // isolation level that we use by default. + } + if ( _txConn.conn instanceof TwoPhaseConnection ) + ( (TwoPhaseConnection) _txConn.conn ).enableSQLTransactions( false ); + } + catch ( SQLException except ) + { + // If error occured at this point, we can only + // report it as resource manager error. + if ( _resManager.getLogWriter() != null ) + _resManager.getLogWriter().println( "XAConnection: failed to begin a transaction: " + except ); + throw new XAException( XAException.XAER_RMERR ); + } + } + else if ( flags == TMJOIN || flags == TMRESUME ) + { + // We are joining another transaction with an + // existing TxConnection. + _txConn = _resManager.getTxConnection( xid ); + if ( _txConn == null ) + throw new XAException( XAException.XAER_INVAL ); + + // Update the number of XAConnections sharing this + // transaction connection. + if ( flags == TMJOIN && _txConn.count == 0 ) + throw new XAException( XAException.XAER_PROTO ); + ++_txConn.count; + + // If we already have an underlying connection (as we can + // expect to), we should release that underlying connection + // and make it available to the resource manager. + if ( _underlying != null ) + { + _resManager.releaseConnection( _underlying ); + _underlying = null; + } + } + else + // No other flags supported in start(). + throw new XAException( XAException.XAER_INVAL ); } - } else - // No other flags supported in start(). - throw new XAException( XAException.XAER_INVAL ); } - } - - - public synchronized void end( Xid xid, int flags ) - throws XAException - { - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - // Note: we could get end with success or failure even it - // we were previously excluded from the transaction. - if ( _txConn == null && flags == TMSUSPEND ) - throw new XAException( XAException.XAER_NOTA ); - - synchronized ( _resManager ) { - if ( flags == TMSUCCESS || flags == TMFAIL) { - // We are now leaving a transaction we started or - // joined before. We can expect any of prepare/ - // commit/rollback to be called next, so TxConnection - // is still valid. - - // If we were suspended from the transaction, we'll - // join it for the duration of this operation. - // Make sure the reference count reaches zero by the - // time we get to prepare. - if ( _txConn == null ) { - _txConn = _resManager.getTxConnection( xid ); - if ( _txConn == null ) - throw new XAException( XAException.XAER_NOTA ); - } else { - if ( _txConn.xid != null && ! _txConn.xid.equals( xid ) ) + + + public synchronized void end( Xid xid, int flags ) + throws XAException + { + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + // Note: we could get end with success or failure even it + // we were previously excluded from the transaction. + if ( _txConn == null && flags == TMSUSPEND ) throw new XAException( XAException.XAER_NOTA ); - --_txConn.count; + + synchronized ( _resManager ) + { + if ( flags == TMSUCCESS || flags == TMFAIL) + { + // We are now leaving a transaction we started or + // joined before. We can expect any of prepare/ + // commit/rollback to be called next, so TxConnection + // is still valid. + + // If we were suspended from the transaction, we'll + // join it for the duration of this operation. + // Make sure the reference count reaches zero by the + // time we get to prepare. + if ( _txConn == null ) + { + _txConn = _resManager.getTxConnection( xid ); + if ( _txConn == null ) + throw new XAException( XAException.XAER_NOTA ); + } + else + { + if ( _txConn.xid != null && ! _txConn.xid.equals( xid ) ) + throw new XAException( XAException.XAER_NOTA ); + --_txConn.count; + } + + // If transaction failed, we can rollback the + // transaction and release the underlying connection. + // We can expect all other resources to recieved the + // same end notification. We don't expect forget to happen. + if ( flags == TMFAIL && _txConn.conn != null ) + { + try + { + if ( _txConn.conn instanceof TwoPhaseConnection ) + ( (TwoPhaseConnection) _txConn.conn ).enableSQLTransactions( true ); + _txConn.conn.rollback(); + _resManager.releaseConnection( _txConn.conn ); + } + catch ( SQLException except ) + { + // There is a problem with the underlying + // connection, but it was not added to the poll. + } + _resManager.setTxConnection( _txConn.xid, null ); + _txConn.conn = null; + _txConn.xid = null; + } + + if ( flags == TMSUCCESS) + { + // We should be looking for a new transaction. + // Next thing we might be participating in a new + // transaction while the current one is being + // rolled back. + _txConn = null; + } + } + else if ( flags == TMSUSPEND ) + { + // We no longer take part in this transaction. + // Possibly we'll be asked to resume later on, but + // right now we have to forget about the transaction + // and the underlying connection. + --_txConn.count; + _txConn = null; + } + else + // No other flags supported in end(). + throw new XAException( XAException.XAER_INVAL ); } + } + - // If transaction failed, we can rollback the - // transaction and release the underlying connection. - // We can expect all other resources to recieved the - // same end notification. We don't expect forget to happen. - if ( flags == TMFAIL && _txConn.conn != null ) { - try { - if ( _txConn.conn instanceof TwoPhaseConnection ) - ( (TwoPhaseConnection) _txConn.conn ).enableSQLTransactions( true ); - _txConn.conn.rollback(); - _resManager.releaseConnection( _txConn.conn ); - } catch ( SQLException except ) { - // There is a problem with the underlying - // connection, but it was not added to the poll. - } - _resManager.setTxConnection( _txConn.xid, null ); - _txConn.conn = null; - _txConn.xid = null; + public synchronized void forget( Xid xid ) + throws XAException + { + TxConnection txConn; + + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + synchronized ( _resManager ) + { + // We have to forget about the transaction, meaning the + // transaction no longer exists for this or any other + // connection. We might be called multiple times. + txConn = _resManager.setTxConnection( xid, null ); + if ( _txConn == txConn ) + _txConn = null; + if ( txConn != null ) + { + if ( txConn.conn != null ) + { + _resManager.releaseConnection( txConn.conn ); + txConn.conn = null; + } + txConn.xid = null; + } } + } - if ( flags == TMSUCCESS) { - // We should be looking for a new transaction. - // Next thing we might be participating in a new - // transaction while the current one is being - // rolled back. - _txConn = null; + + public synchronized int prepare( Xid xid ) + throws XAException + { + TxConnection txConn; + + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + + synchronized ( _resManager ) + { + // Technically, prepare may be called for any connection, + // not just this one. + txConn = _resManager.getTxConnection( xid ); + if ( txConn == null ) + throw new XAException( XAException.XAER_NOTA ); + + // This is an error and should never happen. All other + // parties in the transaction should have left it before. + if ( txConn.count > 0 ) + throw new XAException( XAException.XAER_PROTO ); + + // If the transaction failed, we have to force a rollback. + // We track the case of failure due to a timeout. + if ( txConn.timedOut ) + throw new XAException( XAException.XA_RBTIMEOUT ); + if ( txConn.conn == null ) + throw new XAException( XAException.XA_RBROLLBACK ); + + // Since there is no preparation mechanism in a generic + // JDBC driver, we only test for read-only transaction + // but do not commit at this point. + try + { + txConn.prepared = true; + if ( txConn.conn instanceof TwoPhaseConnection ) + { + // For 2pc connection we ask it to prepare and determine + // whether it's commiting or read-only. If a rollback + // exception happens, we report it. + try + { + if ( ( (TwoPhaseConnection) txConn.conn ).prepare() ) + return XA_OK; + else + { + txConn.readOnly = true; + return XA_RDONLY; + } + } + catch ( SQLException except ) + { + throw new XAException( XAException.XA_RBROLLBACK ); + } + } + else + { + // For standard connection we cannot prepare, we can + // only guess if it's read only. + if ( txConn.conn.isReadOnly() ) + { + txConn.readOnly = true; + return XA_RDONLY; + } + return XA_OK; + } + } + catch ( SQLException except ) + { + try + { + // Fatal error in the connection, kill it. + txConn.conn.close(); + } + catch ( SQLException e ) + { } + txConn.conn = null; + if ( _resManager.getLogWriter() != null ) + _resManager.getLogWriter().println( "XAConnection: failed to commit a transaction: " + except ); + // If we cannot commit the transaction, force a rollback. + throw new XAException( XAException.XA_RBROLLBACK ); + } } - } else if ( flags == TMSUSPEND ) { - // We no longer take part in this transaction. - // Possibly we'll be asked to resume later on, but - // right now we have to forget about the transaction - // and the underlying connection. - --_txConn.count; - _txConn = null; - } else - // No other flags supported in end(). - throw new XAException( XAException.XAER_INVAL ); } - } - public synchronized void forget( Xid xid ) + public Xid[] recover( int flags ) throws XAException - { - TxConnection txConn; - - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - synchronized ( _resManager ) { - // We have to forget about the transaction, meaning the - // transaction no longer exists for this or any other - // connection. We might be called multiple times. - txConn = _resManager.setTxConnection( xid, null ); - if ( _txConn == txConn ) - _txConn = null; - if ( txConn != null ) { - if ( txConn.conn != null ) { - _resManager.releaseConnection( txConn.conn ); - txConn.conn = null; + { + synchronized ( _resManager ) + { + return _resManager.getTxRecover(); } - txConn.xid = null; - } } - } - public synchronized int prepare( Xid xid ) + public synchronized void commit( Xid xid, boolean onePhase ) throws XAException - { - TxConnection txConn; - - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - - synchronized ( _resManager ) { - // Technically, prepare may be called for any connection, - // not just this one. - txConn = _resManager.getTxConnection( xid ); - if ( txConn == null ) - throw new XAException( XAException.XAER_NOTA ); - - // This is an error and should never happen. All other - // parties in the transaction should have left it before. - if ( txConn.count > 0 ) - throw new XAException( XAException.XAER_PROTO ); - - // If the transaction failed, we have to force a rollback. - // We track the case of failure due to a timeout. - if ( txConn.timedOut ) - throw new XAException( XAException.XA_RBTIMEOUT ); - if ( txConn.conn == null ) - throw new XAException( XAException.XA_RBROLLBACK ); - - // Since there is no preparation mechanism in a generic - // JDBC driver, we only test for read-only transaction - // but do not commit at this point. - try { - txConn.prepared = true; - if ( txConn.conn instanceof TwoPhaseConnection ) { - // For 2pc connection we ask it to prepare and determine - // whether it's commiting or read-only. If a rollback - // exception happens, we report it. - try { - if ( ( (TwoPhaseConnection) txConn.conn ).prepare() ) - return XA_OK; - else { - txConn.readOnly = true; - return XA_RDONLY; + { + TxConnection txConn; + + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + + synchronized ( _resManager ) + { + // Technically, commit may be called for any connection, + // not just this one. + txConn = _resManager.getTxConnection( xid ); + if ( txConn == null ) + throw new XAException( XAException.XAER_NOTA ); + + // If the transaction failed, we have to force + // a rollback. + if ( txConn.conn == null ) + throw new XAException( XAException.XA_RBROLLBACK ); + + // If connection has been prepared and is read-only, + // nothing to do at this stage. + if ( txConn.readOnly ) + return ; + + // This must be a one-phase commite, or the connection + // should have been prepared before. + if ( onePhase || txConn.prepared ) + { + try + { + // Prevent multiple commit attempts. + txConn.readOnly = true; + if ( txConn.conn instanceof TwoPhaseConnection ) + ( (TwoPhaseConnection) txConn.conn ).enableSQLTransactions( true ); + txConn.conn.commit(); + } + catch ( SQLException except ) + { + try + { + // Unknown error in the connection, better kill it. + txConn.conn.close(); + } + catch ( SQLException e ) + { } + txConn.conn = null; + if ( _resManager.getLogWriter() != null ) + _resManager.getLogWriter().println( "XAConnection: failed to commit a transaction: " + except ); + // If we cannot commit the transaction, a heuristic tollback. + throw new XAException( XAException.XA_HEURRB ); + } + } + else + { + // 2pc we should have prepared before. + if ( ! txConn.prepared ) + throw new XAException( XAException.XAER_PROTO ); } - } catch ( SQLException except ) { - throw new XAException( XAException.XA_RBROLLBACK ); - } - } else { - // For standard connection we cannot prepare, we can - // only guess if it's read only. - if ( txConn.conn.isReadOnly() ) { - txConn.readOnly = true; - return XA_RDONLY; - } - return XA_OK; } - } catch ( SQLException except ) { - try { - // Fatal error in the connection, kill it. - txConn.conn.close(); - } catch ( SQLException e ) { } - txConn.conn = null; - if ( _resManager.getLogWriter() != null ) - _resManager.getLogWriter().println( "XAConnection: failed to commit a transaction: " + except ); - // If we cannot commit the transaction, force a rollback. - throw new XAException( XAException.XA_RBROLLBACK ); - } } - } - public Xid[] recover( int flags ) - throws XAException - { - synchronized ( _resManager ) { - return _resManager.getTxRecover(); - } - } - - - public synchronized void commit( Xid xid, boolean onePhase ) - throws XAException - { - TxConnection txConn; - - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - - synchronized ( _resManager ) { - // Technically, commit may be called for any connection, - // not just this one. - txConn = _resManager.getTxConnection( xid ); - if ( txConn == null ) - throw new XAException( XAException.XAER_NOTA ); - - // If the transaction failed, we have to force - // a rollback. - if ( txConn.conn == null ) - throw new XAException( XAException.XA_RBROLLBACK ); - - // If connection has been prepared and is read-only, - // nothing to do at this stage. - if ( txConn.readOnly ) - return; - - // This must be a one-phase commite, or the connection - // should have been prepared before. - if ( onePhase || txConn.prepared ) { - try { - // Prevent multiple commit attempts. - txConn.readOnly = true; - if ( txConn.conn instanceof TwoPhaseConnection ) - ( (TwoPhaseConnection) txConn.conn ).enableSQLTransactions( true ); - txConn.conn.commit(); - } catch ( SQLException except ) { - try { - // Unknown error in the connection, better kill it. - txConn.conn.close(); - } catch ( SQLException e ) { } - txConn.conn = null; - if ( _resManager.getLogWriter() != null ) - _resManager.getLogWriter().println( "XAConnection: failed to commit a transaction: " + except ); - // If we cannot commit the transaction, a heuristic tollback. - throw new XAException( XAException.XA_HEURRB ); + public synchronized void rollback( Xid xid ) + throws XAException + { + TxConnection txConn; + + + // General checks. + if ( xid == null ) + throw new XAException( XAException.XAER_INVAL ); + + synchronized ( _resManager ) + { + // Technically, rollback may be called for any connection, + // not just this one. + txConn = _resManager.getTxConnection( xid ); + if ( txConn == null ) + throw new XAException( XAException.XAER_NOTA ); + + // If connection has been prepared and is read-only, + // nothing to do at this stage. If connection has + // been terminated any other way, nothing to do + // either. + if ( txConn.readOnly || txConn.conn == null ) + return ; + + try + { + txConn.prepared = false; + if ( txConn.conn instanceof TwoPhaseConnection ) + ( (TwoPhaseConnection) txConn.conn ).enableSQLTransactions( true ); + txConn.conn.rollback(); + } + catch ( SQLException except ) + { + try + { + // Unknown error in the connection, better kill it. + txConn.conn.close(); + } + catch ( SQLException e ) + { } + txConn.conn = null; + if ( _resManager.getLogWriter() != null ) + _resManager.getLogWriter().println( "XAConnection: failed to rollback a transaction: " + except ); + // If we cannot commit the transaction, a heuristic tollback. + throw new XAException( XAException.XA_RBROLLBACK ); + } + finally + { + forget( xid ); + } } - } else { - // 2pc we should have prepared before. - if ( ! txConn.prepared ) - throw new XAException( XAException.XAER_PROTO ); - } } - } - - - public synchronized void rollback( Xid xid ) - throws XAException - { - TxConnection txConn; - - - // General checks. - if ( xid == null ) - throw new XAException( XAException.XAER_INVAL ); - - synchronized ( _resManager ) { - // Technically, rollback may be called for any connection, - // not just this one. - txConn = _resManager.getTxConnection( xid ); - if ( txConn == null ) - throw new XAException( XAException.XAER_NOTA ); - - // If connection has been prepared and is read-only, - // nothing to do at this stage. If connection has - // been terminated any other way, nothing to do - // either. - if ( txConn.readOnly || txConn.conn == null ) - return; - - try { - txConn.prepared = false; - if ( txConn.conn instanceof TwoPhaseConnection ) - ( (TwoPhaseConnection) txConn.conn ).enableSQLTransactions( true ); - txConn.conn.rollback(); - } catch ( SQLException except ) { - try { - // Unknown error in the connection, better kill it. - txConn.conn.close(); - } catch ( SQLException e ) { } - txConn.conn = null; - if ( _resManager.getLogWriter() != null ) - _resManager.getLogWriter().println( "XAConnection: failed to rollback a transaction: " + except ); - // If we cannot commit the transaction, a heuristic tollback. - throw new XAException( XAException.XA_RBROLLBACK ); - } finally { - forget( xid ); - } - } - } - public synchronized boolean isSameRM( XAResource xaRes ) + public synchronized boolean isSameRM( XAResource xaRes ) throws XAException - { - // Two resource managers are equal if they produce equivalent - // connection (i.e. same database, same user). If the two are - // equivalent they would share a transaction by joining. - if ( xaRes == null || ! ( xaRes instanceof XAConnectionImpl ) ) - return false; - if ( _resManager.equals( ( (XAConnectionImpl) xaRes )._resManager ) ) - return true; - return false; - } - - - public synchronized boolean setTransactionTimeout( int seconds ) + { + // Two resource managers are equal if they produce equivalent + // connection (i.e. same database, same user). If the two are + // equivalent they would share a transaction by joining. + if ( xaRes == null || ! ( xaRes instanceof XAConnectionImpl ) ) + return false; + if ( _resManager.equals( ( (XAConnectionImpl) xaRes )._resManager ) ) + return true; + return false; + } + + + public synchronized boolean setTransactionTimeout( int seconds ) throws XAException - { - if ( seconds < 0 ) - throw new XAException( XAException.XAER_INVAL ); - // Zero resets to the default for all transactions. - if ( seconds == 0 ) - seconds = _resManager.getTransactionTimeout(); - // If a transaction has started, change it's timeout to the new value. - if ( _txConn != null ) { - _txConn.timeout = _txConn.started + ( seconds * 1000 ); - return true; + { + if ( seconds < 0 ) + throw new XAException( XAException.XAER_INVAL ); + // Zero resets to the default for all transactions. + if ( seconds == 0 ) + seconds = _resManager.getTransactionTimeout(); + // If a transaction has started, change it's timeout to the new value. + if ( _txConn != null ) + { + _txConn.timeout = _txConn.started + ( seconds * 1000 ); + return true; + } + return false; } - return false; - } - - - public int getTransactionTimeout() - { - long timeout; - - if ( _txConn == null ) - return 0; - return (int) ( _txConn.timeout - _txConn.started ) / 1000; - } - - - /** - * Returns true if this connection is inside a global transaction. - * If the connection is inside a global transaction it will not - * allow commit/rollback directly from the {@link - * java.sql.Connection} interface. - */ - boolean insideGlobalTx() - { - return ( _txConn != null ); - } - - - /** - * Called to obtain the underlying connections. If this connection - * is part of a transaction, the transction's underlying connection - * is returned, or an exception is thrown if the connection was - * terminated due to timeout. If this connection is not part of a - * transaction, a non-transactional connection is returned. - * - * @param clientId The {@link ClientConnection} identifier - */ - Connection getUnderlying( int clientId ) - throws SQLException - { - // If we were notified of the client closing, or have been - // requested to have a new client connection since then, - // the client id will not match to that of the caller. - // We use that to decide that the caller has been closed. - if ( clientId != _clientId ) - throw new SQLException( "This application connection has been closed" ); - - if ( _txConn != null ) { - if ( _txConn.timedOut ) - throw new SQLException( "The transaction has timed out and has been rolledback and closed" ); - if ( _txConn.conn == null ) - throw new SQLException( "The transaction has been terminated and this connection has been closed" ); - return _txConn.conn; + + + public int getTransactionTimeout() + { + long timeout; + + if ( _txConn == null ) + return 0; + return (int) ( _txConn.timeout - _txConn.started ) / 1000; + } + + + /** + * Returns true if this connection is inside a global transaction. + * If the connection is inside a global transaction it will not + * allow commit/rollback directly from the {@link + * java.sql.Connection} interface. + */ + boolean insideGlobalTx() + { + return ( _txConn != null ); } - if ( _underlying == null ) { - _underlying = _resManager.newConnection(); - _underlying.setAutoCommit( true ); + + + /** + * Called to obtain the underlying connections. If this connection + * is part of a transaction, the transction's underlying connection + * is returned, or an exception is thrown if the connection was + * terminated due to timeout. If this connection is not part of a + * transaction, a non-transactional connection is returned. + * + * @param clientId The {@link ClientConnection} identifier + */ + Connection getUnderlying( int clientId ) + throws SQLException + { + // If we were notified of the client closing, or have been + // requested to have a new client connection since then, + // the client id will not match to that of the caller. + // We use that to decide that the caller has been closed. + if ( clientId != _clientId ) + throw new SQLException( "This application connection has been closed" ); + + if ( _txConn != null ) + { + if ( _txConn.timedOut ) + throw new SQLException( "The transaction has timed out and has been rolledback and closed" ); + if ( _txConn.conn == null ) + throw new SQLException( "The transaction has been terminated and this connection has been closed" ); + return _txConn.conn; + } + if ( _underlying == null ) + { + _underlying = _resManager.newConnection(); + _underlying.setAutoCommit( true ); + } + return _underlying; } - return _underlying; - } } diff --git a/src/interfaces/jdbc/org/postgresql/xa/XADataSourceImpl.java b/src/interfaces/jdbc/org/postgresql/xa/XADataSourceImpl.java index b1e3f4fa0a..df3c4ae987 100644 --- a/src/interfaces/jdbc/org/postgresql/xa/XADataSourceImpl.java +++ b/src/interfaces/jdbc/org/postgresql/xa/XADataSourceImpl.java @@ -1,47 +1,47 @@ /** - * Redistribution and use of this software and associated documentation - * ("Software"), with or without modification, are permitted provided - * that the following conditions are met: - * - * 1. Redistributions of source code must retain copyright - * statements and notices. Redistributions must also contain a - * copy of this document. - * - * 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. The name "Exolab" must not be used to endorse or promote - * products derived from this Software without prior written - * permission of Exoffice Technologies. For written permission, - * please contact info@exolab.org. - * - * 4. Products derived from this Software may not be called "Exolab" - * nor may "Exolab" appear in their names without prior written - * permission of Exoffice Technologies. Exolab is a registered - * trademark of Exoffice Technologies. - * - * 5. Due credit should be given to the Exolab Project - * (http://www.exolab.org/). - * - * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT - * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. - * - * $Id: XADataSourceImpl.java,v 1.1 2000/04/17 20:07:56 peter Exp $ - */ +* Redistribution and use of this software and associated documentation +* ("Software"), with or without modification, are permitted provided +* that the following conditions are met: +* +* 1. Redistributions of source code must retain copyright +* statements and notices. Redistributions must also contain a +* copy of this document. +* +* 2. Redistributions in binary form must reproduce the +* above copyright notice, this list of conditions and the +* following disclaimer in the documentation and/or other +* materials provided with the distribution. +* +* 3. The name "Exolab" must not be used to endorse or promote +* products derived from this Software without prior written +* permission of Exoffice Technologies. For written permission, +* please contact info@exolab.org. +* +* 4. Products derived from this Software may not be called "Exolab" +* nor may "Exolab" appear in their names without prior written +* permission of Exoffice Technologies. Exolab is a registered +* trademark of Exoffice Technologies. +* +* 5. Due credit should be given to the Exolab Project +* (http://www.exolab.org/). +* +* THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT +* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +* EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Copyright 1999 (C) Exoffice Technologies Inc. All Rights Reserved. +* +* $Id: XADataSourceImpl.java,v 1.2 2001/10/25 06:00:05 momjian Exp $ +*/ package org.postgresql.xa; @@ -77,383 +77,412 @@ import javax.transaction.xa.Xid; * @version 1.0 */ public abstract class XADataSourceImpl - implements DataSource, ConnectionPoolDataSource, - XADataSource, Serializable, Runnable + implements DataSource, ConnectionPoolDataSource, + XADataSource, Serializable, Runnable { - /** - * Maps underlying JDBC connections into global transaction Xids. - */ - private transient Hashtable _txConnections = new Hashtable(); - - - /** - * This is a pool of free underlying JDBC connections. If two - * XA connections are used in the same transaction, the second - * one will make its underlying JDBC connection available to - * the pool. This is not a real connection pool, only a marginal - * efficiency solution for dealing with shared transactions. - */ - private transient Stack _pool = new Stack(); - - - /** - * A background deamon thread terminating connections that have - * timed out. - */ - private transient Thread _background; - - - /** - * The default timeout for all new transactions. - */ - private int _txTimeout = DEFAULT_TX_TIMEOUT; - - - /** - * The default timeout for all new transactions is 10 seconds. - */ - public final static int DEFAULT_TX_TIMEOUT = 10; - - - - - /** - * Implementation details: - * If two XAConnections are associated with the same transaction - * (one with a start the other with a join) they must use the - * same underlying JDBC connection. They lookup the underlying - * JDBC connection based on the transaction's Xid in the - * originating XADataSource. - * - * Currently the XADataSource must be the exact same object, - * this should be changed so all XADataSources that are equal - * share a table of all enlisted connections - * - * To test is two connections should fall under the same - * transaction we match the resource managers by comparing the - * database/user they fall under using a comparison of the - * XADataSource properties. - */ - - - public XADataSourceImpl() - { - super(); - - // Create a background thread that will track transactions - // that timeout, abort them and release the underlying - // connections to the pool. - _background = new Thread( this, "XADataSource Timeout Daemon" ); - _background.setPriority( Thread.MIN_PRIORITY ); - _background.setDaemon( true ); - _background.start(); - } - - - public XAConnection getXAConnection() - throws SQLException - { - // Construct a new XAConnection with no underlying connection. - // When a JDBC method requires an underlying connection, one - // will be created. We don't create the underlying connection - // beforehand, as it might be coming from an existing - // transaction. - return new XAConnectionImpl( this, null ); - } - - - public XAConnection getXAConnection( String user, String password ) - throws SQLException - { - // Since we create the connection on-demand with newConnection - // or obtain it from a transaction, we cannot support XA - // connections with a caller specified user name. - throw new SQLException( "XAConnection does not support connections with caller specified user name" ); - } - - - public PooledConnection getPooledConnection() - throws SQLException - { - // Construct a new pooled connection and an underlying JDBC - // connection to go along with it. - return new XAConnectionImpl( this, getConnection() ); - } - - - public PooledConnection getPooledConnection( String user, String password ) - throws SQLException - { - // Construct a new pooled connection and an underlying JDBC - // connection to go along with it. - return new XAConnectionImpl( this, getConnection( user, password ) ); - } - - - /** - * Returns the default timeout for all transactions. - */ - public int getTransactionTimeout() - { - return _txTimeout; - } - - - /** - * This method is defined in the interface and implemented in the - * derived class, we re-define it just to make sure it does not - * throw an {@link SQLException} and that we do not need to - * catch one. - */ - public abstract java.io.PrintWriter getLogWriter(); - - - /** - * Sets the default timeout for all transactions. The timeout is - * specified in seconds. Use zero for the default timeout. Calling - * this method does not affect transactions in progress. - * - * @param seconds The timeout in seconds - */ - public void setTransactionTimeout( int seconds ) - { - if ( seconds <= 0 ) - _txTimeout = DEFAULT_TX_TIMEOUT; - else - _txTimeout = seconds; - _background.interrupt(); - } - - - /** - * Returns an underlying connection for the global transaction, - * if one has been associated before. - * - * @param xid The transaction Xid - * @return A connection associated with that transaction, or null - */ - TxConnection getTxConnection( Xid xid ) - { - return (TxConnection) _txConnections.get( xid ); - } - - - /** - * Associates the global transaction with an underlying connection, - * or dissociate it when null is passed. - * - * @param xid The transaction Xid - * @param conn The connection to associate, null to dissociate - */ - TxConnection setTxConnection( Xid xid, TxConnection txConn ) - { - if ( txConn == null ) - return (TxConnection) _txConnections.remove( xid ); - else - return (TxConnection) _txConnections.put( xid, txConn ); - } - - - /** - * Release an unused connection back to the pool. If an XA - * connection has been asked to join an existing transaction, - * it will no longer use it's own connection and make it available - * to newly created connections. - * - * @param conn An open connection that is no longer in use - */ - void releaseConnection( Connection conn ) - { - _pool.push( conn ); - } - - - /** - * Creates a new underlying connection. Used by XA connection - * that lost it's underlying connection when joining a - * transaction and is now asked to produce a new connection. - * - * @return An open connection ready for use - * @throws SQLException An error occured trying to open - * a connection - */ - Connection newConnection() + /** + * Maps underlying JDBC connections into global transaction Xids. + */ + private transient Hashtable _txConnections = new Hashtable(); + + + /** + * This is a pool of free underlying JDBC connections. If two + * XA connections are used in the same transaction, the second + * one will make its underlying JDBC connection available to + * the pool. This is not a real connection pool, only a marginal + * efficiency solution for dealing with shared transactions. + */ + private transient Stack _pool = new Stack(); + + + /** + * A background deamon thread terminating connections that have + * timed out. + */ + private transient Thread _background; + + + /** + * The default timeout for all new transactions. + */ + private int _txTimeout = DEFAULT_TX_TIMEOUT; + + + /** + * The default timeout for all new transactions is 10 seconds. + */ + public final static int DEFAULT_TX_TIMEOUT = 10; + + + + + /** + * Implementation details: + * If two XAConnections are associated with the same transaction + * (one with a start the other with a join) they must use the + * same underlying JDBC connection. They lookup the underlying + * JDBC connection based on the transaction's Xid in the + * originating XADataSource. + * + * Currently the XADataSource must be the exact same object, + * this should be changed so all XADataSources that are equal + * share a table of all enlisted connections + * + * To test is two connections should fall under the same + * transaction we match the resource managers by comparing the + * database/user they fall under using a comparison of the + * XADataSource properties. + */ + + + public XADataSourceImpl() + { + super(); + + // Create a background thread that will track transactions + // that timeout, abort them and release the underlying + // connections to the pool. + _background = new Thread( this, "XADataSource Timeout Daemon" ); + _background.setPriority( Thread.MIN_PRIORITY ); + _background.setDaemon( true ); + _background.start(); + } + + + public XAConnection getXAConnection() + throws SQLException + { + // Construct a new XAConnection with no underlying connection. + // When a JDBC method requires an underlying connection, one + // will be created. We don't create the underlying connection + // beforehand, as it might be coming from an existing + // transaction. + return new XAConnectionImpl( this, null ); + } + + + public XAConnection getXAConnection( String user, String password ) + throws SQLException + { + // Since we create the connection on-demand with newConnection + // or obtain it from a transaction, we cannot support XA + // connections with a caller specified user name. + throw new SQLException( "XAConnection does not support connections with caller specified user name" ); + } + + + public PooledConnection getPooledConnection() + throws SQLException + { + // Construct a new pooled connection and an underlying JDBC + // connection to go along with it. + return new XAConnectionImpl( this, getConnection() ); + } + + + public PooledConnection getPooledConnection( String user, String password ) throws SQLException - { - Connection conn; + { + // Construct a new pooled connection and an underlying JDBC + // connection to go along with it. + return new XAConnectionImpl( this, getConnection( user, password ) ); + } + + + /** + * Returns the default timeout for all transactions. + */ + public int getTransactionTimeout() + { + return _txTimeout; + } + + + /** + * This method is defined in the interface and implemented in the + * derived class, we re-define it just to make sure it does not + * throw an {@link SQLException} and that we do not need to + * catch one. + */ + public abstract java.io.PrintWriter getLogWriter(); + + + /** + * Sets the default timeout for all transactions. The timeout is + * specified in seconds. Use zero for the default timeout. Calling + * this method does not affect transactions in progress. + * + * @param seconds The timeout in seconds + */ + public void setTransactionTimeout( int seconds ) + { + if ( seconds <= 0 ) + _txTimeout = DEFAULT_TX_TIMEOUT; + else + _txTimeout = seconds; + _background.interrupt(); + } + + + /** + * Returns an underlying connection for the global transaction, + * if one has been associated before. + * + * @param xid The transaction Xid + * @return A connection associated with that transaction, or null + */ + TxConnection getTxConnection( Xid xid ) + { + return (TxConnection) _txConnections.get( xid ); + } + - // Check in the pool first. - if ( ! _pool.empty() ) { - conn = (Connection) _pool.pop(); - return conn; + /** + * Associates the global transaction with an underlying connection, + * or dissociate it when null is passed. + * + * @param xid The transaction Xid + * @param conn The connection to associate, null to dissociate + */ + TxConnection setTxConnection( Xid xid, TxConnection txConn ) + { + if ( txConn == null ) + return (TxConnection) _txConnections.remove( xid ); + else + return (TxConnection) _txConnections.put( xid, txConn ); } - return getConnection(); - } - - - /** - * XXX Not fully implemented yet and no code to really - * test it. - */ - Xid[] getTxRecover() - { - Vector list; - Enumeration enum; - TxConnection txConn; - - list = new Vector(); - enum = _txConnections.elements(); - while ( enum.hasMoreElements() ) { - txConn = (TxConnection) enum.nextElement(); - if ( txConn.conn != null && txConn.prepared ) - list.add( txConn.xid ); + + + /** + * Release an unused connection back to the pool. If an XA + * connection has been asked to join an existing transaction, + * it will no longer use it's own connection and make it available + * to newly created connections. + * + * @param conn An open connection that is no longer in use + */ + void releaseConnection( Connection conn ) + { + _pool.push( conn ); + } + + + /** + * Creates a new underlying connection. Used by XA connection + * that lost it's underlying connection when joining a + * transaction and is now asked to produce a new connection. + * + * @return An open connection ready for use + * @throws SQLException An error occured trying to open + * a connection + */ + Connection newConnection() + throws SQLException + { + Connection conn; + + // Check in the pool first. + if ( ! _pool.empty() ) + { + conn = (Connection) _pool.pop(); + return conn; + } + return getConnection(); } - return (Xid[]) list.toArray(); - } - - - /** - * Returns the transaction isolation level to use with all newly - * created transactions, or {@link Connection#TRANSACTION_NONE} - * if using the driver's default isolation level. - */ - public int isolationLevel() - { - return Connection.TRANSACTION_NONE; - } - - - public void run() - { - Enumeration enum; - int reduce; - long timeout; - TxConnection txConn; - - while ( true ) { - // Go to sleep for the duration of a transaction - // timeout. This mean transactions will timeout on average - // at _txTimeout * 1.5. - try { - Thread.sleep( _txTimeout * 1000 ); - } catch ( InterruptedException except ) { - } - - try { - // Check to see if there are any pooled connections - // we can release. We release 10% of the pooled - // connections each time, so in a heavy loaded - // environment we don't get to release that many, but - // as load goes down we do. These are not actually - // pooled connections, but connections that happen to - // get in and out of a transaction, not that many. - reduce = _pool.size() - ( _pool.size() / 10 ) - 1; - if ( reduce >= 0 && _pool.size() > reduce ) { - if ( getLogWriter() != null ) - getLogWriter().println( "DataSource " + toString() + - ": Reducing internal connection pool size from " + - _pool.size() + " to " + reduce ); - while ( _pool.size() > reduce ) { - try { - ( (Connection) _pool.pop() ).close(); - } catch ( SQLException except ) { } - } + + + /** + * XXX Not fully implemented yet and no code to really + * test it. + */ + Xid[] getTxRecover() + { + Vector list; + Enumeration enum; + TxConnection txConn; + + list = new Vector(); + enum = _txConnections.elements(); + while ( enum.hasMoreElements() ) + { + txConn = (TxConnection) enum.nextElement(); + if ( txConn.conn != null && txConn.prepared ) + list.add( txConn.xid ); } - } catch ( Exception except ) { } - - // Look for all connections inside a transaction that - // should have timed out by now. - timeout = System.currentTimeMillis(); - enum = _txConnections.elements(); - while ( enum.hasMoreElements() ) { - txConn = (TxConnection) enum.nextElement(); - // If the transaction timed out, we roll it back and - // invalidate it, but do not remove it from the transaction - // list yet. We wait for the next iteration, minimizing the - // chance of a NOTA exception. - if ( txConn.conn == null ) { - _txConnections.remove( txConn.xid ); - // Chose not to use an iterator so we must - // re-enumerate the list after removing - // an element from it. - enum = _txConnections.elements(); - } else if ( txConn.timeout < timeout ) { - - try { - Connection underlying; - - synchronized ( txConn ) { - if ( txConn.conn == null ) - continue; - if ( getLogWriter() != null ) - getLogWriter().println( "DataSource " + toString() + - ": Transaction timed out and being aborted: " + - txConn.xid ); - // Remove the connection from the transaction - // association. XAConnection will now have - // no underlying connection and attempt to - // create a new one. - underlying = txConn.conn; - txConn.conn = null; - txConn.timedOut = true; - - // Rollback the underlying connection to - // abort the transaction and release the - // underlying connection to the pool. - try { - underlying.rollback(); - releaseConnection( underlying ); - } catch ( SQLException except ) { - if ( getLogWriter() != null ) - getLogWriter().println( "DataSource " + toString() + - ": Error aborting timed out transaction: " + except ); - try { - underlying.close(); - } catch ( SQLException e2 ) { } - } + return (Xid[]) list.toArray(); + } + + + /** + * Returns the transaction isolation level to use with all newly + * created transactions, or {@link Connection#TRANSACTION_NONE} + * if using the driver's default isolation level. + */ + public int isolationLevel() + { + return Connection.TRANSACTION_NONE; + } + + + public void run() + { + Enumeration enum; + int reduce; + long timeout; + TxConnection txConn; + + while ( true ) + { + // Go to sleep for the duration of a transaction + // timeout. This mean transactions will timeout on average + // at _txTimeout * 1.5. + try + { + Thread.sleep( _txTimeout * 1000 ); + } + catch ( InterruptedException except ) + {} + + try + { + // Check to see if there are any pooled connections + // we can release. We release 10% of the pooled + // connections each time, so in a heavy loaded + // environment we don't get to release that many, but + // as load goes down we do. These are not actually + // pooled connections, but connections that happen to + // get in and out of a transaction, not that many. + reduce = _pool.size() - ( _pool.size() / 10 ) - 1; + if ( reduce >= 0 && _pool.size() > reduce ) + { + if ( getLogWriter() != null ) + getLogWriter().println( "DataSource " + toString() + + ": Reducing internal connection pool size from " + + _pool.size() + " to " + reduce ); + while ( _pool.size() > reduce ) + { + try + { + ( (Connection) _pool.pop() ).close(); + } + catch ( SQLException except ) + { } + } + } + } + catch ( Exception except ) + { } + + // Look for all connections inside a transaction that + // should have timed out by now. + + timeout = System.currentTimeMillis(); + enum = _txConnections.elements(); + while ( enum.hasMoreElements() ) + { + txConn = (TxConnection) enum.nextElement(); + // If the transaction timed out, we roll it back and + // invalidate it, but do not remove it from the transaction + // list yet. We wait for the next iteration, minimizing the + // chance of a NOTA exception. + if ( txConn.conn == null ) + { + _txConnections.remove( txConn.xid ); + // Chose not to use an iterator so we must + // re-enumerate the list after removing + // an element from it. + enum = _txConnections.elements(); + } + else if ( txConn.timeout < timeout ) + { + + try + { + Connection underlying; + + synchronized ( txConn ) + { + if ( txConn.conn == null ) + continue; + if ( getLogWriter() != null ) + getLogWriter().println( "DataSource " + toString() + + ": Transaction timed out and being aborted: " + + txConn.xid ); + // Remove the connection from the transaction + // association. XAConnection will now have + // no underlying connection and attempt to + // create a new one. + underlying = txConn.conn; + txConn.conn = null; + txConn.timedOut = true; + + // Rollback the underlying connection to + // abort the transaction and release the + // underlying connection to the pool. + try + { + underlying.rollback(); + releaseConnection( underlying ); + } + catch ( SQLException except ) + { + if ( getLogWriter() != null ) + getLogWriter().println( "DataSource " + toString() + + ": Error aborting timed out transaction: " + except ); + try + { + underlying.close(); + } + catch ( SQLException e2 ) + { } + } + } + } + catch ( Exception except ) + { } + + } } - } catch ( Exception except ) { } - } - } } - } - - - - public void debug( PrintWriter writer ) - { - Enumeration enum; - TxConnection txConn; - StringBuffer buffer; - - writer.println( "Debug info for XADataSource:" ); - enum = _txConnections.elements(); - if ( ! enum.hasMoreElements() ) - writer.println( "Empty" ); - while ( enum.hasMoreElements() ) { - buffer = new StringBuffer(); - txConn = (TxConnection) enum.nextElement(); - buffer.append( "TxConnection " ); - if ( txConn.xid != null ) - buffer.append( txConn.xid ); - if ( txConn.conn != null ) - buffer.append( ' ' ).append( txConn.conn ); - buffer.append( " count: " ).append( txConn.count ); - if ( txConn.prepared ) - buffer.append( " prepared" ); - if ( txConn.timedOut ) - buffer.append( " timed-out" ); - if ( txConn.readOnly ) - buffer.append( " read-only" ); - writer.println( buffer.toString() ); + + + + public void debug( PrintWriter writer ) + { + Enumeration enum; + TxConnection txConn; + StringBuffer buffer; + + writer.println( "Debug info for XADataSource:" ); + enum = _txConnections.elements(); + if ( ! enum.hasMoreElements() ) + writer.println( "Empty" ); + while ( enum.hasMoreElements() ) + { + buffer = new StringBuffer(); + txConn = (TxConnection) enum.nextElement(); + buffer.append( "TxConnection " ); + if ( txConn.xid != null ) + buffer.append( txConn.xid ); + if ( txConn.conn != null ) + buffer.append( ' ' ).append( txConn.conn ); + buffer.append( " count: " ).append( txConn.count ); + if ( txConn.prepared ) + buffer.append( " prepared" ); + if ( txConn.timedOut ) + buffer.append( " timed-out" ); + if ( txConn.readOnly ) + buffer.append( " read-only" ); + writer.println( buffer.toString() ); + } + enum = _pool.elements(); + while ( enum.hasMoreElements() ) + writer.println( "Pooled underlying: " + enum.nextElement().toString() ); } - enum = _pool.elements(); - while ( enum.hasMoreElements() ) - writer.println( "Pooled underlying: " + enum.nextElement().toString() ); - } - + } diff --git a/src/interfaces/jdbc/utils/CheckVersion.java b/src/interfaces/jdbc/utils/CheckVersion.java index 8832543e72..74c40813cd 100644 --- a/src/interfaces/jdbc/utils/CheckVersion.java +++ b/src/interfaces/jdbc/utils/CheckVersion.java @@ -7,62 +7,68 @@ package utils; */ public class CheckVersion { - /** - * Check for the existence of a class by attempting to load it - */ - public static boolean checkClass(String c) { - try { - Class.forName(c); - } catch(Exception e) { - return false; + /** + * Check for the existence of a class by attempting to load it + */ + public static boolean checkClass(String c) + { + try + { + Class.forName(c); + } + catch (Exception e) + { + return false; + } + return true; } - return true; - } - - /** - * This first checks java.vm.version for 1.1, 1.2 or 1.3. - * - * It writes jdbc1 to stdout for the 1.1.x VM. - * - * For 1.2 or 1.3, it checks for the existence of the javax.sql.DataSource - * interface, and if found writes enterprise to stdout. If the interface - * is not found, it writes jdbc2 to stdout. - * - * PS: It also looks for the existence of java.lang.Byte which appeared in - * JDK1.1.0 incase java.vm.version is not heeded by some JVM's. - * - * If it can't work it out, it writes huho to stdout. - * - * The make file uses the written results to determine which rule to run. - * - * Bugs: This needs thorough testing. - */ - public static void main(String args[]) - { - String vmversion = System.getProperty("java.vm.version"); - - System.out.println("postgresql.jdbc="+System.getProperty("postgresql.jdbc")); - - // We are running a 1.1 JVM - if(vmversion.startsWith("1.1")) { - System.out.println("jdbc1"); - //System.exit(0); - } - else - // We are running a 1.2 or 1.3 JVM - if(vmversion.startsWith("1.2") || - vmversion.startsWith("1.3") || - checkClass("java.lang.Byte") - ) { - - // Check to see if we have the standard extensions. If so, then - // we want the enterprise edition, otherwise the jdbc2 driver. - if(checkClass("javax.sql.DataSource")) - System.out.println("enterprise"); - else - System.out.println("jdbc2"); - //System.exit(0); + + /** + * This first checks java.vm.version for 1.1, 1.2 or 1.3. + * + * It writes jdbc1 to stdout for the 1.1.x VM. + * + * For 1.2 or 1.3, it checks for the existence of the javax.sql.DataSource + * interface, and if found writes enterprise to stdout. If the interface + * is not found, it writes jdbc2 to stdout. + * + * PS: It also looks for the existence of java.lang.Byte which appeared in + * JDK1.1.0 incase java.vm.version is not heeded by some JVM's. + * + * If it can't work it out, it writes huho to stdout. + * + * The make file uses the written results to determine which rule to run. + * + * Bugs: This needs thorough testing. + */ + public static void main(String args[]) + { + String vmversion = System.getProperty("java.vm.version"); + + System.out.println("postgresql.jdbc=" + System.getProperty("postgresql.jdbc")); + + // We are running a 1.1 JVM + if (vmversion.startsWith("1.1")) + { + System.out.println("jdbc1"); + //System.exit(0); + } + else + // We are running a 1.2 or 1.3 JVM + if (vmversion.startsWith("1.2") || + vmversion.startsWith("1.3") || + checkClass("java.lang.Byte") + ) + { + + // Check to see if we have the standard extensions. If so, then + // we want the enterprise edition, otherwise the jdbc2 driver. + if (checkClass("javax.sql.DataSource")) + System.out.println("enterprise"); + else + System.out.println("jdbc2"); + //System.exit(0); + } + System.setProperty("postgresql.jdbc", "yoyo"); } - System.setProperty("postgresql.jdbc","yoyo"); - } }