View Javadoc

1   package org.apache.torque.templates.transformer.sql;
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.util.ArrayList;
23  import java.util.Date;
24  import java.util.List;
25  
26  import org.apache.commons.lang.ObjectUtils;
27  import org.apache.commons.lang.StringUtils;
28  import org.apache.torque.generator.control.ControllerState;
29  import org.apache.torque.generator.source.SourceElement;
30  import org.apache.torque.generator.source.SourcePath;
31  import org.apache.torque.generator.source.transform.SourceTransformer;
32  import org.apache.torque.generator.source.transform.SourceTransformerException;
33  import org.apache.torque.templates.TemplateOptionName;
34  import org.apache.torque.templates.TorqueSchemaAttributeName;
35  import org.apache.torque.templates.TorqueSchemaElementName;
36  import org.apache.torque.templates.TorqueSchemaIdMethod;
37  import org.apache.torque.templates.platform.Platform;
38  import org.apache.torque.templates.platform.PlatformFactory;
39  import org.apache.torque.templates.transformer.CollectAttributeSetTrueTransformer;
40  import org.apache.torque.templates.transformer.SchemaTypeHelper;
41  import org.apache.torque.templates.transformer.om.OMColumnTransformer;
42  import org.apache.torque.templates.transformer.om.TableChildElementName;
43  import org.apache.torque.templates.typemapping.SchemaType;
44  import org.apache.torque.templates.typemapping.SqlType;
45  import org.apache.torque.templates.typemapping.TypeMap;
46  
47  /**
48   * Transforms the tables in the OM model for sql generation.
49   */
50  public class SQLTransformer implements SourceTransformer
51  {
52      private final CollectAttributeSetTrueTransformer collectAttributeSetTrueTransformer
53              = new CollectAttributeSetTrueTransformer();
54  
55      public SourceElement transform(
56              SourceElement databaseElement,
57              ControllerState controllerState)
58          throws SourceTransformerException
59      {
60          TemplateOptionName.checkRequiredOptions(
61                  controllerState,
62                  TemplateOptionName.DATABASE);
63  
64          List<SourceElement> tableElements = databaseElement.getChildren(
65                  TorqueSchemaElementName.TABLE.getName());
66          for (SourceElement tableElement : tableElements)
67          {
68              transformTable(tableElement, controllerState);
69          }
70          return databaseElement;
71      }
72  
73      public void transformTable(
74              SourceElement tableElement,
75              ControllerState controllerState)
76          throws SourceTransformerException
77      {
78          Object idMethod = tableElement.getAttribute(
79                  TorqueSchemaAttributeName.ID_METHOD.getName());
80          if (idMethod == null)
81          {
82              Object defaultIdMethod = tableElement.getParent().getAttribute(
83                      TorqueSchemaAttributeName.DEFAULT_ID_METHOD.getName());
84              if (defaultIdMethod == null)
85              {
86                  throw new SourceTransformerException("id Method is not set"
87                          + " on table "
88                          + tableElement.getAttribute(
89                                  TorqueSchemaAttributeName.NAME.getName())
90                          + " and defaultIdMethod is not set on database");
91              }
92              tableElement.setAttribute(
93                      TorqueSchemaAttributeName.ID_METHOD.getName(),
94                      defaultIdMethod);
95          }
96          String tableName = (String) tableElement.getAttribute(
97                  TorqueSchemaAttributeName.NAME);
98          if (tableElement.getAttribute(
99                  SqlAttributeName.PRIMARY_KEY_CONSTRAINT_NAME)
100             == null)
101         {
102             String primaryKeyConstraintName = tableName + "_PK";
103             tableElement.setAttribute(
104                     SqlAttributeName.PRIMARY_KEY_CONSTRAINT_NAME,
105                     primaryKeyConstraintName);
106         }
107         if (tableElement.getAttribute(SqlAttributeName.SEQUENCE_NAME)
108                 == null)
109         {
110             String sequenceName = null;
111             SourceElement idMethodParameterElement = tableElement.getChild(
112                     TorqueSchemaElementName.ID_METHOD_PARAMETER);
113             if (idMethodParameterElement != null)
114             {
115                 sequenceName = (String) idMethodParameterElement.getAttribute(
116                         TorqueSchemaAttributeName.VALUE);
117             }
118             if (StringUtils.isBlank(sequenceName))
119             {
120                 sequenceName = tableName + "_SEQ";
121             }
122             tableElement.setAttribute(
123                     SqlAttributeName.SEQUENCE_NAME,
124                     sequenceName);
125         }
126 
127         // primary keys
128         collectAttributeSetTrueTransformer.transform(
129                 tableElement,
130                 controllerState,
131                 TorqueSchemaElementName.COLUMN,
132                 TorqueSchemaAttributeName.PRIMARY_KEY,
133                 TableChildElementName.PRIMARY_KEYS);
134 
135         StringBuilder primaryKeyColumnNames = new StringBuilder();
136         SourceElement primaryKeysElement = tableElement.getChild(
137                 TableChildElementName.PRIMARY_KEYS);
138         boolean firstPk = true;
139         for (SourceElement primaryKeyColumn : primaryKeysElement.getChildren())
140         {
141             if (!firstPk)
142             {
143                 primaryKeyColumnNames.append(", ");
144             }
145             primaryKeyColumnNames.append(primaryKeyColumn.getAttribute(
146                             TorqueSchemaAttributeName.NAME.getName()));
147             firstPk = false;
148         }
149         tableElement.setAttribute(
150                 SqlAttributeName.PRIMARY_KEY_COLUMN_NAMES,
151                 primaryKeyColumnNames.toString());
152 
153         // unique
154         int uniqueIndex = 1;
155         for (SourceElement uniqueElement : tableElement.getChildren(
156                 TorqueSchemaElementName.UNIQUE.getName()))
157         {
158             if (uniqueElement.getAttribute(
159                         TorqueSchemaAttributeName.NAME.getName())
160                     == null)
161             {
162                 uniqueElement.setAttribute(
163                         TorqueSchemaAttributeName.NAME.getName(),
164                         uniqueElement.getParent().getAttribute(
165                                TorqueSchemaAttributeName.NAME.getName())
166                             + "_UQ_" + uniqueIndex);
167             }
168             String uniqueColumnNames = collectAttributes(
169                     uniqueElement,
170                     TorqueSchemaElementName.UNIQUE_COLUMN.getName(),
171                     TorqueSchemaAttributeName.NAME.getName());
172             uniqueElement.setAttribute(
173                     SqlAttributeName.UNIQUE_COLUMN_NAMES,
174                     uniqueColumnNames);
175             ++uniqueIndex;
176         }
177 
178         // index
179         int indexIndex = 1;
180         for (SourceElement indexElement : tableElement.getChildren(
181                 TorqueSchemaElementName.INDEX.getName()))
182         {
183             if (indexElement.getAttribute(
184                     TorqueSchemaAttributeName.NAME.getName())
185                     == null)
186             {
187                 indexElement.setAttribute(
188                         TorqueSchemaAttributeName.NAME.getName(),
189                         indexElement.getParent().getAttribute(
190                                TorqueSchemaAttributeName.NAME.getName())
191                             + "_IDX_" + indexIndex);
192             }
193             String indexColumnNames = collectAttributes(
194                     indexElement,
195                     TorqueSchemaElementName.INDEX_COLUMN.getName(),
196                     TorqueSchemaAttributeName.NAME.getName());
197             indexElement.setAttribute(
198                     SqlAttributeName.INDEX_COLUMN_NAMES,
199                     indexColumnNames);
200             indexIndex++;
201         }
202 
203         List<SourceElement> columnElements = tableElement.getChildren(
204                 TorqueSchemaElementName.COLUMN.getName());
205         for (SourceElement columnElement : columnElements)
206         {
207             transformColumn(columnElement, controllerState);
208         }
209         List<SourceElement> foreignKeyElements = tableElement.getChildren(
210                 TorqueSchemaElementName.FOREIGN_KEY.getName());
211 
212         int fkIndex = 1;
213         for (SourceElement foreignKeyElemenElement : foreignKeyElements)
214         {
215             transformForeignKey(
216                     foreignKeyElemenElement,
217                     controllerState,
218                     fkIndex);
219             ++fkIndex;
220         }
221     }
222 
223     /**
224      * Enriches the column elements with additional attributes
225      * needed for SQL generation.
226      *
227      * @param columnElement the element to enrich, not null.
228      * @param controllerState the current controller state, not null.
229      *
230      * @throws SourceTransformerException if the name or type attributes
231      *         are not set or if the type is unknown.
232      */
233     private void transformColumn(
234             SourceElement columnElement,
235             ControllerState controllerState)
236         throws SourceTransformerException
237     {
238         String sql = (String) columnElement.getAttribute(
239                 SqlAttributeName.DDL_SQL);
240         if (sql == null)
241         {
242             sql = getDdlSql(columnElement, controllerState);
243             columnElement.setAttribute(SqlAttributeName.DDL_SQL, sql);
244         }
245     }
246 
247     /**
248      * Creates the SQL for defining a column.
249      *
250      * @param columnElement the column element for which the SQL
251      *        should be created, not null.
252      * @param controllerState the current controller state, not null.
253      *
254      * @return the SQL for defining the column, not null.
255      *
256      * @throws SourceTransformerException
257      */
258     private String getDdlSql(
259             SourceElement columnElement,
260             ControllerState controllerState)
261         throws SourceTransformerException
262     {
263         SchemaType schemaType = SchemaTypeHelper.getSchemaType(
264                 columnElement,
265                 controllerState);
266         SqlType domainType = SchemaTypeHelper.getDomain(
267                 columnElement,
268                 controllerState);
269         Object size = columnElement.getAttribute(
270                 TorqueSchemaAttributeName.SIZE);
271         Object scale = columnElement.getAttribute(
272                 TorqueSchemaAttributeName.SCALE);
273         Object defaultValue = columnElement.getAttribute(
274                 TorqueSchemaAttributeName.DEFAULT);
275         SqlType sqlType = SchemaTypeHelper.getSqlType(
276                 schemaType,
277                 domainType,
278                 controllerState,
279                 ObjectUtils.toString(size, null),
280                 ObjectUtils.toString(scale, null),
281                 ObjectUtils.toString(defaultValue, null));
282         Platform platform = PlatformFactory.getPlatformFor(
283                 controllerState.getStringOption(
284                         TemplateOptionName.DATABASE));
285 
286         List<String> resultList = new ArrayList<String>();
287 
288         String sqlTypeName = sqlType.getSqlTypeName();
289 
290         if (platform.hasSize(sqlTypeName))
291         {
292             sqlTypeName += sqlType.printSize(
293                     platform.getSizeSuffix(sqlTypeName));
294         }
295 
296         resultList.add(sqlTypeName);
297 
298         if (StringUtils.isNotEmpty(sqlType.getDefaultValue()))
299         {
300             resultList.add("default");
301 
302             if ((SchemaType.DATE == schemaType
303                     || SchemaType.TIME == schemaType
304                     || SchemaType.TIMESTAMP == schemaType))
305             {
306                 if (sqlType.getDefaultValue().startsWith("CURRENT_"))
307                 {
308                     resultList.add(sqlType.getDefaultValue());
309                 }
310                 else
311                 {
312                     Date defaultDate
313                             = OMColumnTransformer.getDefaultValueAsDate(
314                                     sqlType.getDefaultValue());
315                     if (SchemaType.DATE == schemaType) {
316                         resultList.add(platform.getDateString(defaultDate));
317                     }
318                     else if (SchemaType.TIME == schemaType)
319                     {
320                         resultList.add(platform.getTimeString(defaultDate));
321                     }
322                     else
323                     {
324                         resultList.add(platform.getTimestampString(
325                                 defaultDate));
326                     }
327                 }
328             }
329             else if (TypeMap.isTextType(schemaType))
330             {
331                 resultList.add(platform.quoteAndEscape(
332                         sqlType.getDefaultValue()));
333             }
334             else
335             {
336                 resultList.add(sqlType.getDefaultValue());
337             }
338         }
339 
340         boolean primaryKey;
341         {
342             String primaryKeyString = (String) columnElement.getAttribute(
343                     TorqueSchemaAttributeName.PRIMARY_KEY);
344             primaryKey = Boolean.parseBoolean(primaryKeyString);
345         }
346         boolean required;
347         {
348             String requiredString = (String) columnElement.getAttribute(
349                     TorqueSchemaAttributeName.REQUIRED);
350             required = Boolean.parseBoolean(requiredString);
351         }
352         boolean isNotNull = primaryKey || required;
353         String isNotNullString = platform.getNullString(isNotNull);
354 
355         if (platform.createNotNullBeforeAutoincrement())
356         {
357             if (StringUtils.isNotEmpty(isNotNullString))
358             {
359                 resultList.add(isNotNullString);
360             }
361         }
362         // if idMethod was not set explicitly by the user,
363         // the transformTable() method sets the attribute from the
364         // defaultIdMethod of the database, so this always returns
365         // the id method
366         Object idMethod = columnElement.getParent().getAttribute(
367                 TorqueSchemaAttributeName.ID_METHOD);
368         if (primaryKey
369                 && TorqueSchemaIdMethod.NATIVE.getName().equals(idMethod))
370         {
371             String autoIncrement = platform.getAutoIncrement();
372             if (StringUtils.isNotEmpty(autoIncrement))
373             {
374                 resultList.add(autoIncrement);
375             }
376         }
377         if (!platform.createNotNullBeforeAutoincrement())
378         {
379             if (StringUtils.isNotEmpty(isNotNullString))
380             {
381                 resultList.add(isNotNullString);
382             }
383         }
384         return StringUtils.join(resultList.iterator(), ' ');
385     }
386 
387     /**
388      * Sets additional attributes on foreign key elements.
389      *
390      * @param foreignKeyElement the foreign key element to enrich, not null.
391      * @param controllerState the current controller state, not null.
392      * @param fkIndex the number of the foreign-key element in the table.
393      */
394     private void transformForeignKey(
395             SourceElement foreignKeyElement,
396             ControllerState controllerState,
397             int fkIndex)
398     {
399         if (foreignKeyElement.getAttribute(
400                     TorqueSchemaAttributeName.NAME.getName())
401                 == null)
402         {
403             foreignKeyElement.setAttribute(
404                     TorqueSchemaAttributeName.NAME.getName(),
405                     foreignKeyElement.getParent().getAttribute(
406                            TorqueSchemaAttributeName.NAME.getName())
407                         + "_FK_" + fkIndex);
408         }
409         String localColumnsNames = collectAttributes(
410                 foreignKeyElement,
411                 TorqueSchemaElementName.REFERENCE.getName(),
412                 TorqueSchemaAttributeName.LOCAL.getName());
413         foreignKeyElement.setAttribute(
414                 SqlAttributeName.LOCAL_COLUMN_NAMES,
415                 localColumnsNames);
416         String foreignColumnsNames = collectAttributes(
417                 foreignKeyElement,
418                 TorqueSchemaElementName.REFERENCE.getName(),
419                 TorqueSchemaAttributeName.FOREIGN.getName());
420         foreignKeyElement.setAttribute(
421                 SqlAttributeName.FOREIGN_COLUMN_NAMES,
422                 foreignColumnsNames);
423     }
424 
425     /**
426      * Collects attribute values in a comma-separated string.
427      * The elements on which the attribute values are read can be reached
428      * from <code>rootElement</code> via the path <code>sourcePath</code>;
429      * and on these elements, the attributes with name
430      * <code>attributeName</code> are collected.
431      *
432      * @param rootElement the base element from where to start, not null.
433      * @param sourcePath the path from <code>rootElement</code> to the elements
434      *        on which to collect the attributes.
435      * @param attributeName the name of the attributes to collect.
436      *
437      * @return a comma-separated (", ") String containing all the matching
438      *         attribute values.
439      */
440     private String collectAttributes(
441             SourceElement rootElement,
442             String sourcePath,
443             String attributeName)
444     {
445         StringBuilder result = new StringBuilder();
446         List<SourceElement> toCollectFrom = SourcePath.getElements(
447                 rootElement, sourcePath);
448         for (SourceElement sourceElement : toCollectFrom)
449         {
450             Object attributeValue = sourceElement.getAttribute(attributeName);
451             if (attributeValue != null)
452             {
453                 if (result.length() > 0)
454                 {
455                     result.append(", ");
456                 }
457                 result.append(sourceElement.getAttribute(attributeName));
458             }
459         }
460         return result.toString();
461     }
462 }