View Javadoc
1   package edu.internet2.middleware.grouper.ext.org.apache.ddlutils.platform.postgresql;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.sql.Connection;
23  import java.sql.DriverManager;
24  import java.sql.PreparedStatement;
25  import java.sql.SQLException;
26  import java.sql.Statement;
27  import java.sql.Types;
28  import java.util.Iterator;
29  import java.util.Map;
30  
31  import org.apache.commons.beanutils.DynaBean;
32  import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.DatabaseOperationException;
33  import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.PlatformInfo;
34  import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.dynabean.SqlDynaProperty;
35  import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.platform.PlatformImplBase;
36  
37  /**
38   * The platform implementation for PostgresSql.
39   * 
40   * @version $Revision: 231306 $
41   */
42  public class PostgreSqlPlatform extends PlatformImplBase
43  {
44      /** Database name of this platform. */
45      public static final String DATABASENAME      = "PostgreSql";
46      /** The standard PostgreSQL jdbc driver. */
47      public static final String JDBC_DRIVER       = "org.postgresql.Driver";
48      /** The subprotocol used by the standard PostgreSQL driver. */
49      public static final String JDBC_SUBPROTOCOL  = "postgresql";
50  
51      /**
52       * Creates a new platform instance.
53       */
54      public PostgreSqlPlatform()
55      {
56          PlatformInfo info = getPlatformInfo();
57  
58          // this is the default length though it might be changed when building PostgreSQL
59          // in file src/include/postgres_ext.h
60          info.setMaxIdentifierLength(31);
61  
62          info.addNativeTypeMapping(Types.ARRAY,         "BYTEA",            Types.LONGVARBINARY);
63          info.addNativeTypeMapping(Types.BINARY,        "BYTEA",            Types.LONGVARBINARY);
64          info.addNativeTypeMapping(Types.BIT,           "BOOLEAN");
65          info.addNativeTypeMapping(Types.BLOB,          "BYTEA",            Types.LONGVARBINARY);
66          info.addNativeTypeMapping(Types.CLOB,          "TEXT",             Types.LONGVARCHAR);
67          info.addNativeTypeMapping(Types.DECIMAL,       "NUMERIC",          Types.NUMERIC);
68          info.addNativeTypeMapping(Types.DISTINCT,      "BYTEA",            Types.LONGVARBINARY);
69          info.addNativeTypeMapping(Types.DOUBLE,        "DOUBLE PRECISION");
70          info.addNativeTypeMapping(Types.FLOAT,         "DOUBLE PRECISION", Types.DOUBLE);
71          info.addNativeTypeMapping(Types.JAVA_OBJECT,   "BYTEA",            Types.LONGVARBINARY);
72          info.addNativeTypeMapping(Types.LONGVARBINARY, "BYTEA");
73          info.addNativeTypeMapping(Types.LONGVARCHAR,   "TEXT",             Types.LONGVARCHAR);
74          info.addNativeTypeMapping(Types.NULL,          "BYTEA",            Types.LONGVARBINARY);
75          info.addNativeTypeMapping(Types.OTHER,         "BYTEA",            Types.LONGVARBINARY);
76          info.addNativeTypeMapping(Types.REF,           "BYTEA",            Types.LONGVARBINARY);
77          info.addNativeTypeMapping(Types.STRUCT,        "BYTEA",            Types.LONGVARBINARY);
78          info.addNativeTypeMapping(Types.TINYINT,       "SMALLINT",         Types.SMALLINT);
79          info.addNativeTypeMapping(Types.VARBINARY,     "BYTEA",            Types.LONGVARBINARY);
80          info.addNativeTypeMapping("BOOLEAN",  "BOOLEAN", "BIT");
81          info.addNativeTypeMapping("DATALINK", "BYTEA",   "LONGVARBINARY");
82  
83          info.setDefaultSize(Types.CHAR,    254);
84          info.setDefaultSize(Types.VARCHAR, 254);
85  
86          // no support for specifying the size for these types (because they are mapped
87          // to BYTEA which back-maps to BLOB)
88          info.setHasSize(Types.BINARY,    false);
89          info.setHasSize(Types.VARBINARY, false);
90  
91          setSqlBuilder(new PostgreSqlBuilder(this));
92          setModelReader(new PostgreSqlModelReader(this));
93      }
94  
95      /**
96       * {@inheritDoc}
97       */
98      public String getName()
99      {
100         return DATABASENAME;
101     }
102 
103     /**
104      * Creates or drops the database referenced by the given connection url.
105      * 
106      * @param jdbcDriverClassName The jdbc driver class name
107      * @param connectionUrl       The url to connect to the database if it were already created
108      * @param username            The username for creating the database
109      * @param password            The password for creating the database
110      * @param parameters          Additional parameters for the operation
111      * @param createDb            Whether to create or drop the database
112      */
113     private void createOrDropDatabase(String jdbcDriverClassName, String connectionUrl, String username, String password, Map parameters, boolean createDb) throws DatabaseOperationException, UnsupportedOperationException
114     {
115         if (JDBC_DRIVER.equals(jdbcDriverClassName))
116         {
117             int slashPos = connectionUrl.lastIndexOf('/');
118 
119             if (slashPos < 0)
120             {
121                 throw new DatabaseOperationException("Cannot parse the given connection url "+connectionUrl);
122             }
123 
124             int          paramPos   = connectionUrl.lastIndexOf('?');
125             String       baseDb     = connectionUrl.substring(0, slashPos + 1) + "template1";
126             String       dbName     = (paramPos > slashPos ? connectionUrl.substring(slashPos + 1, paramPos) : connectionUrl.substring(slashPos + 1));
127             Connection   connection = null;
128             Statement    stmt       = null;
129             StringBuffer sql        = new StringBuffer();
130 
131             sql.append(createDb ? "CREATE" : "DROP");
132             sql.append(" DATABASE ");
133             sql.append(dbName);
134             if ((parameters != null) && !parameters.isEmpty())
135             {
136                 for (Iterator it = parameters.entrySet().iterator(); it.hasNext();)
137                 {
138                     Map.Entry entry = (Map.Entry)it.next();
139 
140                     sql.append(" ");
141                     sql.append(entry.getKey().toString());
142                     if (entry.getValue() != null)
143                     {
144                         sql.append(" ");
145                         sql.append(entry.getValue().toString());
146                     }
147                 }
148             }
149             if (getLog().isDebugEnabled())
150             {
151                 getLog().debug("About to create database via "+baseDb+" using this SQL: "+sql.toString());
152             }
153             try
154             {
155                 Class.forName(jdbcDriverClassName);
156 
157                 connection = DriverManager.getConnection(baseDb, username, password);
158                 stmt       = connection.createStatement();
159                 stmt.execute(sql.toString());
160                 logWarnings(connection);
161             }
162             catch (Exception ex)
163             {
164                 throw new DatabaseOperationException("Error while trying to " + (createDb ? "create" : "drop") + " a database: "+ex.getLocalizedMessage(), ex);
165             }
166             finally
167             {
168                 if (stmt != null)
169                 {
170                     try
171                     {
172                         stmt.close();
173                     }
174                     catch (SQLException ex)
175                     {}
176                 }
177                 if (connection != null)
178                 {
179                     try
180                     {
181                         connection.close();
182                     }
183                     catch (SQLException ex)
184                     {}
185                 }
186             }
187         }
188         else
189         {
190             throw new UnsupportedOperationException("Unable to " + (createDb ? "create" : "drop") + " a PostgreSQL database via the driver "+jdbcDriverClassName);
191         }
192     }
193 
194     /**
195      * {@inheritDoc}
196      */
197     public void createDatabase(String jdbcDriverClassName, String connectionUrl, String username, String password, Map parameters) throws DatabaseOperationException, UnsupportedOperationException
198     {
199         // With PostgreSQL, you create a database by executing "CREATE DATABASE" in an existing database (usually 
200         // the template1 database because it usually exists)
201         createOrDropDatabase(jdbcDriverClassName, connectionUrl, username, password, parameters, true);
202     }
203 
204     /**
205      * {@inheritDoc}
206      */
207     public void dropDatabase(String jdbcDriverClassName, String connectionUrl, String username, String password) throws DatabaseOperationException, UnsupportedOperationException
208     {
209         // With PostgreSQL, you create a database by executing "DROP DATABASE" in an existing database (usually 
210         // the template1 database because it usually exists)
211         createOrDropDatabase(jdbcDriverClassName, connectionUrl, username, password, null, false);
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     protected void setObject(PreparedStatement statement, int sqlIndex, DynaBean dynaBean, SqlDynaProperty property) throws SQLException
218     {
219         int     typeCode = property.getColumn().getTypeCode();
220         Object  value    = dynaBean.get(property.getName());
221 
222         // PostgreSQL doesn't like setNull for BYTEA columns
223         if (value == null)
224         {
225             switch (typeCode)
226             {
227                 case Types.BINARY:
228                 case Types.VARBINARY:
229                 case Types.LONGVARBINARY:
230                 case Types.BLOB:
231                     statement.setBytes(sqlIndex, null);
232                     break;
233                 default:
234                     statement.setNull(sqlIndex, typeCode);
235                     break;
236             }
237         }
238         else
239         {
240             super.setObject(statement, sqlIndex, dynaBean, property);
241         }
242     }
243 }