📄 sqlparser.java
字号:
/* =============================================================
* SmallSQL : a free Java DBMS library for the Java(tm) platform
* =============================================================
*
* (C) Copyright 2004-2007, by Volker Berlin.
*
* Project Info: http://www.smallsql.de/
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ---------------
* SQLParser.java
* ---------------
* Author: Volker Berlin
*
*/
package smallsql.database;
import java.util.List;
import java.sql.*;
import smallsql.database.language.Language;
final class SQLParser {
SSConnection con;
private char[] sql;
private List tokens;
private int tokenIdx;
Command parse(SSConnection con, String sqlString) throws SQLException{
this.con = con;
Command cmd = parse( sqlString.toCharArray() );
SQLToken token = nextToken();
if(token != null){
throw createSyntaxError(token, Language.STXADD_ADDITIONAL_TOK);
}
return cmd;
}
final private Command parse(char[] sql) throws SQLException{
this.sql = sql;
this.tokens = SQLTokenizer.parseSQL( sql );
tokenIdx = 0;
SQLToken token = nextToken(COMMANDS);
switch (token.value){
case SQLTokenizer.SELECT:
return select();
case SQLTokenizer.DELETE:
return delete();
case SQLTokenizer.INSERT:
return insert();
case SQLTokenizer.UPDATE:
return update();
case SQLTokenizer.CREATE:
return create();
case SQLTokenizer.DROP:
return drop();
case SQLTokenizer.ALTER:
return alter();
case SQLTokenizer.SET:
return set();
case SQLTokenizer.USE:
token = nextToken(MISSING_EXPRESSION);
String name = token.getName( sql );
checkValidIdentifier( name, token );
CommandSet set = new CommandSet( con.log, SQLTokenizer.USE);
set.name = name;
return set;
case SQLTokenizer.EXECUTE:
return execute();
case SQLTokenizer.TRUNCATE:
return truncate();
default:
throw new Error();
}
}
Expression parseExpression(String expr) throws SQLException{
this.sql = expr.toCharArray();
this.tokens = SQLTokenizer.parseSQL( sql );
tokenIdx = 0;
return expression( null, 0);
}
/**
* Create a syntax error message, using a custom message.
*
* @param token
* token object; if not null, generates a SYNTAX_BASE_OFS,
* otherwise a SYNTAX_BASE_END.
* @param addMessage
* additional message object to append.
*/
private SQLException createSyntaxError(SQLToken token, String addMessageCode) {
String message = getErrorString(token, addMessageCode, null);
return SmallSQLException.create(Language.CUSTOM_MESSAGE, message);
}
/**
* Create a syntax error message, using a message with a parameter.
*
* @param token
* token object; if not null, generates a SYNTAX_BASE_OFS,
* otherwise a SYNTAX_BASE_END.
* @param addMessageCode
* additional message[Code] to append.
* @param param0
* parameter.
*/
private SQLException createSyntaxError(SQLToken token, String addMessageCode,
Object param0) {
String message = getErrorString(token, addMessageCode, param0);
return SmallSQLException.create(Language.CUSTOM_MESSAGE, message);
}
/**
* Create an "Additional keyword required" syntax error.
*
* @param token
* token object.
* @param validValues
* valid values.
* @return Exception.
*/
private SQLException createSyntaxError(SQLToken token, int[] validValues){
String msgStr = SmallSQLException.translateMsg(
Language.STXADD_KEYS_REQUIRED, new Object[] { });
StringBuffer msgBuf = new StringBuffer( msgStr );
for(int i=0; i<validValues.length; i++){
String name = SQLTokenizer.getKeyWord(validValues[i]);
if(name == null) name = String.valueOf( (char)validValues[i] );
msgBuf.append( name );
if (i < validValues.length - 2)
msgBuf.append( ", ");
else
if ( i == validValues.length - 2 )
msgBuf.append( " or ");
}
String message = getErrorString(
token, Language.CUSTOM_MESSAGE, msgBuf);
return SmallSQLException.create(Language.CUSTOM_MESSAGE, message);
}
/**
* Create the complete error string (begin + middle + end).
*
* @param token
* token object.
* @param middleMsgCode
* middle message[code].
* @param middleMsgParam
* middle message[code] parameter.
* @return complete error message string.
*/
private String getErrorString(SQLToken token, String middleMsgCode,
Object middleMsgParam) {
StringBuffer buffer = new StringBuffer(1024);
/* begin */
if(token != null){
Object[] params = { String.valueOf(token.offset),
String.valueOf(sql, token.offset, token.length) };
String begin = SmallSQLException.translateMsg(Language.SYNTAX_BASE_OFS, params);
buffer.append(begin);
}
else{
String begin = SmallSQLException.translateMsg(
Language.SYNTAX_BASE_END, new Object[] { });
buffer.append(begin);
}
/* middle */
String middle = SmallSQLException.translateMsg(
middleMsgCode, new Object[] { middleMsgParam });
buffer.append(middle);
/* end */
int valOffset = (token != null) ? token.offset : sql.length;
int valBegin = Math.max( 0, valOffset-40);
int valEnd = Math.min( valOffset+20, sql.length );
String lineSeparator = System.getProperty( "line.separator" );
buffer.append( lineSeparator );
buffer.append( sql, valBegin, valEnd-valBegin);
buffer.append( lineSeparator );
for(; valBegin<valOffset; valBegin++) buffer.append(' ');
buffer.append('^');
return buffer.toString();
}
private void checkValidIdentifier(String name, SQLToken token) throws SQLException{
if(token.value == SQLTokenizer.ASTERISK) return;
if(token.value != SQLTokenizer.VALUE &&
token.value != SQLTokenizer.IDENTIFIER &&
token.value < 200){
throw createSyntaxError( token, Language.STXADD_IDENT_EXPECT);
}
if(name.length() == 0) {
throw createSyntaxError( token, Language.STXADD_IDENT_EMPTY, name);
}
char firstChar = name.charAt(0);
if(firstChar != '#' && firstChar < '@') {
throw createSyntaxError( token, Language.STXADD_IDENT_WRONG, name );
}
}
/**
* Returns a valid identifier from this token.
* @param token the token of the identifier
* @return the string with the name
* @throws SQLException if the identifier is invalid
*/
private String getIdentifier(SQLToken token) throws SQLException{
String name = token.getName(sql);
checkValidIdentifier( name, token );
return name;
}
/**
* Returns a valid identifier from the next token from token stack.
* @return the string with the name
* @throws SQLException if the identifier is invalid
*/
private String nextIdentifier() throws SQLException{
return getIdentifier( nextToken( MISSING_IDENTIFIER ) );
}
/**
* Check if the identifier is a 2 part name with a point in the middle like FIRST.SECOND
* @param name the name of the first part
* @return the second part if exist else returns the first part
* @throws SQLException
*/
private String nextIdentiferPart(String name) throws SQLException{
SQLToken token = nextToken();
//check if the object name include a database name
if(token != null && token.value == SQLTokenizer.POINT){
return nextIdentifier();
}else{
previousToken();
}
return name;
}
final private boolean isKeyword(SQLToken token){
if(token == null) return false;
switch(token.value){
case SQLTokenizer.SELECT:
case SQLTokenizer.INSERT:
case SQLTokenizer.UPDATE:
case SQLTokenizer.UNION:
case SQLTokenizer.FROM:
case SQLTokenizer.WHERE:
case SQLTokenizer.GROUP:
case SQLTokenizer.HAVING:
case SQLTokenizer.ORDER:
case SQLTokenizer.COMMA:
case SQLTokenizer.SET:
case SQLTokenizer.JOIN:
return true;
}
return false;
}
/**
* Return the last token that the method nextToken has return
*/
private SQLToken lastToken(){
if(tokenIdx > tokens.size()){
return null;
}
return (SQLToken)tokens.get( tokenIdx-1 );
}
private void previousToken(){
tokenIdx--;
}
private SQLToken nextToken(){
if(tokenIdx >= tokens.size()){
tokenIdx++; // must be ever increment that the method previousToken() is working
return null;
}
return (SQLToken)tokens.get( tokenIdx++ );
}
private SQLToken nextToken( int[] validValues) throws SQLException{
SQLToken token = nextToken();
if(token == null) throw createSyntaxError( token, validValues);
if(validValues == MISSING_EXPRESSION){
return token; // an expression can be contained in every token.
}
if(validValues == MISSING_IDENTIFIER){
// the follow token are not valid identifier
switch(token.value){
case SQLTokenizer.PARENTHESIS_L:
case SQLTokenizer.PARENTHESIS_R:
case SQLTokenizer.COMMA:
throw createSyntaxError( token, validValues);
}
return token;
}
for(int i=validValues.length-1; i>=0; i--){
if(token.value == validValues[i]) return token;
}
throw createSyntaxError( token, validValues);
}
/**
* A single SELECT of a UNION or only a simple single SELECT.
* @return
* @throws SQLException
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -