📄 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.*;
final class SQLParser {
SSConnection con;
private char[] sql;
private List tokens;
private int tokenIdx;
Command parse(SSConnection con, String sql) throws SQLException{
this.con = con;
Command cmd = parse( sql.toCharArray() );
SQLToken token = nextToken();
if(token != null){
throw createSyntaxError( token, "Additional token after end of SQL statement");
}
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);
}
private StringBuffer getSyntaxError( SQLToken token ){
StringBuffer msg = new StringBuffer(256);
if(token != null){
msg.append( "Syntax error at offset ").append( token.offset )
.append( " on '" ).append( sql, token.offset, token.length ).append("'. ");
}else{
msg.append( "Syntax error, unexpected end of SQL string. ");
}
return msg;
}
private SQLException createSyntaxError(SQLToken token, int[] validValues){
StringBuffer msg = getSyntaxError( token );
msg.append( "Requied keywords are: " );
for(int i=0; i<validValues.length; i++){
String name = SQLTokenizer.getKeyWord(validValues[i]);
if(name == null) name = String.valueOf( (char)validValues[i] );
msg.append( name );
if (i < validValues.length - 2)
msg.append( ", ");
else
if ( i == validValues.length - 2 )
msg.append( " or ");
}
return createSyntaxError( msg, token );
}
private SQLException createSyntaxError( StringBuffer msg, SQLToken token){
int offset = (token != null) ? token.offset : sql.length;
int begin = Math.max( 0, offset-40);
int end = Math.min( offset+20, sql.length );
String lineSeparator = System.getProperty( "line.separator" );
msg.append( lineSeparator );
msg.append( sql, begin, end-begin);
msg.append( lineSeparator );
for(; begin<offset; begin++) msg.append(' ');
msg.append('^');
return Utils.createSQLException( msg.toString()/* , offset*/ );
}
private SQLException createSyntaxError(SQLToken token, String errorMsg){
StringBuffer msg = getSyntaxError( token ).append(errorMsg);
return createSyntaxError( msg, token );
}
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, "Identifier expected.");
}
if(name.length() == 0)
throw createSyntaxError( token, "Empty Identifier.");
char firstChar = name.charAt(0);
if(firstChar != '#' && firstChar < '@')
throw createSyntaxError( token, "Wrong Identifier '" + 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 identifer from the next token from token stack.
* @return the string with the name
* @throws SQLException if the identifer is invalid
*/
private String nextIdentifier() throws SQLException{
return getIdentifier( nextToken( MISSING_IDENTIFIER ) );
}
/**
* Check if the identifer 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:
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++; // erh鰄t werden, damit previousToken() funktioniert
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; // eine Expression kann in jedem Token enthalten sein
if(validValues == MISSING_IDENTIFIER){
// folgende Token sind keine g黮tigen Identifer
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
*/
private CommandSelect singleSelect() throws SQLException{
CommandSelect selCmd = new CommandSelect(con.log);
SQLToken token;
// scan for prefixe like DISTINCT, ALL and the TOP clause; sample: SELECT TOP 15 ...
Switch: while(true){
token = nextToken(MISSING_EXPRESSION);
switch(token.value){
case SQLTokenizer.TOP:
token = nextToken(MISSING_EXPRESSION);
try{
int maxRows = Integer.parseInt(token.getName(sql));
selCmd.setMaxRows(maxRows);
}catch(Exception e){
throw createSyntaxError(token, e.getMessage());
}
break;
case SQLTokenizer.ALL:
selCmd.setDistinct(false);
break;
case SQLTokenizer.DISTINCT:
selCmd.setDistinct(true);
break;
default:
previousToken();
break Switch;
}
}
while(true){
Expression column = expression(selCmd, 0);
selCmd.addColumnExpression( column );
token = nextToken();
if(token == null) return selCmd; // SELECT ohne FROM
boolean as = false;
if(token.value == SQLTokenizer.AS){
token = nextToken(MISSING_EXPRESSION);
as = true;
}
if(as || (!isKeyword(token))){
String alias = getIdentifier( token);
column.setAlias( alias );
token = nextToken();
if(token == null) return selCmd; // SELECT ohne FROM
}
switch(token.value){
case SQLTokenizer.COMMA:
if(column == null) throw createSyntaxError( token, MISSING_EXPRESSION );
column = null;
break;
case SQLTokenizer.FROM:
if(column == null) throw createSyntaxError( token, MISSING_EXPRESSION );
column = null;
from(selCmd);
return selCmd;
default:
if(!isKeyword(token))
throw createSyntaxError( token, new int[]{SQLTokenizer.COMMA, SQLTokenizer.FROM} );
previousToken();
return selCmd;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -