/* Copyright (c) 2001-2019, 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.math.BigDecimal;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.IntKeyIntValueHashMap;
import org.hsqldb.map.ValuePool;
import org.hsqldb.types.IntervalType;
import org.hsqldb.types.NumberType;
import org.hsqldb.types.TimeData;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;

Author:Fred Toussi (fredt@users dot sourceforge.net)
Version:2.5.0
Since:1.9.0
/** * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.9.0 */
public class ParserBase { protected Scanner scanner; protected Token token; // private final Token dummyToken = new Token(); // protected int partPosition; protected HsqlException lastError; protected HsqlName lastSynonym; protected boolean isCheckOrTriggerCondition; protected boolean isSchemaDefinition; protected boolean isViewDefinition; protected boolean isRecording; protected HsqlArrayList recordedStatement; static final BigDecimal LONG_MAX_VALUE_INCREMENT = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.valueOf(1));
Constructs a new BaseParser object with the given context.
Params:
  • scanner – the token source from which to parse commands
/** * Constructs a new BaseParser object with the given context. * * @param scanner the token source from which to parse commands */
ParserBase(Scanner scanner) { this.scanner = scanner; this.token = scanner.token; } public Scanner getScanner() { return scanner; } public int getPartPosition() { return partPosition; } public void setPartPosition(int parsePosition) { this.partPosition = parsePosition; }
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
Params:
  • sql – a new SQL character sequence to replace the current one
/** * 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 sql a new SQL character sequence to replace the current one */
void reset(Session session, String sql) { scanner.reset(session, sql); // partPosition = 0; lastError = null; lastSynonym = null; isCheckOrTriggerCondition = false; isSchemaDefinition = false; isViewDefinition = false; isRecording = false; recordedStatement = null; } int getPosition() { return scanner.getTokenPosition(); } void rewind(int position) { if (position == scanner.getTokenPosition()) { return; } scanner.position(position); if (isRecording) { int i = recordedStatement.size() - 1; for (; i >= 0; i--) { Token token = (Token) recordedStatement.get(i); if (token.position < position) { break; } } recordedStatement.setSize(i + 1); } read(); } String getLastPart() { return scanner.getPart(partPosition, scanner.getTokenPosition()); } String getLastPart(int position) { return scanner.getPart(position, scanner.getTokenPosition()); } String getLastPartAndCurrent(int position) { return scanner.getPart(position, scanner.getPosition()); } String getStatement(int startPosition, short[] startTokens) { while (true) { if (token.tokenType == Tokens.SEMICOLON) { break; } else if (token.tokenType == Tokens.X_ENDPARSE) { break; } else { if (ArrayUtil.find(startTokens, token.tokenType) != -1) { break; } } read(); } String sql = scanner.getPart(startPosition, scanner.getTokenPosition()); return sql; } String getStatementForRoutine(int startPosition, short[] startTokens) { int tokenIndex = 0; int semiIndex = -1; int semiPosition = -1; while (true) { if (token.tokenType == Tokens.SEMICOLON) { semiPosition = scanner.getTokenPosition(); semiIndex = tokenIndex; } else if (token.tokenType == Tokens.X_ENDPARSE) { if (semiIndex > 0 && semiIndex == tokenIndex - 1) { rewind(semiPosition); } break; } else { if (ArrayUtil.find(startTokens, token.tokenType) != -1) { break; } } read(); tokenIndex++; } String sql = scanner.getPart(startPosition, scanner.getTokenPosition()); return sql; } // void startRecording() { recordedStatement = new HsqlArrayList(); recordedStatement.add(token.duplicate()); isRecording = true; } Token getRecordedToken() { if (isRecording) { return (Token) recordedStatement.get(recordedStatement.size() - 1); } else { return token.duplicate(); } } Token[] getRecordedStatement() { isRecording = false; recordedStatement.remove(recordedStatement.size() - 1); Token[] tokens = new Token[recordedStatement.size()]; recordedStatement.toArray(tokens); recordedStatement = null; return tokens; } void read() { scanner.scanNext(); if (token.isMalformed) { int errorCode = -1; switch (token.tokenType) { case Tokens.X_MALFORMED_BINARY_STRING : errorCode = ErrorCode.X_42587; break; case Tokens.X_MALFORMED_BIT_STRING : errorCode = ErrorCode.X_42588; break; case Tokens.X_MALFORMED_UNICODE_STRING : errorCode = ErrorCode.X_42586; break; case Tokens.X_MALFORMED_STRING : errorCode = ErrorCode.X_42584; break; case Tokens.X_UNKNOWN_TOKEN : errorCode = ErrorCode.X_42582; break; case Tokens.X_MALFORMED_NUMERIC : errorCode = ErrorCode.X_42585; break; case Tokens.X_MALFORMED_COMMENT : errorCode = ErrorCode.X_42589; break; case Tokens.X_MALFORMED_IDENTIFIER : errorCode = ErrorCode.X_42583; break; default : } throw Error.error(errorCode, token.getFullString()); } if (isRecording) { Token dup = token.duplicate(); dup.position = scanner.getTokenPosition(); recordedStatement.add(dup); } } boolean isReservedKey() { return token.isReservedIdentifier; } boolean isCoreReservedKey() { return token.isCoreReservedIdentifier; } boolean isNonReservedIdentifier() { return !token.isReservedIdentifier && (token.isUndelimitedIdentifier || token.isDelimitedIdentifier); } void checkIsNonReservedIdentifier() { if (!isNonReservedIdentifier()) { throw unexpectedToken(); } } boolean isNonCoreReservedIdentifier() { return !token.isCoreReservedIdentifier && (token.isUndelimitedIdentifier || token.isDelimitedIdentifier); } void checkIsNonCoreReservedIdentifier() { if (!isNonCoreReservedIdentifier()) { throw unexpectedToken(); } } void checkIsIrregularCharInIdentifier() { if (token.hasIrregularChar) { throw unexpectedToken(); } } boolean isIdentifier() { return token.isUndelimitedIdentifier || token.isDelimitedIdentifier; } void checkIsIdentifier() { if (!isIdentifier()) { throw unexpectedToken(); } } boolean isDelimitedIdentifier() { return token.isDelimitedIdentifier; } void checkIsDelimitedIdentifier() { if (!token.isDelimitedIdentifier) { throw Error.error(ErrorCode.X_42569); } } void checkIsUndelimitedIdentifier() { if (!token.isUndelimitedIdentifier) { throw unexpectedToken(); } } void checkIsValue() { if (token.tokenType != Tokens.X_VALUE) { throw unexpectedToken(); } } void checkIsQuotedString() { if (token.tokenType != Tokens.X_VALUE || !token.dataType.isCharacterType()) { throw unexpectedToken(); } } void checkIsThis(int type) { if (token.tokenType != type) { String required = Tokens.getKeyword(type); throw unexpectedTokenRequire(required); } } boolean isUndelimitedSimpleName() { return token.isUndelimitedIdentifier && token.namePrefix == null; } boolean isDelimitedSimpleName() { return token.isDelimitedIdentifier && token.namePrefix == null; } boolean isSimpleName() { return isNonCoreReservedIdentifier() && token.namePrefix == null; } void checkIsSimpleName() { if (!isSimpleName()) { throw unexpectedToken(); } } void readUnquotedIdentifier(String ident) { checkIsSimpleName(); if (!token.tokenString.equals(ident)) { throw unexpectedToken(); } read(); } String readQuotedString() { checkIsValue(); if (!token.dataType.isCharacterType()) { throw Error.error(ErrorCode.X_42563); } String value = token.tokenString; read(); return value; } void readThis(int tokenId) { if (token.tokenType != tokenId) { String required = Tokens.getKeyword(tokenId); throw unexpectedTokenRequire(required); } read(); } boolean readIfThis(int tokenId) { if (token.tokenType == tokenId) { read(); return true; } return false; } void readThis(String tokenString) { if (!tokenString.equals(token.tokenString)) { String required = tokenString; throw unexpectedTokenRequire(required); } read(); } boolean readIfThis(String tokenString) { if (tokenString.equals(token.tokenString)) { read(); return true; } return false; } Integer readIntegerObject() { int value = readInteger(); return ValuePool.getInt(value); } int readInteger() { boolean minus = false; if (token.tokenType == Tokens.MINUS_OP) { minus = true; read(); } checkIsValue(); if (minus && token.dataType.typeCode == Types.SQL_BIGINT && ((Number) token.tokenValue).longValue() == -(long) Integer.MIN_VALUE) { read(); return Integer.MIN_VALUE; } if (token.dataType.typeCode != Types.SQL_INTEGER) { throw Error.error(ErrorCode.X_42563); } int val = ((Number) token.tokenValue).intValue(); if (minus) { val = -val; } read(); return val; } long readBigint() { boolean minus = false; if (token.tokenType == Tokens.MINUS_OP) { minus = true; read(); } checkIsValue(); if (minus && token.dataType.typeCode == Types.SQL_NUMERIC && LONG_MAX_VALUE_INCREMENT.equals(token.tokenValue)) { read(); return Long.MIN_VALUE; } if (token.dataType.typeCode != Types.SQL_INTEGER && token.dataType.typeCode != Types.SQL_BIGINT) { throw Error.error(ErrorCode.X_42563); } long val = ((Number) token.tokenValue).longValue(); if (minus) { val = -val; } read(); return val; } Expression readDateTimeIntervalLiteral(Session session) { int pos = getPosition(); switch (token.tokenType) { case Tokens.DATE : { read(); if (token.tokenType != Tokens.X_VALUE || !token.dataType.isCharacterType()) { break; } String s = token.tokenString; read(); Object date = scanner.newDate(s); return new ExpressionValue(date, Type.SQL_DATE); } case Tokens.TIME : { read(); if (token.tokenType != Tokens.X_VALUE || !token.dataType.isCharacterType()) { break; } String s = token.tokenString; read(); TimeData value = scanner.newTime(s); Type dataType = scanner.dateTimeType; return new ExpressionValue(value, dataType); } case Tokens.TIMESTAMP : { read(); if (token.tokenType != Tokens.X_VALUE || !token.dataType.isCharacterType()) { break; } String s = token.tokenString; read(); Object date = scanner.newTimestamp(s); Type dataType = scanner.dateTimeType; return new ExpressionValue(date, dataType); } case Tokens.INTERVAL : { boolean minus = false; read(); if (token.tokenType == Tokens.MINUS_OP) { read(); minus = true; } else if (token.tokenType == Tokens.PLUS_OP) { read(); } if (token.tokenType != Tokens.X_VALUE) { break; } String s = token.tokenString; /* INT literal accepted in addition to string literal */ if (!token.dataType.isIntegralType() && !token.dataType.isCharacterType()) { break; } read(); IntervalType dataType = readIntervalType(session, false); Object interval = scanner.newInterval(s, dataType); dataType = (IntervalType) scanner.dateTimeType; if (minus) { interval = dataType.negate(interval); } return new ExpressionValue(interval, dataType); } default : throw Error.runtimeError(ErrorCode.U_S0500, "ParserBase"); } rewind(pos); return null; } IntervalType readIntervalType(Session session, boolean maxPrecisionDefault) { int precision = -1; int scale = -1; int startToken; int endToken; String startTokenString; int startIndex = -1; int endIndex = -1; startToken = endToken = token.tokenType; startTokenString = token.tokenString; startIndex = ArrayUtil.find(Tokens.SQL_INTERVAL_FIELD_CODES, startToken); read(); if (token.tokenType == Tokens.OPENBRACKET) { read(); precision = readInteger(); if (precision <= 0) { throw Error.error(ErrorCode.X_42592); } if (token.tokenType == Tokens.COMMA) { if (startToken != Tokens.SECOND) { throw unexpectedToken(); } read(); scale = readInteger(); if (scale < 0) { throw Error.error(ErrorCode.X_42592); } } readThis(Tokens.CLOSEBRACKET); } if (token.tokenType == Tokens.TO) { int position = getPosition(); read(); int end = ArrayUtil.find(Tokens.SQL_INTERVAL_FIELD_CODES, token.tokenType); if (end > startIndex) { endToken = token.tokenType; read(); } else { rewind(position); } } if (token.tokenType == Tokens.OPENBRACKET) { if (endToken != Tokens.SECOND || endToken == startToken) { throw unexpectedToken(); } read(); scale = readInteger(); if (scale < 0) { throw Error.error(ErrorCode.X_42592); } readThis(Tokens.CLOSEBRACKET); } endIndex = ArrayUtil.find(Tokens.SQL_INTERVAL_FIELD_CODES, endToken); if (precision == -1 && maxPrecisionDefault) { if (startIndex == IntervalType.INTERVAL_SECOND_INDEX) { precision = IntervalType.maxIntervalSecondPrecision; } else { precision = IntervalType.maxIntervalPrecision; } } if (startIndex == -1 && session.database.sqlSyntaxMys) { int type = FunctionCustom.getSQLTypeForToken(startTokenString); int startType = IntervalType.getStartIntervalType(type); int endType = IntervalType.getEndIntervalType(type); return IntervalType.getIntervalType( type, startType, endType, IntervalType.maxIntervalPrecision, IntervalType.maxFractionPrecision, true); } return IntervalType.getIntervalType(startIndex, endIndex, precision, scale); } static int getExpressionType(int tokenT) { int type = expressionTypeMap.get(tokenT, -1); if (type == -1) { throw Error.runtimeError(ErrorCode.U_S0500, "ParserBase"); } return type; } private static final IntKeyIntValueHashMap expressionTypeMap = new IntKeyIntValueHashMap(37); static { // comparison expressionTypeMap.put(Tokens.EQUALS_OP, OpTypes.EQUAL); expressionTypeMap.put(Tokens.GREATER_OP, OpTypes.GREATER); expressionTypeMap.put(Tokens.LESS_OP, OpTypes.SMALLER); expressionTypeMap.put(Tokens.GREATER_EQUALS, OpTypes.GREATER_EQUAL); expressionTypeMap.put(Tokens.LESS_EQUALS, OpTypes.SMALLER_EQUAL); expressionTypeMap.put(Tokens.NOT_EQUALS, OpTypes.NOT_EQUAL); // aggregates expressionTypeMap.put(Tokens.COUNT, OpTypes.COUNT); expressionTypeMap.put(Tokens.MAX, OpTypes.MAX); expressionTypeMap.put(Tokens.MIN, OpTypes.MIN); expressionTypeMap.put(Tokens.SUM, OpTypes.SUM); expressionTypeMap.put(Tokens.AVG, OpTypes.AVG); expressionTypeMap.put(Tokens.EVERY, OpTypes.EVERY); expressionTypeMap.put(Tokens.ANY, OpTypes.SOME); expressionTypeMap.put(Tokens.SOME, OpTypes.SOME); expressionTypeMap.put(Tokens.STDDEV_POP, OpTypes.STDDEV_POP); expressionTypeMap.put(Tokens.STDDEV_SAMP, OpTypes.STDDEV_SAMP); expressionTypeMap.put(Tokens.VAR_POP, OpTypes.VAR_POP); expressionTypeMap.put(Tokens.VAR_SAMP, OpTypes.VAR_SAMP); expressionTypeMap.put(Tokens.ARRAY_AGG, OpTypes.ARRAY_AGG); expressionTypeMap.put(Tokens.GROUP_CONCAT, OpTypes.GROUP_CONCAT); expressionTypeMap.put(Tokens.MEDIAN, OpTypes.MEDIAN); } HsqlException unexpectedToken(String tokenS) { return Error.parseError(ErrorCode.X_42581, tokenS, scanner.getLineNumber()); } HsqlException unexpectedToken(int token) { String tokenS = Tokens.getKeyword(token); return Error.parseError(ErrorCode.X_42581, tokenS, scanner.getLineNumber()); } HsqlException unexpectedTokenRequire(String required) { if (token.tokenType == Tokens.X_ENDPARSE) { return Error.parseError(ErrorCode.X_42590, ErrorCode.TOKEN_REQUIRED, scanner.getLineNumber(), new Object[] { "", required }); } String tokenS; if (token.charsetSchema != null) { tokenS = token.charsetSchema; } else if (token.charsetName != null) { tokenS = token.charsetName; } else if (token.namePrePrefix != null) { tokenS = token.namePrePrefix; } else if (token.namePrefix != null) { tokenS = token.namePrefix; } else { tokenS = token.tokenString; } return Error.parseError(ErrorCode.X_42581, ErrorCode.TOKEN_REQUIRED, scanner.getLineNumber(), new Object[] { tokenS, required }); } HsqlException unexpectedToken() { if (token.tokenType == Tokens.X_ENDPARSE) { return Error.parseError(ErrorCode.X_42590, null, scanner.getLineNumber()); } String tokenS; if (token.charsetSchema != null) { tokenS = token.charsetSchema; } else if (token.charsetName != null) { tokenS = token.charsetName; } else if (token.namePrePrefix != null) { tokenS = token.namePrePrefix; } else if (token.namePrefix != null) { tokenS = token.namePrefix; } else { tokenS = token.tokenString; } return Error.parseError(ErrorCode.X_42581, tokenS, scanner.getLineNumber()); } HsqlException tooManyIdentifiers() { String tokenS; if (token.namePrePrePrefix != null) { tokenS = token.namePrePrePrefix; } else if (token.namePrePrefix != null) { tokenS = token.namePrePrefix; } else if (token.namePrefix != null) { tokenS = token.namePrefix; } else { tokenS = token.tokenString; } return Error.parseError(ErrorCode.X_42551, tokenS, scanner.getLineNumber()); } HsqlException unsupportedFeature() { return Error.error(ErrorCode.X_0A501, token.tokenString); } HsqlException unsupportedFeature(String string) { return Error.error(ErrorCode.X_0A501, string); } public Number convertToNumber(String s, NumberType type) { return scanner.convertToNumber(s, type); } }