%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.torque.util.SQLBuilder |
|
|
1 | package org.apache.torque.util; |
|
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.HashSet; |
|
23 | import java.util.Iterator; |
|
24 | import java.util.List; |
|
25 | import java.util.Map; |
|
26 | import java.util.Set; |
|
27 | ||
28 | import org.apache.commons.lang.StringUtils; |
|
29 | import org.apache.commons.logging.Log; |
|
30 | import org.apache.commons.logging.LogFactory; |
|
31 | import org.apache.torque.Torque; |
|
32 | import org.apache.torque.TorqueException; |
|
33 | import org.apache.torque.adapter.DB; |
|
34 | import org.apache.torque.map.ColumnMap; |
|
35 | import org.apache.torque.map.DatabaseMap; |
|
36 | import org.apache.torque.util.Criteria.Criterion; |
|
37 | ||
38 | /** |
|
39 | * Factored out code that is used to process SQL tables. This code comes |
|
40 | * from BasePeer and is put here to reduce complexity in the BasePeer class. |
|
41 | * You should not use the methods here directly! |
|
42 | * |
|
43 | * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> |
|
44 | * @author <a href="mailto:fischer@seitenbau.de">Thomas Fischer</a> |
|
45 | * @version $Id: SQLBuilder.java 535596 2007-05-06 10:47:39Z tfischer $ |
|
46 | */ |
|
47 | public final class SQLBuilder |
|
48 | { |
|
49 | /** Logging */ |
|
50 | 24 | protected static final Log log = LogFactory.getLog(SQLBuilder.class); |
51 | ||
52 | /** Function Characters */ |
|
53 | 12 | public static final String[] COLUMN_CHARS = {".", "*"}; |
54 | 12 | public static final String[] DELIMITERS = {" ", ",", "(", ")", "<", ">"}; |
55 | ||
56 | /** |
|
57 | * Private constructor to prevent instantiation. |
|
58 | * |
|
59 | * Class contains only static method ans should therefore not be |
|
60 | * instantiated. |
|
61 | */ |
|
62 | private SQLBuilder() |
|
63 | 0 | { |
64 | 0 | } |
65 | ||
66 | /** |
|
67 | * Fully qualify a table name with an optional schema reference |
|
68 | * |
|
69 | * @param table The table name to use. If null is passed in, null is returned. |
|
70 | * @param dbName The name of the database to which this tables belongs. |
|
71 | * If null is passed, the default database is used. |
|
72 | * |
|
73 | * @return The table name to use inside the SQL statement. If null is passed |
|
74 | * into this method, null is returned. |
|
75 | * @exception TorqueException if an error occurs |
|
76 | */ |
|
77 | public static String getFullTableName( |
|
78 | final String table, |
|
79 | final String dbName) |
|
80 | throws TorqueException |
|
81 | { |
|
82 | 204 | if (table != null) |
83 | { |
|
84 | 204 | int dotIndex = table.indexOf("."); |
85 | ||
86 | 204 | if (dotIndex == -1) // No schema given |
87 | { |
|
88 | 198 | String targetDBName = (dbName == null) |
89 | ? Torque.getDefaultDB() |
|
90 | : dbName; |
|
91 | ||
92 | 198 | String targetSchema = Torque.getSchema(targetDBName); |
93 | ||
94 | // If we have a default schema, fully qualify the |
|
95 | // table and return. |
|
96 | 198 | if (StringUtils.isNotEmpty(targetSchema)) |
97 | { |
|
98 | 0 | return new StringBuffer() |
99 | .append(targetSchema) |
|
100 | .append(".") |
|
101 | .append(table) |
|
102 | .toString(); |
|
103 | } |
|
104 | } |
|
105 | } |
|
106 | ||
107 | 204 | return table; |
108 | } |
|
109 | ||
110 | /** |
|
111 | * Remove a possible schema name from the table name. |
|
112 | * |
|
113 | * @param table The table name to use |
|
114 | * |
|
115 | * @return The table name with a possible schema name |
|
116 | * stripped off |
|
117 | */ |
|
118 | public static String getUnqualifiedTableName(final String table) |
|
119 | { |
|
120 | 162 | if (table != null) |
121 | { |
|
122 | 156 | int dotIndex = table.lastIndexOf("."); // Do we have a dot? |
123 | ||
124 | 156 | if (++dotIndex > 0) // Incrementation allows for better test _and_ substring... |
125 | { |
|
126 | 0 | return table.substring(dotIndex); |
127 | } |
|
128 | } |
|
129 | ||
130 | 162 | return table; |
131 | } |
|
132 | ||
133 | /** |
|
134 | * Removes a possible function name or clause from a column name |
|
135 | * |
|
136 | * @param name The column name, possibly containing a clause |
|
137 | * |
|
138 | * @return The column name |
|
139 | * |
|
140 | * @throws TorqueException If the column name was malformed |
|
141 | */ |
|
142 | private static String removeSQLFunction(final String name) |
|
143 | throws TorqueException |
|
144 | { |
|
145 | // Empty name => return it |
|
146 | 108 | if (StringUtils.isEmpty(name)) |
147 | { |
|
148 | 12 | return name; |
149 | } |
|
150 | ||
151 | // Find Table.Column |
|
152 | 96 | int dotIndex = name.indexOf('.'); |
153 | 96 | if (dotIndex == -1) |
154 | { |
|
155 | 24 | dotIndex = name.indexOf("*"); |
156 | } |
|
157 | 96 | if (dotIndex == -1) |
158 | { |
|
159 | 6 | throw new TorqueException("removeSQLFunction() : Column name " |
160 | + name |
|
161 | + " does not contain a . or a *"); |
|
162 | } |
|
163 | 90 | String pre = name.substring(0, dotIndex); |
164 | 90 | String post = name.substring(dotIndex + 1, name.length()); |
165 | 90 | int startIndex = StringUtils.lastIndexOfAny(pre, DELIMITERS); |
166 | 90 | int endIndex = StringUtils.indexOfAny(post, DELIMITERS); |
167 | 90 | if (startIndex < 0 && endIndex < 0) |
168 | { |
|
169 | 24 | return name; |
170 | } |
|
171 | else |
|
172 | { |
|
173 | 66 | if (endIndex < 0) |
174 | { |
|
175 | 18 | endIndex = post.length(); |
176 | } |
|
177 | // if startIndex == -1 the formula is correct |
|
178 | 66 | return name.substring(startIndex + 1, dotIndex + 1 + endIndex); |
179 | } |
|
180 | } |
|
181 | ||
182 | /** |
|
183 | * Returns a table name from an identifier. Each identifier is to be qualified |
|
184 | * as [schema.]table.column. This could also contain FUNCTION([schema.]table.column). |
|
185 | * |
|
186 | * @param name The (possible fully qualified) identifier name |
|
187 | * |
|
188 | * @return the fully qualified table name |
|
189 | * |
|
190 | * @throws TorqueException If the identifier name was malformed |
|
191 | */ |
|
192 | public static String getTableName(final String name, class="keyword">final String dbName) |
|
193 | throws TorqueException |
|
194 | { |
|
195 | 102 | final String testName = removeSQLFunction(name); |
196 | ||
197 | 96 | if (StringUtils.isEmpty(testName)) |
198 | { |
|
199 | 12 | throwMalformedColumnNameException( |
200 | "getTableName", |
|
201 | name); |
|
202 | } |
|
203 | ||
204 | // Everything before the last dot is the table name |
|
205 | 84 | int rightDotIndex = testName.lastIndexOf('.'); |
206 | ||
207 | 84 | if (rightDotIndex < 0) |
208 | { |
|
209 | 18 | if ("*".equals(testName)) |
210 | { |
|
211 | 18 | return null; |
212 | } |
|
213 | ||
214 | 0 | throwMalformedColumnNameException( |
215 | "getTableName", |
|
216 | name); |
|
217 | } |
|
218 | ||
219 | 66 | return getFullTableName(testName.substring(0, rightDotIndex), dbName); |
220 | } |
|
221 | ||
222 | ||
223 | ||
224 | /** |
|
225 | * Returns a set of all tables and possible aliases referenced |
|
226 | * from a criterion. The resulting Set can be directly used to |
|
227 | * build a WHERE clause |
|
228 | * |
|
229 | * @param crit A Criteria object |
|
230 | * @param tableCallback A Callback Object |
|
231 | * @return A Set of tables. |
|
232 | */ |
|
233 | public static Set getTableSet( |
|
234 | final Criteria crit, |
|
235 | final TableCallback tableCallback) |
|
236 | { |
|
237 | 0 | HashSet tables = new HashSet(); |
238 | ||
239 | // Loop over all the Criterions |
|
240 | 0 | for (Iterator it = crit.keySet().iterator(); it.hasNext();) |
241 | { |
|
242 | 0 | String key = (String) it.next(); |
243 | 0 | Criteria.Criterion c = crit.getCriterion(key); |
244 | 0 | List tableNames = c.getAllTables(); |
245 | ||
246 | // Loop over all Tables referenced in this criterion. |
|
247 | 0 | for (Iterator it2 = tableNames.iterator(); it2.hasNext();) |
248 | { |
|
249 | 0 | String name = (String) it2.next(); |
250 | 0 | String aliasName = crit.getTableForAlias(name); |
251 | ||
252 | // If the tables have an alias, add an "<xxx> AS <yyy> statement" |
|
253 | 0 | if (StringUtils.isNotEmpty(aliasName)) |
254 | { |
|
255 | 0 | String newName = |
256 | new StringBuffer(name.length() + aliasName.length() + 4) |
|
257 | .append(aliasName) |
|
258 | .append(" AS ") |
|
259 | .append(name) |
|
260 | .toString(); |
|
261 | 0 | name = newName; |
262 | } |
|
263 | 0 | tables.add(name); |
264 | } |
|
265 | ||
266 | 0 | if (tableCallback != null) |
267 | { |
|
268 | 0 | tableCallback.process(tables, key, crit); |
269 | } |
|
270 | } |
|
271 | ||
272 | 0 | return tables; |
273 | } |
|
274 | ||
275 | /** |
|
276 | * Builds a Query clause for Updating and deleting |
|
277 | * |
|
278 | * @param crit a <code>Criteria</code> value |
|
279 | * @param params a <code>List</code> value |
|
280 | * @param qc a <code>QueryCallback</code> value |
|
281 | * @return a <code>Query</code> value |
|
282 | * @exception TorqueException if an error occurs |
|
283 | */ |
|
284 | public static Query buildQueryClause(final Criteria crit, |
|
285 | final List params, |
|
286 | final QueryCallback qc) |
|
287 | throws TorqueException |
|
288 | { |
|
289 | 102 | Query query = new Query(); |
290 | ||
291 | 102 | final String dbName = crit.getDbName(); |
292 | 102 | final DB db = Torque.getDB(dbName); |
293 | 102 | final DatabaseMap dbMap = Torque.getDatabaseMap(dbName); |
294 | ||
295 | 102 | JoinBuilder.processJoins(db, dbMap, crit, query); |
296 | 102 | processModifiers(crit, query); |
297 | 102 | processSelectColumns(crit, query, dbName); |
298 | 102 | processAsColumns(crit, query); |
299 | 102 | processCriterions(db, dbMap, dbName, crit, query, params, qc); |
300 | 102 | processGroupBy(crit, query); |
301 | 102 | processHaving(crit, query); |
302 | 102 | processOrderBy(db, dbMap, crit, query); |
303 | 102 | processLimits(crit, query); |
304 | ||
305 | 102 | if (log.isDebugEnabled()) |
306 | { |
|
307 | 102 | log.debug(query.toString()); |
308 | } |
|
309 | 102 | return query; |
310 | } |
|
311 | ||
312 | ||
313 | /** |
|
314 | * adds the select columns from the criteria to the query |
|
315 | * @param criteria the criteria from which the select columns are taken |
|
316 | * @param query the query to which the select columns should be added |
|
317 | * @throws TorqueException if the select columns can not be processed |
|
318 | */ |
|
319 | private static void processSelectColumns( |
|
320 | final Criteria criteria, |
|
321 | final Query query, |
|
322 | final String dbName) |
|
323 | throws TorqueException |
|
324 | { |
|
325 | 102 | UniqueList selectClause = query.getSelectClause(); |
326 | 102 | UniqueList select = criteria.getSelectColumns(); |
327 | ||
328 | 114 | for (int i = 0; i < select.size(); i++) |
329 | { |
|
330 | 12 | String identifier = (String) select.get(i); |
331 | 12 | selectClause.add(identifier); |
332 | 12 | addTableToFromClause(getTableName(identifier, dbName), criteria, query); |
333 | } |
|
334 | 102 | } |
335 | ||
336 | /** |
|
337 | * adds the As-columns from the criteria to the query. |
|
338 | * @param criteria the criteria from which the As-columns are taken |
|
339 | * @param query the query to which the As-columns should be added |
|
340 | */ |
|
341 | private static void processAsColumns( |
|
342 | final Criteria criteria, |
|
343 | final Query query) |
|
344 | { |
|
345 | 102 | UniqueList querySelectClause = query.getSelectClause(); |
346 | 102 | Map criteriaAsColumns = criteria.getAsColumns(); |
347 | ||
348 | 102 | for (Iterator it = criteriaAsColumns.entrySet().iterator(); it.hasNext();) |
349 | { |
|
350 | 0 | Map.Entry entry = (Map.Entry) it.next(); |
351 | 0 | String key = (String) entry.getKey(); |
352 | 0 | querySelectClause.add( |
353 | new StringBuffer() |
|
354 | .append(entry.getValue()) |
|
355 | .append(SqlEnum.AS) |
|
356 | .append(key) |
|
357 | .toString()); |
|
358 | } |
|
359 | 102 | } |
360 | ||
361 | /** |
|
362 | * adds the Modifiers from the criteria to the query |
|
363 | * @param criteria the criteria from which the Modifiers are taken |
|
364 | * @param query the query to which the Modifiers should be added |
|
365 | */ |
|
366 | private static void processModifiers( |
|
367 | final Criteria criteria, |
|
368 | final Query query) |
|
369 | { |
|
370 | 102 | UniqueList selectModifiers = query.getSelectModifiers(); |
371 | 102 | UniqueList modifiers = criteria.getSelectModifiers(); |
372 | 102 | for (int i = 0; i < modifiers.size(); i++) |
373 | { |
|
374 | 0 | selectModifiers.add(modifiers.get(i)); |
375 | } |
|
376 | 102 | } |
377 | ||
378 | /** |
|
379 | * adds the Criterion-objects from the criteria to the query |
|
380 | * @param criteria the criteria from which the Criterion-objects are taken |
|
381 | * @param query the query to which the Criterion-objects should be added |
|
382 | * @param params the parameters if a prepared statement should be built, |
|
383 | * or null if a normal statement should be built. |
|
384 | * @throws TorqueException if the Criterion-objects can not be processed |
|
385 | */ |
|
386 | private static void processCriterions( |
|
387 | final DB db, |
|
388 | final DatabaseMap dbMap, |
|
389 | final String dbName, |
|
390 | final Criteria crit, |
|
391 | final Query query, |
|
392 | final List params, |
|
393 | final QueryCallback qc) |
|
394 | throws TorqueException |
|
395 | { |
|
396 | 102 | UniqueList whereClause = query.getWhereClause(); |
397 | ||
398 | 102 | for (Iterator it = crit.keySet().iterator(); it.hasNext();) |
399 | { |
|
400 | 108 | String key = (String) it.next(); |
401 | 108 | Criteria.Criterion criterion = crit.getCriterion(key); |
402 | 108 | Criteria.Criterion[] someCriteria = |
403 | criterion.getAttachedCriterion(); |
|
404 | ||
405 | 108 | String table = null; |
406 | 246 | for (int i = 0; i < someCriteria.length; i++) |
407 | { |
|
408 | 138 | String tableName = someCriteria[i].getTable(); |
409 | ||
410 | // add the table to the from clause, if it is not already |
|
411 | // contained there |
|
412 | // it is important that this piece of code is executed AFTER |
|
413 | // the joins are processed |
|
414 | 138 | addTableToFromClause(getFullTableName(tableName, dbName), crit, query); |
415 | ||
416 | 138 | table = crit.getTableForAlias(tableName); |
417 | 138 | if (table == null) |
418 | { |
|
419 | 138 | table = tableName; |
420 | } |
|
421 | ||
422 | 138 | boolean ignoreCase = ((crit.isIgnoreCase() || someCriteria[i].isIgnoreCase()) |
423 | && (dbMap.getTable(table) |
|
424 | .getColumn(someCriteria[i].getColumn()) |
|
425 | .getType() |
|
426 | instanceof String)); |
|
427 | ||
428 | 138 | someCriteria[i].setIgnoreCase(ignoreCase); |
429 | } |
|
430 | ||
431 | 108 | criterion.setDB(db); |
432 | 108 | whereClause.add(qc.process(criterion, params)); |
433 | 54 | } |
434 | 102 | } |
435 | ||
436 | /** |
|
437 | * adds the OrderBy-Columns from the criteria to the query |
|
438 | * @param criteria the criteria from which the OrderBy-Columns are taken |
|
439 | * @param query the query to which the OrderBy-Columns should be added |
|
440 | * @throws TorqueException if the OrderBy-Columns can not be processed |
|
441 | */ |
|
442 | private static void processOrderBy( |
|
443 | final DB db, |
|
444 | final DatabaseMap dbMap, |
|
445 | final Criteria crit, |
|
446 | final Query query) |
|
447 | throws TorqueException |
|
448 | { |
|
449 | 102 | UniqueList orderByClause = query.getOrderByClause(); |
450 | 102 | UniqueList selectClause = query.getSelectClause(); |
451 | ||
452 | 102 | UniqueList orderBy = crit.getOrderByColumns(); |
453 | ||
454 | 102 | if (orderBy != null && orderBy.size() > 0) |
455 | { |
|
456 | // Check for each String/Character column and apply |
|
457 | // toUpperCase(). |
|
458 | 12 | for (int i = 0; i < orderBy.size(); i++) |
459 | { |
|
460 | 6 | String orderByColumn = (String) orderBy.get(i); |
461 | ||
462 | 6 | String strippedColumnName |
463 | = removeSQLFunction(orderByColumn); |
|
464 | 6 | int dotPos = strippedColumnName.lastIndexOf('.'); |
465 | 6 | if (dotPos == -1) |
466 | { |
|
467 | // We are not able to look up the table in the |
|
468 | // tableMap, as no table name is given. Simply add |
|
469 | // the orderBy and hope the user knows what he is |
|
470 | // doing. |
|
471 | 0 | orderByClause.add(orderByColumn); |
472 | 0 | continue; |
473 | } |
|
474 | ||
475 | 6 | String tableName = strippedColumnName.substring(0, dotPos); |
476 | 6 | String table = crit.getTableForAlias(tableName); |
477 | 6 | if (table == null) |
478 | { |
|
479 | 0 | table = tableName; |
480 | } |
|
481 | ||
482 | // See if there's a space (between the column list and sort |
|
483 | // order in ORDER BY table.column DESC). |
|
484 | 6 | int spacePos = strippedColumnName.indexOf(' '); |
485 | String columnName; |
|
486 | 6 | if (spacePos == -1) |
487 | { |
|
488 | 6 | columnName = |
489 | strippedColumnName.substring(dotPos + 1); |
|
490 | 3 | } |
491 | else |
|
492 | { |
|
493 | 0 | columnName = strippedColumnName.substring( |
494 | dotPos + 1, |
|
495 | spacePos); |
|
496 | } |
|
497 | 6 | ColumnMap column = dbMap.getTable(table).getColumn(columnName); |
498 | ||
499 | // only ignore case in order by for string columns |
|
500 | // which do not have a function around them |
|
501 | 6 | if (column.getType() instanceof String |
502 | && orderByColumn.indexOf('(') == -1) |
|
503 | { |
|
504 | // find space pos relative to orderByColumn |
|
505 | 6 | spacePos = orderByColumn.indexOf(' '); |
506 | 6 | if (spacePos == -1) |
507 | { |
|
508 | 0 | orderByClause.add( |
509 | db.ignoreCaseInOrderBy(orderByColumn)); |
|
510 | } |
|
511 | else |
|
512 | { |
|
513 | 6 | orderByClause.add( |
514 | db.ignoreCaseInOrderBy( |
|
515 | orderByColumn.substring(0, spacePos)) |
|
516 | + orderByColumn.substring(spacePos)); |
|
517 | } |
|
518 | 6 | selectClause.add( |
519 | db.ignoreCaseInOrderBy(tableName + '.' + columnName)); |
|
520 | 3 | } |
521 | else |
|
522 | { |
|
523 | 0 | orderByClause.add(orderByColumn); |
524 | } |
|
525 | } |
|
526 | } |
|
527 | 102 | } |
528 | ||
529 | /** |
|
530 | * adds the GroupBy-Columns from the criteria to the query |
|
531 | * @param criteria the criteria from which the GroupBy-Columns are taken |
|
532 | * @param query the query to which the GroupBy-Columns should be added |
|
533 | * @throws TorqueException if the GroupBy-Columns can not be processed |
|
534 | */ |
|
535 | private static void processGroupBy( |
|
536 | final Criteria crit, |
|
537 | final Query query) |
|
538 | throws TorqueException |
|
539 | { |
|
540 | 102 | UniqueList groupByClause = query.getGroupByClause(); |
541 | 102 | UniqueList groupBy = crit.getGroupByColumns(); |
542 | ||
543 | // need to allow for multiple group bys |
|
544 | 102 | if (groupBy != null) |
545 | { |
|
546 | 102 | for (int i = 0; i < groupBy.size(); i++) |
547 | { |
|
548 | 0 | String columnName = (String) groupBy.get(i); |
549 | 0 | String column = (String) crit.getAsColumns().get(columnName); |
550 | ||
551 | 0 | if (column == null) |
552 | { |
|
553 | 0 | column = columnName; |
554 | } |
|
555 | ||
556 | 0 | if (column.indexOf('.') != -1) |
557 | { |
|
558 | 0 | groupByClause.add(column); |
559 | } |
|
560 | else |
|
561 | { |
|
562 | 0 | throwMalformedColumnNameException("group by", |
563 | column); |
|
564 | } |
|
565 | } |
|
566 | } |
|
567 | 102 | } |
568 | ||
569 | /** |
|
570 | * adds the Having-Columns from the criteria to the query |
|
571 | * @param criteria the criteria from which the Having-Columns are taken |
|
572 | * @param query the query to which the Having-Columns should be added |
|
573 | * @throws TorqueException if the Having-Columns can not be processed |
|
574 | */ |
|
575 | private static void processHaving( |
|
576 | final Criteria crit, |
|
577 | final Query query) |
|
578 | throws TorqueException |
|
579 | { |
|
580 | 102 | Criteria.Criterion having = crit.getHaving(); |
581 | 102 | if (having != null) |
582 | { |
|
583 | //String groupByString = null; |
|
584 | 0 | query.setHaving(having.toString()); |
585 | } |
|
586 | 102 | } |
587 | ||
588 | /** |
|
589 | * adds a Limit clause to the query if supported by the database |
|
590 | * @param criteria the criteria from which the Limit and Offset values |
|
591 | * are taken |
|
592 | * @param query the query to which the Limit clause should be added |
|
593 | * @throws TorqueException if the Database adapter cannot be obtained |
|
594 | */ |
|
595 | private static void processLimits( |
|
596 | final Criteria crit, |
|
597 | final Query query) |
|
598 | throws TorqueException |
|
599 | { |
|
600 | 102 | int limit = crit.getLimit(); |
601 | 102 | int offset = crit.getOffset(); |
602 | ||
603 | 102 | if (offset > 0 || limit >= 0) |
604 | { |
|
605 | 36 | DB db = Torque.getDB(crit.getDbName()); |
606 | 36 | db.generateLimits(query, offset, limit); |
607 | } |
|
608 | 102 | } |
609 | ||
610 | /** |
|
611 | * Throws a TorqueException with the malformed column name error |
|
612 | * message. The error message looks like this:<p> |
|
613 | * |
|
614 | * <code> |
|
615 | * Malformed column name in Criteria [criteriaPhrase]: |
|
616 | * '[columnName]' is not of the form 'table.column' |
|
617 | * </code> |
|
618 | * |
|
619 | * @param criteriaPhrase a String, one of "select", "join", or "order by" |
|
620 | * @param columnName a String containing the offending column name |
|
621 | * @throws TorqueException Any exceptions caught during processing will be |
|
622 | * rethrown wrapped into a TorqueException. |
|
623 | */ |
|
624 | public static void throwMalformedColumnNameException( |
|
625 | final String criteriaPhrase, |
|
626 | final String columnName) |
|
627 | throws TorqueException |
|
628 | { |
|
629 | 12 | StringBuffer sb = new StringBuffer() |
630 | .append("Malformed column name in Criteria ") |
|
631 | .append(criteriaPhrase) |
|
632 | .append(": '") |
|
633 | .append(StringUtils.isEmpty(columnName) ? "<empty>" : columnName) |
|
634 | .append("' is not of the form 'table.column'"); |
|
635 | ||
636 | 12 | throw new TorqueException(sb.toString()); |
637 | } |
|
638 | ||
639 | /** |
|
640 | * Returns the tablename which can be added to a From Clause. |
|
641 | * This takes care of any aliases that might be defined. |
|
642 | * For example, if an alias "a" for the table AUTHOR is defined |
|
643 | * in the Criteria criteria, getTableNameForFromClause("a", criteria) |
|
644 | * returns "AUTHOR a". |
|
645 | * @param tableName the name of a table |
|
646 | * or the alias for a table |
|
647 | * @param criteria a criteria object to resolve a possible alias |
|
648 | * @return either the tablename itself if tableOrAliasName is not an alias, |
|
649 | * or a String of the form "tableName tableOrAliasName" |
|
650 | * if tableOrAliasName is an alias for a table name |
|
651 | */ |
|
652 | public static String getTableNameForFromClause( |
|
653 | final String tableName, |
|
654 | final Criteria criteria) |
|
655 | { |
|
656 | 162 | String shortTableName = getUnqualifiedTableName(tableName); |
657 | ||
658 | // Most of the time, the alias would be for the short name... |
|
659 | 162 | String aliasName = criteria.getTableForAlias(shortTableName); |
660 | 162 | if (StringUtils.isEmpty(aliasName)) |
661 | { |
|
662 | // But we should also check the FQN... |
|
663 | 156 | aliasName = criteria.getTableForAlias(tableName); |
664 | } |
|
665 | ||
666 | 162 | if (StringUtils.isNotEmpty(aliasName)) |
667 | { |
|
668 | // If the tables have an alias, add an "<xxx> <yyy> statement" |
|
669 | // <xxx> AS <yyy> causes problems on oracle |
|
670 | 6 | return new StringBuffer( |
671 | tableName.length() + aliasName.length() + 1) |
|
672 | .append(aliasName) |
|
673 | .append(" ") |
|
674 | .append(tableName) |
|
675 | .toString(); |
|
676 | } |
|
677 | ||
678 | 156 | return tableName; |
679 | } |
|
680 | ||
681 | /** |
|
682 | * Checks if the Tablename tableName is already contained in a from clause. |
|
683 | * If tableName and the tablenames in fromClause are generated by |
|
684 | * getTablenameForFromClause(String, Criteria), (which they usually are), |
|
685 | * then different aliases for the same table are treated |
|
686 | * as different tables: E.g. |
|
687 | * fromClauseContainsTableName(fromClause, "table_a a") returns false if |
|
688 | * fromClause contains only another alias for table_a , |
|
689 | * e.g. "table_a aa" and the unaliased tablename "table_a". |
|
690 | * Special case: If tableName is null, true is returned. |
|
691 | * @param fromClause a list containing only elements of type. |
|
692 | * Query.FromElement |
|
693 | * @param tableName the tablename to check |
|
694 | * @return if the Tablename tableName is already contained in a from clause. |
|
695 | * If tableName is null, true is returned. |
|
696 | */ |
|
697 | public static boolean fromClauseContainsTableName( |
|
698 | final UniqueList fromClause, |
|
699 | final String tableName) |
|
700 | { |
|
701 | 162 | if (tableName == null) |
702 | { |
|
703 | // usually this function is called to see if tableName should be |
|
704 | // added to the fromClause. As null should not be added, |
|
705 | // true is returned. |
|
706 | 6 | return true; |
707 | } |
|
708 | 156 | for (Iterator it = fromClause.iterator(); it.hasNext();) |
709 | { |
|
710 | 54 | Query.FromElement fromElement |
711 | = (Query.FromElement) it.next(); |
|
712 | 54 | if (tableName.equals(fromElement.getTableName())) |
713 | { |
|
714 | 48 | return true; |
715 | } |
|
716 | 3 | } |
717 | 108 | return false; |
718 | } |
|
719 | ||
720 | /** |
|
721 | * adds a table to the from clause of a query, if it is not already |
|
722 | * contained there. |
|
723 | * @param tableOrAliasName the name of a table |
|
724 | * or the alias for a table |
|
725 | * @param criteria a criteria object to resolve a possible alias |
|
726 | * @param query the query where the the tablename should be added |
|
727 | * to the from clause |
|
728 | * @return the table in the from clause which represents the |
|
729 | * supplied tableOrAliasName |
|
730 | */ |
|
731 | private static String addTableToFromClause( |
|
732 | final String tableName, |
|
733 | final Criteria criteria, |
|
734 | Query query) |
|
735 | { |
|
736 | 150 | String tableNameForFromClause |
737 | = getTableNameForFromClause(tableName, criteria); |
|
738 | ||
739 | 150 | UniqueList queryFromClause = query.getFromClause(); |
740 | ||
741 | // it is important that this piece of code is executed AFTER |
|
742 | // the joins are processed |
|
743 | 150 | if (!fromClauseContainsTableName( |
744 | queryFromClause, |
|
745 | tableNameForFromClause)) |
|
746 | { |
|
747 | 96 | Query.FromElement fromElement |
748 | = new Query.FromElement( |
|
749 | tableNameForFromClause, null, class="keyword">null); |
|
750 | 96 | queryFromClause.add(fromElement); |
751 | } |
|
752 | 150 | return tableNameForFromClause; |
753 | } |
|
754 | ||
755 | /** |
|
756 | * Inner Interface that defines the Callback method for |
|
757 | * the Table creation loop. |
|
758 | */ |
|
759 | public interface TableCallback |
|
760 | { |
|
761 | /** |
|
762 | * Callback Method for getTableSet() |
|
763 | * |
|
764 | * @param tables The current table name |
|
765 | * @param key The current criterion key. |
|
766 | * @param crit The Criteria used in getTableSet() |
|
767 | */ |
|
768 | void process(Set tables, String key, Criteria crit); |
|
769 | } |
|
770 | ||
771 | /** |
|
772 | * Inner Interface that defines the Callback method for |
|
773 | * the buildQuery Criterion evaluation |
|
774 | */ |
|
775 | public interface QueryCallback |
|
776 | { |
|
777 | /** |
|
778 | * The callback for building a query String |
|
779 | * |
|
780 | * @param criterion The current criterion |
|
781 | * @param params The parameter list passed to buildQueryString() |
|
782 | * @return WHERE SQL fragment for this criterion |
|
783 | */ |
|
784 | String process(Criterion criterion, List params); |
|
785 | } |
|
786 | ||
787 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |