📄 parser.java
字号:
/* Copyright (c) 1995-2000, The Hypersonic SQL Group. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the Hypersonic SQL Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Hypersonic SQL Group. * * * For work added by the HSQL Development Group: * * Copyright (c) 2001-2008, The HSQL Development Group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the HSQL Development Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */package org.hsqldb;import java.util.Locale;import org.hsqldb.HsqlNameManager.HsqlName;import org.hsqldb.lib.ArrayUtil;import org.hsqldb.lib.HashMap;import org.hsqldb.lib.HashMappedList;import org.hsqldb.lib.HsqlArrayList;import org.hsqldb.lib.IntKeyHashMap;import org.hsqldb.lib.IntValueHashMap;import org.hsqldb.lib.Iterator;import org.hsqldb.lib.StringConverter;import org.hsqldb.store.ValuePool;import org.hsqldb.lib.HashSet;// fredt@users 20020130 - patch 497872 by Nitin Chauhan - reordering for speed// fredt@users 20020215 - patch 1.7.0 by fredt - support GROUP BY with more than one column// fredt@users 20020215 - patch 1.7.0 by fredt - SQL standard quoted identifiers// fredt@users 20020218 - patch 1.7.0 by fredt - DEFAULT keyword// fredt@users 20020221 - patch 513005 by sqlbob@users - SELECT INTO types// fredt@users 20020425 - patch 548182 by skitt@users - DEFAULT enhancement// thertz@users 20020320 - patch 473613 by thertz - outer join condition bug// fredt@users 20021229 - patch 1.7.2 by fredt - new solution for above// fredt@users 20020420 - patch 523880 by leptipre@users - VIEW support// fredt@users 20020525 - patch 559914 by fredt@users - SELECT INTO logging// tony_lai@users 20021020 - patch 1.7.2 - improved aggregates and HAVING// aggregate functions can now be used in expressions - HAVING supported// kloska@users 20021030 - patch 1.7.2 - ON UPDATE CASCADE// fredt@users 20021112 - patch 1.7.2 by Nitin Chauhan - use of switch// rewrite of the majority of multiple if(){}else{} chains with switch(){}// boucherb@users 20030705 - patch 1.7.2 - prepared statement support// fredt@users 20030819 - patch 1.7.2 - EXTRACT({YEAR | MONTH | DAY | HOUR | MINUTE | SECOND } FROM datetime)// fredt@users 20030820 - patch 1.7.2 - CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH(string)// fredt@users 20030820 - patch 1.7.2 - POSITION(string IN string)// fredt@users 20030820 - patch 1.7.2 - SUBSTRING(string FROM pos [FOR length])// fredt@users 20030820 - patch 1.7.2 - TRIM({LEADING | TRAILING | BOTH} [<character>] FROM <string expression>)// fredt@users 20030820 - patch 1.7.2 - CASE [expr] WHEN ... THEN ... [ELSE ...] END and its variants// fredt@users 20030820 - patch 1.7.2 - NULLIF(expr,expr)// fredt@users 20030820 - patch 1.7.2 - COALESCE(expr,expr,...)// fredt@users 20031012 - patch 1.7.2 - improved scoping for column names in all areas// boucherb@users 200403xx - patch 1.7.2 - added support for prepared SELECT INTO// boucherb@users 200403xx - doc 1.7.2 - some// thomasm@users 20041001 - patch 1.7.3 - BOOLEAN undefined handling// fredt@users 20050220 - patch 1.8.0 - CAST with precision / scale/* todo: fredt - implement remaining numeric value functions (SQL92 6.6) * * EXTRACT({TIMEZONE_HOUR | TIMEZONE_MINUTE} FROM {datetime | interval}) *//** * Responsible for parsing non-DDL statements. * * Extensively rewritten and extended in successive versions of HSQLDB. * * @author Thomas Mueller (Hypersonic SQL Group) * @version 1.8.0 * @since Hypersonic SQL */class Parser { private Database database; private Tokenizer tokenizer; private Session session; private String sSchema; private String sTable; private String sToken; private boolean wasQuoted; private Object oData; private int iType; private int iToken; private boolean compilingView; // private int subQueryLevel; private HsqlArrayList subQueryList = new HsqlArrayList(); /** * Constructs a new Parser object with the given context. * * @param db the Database instance against which to resolve named * database object references * @param t the token source from which to parse commands * @param session the connected context */ Parser(Session session, Database db, Tokenizer t) { database = db; tokenizer = t; this.session = session; } /** * sets a flag indicating the parser is used for compiling a view */ void setCompilingView() { compilingView = true; } /** * determines whether the parser is used for compiling a view */ boolean isCompilingView() { return compilingView; } /** * Resets this parse context with the given SQL character sequence. * * Internal structures are reset as though a new parser were created * with the given sql and the originally specified database and session * * @param a new SQL character sequence to replace the current one */ void reset(String sql) { sTable = null; sToken = null; oData = null; tokenizer.reset(sql); subQueryList.clear(); subQueryLevel = 0; parameters.clear(); } /** * Tests whether the parsing session has the given write access on the * given Table object. <p> * * @param table the Table object to check * @param userRight the numeric code of the right to check * @throws HsqlException if the session user does not have the right * or the given Table object is simply not writable (e.g. is a * non-updateable View) */ void checkTableWriteAccess(Table table, int userRight) throws HsqlException { // session level user rights session.checkReadWrite(); // object level user rights session.check(table.getName(), userRight); // object type if (table.isView()) { throw Trace.error(Trace.NOT_A_TABLE, table.getName().name); } // object readonly table.checkDataReadOnly(); } /** * Parses a comma-separated, right-bracket terminated list of column * names. <p> * * @param db the Database instance whose name manager is to provide the * resulting HsqlName objects, when the full argument is true * @param t the tokenizer representing the character sequence to be parsed * @param full if true, generate a list of HsqlNames, else a list of * String objects */ static HsqlArrayList getColumnNames(Database db, Table table, Tokenizer t, boolean full) throws HsqlException { HsqlArrayList columns = new HsqlArrayList(); while (true) { if (full) { String token = t.getSimpleName(); boolean quoted = t.wasQuotedIdentifier(); HsqlName name = db.nameManager.newHsqlName(token, quoted); columns.add(name); } else { columns.add(t.getName()); if (t.wasLongName() && !t.getLongNameFirst().equals( table.getName().name)) { throw (Trace.error(Trace.TABLE_NOT_FOUND, t.getLongNameFirst())); } } String token = t.getSimpleToken(); if (token.equals(Token.T_COMMA)) { continue; } if (token.equals(Token.T_CLOSEBRACKET)) { break; } t.throwUnexpected(); } return columns; } /** * The SubQuery objects are added to the end of subquery list. * * When parsing the SELECT for a view, optional HsqlName[] array is used * for view column aliases. * */ SubQuery parseSubquery(int brackets, HsqlName[] colNames, boolean resolveAll, int predicateType) throws HsqlException { SubQuery sq; sq = new SubQuery(); subQueryLevel++; boolean canHaveOrder = predicateType == Expression.VIEW || predicateType == Expression.SELECT; boolean canHaveLimit = predicateType == Expression.SELECT || predicateType == Expression.VIEW || predicateType == Expression.QUERY; boolean limitWithOrder = predicateType == Expression.IN || predicateType == Expression.ALL || predicateType == Expression.ANY; Select s = parseSelect(brackets, canHaveOrder, canHaveLimit, limitWithOrder, true); sq.level = subQueryLevel; subQueryLevel--; boolean isResolved = s.resolveAll(session, resolveAll); sq.select = s; sq.isResolved = isResolved; // it's not a problem that this table has not a unique name HsqlName sqtablename = database.nameManager.newHsqlName("SYSTEM_SUBQUERY", false); sqtablename.schema = database.schemaManager.SYSTEM_SCHEMA_HSQLNAME; Table table = new Table(database, sqtablename, Table.SYSTEM_SUBQUERY); if (colNames != null) { if (colNames.length != s.iResultLen) { throw Trace.error(Trace.COLUMN_COUNT_DOES_NOT_MATCH); } for (int i = 0; i < s.iResultLen; i++) { HsqlName name = colNames[i]; s.exprColumns[i].setAlias(name.name, name.isNameQuoted); } } else { for (int i = 0; i < s.iResultLen; i++) { String colname = s.exprColumns[i].getAlias(); if (colname == null || colname.length() == 0) { // fredt - this does not guarantee the uniqueness of column // names but addColumns() will throw if names are not unique. colname = "COL_" + String.valueOf(i + 1); s.exprColumns[i].setAlias(colname, false); } } } table.addColumns(s); boolean uniqueValues = predicateType == Expression.EXISTS || predicateType == Expression.IN || predicateType == Expression.ALL || predicateType == Expression.ANY; int[] pcol = null; if (uniqueValues) { pcol = new int[s.iResultLen]; ArrayUtil.fillSequence(pcol); } table.createPrimaryKey(pcol); sq.table = table; sq.uniqueRows = uniqueValues; subQueryList.add(sq); return sq; } SubQuery getViewSubquery(View v) { SubQuery sq = v.viewSubQuery; for (int i = 0; i < v.viewSubqueries.length; i++) { subQueryList.add(v.viewSubqueries[i]); } return sq; } /** * Constructs and returns a Select object. * * @param canHaveOrder whether the SELECT being parsed can have an ORDER BY * @param canHaveLimit whether LIMIT without ORDER BY is allowed * @param limitWithOrder whether LIMIT is allowed only with ORDER BY * @param isMain whether the SELECT being parsed is the first * select statement in the set * @return a new Select object * @throws HsqlException if a parsing error occurs */ Select parseSelect(int brackets, boolean canHaveOrder, boolean canHaveLimit, boolean limitWithOrder, boolean isMain) throws HsqlException { Select select = new Select(); String token = tokenizer.getString(); if (canHaveLimit || limitWithOrder) { if (tokenizer.wasThis(Token.T_LIMIT) || tokenizer.wasThis(Token.T_TOP)) { parseLimit(token, select, false); token = tokenizer.getString(); } } if (tokenizer.wasThis(Token.T_DISTINCT)) { select.isDistinctSelect = true; } else if (tokenizer.wasThis(Token.T_ALL)) {} else { tokenizer.back(); } // parse column list HsqlArrayList vcolumn = new HsqlArrayList(); do { int expPos = tokenizer.getPosition(); Expression e = parseExpression(); if (isCompilingView()) { if (e.getType() == Expression.ASTERISK) { if (select.asteriskPositions == null) { select.asteriskPositions = new IntKeyHashMap(); } // remember the position of the asterisk. For the moment, just // remember the expression, so it can later be found and replaced // with the concrete column list select.asteriskPositions.put(expPos, e); } } token = tokenizer.getString(); if (tokenizer.wasThis(Token.T_AS)) { e.setAlias(tokenizer.getSimpleName(), tokenizer.wasQuotedIdentifier()); token = tokenizer.getString(); } else if (tokenizer.wasSimpleName()) { e.setAlias(token, tokenizer.wasQuotedIdentifier());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -