fqlite.parser.SimpleSQLiteParser Maven / Gradle / Ivy
package fqlite.parser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import fqlite.base.Base;
import fqlite.descriptor.IndexDescriptor;
import fqlite.descriptor.TableDescriptor;
import fqlite.pattern.HeaderPattern;
import fqlite.pattern.IntegerConstraint;
import fqlite.util.Logger;
/**
* With the help of the present class CREATE TABLE and CREATE INDEX statements can be parsed and decomposed.
*
* Note: ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for
* reading, processing, executing, or translating structured text or binary files.
*
* This class builds up on the parser classes of ANTLR-library.
*
*
* @author pawlaszc
*
* @version 1.0
*
*/
public class SimpleSQLiteParser extends Base {
/* determine data type */
/* INT */
static String[] inttypes = { "INT", "INTEGER", "INTUNSIGNED", "INTSIGNED", "LONG", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT",
"UNSIGNEDBIGINT", "INT2", "INT8"};
/* TEXT */
static String[] texttypes = { "TEXT", "CHARACTER", "CLOB", "VARCHAR", "VARYINGCHARACTER", "NCHAR",
"NATIVE CHARACTER", "NVARCHAR" };
/* BLOB */
static String[] blobtype = { "BLOB" };
/* REAL */
static String[] realtype = { "REAL", "DOUBLE", "DOUBLEPRECISION", "FLOAT" };
/* could by any of the above STORAGE class */
static String[] numerictype = { "NUMERIC", "DECIMAL", "BOOLEAN", "DATE", "DATETIME" };
SQLiteLexer lexer;
String tablename = null;
String modulname = null;
/* prepare data fields */
List coltypes = new ArrayList();
List sqltypes = new ArrayList();
List colconstraints = new ArrayList();
List colnames = new ArrayList();
List tableconstraint = new ArrayList();
Map constraints = new HashMap();
HeaderPattern pattern = new HeaderPattern();
TableDescriptor tds = null;
IndexDescriptor ids = null;
int column;
/**
* Call this method to parse the SQL-statement CREATE TABLE.
* The result will be a TableDescriptor object, that contains component name, column names
* an types. This information is necessary for matching the data records.
*
* Example statement would look like this:
*
* CREATE TABLE 'users' (
* 'name' TEXT,
* 'surname' TEXT,
* 'lastUpdate' TEXT
* );
*
* @param stmt The statement to be parsed.
* @return a TableDescriptor Object with all the information about the component.
*/
public TableDescriptor parseTable(String stmt)
{
/* what kind of SQL statement ? */
if ((stmt.indexOf("CREATE TABLE")) >= 0)
{
return parseCreateTable(stmt);
}
else if ((stmt.indexOf("CREATE TEMP TABLE")) >= 0)
{
Logger.out.debug("Found CREATE TEMP TABLE statement");
}
else if ((stmt.indexOf("CREATE VIRTUAL TABLE")) >= 0)
{
return parseCreateVirtualTable(stmt);
}
return null;
}
public IndexDescriptor parseIndex(String stmt)
{
if ((stmt.indexOf("CREATE INDEX ") >= 0))
{
return parseCreateIndex(stmt);
}
return null;
}
public String trim(String value)
{
if (value.startsWith("'") || value.startsWith("\""))
{
value = value.substring(1);
if (value.endsWith("'") || value.endsWith("\""))
value = value.substring(0,value.length()-1);
}
return value;
}
/**
* Parse a CREATE VIRTUAL TABLE statement and return a TableDescriptor object.
*
* @param stmt String with the CREATE VIRTUAL TABLE statement to parse.
* @return the TableDescriptor object
*/
private TableDescriptor parseCreateVirtualTable(String stmt)
{
column = 0;
modulname = null;
// Create a lexer and parser for the input.
SQLiteLexer lexer = new SQLiteLexer(CharStreams.fromString(stmt));
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
SQLiteParser parser = new SQLiteParser(new CommonTokenStream(lexer));
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
// Invoke the `create_table_stmt` production.
ParseTree tree = parser.create_virtual_table_stmt();
/* let's walk through the syntax tree */
ParseTreeWalker.DEFAULT.walk(new SQLiteBaseListener(){
/* callback methods */
@Override public void enterTable_name(SQLiteParser.Table_nameContext ctx)
{
tablename = ctx.getText();
tablename = trim(tablename);
if (tablename.length()==0)
tablename = "";
info("Tablename " + tablename);
}
@Override public void enterModule_name(SQLiteParser.Module_nameContext ctx)
{
modulname = ctx.getText();
info(" name " + modulname);
if (modulname.equals("fts4")|| modulname.equals("fts3"))
{
info("Found FreeTextSearch Module (fts3/4)");
}
else if (modulname.equals("rtree"))
{
info("Found rtree Module");
}
}
@Override public void enterModule_argument(SQLiteParser.Module_argumentContext ctx)
{
String modulargument = ctx.getText();
info(" arg " + modulargument);
/* RTree Modul ?*/
if(modulname.equals("rtree"))
{
colnames.add(modulargument);
if (column == 0)
coltypes.add("INT");
else
coltypes.add("NUMERIC");
column++;
}
}
},tree);
tds = new TableDescriptor(tablename,stmt,sqltypes,coltypes,colnames,colconstraints,tableconstraint,null,stmt.contains("WITHOUT ROWID"));
tds.setVirtual(true);
if (null!=modulname)
tds.setModulname(modulname);
return tds;
}
String idxname;
private IndexDescriptor parseCreateIndex(String stmt)
{
column = 0;
// Create a lexer and parser for the input.
SQLiteLexer lexer = new SQLiteLexer(CharStreams.fromString(stmt));
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
SQLiteParser parser = new SQLiteParser(new CommonTokenStream(lexer));
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
// Invoke the `create_index_stmt` production.
ParseTree tree = parser.create_index_stmt();
/* let's walk through the syntax tree */
ParseTreeWalker.DEFAULT.walk(new SQLiteBaseListener(){
/**
* {@inheritDoc}
*
* The default implementation does nothing.
*/
@Override public void enterIndex_name(SQLiteParser.Index_nameContext ctx)
{
idxname = ctx.getText(); //"_IDX_" + ctx.getText();
info("INDEX " + idxname);
}
/**
* {@inheritDoc}
*
* The default implementation does nothing.
*/
@Override public void enterIndexed_column(SQLiteParser.Indexed_columnContext ctx)
{
String colname = ctx.getText();
colname = trim(colname);
colnames.add(colname);
info("Columnname " + colname);
}
@Override public void enterTable_name(SQLiteParser.Table_nameContext ctx)
{
tablename = ctx.getText();
tablename = trim(tablename);
if (tablename.length()==0)
tablename = "";
info("Tablename " + tablename);
}
},tree);
return new IndexDescriptor(idxname,tablename,stmt,colnames);
}
/**
* Parse a CREATE TABLE statement and return a TableDescriptor object.
*
* @param stmt String with the CREATE TABLE statement to parse.
* @return the TableDescriptor object
*/
private TableDescriptor parseCreateTable(String stmt)
{
column = 0;
// Create a lexer and parser for the input.
SQLiteLexer lexer = new SQLiteLexer(CharStreams.fromString(stmt));
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
SQLiteParser parser = new SQLiteParser(new CommonTokenStream(lexer));
parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
// Invoke the `create_table_stmt` production.
ParseTree tree = parser.create_table_stmt();
/* let's walk through the syntax tree */
ParseTreeWalker.DEFAULT.walk(new SQLiteBaseListener(){
@Override public void enterTable_name(SQLiteParser.Table_nameContext ctx)
{
isTableConstraint = false;
tablename = ctx.getText();
tablename = trim(tablename);
if (tablename.length()==0)
tablename = "";
info("Tablename " + tablename);
}
boolean tblconstraint = false;
@Override public void enterColumn_name(SQLiteParser.Column_nameContext ctx)
{
if(inForeignTable)
{
inForeignTable = false;
return;
}
if(isTableConstraint)
{
return;
}
cons="";
String colname = ctx.getText();
info("enterColumn_name()::colname =" + colname);
if (!colname.equals("CONSTRAINT"))
{
colname = trim(colname);
colnames.add(colname);
info("Columnname " + colname);
}
else
{
info("aha!!!");
tblconstraint = true;
}
}
boolean inForeignTable = false;
/**
* Enter a parse tree produced by {@link SQLiteParser#foreign_table}.
* @param ctx the parse tree
*/
@Override public void enterForeign_table(SQLiteParser.Foreign_tableContext ctx)
{
info("Enter foreign Table!!!");
inForeignTable = true;
}
@Override public void exitForeign_table(SQLiteParser.Foreign_tableContext ctx)
{
info("Exit foreign Table!!!");
}
@Override public void enterType_name(SQLiteParser.Type_nameContext ctx)
{
String value = ctx.getText();
value = value.trim();
/* the CONSTRAINT key word is mistakenly identified a type */
if (tblconstraint)
{
info("Table Constraint: " + value);
tableconstraint.add(value);
tblconstraint = false;
}
else
{
sqltypes.add(value);
info("SQLType::" + value);
String type = getType(value);
if (type.length()>0)
{
coltypes.add(type);
info("Typename " + trim(type));
}
}
}
@Override public void enterKeyword(SQLiteParser.KeywordContext ctx)
{
info("Enter keyword ");
}
@Override public void exitKeyword(SQLiteParser.KeywordContext ctx)
{
info("Exit keyword ");
}
String cons = "";
@Override public void enterColumn_constraint(SQLiteParser.Column_constraintContext ctx)
{
String constraint = ctx.getText().toUpperCase();
if (constraint.contains("NOTNULL"))
constraints.put(column,constraint);
}
@Override public void exitColumn_constraint(SQLiteParser.Column_constraintContext ctx)
{
String constraint = ctx.getText();
info("Columnconstraint " + constraint);
cons += constraint.toUpperCase() + " ";
}
@Override public void exitColumn_def(SQLiteParser.Column_defContext ctx)
{
info("adding cons:" + cons);
colconstraints.add(cons);
column++;
}
boolean isTableConstraint = false;
@Override public void enterTable_constraint(SQLiteParser.Table_constraintContext ctx)
{
isTableConstraint = true;
info("Table_constraint :: " +ctx.getText());
tableconstraint.add(ctx.getText());
}
@Override public void exitCreate_table_stmt(SQLiteParser.Create_table_stmtContext ctx)
{
/* create a pattern object for constrain matching of records */
HeaderPattern pattern = new HeaderPattern();
/* the pattern always starts with a header constraint */
pattern.addHeaderConstraint(colnames.size()+1,colnames.size()*2);
int cc = 0;
ListIterator list = coltypes.listIterator();
while(list.hasNext())
{
switch (list.next()) {
case "INT":
if (constraints.containsKey(cc))
pattern.add(new IntegerConstraint(true));
else
pattern.add(new IntegerConstraint(false));
break;
case "TEXT":
pattern.addStringConstraint();
break;
case "BLOB":
pattern.addBLOBConstraint();
break;
case "REAL":
pattern.addFloatingConstraint();
break;
case "NUMERIC":
pattern.addNumericConstraint();
break;
}
cc++;
}
tds = new TableDescriptor(tablename,stmt,sqltypes,coltypes,colnames,colconstraints,tableconstraint,pattern,stmt.contains("WITHOUT ROWID"));
info("PATTTERN: " + pattern);
}
},tree);
return tds;
}
/**
* Find out if there is any type information (key word) in
* the substring
* @param s the string, that should be parsed.
* @return the type (INT, TEXT, REAL etc.)
*/
private String getType(String s)
{
String type="";
/* Attention: issue 3 */
s = s.toUpperCase();
info("Datentyp" + s);
if (s.startsWith("TIMESTAMP"))
{
type="INT";
}
else if (stringContainsItemFromList(s, texttypes))
{
type="TEXT";
}
else if (stringContainsItemFromList(s, inttypes))
{
type="INT";
}
else if (stringContainsItemFromList(s, blobtype))
{
type="BLOB";
}
else if (stringContainsItemFromList(s, realtype))
{
type="REAL";
}
else if (stringContainsItemFromList(s, numerictype))
{
type="NUMERIC";
}
return type;
}
public static boolean stringContainsItemFromList(String inputStr, String[] items) {
return Arrays.stream(items).parallel().anyMatch(inputStr::contains);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy