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