KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > interpret > TableExpressionFromSet


1 /**
2  * com.mckoi.database.interpret.TableExpressionFromSet 01 Nov 2001
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database.interpret;
26
27 import com.mckoi.database.Variable;
28 import com.mckoi.database.TableName;
29 import com.mckoi.database.Expression;
30 import com.mckoi.database.DatabaseSystem;
31 import com.mckoi.database.DatabaseException;
32 import com.mckoi.database.StatementException;
33 import com.mckoi.database.ExpressionPreparer;
34 import com.mckoi.database.CorrelatedVariable;
35 import com.mckoi.database.DatabaseConnection;
36 import java.util.ArrayList JavaDoc;
37
38 /**
39  * A set of tables and function references that make up the resources made
40  * available by a table expression. When a SelectQueriable is prepared this
41  * object is created and is used to dereference names to sources. It also
42  * has the ability to chain to another TableExpressionFromSet and resolve
43  * references over a complex sub-query hierarchy.
44  *
45  * @author Tobias Downer
46  */

47
48 class TableExpressionFromSet {
49
50   /**
51    * The list of table resources in this set.
52    * (FromTableInterface).
53    */

54   private ArrayList JavaDoc table_resources;
55
56   /**
57    * The list of function expression resources. For example, one table
58    * expression may expose a function as 'SELECT (a + b) AS c, ....' in which
59    * case we have a virtual assignment of c = (a + b) in this set.
60    */

61   private ArrayList JavaDoc function_resources;
62
63   /**
64    * The list of Variable references in this set that are exposed to the
65    * outside, including function aliases. For example,
66    * SELECT a, b, c, (a + 1) d FROM ABCTable
67    * Would be exposing variables 'a', 'b', 'c' and 'd'.
68    */

69   private ArrayList JavaDoc exposed_variables;
70
71   /**
72    * Set to true if this should do case insensitive resolutions.
73    */

74   private boolean case_insensitive = false;
75
76   /**
77    * The parent TableExpressionFromSet if one exists. This is used for
78    * chaining a set of table sets together. When chained the
79    * 'globalResolveVariable' method can be used to resolve a reference in the
80    * chain.
81    */

82   private TableExpressionFromSet parent;
83
84
85
86
87
88   /**
89    * Constructs the object.
90    */

91   public TableExpressionFromSet(DatabaseConnection connection) {
92     table_resources = new ArrayList JavaDoc();
93     function_resources = new ArrayList JavaDoc();
94     exposed_variables = new ArrayList JavaDoc();
95     // Is the database case insensitive?
96
this.case_insensitive = connection.isInCaseInsensitiveMode();
97   }
98
99   /**
100    * Sets the parent of this expression. parent can be set to null.
101    */

102   public void setParent(TableExpressionFromSet parent) {
103     this.parent = parent;
104   }
105
106   /**
107    * Returns the parent of this set. If it has no parent it returns null.
108    */

109   public TableExpressionFromSet getParent() {
110     return parent;
111   }
112
113   /**
114    * Toggle the case sensitivity flag.
115    */

116   public void setCaseInsensitive(boolean status) {
117     case_insensitive = status;
118   }
119
120   boolean stringCompare(String JavaDoc str1, String JavaDoc str2) {
121     if (!case_insensitive) {
122       return str1.equals(str2);
123     }
124     return str1.equalsIgnoreCase(str2);
125   }
126
127   /**
128    * Adds a table resource to the set.
129    */

130   public void addTable(FromTableInterface table_resource) {
131     table_resources.add(table_resource);
132   }
133
134   /**
135    * Adds a function resource to the set. Note that is possible for there to
136    * be references in the 'expression' that do not reference resources in this
137    * set. For example, a correlated reference.
138    */

139   public void addFunctionRef(String JavaDoc name, Expression expression) {
140 // System.out.println("addFunctionRef: " + name + ", " + expression);
141
function_resources.add(name);
142     function_resources.add(expression);
143   }
144
145   /**
146    * Adds a variable in this from set that is exposed to the outside. This
147    * list should contain all references from the SELECT ... part of the
148    * query. For example, SELECT a, b, (a + 1) d exposes variables
149    * a, b and d.
150    */

151   public void exposeVariable(Variable v) {
152 // System.out.println("exposeVariable: " + v);
153
// new Error().printStackTrace();
154
exposed_variables.add(v);
155   }
156
157   /**
158    * Exposes all the columns from the given FromTableInterface.
159    */

160   public void exposeAllColumnsFromSource(FromTableInterface table) {
161     Variable[] v = table.allColumns();
162     for (int p = 0; p < v.length; ++p) {
163       exposeVariable(v[p]);
164     }
165   }
166
167   /**
168    * Exposes all the columns in all the child tables.
169    */

170   public void exposeAllColumns() {
171     for (int i = 0; i < setCount(); ++i) {
172       exposeAllColumnsFromSource(getTable(i));
173     }
174   }
175
176   /**
177    * Exposes all the columns from the given table name.
178    */

179   public void exposeAllColumnsFromSource(TableName tn) {
180     FromTableInterface table_interface =
181                                       findTable(tn.getSchema(), tn.getName());
182     if (table_interface == null) {
183       throw new StatementException("Table name found: " + tn);
184     }
185     exposeAllColumnsFromSource(table_interface);
186   }
187
188   /**
189    * Returns a Variable[] array for each variable that is exposed in this
190    * from set. This is a list of fully qualified variables that are
191    * referencable from the final result of the table expression.
192    */

193   public Variable[] generateResolvedVariableList() {
194     int sz = exposed_variables.size();
195     Variable[] list = new Variable[sz];
196     for (int i = 0; i < sz; ++i) {
197       list[i] = new Variable((Variable) exposed_variables.get(i));
198     }
199     return list;
200   }
201
202   /**
203    * Returns the first FromTableInterface object that matches the given schema,
204    * table reference. Returns null if no objects with the given schema/name
205    * reference match.
206    */

207   FromTableInterface findTable(String JavaDoc schema, String JavaDoc name) {
208     for (int p = 0; p < setCount(); ++p) {
209       FromTableInterface table = getTable(p);
210       if (table.matchesReference(null, schema, name)) {
211         return table;
212       }
213     }
214     return null;
215   }
216
217   /**
218    * Returns the number of FromTableInterface objects in this set.
219    */

220   int setCount() {
221     return table_resources.size();
222   }
223
224   /**
225    * Returns the FromTableInterface object at the given index position in this
226    * set.
227    */

228   FromTableInterface getTable(int i) {
229     return (FromTableInterface) table_resources.get(i);
230   }
231
232
233   /**
234    * Dereferences a fully qualified reference that is within this set. For
235    * example, SELECT ( a + b ) AS z given 'z' would return the expression
236    * (a + b).
237    * <p>
238    * Returns null if unable to dereference assignment because it does not
239    * exist.
240    */

241   Expression dereferenceAssignment(Variable v) {
242     TableName tname = v.getTableName();
243     String JavaDoc var_name = v.getName();
244     // We are guarenteed not to match with a function if the table name part
245
// of a Variable is present.
246
if (tname != null) {
247       return null;
248     }
249
250     // Search for the function with this name
251
Expression last_found = null;
252     int matches_found = 0;
253     for (int i = 0; i < function_resources.size(); i += 2) {
254       String JavaDoc fun_name = (String JavaDoc) function_resources.get(i);
255       if (stringCompare(fun_name, var_name)) {
256         if (matches_found > 0) {
257           throw new StatementException("Ambiguous reference '" + v + "'");
258         }
259         last_found = (Expression) function_resources.get(i + 1);
260         ++matches_found;
261       }
262     }
263
264     return last_found;
265   }
266
267   /**
268    * Resolves the given Variable object to an assignment if it's possible to do
269    * so within the context of this set. If the variable can not be
270    * unambiguously resolved to a function or aliased column, a
271    * StatementException is thrown. If the variable isn't assigned to any
272    * function or aliased column, 'null' is returned.
273    */

274   private Variable resolveAssignmentReference(Variable v) {
275     TableName tname = v.getTableName();
276     String JavaDoc var_name = v.getName();
277     // We are guarenteed not to match with a function if the table name part
278
// of a Variable is present.
279
if (tname != null) {
280       return null;
281     }
282
283     // Search for the function with this name
284
Variable last_found = null;
285     int matches_found = 0;
286     for (int i = 0; i < function_resources.size(); i += 2) {
287       String JavaDoc fun_name = (String JavaDoc) function_resources.get(i);
288       if (stringCompare(fun_name, var_name)) {
289         if (matches_found > 0) {
290           throw new StatementException("Ambiguous reference '" + v + "'");
291         }
292         last_found = new Variable(fun_name);
293         ++matches_found;
294       }
295     }
296
297     return last_found;
298   }
299
300
301   /**
302    * Resolves the given Variable against the table columns in this from set.
303    * If the variable does not resolve to anything 'null' is returned. If the
304    * variable is ambiguous, a StatementException is thrown.
305    * <p>
306    * Note that the given variable does not have to be fully qualified but the
307    * returned expressions are fully qualified.
308    */

309   Variable resolveTableColumnReference(Variable v) {
310     TableName tname = v.getTableName();
311     String JavaDoc sch_name = null;
312     String JavaDoc tab_name = null;
313     String JavaDoc col_name = v.getName();
314     if (tname != null) {
315       sch_name = tname.getSchema();
316       tab_name = tname.getName();
317     }
318
319     // Find matches in our list of tables sources,
320
Variable matched_var = null;
321
322     for (int i = 0; i < table_resources.size(); ++i) {
323       FromTableInterface table = (FromTableInterface) table_resources.get(i);
324       int rcc = table.resolveColumnCount(null, sch_name, tab_name, col_name);
325       if (rcc == 0) {
326         // do nothing if no matches
327
}
328       else if (rcc == 1 && matched_var == null) {
329         // If 1 match and matched_var = null
330
matched_var =
331                      table.resolveColumn(null, sch_name, tab_name, col_name);
332       }
333       else { // if (rcc >= 1 and matched_var != null)
334
System.out.println(matched_var);
335         System.out.println(rcc);
336         throw new StatementException("Ambiguous reference '" + v + "'");
337       }
338     }
339
340     return matched_var;
341   }
342
343   /**
344    * Resolves the given Variable object to a fully resolved Variable
345    * within the context of this table expression. If the variable does not
346    * resolve to anything 'null' is returned. If the variable is ambiguous, a
347    * StatementException is thrown.
348    * <p>
349    * If the variable name references a table column, an expression with a
350    * single Variable element is returned. If the variable name references a
351    * function, an expression of the function is returned.
352    * <p>
353    * Note that the given variable does not have to be fully qualified but the
354    * returned expressions are fully qualified.
355    */

356   Variable resolveReference(Variable v) {
357     // Try and resolve against alias names first,
358
ArrayList JavaDoc list = new ArrayList JavaDoc();
359
360 // Expression exp = dereferenceAssignment(v);
361
// // If this is an alias like 'a AS b' then add 'a' to the list instead of
362
// // adding 'b'. This allows us to handle a number of ambiguous conditions.
363
// if (exp != null) {
364
// Variable v2 = exp.getVariable();
365
// if (v2 != null) {
366
// list.add(resolveTableColumnReference(v2));
367
// }
368
// else {
369
// list.add(resolveAssignmentReference(v));
370
// }
371
// }
372

373     Variable function_var = resolveAssignmentReference(v);
374     if (function_var != null) {
375       list.add(function_var);
376     }
377
378     Variable tc_var = resolveTableColumnReference(v);
379     if (tc_var != null) {
380       list.add(tc_var);
381     }
382
383 // TableName tname = v.getTableName();
384
// String sch_name = null;
385
// String tab_name = null;
386
// String col_name = v.getName();
387
// if (tname != null) {
388
// sch_name = tname.getSchema();
389
// tab_name = tname.getName();
390
// }
391
//
392
// // Find matches in our list of tables sources,
393
// for (int i = 0; i < table_resources.size(); ++i) {
394
// FromTableInterface table = (FromTableInterface) table_resources.get(i);
395
// int rcc = table.resolveColumnCount(null, sch_name, tab_name, col_name);
396
// if (rcc == 1) {
397
// Variable matched =
398
// table.resolveColumn(null, sch_name, tab_name, col_name);
399
// list.add(matched);
400
// }
401
// else if (rcc > 1) {
402
// throw new StatementException("Ambiguous reference '" + v + "'");
403
// }
404
// }
405

406     // Return the variable if we found one unambiguously.
407
int list_size = list.size();
408     if (list_size == 0) {
409       return null;
410     }
411     else if (list_size == 1) {
412       return (Variable) list.get(0);
413     }
414     else {
415 // // Check if the variables are the same?
416
// Variable cv = (Variable) list.get(0);
417
// for (int i = 1; i < list.size(); ++i) {
418
// if (!cv.equals(list.get(i))) {
419
throw new StatementException("Ambiguous reference '" + v + "'");
420 // }
421
// }
422
// // If they are all the same return the variable.
423
// return v;
424
}
425
426   }
427
428   /**
429    * Resolves the given Variable reference within the chained list of
430    * TableExpressionFromSet objects to a CorrelatedVariable. If the reference
431    * is not found in this set the method recurses to the parent set. The first
432    * unambiguous reference is returned.
433    * <p>
434    * If resolution is ambiguous within a set, a StatementException is thrown.
435    * <p>
436    * Returns null if the reference could not be resolved.
437    */

438   CorrelatedVariable globalResolveReference(int level, Variable v) {
439     Variable nv = resolveReference(v);
440     if (nv == null && getParent() != null) {
441       // If we need to descend to the parent, increment the level.
442
return getParent().globalResolveReference(level + 1, v);
443     }
444     else if (nv != null) {
445       return new CorrelatedVariable(nv, level);
446     }
447     return null;
448   }
449
450   /**
451    * Attempts to qualify the given Variable object to a value found either
452    * in this from set, or a value in the parent from set. A variable that
453    * is qualified by the parent is called a correlated variable. Any
454    * correlated variables that are successfully qualified are returned as
455    * CorrelatedVariable objects.
456    */

457   Object JavaDoc qualifyVariable(Variable v_in) {
458     Variable v = resolveReference(v_in);
459     if (v == null) {
460       // If not found, try and resolve in parent set (correlated)
461
if (getParent() != null) {
462         CorrelatedVariable cv = getParent().globalResolveReference(1, v_in);
463         if (cv == null) {
464           throw new StatementException("Reference '" +
465                                        v_in + "' not found.");
466         }
467         return cv;
468       }
469       if (v == null) {
470         throw new StatementException("Reference '" +
471                                      v_in + "' not found.");
472       }
473     }
474     return v;
475   }
476
477   /**
478    * Returns an ExpressionPreparer that qualifies all variables in an
479    * expression to either a qualified Variable or a CorrelatedVariable object.
480    */

481   ExpressionPreparer expressionQualifier() {
482     return new ExpressionPreparer() {
483       public boolean canPrepare(Object JavaDoc element) {
484         return element instanceof Variable;
485       }
486       public Object JavaDoc prepare(Object JavaDoc element) throws DatabaseException {
487         return qualifyVariable((Variable) element);
488       }
489     };
490   }
491
492 }
493
Popular Tags