0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.amber.query;
0031:
0032: import com.caucho.amber.AmberException;
0033: import com.caucho.amber.entity.AmberEntityHome;
0034: import com.caucho.amber.expr.*;
0035: import com.caucho.amber.expr.fun.*;
0036: import com.caucho.amber.manager.AmberPersistenceUnit;
0037: import com.caucho.amber.table.ForeignColumn;
0038: import com.caucho.amber.table.LinkColumns;
0039: import com.caucho.amber.table.Table;
0040: import com.caucho.amber.type.EntityType;
0041: import com.caucho.amber.type.RelatedType;
0042: import com.caucho.amber.type.Type;
0043: import com.caucho.jdbc.JdbcMetaData;
0044: import com.caucho.log.Log;
0045: import com.caucho.util.CharBuffer;
0046: import com.caucho.util.IntMap;
0047: import com.caucho.util.L10N;
0048:
0049: import javax.sql.DataSource;
0050: import java.sql.Connection;
0051: import java.sql.ResultSet;
0052: import java.sql.SQLException;
0053: import java.sql.Statement;
0054: import java.util.ArrayList;
0055: import java.util.HashMap;
0056: import java.util.Map;
0057: import java.util.logging.Level;
0058: import java.util.logging.Logger;
0059:
0060: /**
0061: * Contains the parser for EJB 3.0 style queries and stores
0062: * the parsed expressions.
0063: */
0064: public class QueryParser {
0065: static final Logger log = Log.open(QueryParser.class);
0066: static final L10N L = new L10N(QueryParser.class);
0067:
0068: public final static int IDENTIFIER = 128;
0069: public final static int INTEGER = IDENTIFIER + 1;
0070: public final static int LONG = INTEGER + 1;
0071: public final static int DOUBLE = LONG + 1;
0072: public final static int STRING = DOUBLE + 1;
0073: public final static int TRUE = STRING + 1;
0074: public final static int FALSE = TRUE + 1;
0075: public final static int UNKNOWN = FALSE + 1;
0076: public final static int MEMBER = UNKNOWN + 1;
0077: public final static int OF = MEMBER + 1;
0078: public final static int EMPTY = OF + 1;
0079: public final static int NULL = EMPTY + 1;
0080:
0081: public final static int FROM = NULL + 1;
0082: public final static int IN = FROM + 1;
0083: public final static int SELECT = IN + 1;
0084: public final static int UPDATE = SELECT + 1;
0085: public final static int DELETE = UPDATE + 1;
0086: public final static int DISTINCT = DELETE + 1;
0087: public final static int WHERE = DISTINCT + 1;
0088: public final static int AS = WHERE + 1;
0089: public final static int SET = AS + 1;
0090: public final static int ORDER = SET + 1;
0091: public final static int GROUP = ORDER + 1;
0092: public final static int BY = GROUP + 1;
0093: public final static int ASC = BY + 1;
0094: public final static int DESC = ASC + 1;
0095: public final static int LIMIT = DESC + 1;
0096: public final static int OFFSET = LIMIT + 1;
0097:
0098: public final static int JOIN = OFFSET + 1;
0099: public final static int INNER = JOIN + 1;
0100: public final static int LEFT = INNER + 1;
0101: public final static int OUTER = LEFT + 1;
0102: public final static int FETCH = OUTER + 1;
0103:
0104: public final static int BETWEEN = FETCH + 1;
0105: public final static int LIKE = BETWEEN + 1;
0106: public final static int ESCAPE = LIKE + 1;
0107: public final static int IS = ESCAPE + 1;
0108:
0109: public final static int CONCAT_OP = IS + 1;
0110:
0111: public final static int EQ = CONCAT_OP + 1;
0112: public final static int NE = EQ + 1;
0113: public final static int LT = NE + 1;
0114: public final static int LE = LT + 1;
0115: public final static int GT = LE + 1;
0116: public final static int GE = GT + 1;
0117:
0118: public final static int AND = GE + 1;
0119: public final static int OR = AND + 1;
0120: public final static int NOT = OR + 1;
0121:
0122: public final static int LENGTH = NOT + 1;
0123: public final static int LOCATE = LENGTH + 1;
0124:
0125: public final static int ABS = LOCATE + 1;
0126: public final static int SQRT = ABS + 1;
0127: public final static int MOD = SQRT + 1;
0128: public final static int SIZE = MOD + 1;
0129:
0130: public final static int MAX = SIZE + 1;
0131: public final static int MIN = MAX + 1;
0132: public final static int SUM = MIN + 1;
0133:
0134: public final static int CONCAT = SUM + 1;
0135: public final static int LOWER = CONCAT + 1;
0136: public final static int UPPER = LOWER + 1;
0137: public final static int SUBSTRING = UPPER + 1;
0138: public final static int TRIM = SUBSTRING + 1;
0139:
0140: public final static int BOTH = TRIM + 1;
0141: public final static int LEADING = BOTH + 1;
0142: public final static int TRAILING = LEADING + 1;
0143:
0144: public final static int CURRENT_DATE = TRAILING + 1;
0145: public final static int CURRENT_TIME = CURRENT_DATE + 1;
0146: public final static int CURRENT_TIMESTAMP = CURRENT_TIME + 1;
0147:
0148: public final static int EXTERNAL_DOT = CURRENT_TIMESTAMP + 1;
0149:
0150: public final static int ARG = EXTERNAL_DOT + 1;
0151: public final static int NAMED_ARG = ARG + 1;
0152:
0153: public final static int NEW = NAMED_ARG + 1;
0154:
0155: public final static int THIS = NEW + 1;
0156:
0157: public final static int NOT_NULL = THIS + 1;
0158:
0159: public final static int HAVING = NOT_NULL + 1;
0160:
0161: private static IntMap _reserved;
0162:
0163: private AmberPersistenceUnit _persistenceUnit;
0164:
0165: // The query
0166: private String _sql;
0167:
0168: /*
0169: // list of the relation links
0170: private ArrayList<LinkItem> _linkList;
0171: // select expression
0172: private Expr _selectExpr;
0173: // is distinct (set)
0174: private boolean _isDistinct;
0175: */
0176:
0177: // True if entities should be lazily loaded
0178: private boolean _isLazyResult;
0179:
0180: // The select query
0181: private AbstractQuery _query;
0182:
0183: // list of relations
0184: private HashMap<PathExpr, PathExpr> _pathMap = new HashMap<PathExpr, PathExpr>();
0185:
0186: // parse index
0187: private int _parseIndex;
0188: // current token
0189: private int _token;
0190: // unique
0191: private int _unique;
0192: // parameter count
0193: private int _parameterCount;
0194: // temp for parsing
0195: private String _lexeme;
0196:
0197: private ArrayList<ArgExpr> _argList = new ArrayList<ArgExpr>();
0198:
0199: private HashMap<AmberExpr, String> _joinFetchMap;
0200:
0201: ArrayList<AmberExpr> _groupList = null;
0202:
0203: private int _sqlArgCount;
0204:
0205: private FromItem.JoinSemantics _joinSemantics = FromItem.JoinSemantics.UNKNOWN;
0206:
0207: private boolean _isJoinFetch = false;
0208:
0209: // Parsing control variable, jpa/0tp4 (TRIM FROM)
0210: // SELECT .._depth=0.. TRIM(.._depth=1.. 'a' FROM o.d1) .._depth=0 FROM ...
0211: private int _depth = 0;
0212:
0213: private boolean _parsingResult;
0214: private boolean _parsingFrom;
0215: private boolean _parsingHaving;
0216:
0217: // jpa/119l: WHERE SIZE(xxx) > 0 => GROUP BY ... HAVING COUNT(xxx) > 0
0218: private boolean _isSizeFunExpr;
0219: private AmberExpr _havingExpr;
0220: // jpa/1199
0221: ArrayList<AmberExpr> _appendResultList = null;
0222:
0223: private boolean _isDerbyDBMS;
0224: private boolean _isPostgresDBMS;
0225:
0226: /**
0227: * Creates the query parser.
0228: */
0229: public QueryParser(String query) {
0230: _sql = query;
0231: }
0232:
0233: /**
0234: * Returns true for Derby-like DBMS.
0235: */
0236: public boolean isDerbyDBMS() {
0237: return _isDerbyDBMS;
0238: }
0239:
0240: /**
0241: * Returns true for Postgres-like DBMS.
0242: */
0243: public boolean isPostgresDBMS() {
0244: return _isPostgresDBMS;
0245: }
0246:
0247: /**
0248: * Sets the persistence unit.
0249: */
0250: public void setPersistenceUnit(AmberPersistenceUnit persistenceUnit) {
0251: _persistenceUnit = persistenceUnit;
0252:
0253: _isDerbyDBMS = false;
0254: _isPostgresDBMS = false;
0255:
0256: if (persistenceUnit == null)
0257: return;
0258:
0259: _isDerbyDBMS = !persistenceUnit.hasPositionFunction();
0260: _isPostgresDBMS = persistenceUnit.getFalseLiteral()
0261: .equalsIgnoreCase("false");
0262: }
0263:
0264: /**
0265: * Sets true for lazy loading.
0266: */
0267: public void setLazyResult(boolean isLazy) {
0268: _isLazyResult = isLazy;
0269: }
0270:
0271: /**
0272: * Returns the query string
0273: */
0274: public String getQuery() {
0275: return _sql;
0276: }
0277:
0278: /**
0279: * Returns the query string
0280: */
0281: public AbstractQuery getSelectQuery() {
0282: return _query;
0283: }
0284:
0285: /**
0286: * Initialize the parse.
0287: */
0288: private void init() {
0289: _parseIndex = 0;
0290: _unique = 0;
0291: _token = -1;
0292: _depth = 0;
0293: _parsingResult = false;
0294: _parsingFrom = false;
0295: _parsingHaving = false;
0296: _havingExpr = null;
0297: _appendResultList = null;
0298: _groupList = null;
0299: _joinFetchMap = new HashMap<AmberExpr, String>();
0300: }
0301:
0302: /**
0303: * Generates a new arg.
0304: */
0305: public int generateSQLArg() {
0306: return _sqlArgCount++;
0307: }
0308:
0309: /**
0310: * Parses the query.
0311: */
0312: public AbstractQuery parse() throws AmberException {
0313: /*
0314: _query = query;
0315: _fromList = new ArrayList<FromItem>();
0316: _pathMap = new HashMap<Expr,Expr>();
0317: _idMap = new HashMap<String,PathExpr>();
0318: _argList = new ArrayList<Expr>();
0319: _linkList = new ArrayList<LinkItem>();
0320: */
0321:
0322: init();
0323:
0324: int token = scanToken();
0325: if (token == UPDATE)
0326: return parseUpdate();
0327: else if (token == DELETE)
0328: return parseDelete();
0329:
0330: _token = token;
0331: return parseSelect(false);
0332: }
0333:
0334: private SelectQuery parseSelect(boolean innerSelect)
0335: throws QueryParseException {
0336: int oldParseIndex = _parseIndex;
0337: int oldToken = _token;
0338: FromItem.JoinSemantics oldJoinSemantics = _joinSemantics;
0339: boolean oldIsJoinFetch = _isJoinFetch;
0340: AbstractQuery oldQuery = _query;
0341: int oldDepth = _depth;
0342: AmberExpr oldHavingExpr = _havingExpr;
0343: ArrayList oldAppendResultList = _appendResultList;
0344:
0345: // Reset depth: subselect
0346: _depth = 0;
0347:
0348: _havingExpr = null;
0349: _appendResultList = null;
0350:
0351: SelectQuery query = new SelectQuery(_sql, getMetaData());
0352: query.setParentQuery(_query);
0353: _query = query;
0354:
0355: int token;
0356: while ((token = scanToken()) >= 0
0357: && ((token != FROM) || (_depth > 0))) {
0358: }
0359:
0360: // "SELECT CURRENT_DATE" does NOT have a FROM clause.
0361: boolean hasFrom = (token == FROM);
0362:
0363: _token = token;
0364:
0365: if (hasFrom) {
0366:
0367: _parsingFrom = true;
0368:
0369: do {
0370:
0371: scanToken();
0372:
0373: _isJoinFetch = false;
0374:
0375: if (token == JOIN) {
0376: if ((token = peekToken()) == FETCH) {
0377: scanToken();
0378: _isJoinFetch = true;
0379: }
0380: }
0381:
0382: FromItem from = parseFrom();
0383:
0384: token = peekToken();
0385:
0386: _joinSemantics = FromItem.JoinSemantics.UNKNOWN;
0387:
0388: if (token == INNER) {
0389: scanToken();
0390:
0391: token = peekToken();
0392:
0393: if (token != JOIN) {
0394: throw error(L.l("expected JOIN at {0}",
0395: tokenName(token)));
0396: }
0397:
0398: _joinSemantics = FromItem.JoinSemantics.INNER;
0399: } else if (token == LEFT) {
0400: scanToken();
0401:
0402: token = peekToken();
0403:
0404: if (token == OUTER) {
0405: scanToken();
0406:
0407: token = peekToken();
0408: }
0409:
0410: if (token != JOIN)
0411: throw error(L.l("expected JOIN at {0}",
0412: tokenName(token)));
0413:
0414: _joinSemantics = FromItem.JoinSemantics.OUTER;
0415: } else if (token == JOIN) {
0416: _joinSemantics = FromItem.JoinSemantics.INNER;
0417: }
0418:
0419: } while ((token == ',') || (token == JOIN));
0420:
0421: _parsingFrom = false;
0422: }
0423:
0424: int fromParseIndex = _parseIndex;
0425: int fromToken = _token;
0426:
0427: _parseIndex = oldParseIndex;
0428: _token = oldToken;
0429:
0430: ArrayList<AmberExpr> resultList = new ArrayList<AmberExpr>();
0431:
0432: _parsingResult = true;
0433:
0434: if (peekToken() == SELECT) {
0435: scanToken();
0436:
0437: if (peekToken() == DISTINCT) {
0438: scanToken();
0439: query.setDistinct(true);
0440: }
0441:
0442: String constructorName = null;
0443:
0444: if (peekToken() == NEW) {
0445:
0446: scanToken();
0447:
0448: // Scans the fully qualified constructor
0449:
0450: String s = "";
0451:
0452: boolean isDot = false;
0453:
0454: while ((token = scanToken()) != '(') {
0455:
0456: if (!isDot) {
0457: s += _lexeme;
0458: isDot = true;
0459: } else if (token == '.') {
0460: s += '.';
0461: isDot = false;
0462: } else
0463: throw error(L
0464: .l(
0465: "Constructor with SELECT NEW must be fully qualified. Expected '.' at {0}",
0466: tokenName(token)));
0467: }
0468:
0469: constructorName = s;
0470: }
0471:
0472: do {
0473:
0474: AmberExpr expr = parseExpr();
0475:
0476: if (!hasFrom) {
0477: if (!(expr instanceof DateTimeFunExpr))
0478: throw error(L
0479: .l("expected FROM clause when the select clause has not date/time functions only"));
0480: } else {
0481:
0482: // jpa/1199
0483: if (expr == null)
0484: continue;
0485:
0486: expr = expr.bindSelect(this );
0487:
0488: if (_isLazyResult) {
0489: } else if (expr instanceof PathExpr) {
0490: PathExpr pathExpr = (PathExpr) expr;
0491:
0492: FromItem rootItem = null;
0493:
0494: Type targetType = pathExpr.getTargetType();
0495:
0496: // jpa/0w24
0497: if (targetType instanceof RelatedType) {
0498: RelatedType relatedType = (RelatedType) targetType;
0499: RelatedType parentType = relatedType;
0500:
0501: while (parentType.getParentType() != null) {
0502: if (parentType.getParentType() instanceof EntityType)
0503: parentType = parentType
0504: .getParentType();
0505: else
0506: break;
0507: }
0508:
0509: // jpa/0l4b
0510: if (parentType != relatedType) {
0511: FromItem child = pathExpr
0512: .getChildFromItem();
0513:
0514: Table table = relatedType.getTable(); // parentType.getTable();
0515: ArrayList<LinkColumns> outgoingLinks = table
0516: .getOutgoingLinks();
0517:
0518: for (LinkColumns link : outgoingLinks) {
0519: if (link.getTargetTable().equals(
0520: parentType.getTable())) {
0521: rootItem = addFromItem(
0522: (EntityType) parentType,
0523: parentType.getTable());
0524:
0525: JoinExpr join = new ManyToOneJoinExpr(
0526: link, rootItem, child);
0527:
0528: rootItem.setJoinExpr(join);
0529:
0530: rootItem
0531: .setJoinSemantics(FromItem.JoinSemantics.INNER);
0532:
0533: break;
0534: }
0535: }
0536: }
0537: }
0538:
0539: expr = LoadExpr.create(pathExpr, rootItem);
0540:
0541: expr = expr.bindSelect(this );
0542: }
0543: }
0544:
0545: resultList.add(expr);
0546: } while ((token = scanToken()) == ',');
0547:
0548: query.setHasFrom(hasFrom);
0549:
0550: if (hasFrom && (constructorName != null)) {
0551:
0552: if (token != ')')
0553: throw error(L
0554: .l(
0555: "Expected ')' at {0} when calling constructor with SELECT NEW",
0556: tokenName(token)));
0557:
0558: token = scanToken();
0559:
0560: try {
0561:
0562: ClassLoader loader = Thread.currentThread()
0563: .getContextClassLoader();
0564:
0565: Class cl = Class.forName(constructorName, false,
0566: loader);
0567:
0568: query.setConstructorClass(cl);
0569:
0570: } catch (ClassNotFoundException ex) {
0571: throw error(L
0572: .l(
0573: "Unable to find class {0}. Make sure the class is fully qualified.",
0574: constructorName));
0575: }
0576: }
0577:
0578: _token = token;
0579: }
0580:
0581: if (hasFrom && (peekToken() != FROM))
0582: throw error(L.l("expected FROM at {0}", tokenName(token)));
0583:
0584: if (resultList.size() == 0) {
0585:
0586: if (_joinFetchMap.size() > 0)
0587: throw error(L
0588: .l("All associations referenced by JOIN FETCH must belong to an entity that is returned as a result of the query"));
0589:
0590: ArrayList<FromItem> fromList = _query.getFromList();
0591:
0592: if (fromList.size() > 0) {
0593: FromItem fromItem = fromList.get(0);
0594:
0595: AmberExpr expr = fromItem.getIdExpr();
0596:
0597: if (_isLazyResult) {
0598: } else if (expr instanceof PathExpr) {
0599: PathExpr pathExpr = (PathExpr) expr;
0600:
0601: expr = LoadExpr.create(pathExpr);
0602: expr = expr.bindSelect(this );
0603: }
0604:
0605: resultList.add(expr);
0606: }
0607: } else if (hasFrom) {
0608:
0609: int size = resultList.size();
0610:
0611: int matches = 0;
0612:
0613: for (int i = 0; i < size; i++) {
0614:
0615: AmberExpr expr = resultList.get(i);
0616:
0617: if (expr instanceof LoadEntityExpr) {
0618:
0619: expr = ((LoadEntityExpr) expr).getExpr();
0620:
0621: if (_joinFetchMap.get(expr) != null) {
0622: matches++;
0623: }
0624: }
0625: }
0626:
0627: if (matches < _joinFetchMap.size())
0628: throw error(L
0629: .l("All associations referenced by JOIN FETCH must belong to an entity that is returned as a result of the query"));
0630:
0631: }
0632:
0633: // jpa/1199
0634: if (_appendResultList != null)
0635: resultList.addAll(_appendResultList);
0636:
0637: query.setResultList(resultList);
0638:
0639: _parsingResult = false;
0640:
0641: _parseIndex = fromParseIndex;
0642: _token = fromToken;
0643:
0644: token = peekToken();
0645:
0646: boolean hasWhere = false;
0647:
0648: if (token == WHERE) {
0649: scanToken();
0650:
0651: hasWhere = true;
0652:
0653: AmberExpr expr = parseExpr();
0654:
0655: // jpa/119l: WHERE SIZE() is moved to HAVING COUNT()
0656: if (expr != null) {
0657: expr = expr.createBoolean();
0658:
0659: query.setWhere(expr.bindSelect(this ));
0660: }
0661: }
0662:
0663: boolean hasGroupBy = false;
0664:
0665: ArrayList<AmberExpr> groupList = _groupList;
0666:
0667: token = peekToken();
0668: if (token == GROUP) {
0669: scanToken();
0670:
0671: if (peekToken() == BY) {
0672: scanToken();
0673: hasGroupBy = true;
0674: }
0675:
0676: if (groupList == null)
0677: groupList = new ArrayList<AmberExpr>();
0678:
0679: while (true) {
0680: // jpa/0w23
0681: AmberExpr groupExpr = parseExpr();
0682:
0683: groupExpr = groupExpr.bindSelect(this );
0684:
0685: if (groupExpr instanceof PathExpr) {
0686: // jpa/119n
0687:
0688: PathExpr pathExpr = (PathExpr) groupExpr;
0689:
0690: groupExpr = LoadExpr.create(pathExpr);
0691:
0692: groupExpr = groupExpr.bindSelect(this );
0693: }
0694:
0695: groupList.add(groupExpr);
0696:
0697: if (peekToken() == ',')
0698: scanToken();
0699: else
0700: break;
0701: }
0702:
0703: query.setGroupList(groupList);
0704:
0705: // Reset temp group list after parsing subselect.
0706: _groupList = null;
0707: }
0708:
0709: token = peekToken();
0710: if (token == HAVING) {
0711:
0712: if (!hasGroupBy)
0713: throw error(L
0714: .l("Use of HAVING without GROUP BY is not currently supported"));
0715:
0716: _parsingHaving = true;
0717:
0718: scanToken();
0719:
0720: AmberExpr havingExpr = parseExpr();
0721:
0722: // jpa/119l: SIZE()
0723: if (_havingExpr != null)
0724: havingExpr = AndExpr.create(havingExpr, _havingExpr);
0725:
0726: query
0727: .setHaving(havingExpr.createBoolean().bindSelect(
0728: this ));
0729:
0730: _parsingHaving = false;
0731: } else if (hasWhere && _havingExpr != null) { // jpa/1199, jpa/119l
0732: query.setHaving(_havingExpr.createBoolean()
0733: .bindSelect(this ));
0734: }
0735:
0736: token = peekToken();
0737: if (token == ORDER) {
0738: scanToken();
0739:
0740: if (peekToken() == BY)
0741: scanToken();
0742:
0743: ArrayList<AmberExpr> orderList = new ArrayList<AmberExpr>();
0744: ArrayList<Boolean> ascList = new ArrayList<Boolean>();
0745:
0746: while (true) {
0747: AmberExpr expr = parseExpr();
0748:
0749: // jpa/1114
0750: if (isCollectionExpr(expr))
0751: throw error(L.l(
0752: "Unexpected collection at ORDER BY '{0}'.",
0753: expr.getClass().getName()));
0754:
0755: expr = expr.bindSelect(this );
0756:
0757: orderList.add(expr);
0758:
0759: if (peekToken() == DESC) {
0760: scanToken();
0761: ascList.add(Boolean.FALSE);
0762: } else if (peekToken() == ASC) {
0763: scanToken();
0764: ascList.add(Boolean.TRUE);
0765: } else
0766: ascList.add(Boolean.TRUE);
0767:
0768: if (peekToken() == ',')
0769: scanToken();
0770: else
0771: break;
0772: }
0773:
0774: query.setOrderList(orderList, ascList);
0775: }
0776:
0777: token = peekToken();
0778:
0779: if (token == OFFSET) {
0780: scanToken();
0781:
0782: token = scanToken();
0783: if (token != INTEGER)
0784: throw error(L.l("Expected INTEGER at {0}",
0785: tokenName(token)));
0786:
0787: int offset = Integer.parseInt(_lexeme);
0788:
0789: token = peekToken();
0790:
0791: query.setOffset(offset);
0792: }
0793:
0794: if (token == LIMIT) {
0795: scanToken();
0796:
0797: token = scanToken();
0798: if (token != INTEGER)
0799: throw error(L.l("Expected INTEGER at {0}",
0800: tokenName(token)));
0801:
0802: int limit = Integer.parseInt(_lexeme);
0803: query.setLimit(limit);
0804:
0805: token = peekToken();
0806: }
0807:
0808: if (!innerSelect) {
0809: query.setJoinFetchMap(_joinFetchMap);
0810:
0811: if (token > 0)
0812: throw error(L.l("expected end of query at {0}",
0813: tokenName(token)));
0814:
0815: if (!query.setArgList(_argList.toArray(new ArgExpr[_argList
0816: .size()])))
0817: throw error(L
0818: .l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
0819:
0820: }
0821:
0822: query.init();
0823:
0824: _joinSemantics = oldJoinSemantics;
0825: _isJoinFetch = oldIsJoinFetch;
0826: _query = oldQuery;
0827: _depth = oldDepth;
0828: _havingExpr = oldHavingExpr;
0829: _appendResultList = oldAppendResultList;
0830:
0831: return query;
0832: }
0833:
0834: private AbstractQuery parseUpdate() throws QueryParseException {
0835: UpdateQuery query = new UpdateQuery(_sql, getMetaData());
0836: _query = query;
0837:
0838: FromItem fromItem = parseFrom();
0839:
0840: int token = scanToken();
0841: if (token != SET)
0842: throw error(L.l("expected 'SET' at {0}", tokenName(token)));
0843:
0844: ArrayList<AmberExpr> fields = new ArrayList<AmberExpr>();
0845: ArrayList<AmberExpr> values = new ArrayList<AmberExpr>();
0846:
0847: parseSetValues(fromItem, fields, values);
0848:
0849: query.setFieldList(fields);
0850: query.setValueList(values);
0851:
0852: token = scanToken();
0853: if (token == WHERE) {
0854: AmberExpr expr = parseExpr();
0855:
0856: query.setWhere(expr.createBoolean().bindSelect(this ));
0857:
0858: token = scanToken();
0859: }
0860:
0861: if (token >= 0)
0862: throw error(L.l("'{0}' not expected at end of query.",
0863: tokenName(token)));
0864:
0865: if (!query.setArgList(_argList.toArray(new ArgExpr[_argList
0866: .size()])))
0867: throw error(L
0868: .l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
0869:
0870: query.init();
0871:
0872: return query;
0873: }
0874:
0875: private AbstractQuery parseDelete() throws QueryParseException {
0876: DeleteQuery query = new DeleteQuery(_sql, getMetaData());
0877: _query = query;
0878:
0879: int token = peekToken();
0880: if (token == FROM)
0881: scanToken();
0882:
0883: FromItem fromItem = parseFrom();
0884:
0885: token = scanToken();
0886: if (token == WHERE) {
0887: query
0888: .setWhere(parseExpr().createBoolean().bindSelect(
0889: this ));
0890:
0891: token = scanToken();
0892: }
0893:
0894: if (token >= 0)
0895: throw error(L.l("'{0}' not expected at end of query.",
0896: tokenName(token)));
0897:
0898: if (!query.setArgList(_argList.toArray(new ArgExpr[_argList
0899: .size()])))
0900: throw error(L
0901: .l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
0902:
0903: query.init();
0904:
0905: return query;
0906: }
0907:
0908: /**
0909: * Parses the set values.
0910: */
0911: private void parseSetValues(FromItem fromItem,
0912: ArrayList<AmberExpr> fields, ArrayList<AmberExpr> values)
0913: throws QueryParseException {
0914: EntityType entity = fromItem.getEntityType();
0915:
0916: int token = -1;
0917:
0918: do {
0919:
0920: token = scanToken();
0921:
0922: AmberExpr expr = null;
0923:
0924: String name = _lexeme.toString();
0925:
0926: IdExpr tableExpr = getIdentifier(name);
0927:
0928: if (tableExpr != null) {
0929: expr = parsePath(tableExpr);
0930: } else {
0931:
0932: tableExpr = fromItem.getIdExpr();
0933:
0934: AmberExpr next = tableExpr.createField(this , name);
0935:
0936: if (next instanceof PathExpr)
0937: expr = addPath((PathExpr) next);
0938: else if (next != null)
0939: expr = next;
0940: }
0941:
0942: expr = expr.bindSelect(this );
0943:
0944: fields.add(expr);
0945:
0946: if ((token = peekToken()) != EQ)
0947: throw error(L
0948: .l("expected '=' at {0}", tokenName(token)));
0949:
0950: scanToken();
0951:
0952: // jpa/1222 expr = parseSimpleTerm();
0953: expr = parseConcatExpr();
0954:
0955: if (expr.hasRelationship())
0956: throw error(L
0957: .l(
0958: "UPDATE cannot set values with relationships. Unexpected path expression at {0}",
0959: expr));
0960:
0961: expr = expr.bindSelect(this );
0962:
0963: values.add(expr);
0964:
0965: } while ((token = scanToken()) == ',');
0966:
0967: _token = token;
0968: }
0969:
0970: /**
0971: * Parses the FROM block. parseFrom's effect is to populate the
0972: * core identifiers.
0973: *
0974: * <pre>
0975: * from-item ::= schema AS? IDENTIFIER
0976: * </pre>
0977: */
0978: private FromItem parseFrom() throws QueryParseException {
0979: SchemaExpr schema = parseSchema();
0980:
0981: String id;
0982:
0983: int token = peekToken();
0984: if (token == AS) {
0985: scanToken();
0986: token = peekToken();
0987: id = parseIdentifier();
0988: } else if (token == IDENTIFIER)
0989: id = parseIdentifier();
0990: else {
0991: // jpa/116c
0992: if (schema instanceof OneToManySchemaExpr)
0993: id = createTableName();
0994: else
0995: id = schema.getTailName();
0996: }
0997:
0998: /*
0999: AmberEntityHome home = _persistenceUnit.getHomeBySchema(schema);
1000:
1001: if (home == null)
1002: throw error(L.l("`{0}' is an unknown persistent class.",
1003: schema));
1004: */
1005:
1006: FromItem item = schema.addFromItem(this , id);
1007:
1008: if (schema instanceof EmbeddedSchemaExpr) {
1009:
1010: // jpa/0w22
1011:
1012: EmbeddedSchemaExpr embeddedSchema = (EmbeddedSchemaExpr) schema;
1013:
1014: _query.addEmbeddedAlias(id, embeddedSchema.getExpr()); // pathString);
1015: }
1016:
1017: // jpa/114h
1018: item.setJoinSemantics(_joinSemantics);
1019:
1020: return item;
1021: }
1022:
1023: /**
1024: * Adds a new FromItem.
1025: */
1026: public FromItem addFromItem(Table table) {
1027: return addFromItem(null, table, createTableName());
1028: }
1029:
1030: /**
1031: * Adds a new FromItem.
1032: */
1033: public FromItem addFromItem(EntityType entityType, Table table) {
1034: return addFromItem(entityType, table, createTableName());
1035: }
1036:
1037: /**
1038: * Returns a unique table name
1039: */
1040: public String createTableName() {
1041: return "caucho" + _unique++;
1042: }
1043:
1044: /**
1045: * Adds a new FromItem.
1046: */
1047: public FromItem addFromItem(Table table, String id) {
1048: return addFromItem(null, table, id);
1049: }
1050:
1051: /**
1052: * Adds a new FromItem.
1053: */
1054: public FromItem addFromItem(EntityType entityType, Table table,
1055: String id) {
1056: if (id == null)
1057: id = createTableName();
1058:
1059: FromItem item = _query.createFromItem(entityType, table, id);
1060:
1061: item.setJoinSemantics(_joinSemantics);
1062:
1063: return item;
1064: }
1065:
1066: /**
1067: * Adds a new FromItem.
1068: */
1069: public FromItem createDependentFromItem(FromItem item,
1070: LinkColumns link) {
1071: item = _query.createDependentFromItem(item, link,
1072: createTableName());
1073:
1074: item.setJoinSemantics(_joinSemantics);
1075:
1076: return item;
1077: }
1078:
1079: /**
1080: * Adds a new link
1081: */
1082: void addLink(AmberExpr expr) {
1083: // _andExpr.add(expr);
1084: throw new IllegalStateException();
1085: }
1086:
1087: /**
1088: * Adds an entity path
1089: */
1090: public PathExpr addPath(PathExpr path) {
1091: PathExpr oldPath = _pathMap.get(path);
1092:
1093: if (oldPath != null)
1094: return oldPath;
1095:
1096: _pathMap.put(path, path);
1097:
1098: return path;
1099: }
1100:
1101: /**
1102: * Adds a new argument
1103: */
1104: public void addArg(ArgExpr arg) {
1105: _argList.add(arg);
1106: }
1107:
1108: /**
1109: * Parses a schema.
1110: */
1111: private SchemaExpr parseSchema() throws QueryParseException {
1112: int token = peekToken();
1113: boolean isIn = token == IN;
1114:
1115: if (isIn) {
1116:
1117: scanToken();
1118:
1119: _joinSemantics = FromItem.JoinSemantics.INNER;
1120:
1121: if ((token = scanToken()) != '(')
1122: throw error(L.l("expected '(' at '{0}'",
1123: tokenName(token)));
1124: }
1125:
1126: String name = parseIdentifier();
1127:
1128: SchemaExpr schema = null;
1129:
1130: if (!isIn) {
1131: AmberEntityHome home = _persistenceUnit
1132: .getHomeBySchema(name);
1133:
1134: if (home != null) {
1135: EntityType type = home.getEntityType();
1136:
1137: schema = new TableIdExpr(home.getEntityType(), type
1138: .getTable().getName());
1139: }
1140: }
1141:
1142: IdExpr id = null;
1143:
1144: if (schema == null) {
1145: id = getIdentifier(name);
1146:
1147: if (id != null)
1148: schema = new FromIdSchemaExpr(id);
1149: }
1150:
1151: if (!isIn && schema == null) {
1152: while (peekToken() == '.') {
1153: scanToken();
1154: String segment = parseIdentifier();
1155:
1156: name = name + '.' + segment;
1157:
1158: AmberEntityHome home = _persistenceUnit
1159: .getHomeBySchema(name);
1160:
1161: if (home != null) {
1162: schema = new TableIdExpr(home.getEntityType(), name);
1163: break;
1164: }
1165: }
1166: }
1167:
1168: if (schema == null) {
1169: throw error(L.l("'{0}' is an unknown entity.", name));
1170: }
1171:
1172: name = "";
1173: boolean isFirst = true;
1174:
1175: while (peekToken() == '.') {
1176: scanToken();
1177: String segment = parseIdentifier();
1178:
1179: if (isFirst) {
1180: name += segment;
1181: isFirst = false;
1182: } else
1183: name += "." + segment;
1184:
1185: schema = schema.createField(this , segment);
1186: }
1187:
1188: if (_isJoinFetch && (!name.equals(""))) {
1189: _joinFetchMap.put(id, name);
1190: }
1191:
1192: if (isIn) {
1193: if ((token = scanToken()) != ')')
1194: throw error(L.l("expected ')' at '{0}'",
1195: tokenName(token)));
1196: }
1197:
1198: return schema;
1199: }
1200:
1201: /**
1202: * Parses an expression.
1203: */
1204: private AmberExpr parseExpr() throws QueryParseException {
1205: if (peekToken() == SELECT) {
1206: SelectQuery select = parseSelect(true);
1207:
1208: return new SubSelectExpr(select);
1209: }
1210:
1211: AmberExpr expr = parseOrExpr();
1212:
1213: return expr; // .bindSelect(this);
1214: }
1215:
1216: /**
1217: * Parses an or expression.
1218: */
1219: private AmberExpr parseOrExpr() throws QueryParseException {
1220: AmberExpr expr = parseAndExpr();
1221: OrExpr orExpr = null;
1222:
1223: while (peekToken() == OR) {
1224: scanToken();
1225:
1226: if (orExpr == null) {
1227: orExpr = new OrExpr();
1228: orExpr.add(expr);
1229: }
1230:
1231: AmberExpr andExpr = parseAndExpr();
1232:
1233: if (andExpr == null)
1234: continue;
1235:
1236: orExpr.add(andExpr);
1237: }
1238:
1239: return orExpr == null ? expr : orExpr;
1240: }
1241:
1242: /**
1243: * Parses an and expression.
1244: */
1245: private AmberExpr parseAndExpr() throws QueryParseException {
1246: AmberExpr expr = parseNotExpr();
1247: AndExpr andExpr = null;
1248:
1249: while (peekToken() == AND) {
1250: scanToken();
1251:
1252: if (andExpr == null) {
1253: andExpr = new AndExpr();
1254: andExpr.add(expr);
1255: }
1256:
1257: AmberExpr notExpr = parseNotExpr();
1258:
1259: if (notExpr == null)
1260: continue;
1261:
1262: andExpr.add(notExpr);
1263: }
1264:
1265: return andExpr == null ? expr : andExpr;
1266: }
1267:
1268: /**
1269: * Parses a NOT expression.
1270: *
1271: */
1272: private AmberExpr parseNotExpr() throws QueryParseException {
1273: AmberExpr expr;
1274:
1275: if (peekToken() == NOT) {
1276: scanToken();
1277:
1278: expr = new UnaryExpr(NOT, parseCmpExpr());
1279: } else
1280: expr = parseCmpExpr();
1281:
1282: // jpa/1199, jpa/119l
1283:
1284: if (_parsingResult || _parsingHaving)
1285: return expr;
1286:
1287: if (!_isSizeFunExpr)
1288: return expr;
1289:
1290: if (_havingExpr == null) {
1291: _havingExpr = expr;
1292: } else if (expr != null) { // jpa/1199
1293: _havingExpr = AndExpr.create(_havingExpr, expr);
1294: }
1295:
1296: return null;
1297: }
1298:
1299: /**
1300: * Parses a comparison expression.
1301: *
1302: * <pre>
1303: * cmp-expr ::= add-expr '=' add-expr is-term?
1304: * ::= add-expr 'NOT'? 'BETWEEN' add-expr 'AND' add-expr is-term?
1305: * ::= add-expr 'NOT'? 'LIKE' string ('ESCAPE' string)? is-term?
1306: * ::= add-expr 'NOT'? 'IN' ('lit-1', ..., 'lit-n')
1307: * ::= add-expr
1308: * </pre>
1309: *
1310: * @return the parsed expression
1311: */
1312: private AmberExpr parseCmpExpr() throws QueryParseException {
1313: AmberExpr expr = parseConcatExpr();
1314:
1315: int token = peekToken();
1316: boolean isNot = false;
1317:
1318: if (token == NOT) {
1319: scanToken();
1320: isNot = true;
1321: token = peekToken();
1322:
1323: if (token != BETWEEN && token != LIKE && token != MEMBER
1324: && token != IN)
1325: throw error(L.l("'NOT' is not expected here."));
1326: }
1327:
1328: if (token >= EQ && token <= GE) {
1329: scanToken();
1330:
1331: AmberExpr concatExpr = parseConcatExpr();
1332:
1333: return parseIs(new BinaryExpr(token, expr, concatExpr));
1334: } else if (token == BETWEEN) {
1335: scanToken();
1336:
1337: AmberExpr min = parseConcatExpr();
1338:
1339: if ((token = scanToken()) != AND)
1340: throw error(L.l("Expected 'AND' at {0}",
1341: tokenName(token)));
1342:
1343: AmberExpr max = parseConcatExpr();
1344:
1345: // jpa/106a
1346: if (!isCompatibleExpression(expr, min))
1347: throw error(L
1348: .l(
1349: "Expected compatible expression at {0} BETWEEN {1}",
1350: expr, min));
1351:
1352: if (!isCompatibleExpression(expr, max))
1353: throw error(L
1354: .l(
1355: "Expected compatible expression at BETWEEN {0} AND {1}",
1356: min, max));
1357:
1358: return new BetweenExpr(expr, min, max, isNot);
1359: } else if (token == LIKE) {
1360: scanToken();
1361:
1362: AmberExpr pattern = parseConcatExpr();
1363:
1364: // jpa/1075
1365: if (pattern instanceof LiteralExpr) {
1366: LiteralExpr literalExpr = (LiteralExpr) pattern;
1367:
1368: if (literalExpr.getJavaType() != String.class)
1369: throw error(L.l("Expected string at {0}", pattern));
1370: } else if (!(pattern instanceof ArgExpr)) // jpa/1076
1371: throw error(L.l("Expected string at {0}", pattern));
1372:
1373: String escape = null;
1374: if (peekToken() == ESCAPE) {
1375: scanToken();
1376:
1377: if ((token = scanToken()) != STRING)
1378: throw error(L.l("Expected string at {0}",
1379: tokenName(token)));
1380:
1381: escape = _lexeme.toString();
1382: }
1383:
1384: return parseIs(new LikeExpr(expr, pattern, escape, isNot));
1385: } else if (token == IN) {
1386: scanToken();
1387: token = scanToken();
1388:
1389: if (token != '(')
1390: throw error(L.l("Expected '(' after IN at {0}",
1391: tokenName(token)));
1392:
1393: ArrayList<AmberExpr> args = new ArrayList<AmberExpr>();
1394: while ((token = peekToken()) > 0 && token != ')') {
1395: AmberExpr arg = parseExpr();
1396:
1397: args.add(arg);
1398:
1399: token = peekToken();
1400: if (token == ',') {
1401: scanToken();
1402: token = peekToken();
1403: }
1404: }
1405:
1406: if (peekToken() != ')')
1407: throw error(L.l("Expected ')' after IN at {0}",
1408: tokenName(token)));
1409:
1410: scanToken();
1411:
1412: if (expr instanceof IdExpr) {
1413: IdExpr idExpr = (IdExpr) expr;
1414:
1415: // jpa/1174
1416: if (idExpr.getFromItem().isEntityType())
1417: throw error(L.l("Unexpected entity at '{0} IN'",
1418: expr));
1419: }
1420:
1421: return new InExpr(expr, args, isNot);
1422: } else if (token == MEMBER) {
1423: scanToken();
1424:
1425: token = peekToken();
1426: if (token == OF)
1427: token = scanToken();
1428:
1429: AmberExpr collection = parseExpr();
1430:
1431: // jpa/10c8
1432: if (expr instanceof ArgExpr) {
1433: addArg((ArgExpr) expr);
1434: } else if (!(expr instanceof PathExpr))
1435: throw error(L
1436: .l("MEMBER OF requires an entity-valued item."));
1437:
1438: if (!isCollectionExpr(collection))
1439: throw error(L
1440: .l(
1441: "MEMBER OF requires an entity-valued collection at '{0}'.",
1442: collection.getClass().getName()));
1443:
1444: return parseIs(MemberExpr.create(this , expr, collection,
1445: isNot));
1446: } else
1447: return parseIs(expr);
1448: }
1449:
1450: private AmberExpr parseIs(AmberExpr expr)
1451: throws QueryParseException {
1452: int token = peekToken();
1453:
1454: if (token != IS)
1455: return expr;
1456:
1457: scanToken();
1458:
1459: boolean isNot = false;
1460: token = scanToken();
1461:
1462: if (token == NOT) {
1463: isNot = true;
1464: token = scanToken();
1465: }
1466:
1467: if (token == NULL) {
1468: if (expr instanceof KeyColumnExpr)
1469: expr = ((KeyColumnExpr) expr).getParent();
1470: else if (expr instanceof IdExpr) {
1471: IdExpr idExpr = (IdExpr) expr;
1472:
1473: // jpa/1093
1474: if (idExpr.getFromItem().isEntityType())
1475: throw error(L.l("Unexpected entity at '{0} IS'",
1476: expr));
1477: }
1478:
1479: if (isNot)
1480: return new UnaryExpr(NOT_NULL, expr);
1481: else
1482: return new UnaryExpr(NULL, expr);
1483: } else if (token == EMPTY) {
1484: if (!isCollectionExpr(expr))
1485: throw error(L
1486: .l(
1487: "IS EMPTY requires an entity-valued collection at '{0}'.",
1488: expr.getClass().getName()));
1489:
1490: expr = new EmptyExpr(expr);
1491:
1492: if (!isNot)
1493: expr = new UnaryExpr(NOT, expr);
1494:
1495: return expr;
1496: } else
1497: throw error(L.l("expected NULL at '{0}'", tokenName(token)));
1498: }
1499:
1500: /**
1501: * Parses a concat expression.
1502: */
1503: private AmberExpr parseConcatExpr() throws QueryParseException {
1504: AmberExpr expr = parseAddExpr();
1505:
1506: while (true) {
1507: int token = peekToken();
1508:
1509: switch (token) {
1510: case CONCAT_OP:
1511: scanToken();
1512:
1513: ArrayList<AmberExpr> args = new ArrayList<AmberExpr>();
1514:
1515: args.add(expr);
1516: args.add(parseAddExpr());
1517:
1518: expr = ConcatFunExpr.create(this , args);
1519: break;
1520: default:
1521: return expr;
1522: }
1523: }
1524: }
1525:
1526: /**
1527: * Parses an add expression.
1528: */
1529: private AmberExpr parseAddExpr() throws QueryParseException {
1530: AmberExpr expr = parseMulExpr();
1531:
1532: while (true) {
1533: int token = peekToken();
1534:
1535: switch (token) {
1536: case '+':
1537: case '-':
1538: scanToken();
1539: expr = new BinaryExpr(token, expr, parseMulExpr());
1540: break;
1541: default:
1542: return expr;
1543: }
1544: }
1545: }
1546:
1547: /**
1548: * Parses a mul expression.
1549: */
1550: private AmberExpr parseMulExpr() throws QueryParseException {
1551: AmberExpr expr = parseTerm();
1552:
1553: while (true) {
1554: int token = peekToken();
1555:
1556: switch (token) {
1557: case '*':
1558: case '/':
1559: scanToken();
1560: expr = new BinaryExpr(token, expr, parseTerm());
1561: break;
1562: default:
1563: return expr;
1564: }
1565: }
1566: }
1567:
1568: /**
1569: * Parses a term
1570: *
1571: * <pre>
1572: * term ::= - term
1573: * ::= + term
1574: * ::= NOT term
1575: * </pre>
1576: */
1577: private AmberExpr parseTerm() throws QueryParseException {
1578: int token = peekToken();
1579:
1580: switch (token) {
1581: case '+':
1582: case '-':
1583: case NOT:
1584: scanToken();
1585:
1586: return new UnaryExpr(token, parseTerm());
1587:
1588: default:
1589: return parseSimpleTerm();
1590: }
1591: }
1592:
1593: /**
1594: * Parses a simple term
1595: *
1596: * <pre>
1597: * term ::= INTEGER | LONG | DOUBLE | STRING
1598: * ::= THIS
1599: * ::= IDENTIFIER
1600: * ::= IDENTIFIER '(' args ')'
1601: * ::= '(' expr ')'
1602: * </pre>
1603: */
1604: private AmberExpr parseSimpleTerm() throws QueryParseException {
1605: int token = scanToken();
1606:
1607: switch (token) {
1608: case IDENTIFIER:
1609: case LOCATE:
1610: case LENGTH:
1611: case MAX:
1612: case MIN:
1613: case SUM:
1614: case ABS:
1615: case SQRT:
1616: case MOD:
1617: case SIZE:
1618: case CONCAT:
1619: case LOWER:
1620: case UPPER:
1621: case SUBSTRING:
1622: case TRIM: {
1623: String name = _lexeme.toString();
1624:
1625: if (peekToken() != '(') {
1626: // Either IdExpr or EmbeddedExpr
1627: AbstractPathExpr tableExpr = getIdentifier(name);
1628:
1629: if (tableExpr == null) {
1630: // jpa/0w22
1631: tableExpr = getEmbeddedAlias(name);
1632: }
1633:
1634: if (tableExpr == null) {
1635: // jpa/11z6
1636: AmberExpr amberExpr = parseEnum(name);
1637:
1638: if (amberExpr != null)
1639: return amberExpr;
1640: }
1641:
1642: if (tableExpr != null) {
1643: AmberExpr amberExpr = parsePath(tableExpr);
1644:
1645: return amberExpr;
1646: }
1647:
1648: if (_query.getFromList().size() == 0)
1649: throw error(L
1650: .l("Expected a FROM clause before '{0}'",
1651: name));
1652:
1653: FromItem fromItem = _query.getFromList().get(0);
1654:
1655: tableExpr = fromItem.getIdExpr();
1656:
1657: AmberExpr next = tableExpr.createField(this , name);
1658:
1659: if (next instanceof PathExpr)
1660: return addPath((PathExpr) next);
1661: else if (next != null)
1662: return next;
1663:
1664: throw error(L.l("'{0}' is an unknown table or column",
1665: name));
1666: } else {
1667:
1668: name = name.toLowerCase();
1669:
1670: // EXISTS | ALL | ANY | SOME
1671: if (name.equals("exists") || name.equals("all")
1672: || name.equals("any") || name.equals("some")) {
1673:
1674: scanToken();
1675:
1676: if (peekToken() != SELECT && peekToken() != FROM)
1677: throw error(L.l(name.toUpperCase()
1678: + " requires '(SELECT'"));
1679:
1680: SelectQuery select = parseSelect(true);
1681:
1682: if (peekToken() != ')')
1683: throw error(L.l(name.toUpperCase()
1684: + " requires ')'"));
1685:
1686: scanToken();
1687:
1688: ArrayList<FromItem> parentFromList;
1689: parentFromList = select.getParentQuery()
1690: .getFromList();
1691:
1692: // jpa/1178
1693: select.getFromList().addAll(0, parentFromList);
1694:
1695: if (name.equals("exists"))
1696: return new ExistsExpr(select);
1697: else if (name.equals("all"))
1698: return new AllExpr(select);
1699: else
1700: // SOME is a synonymous with ANY
1701: return new AnyExpr(select);
1702: } else {
1703: return parseFunction(name, token);
1704: }
1705: }
1706: }
1707:
1708: case CURRENT_DATE:
1709: case CURRENT_TIME:
1710: case CURRENT_TIMESTAMP: {
1711: String name = _lexeme.toString();
1712:
1713: return parseFunction(name, token);
1714: }
1715:
1716: case FALSE:
1717: return new LiteralExpr(this , _lexeme, boolean.class);
1718:
1719: case TRUE:
1720: return new LiteralExpr(this , _lexeme, boolean.class);
1721:
1722: case NULL:
1723: return new NullExpr();
1724:
1725: case INTEGER:
1726: return new LiteralExpr(this , _lexeme, int.class);
1727:
1728: case LONG:
1729: return new LiteralExpr(this , _lexeme, long.class);
1730:
1731: case DOUBLE:
1732: return new LiteralExpr(this , _lexeme, double.class);
1733:
1734: case STRING:
1735: return new LiteralExpr(this , _lexeme, String.class);
1736:
1737: case ARG: {
1738: ArgExpr arg = new ArgExpr(this , Integer.parseInt(_lexeme));
1739: /*
1740: if (_addArgToQuery)
1741: addArg(arg);
1742: */
1743: return arg;
1744: }
1745:
1746: case NAMED_ARG: {
1747: ArgExpr arg = new ArgExpr(this , _lexeme, _parameterCount);
1748: return arg;
1749: }
1750:
1751: /*
1752: case THIS:
1753: {
1754: if (_thisExpr == null) {
1755: _thisExpr = new IdExpr(this, "caucho_this", _bean);
1756: addFromItem("caucho_this", _bean.getSQLTable());
1757: _argList.add(0, new ThisExpr(this, _bean));
1758: }
1759:
1760: return _thisExpr;
1761: }
1762: */
1763:
1764: case '(':
1765: AmberExpr expr = parseExpr();
1766: if ((token = scanToken()) != ')')
1767: throw error(L
1768: .l("expected `)' at {0}", tokenName(token)));
1769:
1770: return expr;
1771:
1772: default:
1773: throw error(L.l("expected term at {0}", tokenName(token)));
1774: }
1775: }
1776:
1777: /**
1778: * Parses a path
1779: *
1780: * <pre>
1781: * path ::= IDENTIFIER
1782: * ::= path . IDENTIFIER
1783: * </pre>
1784: */
1785: private AmberExpr parsePath(PathExpr path)
1786: throws QueryParseException {
1787: while (peekToken() == '.') {
1788: scanToken();
1789:
1790: String field = parseIdentifier();
1791:
1792: AmberExpr next = path.createField(this , field);
1793:
1794: if (next == null)
1795: throw error(L.l("'{0}' does not have a field '{1}'",
1796: path, field));
1797:
1798: if (!(next instanceof PathExpr))
1799: return next;
1800:
1801: PathExpr nextPath = addPath((PathExpr) next);
1802:
1803: if (peekToken() == '[') {
1804: scanToken();
1805:
1806: AmberExpr index = parseExpr();
1807:
1808: next = nextPath.createArray(index);
1809:
1810: if (next == null)
1811: throw error(L.l(
1812: "'{0}' does not have a map field '{1}'",
1813: path, field));
1814:
1815: if (peekToken() != ']') {
1816: throw error(L.l("expected ']' at '{0}'",
1817: tokenName(peekToken())));
1818: }
1819:
1820: scanToken();
1821: }
1822:
1823: if (next instanceof PathExpr)
1824: path = addPath((PathExpr) next);
1825: else
1826: return next;
1827: }
1828:
1829: return path;
1830: }
1831:
1832: /**
1833: * Parses a enum value
1834: *
1835: * <pre>
1836: * enum ::= (IDENTIFIER '.')+ IDENTIFIER
1837: * </pre>
1838: */
1839: private EnumExpr parseEnum(String head) throws QueryParseException {
1840: CharBuffer cb = CharBuffer.allocate();
1841:
1842: int token;
1843:
1844: while ((token = scanToken()) == '.') {
1845:
1846: if (cb.length() > 0)
1847: cb.append('.');
1848:
1849: cb.append(head);
1850:
1851: token = scanToken();
1852:
1853: if (token != IDENTIFIER)
1854: throw error(L
1855: .l(
1856: "expected identifier for enumerated type {0} at {1}",
1857: cb.toString(), tokenName(token)));
1858:
1859: head = _lexeme.toString();
1860: }
1861:
1862: int value = -1;
1863: Class cl = null;
1864:
1865: try {
1866: ClassLoader loader = Thread.currentThread()
1867: .getContextClassLoader();
1868:
1869: cl = Class.forName(cb.toString(), false, loader);
1870:
1871: Enum enumValue = Enum.valueOf(cl, head);
1872:
1873: value = enumValue.ordinal();
1874: } catch (ClassNotFoundException e) {
1875: // Not an error; only this is not a enum.
1876: // Continue - see parseSimpleTerm().
1877: return null;
1878: }
1879:
1880: return new EnumExpr(cl, head, value);
1881: }
1882:
1883: /**
1884: * Parses a function
1885: *
1886: * <pre>
1887: * fun ::= IDENTIFIER ( expr* )
1888: * ::= IDENTIFIER ( DISTINCT expr* )
1889: * </pre>
1890: */
1891: private AmberExpr parseFunction(String id, int functionToken)
1892: throws QueryParseException {
1893: // Function with no arguments.
1894: switch (functionToken) {
1895:
1896: case CURRENT_DATE:
1897: return CurrentDateFunExpr.create(this );
1898:
1899: case CURRENT_TIME:
1900: return CurrentTimeFunExpr.create(this );
1901:
1902: case CURRENT_TIMESTAMP:
1903: return CurrentTimestampFunExpr.create(this );
1904: }
1905:
1906: // Function with arguments.
1907:
1908: scanToken();
1909:
1910: // Example: "'c'"
1911: AmberExpr trimChar = null;
1912: TrimFunExpr.TrimSemantics trimSemantics = TrimFunExpr.TrimSemantics.BOTH;
1913: boolean distinct = false;
1914:
1915: ArrayList<AmberExpr> args = new ArrayList<AmberExpr>();
1916:
1917: if (functionToken == TRIM) {
1918:
1919: switch (peekToken()) {
1920:
1921: case LEADING:
1922: trimSemantics = TrimFunExpr.TrimSemantics.LEADING;
1923: scanToken();
1924: break;
1925:
1926: case TRAILING:
1927: trimSemantics = TrimFunExpr.TrimSemantics.TRAILING;
1928: scanToken();
1929: break;
1930:
1931: case BOTH:
1932: scanToken();
1933: break;
1934:
1935: // default: [BOTH], but no scanToken().
1936: }
1937:
1938: AmberExpr arg = null;
1939:
1940: if (peekToken() != FROM) {
1941:
1942: arg = parseExpr();
1943:
1944: if (arg instanceof LiteralExpr) {
1945:
1946: String v = ((LiteralExpr) arg).getValue();
1947:
1948: if (v.length() != 3) // "'c'"
1949: throw error(L
1950: .l(
1951: "expected a single char expression for TRIM at {0}",
1952: v));
1953: }
1954: }
1955:
1956: if (peekToken() == FROM) {
1957: scanToken();
1958:
1959: trimChar = arg;
1960:
1961: arg = parseExpr();
1962: }
1963:
1964: args.add(arg);
1965: } else {
1966:
1967: if (peekToken() == DISTINCT) {
1968: distinct = true;
1969: scanToken();
1970: }
1971:
1972: while ((peekToken() >= 0) && (peekToken() != ')')) {
1973:
1974: AmberExpr arg = parseExpr();
1975:
1976: if (id.equalsIgnoreCase("object")) {
1977: if (arg instanceof PathExpr) {
1978: PathExpr pathExpr = (PathExpr) arg;
1979:
1980: arg = LoadExpr.create(pathExpr);
1981:
1982: arg = arg.bindSelect(this );
1983:
1984: int token = scanToken();
1985:
1986: if (token != ')')
1987: throw error(L.l("expected ')' at '{0}'",
1988: tokenName(token)));
1989:
1990: return arg;
1991: }
1992: }
1993:
1994: args.add(arg);
1995:
1996: if (peekToken() != ',')
1997: break;
1998:
1999: scanToken();
2000: }
2001: }
2002:
2003: if (peekToken() != ')')
2004: throw error(L.l("expected ')' at '{0}'",
2005: tokenName(scanToken())));
2006:
2007: scanToken();
2008:
2009: FunExpr funExpr;
2010:
2011: switch (functionToken) {
2012:
2013: case LOCATE:
2014: funExpr = LocateFunExpr.create(this , args);
2015: break;
2016:
2017: case LENGTH:
2018: funExpr = LengthFunExpr.create(this , args);
2019: break;
2020:
2021: case MAX:
2022: funExpr = MaxFunExpr.create(this , id, args, distinct);
2023: break;
2024:
2025: case MIN:
2026: funExpr = MinFunExpr.create(this , id, args, distinct);
2027: break;
2028:
2029: case SUM:
2030: funExpr = SumFunExpr.create(this , id, args, distinct);
2031: break;
2032:
2033: case ABS:
2034: funExpr = AbsFunExpr.create(this , args);
2035: break;
2036:
2037: case SQRT:
2038: funExpr = SqrtFunExpr.create(this , args);
2039: break;
2040:
2041: case MOD:
2042: funExpr = ModFunExpr.create(this , args);
2043: break;
2044:
2045: case SIZE:
2046: if (!(_query instanceof SelectQuery))
2047: throw error(L
2048: .l("The SIZE() function is only supported for SELECT or subselect queries"));
2049:
2050: // jpa/119l
2051:
2052: AmberExpr arg = args.get(0);
2053: if (arg instanceof ManyToOneExpr) {
2054: // @ManyToMany
2055: arg = ((ManyToOneExpr) arg).getParent();
2056: }
2057:
2058: if (!(arg instanceof OneToManyExpr))
2059: throw error(L
2060: .l(
2061: "The SIZE() function is only supported for @ManyToMany or @OneToMany relationships. The argument '{0}' is not supported.",
2062: args.get(0)));
2063:
2064: OneToManyExpr oneToMany = (OneToManyExpr) arg;
2065:
2066: _groupList = new ArrayList<AmberExpr>();
2067:
2068: LinkColumns linkColumns = oneToMany.getLinkColumns();
2069: ForeignColumn fkColumn = linkColumns.getColumns().get(0);
2070:
2071: AmberExpr groupExpr = oneToMany.getParent();
2072:
2073: if (groupExpr instanceof PathExpr) {
2074: // jpa/119n
2075:
2076: PathExpr pathExpr = (PathExpr) groupExpr;
2077:
2078: groupExpr = LoadExpr.create(pathExpr);
2079:
2080: groupExpr = groupExpr.bindSelect(this );
2081: }
2082:
2083: // groupExpr = new ColumnExpr(oneToMany.getParent(),
2084: // fkColumn.getTargetColumn());
2085:
2086: _groupList.add(groupExpr);
2087:
2088: ((SelectQuery) _query).setGroupList(_groupList);
2089:
2090: funExpr = SizeFunExpr.create(this , args);
2091:
2092: // jpa/1199, jpa/119l
2093: if (!_parsingResult) {
2094: if (_query instanceof SelectQuery) {
2095: SelectQuery query = (SelectQuery) _query;
2096: ArrayList<AmberExpr> resultList = query
2097: .getResultList();
2098:
2099: for (AmberExpr expr : resultList) {
2100: if (expr instanceof SizeFunExpr) {
2101: SizeFunExpr sizeFun = (SizeFunExpr) expr;
2102: AmberExpr amberExpr = sizeFun.getArgs()
2103: .get(0);
2104:
2105: // @ManyToMany
2106: if (amberExpr instanceof ManyToOneExpr) {
2107: amberExpr = ((ManyToOneExpr) amberExpr)
2108: .getParent();
2109: }
2110:
2111: if (amberExpr.equals(arg))
2112: args.set(0, amberExpr);
2113: }
2114: }
2115: }
2116:
2117: if (_appendResultList == null)
2118: _appendResultList = new ArrayList<AmberExpr>();
2119:
2120: _appendResultList.add(funExpr.bindSelect(this ));
2121:
2122: _isSizeFunExpr = true;
2123: }
2124:
2125: break;
2126:
2127: case CONCAT:
2128: funExpr = ConcatFunExpr.create(this , args);
2129: break;
2130:
2131: case LOWER:
2132: funExpr = LowerFunExpr.create(this , args);
2133: break;
2134:
2135: case UPPER:
2136: funExpr = UpperFunExpr.create(this , args);
2137: break;
2138:
2139: case SUBSTRING:
2140: funExpr = SubstringFunExpr.create(this , args);
2141: break;
2142:
2143: case TRIM: {
2144: TrimFunExpr trimFunExpr = TrimFunExpr.create(this , args);
2145: trimFunExpr.setTrimChar(trimChar);
2146: trimFunExpr.setTrimSemantics(trimSemantics);
2147: funExpr = trimFunExpr;
2148: break;
2149: }
2150:
2151: default:
2152: funExpr = FunExpr.create(this , id, args, distinct);
2153: }
2154:
2155: return funExpr;
2156: }
2157:
2158: /**
2159: * Returns the matching identifier.
2160: */
2161: private IdExpr getIdentifier(String name)
2162: throws QueryParseException {
2163: AbstractQuery query = _query;
2164:
2165: for (; query != null; query = query.getParentQuery()) {
2166: ArrayList<FromItem> fromList = query.getFromList();
2167:
2168: for (int i = 0; i < fromList.size(); i++) {
2169: FromItem from = fromList.get(i);
2170:
2171: if (from.getName().equalsIgnoreCase(name))
2172: return from.getIdExpr();
2173: }
2174: }
2175:
2176: return null;
2177:
2178: // throw error(L.l("`{0}' is an unknown table", name));
2179: }
2180:
2181: /**
2182: * Returns the matching embedded alias.
2183: */
2184: private EmbeddedExpr getEmbeddedAlias(String name)
2185: throws QueryParseException {
2186: // jpa/0w22
2187:
2188: AbstractQuery query = _query;
2189:
2190: for (; query != null; query = query.getParentQuery()) {
2191: HashMap<String, EmbeddedExpr> embeddedAliases = query
2192: .getEmbeddedAliases();
2193:
2194: for (Map.Entry<String, EmbeddedExpr> entry : embeddedAliases
2195: .entrySet()) {
2196:
2197: if (entry.getKey().equalsIgnoreCase(name))
2198: return entry.getValue();
2199: }
2200: }
2201:
2202: return null;
2203: }
2204:
2205: /**
2206: * Returns true if expr is a collection.
2207: */
2208: private boolean isCollectionExpr(AmberExpr expr) {
2209: // jpa/10a2
2210:
2211: // ManyToMany is implemented as a
2212: // ManyToOne[embeddeding OneToMany]
2213: if ((expr instanceof ManyToOneExpr)
2214: && (((ManyToOneExpr) expr).getParent() instanceof OneToManyExpr))
2215: return true;
2216: else if (expr instanceof OneToManyExpr)
2217: return true;
2218: else if (expr instanceof CollectionIdExpr)
2219: return true;
2220:
2221: return false;
2222: }
2223:
2224: /**
2225: * Returns true if expr1 and expr2 are compatible.
2226: */
2227: private boolean isCompatibleExpression(AmberExpr expr1,
2228: AmberExpr expr2) {
2229: // XXX: jpa/106a
2230: if (expr1 instanceof LiteralExpr) {
2231: if (expr2 instanceof LiteralExpr) {
2232: Class javaType1 = ((LiteralExpr) expr1).getJavaType();
2233: Class javaType2 = ((LiteralExpr) expr2).getJavaType();
2234:
2235: if (javaType1.isAssignableFrom(javaType2))
2236: return true;
2237:
2238: return false;
2239: }
2240: }
2241:
2242: return true;
2243: }
2244:
2245: /**
2246: * Parses an identifier.
2247: */
2248: private String parseIdentifier() throws QueryParseException {
2249: int token = scanToken();
2250:
2251: String identifier = _lexeme;
2252:
2253: // Resolves ambiguous identifiers:
2254: // 1. 'order': "SELECT o FROM Order o"
2255: if (token == ORDER) {
2256: int parseIndex = _parseIndex;
2257:
2258: scanToken();
2259:
2260: if (peekToken() != BY) {
2261: token = IDENTIFIER;
2262:
2263: // Restores parse index right after ORDER BY.
2264: _parseIndex = parseIndex;
2265: _lexeme = identifier;
2266: _token = -1;
2267: }
2268: } // 2. 'member': "SELECT m FROM Member m" (jpa/0x02)
2269: else if (_parsingFrom && token == MEMBER) {
2270: token = IDENTIFIER;
2271: }
2272:
2273: if (token != IDENTIFIER) {
2274: throw error(L.l("expected identifier at `{0}'",
2275: tokenName(token)));
2276: }
2277:
2278: return identifier;
2279: }
2280:
2281: /**
2282: * Peeks the next token
2283: *
2284: * @return integer code for the token
2285: */
2286: private int peekToken() throws QueryParseException {
2287: if (_token > 0)
2288: return _token;
2289:
2290: _token = scanToken();
2291:
2292: return _token;
2293: }
2294:
2295: /**
2296: * Scan the next token. If the lexeme is a string, its string
2297: * representation is in "lexeme".
2298: *
2299: * @return integer code for the token
2300: */
2301: private int scanToken() throws QueryParseException {
2302: if (_token > 0) {
2303: int value = _token;
2304: _token = -1;
2305: return value;
2306: }
2307:
2308: int sign = 1;
2309: int ch;
2310:
2311: for (ch = read(); Character.isWhitespace((char) ch); ch = read()) {
2312: }
2313:
2314: switch (ch) {
2315: case -1:
2316: case '.':
2317: case '*':
2318: case '/':
2319: case ',':
2320: case '+':
2321: case '-':
2322: case '[':
2323: case ']':
2324: return ch;
2325:
2326: case '(':
2327: _depth++;
2328: return ch;
2329:
2330: case ')':
2331: _depth--;
2332: return ch;
2333:
2334: case '=':
2335: if ((ch = read()) == '>')
2336: return EXTERNAL_DOT;
2337: else {
2338: unread(ch);
2339: return EQ;
2340: }
2341:
2342: case '!':
2343: if ((ch = read()) == '=')
2344: return NE;
2345: else {
2346: unread(ch);
2347: return '!';
2348: }
2349:
2350: case '<':
2351: if ((ch = read()) == '=')
2352: return LE;
2353: else if (ch == '>')
2354: return NE;
2355: else {
2356: unread(ch);
2357: return LT;
2358: }
2359:
2360: case '>':
2361: if ((ch = read()) == '=')
2362: return GE;
2363: else {
2364: unread(ch);
2365: return GT;
2366: }
2367:
2368: case '?':
2369: CharBuffer cb = CharBuffer.allocate();
2370: int index = 0;
2371: for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
2372: cb.append((char) ch);
2373: index = 10 * index + ch - '0';
2374: }
2375: unread(ch);
2376:
2377: _lexeme = cb.close();
2378:
2379: if (_lexeme.length() == 0) {
2380: _lexeme = String.valueOf(++_parameterCount);
2381: } else if (index <= 0)
2382: throw error(L.l(
2383: "`{0}' must refer to a positive argument", "?"
2384: + _lexeme));
2385:
2386: return ARG;
2387:
2388: case ':':
2389: if (Character.isJavaIdentifierStart((char) (ch = read()))) {
2390: cb = CharBuffer.allocate();
2391:
2392: for (; ch > 0
2393: && Character.isJavaIdentifierPart((char) ch); ch = read())
2394: cb.append((char) ch);
2395:
2396: unread(ch);
2397:
2398: _lexeme = cb.close();
2399:
2400: _parameterCount++;
2401: } else
2402: throw error(L.l(
2403: "`{0}' must be a valid parameter identifier",
2404: ":" + ((char) ch)));
2405:
2406: return NAMED_ARG;
2407:
2408: case '|':
2409: if ((ch = read()) == '|')
2410: return CONCAT_OP;
2411: else
2412: throw error(L.l("unexpected char at {0}", String
2413: .valueOf((char) ch)));
2414:
2415: // @@ is useless?
2416: case '@':
2417: if ((ch = read()) != '@')
2418: throw error(L.l("`@' expected at {0}", charName(ch)));
2419: return scanToken();
2420: }
2421:
2422: if (Character.isJavaIdentifierStart((char) ch)) {
2423: CharBuffer cb = CharBuffer.allocate();
2424:
2425: for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
2426: cb.append((char) ch);
2427:
2428: unread(ch);
2429:
2430: _lexeme = cb.close();
2431: String lower = _lexeme.toLowerCase();
2432:
2433: int token = _reserved.get(lower);
2434:
2435: if (token > 0)
2436: return token;
2437: else
2438: return IDENTIFIER;
2439: } else if (ch >= '0' && ch <= '9') {
2440: CharBuffer cb = CharBuffer.allocate();
2441:
2442: int type = INTEGER;
2443:
2444: if (sign < 0)
2445: cb.append('-');
2446:
2447: for (; ch >= '0' && ch <= '9'; ch = read())
2448: cb.append((char) ch);
2449:
2450: if (ch == '.') {
2451: type = DOUBLE;
2452:
2453: cb.append('.');
2454: for (ch = read(); ch >= '0' && ch <= '9'; ch = read())
2455: cb.append((char) ch);
2456: }
2457:
2458: if (ch == 'e' || ch == 'E') {
2459: type = DOUBLE;
2460:
2461: cb.append('e');
2462: if ((ch = read()) == '+' || ch == '-') {
2463: cb.append((char) ch);
2464: ch = read();
2465: }
2466:
2467: if (!(ch >= '0' && ch <= '9'))
2468: throw error(L.l("exponent needs digits at {0}",
2469: charName(ch)));
2470:
2471: for (; ch >= '0' && ch <= '9'; ch = read())
2472: cb.append((char) ch);
2473: }
2474:
2475: if (ch == 'F' || ch == 'D')
2476: type = DOUBLE;
2477: else if (ch == 'L') {
2478: type = LONG;
2479: } else
2480: unread(ch);
2481:
2482: _lexeme = cb.close();
2483:
2484: return type;
2485: } else if (ch == '\'') {
2486: CharBuffer cb = CharBuffer.allocate();
2487:
2488: cb.append("'");
2489: for (ch = read(); ch >= 0; ch = read()) {
2490: if (ch == '\'') {
2491: if ((ch = read()) == '\'')
2492: cb.append("''");
2493: else {
2494: unread(ch);
2495: break;
2496: }
2497: } else
2498: cb.append((char) ch);
2499: }
2500: cb.append("'");
2501:
2502: _lexeme = cb.close();
2503:
2504: return STRING;
2505: }
2506:
2507: throw error(L.l("unexpected char at {0}", "" + (char) ch));
2508: }
2509:
2510: /**
2511: * Returns the next character.
2512: */
2513: private int read() {
2514: if (_parseIndex < _sql.length())
2515: return _sql.charAt(_parseIndex++);
2516: else
2517: return -1;
2518: }
2519:
2520: /**
2521: * Unread the last character.
2522: */
2523: private void unread(int ch) {
2524: if (ch >= 0)
2525: _parseIndex--;
2526: }
2527:
2528: /**
2529: * Returns the jdbc meta data, if available.
2530: */
2531: private JdbcMetaData getMetaData() {
2532: if (_persistenceUnit == null)
2533: return null;
2534:
2535: return _persistenceUnit.getMetaData();
2536: }
2537:
2538: /**
2539: * Creates an error.
2540: */
2541: public QueryParseException error(String msg) {
2542: msg += "\nin \"" + _sql + "\"";
2543:
2544: return new QueryParseException(msg);
2545: }
2546:
2547: /**
2548: * Returns the name for a character
2549: */
2550: private String charName(int ch) {
2551: if (ch < 0)
2552: return L.l("end of query");
2553: else
2554: return String.valueOf((char) ch);
2555: }
2556:
2557: /**
2558: * Returns the name of a token
2559: */
2560: private String tokenName(int token) {
2561: switch (token) {
2562: case AS:
2563: return "AS";
2564: case FROM:
2565: return "FROM";
2566: case IN:
2567: return "IN";
2568: case SELECT:
2569: return "SELECT";
2570: case WHERE:
2571: return "WHERE";
2572: case OR:
2573: return "OR";
2574: case AND:
2575: return "AND";
2576: case NOT:
2577: return "NOT";
2578: case BETWEEN:
2579: return "BETWEEN";
2580: case THIS:
2581: return "THIS";
2582: case TRUE:
2583: return "FALSE";
2584: case EMPTY:
2585: return "EMPTY";
2586: case MEMBER:
2587: return "MEMBER";
2588: case OF:
2589: return "OF";
2590: case NULL:
2591: return "NULL";
2592: case ORDER:
2593: return "ORDER";
2594: case BY:
2595: return "BY";
2596: case ASC:
2597: return "ASC";
2598: case DESC:
2599: return "DESC";
2600: case LIMIT:
2601: return "LIMIT";
2602:
2603: case EXTERNAL_DOT:
2604: return "=>";
2605:
2606: case -1:
2607: return L.l("end of query");
2608:
2609: default:
2610: if (token < 128)
2611: return "'" + String.valueOf((char) token) + "'";
2612: else
2613: return "'" + _lexeme + "'";
2614: }
2615: }
2616:
2617: /**
2618: * Returns a debuggable description of the select.
2619: */
2620: public String toString() {
2621: return "QueryParser[]";
2622: }
2623:
2624: static {
2625: _reserved = new IntMap();
2626: _reserved.put("as", AS);
2627: _reserved.put("from", FROM);
2628: _reserved.put("in", IN);
2629: _reserved.put("select", SELECT);
2630: _reserved.put("update", UPDATE);
2631: _reserved.put("delete", DELETE);
2632: _reserved.put("set", SET);
2633: _reserved.put("distinct", DISTINCT);
2634: _reserved.put("where", WHERE);
2635: _reserved.put("order", ORDER);
2636: _reserved.put("group", GROUP);
2637: _reserved.put("by", BY);
2638: _reserved.put("having", HAVING);
2639: _reserved.put("asc", ASC);
2640: _reserved.put("desc", DESC);
2641: _reserved.put("limit", LIMIT);
2642: _reserved.put("offset", OFFSET);
2643:
2644: _reserved.put("join", JOIN);
2645: _reserved.put("inner", INNER);
2646: _reserved.put("left", LEFT);
2647: _reserved.put("outer", OUTER);
2648: _reserved.put("fetch", FETCH);
2649:
2650: _reserved.put("or", OR);
2651: _reserved.put("and", AND);
2652: _reserved.put("not", NOT);
2653:
2654: _reserved.put("length", LENGTH);
2655: _reserved.put("locate", LOCATE);
2656:
2657: _reserved.put("abs", ABS);
2658: _reserved.put("sqrt", SQRT);
2659: _reserved.put("mod", MOD);
2660: _reserved.put("size", SIZE);
2661:
2662: _reserved.put("max", MAX);
2663: _reserved.put("min", MIN);
2664: _reserved.put("sum", SUM);
2665:
2666: _reserved.put("concat", CONCAT);
2667: _reserved.put("lower", LOWER);
2668: _reserved.put("upper", UPPER);
2669: _reserved.put("substring", SUBSTRING);
2670: _reserved.put("trim", TRIM);
2671: _reserved.put("both", BOTH);
2672: _reserved.put("leading", LEADING);
2673: _reserved.put("trailing", TRAILING);
2674:
2675: _reserved.put("current_date", CURRENT_DATE);
2676: _reserved.put("current_time", CURRENT_TIME);
2677: _reserved.put("current_timestamp", CURRENT_TIMESTAMP);
2678:
2679: _reserved.put("between", BETWEEN);
2680: _reserved.put("like", LIKE);
2681: _reserved.put("escape", ESCAPE);
2682: _reserved.put("is", IS);
2683:
2684: _reserved.put("new", NEW);
2685:
2686: _reserved.put("this", THIS);
2687: _reserved.put("true", TRUE);
2688: _reserved.put("false", FALSE);
2689: _reserved.put("unknown", UNKNOWN);
2690: _reserved.put("empty", EMPTY);
2691: _reserved.put("member", MEMBER);
2692: _reserved.put("of", OF);
2693: _reserved.put("null", NULL);
2694: }
2695: }
|