mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 02:50:46 -06:00
Add a sqlite3 antlr2 "create table" grammar + generated parser files
There is also a "create table" ast walker which fills info for the new sqlitetype objects. A dependency to the antlr2 runtime was added. The grammar most probably still contain bugs. Why all this? First writing grammars is fun and this is the only way we can get all information for proper table editing + some time in the future when the grammar is finished we can provide real auto completion.
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
#include "sqlitetypes.h"
|
||||
#include "grammar/Sqlite3Lexer.hpp"
|
||||
#include "grammar/Sqlite3Parser.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <QDebug>
|
||||
|
||||
namespace sqlb {
|
||||
|
||||
@@ -9,6 +14,8 @@ QString Field::toString(const QString& indent, const QString& sep) const
|
||||
QString str = indent + m_name + sep + m_type;
|
||||
if(m_notnull)
|
||||
str += " NOT NULL";
|
||||
if(!m_defaultvalue.isEmpty())
|
||||
str += " DEFAULT " + m_defaultvalue;
|
||||
if(!m_check.isEmpty())
|
||||
str += " CHECK(" + m_check + ")";
|
||||
if(m_autoincrement)
|
||||
@@ -47,9 +54,6 @@ bool Field::isInteger() const
|
||||
|
||||
void Table::clear()
|
||||
{
|
||||
foreach(Field* f, m_fields) {
|
||||
delete f;
|
||||
}
|
||||
m_fields.clear();
|
||||
m_primarykey.clear();
|
||||
}
|
||||
@@ -59,9 +63,9 @@ Table::~Table()
|
||||
clear();
|
||||
}
|
||||
|
||||
void Table::addField(Field *f)
|
||||
void Table::addField(const FieldPtr& f)
|
||||
{
|
||||
m_fields.append(f);
|
||||
m_fields.append(FieldPtr(f));
|
||||
}
|
||||
|
||||
void Table::setFields(const FieldList &fields)
|
||||
@@ -71,9 +75,19 @@ void Table::setFields(const FieldList &fields)
|
||||
m_fields = fields;
|
||||
}
|
||||
|
||||
int Table::findField(const QString &sname)
|
||||
{
|
||||
for(int i = 0; i < m_fields.count(); ++i)
|
||||
{
|
||||
if(sname == m_fields.at(i)->name())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Table::setPrimaryKey(const FieldList& pk)
|
||||
{
|
||||
foreach(Field* f, pk) {
|
||||
foreach(FieldPtr f, pk) {
|
||||
if(!m_fields.contains(f))
|
||||
return false;
|
||||
}
|
||||
@@ -82,7 +96,7 @@ bool Table::setPrimaryKey(const FieldList& pk)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Table::setPrimaryKey(Field* pk, bool autoincrement)
|
||||
bool Table::setPrimaryKey(FieldPtr pk, bool autoincrement)
|
||||
{
|
||||
if(m_fields.contains(pk))
|
||||
{
|
||||
@@ -100,7 +114,7 @@ QStringList Table::fieldList() const
|
||||
{
|
||||
QStringList sl;
|
||||
|
||||
foreach(Field* f, m_fields) {
|
||||
foreach(FieldPtr f, m_fields) {
|
||||
sl << f->toString();
|
||||
}
|
||||
|
||||
@@ -109,13 +123,39 @@ QStringList Table::fieldList() const
|
||||
|
||||
bool Table::hasAutoIncrement() const
|
||||
{
|
||||
foreach(Field* f, m_fields) {
|
||||
foreach(FieldPtr f, m_fields) {
|
||||
if(f->autoIncrement())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Table Table::parseSQL(const QString &sSQL)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << sSQL.toStdString();
|
||||
Sqlite3Lexer lex(s);
|
||||
|
||||
Sqlite3Parser parser(lex);
|
||||
|
||||
antlr::ASTFactory ast_factory;
|
||||
parser.initializeASTFactory(ast_factory);
|
||||
parser.setASTFactory(&ast_factory);
|
||||
|
||||
try
|
||||
{
|
||||
parser.createtable();
|
||||
CreateTableWalker ctw(parser.getAST());
|
||||
return ctw.table();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
qCritical() << "Sqlite parse error."; //TODO
|
||||
}
|
||||
|
||||
return Table("");
|
||||
}
|
||||
|
||||
QString Table::sql() const
|
||||
{
|
||||
QString sql = QString("CREATE TABLE `%1` (\n").arg(m_name);
|
||||
@@ -126,17 +166,180 @@ QString Table::sql() const
|
||||
if( m_primarykey.size() > 0 && !hasAutoIncrement())
|
||||
{
|
||||
sql += ",\n\tPRIMARY KEY(";
|
||||
for(QList<Field*>::ConstIterator it = m_primarykey.constBegin();
|
||||
for(FieldList::ConstIterator it = m_primarykey.constBegin();
|
||||
it != m_primarykey.constEnd();
|
||||
++it)
|
||||
{
|
||||
sql += (*it)->name();
|
||||
if(it != --m_primarykey.constEnd())
|
||||
if(*it != m_primarykey.last())
|
||||
sql += ",";
|
||||
}
|
||||
sql += ")";
|
||||
}
|
||||
|
||||
return sql + "\n)";
|
||||
return sql + "\n);";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QString identifier(antlr::RefAST ident)
|
||||
{
|
||||
QString sident = ident->getText().c_str();
|
||||
if(ident->getType() == sqlite3TokenTypes::QUOTEDID)
|
||||
{
|
||||
sident.remove(0, 1);
|
||||
sident.remove(sident.length() - 1, 1);
|
||||
}
|
||||
return sident;
|
||||
}
|
||||
|
||||
QString concatTextAST(antlr::RefAST t)
|
||||
{
|
||||
QString stext;
|
||||
while(t != antlr::nullAST)
|
||||
{
|
||||
stext.append(t->getText().c_str());
|
||||
t = t->getNextSibling();
|
||||
}
|
||||
return stext;
|
||||
}
|
||||
}
|
||||
|
||||
Table CreateTableWalker::table()
|
||||
{
|
||||
Table tab("");
|
||||
|
||||
if( m_root ) //CREATE TABLE
|
||||
{
|
||||
antlr::RefAST s = m_root->getFirstChild();
|
||||
//qDebug() << m_root->toStringTree().cd;
|
||||
//skip to tablename
|
||||
while(s->getType() != Sqlite3Lexer::ID && s->getType() != Sqlite3Lexer::QUOTEDID) s = s->getNextSibling();
|
||||
tab.setName(identifier(s));
|
||||
|
||||
s = s->getNextSibling(); // LPAREN
|
||||
s = s->getNextSibling(); // first column name
|
||||
antlr::RefAST column = s;
|
||||
// loop columndefs
|
||||
FieldList pks;
|
||||
while(column != antlr::nullAST && column->getType() == sqlite3TokenTypes::COLUMNDEF)
|
||||
{
|
||||
FieldPtr f;
|
||||
bool pk = parsecolumn(f, column->getFirstChild());
|
||||
tab.addField(f);
|
||||
if(pk)
|
||||
pks.append(FieldPtr(f));
|
||||
column = column->getNextSibling(); //COMMA or RPAREN
|
||||
column = column->getNextSibling(); //null or tableconstraint
|
||||
}
|
||||
|
||||
tab.setPrimaryKey(pks);
|
||||
|
||||
// now we are finished or it is a tableconstraint
|
||||
while(s != antlr::nullAST)
|
||||
{
|
||||
antlr::RefAST tc = s->getFirstChild();
|
||||
// skip constraint name, if there is any
|
||||
if(tc->getType() == sqlite3TokenTypes::CONSTRAINT)
|
||||
tc = tc->getNextSibling()->getNextSibling();
|
||||
|
||||
switch(tc->getType())
|
||||
{
|
||||
case sqlite3TokenTypes::PRIMARY:
|
||||
{
|
||||
tc = tc->getNextSibling()->getNextSibling(); // skip primary and key
|
||||
tc = tc->getNextSibling(); // skip LPAREN
|
||||
pks.clear();
|
||||
do
|
||||
{
|
||||
QString col = identifier(tc);
|
||||
int fieldindex = tab.findField(col);
|
||||
if(fieldindex != -1)
|
||||
pks.append(tab.fields().at(fieldindex));
|
||||
tc = tc->getNextSibling(); // skip ident and comma
|
||||
tc = tc->getNextSibling();
|
||||
} while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN);
|
||||
|
||||
tab.setPrimaryKey(pks);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
s = s->getNextSibling(); //COMMA or RPAREN
|
||||
s = s->getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
bool CreateTableWalker::parsecolumn(FieldPtr& f, antlr::RefAST c)
|
||||
{
|
||||
QString columnname;
|
||||
QString type = "TEXT";
|
||||
bool autoincrement = false;
|
||||
bool primarykey = false;
|
||||
bool notnull = false;
|
||||
QString defaultvalue;
|
||||
QString check;
|
||||
|
||||
columnname = identifier(c);
|
||||
c = c->getNextSibling(); //type?
|
||||
if(c != antlr::nullAST && c->getType() == sqlite3TokenTypes::TYPE_NAME)
|
||||
{
|
||||
type = concatTextAST(c->getFirstChild());
|
||||
c = c->getNextSibling();
|
||||
}
|
||||
|
||||
// finished with type parsing
|
||||
// now columnconstraints
|
||||
while(c != antlr::nullAST)
|
||||
{
|
||||
antlr::RefAST con = c->getFirstChild();
|
||||
|
||||
// skip constraint name, if there is any
|
||||
if(con->getType() == sqlite3TokenTypes::CONSTRAINT)
|
||||
con = con->getNextSibling()->getNextSibling();
|
||||
|
||||
switch(con->getType())
|
||||
{
|
||||
case sqlite3TokenTypes::PRIMARY:
|
||||
{
|
||||
primarykey = true;
|
||||
con = con->getNextSibling()->getNextSibling(); // skip KEY
|
||||
if(con != antlr::nullAST && (con->getType() == sqlite3TokenTypes::ASC
|
||||
|| con->getType() == sqlite3TokenTypes::DESC))
|
||||
con = con->getNextSibling(); //skip
|
||||
if(con != antlr::nullAST && con->getType() == sqlite3TokenTypes::AUTOINCREMENT)
|
||||
autoincrement = true;
|
||||
}
|
||||
break;
|
||||
case sqlite3TokenTypes::NOT:
|
||||
{
|
||||
notnull = true;
|
||||
}
|
||||
break;
|
||||
case sqlite3TokenTypes::CHECK:
|
||||
{
|
||||
con = con->getNextSibling(); //LPAREN
|
||||
check = concatTextAST(con);
|
||||
}
|
||||
break;
|
||||
case sqlite3TokenTypes::DEFAULT:
|
||||
{
|
||||
con = con->getNextSibling(); //SIGNEDNUMBER,STRING,LPAREN
|
||||
defaultvalue = concatTextAST(con);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
c = c->getNextSibling();
|
||||
}
|
||||
|
||||
f = FieldPtr( new Field(columnname, type, notnull, defaultvalue, check));
|
||||
f->setAutoIncrement(autoincrement);
|
||||
return primarykey;
|
||||
}
|
||||
|
||||
} //namespace sqlb
|
||||
|
||||
Reference in New Issue
Block a user