0001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
0002: * All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * Redistributions of source code must retain the above copyright notice, this
0008: * list of conditions and the following disclaimer.
0009: *
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * Neither the name of the Hypersonic SQL Group nor the names of its
0015: * contributors may be used to endorse or promote products derived from this
0016: * software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
0022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: *
0030: * This software consists of voluntary contributions made by many individuals
0031: * on behalf of the Hypersonic SQL Group.
0032: *
0033: *
0034: * For work added by the HSQL Development Group:
0035: *
0036: * Copyright (c) 2001-2005, The HSQL Development Group
0037: * All rights reserved.
0038: *
0039: * Redistribution and use in source and binary forms, with or without
0040: * modification, are permitted provided that the following conditions are met:
0041: *
0042: * Redistributions of source code must retain the above copyright notice, this
0043: * list of conditions and the following disclaimer.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution.
0048: *
0049: * Neither the name of the HSQL Development Group nor the names of its
0050: * contributors may be used to endorse or promote products derived from this
0051: * software without specific prior written permission.
0052: *
0053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0064: */
0065:
0066: package org.hsqldb;
0067:
0068: import java.util.Locale;
0069:
0070: import org.hsqldb.HsqlNameManager.HsqlName;
0071: import org.hsqldb.lib.ArrayUtil;
0072: import org.hsqldb.lib.HashMap;
0073: import org.hsqldb.lib.HashMappedList;
0074: import org.hsqldb.lib.HsqlArrayList;
0075: import org.hsqldb.lib.IntKeyHashMap;
0076: import org.hsqldb.lib.IntValueHashMap;
0077: import org.hsqldb.lib.Iterator;
0078: import org.hsqldb.lib.StringConverter;
0079: import org.hsqldb.store.ValuePool;
0080: import org.hsqldb.lib.HashSet;
0081:
0082: // fredt@users 20020130 - patch 497872 by Nitin Chauhan - reordering for speed
0083: // fredt@users 20020215 - patch 1.7.0 by fredt - support GROUP BY with more than one column
0084: // fredt@users 20020215 - patch 1.7.0 by fredt - SQL standard quoted identifiers
0085: // fredt@users 20020218 - patch 1.7.0 by fredt - DEFAULT keyword
0086: // fredt@users 20020221 - patch 513005 by sqlbob@users - SELECT INTO types
0087: // fredt@users 20020425 - patch 548182 by skitt@users - DEFAULT enhancement
0088: // thertz@users 20020320 - patch 473613 by thertz - outer join condition bug
0089: // fredt@users 20021229 - patch 1.7.2 by fredt - new solution for above
0090: // fredt@users 20020420 - patch 523880 by leptipre@users - VIEW support
0091: // fredt@users 20020525 - patch 559914 by fredt@users - SELECT INTO logging
0092: // tony_lai@users 20021020 - patch 1.7.2 - improved aggregates and HAVING
0093: // aggregate functions can now be used in expressions - HAVING supported
0094: // kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE
0095: // fredt@users 20021112 - patch 1.7.2 by Nitin Chauhan - use of switch
0096: // rewrite of the majority of multiple if(){}else{} chains with switch(){}
0097: // boucherb@users 20030705 - patch 1.7.2 - prepared statement support
0098: // fredt@users 20030819 - patch 1.7.2 - EXTRACT({YEAR | MONTH | DAY | HOUR | MINUTE | SECOND } FROM datetime)
0099: // fredt@users 20030820 - patch 1.7.2 - CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH(string)
0100: // fredt@users 20030820 - patch 1.7.2 - POSITION(string IN string)
0101: // fredt@users 20030820 - patch 1.7.2 - SUBSTRING(string FROM pos [FOR length])
0102: // fredt@users 20030820 - patch 1.7.2 - TRIM({LEADING | TRAILING | BOTH} [<character>] FROM <string expression>)
0103: // fredt@users 20030820 - patch 1.7.2 - CASE [expr] WHEN ... THEN ... [ELSE ...] END and its variants
0104: // fredt@users 20030820 - patch 1.7.2 - NULLIF(expr,expr)
0105: // fredt@users 20030820 - patch 1.7.2 - COALESCE(expr,expr,...)
0106: // fredt@users 20031012 - patch 1.7.2 - improved scoping for column names in all areas
0107: // boucherb@users 200403xx - patch 1.7.2 - added support for prepared SELECT INTO
0108: // boucherb@users 200403xx - doc 1.7.2 - some
0109: // thomasm@users 20041001 - patch 1.7.3 - BOOLEAN undefined handling
0110: // fredt@users 20050220 - patch 1.8.0 - CAST with precision / scale
0111: /* todo: fredt - implement remaining numeric value functions (SQL92 6.6)
0112: *
0113: * EXTRACT({TIMEZONE_HOUR | TIMEZONE_MINUTE} FROM {datetime | interval})
0114: */
0115:
0116: /**
0117: * Responsible for parsing non-DDL statements.
0118: *
0119: * Extensively rewritten and extended in successive versions of HSQLDB.
0120: *
0121: * @author Thomas Mueller (Hypersonic SQL Group)
0122: * @version 1.8.0
0123: * @since Hypersonic SQL
0124: */
0125: class Parser {
0126:
0127: private Database database;
0128: private Tokenizer tokenizer;
0129: private Session session;
0130: private String sSchema;
0131: private String sTable;
0132: private String sToken;
0133: private boolean wasQuoted;
0134: private Object oData;
0135: private int iType;
0136: private int iToken;
0137: private boolean compilingView;
0138:
0139: //
0140: private int subQueryLevel;
0141: private HsqlArrayList subQueryList = new HsqlArrayList();
0142:
0143: /**
0144: * Constructs a new Parser object with the given context.
0145: *
0146: * @param db the Database instance against which to resolve named
0147: * database object references
0148: * @param t the token source from which to parse commands
0149: * @param session the connected context
0150: */
0151: Parser(Session session, Database db, Tokenizer t) {
0152:
0153: database = db;
0154: tokenizer = t;
0155: this .session = session;
0156: }
0157:
0158: /**
0159: * sets a flag indicating the parser is used for compiling a view
0160: */
0161: void setCompilingView() {
0162: compilingView = true;
0163: }
0164:
0165: /**
0166: * determines whether the parser is used for compiling a view
0167: */
0168: boolean isCompilingView() {
0169: return compilingView;
0170: }
0171:
0172: /**
0173: * Resets this parse context with the given SQL character sequence.
0174: *
0175: * Internal structures are reset as though a new parser were created
0176: * with the given sql and the originally specified database and session
0177: *
0178: * @param a new SQL character sequence to replace the current one
0179: */
0180: void reset(String sql) {
0181:
0182: sTable = null;
0183: sToken = null;
0184: oData = null;
0185:
0186: tokenizer.reset(sql);
0187: subQueryList.clear();
0188:
0189: subQueryLevel = 0;
0190:
0191: parameters.clear();
0192: }
0193:
0194: /**
0195: * Tests whether the parsing session has the given write access on the
0196: * given Table object. <p>
0197: *
0198: * @param table the Table object to check
0199: * @param userRight the numeric code of the right to check
0200: * @throws HsqlException if the session user does not have the right
0201: * or the given Table object is simply not writable (e.g. is a
0202: * non-updateable View)
0203: */
0204: void checkTableWriteAccess(Table table, int userRight)
0205: throws HsqlException {
0206:
0207: // session level user rights
0208: session.checkReadWrite();
0209:
0210: // object level user rights
0211: session.check(table.getName(), userRight);
0212:
0213: // object type
0214: if (table.isView()) {
0215: throw Trace.error(Trace.NOT_A_TABLE, table.getName().name);
0216: }
0217:
0218: // object readonly
0219: table.checkDataReadOnly();
0220: }
0221:
0222: /**
0223: * Parses a comma-separated, right-bracket terminated list of column
0224: * names. <p>
0225: *
0226: * @param db the Database instance whose name manager is to provide the
0227: * resulting HsqlName objects, when the full argument is true
0228: * @param t the tokenizer representing the character sequence to be parsed
0229: * @param full if true, generate a list of HsqlNames, else a list of
0230: * String objects
0231: */
0232: static HsqlArrayList getColumnNames(Database db, Table table,
0233: Tokenizer t, boolean full) throws HsqlException {
0234:
0235: HsqlArrayList columns = new HsqlArrayList();
0236:
0237: while (true) {
0238: if (full) {
0239: String token = t.getSimpleName();
0240: boolean quoted = t.wasQuotedIdentifier();
0241: HsqlName name = db.nameManager.newHsqlName(token,
0242: quoted);
0243:
0244: columns.add(name);
0245: } else {
0246: columns.add(t.getName());
0247:
0248: if (t.wasLongName()
0249: && !t.getLongNameFirst().equals(
0250: table.getName().name)) {
0251: throw (Trace.error(Trace.TABLE_NOT_FOUND, t
0252: .getLongNameFirst()));
0253: }
0254: }
0255:
0256: String token = t.getSimpleToken();
0257:
0258: if (token.equals(Token.T_COMMA)) {
0259: continue;
0260: }
0261:
0262: if (token.equals(Token.T_CLOSEBRACKET)) {
0263: break;
0264: }
0265:
0266: t.throwUnexpected();
0267: }
0268:
0269: return columns;
0270: }
0271:
0272: /**
0273: * The SubQuery objects are added to the end of subquery list.
0274: *
0275: * When parsing the SELECT for a view, optional HsqlName[] array is used
0276: * for view column aliases.
0277: *
0278: */
0279: SubQuery parseSubquery(int brackets, HsqlName[] colNames,
0280: boolean resolveAll, int predicateType) throws HsqlException {
0281:
0282: SubQuery sq;
0283:
0284: sq = new SubQuery();
0285:
0286: subQueryLevel++;
0287:
0288: boolean canHaveOrder = predicateType == Expression.VIEW;
0289: boolean canHaveLimit = predicateType == Expression.SELECT
0290: || predicateType == Expression.VIEW
0291: || predicateType == Expression.QUERY;
0292: boolean limitWithOrder = predicateType == Expression.IN
0293: || predicateType == Expression.ALL
0294: || predicateType == Expression.ANY;
0295: Select s = parseSelect(brackets, canHaveOrder, canHaveLimit,
0296: limitWithOrder, true);
0297:
0298: sq.level = subQueryLevel;
0299:
0300: subQueryLevel--;
0301:
0302: boolean isResolved = s.resolveAll(session, resolveAll);
0303:
0304: sq.select = s;
0305: sq.isResolved = isResolved;
0306:
0307: // it's not a problem that this table has not a unique name
0308: HsqlName sqtablename = database.nameManager.newHsqlName(
0309: "SYSTEM_SUBQUERY", false);
0310:
0311: sqtablename.schema = database.schemaManager.SYSTEM_SCHEMA_HSQLNAME;
0312:
0313: Table table = new Table(database, sqtablename,
0314: Table.SYSTEM_SUBQUERY);
0315:
0316: if (colNames != null) {
0317: if (colNames.length != s.iResultLen) {
0318: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
0319: }
0320:
0321: for (int i = 0; i < s.iResultLen; i++) {
0322: HsqlName name = colNames[i];
0323:
0324: s.exprColumns[i].setAlias(name.name, name.isNameQuoted);
0325: }
0326: } else {
0327: for (int i = 0; i < s.iResultLen; i++) {
0328: String colname = s.exprColumns[i].getAlias();
0329:
0330: if (colname == null || colname.length() == 0) {
0331:
0332: // fredt - this does not guarantee the uniqueness of column
0333: // names but addColumns() will throw if names are not unique.
0334: colname = "COL_" + String.valueOf(i + 1);
0335:
0336: s.exprColumns[i].setAlias(colname, false);
0337: }
0338: }
0339: }
0340:
0341: table.addColumns(s);
0342:
0343: boolean uniqueValues = predicateType == Expression.EXISTS
0344: || predicateType == Expression.IN
0345: || predicateType == Expression.ALL
0346: || predicateType == Expression.ANY;
0347: int[] pcol = null;
0348:
0349: if (uniqueValues) {
0350: pcol = new int[s.iResultLen];
0351:
0352: ArrayUtil.fillSequence(pcol);
0353: }
0354:
0355: table.createPrimaryKey(pcol);
0356:
0357: sq.table = table;
0358: sq.uniqueRows = uniqueValues;
0359:
0360: subQueryList.add(sq);
0361:
0362: return sq;
0363: }
0364:
0365: SubQuery getViewSubquery(View v) {
0366:
0367: SubQuery sq = v.viewSubQuery;
0368:
0369: for (int i = 0; i < v.viewSubqueries.length; i++) {
0370: subQueryList.add(v.viewSubqueries[i]);
0371: }
0372:
0373: return sq;
0374: }
0375:
0376: /**
0377: * Constructs and returns a Select object.
0378: *
0379: * @param canHaveOrder whether the SELECT being parsed can have an ORDER BY
0380: * @param canHaveLimit whether LIMIT without ORDER BY is allowed
0381: * @param limitWithOrder whether LIMIT is allowed only with ORDER BY
0382: * @param isMain whether the SELECT being parsed is the first
0383: * select statement in the set
0384: * @return a new Select object
0385: * @throws HsqlException if a parsing error occurs
0386: */
0387: Select parseSelect(int brackets, boolean canHaveOrder,
0388: boolean canHaveLimit, boolean limitWithOrder, boolean isMain)
0389: throws HsqlException {
0390:
0391: Select select = new Select();
0392: String token = tokenizer.getString();
0393:
0394: if (canHaveLimit || limitWithOrder) {
0395: if (tokenizer.wasThis(Token.T_LIMIT)
0396: || tokenizer.wasThis(Token.T_TOP)) {
0397: parseLimit(token, select, false);
0398:
0399: token = tokenizer.getString();
0400: }
0401: }
0402:
0403: if (tokenizer.wasThis(Token.T_DISTINCT)) {
0404: select.isDistinctSelect = true;
0405: } else if (tokenizer.wasThis(Token.T_ALL)) {
0406: } else {
0407: tokenizer.back();
0408: }
0409:
0410: // parse column list
0411: HsqlArrayList vcolumn = new HsqlArrayList();
0412:
0413: do {
0414: int expPos = tokenizer.getPosition();
0415: Expression e = parseExpression();
0416:
0417: if (isCompilingView()) {
0418: if (e.getType() == Expression.ASTERISK) {
0419: if (select.asteriskPositions == null) {
0420: select.asteriskPositions = new IntKeyHashMap();
0421: }
0422:
0423: // remember the position of the asterisk. For the moment, just
0424: // remember the expression, so it can later be found and replaced
0425: // with the concrete column list
0426: select.asteriskPositions.put(expPos, e);
0427: }
0428: }
0429:
0430: token = tokenizer.getString();
0431:
0432: if (tokenizer.wasThis(Token.T_AS)) {
0433: e.setAlias(tokenizer.getSimpleName(), tokenizer
0434: .wasQuotedIdentifier());
0435:
0436: token = tokenizer.getString();
0437: } else if (tokenizer.wasSimpleName()) {
0438: e.setAlias(token, tokenizer.wasQuotedIdentifier());
0439:
0440: token = tokenizer.getString();
0441: }
0442:
0443: vcolumn.add(e);
0444: } while (tokenizer.wasThis(Token.T_COMMA));
0445:
0446: if (token.equals(Token.T_INTO)) {
0447: boolean getname = true;
0448:
0449: token = tokenizer.getString();
0450: select.intoType = database.getDefaultTableType();
0451:
0452: if (tokenizer.wasSimpleToken()) {
0453: switch (Token.get(token)) {
0454:
0455: case Token.CACHED:
0456: select.intoType = Table.CACHED_TABLE;
0457: break;
0458:
0459: case Token.TEMP:
0460: select.intoType = Table.TEMP_TABLE;
0461: break;
0462:
0463: case Token.TEXT:
0464: select.intoType = Table.TEXT_TABLE;
0465: break;
0466:
0467: case Token.MEMORY:
0468: select.intoType = Table.MEMORY_TABLE;
0469: break;
0470:
0471: default:
0472: getname = false;
0473: break;
0474: }
0475:
0476: if (getname) {
0477: token = tokenizer.getName();
0478: }
0479: }
0480:
0481: if (!tokenizer.wasName()) {
0482: tokenizer.throwUnexpected();
0483: }
0484:
0485: select.sIntoTable = database.nameManager.newHsqlName(token,
0486: tokenizer.wasQuotedIdentifier());
0487: select.sIntoTable.schema = session
0488: .getSchemaHsqlName(tokenizer.getLongNameFirst());
0489: token = tokenizer.getString();
0490: }
0491:
0492: tokenizer.matchThis(Token.T_FROM);
0493:
0494: Expression condition = null;
0495:
0496: // parse table list
0497: HsqlArrayList vfilter = new HsqlArrayList();
0498:
0499: vfilter.add(parseTableFilter(false));
0500:
0501: while (true) {
0502: token = tokenizer.getString();
0503:
0504: boolean cross = false;
0505:
0506: if (tokenizer.wasThis(Token.T_INNER)) {
0507: tokenizer.getThis(Token.T_JOIN);
0508:
0509: token = Token.T_JOIN;
0510: } else if (tokenizer.wasThis(Token.T_CROSS)) {
0511: tokenizer.getThis(Token.T_JOIN);
0512:
0513: token = Token.T_JOIN;
0514: cross = true;
0515: }
0516:
0517: if (token.equals(Token.T_LEFT)
0518: && !tokenizer.wasQuotedIdentifier()) {
0519: tokenizer.isGetThis(Token.T_OUTER);
0520: tokenizer.getThis(Token.T_JOIN);
0521:
0522: TableFilter tf = parseTableFilter(true);
0523:
0524: vfilter.add(tf);
0525: tokenizer.getThis(Token.T_ON);
0526:
0527: Expression newcondition = parseExpression();
0528:
0529: newcondition.checkTables(vfilter);
0530:
0531: condition = addJoinCondition(condition, newcondition,
0532: tf, true);
0533:
0534: // MarcH HuugO RIGHT JOIN SUPPORT
0535: } else if (token.equals(Token.T_RIGHT)
0536: && !tokenizer.wasQuotedIdentifier()) {
0537: tokenizer.isGetThis(Token.T_OUTER);
0538: tokenizer.getThis(Token.T_JOIN);
0539:
0540: // this object is not an outerjoin, the next object is an outerjoin
0541: TableFilter tf = parseTableFilter(false);
0542:
0543: // insert new condition as first element in a new vfilter (nvfilter), copy the content of vfilter and rename nvfilter back to vfilter.
0544: HsqlArrayList nvfilter = new HsqlArrayList();
0545:
0546: nvfilter.add(tf);
0547: nvfilter.addAll(vfilter);
0548:
0549: vfilter = nvfilter;
0550:
0551: // set isOuterJoin correct
0552: ((TableFilter) vfilter.get(1)).isOuterJoin = true;
0553:
0554: tokenizer.getThis(Token.T_ON);
0555:
0556: Expression newcondition = parseExpression();
0557:
0558: newcondition.checkTables(vfilter);
0559:
0560: condition = addJoinCondition(condition, newcondition,
0561: ((TableFilter) vfilter.get(1)), true);
0562: } else if (tokenizer.wasThis(Token.T_JOIN)) {
0563: vfilter.add(parseTableFilter(false));
0564:
0565: if (!cross) {
0566: tokenizer.getThis(Token.T_ON);
0567:
0568: Expression newcondition = parseExpression();
0569:
0570: newcondition.checkTables(vfilter);
0571:
0572: condition = addJoinCondition(condition,
0573: newcondition, null, false);
0574: }
0575: } else if (tokenizer.wasThis(Token.T_COMMA)) {
0576: vfilter.add(parseTableFilter(false));
0577: } else {
0578: tokenizer.back();
0579:
0580: break;
0581: }
0582: }
0583:
0584: resolveSelectTableFilter(select, vcolumn, vfilter);
0585:
0586: // where
0587: token = tokenizer.getString();
0588:
0589: if (tokenizer.wasThis(Token.T_WHERE)) {
0590: Expression newcondition = parseExpression();
0591:
0592: condition = addCondition(condition, newcondition);
0593: token = tokenizer.getString();
0594: }
0595:
0596: select.queryCondition = condition;
0597:
0598: // group by
0599: if (tokenizer.wasThis(Token.T_GROUP)) {
0600: tokenizer.getThis(Token.T_BY);
0601:
0602: int len = 0;
0603:
0604: do {
0605: Expression e = parseExpression();
0606:
0607: vcolumn.add(e);
0608:
0609: token = tokenizer.getString();
0610:
0611: len++;
0612: } while (tokenizer.wasThis(Token.T_COMMA));
0613:
0614: select.iGroupLen = len;
0615: }
0616:
0617: // having
0618: if (tokenizer.wasThis(Token.T_HAVING)) {
0619: select.iHavingLen = 1;
0620: select.havingCondition = parseExpression();
0621: token = tokenizer.getString();
0622:
0623: vcolumn.add(select.havingCondition);
0624: }
0625:
0626: if (isMain || limitWithOrder) {
0627: if (tokenizer.wasThis(Token.T_ORDER)) {
0628: tokenizer.getThis(Token.T_BY);
0629: parseOrderBy(select, vcolumn);
0630:
0631: token = tokenizer.getString();
0632: }
0633:
0634: if (tokenizer.wasThis(Token.T_LIMIT)) {
0635: parseLimit(token, select, true);
0636:
0637: token = tokenizer.getString();
0638: }
0639: }
0640:
0641: boolean closebrackets = false;
0642:
0643: if (brackets > 0 && token.equals(Token.T_CLOSEBRACKET)) {
0644: closebrackets = true;
0645: brackets -= parseCloseBrackets(brackets - 1) + 1;
0646: token = tokenizer.getString();
0647: }
0648:
0649: select.unionDepth = brackets;
0650:
0651: // checks for ORDER and LIMIT
0652: if (!(isMain || closebrackets)) {
0653: limitWithOrder = false;
0654: }
0655:
0656: boolean hasOrder = select.iOrderLen != 0;
0657: boolean hasLimit = select.limitCondition != null;
0658:
0659: if (limitWithOrder) {
0660: if (hasLimit && !hasOrder) {
0661: throw Trace.error(Trace.ORDER_LIMIT_REQUIRED);
0662: }
0663: } else {
0664: if (hasOrder && !canHaveOrder) {
0665: throw Trace.error(Trace.INVALID_ORDER_BY);
0666: }
0667:
0668: if (hasLimit && !canHaveLimit) {
0669: throw Trace.error(Trace.INVALID_LIMIT);
0670: }
0671: }
0672:
0673: int unionType = parseUnion(token);
0674:
0675: if (unionType != Select.NOUNION) {
0676: boolean openbracket = false;
0677:
0678: select.unionType = unionType;
0679:
0680: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
0681: openbracket = true;
0682: brackets += parseOpenBrackets() + 1;
0683: }
0684:
0685: tokenizer.getThis(Token.T_SELECT);
0686:
0687: // accept ORDRY BY with LIMIT when in brackets
0688: select.unionSelect = parseSelect(brackets, false, false,
0689: openbracket, false);
0690: token = tokenizer.getString();
0691: }
0692:
0693: if (isMain && (canHaveOrder || limitWithOrder)
0694: && select.iOrderLen == 0) {
0695: if (tokenizer.wasThis(Token.T_ORDER)) {
0696: tokenizer.getThis(Token.T_BY);
0697: parseOrderBy(select, vcolumn);
0698:
0699: token = tokenizer.getString();
0700: select.sortUnion = true;
0701: }
0702:
0703: if (tokenizer.wasThis(Token.T_LIMIT)) {
0704: parseLimit(token, select, true);
0705:
0706: token = tokenizer.getString();
0707: }
0708: }
0709:
0710: tokenizer.back();
0711:
0712: if (isMain) {
0713: select.prepareUnions();
0714: }
0715:
0716: int len = vcolumn.size();
0717:
0718: select.exprColumns = new Expression[len];
0719:
0720: vcolumn.toArray(select.exprColumns);
0721:
0722: return select;
0723: }
0724:
0725: /**
0726: * Parses the given token and any further tokens in tokenizer to return
0727: * any UNION or other set operation ID.
0728: */
0729: int parseUnion(String token) throws HsqlException {
0730:
0731: int unionType = Select.NOUNION;
0732:
0733: if (tokenizer.wasSimpleToken()) {
0734: switch (Token.get(token)) {
0735:
0736: case Token.UNION:
0737: token = tokenizer.getSimpleToken();
0738:
0739: if (token.equals(Token.T_ALL)) {
0740: unionType = Select.UNIONALL;
0741: } else if (token.equals(Token.T_DISTINCT)) {
0742: unionType = Select.UNION;
0743: } else {
0744: unionType = Select.UNION;
0745:
0746: tokenizer.back();
0747: }
0748: break;
0749:
0750: case Token.INTERSECT:
0751: tokenizer.isGetThis(Token.T_DISTINCT);
0752:
0753: unionType = Select.INTERSECT;
0754: break;
0755:
0756: case Token.EXCEPT:
0757: case Token.MINUS:
0758: tokenizer.isGetThis(Token.T_DISTINCT);
0759:
0760: unionType = Select.EXCEPT;
0761: break;
0762:
0763: default:
0764: break;
0765: }
0766: }
0767:
0768: return unionType;
0769: }
0770:
0771: // fredt@users 20011010 - patch 471710 by fredt - LIMIT rewritten
0772: // SELECT LIMIT n m DISTINCT ... queries and error message
0773: // "SELECT LIMIT n m ..." creates the result set for the SELECT statement then
0774: // discards the first n rows and returns m rows of the remaining result set
0775: // "SELECT LIMIT 0 m" is equivalent to "SELECT TOP m" or "SELECT FIRST m"
0776: // in other RDBMS's
0777: // "SELECT LIMIT n 0" discards the first n rows and returns the remaining rows
0778: // fredt@users 20020225 - patch 456679 by hiep256 - TOP keyword
0779: private void parseLimit(String token, Select select, boolean isEnd)
0780: throws HsqlException {
0781:
0782: if (select.limitCondition != null) {
0783: return;
0784: }
0785:
0786: Expression e1 = null;
0787: Expression e2;
0788: boolean islimit = false;
0789:
0790: if (isEnd) {
0791: if (token.equals(Token.T_LIMIT)) {
0792: islimit = true;
0793:
0794: read();
0795:
0796: e2 = readTerm();
0797:
0798: if (sToken.equals(Token.T_OFFSET)) {
0799: read();
0800:
0801: e1 = readTerm();
0802: }
0803:
0804: tokenizer.back();
0805: } else {
0806: return;
0807: }
0808: } else if (token.equals(Token.T_LIMIT)) {
0809: read();
0810:
0811: e1 = readTerm();
0812: e2 = readTerm();
0813: islimit = true;
0814:
0815: tokenizer.back();
0816: } else if (token.equals(Token.T_TOP)) {
0817: read();
0818:
0819: e2 = readTerm();
0820:
0821: tokenizer.back();
0822: } else {
0823: return;
0824: }
0825:
0826: if (e1 == null) {
0827: e1 = new Expression(Types.INTEGER, ValuePool.getInt(0));
0828: }
0829:
0830: if (e1.isParam()
0831: || (e1.getType() == Expression.VALUE
0832: && e1.getDataType() == Types.INTEGER
0833: && e1.getValue(null) != null && ((Integer) e1
0834: .getValue(null)).intValue() >= 0)) {
0835: if (e2.isParam()
0836: || (e2.getType() == Expression.VALUE
0837: && e2.getDataType() == Types.INTEGER
0838: && e2.getValue(null) != null && ((Integer) e2
0839: .getValue(null)).intValue() >= 0)) {
0840:
0841: // necessary for params
0842: e1.setDataType(Types.INTEGER);
0843: e2.setDataType(Types.INTEGER);
0844:
0845: select.limitCondition = new Expression(
0846: Expression.LIMIT, e1, e2);
0847:
0848: return;
0849: }
0850: }
0851:
0852: int messageid = islimit ? Trace.INVALID_LIMIT_EXPRESSION
0853: : Trace.INVALID_TOP_EXPRESSION;
0854:
0855: throw Trace.error(Trace.WRONG_DATA_TYPE, messageid);
0856: }
0857:
0858: private void parseOrderBy(Select select, HsqlArrayList vcolumn)
0859: throws HsqlException {
0860:
0861: String token;
0862: int len = 0;
0863:
0864: do {
0865: Expression e = parseExpression();
0866:
0867: e = resolveOrderByExpression(e, select, vcolumn);
0868: token = tokenizer.getString();
0869:
0870: if (token.equals(Token.T_DESC)) {
0871: e.setDescending();
0872:
0873: token = tokenizer.getString();
0874: } else if (token.equals(Token.T_ASC)) {
0875: token = tokenizer.getString();
0876: }
0877:
0878: vcolumn.add(e);
0879:
0880: len++;
0881: } while (token.equals(Token.T_COMMA));
0882:
0883: tokenizer.back();
0884:
0885: select.iOrderLen = len;
0886: }
0887:
0888: private void resolveSelectTableFilter(Select select,
0889: HsqlArrayList vcolumn, HsqlArrayList vfilter)
0890: throws HsqlException {
0891:
0892: int colcount;
0893: TableFilter[] filters = new TableFilter[vfilter.size()];
0894:
0895: vfilter.toArray(filters);
0896:
0897: select.tFilter = filters;
0898:
0899: // expand [table.]* columns
0900: colcount = vcolumn.size();
0901:
0902: for (int pos = 0; pos < colcount;) {
0903: Expression e = (Expression) (vcolumn.get(pos));
0904:
0905: if (e.getType() == Expression.ASTERISK) {
0906: vcolumn.remove(pos);
0907:
0908: colcount = vcolumn.size();
0909:
0910: String tablename = e.getTableName();
0911: int oldPos = pos;
0912:
0913: if (tablename == null) {
0914: for (int i = 0; i < filters.length; i++) {
0915: pos = addFilterColumns(filters[i], vcolumn, pos);
0916: colcount = vcolumn.size();
0917: }
0918: } else {
0919: TableFilter f = e.findTableFilter(filters);
0920:
0921: if (f == null) {
0922: throw Trace.error(Trace.TABLE_NOT_FOUND,
0923: tablename);
0924: }
0925:
0926: pos = addFilterColumns(f, vcolumn, pos);
0927: colcount = vcolumn.size();
0928: }
0929:
0930: if (isCompilingView()) {
0931:
0932: // find this expression's position in the Select's asterisk list
0933: boolean foundAsteriskPos = false;
0934: Iterator expSearch = select.asteriskPositions
0935: .keySet().iterator();
0936:
0937: while (expSearch.hasNext()) {
0938: int expPos = expSearch.nextInt();
0939:
0940: if (e == select.asteriskPositions.get(expPos)) {
0941:
0942: // compile the complete column list which later is to replace the asterisk
0943: StringBuffer completeColList = new StringBuffer();
0944:
0945: for (int col = oldPos; col < pos; ++col) {
0946: Expression resolvedColExpr = (Expression) (vcolumn
0947: .get(col));
0948:
0949: completeColList.append(resolvedColExpr
0950: .getColumnDDL());
0951:
0952: if (col < pos - 1) {
0953: completeColList.append(", ");
0954: }
0955: }
0956:
0957: select.asteriskPositions.put(expPos,
0958: completeColList.toString());
0959:
0960: foundAsteriskPos = true;
0961:
0962: break;
0963: }
0964: }
0965:
0966: Trace.doAssert(foundAsteriskPos);
0967: }
0968: } else {
0969: if (e.getFilter() == null) {
0970: for (int i = 0; i < filters.length; i++) {
0971: e.resolveTables(filters[i]);
0972: }
0973: }
0974:
0975: pos++;
0976: }
0977: }
0978:
0979: for (int i = 0; i < colcount; i++) {
0980: Expression e = (Expression) (vcolumn.get(i));
0981:
0982: e.resolveTypes(session);
0983: }
0984:
0985: select.iResultLen = colcount;
0986: }
0987:
0988: /**
0989: * Add all columns of a table filter to list of columns
0990: */
0991: int addFilterColumns(TableFilter filter, HsqlArrayList columnList,
0992: int position) throws HsqlException {
0993:
0994: Table table = filter.getTable();
0995: int count = table.getColumnCount();
0996:
0997: for (int i = 0; i < count; i++) {
0998: Expression e = new Expression(filter, table.getColumn(i));
0999:
1000: if (isCompilingView()) {
1001: e.resolveTables(filter);
1002: }
1003:
1004: columnList.add(position++, e);
1005: }
1006:
1007: return position;
1008: }
1009:
1010: /**
1011: * Resolves an ORDER BY Expression, returning the column Expression object
1012: * to which it refers if it is an alias or column index. <p>
1013: *
1014: * If select is a SET QUERY, then only column indexes or names in the first
1015: * query are allowed.
1016: *
1017: * @param e search column expression
1018: * @param vcolumn list of columns
1019: * @param union is select a union
1020: * @return new or the same expression
1021: * @throws HsqlException if an ambiguous reference to an alias or
1022: * non-integer column index is encountered
1023: */
1024: private static Expression resolveOrderByExpression(Expression e,
1025: Select select, HsqlArrayList vcolumn) throws HsqlException {
1026:
1027: int visiblecols = select.iResultLen;
1028: boolean union = select.unionSelect != null;
1029:
1030: if (e.getType() == Expression.VALUE) {
1031: return resolveOrderByColumnIndex(e, vcolumn, visiblecols);
1032: }
1033:
1034: if (e.getType() != Expression.COLUMN) {
1035: if (union) {
1036: throw Trace.error(Trace.INVALID_ORDER_BY);
1037: }
1038:
1039: return e;
1040: }
1041:
1042: String ecolname = e.getColumnName();
1043: String etablename = e.getTableName();
1044:
1045: for (int i = 0, size = visiblecols; i < size; i++) {
1046: Expression colexpr = (Expression) vcolumn.get(i);
1047: String colalias = colexpr.getDefinedAlias();
1048: String colname = colexpr.getColumnName();
1049: String tablename = colexpr.getTableName();
1050: String filtername = colexpr.getFilterTableName();
1051:
1052: if ((ecolname.equals(colalias) || ecolname.equals(colname))
1053: && (etablename == null
1054: || etablename.equals(tablename) || etablename
1055: .equals(filtername))) {
1056: colexpr.joinedTableColumnIndex = i;
1057:
1058: return colexpr;
1059: }
1060: }
1061:
1062: if (union) {
1063: throw Trace.error(Trace.INVALID_ORDER_BY, ecolname);
1064: }
1065:
1066: return e;
1067: }
1068:
1069: private static Expression resolveOrderByColumnIndex(Expression e,
1070: HsqlArrayList vcolumn, int visiblecols)
1071: throws HsqlException {
1072:
1073: // order by 1,2,3
1074: if (e.getDataType() == Types.INTEGER) {
1075: int i = ((Integer) e.getValue(null)).intValue();
1076:
1077: if (0 < i && i <= visiblecols) {
1078: Expression colexpr = (Expression) vcolumn.get(i - 1);
1079:
1080: colexpr.joinedTableColumnIndex = i - 1;
1081:
1082: return colexpr;
1083: }
1084: }
1085:
1086: throw Trace.error(Trace.INVALID_ORDER_BY);
1087: }
1088:
1089: private TableFilter parseSimpleTableFilter(int type)
1090: throws HsqlException {
1091:
1092: String alias = null;
1093: String token = tokenizer.getName();
1094: String schema = session.getSchemaName(tokenizer
1095: .getLongNameFirst());
1096: Table table = database.schemaManager.getTable(session, token,
1097: schema);
1098:
1099: checkTableWriteAccess(table, type);
1100:
1101: //
1102: token = tokenizer.getString();
1103:
1104: if (token.equals(Token.T_AS)) {
1105: alias = tokenizer.getSimpleName();
1106: } else if (tokenizer.wasSimpleName()) {
1107: alias = token;
1108: } else {
1109: tokenizer.back();
1110: }
1111:
1112: return new TableFilter(table, alias, null, false);
1113: }
1114:
1115: /**
1116: * Retrieves a TableFilter object newly constructed from the current
1117: * parse context. <p>
1118: *
1119: * @param outerjoin if the filter is to back an outer join
1120: * @return a newly constructed TableFilter object
1121: * @throws HsqlException if a parsing error occurs
1122: */
1123: private TableFilter parseTableFilter(boolean outerjoin)
1124: throws HsqlException {
1125:
1126: Table t = null;
1127: SubQuery sq = null;
1128: String sAlias = null;
1129: HashMappedList columnList = null;
1130:
1131: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
1132: int brackets = parseOpenBrackets();
1133:
1134: tokenizer.getThis(Token.T_SELECT);
1135:
1136: // fredt - not correlated - a joined subquery table must resolve fully
1137: sq = parseSubquery(brackets, null, true, Expression.QUERY);
1138:
1139: tokenizer.getThis(Token.T_CLOSEBRACKET);
1140:
1141: t = sq.table;
1142: } else {
1143: String token = tokenizer.getName();
1144: String schema = session.getSchemaName(tokenizer
1145: .getLongNameFirst());
1146:
1147: t = database.schemaManager.getTable(session, token, schema);
1148:
1149: session.check(t.getName(), UserManager.SELECT);
1150:
1151: if (t.isView()) {
1152: sq = getViewSubquery((View) t);
1153: sq.select = ((View) t).viewSelect;
1154: t = sq.table;
1155: sAlias = token;
1156: }
1157: }
1158:
1159: // fredt - we removed LEFT from the list of reserved words in Tokenizer
1160: // to allow LEFT() to work. Thus wasName() will return true for LEFT
1161: // and we check separately for this token
1162: String token = tokenizer.getString();
1163:
1164: if (tokenizer.wasLongName()) {
1165: tokenizer.throwUnexpected();
1166: }
1167:
1168: if ((token.equals(Token.T_LEFT) || token.equals(Token.T_RIGHT))
1169: && !tokenizer.wasQuotedIdentifier()) {
1170: tokenizer.back();
1171: } else if (token.equals(Token.T_AS)
1172: && !tokenizer.wasQuotedIdentifier()) {
1173: sAlias = tokenizer.getSimpleName();
1174:
1175: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
1176: tokenizer.back();
1177:
1178: columnList = parseColumnList();
1179: }
1180: } else if (tokenizer.wasSimpleName()) {
1181: sAlias = token;
1182:
1183: if (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
1184: tokenizer.back();
1185:
1186: columnList = parseColumnList();
1187: }
1188: } else {
1189: tokenizer.back();
1190: }
1191:
1192: if (columnList != null
1193: && t.getColumnCount() != columnList.size()) {
1194: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
1195: }
1196:
1197: return new TableFilter(t, sAlias, columnList, outerjoin);
1198: }
1199:
1200: /**
1201: * Add a condition from the WHERE clause.
1202: *
1203: * @param e1
1204: * @param e2
1205: * @return
1206: */
1207: private static Expression addCondition(Expression e1, Expression e2) {
1208:
1209: if (e1 == null) {
1210: return e2;
1211: } else if (e2 == null) {
1212: return e1;
1213: } else {
1214: return new Expression(Expression.AND, e1, e2);
1215: }
1216: }
1217:
1218: /**
1219: * Conjuntively adds a condition from the JOIN table ON clause.
1220: *
1221: * @param e1 an existing condition with which e2 is to be combined
1222: * in order to form a new conjunction
1223: * @param e2 the new condition
1224: * @param tf the table filter that should become e2's join
1225: * table filter
1226: * @param outer true if join is outer
1227: * @throws HsqlException if e2 responds that it cannot participate
1228: * in the join
1229: * @return a new Expression object; the conjunction of e1 and e2
1230: */
1231: private static Expression addJoinCondition(Expression e1,
1232: Expression e2, TableFilter tf, boolean outer)
1233: throws HsqlException {
1234:
1235: if (!e2.setForJoin(tf, outer)) {
1236: throw Trace.error(Trace.OUTER_JOIN_CONDITION);
1237: }
1238:
1239: return addCondition(e1, e2);
1240: }
1241:
1242: /**
1243: * Method declaration
1244: *
1245: * @return the Expression resulting from the parse
1246: * @throws HsqlException
1247: */
1248: Expression parseExpression() throws HsqlException {
1249:
1250: read();
1251:
1252: Expression r = readOr();
1253:
1254: tokenizer.back();
1255:
1256: return r;
1257: }
1258:
1259: private Expression readAggregate() throws HsqlException {
1260:
1261: boolean distinct = false;
1262: boolean all = false;
1263: int type = iToken;
1264:
1265: read();
1266:
1267: String token = tokenizer.getString();
1268:
1269: if (token.equals(Token.T_DISTINCT)) {
1270: distinct = true;
1271: } else if (token.equals(Token.T_ALL)) {
1272: all = true;
1273: } else {
1274: tokenizer.back();
1275: }
1276:
1277: readThis(Expression.OPEN);
1278:
1279: Expression s = readOr();
1280:
1281: readThis(Expression.CLOSE);
1282:
1283: if ((all || distinct)
1284: && (type == Expression.STDDEV_POP
1285: || type == Expression.STDDEV_SAMP
1286: || type == Expression.VAR_POP || type == Expression.VAR_SAMP)) {
1287: throw Trace.error(Trace.INVALID_FUNCTION_ARGUMENT);
1288: }
1289:
1290: Expression aggregateExp = new Expression(type, s, null);
1291:
1292: aggregateExp.setDistinctAggregate(distinct);
1293:
1294: return aggregateExp;
1295: }
1296:
1297: /**
1298: * Method declaration
1299: *
1300: * @return a disjuntion, possibly degenerate
1301: * @throws HsqlException
1302: */
1303: private Expression readOr() throws HsqlException {
1304:
1305: Expression r = readAnd();
1306:
1307: while (iToken == Expression.OR) {
1308: int type = iToken;
1309: Expression a = r;
1310:
1311: read();
1312:
1313: r = new Expression(type, a, readAnd());
1314: }
1315:
1316: return r;
1317: }
1318:
1319: /**
1320: * Method declaration
1321: *
1322: * @return a conjunction, possibly degenerate
1323: * @throws HsqlException
1324: */
1325: private Expression readAnd() throws HsqlException {
1326:
1327: Expression r = readCondition();
1328:
1329: while (iToken == Expression.AND) {
1330: int type = iToken;
1331: Expression a = r;
1332:
1333: read();
1334:
1335: r = new Expression(type, a, readCondition());
1336: }
1337:
1338: return r;
1339: }
1340:
1341: /**
1342: * Method declaration
1343: *
1344: * @return a predicate, possibly composite
1345: * @throws HsqlException
1346: */
1347: private Expression readCondition() throws HsqlException {
1348:
1349: switch (iToken) {
1350:
1351: case Expression.NOT: {
1352: int type = iToken;
1353:
1354: read();
1355:
1356: return new Expression(type, readCondition(), null);
1357: }
1358: case Expression.EXISTS: {
1359: int type = iToken;
1360:
1361: read();
1362: readThis(Expression.OPEN);
1363:
1364: int brackets = 0;
1365:
1366: if (iToken == Expression.OPEN) {
1367: brackets += parseOpenBrackets() + 1;
1368:
1369: read();
1370: }
1371:
1372: Trace.check(iToken == Expression.SELECT,
1373: Trace.UNEXPECTED_TOKEN);
1374:
1375: SubQuery sq = parseSubquery(brackets, null, false,
1376: Expression.EXISTS);
1377: Expression s = new Expression(sq);
1378:
1379: read();
1380: readThis(Expression.CLOSE);
1381:
1382: return new Expression(type, s, null);
1383: }
1384: default: {
1385: Expression a = readConcat();
1386:
1387: if (iToken == Expression.IS) {
1388: read();
1389:
1390: boolean not;
1391:
1392: if (iToken == Expression.NOT) {
1393: not = true;
1394:
1395: read();
1396: } else {
1397: not = false;
1398: }
1399:
1400: Trace.check(
1401: iToken == Expression.VALUE && oData == null,
1402: Trace.UNEXPECTED_TOKEN);
1403: read();
1404:
1405: // TODO: the TableFilter needs a right hand side to avoid null pointer exceptions...
1406: a = new Expression(Expression.IS_NULL, a,
1407: new Expression(Types.NULL, null));
1408:
1409: if (not) {
1410: a = new Expression(Expression.NOT, a, null);
1411: }
1412:
1413: return a;
1414: }
1415:
1416: boolean not = false;
1417:
1418: if (iToken == Expression.NOT) {
1419: not = true;
1420:
1421: read();
1422: }
1423:
1424: switch (iToken) {
1425:
1426: case Expression.LIKE: {
1427: a = parseLikePredicate(a);
1428:
1429: break;
1430: }
1431: case Expression.BETWEEN: {
1432: a = parseBetweenPredicate(a);
1433:
1434: break;
1435: }
1436: case Expression.IN: {
1437: a = this .parseInPredicate(a);
1438:
1439: break;
1440: }
1441: default: {
1442: Trace.check(!not, Trace.UNEXPECTED_TOKEN);
1443:
1444: if (Expression.isCompare(iToken)) {
1445: int type = iToken;
1446:
1447: read();
1448:
1449: return new Expression(type, a, readConcat());
1450: }
1451:
1452: return a;
1453: }
1454: }
1455:
1456: if (not) {
1457: a = new Expression(Expression.NOT, a, null);
1458: }
1459:
1460: return a;
1461: }
1462: }
1463: }
1464:
1465: private Expression parseLikePredicate(Expression a)
1466: throws HsqlException {
1467:
1468: read();
1469:
1470: Expression b = readConcat();
1471: Character escape = null;
1472:
1473: if (sToken.equals(Token.T_ESCAPE)) {
1474: read();
1475:
1476: Expression c = readTerm();
1477:
1478: Trace.check(c.getType() == Expression.VALUE,
1479: Trace.INVALID_ESCAPE);
1480:
1481: String s = (String) c.getValue(session, Types.VARCHAR);
1482:
1483: // boucherb@users 2003-09-25
1484: // CHECKME:
1485: // Assert s.length() == 1 for xxxchar comparisons?
1486: // TODO:
1487: // SQL200n says binary escape can be 1 or more octets.
1488: // Maybe we need to retain s and check this in
1489: // Expression.resolve()?
1490: if (s == null || s.length() < 1) {
1491: throw Trace.error(Trace.INVALID_ESCAPE, s);
1492: }
1493:
1494: escape = new Character(s.charAt(0));
1495: }
1496:
1497: boolean hasCollation = database.collation.name != null;
1498:
1499: a = new Expression(a, b, escape, hasCollation);
1500:
1501: return a;
1502: }
1503:
1504: private Expression parseBetweenPredicate(Expression a)
1505: throws HsqlException {
1506:
1507: read();
1508:
1509: Expression l = new Expression(Expression.BIGGER_EQUAL, a,
1510: readConcat());
1511:
1512: readThis(Expression.AND);
1513:
1514: Expression h = new Expression(Expression.SMALLER_EQUAL, a,
1515: readConcat());
1516:
1517: if (l.getArg().isParam() && l.getArg2().isParam()) {
1518: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
1519: Trace.Parser_ambiguous_between1);
1520: }
1521:
1522: if (h.getArg().isParam() && h.getArg2().isParam()) {
1523: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
1524: Trace.Parser_ambiguous_between1);
1525: }
1526:
1527: return new Expression(Expression.AND, l, h);
1528: }
1529:
1530: private Expression parseInPredicate(Expression a)
1531: throws HsqlException {
1532:
1533: int type = iToken;
1534:
1535: read();
1536: readThis(Expression.OPEN);
1537:
1538: Expression b = null;
1539: int brackets = 0;
1540:
1541: if (iToken == Expression.OPEN) {
1542: brackets += parseOpenBrackets() + 1;
1543:
1544: read();
1545: }
1546:
1547: if (iToken == Expression.SELECT) {
1548: SubQuery sq = parseSubquery(brackets, null, false,
1549: Expression.IN);
1550:
1551: // until we support rows in IN predicates
1552: Trace.check(sq.select.iResultLen == 1,
1553: Trace.SINGLE_COLUMN_EXPECTED);
1554:
1555: b = new Expression(sq);
1556:
1557: read();
1558: } else {
1559: tokenizer.back();
1560:
1561: HsqlArrayList v = new HsqlArrayList();
1562:
1563: while (true) {
1564: Expression value = parseExpression();
1565:
1566: if (value.exprType == Expression.VALUE
1567: && value.valueData == null && !value.isParam()) {
1568: throw Trace.error(Trace.NULL_IN_VALUE_LIST);
1569: }
1570:
1571: v.add(value);
1572: read();
1573:
1574: if (iToken != Expression.COMMA) {
1575: break;
1576: }
1577: }
1578:
1579: Expression[] valueList;
1580:
1581: valueList = (Expression[]) v.toArray(new Expression[v
1582: .size()]);
1583: b = new Expression(valueList);
1584: }
1585:
1586: readThis(Expression.CLOSE);
1587:
1588: return new Expression(type, a, b);
1589: }
1590:
1591: private Expression parseAllAnyPredicate() throws HsqlException {
1592:
1593: int type = iToken;
1594:
1595: read();
1596: readThis(Expression.OPEN);
1597:
1598: Expression b = null;
1599: int brackets = 0;
1600:
1601: if (iToken == Expression.OPEN) {
1602: brackets += parseOpenBrackets() + 1;
1603:
1604: read();
1605: }
1606:
1607: if (iToken != Expression.SELECT) {
1608: throw Trace.error(Trace.INVALID_IDENTIFIER);
1609: }
1610:
1611: SubQuery sq = parseSubquery(brackets, null, false, type);
1612: Select select = sq.select;
1613:
1614: // until we support rows
1615: Trace.check(sq.select.iResultLen == 1,
1616: Trace.SINGLE_COLUMN_EXPECTED);
1617:
1618: b = new Expression(sq);
1619:
1620: read();
1621: readThis(Expression.CLOSE);
1622:
1623: return new Expression(type, b, null);
1624: }
1625:
1626: /**
1627: * Method declaration
1628: *
1629: * @param type
1630: * @throws HsqlException
1631: */
1632: private void readThis(int type) throws HsqlException {
1633: Trace.check(iToken == type, Trace.UNEXPECTED_TOKEN);
1634: read();
1635: }
1636:
1637: /**
1638: * Method declaration
1639: *
1640: * @return a concatenation, possibly degenerate
1641: * @throws HsqlException
1642: */
1643: private Expression readConcat() throws HsqlException {
1644:
1645: Expression r = readSum();
1646:
1647: while (iToken == Expression.CONCAT) {
1648: int type = Expression.CONCAT;
1649: Expression a = r;
1650:
1651: read();
1652:
1653: r = new Expression(type, a, readSum());
1654: }
1655:
1656: return r;
1657: }
1658:
1659: static HashMap simpleFunctions = new HashMap();
1660:
1661: static {
1662: simpleFunctions.put(Token.T_CURRENT_DATE,
1663: "org.hsqldb.Library.curdate");
1664: simpleFunctions.put(Token.T_CURRENT_TIME,
1665: "org.hsqldb.Library.curtime");
1666: simpleFunctions.put(Token.T_CURRENT_TIMESTAMP,
1667: "org.hsqldb.Library.now");
1668: simpleFunctions.put(Token.T_CURRENT_USER,
1669: "org.hsqldb.Library.user");
1670: simpleFunctions.put(Token.T_SYSDATE,
1671: "org.hsqldb.Library.curdate");
1672: simpleFunctions.put(Token.T_NOW, "org.hsqldb.Library.now");
1673: simpleFunctions
1674: .put(Token.T_TODAY, "org.hsqldb.Library.curdate");
1675: }
1676:
1677: /**
1678: * Method declaration
1679: *
1680: * @return a summation, possibly degenerate
1681: * @throws HsqlException
1682: */
1683: private Expression readSum() throws HsqlException {
1684:
1685: Expression r = readFactor();
1686:
1687: while (true) {
1688: int type;
1689:
1690: if (iToken == Expression.PLUS) {
1691: type = Expression.ADD;
1692: } else if (iToken == Expression.NEGATE) {
1693: type = Expression.SUBTRACT;
1694: } else {
1695: break;
1696: }
1697:
1698: Expression a = r;
1699:
1700: read();
1701:
1702: r = new Expression(type, a, readFactor());
1703: }
1704:
1705: return r;
1706: }
1707:
1708: /**
1709: * Method declaration
1710: *
1711: * @return a product, possibly degenerate
1712: * @throws HsqlException
1713: */
1714: private Expression readFactor() throws HsqlException {
1715:
1716: Expression r = readTerm();
1717:
1718: while (iToken == Expression.MULTIPLY
1719: || iToken == Expression.DIVIDE) {
1720: int type = iToken;
1721: Expression a = r;
1722:
1723: read();
1724:
1725: r = new Expression(type, a, readTerm());
1726: }
1727:
1728: return r;
1729: }
1730:
1731: /**
1732: * Method declaration
1733: *
1734: * @return a term, possibly composite
1735: * @throws HsqlException
1736: */
1737: private Expression readTerm() throws HsqlException {
1738:
1739: Expression r = null;
1740:
1741: switch (iToken) {
1742:
1743: case Expression.COLUMN: {
1744: r = readColumnExpression();
1745:
1746: break;
1747: }
1748: case Expression.NEGATE: {
1749: int type = iToken;
1750:
1751: read();
1752:
1753: r = new Expression(type, readTerm(), null);
1754:
1755: Trace.check(!r.getArg().isParam(),
1756: Trace.Expression_resolveTypes1);
1757:
1758: break;
1759: }
1760: case Expression.PLUS: {
1761: read();
1762:
1763: r = readTerm();
1764:
1765: Trace.check(!r.isParam(), Trace.UNRESOLVED_PARAMETER_TYPE,
1766: Trace.getMessage(Trace.Expression_resolveTypes1));
1767:
1768: break;
1769: }
1770: case Expression.OPEN: {
1771: read();
1772:
1773: r = readOr();
1774:
1775: if (iToken != Expression.CLOSE) {
1776: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
1777: }
1778:
1779: read();
1780:
1781: break;
1782: }
1783: case Expression.VALUE: {
1784: r = new Expression(iType, oData);
1785:
1786: read();
1787:
1788: break;
1789: }
1790: case Expression.PARAM: {
1791: r = new Expression(Types.NULL, null, true);
1792:
1793: parameters.add(r);
1794: read();
1795:
1796: break;
1797: }
1798: case Expression.SELECT: {
1799: SubQuery sq = parseSubquery(0, null, false,
1800: Expression.SELECT);
1801:
1802: r = new Expression(sq);
1803:
1804: read();
1805:
1806: break;
1807: }
1808: case Expression.ANY:
1809: case Expression.ALL: {
1810: r = parseAllAnyPredicate();
1811:
1812: // read();
1813: break;
1814: }
1815: case Expression.MULTIPLY: {
1816: r = new Expression(sSchema, sTable, (String) null);
1817:
1818: read();
1819:
1820: break;
1821: }
1822: case Expression.CASEWHEN:
1823: return readCaseWhenExpression();
1824:
1825: case Expression.CASE:
1826: return readCaseExpression();
1827:
1828: case Expression.NULLIF:
1829: return readNullIfExpression();
1830:
1831: case Expression.COALESCE:
1832: case Expression.IFNULL:
1833: return readCoalesceExpression();
1834:
1835: case Expression.SEQUENCE:
1836: return readSequenceExpression();
1837:
1838: case Expression.CAST:
1839: case Expression.CONVERT:
1840: return readCastExpression();
1841:
1842: case Expression.EXTRACT:
1843: return readExtractExpression();
1844:
1845: case Expression.TRIM:
1846: return readTrimExpression();
1847:
1848: case Expression.POSITION:
1849: return readPositionExpression();
1850:
1851: case Expression.SUBSTRING:
1852: return readSubstringExpression();
1853:
1854: default:
1855: if (Expression.isAggregate(iToken)) {
1856: return readAggregate();
1857: } else {
1858: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
1859: }
1860: }
1861:
1862: return r;
1863: }
1864:
1865: /**
1866: * reads a CASE .. WHEN expression
1867: */
1868: Expression readCaseExpression() throws HsqlException {
1869:
1870: int type = Expression.CASEWHEN;
1871: Expression r = null;
1872: Expression predicand = null;
1873:
1874: read();
1875:
1876: if (iToken != Expression.WHEN) {
1877: predicand = readOr();
1878: }
1879:
1880: Expression leaf = null;
1881:
1882: while (true) {
1883: Expression casewhen = parseCaseWhen(predicand);
1884:
1885: if (r == null) {
1886: r = casewhen;
1887: } else {
1888: leaf.setRightExpression(casewhen);
1889: }
1890:
1891: leaf = casewhen.getRightExpression();
1892:
1893: if (iToken != Expression.WHEN) {
1894: break;
1895: }
1896: }
1897:
1898: if (iToken == Expression.ELSE) {
1899: readThis(Expression.ELSE);
1900:
1901: Expression elsexpr = readOr();
1902:
1903: leaf.setRightExpression(elsexpr);
1904: }
1905:
1906: readThis(Expression.ENDWHEN);
1907:
1908: return r;
1909: }
1910:
1911: /**
1912: * Reads part of a CASE .. WHEN expression
1913: */
1914: private Expression parseCaseWhen(Expression r) throws HsqlException {
1915:
1916: readThis(Expression.WHEN);
1917:
1918: Expression condition;
1919:
1920: if (r == null) {
1921: condition = readOr();
1922: } else {
1923: condition = new Expression(Expression.EQUAL, r, readOr());
1924: }
1925:
1926: readThis(Expression.THEN);
1927:
1928: Expression current = readOr();
1929: Expression alternatives = new Expression(
1930: Expression.ALTERNATIVE, current, new Expression(
1931: Types.NULL, null));
1932: Expression casewhen = new Expression(Expression.CASEWHEN,
1933: condition, alternatives);
1934:
1935: return casewhen;
1936: }
1937:
1938: /**
1939: * reads a CASEWHEN expression
1940: */
1941: private Expression readCaseWhenExpression() throws HsqlException {
1942:
1943: int type = iToken;
1944: Expression r = null;
1945:
1946: read();
1947: readThis(Expression.OPEN);
1948:
1949: r = readOr();
1950:
1951: readThis(Expression.COMMA);
1952:
1953: Expression thenelse = readOr();
1954:
1955: readThis(Expression.COMMA);
1956:
1957: // thenelse part is never evaluated; only init
1958: thenelse = new Expression(Expression.ALTERNATIVE, thenelse,
1959: readOr());
1960: r = new Expression(type, r, thenelse);
1961:
1962: readThis(Expression.CLOSE);
1963:
1964: return r;
1965: }
1966:
1967: /**
1968: * Reads a CAST or CONVERT expression
1969: */
1970: private Expression readCastExpression() throws HsqlException {
1971:
1972: boolean isConvert = iToken == Expression.CONVERT;
1973:
1974: read();
1975: readThis(Expression.OPEN);
1976:
1977: Expression r = readOr();
1978:
1979: if (isConvert) {
1980: readThis(Expression.COMMA);
1981: } else {
1982: readThis(Expression.AS);
1983: }
1984:
1985: int typeNr = Types.getTypeNr(sToken);
1986: int length = 0;
1987: int scale = 0;
1988: boolean hasLength = false;
1989:
1990: if (Types.acceptsPrecisionCreateParam(typeNr)
1991: && tokenizer.isGetThis(Token.T_OPENBRACKET)) {
1992: length = tokenizer.getInt();
1993: hasLength = true;
1994:
1995: if (Types.acceptsScaleCreateParam(typeNr)
1996: && tokenizer.isGetThis(Token.T_COMMA)) {
1997: scale = tokenizer.getInt();
1998: }
1999:
2000: tokenizer.getThis(Token.T_CLOSEBRACKET);
2001: }
2002:
2003: if (typeNr == Types.FLOAT && length > 53) {
2004: throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
2005: }
2006:
2007: if (typeNr == Types.TIMESTAMP) {
2008: if (!hasLength) {
2009: length = 6;
2010: } else if (length != 0 && length != 6) {
2011: throw Trace.error(Trace.NUMERIC_VALUE_OUT_OF_RANGE);
2012: }
2013: }
2014:
2015: if (r.isParam()) {
2016: r.setDataType(typeNr);
2017: }
2018:
2019: r = new Expression(r, typeNr, length, scale);
2020:
2021: read();
2022: readThis(Expression.CLOSE);
2023:
2024: return r;
2025: }
2026:
2027: /**
2028: * reads a Column or Function expression
2029: */
2030: private Expression readColumnExpression() throws HsqlException {
2031:
2032: String name = sToken;
2033: Expression r = new Expression(sTable, name, wasQuoted);
2034:
2035: read();
2036:
2037: if (iToken == Expression.OPEN) {
2038: String javaName = database.getJavaName(name);
2039: Function f = new Function(name, javaName, false);
2040:
2041: session.check(javaName);
2042:
2043: int len = f.getArgCount();
2044: int i = 0;
2045:
2046: read();
2047:
2048: if (iToken != Expression.CLOSE) {
2049: while (true) {
2050: f.setArgument(i++, readOr());
2051:
2052: if (iToken != Expression.COMMA) {
2053: break;
2054: }
2055:
2056: read();
2057: }
2058: }
2059:
2060: readThis(Expression.CLOSE);
2061:
2062: r = new Expression(f);
2063: } else {
2064: String javaName = (String) simpleFunctions.get(name);
2065:
2066: if (javaName != null) {
2067: Function f = new Function(name, javaName, true);
2068:
2069: r = new Expression(f);
2070: }
2071: }
2072:
2073: return r;
2074: }
2075:
2076: /**
2077: * reads a CONCAT expression
2078: */
2079: private Expression readConcatExpression() throws HsqlException {
2080:
2081: int type = iToken;
2082:
2083: read();
2084: readThis(Expression.OPEN);
2085:
2086: Expression r = readOr();
2087:
2088: readThis(Expression.COMMA);
2089:
2090: r = new Expression(type, r, readOr());
2091:
2092: readThis(Expression.CLOSE);
2093:
2094: return r;
2095: }
2096:
2097: /**
2098: * Reads a NULLIF expression
2099: */
2100: private Expression readNullIfExpression() throws HsqlException {
2101:
2102: // turn into a CASEWHEN
2103: read();
2104: readThis(Expression.OPEN);
2105:
2106: Expression r = readOr();
2107:
2108: readThis(Expression.COMMA);
2109:
2110: Expression thenelse = new Expression(Expression.ALTERNATIVE,
2111: new Expression(Types.NULL, null), r);
2112:
2113: r = new Expression(Expression.EQUAL, r, readOr());
2114: r = new Expression(Expression.CASEWHEN, r, thenelse);
2115:
2116: readThis(Expression.CLOSE);
2117:
2118: return r;
2119: }
2120:
2121: /**
2122: * Reads a COALESE or IFNULL expression
2123: */
2124: private Expression readCoalesceExpression() throws HsqlException {
2125:
2126: Expression r = null;
2127:
2128: // turn into a CASEWHEN
2129: read();
2130: readThis(Expression.OPEN);
2131:
2132: Expression leaf = null;
2133:
2134: while (true) {
2135: Expression current = readOr();
2136:
2137: if (leaf != null && iToken == Expression.CLOSE) {
2138: readThis(Expression.CLOSE);
2139: leaf.setLeftExpression(current);
2140:
2141: break;
2142: }
2143:
2144: Expression condition = new Expression(Expression.IS_NULL,
2145: current, null);
2146: Expression alternatives = new Expression(
2147: Expression.ALTERNATIVE, new Expression(Types.NULL,
2148: null), current);
2149: Expression casewhen = new Expression(Expression.CASEWHEN,
2150: condition, alternatives);
2151:
2152: if (r == null) {
2153: r = casewhen;
2154: } else {
2155: leaf.setLeftExpression(casewhen);
2156: }
2157:
2158: leaf = alternatives;
2159:
2160: readThis(Expression.COMMA);
2161: }
2162:
2163: return r;
2164: }
2165:
2166: /**
2167: * Reads an EXTRACT expression
2168: */
2169: private Expression readExtractExpression() throws HsqlException {
2170:
2171: read();
2172: readThis(Expression.OPEN);
2173:
2174: String name = sToken;
2175:
2176: // must be an accepted identifier
2177: if (!Expression.SQL_EXTRACT_FIELD_NAMES.contains(name)) {
2178: throw Trace.error(Trace.UNEXPECTED_TOKEN, sToken);
2179: }
2180:
2181: readToken();
2182: readThis(Expression.FROM);
2183:
2184: // the name argument is DAY, MONTH etc. - OK for now for CHECK constraints
2185: Function f = new Function(name, database.getJavaName(name),
2186: false);
2187:
2188: f.setArgument(0, readOr());
2189: readThis(Expression.CLOSE);
2190:
2191: return new Expression(f);
2192: }
2193:
2194: /**
2195: * Reads a POSITION expression
2196: */
2197: private Expression readPositionExpression() throws HsqlException {
2198:
2199: read();
2200: readThis(Expression.OPEN);
2201:
2202: Function f = new Function(Token.T_POSITION,
2203: "org.hsqldb.Library.position", false);
2204:
2205: f.setArgument(0, readTerm());
2206: readThis(Expression.IN);
2207: f.setArgument(1, readOr());
2208: readThis(Expression.CLOSE);
2209:
2210: return new Expression(f);
2211: }
2212:
2213: /**
2214: * Reads a SUBSTRING expression
2215: */
2216: private Expression readSubstringExpression() throws HsqlException {
2217:
2218: boolean commas = false;
2219:
2220: read();
2221: readThis(Expression.OPEN);
2222:
2223: // OK for now for CHECK search conditions
2224: Function f = new Function(Token.T_SUBSTRING,
2225: "org.hsqldb.Library.substring", false);
2226:
2227: f.setArgument(0, readTerm());
2228:
2229: if (iToken == Expression.FROM) {
2230: readThis(Expression.FROM);
2231: } else {
2232: readThis(Expression.COMMA);
2233:
2234: commas = true;
2235: }
2236:
2237: f.setArgument(1, readOr());
2238:
2239: Expression count = null;
2240:
2241: if (!commas && iToken == Expression.FOR) {
2242: readThis(Expression.FOR);
2243:
2244: count = readTerm();
2245: } else if (commas && iToken == Expression.COMMA) {
2246: readThis(Expression.COMMA);
2247:
2248: count = readTerm();
2249: }
2250:
2251: f.setArgument(2, count);
2252: readThis(Expression.CLOSE);
2253:
2254: return new Expression(f);
2255: }
2256:
2257: private Expression readSequenceExpression() throws HsqlException {
2258:
2259: tokenizer.getThis(Token.T_VALUE);
2260: tokenizer.getThis(Token.T_FOR);
2261:
2262: String name = tokenizer.getName();
2263: String schemaname = tokenizer.getLongNameFirst();
2264:
2265: schemaname = session.getSchemaName(schemaname);
2266:
2267: // Read next because Tokenizer.back() will run after this.
2268: // (This is because usually when reading expressions, you need to
2269: // get the following token to know whether you have finished.
2270: tokenizer.getString();
2271:
2272: NumberSequence sequence = database.schemaManager.getSequence(
2273: name, schemaname);
2274:
2275: return new Expression(sequence);
2276: }
2277:
2278: /**
2279: * Reads a TRIM expression
2280: */
2281: private Expression readTrimExpression() throws HsqlException {
2282:
2283: read();
2284: readThis(Expression.OPEN);
2285:
2286: String type = sToken;
2287:
2288: if (Expression.SQL_TRIM_SPECIFICATION.contains(type)) {
2289: read();
2290: } else {
2291: type = Token.T_BOTH;
2292: }
2293:
2294: String trimstr;
2295:
2296: if (sToken.length() == 1) {
2297: trimstr = sToken;
2298:
2299: read();
2300: } else {
2301: trimstr = " ";
2302: }
2303:
2304: readThis(Expression.FROM);
2305:
2306: Expression trim = new Expression(Types.CHAR, trimstr);
2307: Expression leading;
2308: Expression trailing;
2309:
2310: if (type.equals(Token.T_LEADING)) {
2311: leading = new Expression(true);
2312: trailing = new Expression(false);
2313: } else if (type.equals(Token.T_TRAILING)) {
2314: leading = new Expression(false);
2315: trailing = new Expression(true);
2316: } else {
2317: leading = trailing = new Expression(true);
2318: }
2319:
2320: // name argument is OK for now for CHECK constraints
2321: Function f = new Function(Token.T_TRIM,
2322: "org.hsqldb.Library.trim", false);
2323:
2324: f.setArgument(0, readOr());
2325: f.setArgument(1, trim);
2326: f.setArgument(2, leading);
2327: f.setArgument(3, trailing);
2328: readThis(Expression.CLOSE);
2329:
2330: return new Expression(f);
2331: }
2332:
2333: /**
2334: * Reads a DEFAULT clause expression.
2335: */
2336: Expression readDefaultClause(int dataType) throws HsqlException {
2337:
2338: Expression r = null;
2339:
2340: read();
2341:
2342: switch (iToken) {
2343:
2344: case Expression.COLUMN: {
2345: String name = sToken;
2346: String javaName = (String) simpleFunctions.get(name);
2347:
2348: if (javaName != null) {
2349: Function f = new Function(name, javaName, true);
2350:
2351: return new Expression(f);
2352: }
2353:
2354: break;
2355: }
2356: case Expression.NEGATE: {
2357: int exprType = iToken;
2358:
2359: read();
2360:
2361: if (iToken == Expression.VALUE) {
2362: oData = Column.convertObject(oData, dataType);
2363:
2364: return new Expression(exprType, new Expression(
2365: dataType, oData), null);
2366: }
2367:
2368: break;
2369: }
2370: case Expression.VALUE: {
2371: String name = sToken.toUpperCase(Locale.ENGLISH);
2372: String javaName = (String) simpleFunctions.get(name);
2373:
2374: if (Types.isDatetimeType(dataType) && javaName != null) {
2375: Function f = new Function(name, javaName, true);
2376:
2377: return new Expression(f);
2378: }
2379:
2380: oData = Column.convertObject(oData, dataType);
2381:
2382: return new Expression(dataType, oData);
2383: }
2384: }
2385:
2386: throw Trace.error(Trace.WRONG_DEFAULT_CLAUSE, sToken);
2387: }
2388:
2389: /**
2390: * Method declaration
2391: *
2392: * @throws HsqlException
2393: */
2394: private void read() throws HsqlException {
2395:
2396: sToken = tokenizer.getString();
2397: wasQuoted = tokenizer.wasQuotedIdentifier();
2398:
2399: if (tokenizer.wasValue()) {
2400: iToken = Expression.VALUE;
2401: oData = tokenizer.getAsValue();
2402: iType = tokenizer.getType();
2403: } else if (tokenizer.wasSimpleName()) {
2404: iToken = Expression.COLUMN;
2405: sTable = null;
2406: } else if (tokenizer.wasLongName()) {
2407: sSchema = tokenizer.getLongNamePre();
2408: sTable = tokenizer.getLongNameFirst();
2409:
2410: if (sToken.equals(Token.T_MULTIPLY)) {
2411: iToken = Expression.MULTIPLY;
2412: } else {
2413: iToken = Expression.COLUMN;
2414: }
2415: } else if (tokenizer.wasParameter()) {
2416: iToken = Expression.PARAM;
2417: } else if (sToken.length() == 0) {
2418: iToken = Expression.END;
2419: } else {
2420: iToken = tokenSet.get(sToken, -1);
2421:
2422: if (iToken == -1) {
2423: iToken = Expression.END;
2424: }
2425:
2426: switch (iToken) {
2427:
2428: case Expression.COMMA:
2429: case Expression.EQUAL:
2430: case Expression.NOT_EQUAL:
2431: case Expression.SMALLER:
2432: case Expression.BIGGER:
2433: case Expression.SMALLER_EQUAL:
2434: case Expression.BIGGER_EQUAL:
2435: case Expression.AND:
2436: case Expression.OR:
2437: case Expression.NOT:
2438: case Expression.ALL:
2439: case Expression.ANY:
2440: case Expression.IN:
2441: case Expression.EXISTS:
2442: case Expression.BETWEEN:
2443: case Expression.PLUS:
2444: case Expression.NEGATE:
2445: case Expression.DIVIDE:
2446: case Expression.CONCAT:
2447: case Expression.OPEN:
2448: case Expression.CLOSE:
2449: case Expression.SELECT:
2450: case Expression.LIKE:
2451: case Expression.COUNT:
2452: case Expression.SUM:
2453: case Expression.MIN:
2454: case Expression.MAX:
2455: case Expression.AVG:
2456: case Expression.EVERY:
2457: case Expression.SOME:
2458: case Expression.STDDEV_POP:
2459: case Expression.STDDEV_SAMP:
2460: case Expression.VAR_POP:
2461: case Expression.VAR_SAMP:
2462: case Expression.CONVERT:
2463: case Expression.CAST:
2464: case Expression.SEQUENCE:
2465: case Expression.IFNULL:
2466: case Expression.COALESCE:
2467: case Expression.NULLIF:
2468: case Expression.CASE:
2469: case Expression.WHEN:
2470: case Expression.THEN:
2471: case Expression.ELSE:
2472: case Expression.ENDWHEN:
2473: case Expression.CASEWHEN:
2474: case Expression.EXTRACT:
2475: case Expression.POSITION:
2476: case Expression.SUBSTRING:
2477: case Expression.FROM:
2478: case Expression.FOR:
2479: case Expression.END:
2480: case Expression.PARAM:
2481: case Expression.TRIM:
2482: case Expression.LEADING:
2483: case Expression.TRAILING:
2484: case Expression.BOTH:
2485: case Expression.AS:
2486: case Expression.IS:
2487: case Expression.DISTINCT:
2488: break; // nothing else required, iToken initialized properly
2489:
2490: case Expression.MULTIPLY:
2491: sTable = null; // in case of ASTERIX
2492: break;
2493:
2494: default:
2495: iToken = Expression.END;
2496: }
2497: }
2498: }
2499:
2500: /**
2501: * A workaround for parsing EXTRACT clause elements such as MONTH, DAY
2502: * and YEAR, without having to make each of them SQL KEYWORDS in Tokenizer.
2503: *
2504: * @throws HsqlException if a tokenization error occurs
2505: */
2506: private void readToken() throws HsqlException {
2507: sToken = tokenizer.getString();
2508: iToken = tokenSet.get(sToken, -1);
2509: }
2510:
2511: private static IntValueHashMap tokenSet = new IntValueHashMap(37);
2512:
2513: static {
2514: tokenSet.put(Token.T_COMMA, Expression.COMMA);
2515: tokenSet.put(Token.T_EQUALS, Expression.EQUAL);
2516: tokenSet.put("!=", Expression.NOT_EQUAL);
2517: tokenSet.put("<>", Expression.NOT_EQUAL);
2518: tokenSet.put("<", Expression.SMALLER);
2519: tokenSet.put(">", Expression.BIGGER);
2520: tokenSet.put("<=", Expression.SMALLER_EQUAL);
2521: tokenSet.put(">=", Expression.BIGGER_EQUAL);
2522: tokenSet.put(Token.T_AND, Expression.AND);
2523: tokenSet.put(Token.T_NOT, Expression.NOT);
2524: tokenSet.put(Token.T_OR, Expression.OR);
2525: tokenSet.put(Token.T_ALL, Expression.ALL);
2526: tokenSet.put(Token.T_ANY, Expression.ANY);
2527: tokenSet.put(Token.T_IN, Expression.IN);
2528: tokenSet.put(Token.T_EXISTS, Expression.EXISTS);
2529: tokenSet.put(Token.T_BETWEEN, Expression.BETWEEN);
2530: tokenSet.put(Token.T_PLUS, Expression.PLUS);
2531: tokenSet.put("-", Expression.NEGATE);
2532: tokenSet.put(Token.T_MULTIPLY, Expression.MULTIPLY);
2533: tokenSet.put("/", Expression.DIVIDE);
2534: tokenSet.put("||", Expression.CONCAT);
2535: tokenSet.put(Token.T_OPENBRACKET, Expression.OPEN);
2536: tokenSet.put(Token.T_CLOSEBRACKET, Expression.CLOSE);
2537: tokenSet.put(Token.T_SELECT, Expression.SELECT);
2538: tokenSet.put(Token.T_LIKE, Expression.LIKE);
2539: tokenSet.put(Token.T_COUNT, Expression.COUNT);
2540: tokenSet.put(Token.T_SUM, Expression.SUM);
2541: tokenSet.put(Token.T_MIN, Expression.MIN);
2542: tokenSet.put(Token.T_MAX, Expression.MAX);
2543: tokenSet.put(Token.T_AVG, Expression.AVG);
2544: tokenSet.put(Token.T_EVERY, Expression.EVERY);
2545: tokenSet.put(Token.T_SOME, Expression.SOME);
2546: tokenSet.put(Token.T_STDDEV_POP, Expression.STDDEV_POP);
2547: tokenSet.put(Token.T_STDDEV_SAMP, Expression.STDDEV_SAMP);
2548: tokenSet.put(Token.T_VAR_POP, Expression.VAR_POP);
2549: tokenSet.put(Token.T_VAR_SAMP, Expression.VAR_SAMP);
2550: tokenSet.put(Token.T_IFNULL, Expression.IFNULL);
2551: tokenSet.put(Token.T_NVL, Expression.IFNULL);
2552: tokenSet.put(Token.T_NULLIF, Expression.NULLIF);
2553: tokenSet.put(Token.T_CONVERT, Expression.CONVERT);
2554: tokenSet.put(Token.T_CAST, Expression.CAST);
2555: tokenSet.put(Token.T_NEXT, Expression.SEQUENCE);
2556: tokenSet.put(Token.T_CASE, Expression.CASE);
2557: tokenSet.put(Token.T_WHEN, Expression.WHEN);
2558: tokenSet.put(Token.T_THEN, Expression.THEN);
2559: tokenSet.put(Token.T_ELSE, Expression.ELSE);
2560: tokenSet.put(Token.T_END, Expression.ENDWHEN);
2561: tokenSet.put(Token.T_CASEWHEN, Expression.CASEWHEN);
2562: tokenSet.put(Token.T_COALESCE, Expression.COALESCE);
2563: tokenSet.put(Token.T_EXTRACT, Expression.EXTRACT);
2564: tokenSet.put(Token.T_POSITION, Expression.POSITION);
2565: tokenSet.put(Token.T_FROM, Expression.FROM);
2566: tokenSet.put(Token.T_TRIM, Expression.TRIM);
2567: tokenSet.put(Token.T_SUBSTRING, Expression.SUBSTRING);
2568: tokenSet.put(Token.T_FOR, Expression.FOR);
2569: tokenSet.put(Token.T_AS, Expression.AS);
2570: tokenSet.put(Token.T_IS, Expression.IS);
2571: tokenSet.put(Token.T_QUESTION, Expression.PARAM);
2572: }
2573:
2574: // boucherb@users 20030411 - patch 1.7.2 - for prepared statements
2575: // ---------------------------------------------------------------
2576: HsqlArrayList parameters = new HsqlArrayList();
2577: private static final Expression[] noParameters = new Expression[0];
2578: private static final SubQuery[] noSubqueries = new SubQuery[0];
2579:
2580: /**
2581: * Destructive get method
2582: */
2583: Expression[] getParameters() {
2584:
2585: Expression[] result = parameters.size() == 0 ? noParameters
2586: : (Expression[]) parameters
2587: .toArray(new Expression[parameters.size()]);
2588:
2589: parameters.clear();
2590:
2591: return result;
2592: }
2593:
2594: void clearParameters() {
2595: parameters.clear();
2596: }
2597:
2598: // fredt - new implementation of subquery list
2599:
2600: /**
2601: * Sets the subqueries as belonging to the View being constructed
2602: */
2603: void setAsView(View view) {
2604:
2605: for (int i = 0; i < subQueryList.size(); i++) {
2606: SubQuery sq = (SubQuery) subQueryList.get(i);
2607:
2608: if (sq.view == null) {
2609: sq.view = view;
2610: }
2611: }
2612: }
2613:
2614: /**
2615: * Return the list of subqueries as an array sorted according to the order
2616: * of materialization, then clear the internal subquery list
2617: */
2618: SubQuery[] getSortedSubqueries() {
2619:
2620: if (subQueryList.size() == 0) {
2621: return noSubqueries;
2622: }
2623:
2624: subQueryList.sort((SubQuery) subQueryList.get(0));
2625:
2626: SubQuery[] subqueries = new SubQuery[subQueryList.size()];
2627:
2628: subQueryList.toArray(subqueries);
2629: subQueryList.clear();
2630:
2631: return subqueries;
2632: }
2633:
2634: /**
2635: * Retrieves a CALL-type CompiledStatement from this parse context.
2636: */
2637: CompiledStatement compileCallStatement() throws HsqlException {
2638:
2639: clearParameters();
2640:
2641: Expression expression = parseExpression();
2642: CompiledStatement cs = new CompiledStatement(session, database,
2643: session.currentSchema, expression,
2644: getSortedSubqueries(), getParameters());
2645:
2646: return cs;
2647: }
2648:
2649: /**
2650: * Retrieves a DELETE-type CompiledStatement from this parse context.
2651: */
2652: CompiledStatement compileDeleteStatement() throws HsqlException {
2653:
2654: String token;
2655: Expression condition = null;
2656: TableFilter tableFilter;
2657:
2658: clearParameters();
2659: tokenizer.getThis(Token.T_FROM);
2660:
2661: tableFilter = parseSimpleTableFilter(UserManager.DELETE);
2662: token = tokenizer.getString();
2663:
2664: if (token.equals(Token.T_WHERE)) {
2665: condition = parseExpression();
2666: } else {
2667: tokenizer.back();
2668: }
2669:
2670: CompiledStatement cs = new CompiledStatement(session, database,
2671: session.currentSchema, tableFilter, condition,
2672: getSortedSubqueries(), getParameters());
2673:
2674: return cs;
2675: }
2676:
2677: private void getInsertColumnValueExpressions(Table t,
2678: Expression[] acve, int len) throws HsqlException {
2679:
2680: tokenizer.getThis(Token.T_OPENBRACKET);
2681:
2682: for (int i = 0; i < len; i++) {
2683: Expression columnValExpression = parseExpression();
2684:
2685: columnValExpression.resolveTables(null);
2686: columnValExpression.resolveTypes(session);
2687:
2688: acve[i] = columnValExpression;
2689:
2690: String token = tokenizer.getSimpleToken();
2691:
2692: if (token.equals(Token.T_COMMA)) {
2693: continue;
2694: }
2695:
2696: if (token.equals(Token.T_CLOSEBRACKET)) {
2697: if (i == len - 1) {
2698: return;
2699: } else {
2700: break;
2701: }
2702: }
2703:
2704: tokenizer.throwUnexpected();
2705: }
2706:
2707: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
2708: }
2709:
2710: /**
2711: * Retrieves an INSERT_XXX-type CompiledStatement from this parse context.
2712: */
2713: CompiledStatement compileInsertStatement() throws HsqlException {
2714:
2715: clearParameters();
2716: tokenizer.getThis(Token.T_INTO);
2717:
2718: HsqlArrayList columnNames;
2719: boolean[] columnCheckList;
2720: int[] columnMap;
2721: int len;
2722: String token = tokenizer.getName();
2723: String schema = session.getSchemaName(tokenizer
2724: .getLongNameFirst());
2725: Table table = database.schemaManager.getTable(session, token,
2726: schema);
2727:
2728: checkTableWriteAccess(table, UserManager.INSERT);
2729:
2730: columnNames = null;
2731: columnCheckList = null;
2732: columnMap = table.getColumnMap();
2733: len = table.getColumnCount();
2734:
2735: int brackets = parseOpenBrackets();
2736:
2737: token = tokenizer.getString();
2738:
2739: if (brackets == 1 && !tokenizer.wasThis(Token.T_SELECT)) {
2740: brackets = 0;
2741:
2742: tokenizer.back();
2743:
2744: columnNames = getColumnNames(database, table, tokenizer,
2745: false);
2746:
2747: if (columnNames.size() > len) {
2748: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
2749: }
2750:
2751: len = columnNames.size();
2752: columnCheckList = table.getNewColumnCheckList();
2753: columnMap = new int[len];
2754:
2755: for (int i = 0; i < len; i++) {
2756: int ci = table.getColumnNr((String) columnNames.get(i));
2757:
2758: columnMap[i] = ci;
2759: columnCheckList[ci] = true;
2760: }
2761:
2762: token = tokenizer.getSimpleToken();
2763: } else if (!tokenizer.wasSimpleToken()) {
2764: tokenizer.throwUnexpected();
2765: }
2766:
2767: int command = Token.get(token);
2768:
2769: switch (command) {
2770:
2771: case Token.VALUES: {
2772: Expression[] acve = new Expression[len];
2773:
2774: getInsertColumnValueExpressions(table, acve, len);
2775:
2776: CompiledStatement cs = new CompiledStatement(
2777: session.currentSchema, table, columnMap, acve,
2778: columnCheckList, getSortedSubqueries(),
2779: getParameters());
2780:
2781: return cs;
2782: }
2783: case Token.OPENBRACKET: {
2784: brackets = parseOpenBrackets() + 1;
2785:
2786: tokenizer.getThis(Token.T_SELECT);
2787: }
2788: case Token.SELECT: {
2789:
2790: // accept ORDER BY or ORDRY BY with LIMIT
2791: Select select = parseSelect(brackets, true, false, true,
2792: true);
2793:
2794: if (len != select.iResultLen) {
2795: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
2796: }
2797:
2798: CompiledStatement cs = new CompiledStatement(session,
2799: database, session.currentSchema, table, columnMap,
2800: columnCheckList, select, getSortedSubqueries(),
2801: getParameters());
2802:
2803: return cs;
2804: }
2805: default: {
2806: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2807: }
2808: }
2809: }
2810:
2811: /**
2812: * Retrieves a SELECT-type CompiledStatement from this parse context.
2813: */
2814: CompiledStatement compileSelectStatement(int brackets)
2815: throws HsqlException {
2816:
2817: clearParameters();
2818:
2819: Select select = parseSelect(brackets, true, true, false, true);
2820:
2821: if (select.sIntoTable != null) {
2822: String name = select.sIntoTable.name;
2823: String schema = select.sIntoTable.schema.name;
2824:
2825: if (database.schemaManager.findUserTable(session, name,
2826: schema) != null) {
2827: throw Trace.error(Trace.TABLE_ALREADY_EXISTS, name);
2828: }
2829: }
2830:
2831: CompiledStatement cs = new CompiledStatement(session, database,
2832: session.currentSchema, select, getSortedSubqueries(),
2833: getParameters());
2834:
2835: return cs;
2836: }
2837:
2838: /**
2839: * Retrieves an UPDATE-type CompiledStatement from this parse context.
2840: */
2841: CompiledStatement compileUpdateStatement() throws HsqlException {
2842:
2843: String token;
2844: Table table;
2845: int[] colList;
2846: Expression[] exprList;
2847: int len;
2848: Expression cve;
2849: Expression condition;
2850:
2851: clearParameters();
2852:
2853: TableFilter tableFilter = parseSimpleTableFilter(UserManager.UPDATE);
2854:
2855: table = tableFilter.filterTable;
2856:
2857: tokenizer.getThis(Token.T_SET);
2858:
2859: colList = table.getNewColumnMap();
2860: exprList = new Expression[colList.length];
2861: len = 0;
2862: token = null;
2863:
2864: do {
2865: int ci = table.getColumnNr(tokenizer.getName());
2866: String tablename = tokenizer.getLongNameFirst();
2867:
2868: if (tablename != null
2869: && !tableFilter.getName().equals(tablename)) {
2870: throw Trace.error(Trace.TABLE_NOT_FOUND);
2871: }
2872:
2873: tokenizer.getThis(Token.T_EQUALS);
2874:
2875: cve = parseExpression();
2876:
2877: if (len == colList.length) {
2878:
2879: // too many (repeat) assignments
2880: throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH);
2881: }
2882:
2883: colList[len] = ci;
2884: exprList[len] = cve;
2885: token = tokenizer.getSimpleToken();
2886:
2887: len++;
2888: } while (token.equals(Token.T_COMMA));
2889:
2890: condition = null;
2891:
2892: if (token.equals(Token.T_WHERE)) {
2893: condition = parseExpression();
2894: } else {
2895: tokenizer.back();
2896: }
2897:
2898: colList = (int[]) ArrayUtil.resizeArray(colList, len);
2899: exprList = (Expression[]) ArrayUtil.resizeArray(exprList, len);
2900:
2901: CompiledStatement cs = new CompiledStatement(session, database,
2902: session.currentSchema, tableFilter, colList, exprList,
2903: condition, getSortedSubqueries(), getParameters());
2904:
2905: return cs;
2906: }
2907:
2908: int parseOpenBracketsSelect() throws HsqlException {
2909:
2910: int count = parseOpenBrackets();
2911:
2912: tokenizer.getThis(Token.T_SELECT);
2913:
2914: return count;
2915: }
2916:
2917: int parseOpenBrackets() throws HsqlException {
2918:
2919: int count = 0;
2920:
2921: while (tokenizer.isGetThis(Token.T_OPENBRACKET)) {
2922: count++;
2923: }
2924:
2925: return count;
2926: }
2927:
2928: int parseCloseBrackets(int limit) throws HsqlException {
2929:
2930: int count = 0;
2931:
2932: while (count < limit
2933: && tokenizer.isGetThis(Token.T_CLOSEBRACKET)) {
2934: count++;
2935: }
2936:
2937: return count;
2938: }
2939:
2940: HashMappedList parseColumnList() throws HsqlException {
2941: return processColumnList(tokenizer, false);
2942: }
2943:
2944: static HashMappedList processColumnList(Tokenizer tokenizer,
2945: boolean acceptAscDesc) throws HsqlException {
2946:
2947: HashMappedList list;
2948: String token;
2949:
2950: list = new HashMappedList();
2951:
2952: tokenizer.getThis(Token.T_OPENBRACKET);
2953:
2954: while (true) {
2955: token = tokenizer.getSimpleName();
2956:
2957: boolean result = list.add(token, null);
2958:
2959: if (!result) {
2960: throw Trace.error(Trace.COLUMN_ALREADY_EXISTS, token);
2961: }
2962:
2963: token = tokenizer.getSimpleToken();
2964:
2965: if (acceptAscDesc
2966: && (token.equals(Token.T_DESC) || token
2967: .equals(Token.T_ASC))) {
2968: token = tokenizer.getSimpleToken(); // OJ: eat it up
2969: }
2970:
2971: if (token.equals(Token.T_COMMA)) {
2972: continue;
2973: }
2974:
2975: if (token.equals(Token.T_CLOSEBRACKET)) {
2976: break;
2977: }
2978:
2979: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
2980: }
2981:
2982: return list;
2983: }
2984: }
|