001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.sql.exp;
012:
013: import com.versant.core.util.CharBuf;
014: import com.versant.core.jdbc.sql.SqlDriver;
015: import com.versant.core.jdo.query.ParamNode;
016: import com.versant.core.common.Debug;
017:
018: import java.util.Map;
019: import java.util.HashMap;
020:
021: import com.versant.core.common.BindingSupportImpl;
022:
023: /**
024: * An expression in an SQL query.
025: */
026: public class SqlExp {
027:
028: /** Do not convert this expression into a join. **/
029: public static final int NO = 0;
030: /** Convert into a join. **/
031: public static final int YES = 1;
032: /** Convert into a join and select distinct. **/
033: public static final int YES_DISTINCT = 2;
034: /** Convert into an outer join with is null check and select distinct. **/
035: public static final int YES_DISTINCT_NOT = 3;
036:
037: /**
038: * This makes it easy to form linked lists of expressions. This is
039: * faster and uses less memory than arrays of expressions.
040: */
041: public SqlExp next;
042: /**
043: * Linked list of children formed using their next fields.
044: */
045: public SqlExp childList;
046:
047: private int preFirstCharIndex;
048: private int lastCharIndex;
049:
050: public SqlExp() {
051: }
052:
053: public SqlExp createInstance() {
054: return new SqlExp();
055: }
056:
057: /**
058: * Use this to clone a SqlExp tree.
059: * @param sqlExp
060: * @return
061: */
062: public static SqlExp createClone(SqlExp sqlExp) {
063: return createClone(sqlExp, new HashMap());
064: }
065:
066: /**
067: * This is a util method that decides if a instance should be cloned. If the
068: * instance was already cloned then the already cloned instance is returned.
069: * @param inst
070: * @param cloneMap
071: * @return A cloned instance.
072: */
073: public static SqlExp createClone(SqlExp inst, Map cloneMap) {
074: if (inst == null)
075: return null;
076: if (cloneMap.containsKey(inst)) {
077: return (SqlExp) cloneMap.get(inst);
078: } else {
079: cloneMap.put(inst, inst.createInstance());
080: }
081: return inst.getClone((SqlExp) cloneMap.get(inst), cloneMap);
082: }
083:
084: /**
085: * Clone the current instance. This method is called by subclassed to create a clone of its
086: * superclass.
087: *
088: * @param clone The instance it must set the cloned values on.
089: * @param cloneMap This map is used to avoid creating multiple clones for a singel instance.
090: * @return The update SqlExp
091: */
092: public SqlExp getClone(SqlExp clone, Map cloneMap) {
093: if (next != null)
094: clone.next = createClone(next, cloneMap);
095: if (childList != null)
096: clone.childList = createClone(childList, cloneMap);
097: clone.preFirstCharIndex = preFirstCharIndex;
098: clone.lastCharIndex = lastCharIndex;
099: return clone;
100: }
101:
102: public SqlExp(SqlExp childList) {
103: this .childList = childList;
104: }
105:
106: public String toString() {
107: String n = getClass().getName();
108: int i = n.lastIndexOf('.');
109: if (i >= 0)
110: n = n.substring(i + 1);
111: return n + "@"
112: + Integer.toHexString(System.identityHashCode(this ));
113: }
114:
115: /**
116: * Dump debugging info to System.out.
117: */
118: public void dump(String indent) {
119: Debug.OUT.println(indent + this );
120: if (childList != null)
121: childList.dumpList(indent + " ");
122: }
123:
124: /**
125: * Dump us and our list to System.out.
126: */
127: public void dumpList(String indent) {
128: dump(indent);
129: for (SqlExp e = next; e != null; e = e.next)
130: e.dump(indent);
131: }
132:
133: /**
134: * Create an aliases for any subtables we may have.
135: */
136: public int createAlias(int index) {
137: for (SqlExp e = childList; e != null; e = e.next) {
138: index = e.createAlias(index);
139: }
140: return index;
141: }
142:
143: /**
144: * Replace any references to old with nw. This is used when redundant
145: * joins are removed.
146: */
147: public void replaceSelectExpRef(SelectExp old, SelectExp nw) {
148: for (SqlExp e = childList; e != null; e = e.next) {
149: e.replaceSelectExpRef(old, nw);
150: }
151: }
152:
153: /**
154: * Normalize this node i.e. transform it into its simplist possible form.
155: * This will turn sub selects into joins and so on. Return expression to
156: * replace us with or null if no change.
157: */
158: public SqlExp normalize(SqlDriver driver, SelectExp sel,
159: boolean convertExists) {
160: SqlExp p = null;
161: for (SqlExp e = childList; e != null; e = e.next) {
162: SqlExp r = e.normalize(driver, sel, convertExists);
163: if (r == null) {
164: p = e;
165: } else {
166: if (p == null)
167: childList = r;
168: else
169: p.next = r;
170: r.next = e.next;
171: }
172: }
173: return null;
174: }
175:
176: /**
177: * Append SQL for this node to s.
178: *
179: * @param driver The driver being used
180: * @param s Append the SQL here
181: * @param leftSibling The SqlExp to the left of us or null if none
182: */
183: public final void appendSQL(SqlDriver driver, CharBuf s,
184: SqlExp leftSibling) {
185: preFirstCharIndex = s.size();
186: appendSQLImp(driver, s, leftSibling);
187: lastCharIndex = s.size();
188: }
189:
190: /**
191: * Append SQL for this node to s. This is the method that subclasses
192: * should override.
193: *
194: * @param driver The driver being used
195: * @param s Append the SQL here
196: * @param leftSibling The SqlExp to the left of us or null if none
197: */
198: protected void appendSQLImp(SqlDriver driver, CharBuf s,
199: SqlExp leftSibling) {
200: }
201:
202: /**
203: * If this expression is added to an AndExp should it be enclosed in
204: * parenthesis?
205: */
206: public boolean requiresParensInAnd() {
207: return false;
208: }
209:
210: /**
211: * If this expression is added to an MultiplyExp should it be enclosed in
212: * parenthesis?
213: */
214: public boolean requiresParensInMultiply() {
215: return false;
216: }
217:
218: /**
219: * Get the index of first character of the this expression in the output
220: * buffer. This is used when the whole expression needs to be replaced.
221: */
222: public final int getPreFirstCharIndex() {
223: return preFirstCharIndex;
224: }
225:
226: /**
227: * Get the index of first character of the 'is null' parameter
228: * replacement span for this expression.
229: */
230: public int getFirstCharIndex() {
231: throw BindingSupportImpl.getInstance().internal(
232: "getFirstCharIndex called on " + this );
233: }
234:
235: /**
236: * Get the index of the character after the last character of the
237: * 'is null' parameter replacement span for this expression.
238: */
239: public final int getLastCharIndex() {
240: return lastCharIndex;
241: }
242:
243: /**
244: * Is this a negative expression for the purposes of replacing parameter
245: * values with 'is null' (false) or 'is not null' (true)?
246: */
247: public boolean isNegative() {
248: return false;
249: }
250:
251: /**
252: * What is the JDBC type of this expression (0 if unknown)?
253: */
254: public int getJdbcType() {
255: return 0;
256: }
257:
258: /**
259: * What is the java type code of this expression (0 if unknown)?
260: */
261: public int getJavaTypeCode() {
262: return 0;
263: }
264:
265: /**
266: * What is the class index for this expression (-1 if unknown)?
267: * @see ParamNode
268: */
269: public int getClassIndex() {
270: return -1;
271: }
272:
273: /**
274: * Can this expression be removed and its child be converted into a join?
275: * @see ExistsExp
276: * @see #NO
277: * @see #YES
278: * @see #YES_DISTINCT
279: * @see #YES_DISTINCT_NOT
280: */
281: public int getConvertToJoin() {
282: return NO;
283: }
284:
285: /**
286: * Add a list of expressions to the end of our childList.
287: */
288: public void append(SqlExp extra) {
289: if (childList == null) {
290: childList = extra;
291: } else {
292: SqlExp e;
293: for (e = childList; e.next != null; e = e.next)
294: ;
295: e.next = extra;
296: }
297: }
298:
299: /**
300: * Append e to base using an AndExp. If base is already an AndExp then
301: * e is simply appended. If base is null then e becomes base. Otherwise
302: * base becomes a a new AndExp created from the old base and e.
303: * @return New value for base
304: */
305: public static SqlExp appendWithAnd(SqlExp base, SqlExp e) {
306: if (e == null)
307: return base;
308: if (base == null) {
309: if (e.next == null) {
310: base = e;
311: } else {
312: base = new AndExp(e);
313: }
314: } else if (base instanceof AndExp) {
315: base.append(e);
316: } else {
317: base.next = e;
318: base = new AndExp(base);
319: }
320: return base;
321: }
322:
323: /**
324: * Make us an outer join or not. This is a NOP except for JoinExp and
325: * AndJoinExp.
326: * @see JoinExp
327: * @see AndJoinExp
328: */
329: public void setOuter(boolean on) {
330: }
331:
332: /**
333: * If this expression involves a single table (other than exclude) only
334: * then return the SelectExp for the table (e.g. a.col1 == 10 returns a,
335: * a.col1 = b.col2 returns null, a.col1 = b.col2 with exclude = b returns
336: * a). This is used to detect expressions that can be moved
337: * into the ON list for the join to the table involved.
338: */
339: public SelectExp getSingleSelectExp(SelectExp exclude) {
340: return null;
341: }
342:
343: /**
344: * Create an expression comparing the left and right exp with an
345: * operator. Only == and != really make sense. If left and right are
346: * lists then comparing matching exp's joined together in an 'and'
347: * list.
348: */
349: public static SqlExp createBinaryOpExp(SqlExp left, int op,
350: SqlExp right) {
351: if (left.next == null && right.next == null) {
352: BinaryOpExp ans = new BinaryOpExp(left, op, right);
353: if (right instanceof ParamExp) {
354: ParamExp p = (ParamExp) right;
355: p.usage.expList = ans;
356: if (left instanceof ColumnExp)
357: p.usage.expCount = 1;
358: }
359: return ans;
360: } else {
361: SqlExp l = left.next;
362: SqlExp r = right.next;
363: SqlExp list = new BinaryOpExp(left, op, right);
364: AndExp ans = new AndExp(list);
365: ParamExp pe;
366: if (right instanceof ParamExp) {
367: pe = (ParamExp) right;
368: pe.usage.expList = list;
369: } else {
370: pe = null;
371: }
372: boolean colExp = left instanceof ColumnExp;
373: int c = 1;
374: for (; l != null && r != null; c++) {
375: left = l;
376: right = r;
377: l = left.next;
378: r = right.next;
379: BinaryOpExp e = new BinaryOpExp(left, op, right);
380: list = list.next = e;
381: }
382: if (l != null || r != null) {
383: throw BindingSupportImpl.getInstance().internal(
384: "left and right lists have different length");
385: }
386: if (pe != null && colExp)
387: pe.usage.expCount = c;
388: return ans;
389: }
390: }
391:
392: }
|