📄 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-2005, 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.HsqlArrayList;import org.hsqldb.lib.IntValueHashMap;import org.hsqldb.store.ValuePool;// 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 sTable; private String sToken; private Object oData; private int iType; private int iToken; // 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; } /** * 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, 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.getSimpleName()); } 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; boolean limitWithOrder = predicateType == Expression.VIEW || predicateType == Expression.QUERY || predicateType == Expression.IN || predicateType == Expression.ALL || predicateType == Expression.ANY; Select s = parseSelect(brackets, canHaveOrder, false, 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 { Expression e = parseExpression(); 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()); token = tokenizer.getString(); } vcolumn.add(e); } while (tokenizer.wasThis(Token.T_COMMA)); if (token.equals(Token.T_INTO)) { boolean getname = true; token = tokenizer.getString(); if (tokenizer.wasSimpleToken()) { switch (Token.get(token)) { case Token.CACHED : select.intoType = Table.CACHED_TABLE; break; case Token.TEMP : select.intoType = Table.TEMP_TABLE; break; case Token.TEXT : select.intoType = Table.TEXT_TABLE; break; case Token.MEMORY : select.intoType = Table.MEMORY_TABLE; break; default : select.intoType = database.getDefaultTableType(); getname = false; break; } if (getname) { token = tokenizer.getName(); } else { if (!tokenizer.wasName()) { tokenizer.throwUnexpected(); } } } select.sIntoTable = database.nameManager.newHsqlName(token, tokenizer.wasQuotedIdentifier()); select.sIntoTable.schema = session.getSchemaHsqlName(tokenizer.getLongNameFirst()); token = tokenizer.getString(); } tokenizer.matchThis(Token.T_FROM);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -