%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 476550 2006-11-18 16:08:37Z tfischer $ |
|
46 | */ |
|
47 | 2 | public final class SQLBuilder |
48 | { |
|
49 | /** Logging */ |
|
50 | 90 | protected static final Log log = LogFactory.getLog(SQLBuilder.class); |
51 | ||
52 | /** Function Characters */ |
|
53 | 46 | public static final String[] COLUMN_CHARS = {".", "*"}; |
54 | 46 | 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 | 736 | if (table != null) |
83 | { |
|
84 | 736 | int dotIndex = table.indexOf("."); |
85 | ||
86 | 736 | if (dotIndex == -1) // No schema given |
87 | { |
|
88 | 744 | String targetDBName = (dbName == null) |
89 | 9 | ? Torque.getDefaultDB() |
90 | 22 | : dbName; |
91 | ||
92 | 713 | String targetSchema = Torque.getSchema(targetDBName); |
93 | ||
94 | // If we have a default schema, fully qualify the |
|
95 | // table and return. |
|
96 | 713 | 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 | 736 | 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 | 575 | if (table != null) |
121 | { |
|
122 | 552 | int dotIndex = table.lastIndexOf("."); // Do we have a dot? |
123 | ||
124 | 552 | if (++dotIndex > 0) // Incrementation allows for better test _and_ substring... |
125 | { |
|
126 | 0 | return table.substring(dotIndex); |
127 | } |
|
128 | } |
|
129 | ||
130 | 575 | 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 | 414 | if (StringUtils.isEmpty(name)) |
147 | { |
|
148 | 46 | return name; |
149 | } |
|
150 | ||
151 | // Find Table.Column |
|
152 | 368 | int dotIndex = name.indexOf('.'); |
153 | 368 | if (dotIndex == -1) |
154 | { |
|
155 | 92 | dotIndex = name.indexOf("*"); |
156 | } |
|
157 | 368 | if (dotIndex == -1) |
158 | { |
|
159 | 24 | throw new TorqueException("removeSQLFunction() : Column name " |
160 | 1 | + name |
161 | 1 | + " does not contain a . or a *"); |
162 | } |
|
163 | 345 | String pre = name.substring(0, dotIndex); |
164 | 345 | String post = name.substring(dotIndex + 1, name.length()); |
165 | 345 | int startIndex = StringUtils.lastIndexOfAny(pre, DELIMITERS); |
166 | 345 | int endIndex = StringUtils.indexOfAny(post, DELIMITERS); |
167 | 345 | if (startIndex < 0 && endIndex < 0) |
168 | { |
|
169 | 92 | return name; |
170 | } |
|
171 | else |
|
172 | { |
|
173 | 253 | if (endIndex < 0) |
174 | { |
|
175 | 69 | endIndex = post.length(); |
176 | } |
|
177 | // if startIndex == -1 the formula is correct |
|
178 | 253 | 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 | 391 | final String testName = removeSQLFunction(name); |
196 | ||
197 | 368 | if (StringUtils.isEmpty(testName)) |
198 | { |
|
199 | 46 | throwMalformedColumnNameException( |
200 | 2 | "getTableName", |
201 | 2 | name); |
202 | } |
|
203 | ||
204 | // Everything before the last dot is the table name |
|
205 | 322 | int rightDotIndex = testName.lastIndexOf('.'); |
206 | ||
207 | 322 | if (rightDotIndex < 0) |
208 | { |
|
209 | 69 | if ("*".equals(testName)) |
210 | { |
|
211 | 69 | return null; |
212 | } |
|
213 | ||
214 | 0 | throwMalformedColumnNameException( |
215 | "getTableName", |
|
216 | name); |
|
217 | } |
|
218 | ||
219 | 253 | 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 | 0 | } |
265 | ||
266 | 0 | if (tableCallback != null) |
267 | { |
|
268 | 0 | tableCallback.process(tables, key, crit); |
269 | } |
|
270 | 0 | } |
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 | 345 | Query query = new Query(); |
290 | ||
291 | 345 | final String dbName = crit.getDbName(); |
292 | 345 | final DB db = Torque.getDB(dbName); |
293 | 345 | final DatabaseMap dbMap = Torque.getDatabaseMap(dbName); |
294 | ||
295 | 345 | JoinBuilder.processJoins(db, dbMap, crit, query); |
296 | 345 | processModifiers(crit, query); |
297 | 345 | processSelectColumns(crit, query, dbName); |
298 | 345 | processAsColumns(crit, query); |
299 | 345 | processCriterions(db, dbMap, dbName, crit, query, params, qc); |
300 | 345 | processGroupBy(crit, query); |
301 | 345 | processHaving(crit, query); |
302 | 345 | processOrderBy(db, dbMap, crit, query); |
303 | 345 | processLimits(crit, query); |
304 | ||
305 | 345 | if (log.isDebugEnabled()) |
306 | { |
|
307 | 345 | log.debug(query.toString()); |
308 | } |
|
309 | 345 | 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 | 345 | UniqueList selectClause = query.getSelectClause(); |
326 | 345 | UniqueList select = criteria.getSelectColumns(); |
327 | ||
328 | 391 | for (int i = 0; i < select.size(); i++) |
329 | { |
|
330 | 46 | String identifier = (String) select.get(i); |
331 | 46 | selectClause.add(identifier); |
332 | 46 | addTableToFromClause(getTableName(identifier, dbName), criteria, query); |
333 | } |
|
334 | 345 | } |
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 | 345 | UniqueList querySelectClause = query.getSelectClause(); |
346 | 345 | Map criteriaAsColumns = criteria.getAsColumns(); |
347 | ||
348 | 360 | 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 | 0 | } |
359 | 345 | } |
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 | 345 | UniqueList selectModifiers = query.getSelectModifiers(); |
371 | 345 | UniqueList modifiers = criteria.getSelectModifiers(); |
372 | 345 | for (int i = 0; i < modifiers.size(); i++) |
373 | { |
|
374 | 0 | selectModifiers.add(modifiers.get(i)); |
375 | } |
|
376 | 345 | } |
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 | 345 | UniqueList whereClause = query.getWhereClause(); |
397 | ||
398 | 376 | for (Iterator it = crit.keySet().iterator(); it.hasNext();) |
399 | { |
|
400 | 368 | String key = (String) it.next(); |
401 | 368 | Criteria.Criterion criterion = crit.getCriterion(key); |
402 | 368 | Criteria.Criterion[] someCriteria = |
403 | 16 | criterion.getAttachedCriterion(); |
404 | ||
405 | 368 | String table = null; |
406 | 851 | for (int i = 0; i < someCriteria.length; i++) |
407 | { |
|
408 | 483 | 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 | 483 | addTableToFromClause(getFullTableName(tableName, dbName), crit, query); |
415 | ||
416 | 483 | table = crit.getTableForAlias(tableName); |
417 | 483 | if (table == null) |
418 | { |
|
419 | 483 | table = tableName; |
420 | } |
|
421 | ||
422 | 504 | boolean ignoreCase = ((crit.isIgnoreCase() || someCriteria[i].isIgnoreCase()) |
423 | && (dbMap.getTable(table) |
|
424 | .getColumn(someCriteria[i].getColumn()) |
|
425 | .getType() |
|
426 | instanceof String)); |
|
427 | ||
428 | 483 | someCriteria[i].setIgnoreCase(ignoreCase); |
429 | } |
|
430 | ||
431 | 368 | criterion.setDB(db); |
432 | 368 | whereClause.add(qc.process(criterion, params)); |
433 | 352 | } |
434 | 345 | } |
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 | 345 | UniqueList orderByClause = query.getOrderByClause(); |
450 | 345 | UniqueList selectClause = query.getSelectClause(); |
451 | ||
452 | 345 | UniqueList orderBy = crit.getOrderByColumns(); |
453 | ||
454 | 345 | if (orderBy != null && orderBy.size() > 0) |
455 | { |
|
456 | // Check for each String/Character column and apply |
|
457 | // toUpperCase(). |
|
458 | 46 | for (int i = 0; i < orderBy.size(); i++) |
459 | { |
|
460 | 23 | String orderByColumn = (String) orderBy.get(i); |
461 | ||
462 | 23 | String strippedColumnName |
463 | 1 | = removeSQLFunction(orderByColumn); |
464 | 23 | int dotPos = strippedColumnName.lastIndexOf('.'); |
465 | 23 | if (dotPos == -1) |
466 | { |
|
467 | 0 | throwMalformedColumnNameException( |
468 | "order by", |
|
469 | orderByColumn); |
|
470 | } |
|
471 | ||
472 | 23 | String tableName = strippedColumnName.substring(0, dotPos); |
473 | 23 | String table = crit.getTableForAlias(tableName); |
474 | 23 | if (table == null) |
475 | { |
|
476 | 0 | table = tableName; |
477 | } |
|
478 | ||
479 | // See if there's a space (between the column list and sort |
|
480 | // order in ORDER BY table.column DESC). |
|
481 | 23 | int spacePos = strippedColumnName.indexOf(' '); |
482 | String columnName; |
|
483 | 23 | if (spacePos == -1) |
484 | { |
|
485 | 23 | columnName = |
486 | 1 | strippedColumnName.substring(dotPos + 1); |
487 | 22 | } |
488 | else |
|
489 | { |
|
490 | 0 | columnName = strippedColumnName.substring( |
491 | dotPos + 1, |
|
492 | spacePos); |
|
493 | } |
|
494 | 23 | ColumnMap column = dbMap.getTable(table).getColumn(columnName); |
495 | ||
496 | // only ignore case in order by for string columns |
|
497 | // which do not have a function around them |
|
498 | 23 | if (column.getType() instanceof String |
499 | 1 | && orderByColumn.indexOf('(') == -1) |
500 | { |
|
501 | // find space pos relative to orderByColumn |
|
502 | 23 | spacePos = orderByColumn.indexOf(' '); |
503 | 23 | if (spacePos == -1) |
504 | { |
|
505 | 0 | orderByClause.add( |
506 | db.ignoreCaseInOrderBy(orderByColumn)); |
|
507 | 0 | } |
508 | else |
|
509 | { |
|
510 | 24 | orderByClause.add( |
511 | 3 | db.ignoreCaseInOrderBy( |
512 | 1 | orderByColumn.substring(0, spacePos)) |
513 | 1 | + orderByColumn.substring(spacePos)); |
514 | } |
|
515 | 24 | selectClause.add( |
516 | 1 | db.ignoreCaseInOrderBy(tableName + '.' + columnName)); |
517 | 22 | } |
518 | else |
|
519 | { |
|
520 | 0 | orderByClause.add(orderByColumn); |
521 | } |
|
522 | } |
|
523 | } |
|
524 | 345 | } |
525 | ||
526 | /** |
|
527 | * adds the GroupBy-Columns from the criteria to the query |
|
528 | * @param criteria the criteria from which the GroupBy-Columns are taken |
|
529 | * @param query the query to which the GroupBy-Columns should be added |
|
530 | * @throws TorqueException if the GroupBy-Columns can not be processed |
|
531 | */ |
|
532 | private static void processGroupBy( |
|
533 | final Criteria crit, |
|
534 | final Query query) |
|
535 | throws TorqueException |
|
536 | { |
|
537 | 345 | UniqueList groupByClause = query.getGroupByClause(); |
538 | 345 | UniqueList groupBy = crit.getGroupByColumns(); |
539 | ||
540 | // need to allow for multiple group bys |
|
541 | 345 | if (groupBy != null) |
542 | { |
|
543 | 345 | for (int i = 0; i < groupBy.size(); i++) |
544 | { |
|
545 | 0 | String columnName = (String) groupBy.get(i); |
546 | 0 | String column = (String) crit.getAsColumns().get(columnName); |
547 | ||
548 | 0 | if (column == null) |
549 | { |
|
550 | 0 | column = columnName; |
551 | } |
|
552 | ||
553 | 0 | if (column.indexOf('.') != -1) |
554 | { |
|
555 | 0 | groupByClause.add(column); |
556 | 0 | } |
557 | else |
|
558 | { |
|
559 | 0 | throwMalformedColumnNameException("group by", |
560 | column); |
|
561 | } |
|
562 | } |
|
563 | } |
|
564 | 345 | } |
565 | ||
566 | /** |
|
567 | * adds the Having-Columns from the criteria to the query |
|
568 | * @param criteria the criteria from which the Having-Columns are taken |
|
569 | * @param query the query to which the Having-Columns should be added |
|
570 | * @throws TorqueException if the Having-Columns can not be processed |
|
571 | */ |
|
572 | private static void processHaving( |
|
573 | final Criteria crit, |
|
574 | final Query query) |
|
575 | throws TorqueException |
|
576 | { |
|
577 | 345 | Criteria.Criterion having = crit.getHaving(); |
578 | 345 | if (having != null) |
579 | { |
|
580 | //String groupByString = null; |
|
581 | 0 | query.setHaving(having.toString()); |
582 | } |
|
583 | 345 | } |
584 | ||
585 | /** |
|
586 | * adds a Limit clause to the query if supported by the database |
|
587 | * @param criteria the criteria from which the Limit and Offset values |
|
588 | * are taken |
|
589 | * @param query the query to which the Limit clause should be added |
|
590 | * @throws TorqueException if the Database adapter cannot be obtained |
|
591 | */ |
|
592 | private static void processLimits( |
|
593 | final Criteria crit, |
|
594 | final Query query) |
|
595 | throws TorqueException |
|
596 | { |
|
597 | 345 | int limit = crit.getLimit(); |
598 | 345 | int offset = crit.getOffset(); |
599 | ||
600 | 345 | if (offset > 0 || limit >= 0) |
601 | { |
|
602 | 92 | DB db = Torque.getDB(crit.getDbName()); |
603 | 92 | db.generateLimits(query, offset, limit); |
604 | } |
|
605 | 345 | } |
606 | ||
607 | /** |
|
608 | * Throws a TorqueException with the malformed column name error |
|
609 | * message. The error message looks like this:<p> |
|
610 | * |
|
611 | * <code> |
|
612 | * Malformed column name in Criteria [criteriaPhrase]: |
|
613 | * '[columnName]' is not of the form 'table.column' |
|
614 | * </code> |
|
615 | * |
|
616 | * @param criteriaPhrase a String, one of "select", "join", or "order by" |
|
617 | * @param columnName a String containing the offending column name |
|
618 | * @throws TorqueException Any exceptions caught during processing will be |
|
619 | * rethrown wrapped into a TorqueException. |
|
620 | */ |
|
621 | public static void throwMalformedColumnNameException( |
|
622 | final String criteriaPhrase, |
|
623 | final String columnName) |
|
624 | throws TorqueException |
|
625 | { |
|
626 | 48 | StringBuffer sb = new StringBuffer() |
627 | 2 | .append("Malformed column name in Criteria ") |
628 | 2 | .append(criteriaPhrase) |
629 | 2 | .append(": '") |
630 | 2 | .append(StringUtils.isEmpty(columnName) ? "<empty>" : columnName) |
631 | 2 | .append("' is not of the form 'table.column'"); |
632 | ||
633 | 46 | throw new TorqueException(sb.toString()); |
634 | } |
|
635 | ||
636 | /** |
|
637 | * Returns the tablename which can be added to a From Clause. |
|
638 | * This takes care of any aliases that might be defined. |
|
639 | * For example, if an alias "a" for the table AUTHOR is defined |
|
640 | * in the Criteria criteria, getTableNameForFromClause("a", criteria) |
|
641 | * returns "AUTHOR a". |
|
642 | * @param tableName the name of a table |
|
643 | * or the alias for a table |
|
644 | * @param criteria a criteria object to resolve a possible alias |
|
645 | * @return either the tablename itself if tableOrAliasName is not an alias, |
|
646 | * or a String of the form "tableName tableOrAliasName" |
|
647 | * if tableOrAliasName is an alias for a table name |
|
648 | */ |
|
649 | public static String getTableNameForFromClause( |
|
650 | final String tableName, |
|
651 | final Criteria criteria) |
|
652 | { |
|
653 | 575 | String shortTableName = getUnqualifiedTableName(tableName); |
654 | ||
655 | // Most of the time, the alias would be for the short name... |
|
656 | 575 | String aliasName = criteria.getTableForAlias(shortTableName); |
657 | 575 | if (StringUtils.isEmpty(aliasName)) |
658 | { |
|
659 | // But we should also check the FQN... |
|
660 | 552 | aliasName = criteria.getTableForAlias(tableName); |
661 | } |
|
662 | ||
663 | 575 | if (StringUtils.isNotEmpty(aliasName)) |
664 | { |
|
665 | // If the tables have an alias, add an "<xxx> <yyy> statement" |
|
666 | // <xxx> AS <yyy> causes problems on oracle |
|
667 | 25 | return new StringBuffer( |
668 | 1 | tableName.length() + aliasName.length() + 1) |
669 | 1 | .append(aliasName) |
670 | 1 | .append(" ") |
671 | 1 | .append(tableName) |
672 | 1 | .toString(); |
673 | } |
|
674 | ||
675 | 552 | return tableName; |
676 | } |
|
677 | ||
678 | /** |
|
679 | * Checks if the Tablename tableName is already contained in a from clause. |
|
680 | * If tableName and the tablenames in fromClause are generated by |
|
681 | * getTablenameForFromClause(String, Criteria), (which they usually are), |
|
682 | * then different aliases for the same table are treated |
|
683 | * as different tables: E.g. |
|
684 | * fromClauseContainsTableName(fromClause, "table_a a") returns false if |
|
685 | * fromClause contains only another alias for table_a , |
|
686 | * e.g. "table_a aa" and the unaliased tablename "table_a". |
|
687 | * Special case: If tableName is null, true is returned. |
|
688 | * @param fromClause a list containing only elements of type. |
|
689 | * Query.FromElement |
|
690 | * @param tableName the tablename to check |
|
691 | * @return if the Tablename tableName is already contained in a from clause. |
|
692 | * If tableName is null, true is returned. |
|
693 | */ |
|
694 | public static boolean fromClauseContainsTableName( |
|
695 | final UniqueList fromClause, |
|
696 | final String tableName) |
|
697 | { |
|
698 | 575 | if (tableName == null) |
699 | { |
|
700 | // usually this function is called to see if tableName should be |
|
701 | // added to the fromClause. As null should not be added, |
|
702 | // true is returned. |
|
703 | 23 | return true; |
704 | } |
|
705 | 577 | for (Iterator it = fromClause.iterator(); it.hasNext();) |
706 | { |
|
707 | 207 | Query.FromElement fromElement |
708 | 9 | = (Query.FromElement) it.next(); |
709 | 207 | if (tableName.equals(fromElement.getTableName())) |
710 | { |
|
711 | 184 | return true; |
712 | } |
|
713 | 22 | } |
714 | 368 | return false; |
715 | } |
|
716 | ||
717 | /** |
|
718 | * adds a table to the from clause of a query, if it is not already |
|
719 | * contained there. |
|
720 | * @param tableOrAliasName the name of a table |
|
721 | * or the alias for a table |
|
722 | * @param criteria a criteria object to resolve a possible alias |
|
723 | * @param query the query where the the tablename should be added |
|
724 | * to the from clause |
|
725 | * @return the table in the from clause which represents the |
|
726 | * supplied tableOrAliasName |
|
727 | */ |
|
728 | private static String addTableToFromClause( |
|
729 | final String tableName, |
|
730 | final Criteria criteria, |
|
731 | Query query) |
|
732 | { |
|
733 | 529 | String tableNameForFromClause |
734 | 23 | = getTableNameForFromClause(tableName, criteria); |
735 | ||
736 | 529 | UniqueList queryFromClause = query.getFromClause(); |
737 | ||
738 | // it is important that this piece of code is executed AFTER |
|
739 | // the joins are processed |
|
740 | 529 | if (!fromClauseContainsTableName( |
741 | 23 | queryFromClause, |
742 | 23 | tableNameForFromClause)) |
743 | { |
|
744 | 322 | Query.FromElement fromElement |
745 | 28 | = new Query.FromElement( |
746 | 14 | tableNameForFromClause, null, class="keyword">null); |
747 | 322 | queryFromClause.add(fromElement); |
748 | } |
|
749 | 529 | return tableNameForFromClause; |
750 | } |
|
751 | ||
752 | /** |
|
753 | * Inner Interface that defines the Callback method for |
|
754 | * the Table creation loop. |
|
755 | */ |
|
756 | public interface TableCallback |
|
757 | { |
|
758 | /** |
|
759 | * Callback Method for getTableSet() |
|
760 | * |
|
761 | * @param tables The current table name |
|
762 | * @param key The current criterion key. |
|
763 | * @param crit The Criteria used in getTableSet() |
|
764 | */ |
|
765 | void process(Set tables, String key, Criteria crit); |
|
766 | } |
|
767 | ||
768 | /** |
|
769 | * Inner Interface that defines the Callback method for |
|
770 | * the buildQuery Criterion evaluation |
|
771 | */ |
|
772 | public interface QueryCallback |
|
773 | { |
|
774 | /** |
|
775 | * The callback for building a query String |
|
776 | * |
|
777 | * @param criterion The current criterion |
|
778 | * @param params The parameter list passed to buildQueryString() |
|
779 | * @return WHERE SQL fragment for this criterion |
|
780 | */ |
|
781 | String process(Criterion criterion, List params); |
|
782 | } |
|
783 | ||
784 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |