001: /*
002: * $Id: ResolveSelectableVisitor.java,v 1.9 2005/12/22 09:02:29 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2004-2005 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040: package org.axiondb.engine.visitors;
041:
042: import java.util.ArrayList;
043: import java.util.List;
044:
045: import org.axiondb.AxionException;
046: import org.axiondb.BindVariable;
047: import org.axiondb.Column;
048: import org.axiondb.ColumnIdentifier;
049: import org.axiondb.DataType;
050: import org.axiondb.Database;
051: import org.axiondb.Literal;
052: import org.axiondb.Selectable;
053: import org.axiondb.Sequence;
054: import org.axiondb.SequenceEvaluator;
055: import org.axiondb.Table;
056: import org.axiondb.TableIdentifier;
057: import org.axiondb.VariableContext;
058: import org.axiondb.engine.commands.SelectCommand;
059: import org.axiondb.engine.commands.SubSelectCommand;
060: import org.axiondb.functions.CastAsFunction;
061: import org.axiondb.functions.ConcreteFunction;
062: import org.axiondb.functions.FunctionIdentifier;
063: import org.axiondb.util.Utils;
064:
065: /**
066: * Resolves a (@link Selectable) for a given (@link Database)
067: *
068: * @author Ahimanikya Satapathy
069: */
070: public class ResolveSelectableVisitor {
071:
072: private Database _db = null;
073:
074: public ResolveSelectableVisitor(Database db) {
075: _db = db;
076: }
077:
078: public Selectable visit(ColumnIdentifier column, List selected,
079: TableIdentifier[] tables) throws AxionException {
080: Selectable result = column;
081: boolean found = false;
082:
083: if (selected != null && !selected.isEmpty()) {
084: String aliasName = column.getName();
085:
086: for (int i = 0, I = selected.size(); i < I; i++) {
087: Selectable sel = (Selectable) selected.get(i);
088: if (sel instanceof ColumnIdentifier
089: && (aliasName.equals(sel.getAlias()) || (aliasName
090: .equals(sel.getName())))) {
091: // if alias is qualified by tableName, then check for a match
092: ColumnIdentifier colid = (ColumnIdentifier) sel;
093: if (matchTableName(column, colid)) {
094: // In case of Insert Select same column with diffrent alias
095: // is used in select cluase
096: String resolvedAlias = colid.getAlias();
097: String colAlias = column.getAlias();
098: if (resolvedAlias != null && colAlias != null
099: && !resolvedAlias.equals(colAlias)) {
100: column.setTableIdentifier(colid
101: .getTableIdentifier());
102: column.setDataType(colid.getDataType());
103: column.setName(column.getName());
104: } else {
105: column = colid;
106: }
107: break;
108: }
109: } else if (aliasName.equals(sel.getAlias())) {
110: // assumption: sel has been resolved before
111: result = sel;
112: found = true;
113: break;
114: }
115: }
116: }
117:
118: if (!found) {
119: if (isAlreadyResolved(column)) {
120: result = column;
121: } else if (identifiesSequence(column)) {
122: // Note: sequenceName.NEXTVAL, parser create a ColumnIdentifier
123: // and sets table name as sequenceName and column name as NEXTVAL
124: Sequence seq = _db.getSequence(column.getTableName());
125: result = new SequenceEvaluator(seq, column.getName());
126: } else {
127: result = resolveTrueColumn(column, tables);
128: }
129: }
130:
131: return setVariableContext(result);
132: }
133:
134: public Selectable visit(FunctionIdentifier fn, List selected,
135: TableIdentifier[] tables) throws AxionException {
136: ConcreteFunction cfn = _db.getFunction(fn.getName());
137: if (null == cfn) {
138: throw new AxionException("No function matching "
139: + fn.getName());
140: }
141:
142: // special handling for cast as, we want to get the user supplied
143: // literal data type then change the data type of literal based on
144: // what we get from the database
145: boolean isCastAsFunction = (cfn instanceof CastAsFunction) ? true
146: : false;
147: if (isCastAsFunction) {
148: List args = new ArrayList(2);
149: Literal literal = (Literal) fn.getArgument(1);
150:
151: DataType targetType = _db.getDataType(literal.getName());
152:
153: if (Utils.isPrecisionRequired(targetType.getJdbcType())) {
154: targetType = targetType.makeNewInstance();
155: }
156:
157: if (targetType != null) {
158: if (targetType instanceof DataType.NonFixedPrecision) {
159: if (targetType instanceof DataType.ExactNumeric) {
160: Literal scaleVal = (Literal) fn.getArgument(3);
161: int scale = Integer.parseInt(scaleVal
162: .evaluate().toString());
163: ((DataType.ExactNumeric) targetType)
164: .setScale(scale);
165: }
166:
167: Literal precisionVal = (Literal) fn.getArgument(2);
168: int precision = Integer.parseInt(precisionVal
169: .evaluate().toString());
170: ((DataType.NonFixedPrecision) targetType)
171: .setPrecision(precision);
172: }
173: literal.setDataType(targetType);
174: } else {
175: throw new AxionException("invalid data type: "
176: + literal.getName());
177: }
178:
179: args.add(fn.getArgument(0));
180: args.add(fn.getArgument(1));
181:
182: String alias = fn.getAlias();
183: fn = new FunctionIdentifier(fn.getName(), args);
184: fn.setAlias(alias);
185: }
186:
187: for (int i = 0, I = fn.getArgumentCount(); i < I; i++) {
188: cfn.addArgument(visit(fn.getArgument(i), selected, tables));
189: }
190:
191: if (!cfn.isValid()) {
192: throw new AxionException("Function " + cfn
193: + " isn't valid.");
194: }
195: cfn.setAlias(fn.getAlias());
196:
197: return setVariableContext(cfn);
198: }
199:
200: /**
201: * "Resolve" the given {@link Selectable}relative to the given list of
202: * {@link TableIdentifier tables}, converting aliased or relative references into
203: * absolute ones.
204: */
205: public Selectable visit(Selectable selectable, List selected,
206: TableIdentifier[] tables) throws AxionException {
207: Selectable result = selectable;
208:
209: if (selectable instanceof ColumnIdentifier) {
210: result = visit((ColumnIdentifier) selectable, selected,
211: tables);
212: } else if (selectable instanceof FunctionIdentifier) {
213: result = visit((FunctionIdentifier) selectable, selected,
214: tables);
215: } else if (selectable instanceof ConcreteFunction) {
216: result = selectable;
217: } else if (selectable instanceof BindVariable) {
218: result = selectable;
219: } else if (selectable instanceof Literal) {
220: result = selectable;
221: } else if (selectable instanceof SelectCommand) {
222: result = visit((SubSelectCommand) selectable, tables);
223: } else if (null != selectable) {
224: throw new AxionException("Couldn't resolve Selectable "
225: + selectable);
226: }
227:
228: return setVariableContext(result);
229: }
230:
231: public Selectable visit(SubSelectCommand select,
232: TableIdentifier[] tables) throws AxionException {
233: select.setDB(_db);
234: select.setParentTables(tables);
235: return setVariableContext(select);
236: }
237:
238: private boolean identifiesSequence(ColumnIdentifier column) {
239: return column.getTableName() != null
240: && _db.getSequence(column.getTableName()) != null;
241: }
242:
243: private boolean isAlreadyResolved(ColumnIdentifier column) {
244: return column.getTableName() != null
245: && column.getTableAlias() != null
246: && column.getDataType() != null;
247: }
248:
249: private boolean matchTableName(ColumnIdentifier alias,
250: ColumnIdentifier colid) {
251: String aliasTableName = alias.getTableName();
252: String tableName = colid.getTableName();
253: String aliasName = colid.getTableAlias();
254:
255: if (aliasTableName == null) { // No qualifire used
256: return true;
257: } else if (tableName != null
258: && aliasTableName.equals(tableName)) {
259: return true;
260: } else if (tableName != null
261: && aliasTableName.equals(aliasName)) {
262: return true;
263: }
264: return false;
265: }
266:
267: private Selectable resolveTrueColumn(ColumnIdentifier column,
268: TableIdentifier[] tables) throws AxionException {
269: for (int i = 0; null != tables && i < tables.length; i++) {
270: if (!_db.hasTable(tables[i])) {
271: throw new AxionException(42704);
272: }
273:
274: if (resolveTrueColumnForTable(column, tables[i])) {
275: return column;
276: }
277: }
278: throw new AxionException(42703);
279: }
280:
281: private boolean resolveTrueColumnForTable(ColumnIdentifier column,
282: TableIdentifier tableId) throws AxionException {
283: if (null != tableId.getTableAlias()
284: && tableId.getTableAlias()
285: .equals(column.getTableName())) {
286: // if the column's table name is the table's alias name,
287: // the column belongs to this table
288: column.setTableIdentifier(tableId);
289: return isStar(column) ? true : resolveDataType(column, _db
290: .getTable(tableId));
291: } else if (tableId.getTableName().equals(column.getTableName())) {
292: // else if the column's table name is the table's name
293: // the column belongs to this table
294: column.setTableIdentifier(tableId);
295: return isStar(column) ? true : resolveDataType(column, _db
296: .getTable(tableId));
297: } else if (null == column.getTableName()) {
298: // if the column has no table name
299: if (isStar(column)) {
300: return true;
301: } else if (resolveDataType(column, _db.getTable(tableId))) {
302: column.setTableIdentifier(tableId);
303: return true;
304: }
305: }
306: return false;
307: }
308:
309: private boolean resolveDataType(ColumnIdentifier column, Table table)
310: throws AxionException {
311: if (table.hasColumn(column)) {
312: Column col = table.getColumn(column.getName());
313: column.setDataType(col.getDataType());
314: return true;
315: }
316: return false;
317: }
318:
319: private boolean isStar(ColumnIdentifier column) {
320: if ("*".equals(column.getName())) {
321: // if the column is "*", we're done
322: return true;
323: } else {
324: return false;
325: }
326: }
327:
328: private Selectable setVariableContext(Selectable sel) {
329: if (sel != null && _db instanceof VariableContext) {
330: sel.setVariableContext((VariableContext) _db);
331: }
332: return sel;
333: }
334: }
|