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 }