最近项目快release了,我手头上也不忙,花三个小时写了个Sql语言解析器框架,也可以用于产品自定义语言的实现方法。本文实现了insert的command,其他命令可以参照实现。
MysqlShell.java:
SqlParser.java:
Context.java:
Row.java:
Command.java:
Constant.java:
InsertCommand.java:
SqlParserException.java:
SqlExecutionException.java:
MySQL.java:
package com.cisco.gendwang;
public class MySQL {
public static void main(String[] args)
{
MysqlShell shell = MysqlShell.getInstance();
shell.run();
}
}
MysqlShell.java:
package com.cisco.gendwang;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class MysqlShell
{
private static final MysqlShell instance = new MysqlShell();
private MysqlShell()
{
}
public static MysqlShell getInstance()
{
return instance;
}
public void run()
{
Context context = new Context();
SqlParser parser = new SqlParser(context);
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
System.out.print("mysql> ");
//clear the context and the parser
context.clear();
parser.reset();
//read the command from the standard input
String commandString = null;
try
{
commandString = input.readLine();
if(commandString.equalsIgnoreCase("exit"))
{
break;
}
}
catch (IOException e)
{
e.printStackTrace();
break;
}
//parse and execute the command
parser.setCommandString(commandString);
try
{
Command command = parser.parse();
command.execute(context);
}
catch (SqlParserException e)
{
System.out.println(e.getMessage());
}
catch (SqlExecutionException e)
{
System.out.println(e.getMessage());
}
}
}
}
SqlParser.java:
package com.cisco.gendwang;
public class SqlParser
{
private static final String SQL_SYNTAX_ERROR = "ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1";
private final Context context;
private String commandString;
public SqlParser(Context context)
{
this.context = context;
}
public void reset()
{
commandString = null;
}
public void setCommandString(String commandString)
{
this.commandString = commandString;
}
public Command parse() throws SqlParserException
{
if(commandString.startsWith("insert"))
{
return parseInsertCommand();
}
return null;
}
private Command parseInsertCommand() throws SqlParserException
{
if(commandString.length() <= "insert".length())
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the insert
String left = commandString.substring("insert".length());
left = left.trim();
if(left.length() <= "into".length())
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the into
left = left.substring("into".length());
left = left.trim();
String tableName = context.getTableName();
if(left.length() <= tableName.length())
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the table name
left = left.substring(tableName.length());
left = left.trim();
if(left.length() <= "values".length())
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the values
left = left.substring("values".length());
left = left.trim();
if(left.length() <= "(".length())
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the (
left = left.substring("(".length());
left = left.trim();
if(!(left.endsWith(");")))
{
throw new SqlParserException(SQL_SYNTAX_ERROR);
}
//remove the );
left = left.substring(0, left.length() - 2);
String[] vals = left.split("\\s*,\\s*");
//build the InsertCommand
InsertCommand insert = new InsertCommand();
for(String val: vals)
{
insert.addParam(new Constant(val));
}
return insert;
}
}
Context.java:
package com.cisco.gendwang;
import java.util.LinkedList;
import java.util.List;
public class Context
{
private final String DATABASE_NAME = "HRSystem";
private final String TABLE_NAME = "employee";
private final List<Row> rows = new LinkedList<Row>();
public Context()
{
}
public void clear()
{
}
public String getDatabaseName()
{
return DATABASE_NAME;
}
public String getTableName()
{
return TABLE_NAME;
}
public List<Row> getRows()
{
return rows;
}
}
Row.java:
package com.cisco.gendwang;
public class Row
{
private int id;
private String name;
private int salary;
private String email;
private Row(RowBuilder builder)
{
id = builder.id;
name = builder.name;
salary = builder.salary;
email = builder.email;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getSalary()
{
return salary;
}
public void setSalary(int salary)
{
this.salary = salary;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
@Override
public boolean equals(Object obj)
{
if(!(obj instanceof Row))
{
return false;
}
Row row = (Row)obj;
return (id == row.id);
}
@Override
public int hashCode()
{
int result = 17;
result = 31 * result + id;
return result;
}
public static class RowBuilder
{
private int id;
private String name;
private int salary;
private String email;
public RowBuilder()
{
}
public RowBuilder setId(int id)
{
this.id = id;
return this;
}
public RowBuilder setName(String name)
{
this.name = name;
return this;
}
public RowBuilder setSalary(int salary)
{
this.salary = salary;
return this;
}
public RowBuilder setEmail(String email)
{
this.email = email;
return this;
}
public Row build()
{
//validation can be handled here
return new Row(this);
}
}
}
Command.java:
package com.cisco.gendwang;
public abstract class Command
{
public abstract Object execute(Context ctx) throws SqlExecutionException;
}
Constant.java:
package com.cisco.gendwang;
public class Constant extends Command
{
private Object value = null;
public Constant(String value)
{
char ch = value.charAt(0);
if((ch == '"') || (ch == '\'') )
{
this.value = value.substring(1, value.length() - 1);
}
else if(Character.isDigit(ch))
{
this.value = Integer.parseInt(value);
}
}
@Override
public Object execute(Context ctx) throws SqlExecutionException
{
return value;
}
}
InsertCommand.java:
package com.cisco.gendwang;
import java.util.LinkedList;
import java.util.List;
public class InsertCommand extends Command
{
private List<Constant> params = new LinkedList<Constant>();
public InsertCommand()
{
}
public void addParam(Constant param)
{
params.add(param);
}
public Object execute(Context ctx) throws SqlExecutionException
{
//validation could be put here
//insert into the table
int id = (int)params.get(0).execute(ctx);
String name = (String)params.get(1).execute(ctx);
int salary = (int)params.get(2).execute(ctx);
String email = (String)params.get(3).execute(ctx);
Row.RowBuilder builder = new Row.RowBuilder();
builder.setId(id);
builder.setName(name);
builder.setSalary(salary);
builder.setEmail(email);
Row row = builder.build();
List<Row> rows = ctx.getRows();
rows.add(row);
return null;
}
}
SqlParserException.java:
package com.cisco.gendwang;
public class SqlParserException extends Exception
{
public SqlParserException(String msg)
{
super(msg);
}
}
SqlExecutionException.java:
package com.cisco.gendwang;
public class SqlExecutionException extends Exception
{
public SqlExecutionException(String msg)
{
super(msg);
}
}