View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2000 David Warnock
6    * 
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     David Warnock (david At sundayta.co.uk)
42   *     Sundayta Ltd
43   *     International House, 
44   *     174 Three Bridges Road, 
45   *     Crawley, West Sussex 
46   *     RH10 1LE, UK
47   *
48   */
49  
50  package org.melati.poem.dbms;
51  
52  import java.sql.DatabaseMetaData;
53  import java.sql.ResultSet;
54  import java.sql.SQLException;
55  
56  import org.melati.poem.Table;
57  
58  import org.melati.poem.PoemType;
59  import org.melati.poem.SQLPoemType;
60  import org.melati.poem.BinaryPoemType;
61  import org.melati.poem.DoublePoemType;
62  import org.melati.poem.IntegerPoemType;
63  import org.melati.poem.LongPoemType;
64  
65  import org.melati.poem.NoSuchColumnPoemException;
66  import org.melati.poem.SeriousPoemException;
67  import org.melati.poem.SQLPoemException;
68  
69  
70   /**
71    * A Driver for Postgresql.
72    * See http://www.postgresql.org/
73    * 
74    * <b>Backwards compatabilty</b>
75    * Previous (7.4) versions allowed single quotes to be quoted using C style escaping.
76    * In version 9.1 you need to explicitly allow this escaping by configuring the server:
77    * <code>
78    * backslash_quote = on
79    * standard_conforming_strings = off
80    * </code>
81    **/
82  
83  public class Postgresql extends AnsiStandard {
84  
85    /**
86     * Constructor.
87     */
88    public Postgresql() {
89      setDriverClassName("org.postgresql.Driver");
90    }
91  
92  
93    /**
94     * Whether this DBMS can drop columns. 
95     * 
96     * Postgresql has been able to since 7.4
97     * 
98     * @return true if we can
99     */
100   @Override
101   public boolean canDropColumns() {
102     return true;
103   }
104 
105 
106   /**
107    * Accommodate casting in placeholders.
108    *  
109    * @param type the type to cast
110    * @return the place holder string
111    */
112   @Override
113   public String preparedStatementPlaceholder(PoemType<?> type) {
114     if (type instanceof IntegerPoemType)
115       return "CAST(? AS INT4)";
116     else if (type instanceof LongPoemType)
117       return "CAST(? AS INT8)";
118     else if (type instanceof DoublePoemType)
119       return "CAST(? AS FLOAT8)";
120     else 
121       return "?";
122   }
123 
124   /**
125    * Accommodate String/Text distinction. 
126    * 
127    * @param size the string length (-1 means no limit)
128    * @return the SQL definition for a string of this size 
129    * @throws SQLException
130    * @see org.melati.poem.dbms.AnsiStandard#getStringSqlDefinition(int)
131    */
132   @Override
133   public String getStringSqlDefinition(int size) throws SQLException {
134     if (size < 0) { 
135        return "TEXT";
136     }
137        return super.getStringSqlDefinition(size);
138   }
139 
140   @Override
141   public String getBinarySqlDefinition(int size) throws SQLException {
142     return "BYTEA";
143   }
144 
145 /*
146   public String getBinarySqlDefinition(int size) {
147    // BLOBs in Postgres are represented as OIDs pointing to the data
148     return "OID";
149   }
150 */
151 
152   /**
153    * An Object Id <code>PoemType</code>.
154    */
155 /*
156   
157   public static class OidPoemType extends IntegerPoemType {
158       public OidPoemType(boolean nullable) {
159           super(Types.INTEGER, "OID", nullable);
160       }
161 
162       protected boolean _canRepresent(SQLPoemType other) {
163           return other instanceof BinaryPoemType;
164       }
165 
166       public PoemType canRepresent(PoemType other) {
167           return other instanceof BinaryPoemType &&
168                  !(!getNullable() && 
169        ((BinaryPoemType)other).getNullable()) ? other : null;
170       }
171   }
172 */
173   
174   @Override
175   public SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet md)
176       throws SQLException {
177     return
178     md.getString("TYPE_NAME").equals("bytea") ?
179             new BinaryPoemType(md.getInt("NULLABLE") ==
180                                 DatabaseMetaData.columnNullable, -1) :
181             super.defaultPoemTypeOfColumnMetaData(md);
182 //    md.getString("TYPE_NAME").equals("oid") ?
183 //            new OidPoemType(md.getInt("NULLABLE") ==
184 //                                DatabaseMetaData.columnNullable) :
185 //           super.defaultPoemTypeOfColumnMetaData(md);
186   }
187 
188   @Override
189   public SQLPoemException exceptionForUpdate(
190       Table<?> table, String sql, boolean insert, SQLException e) {
191 
192     String m = e.getMessage();
193 
194       // Postgres's duplicate key message is:
195       // "Cannot insert a duplicate key into unique index user_login_index"
196 
197     if (m != null &&
198         m.indexOf("duplicate key") >= 0) {
199 
200       // We call POEM's own indexes <table>_<column>_index:
201       // see Table.dbCreateIndex
202 
203       int s, u;
204       if (m.endsWith("_index\n") &&
205          (s = m.lastIndexOf(' ')) >= 0 && (u = m.indexOf('_', s+1)) >= 0) {
206         String colname = m.substring(u+1, m.length() - 7);
207         try {
208           return new DuplicateKeySQLPoemException(table.getColumn(colname),
209                                                   sql, insert, e);
210         }
211         catch (NoSuchColumnPoemException f) {
212           throw new SeriousPoemException(
213                "Duplicate Key exception thrown on a non-existant column",f);
214         }
215       }
216       return new DuplicateKeySQLPoemException(table, sql, insert, e);
217     }
218       else
219         return super.exceptionForUpdate(table, sql, insert, e);
220   }
221 
222 
223 }