KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * com.mckoi.database.interpret.Statement 09 Sep 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.jdbc.SQLQuery;
28 import com.mckoi.database.*;
29 import com.mckoi.debug.DebugLogger;
30 import java.util.*;
31
32 /**
33  * Provides a set of useful utility functions to use by all the
34  * interpretted statements.
35  *
36  * @author Tobias Downer
37  */

38
39 public abstract class Statement {
40
41   /**
42    * The Database context.
43    */

44   protected DatabaseConnection database;
45
46   /**
47    * The user context.
48    */

49   protected User user;
50
51   /**
52    * The StatementTree object that is the container for the query.
53    */

54   protected StatementTree cmd;
55
56   /**
57    * The SQLQuery object that was used to produce this statement.
58    */

59   protected SQLQuery query;
60   
61   /**
62    * The list of all FromTableInterface objects of resources referenced in
63    * this query. (FromTableInterface)
64    */

65   protected Vector table_list = new Vector();
66
67   /**
68    * Returns a DebugLogger object used to log debug commands.
69    */

70   public final DebugLogger Debug() {
71     return database.Debug();
72   }
73
74   /**
75    * Resets this statement so it may be re-prepared and evaluated again.
76    * Useful for repeating a query multiple times.
77    */

78   void reset() {
79     database = null;
80     user = null;
81     table_list = new Vector();
82   }
83
84   /**
85    * Performs initial preparation on the contents of the StatementTree by
86    * resolving all sub queries and mapping functions to their executable
87    * forms.
88    * <p>
89    * Given a StatementTree and a Database context, this method will convert
90    * all sub-queries found in the StatementTree to a Queriable object. In
91    * other words, all StatementTree are converted to Select objects. The given
92    * 'database' object is used as the session to prepare the sub-queries
93    * against.
94    * <p>
95    * This is called after 'init' and before 'prepare'.
96    */

97   public final void resolveTree() throws DatabaseException {
98
99     // For every expression in this select we must go through and resolve
100
// any sub-queries we find to the correct Select object.
101
// This method will prepare the sub-query substitute the StatementTree
102
// object for a Select object in the expression.
103
ExpressionPreparer preparer = new ExpressionPreparer() {
104       public boolean canPrepare(Object JavaDoc element) {
105         return element instanceof StatementTree;
106       }
107       public Object JavaDoc prepare(Object JavaDoc element) throws DatabaseException {
108         StatementTree stmt_tree = (StatementTree) element;
109         Select stmt = new Select();
110         stmt.init(database, stmt_tree, null);
111         stmt.resolveTree();
112         stmt.prepare();
113         return stmt;
114       }
115     };
116     cmd.prepareAllExpressions(preparer);
117
118   }
119
120   /**
121    * Given a fully resolved table name ( eg. Part.id ) this method will
122    * attempt to find the Table object that the column is in.
123    */

124   FromTableInterface findTableWithColumn(Variable column_name) {
125     for (int i = 0; i < table_list.size(); ++i) {
126       FromTableInterface table = (FromTableInterface) table_list.elementAt(i);
127       TableName tname = column_name.getTableName();
128       String JavaDoc sch_name = null;
129       String JavaDoc tab_name = null;
130       String JavaDoc col_name = column_name.getName();
131       if (tname != null) {
132         sch_name = tname.getSchema();
133         tab_name = tname.getName();
134       }
135       int rcc = table.resolveColumnCount(null, sch_name, tab_name, col_name);
136       if (rcc > 0) {
137         return table;
138       }
139     }
140     return null;
141   }
142
143   /**
144    * Given a fully resolved table name ( eg. Part.id ) this returns true if
145    * there is a table with the given column name, otherwise false.
146    * <p>
147    * NOTE: Intended to be overwritten...
148    */

149   boolean existsTableWithColumn(Variable column_name) {
150     return findTableWithColumn(column_name) != null;
151   }
152
153   /**
154    * Overwrite this method if your statement has some sort of column aliasing
155    * capability (such as a select statement). Returns a list of all fully
156    * qualified Variables that match the alias name, or an empty list if no
157    * matches found.
158    * <p>
159    * By default, returns an empty list.
160    */

161   ArrayList resolveAgainstAliases(Variable alias_name) {
162     return new ArrayList(0);
163   }
164
165   /**
166    * Resolves a TableName string (eg. 'Customer' 'APP.Customer' ) to a
167    * TableName object. If the schema part of the table name is not present
168    * then it is set to the current schema of the database connection. If the
169    * database is ignoring the case then this will correctly resolve the table
170    * to the cased version of the table name.
171    */

172   TableName resolveTableName(String JavaDoc name, DatabaseConnection db) {
173     return db.resolveTableName(name);
174   }
175
176   /**
177    * Returns the first FromTableInterface object that matches the given schema,
178    * table reference. Returns null if no objects with the given schema/name
179    * reference match.
180    */

181   FromTableInterface findTableInQuery(String JavaDoc schema, String JavaDoc name) {
182     for (int p = 0; p < table_list.size(); ++p) {
183       FromTableInterface table = (FromTableInterface) table_list.get(p);
184       if (table.matchesReference(null, schema, name)) {
185         return table;
186       }
187     }
188     return null;
189   }
190
191   /**
192    * Attempts to resolve an ambiguous column name such as 'id' into a
193    * Variable from the tables in this statement.
194    */

195   Variable resolveColumn(Variable v) {
196     // Try and resolve against alias names first,
197
ArrayList list = new ArrayList();
198     list.addAll(resolveAgainstAliases(v));
199
200     TableName tname = v.getTableName();
201     String JavaDoc sch_name = null;
202     String JavaDoc tab_name = null;
203     String JavaDoc col_name = v.getName();
204     if (tname != null) {
205       sch_name = tname.getSchema();
206       tab_name = tname.getName();
207     }
208
209     int matches_found = 0;
210     // Find matches in our list of tables sources,
211
for (int i = 0; i < table_list.size(); ++i) {
212       FromTableInterface table = (FromTableInterface) table_list.elementAt(i);
213       int rcc = table.resolveColumnCount(null, sch_name, tab_name, col_name);
214       if (rcc == 1) {
215         Variable matched =
216                       table.resolveColumn(null, sch_name, tab_name, col_name);
217         list.add(matched);
218       }
219       else if (rcc > 1) {
220         throw new StatementException("Ambiguous column name (" + v + ")");
221       }
222     }
223
224     int total_matches = list.size();
225     if (total_matches == 0) {
226       throw new StatementException("Can't find column: " + v);
227     }
228     else if (total_matches == 1) {
229       return (Variable) list.get(0);
230     }
231     else if (total_matches > 1) {
232       // if there more than one match, check if they all match the identical
233
// resource,
234
throw new StatementException("Ambiguous column name (" + v + ")");
235     }
236     else {
237       // Should never reach here but we include this exception to keep the
238
// compiler happy.
239
throw new Error JavaDoc("Negative total matches?");
240     }
241
242   }
243
244   /**
245    * Given a Variable object, this will resolve the name into a column name
246    * the database understands (substitutes aliases, etc).
247    */

248   public Variable resolveVariableName(Variable v) {
249     return resolveColumn(v);
250   }
251
252   /**
253    * Given an Expression, this will run through the expression and resolve
254    * any variable names via the 'resolveVariableName' method here.
255    */

256   void resolveExpression(Expression exp) {
257     // NOTE: This gets variables in all function parameters.
258
List vars = exp.allVariables();
259     for (int i = 0; i < vars.size(); ++i) {
260       Variable v = (Variable) vars.get(i);
261       Variable to_set = resolveVariableName(v);
262       v.set(to_set);
263     }
264   }
265
266   /**
267    * Add an FromTableInterface that is used within this query. These tables
268    * are used when we try to resolve a column name.
269    */

270   protected void addTable(FromTableInterface table) {
271     table_list.addElement(table);
272   }
273
274   /**
275    * Sets up internal variables for this statement for derived classes to use.
276    * This is called before 'prepare' and 'isExclusive' is called.
277    * <p>
278    * It is assumed that any ? style parameters in the StatementTree will have
279    * been resolved previous to a call to this method.
280    *
281    * @param db the DatabaseConnection that will execute this statement.
282    * @param stree the StatementTree that contains the parsed content of the
283    * statement being executed.
284    */

285   public final void init(DatabaseConnection db, StatementTree stree,
286                         SQLQuery query) {
287     this.database = db;
288     this.user = db.getUser();
289     this.cmd = stree;
290     this.query = query;
291   }
292
293   /**
294    * Prepares the statement with the given Database object. This is called
295    * before the statement is evaluated. The prepare statement queries the
296    * database and resolves information about the statement (for example, it
297    * resolves column names and aliases and determines the tables that are
298    * touched by this statement so we can lock the appropriate tables before
299    * we evaluate).
300    * <p>
301    * NOTE: Care must be taken to ensure that all methods called here are safe
302    * in as far as modifications to the data occuring. The rules for
303    * safety should be as follows. If the database is in EXCLUSIVE mode,
304    * then we need to wait until it's switched back to SHARED mode before
305    * this method is called.
306    * All collection of information done here should not involve any table
307    * state info. except for column count, column names, column types, etc.
308    * Queries such as obtaining the row count, selectable scheme information,
309    * and certainly 'getCellContents' must never be called during prepare.
310    * When prepare finishes, the affected tables are locked and the query is
311    * safe to 'evaluate' at which time table state is safe to inspect.
312    */

313   public abstract void prepare() throws DatabaseException;
314
315   /**
316    * Evaluates the statement and returns a table that represents the result
317    * set. This is called after 'prepare'.
318    */

319   public abstract Table evaluate()
320                                throws DatabaseException, TransactionException;
321
322 }
323
Popular Tags