#include "sqlitetypes.h" #include "grammar/Sqlite3Lexer.hpp" #include "grammar/Sqlite3Parser.hpp" #include #include namespace sqlb { QStringList Field::Datatypes = QStringList() << "INTEGER" << "TEXT" << "BLOB" << "REAL" << "NUMERIC"; QString escapeIdentifier(QString id) { return '`' + id.replace('`', "``") + '`'; } QStringList fieldVectorToFieldNames(const FieldVector& vector) { QStringList result; foreach(const FieldPtr& field, vector) result.append(escapeIdentifier(field->name())); return result; } /** * @brief The CreateTableWalker class * Goes trough the createtable AST and returns * Table object. */ class CreateTableWalker { public: explicit CreateTableWalker(antlr::RefAST r) : m_root(r) {} TablePtr table(); private: void parsecolumn(Table* table, antlr::RefAST c); private: antlr::RefAST m_root; }; /** * @brief The CreateIndexWalker class * Goes trough the createtable AST and returns * Index object. */ class CreateIndexWalker { public: explicit CreateIndexWalker(antlr::RefAST r) : m_root(r) {} IndexPtr index(); private: void parsecolumn(Index* index, antlr::RefAST c); private: antlr::RefAST m_root; }; ObjectPtr Object::parseSQL(Object::Types type, const QString& sSQL) { // Parse SQL statement according to type ObjectPtr result; switch(type) { case Object::Types::Table: result = Table::parseSQL(sSQL); break; case Object::Types::Index: result = Index::parseSQL(sSQL); break; default: return ObjectPtr(nullptr); } // Strore the original SQL statement and return the result result->setOriginalSql(sSQL); return result; } bool ForeignKeyClause::isSet() const { return m_override.size() || m_table.size(); } QString ForeignKeyClause::toString() const { if(!isSet()) return QString(); if(m_override.size()) return m_override; QString result = escapeIdentifier(m_table); if(m_columns.size()) { result += "("; foreach(const QString& column, m_columns) result += escapeIdentifier(column) + ','; result.chop(1); // Remove last comma result += ")"; } if(m_constraint.size()) result += " " + m_constraint; return result; } void ForeignKeyClause::setFromString(const QString& fk) { m_override = fk; } QString ForeignKeyClause::toSql(const FieldVector& applyOn) const { QString result; if(!m_name.isNull()) result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name)); result += QString("FOREIGN KEY(%1) REFERENCES %2").arg(fieldVectorToFieldNames(applyOn).join(",")).arg(this->toString()); return result; } QString UniqueConstraint::toSql(const FieldVector& applyOn) const { QString result; if(!m_name.isNull()) result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name)); result += QString("UNIQUE(%1)").arg(fieldVectorToFieldNames(applyOn).join(",")); return result; } QString PrimaryKeyConstraint::toSql(const FieldVector& applyOn) const { QString result; if(!m_name.isNull()) result += QString("CONSTRAINT %1 ").arg(escapeIdentifier(m_name)); result += QString("PRIMARY KEY(%1)").arg(fieldVectorToFieldNames(applyOn).join(",")); return result; } QString Field::toString(const QString& indent, const QString& sep) const { QString str = indent + escapeIdentifier(m_name) + sep + m_type; if(m_notnull) str += " NOT NULL"; if(!m_defaultvalue.isEmpty()) str += QString(" DEFAULT %1").arg(m_defaultvalue); if(!m_check.isEmpty()) str += " CHECK(" + m_check + ")"; if(m_autoincrement) str += " PRIMARY KEY AUTOINCREMENT"; if(m_unique) str += " UNIQUE"; return str; } bool Field::isText() const { QString norm = m_type.trimmed().toLower(); return norm.startsWith("character") || norm.startsWith("varchar") || norm.startsWith("varying character") || norm.startsWith("nchar") || norm.startsWith("native character") || norm.startsWith("nvarchar") || norm == "text" || norm == "clob"; } bool Field::isInteger() const { QString norm = m_type.trimmed().toLower(); return norm == "int" || norm == "integer" || norm == "tinyint" || norm == "smallint" || norm == "mediumint" || norm == "bigint" || norm == "unsigned big int" || norm == "int2" || norm == "int8"; } void Table::clear() { m_rowidColumn = "_rowid_"; m_fields.clear(); m_constraints.clear(); m_virtual = QString(); m_temporary = false; } Table::~Table() { clear(); } void Table::addField(const FieldPtr& f) { m_fields.append(FieldPtr(f)); } bool Table::removeField(const QString& sFieldName) { int index = findField(sFieldName); if( index != -1) { m_fields.remove(index); return true; } return false; } void Table::setFields(const FieldVector &fields) { clear(); m_fields = fields; } void Table::setField(int index, FieldPtr f) { FieldPtr oldField = m_fields[index]; m_fields[index] = f; // Update all constraints. If an existing field is updated but was used in a constraint, the pointers in the constraint key needs to be updated // to the new field, too. if(oldField) { ConstraintMap::iterator it = m_constraints.begin(); while(it != m_constraints.end()) { // Loop through all fields mentioned in a foreign key FieldVector fields = it.key(); bool modified = false; for(int i=0;iname().compare(sname, Qt::CaseInsensitive) == 0) return i; } return -1; } int Table::findPk() const { // TODO This is a stupid function (and always was) which should be fixed/improved FieldVector pk = primaryKey(); if(pk.empty()) return -1; else return findField(pk.at(0)->name()); } QStringList Table::fieldList() const { QStringList sl; foreach(const FieldPtr& f, m_fields) { sl << f->toString(); } return sl; } QStringList Table::fieldNames() const { QStringList sl; foreach(FieldPtr f, m_fields) sl << f->name(); return sl; } bool Table::hasAutoIncrement() const { foreach(FieldPtr f, m_fields) { if(f->autoIncrement()) return true; } return false; } ObjectPtr 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(antlr::ANTLRException& ex) { qCritical() << "Sqlite parse error: " << QString::fromStdString(ex.toString()) << "(" << sSQL << ")"; } catch(...) { qCritical() << "Sqlite parse error: " << sSQL; //TODO } return TablePtr(new Table("")); } QString Table::sql() const { // Special handling for virtual tables: just build an easy create statement and copy the using part in there if(isVirtual()) return QString("CREATE VIRTUAL TABLE %1 USING %2;").arg(escapeIdentifier(m_name)).arg(m_virtual); // This is a normal table, not a virtual one QString sql = QString("CREATE %1TABLE %2 (\n").arg(m_temporary ? QString("TEMPORARY ") : QString("")).arg(escapeIdentifier(m_name)); sql += fieldList().join(",\n"); // Constraints ConstraintMap::const_iterator it = m_constraints.constBegin(); bool autoincrement = hasAutoIncrement(); while(it != m_constraints.constEnd()) { if((!autoincrement || it.value()->type() != Constraint::PrimaryKeyConstraintType) && !it.key().isEmpty()) { sql += QString(",\n\t"); sql += it.value()->toSql(it.key()); } ++it; } sql += "\n)"; // without rowid if(isWithoutRowidTable()) sql += " WITHOUT ROWID"; return sql + ";"; } void Table::addConstraint(FieldVector fields, ConstraintPtr constraint) { m_constraints.insert(fields, constraint); } void Table::setConstraint(FieldVector fields, ConstraintPtr constraint) { // Delete any old constraints of this type for these fields removeConstraints(fields, constraint->type()); // Add the new constraint to the table, effectively overwriting all old constraints for that fields/type combination addConstraint(fields, constraint); } void Table::removeConstraints(FieldVector fields, Constraint::ConstraintTypes type) { QList list = constraints(fields, type); for (ConstraintPtr c : list) { m_constraints.remove(fields, c); } } ConstraintPtr Table::constraint(FieldVector fields, Constraint::ConstraintTypes type) const { QList list = constraints(fields, type); if(list.size()) return list.at(0); else return ConstraintPtr(nullptr); } QList Table::constraints(FieldVector fields, Constraint::ConstraintTypes type) const { QList clist; if(fields.isEmpty()) clist = m_constraints.values(); else clist = m_constraints.values(fields); if(type == Constraint::NoType) { return clist; } else { QList clist_typed; foreach(const ConstraintPtr& ptr, clist) { if(ptr->type() == type) clist_typed.push_back(ptr); } return clist_typed; } } void Table::setConstraints(const ConstraintMap& constraints) { m_constraints = constraints; } FieldVector& Table::primaryKeyRef() { return const_cast(static_cast(this)->primaryKey()); } const FieldVector& Table::primaryKey() const { auto it = m_constraints.constBegin(); while(it != m_constraints.constEnd()) { if(it.value()->type() == Constraint::PrimaryKeyConstraintType) return it.key(); ++it; } static FieldVector emptyFieldVector; return emptyFieldVector; } namespace { QString identifier(antlr::RefAST ident) { QString sident = ident->getText().c_str(); if(ident->getType() == sqlite3TokenTypes::QUOTEDID || ident->getType() == Sqlite3Lexer::QUOTEDLITERAL || ident->getType() == sqlite3TokenTypes::STRINGLITERAL) { // Remember the way the identifier is quoted QChar quoteChar = sident.at(0); // Remove first and final character, i.e. the quotes sident.remove(0, 1); sident.chop(1); // Replace all remaining occurences of two succeeding quote characters and replace them // by a single instance. This is done because two quotes can be used as a means of escaping // the quote character, thus only the visual representation has its two quotes, the actual // name contains only one. sident.replace(QString(quoteChar) + quoteChar, quoteChar); } return sident; } QString concatTextAST(antlr::RefAST t, bool withspace = false) { QStringList stext; while(t != antlr::nullAST) { // When this is called for a KEYWORDASTABLENAME token, we must take the child's content to get the actual value // instead of 'KEYWORDASTABLENAME' as a string. The same applies for KEYWORDASCOLUMNNAME tokens. if(t != antlr::nullAST && (t->getType() == sqlite3TokenTypes::KEYWORDASTABLENAME || t->getType() == sqlite3TokenTypes::KEYWORDASCOLUMNNAME)) stext.append(t->getFirstChild()->getText().c_str()); else stext.append(t->getText().c_str()); t = t->getNextSibling(); } return stext.join(withspace ? " " : ""); } } namespace { QString tablename(const antlr::RefAST& n) { if(n->getType() == sqlite3TokenTypes::KEYWORDASTABLENAME) return concatTextAST(n->getFirstChild()); else return identifier(n); } QString columnname(const antlr::RefAST& n) { if(n->getType() == sqlite3TokenTypes::KEYWORDASCOLUMNNAME) return concatTextAST(n->getFirstChild()); else return identifier(n); } } TablePtr CreateTableWalker::table() { Table* tab = new Table(""); tab->setFullyParsed(true); if( m_root ) //CREATE TABLE { antlr::RefAST s = m_root->getFirstChild(); // If the primary tree isn't filled, this isn't a normal CREATE TABLE statement. Switch to the next alternative tree. if(s == 0) s = m_root->getNextSibling(); // Skip to table name bool is_virtual_table = false; while(s->getType() != Sqlite3Lexer::ID && s->getType() != Sqlite3Lexer::QUOTEDID && s->getType() != Sqlite3Lexer::QUOTEDLITERAL && s->getType() != Sqlite3Lexer::STRINGLITERAL && s->getType() != sqlite3TokenTypes::KEYWORDASTABLENAME) { // Is this one of these virtual tables? if(s->getType() == Sqlite3Lexer::VIRTUAL) is_virtual_table = true; s = s->getNextSibling(); } // Extract and set table name tab->setName(tablename(s)); // Special handling for virtual tables. If this is a virtual table, extract the USING part and skip all the // rest of this function because virtual tables don't have column definitons if(is_virtual_table) { s = s->getNextSibling(); // USING s = s->getNextSibling(); // module name tab->setVirtualUsing(concatTextAST(s, true)); tab->setFullyParsed(false); return TablePtr(tab); } // This is a normal table, not a virtual one s = s->getNextSibling(); // LPAREN s = s->getNextSibling(); // first column name antlr::RefAST column = s; // loop columndefs while(column != antlr::nullAST && column->getType() == sqlite3TokenTypes::COLUMNDEF) { parsecolumn(tab, column->getFirstChild()); column = column->getNextSibling(); //COMMA or RPAREN column = column->getNextSibling(); //null or tableconstraint s = s->getNextSibling(); // COLUMNDEF s = s->getNextSibling(); // COMMA or RPAREN } // now we are finished or it is a tableconstraint while(s != antlr::nullAST) { // Is this a 'without rowid' definiton? if(s->getType() != sqlite3TokenTypes::WITHOUT) { // It's not, so treat this as table constraints antlr::RefAST tc = s->getFirstChild(); // Extract constraint name, if there is any QString constraint_name; if(tc->getType() == sqlite3TokenTypes::CONSTRAINT) { tc = tc->getNextSibling(); // CONSTRAINT constraint_name = identifier(tc); tc = tc->getNextSibling(); // identifier } switch(tc->getType()) { case sqlite3TokenTypes::PRIMARY: { PrimaryKeyConstraint* pk = new PrimaryKeyConstraint; pk->setName(constraint_name); tc = tc->getNextSibling()->getNextSibling(); // skip primary and key tc = tc->getNextSibling(); // skip LPAREN FieldVector fields; do { QString col = columnname(tc); FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); if(tc != antlr::nullAST && (tc->getType() == sqlite3TokenTypes::ASC || tc->getType() == sqlite3TokenTypes::DESC)) { // TODO save ASC / DESC information? tab->setFullyParsed(false); tc = tc->getNextSibling(); } if(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::AUTOINCREMENT) { field->setAutoIncrement(true); tc = tc->getNextSibling(); } while(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::COMMA) { tc = tc->getNextSibling(); // skip ident and comma } } while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN); tab->addConstraint(fields, ConstraintPtr(pk)); } break; case sqlite3TokenTypes::UNIQUE: { UniqueConstraint* unique = new UniqueConstraint; unique->setName(constraint_name); tc = tc->getNextSibling(); // skip UNIQUE tc = tc->getNextSibling(); // skip LPAREN FieldVector fields; do { QString col = columnname(tc); FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); if(tc != antlr::nullAST && (tc->getType() == sqlite3TokenTypes::ASC || tc->getType() == sqlite3TokenTypes::DESC)) { // TODO save ASC / DESC information? tab->setFullyParsed(false); tc = tc->getNextSibling(); } while(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::COMMA) { tc = tc->getNextSibling(); // skip ident and comma } } while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN); if(fields.size() == 1 && constraint_name.isEmpty()) { fields[0]->setUnique(true); delete unique; } else { tab->addConstraint(fields, ConstraintPtr(unique)); } } break; case sqlite3TokenTypes::FOREIGN: { ForeignKeyClause* fk = new ForeignKeyClause; fk->setName(constraint_name); tc = tc->getNextSibling(); // FOREIGN tc = tc->getNextSibling(); // KEY tc = tc->getNextSibling(); // LPAREN FieldVector fields; do { QString col = columnname(tc); FieldPtr field = tab->field(tab->findField(col)); fields.push_back(field); tc = tc->getNextSibling(); while(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::COMMA) tc = tc->getNextSibling(); // skip ident and comma } while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN); tc = tc->getNextSibling(); tc = tc->getNextSibling(); // REFERENCES fk->setTable(identifier(tc)); tc = tc->getNextSibling(); // identifier if(tc != antlr::nullAST && tc->getType() == sqlite3TokenTypes::LPAREN) { tc = tc->getNextSibling(); // LPAREN QStringList fk_cols; while(tc != antlr::nullAST && tc->getType() != sqlite3TokenTypes::RPAREN) { if(tc->getType() != sqlite3TokenTypes::COMMA) fk_cols.push_back(identifier(tc)); tc = tc->getNextSibling(); } fk->setColumns(fk_cols); tc = tc->getNextSibling(); // RPAREN } fk->setConstraint(concatTextAST(tc, true)); tab->addConstraint(fields, ConstraintPtr(fk)); } break; default: { tab->setFullyParsed(false); } break; } s = s->getNextSibling(); //COMMA or RPAREN if(s->getType() == sqlite3TokenTypes::COMMA || s->getType() == sqlite3TokenTypes::RPAREN) s = s->getNextSibling(); } else { // It is s = s->getNextSibling(); // WITHOUT s = s->getNextSibling(); // ROWID tab->setRowidColumn(tab->fields().at(tab->findPk())->name()); } } } return TablePtr(tab); } void CreateTableWalker::parsecolumn(Table* table, antlr::RefAST c) { QString colname; QString type = "TEXT"; bool autoincrement = false; bool notnull = false; bool unique = false; QString defaultvalue; QString check; sqlb::PrimaryKeyConstraint* primaryKey = 0; sqlb::ForeignKeyClause* foreignKey = 0; colname = columnname(c); c = c->getNextSibling(); //type? if(c != antlr::nullAST && c->getType() == sqlite3TokenTypes::TYPE_NAME) { type = concatTextAST(c->getFirstChild(), true); c = c->getNextSibling(); } // finished with type parsing // now columnconstraints while(c != antlr::nullAST) { antlr::RefAST con = c->getFirstChild(); // Extract constraint name, if there is any QString constraint_name; if(con->getType() == sqlite3TokenTypes::CONSTRAINT) { con = con->getNextSibling(); // CONSTRAINT constraint_name = identifier(con); con = con->getNextSibling(); // identifier } switch(con->getType()) { case sqlite3TokenTypes::PRIMARY: { primaryKey = new PrimaryKeyConstraint; primaryKey->setName(constraint_name); con = con->getNextSibling()->getNextSibling(); // skip KEY if(con != antlr::nullAST && (con->getType() == sqlite3TokenTypes::ASC || con->getType() == sqlite3TokenTypes::DESC)) { table->setFullyParsed(false); con = con->getNextSibling(); //skip } if(con != antlr::nullAST && con->getType() == sqlite3TokenTypes::AUTOINCREMENT) autoincrement = true; } break; case sqlite3TokenTypes::NOT: { // TODO Support constraint names here if(!constraint_name.isEmpty()) table->setFullyParsed(false); notnull = true; } break; case sqlite3TokenTypes::NULL_T: { notnull = false; } break; case sqlite3TokenTypes::CHECK: { // TODO Support constraint names here if(!constraint_name.isEmpty()) table->setFullyParsed(false); con = con->getNextSibling(); //LPAREN check = concatTextAST(con, true); // remove parenthesis check.remove(check.length()-1, 1); check.remove(0,1); check = check.trimmed(); } break; case sqlite3TokenTypes::DEFAULT: { // TODO Support constraint names here if(!constraint_name.isEmpty()) table->setFullyParsed(false); con = con->getNextSibling(); //SIGNEDNUMBER,STRING,LPAREN defaultvalue = concatTextAST(con); } break; case sqlite3TokenTypes::UNIQUE: { // TODO Support constraint names here if(!constraint_name.isEmpty()) table->setFullyParsed(false); unique = true; } break; case sqlite3TokenTypes::REFERENCES: { con = con->getNextSibling(); // REFERENCES foreignKey = new ForeignKeyClause; foreignKey->setTable(identifier(con)); foreignKey->setName(constraint_name); con = con->getNextSibling(); // identifier if(con != antlr::nullAST && con->getType() == sqlite3TokenTypes::LPAREN) { con = con->getNextSibling(); // LPAREN QStringList fk_cols; while(con != antlr::nullAST && con->getType() != sqlite3TokenTypes::RPAREN) { if(con->getType() != sqlite3TokenTypes::COMMA) fk_cols.push_back(identifier(con)); con = con->getNextSibling(); } foreignKey->setColumns(fk_cols); con = con->getNextSibling(); // RPAREN } foreignKey->setConstraint(concatTextAST(con, true)); } break; default: { table->setFullyParsed(false); } break; } c = c->getNextSibling(); } FieldPtr f = FieldPtr(new Field(colname, type, notnull, defaultvalue, check, unique)); f->setAutoIncrement(autoincrement); table->addField(f); if(foreignKey) table->addConstraint({f}, ConstraintPtr(foreignKey)); if(primaryKey) { FieldVector v; if(table->constraint(v, Constraint::PrimaryKeyConstraintType)) table->primaryKeyRef().push_back(f); else table->addConstraint({f}, ConstraintPtr(primaryKey)); } } QString IndexedColumn::toString(const QString& indent, const QString& sep) const { return indent + (m_isExpression ? m_name : escapeIdentifier(m_name)) + sep + m_order; } Index::~Index() { clear(); } void Index::clear() { m_name.clear(); m_unique = false; m_table.clear(); m_whereExpr.clear(); m_columns.clear(); } bool Index::removeColumn(const QString& name) { int index = findColumn(name); if(index != -1) { m_columns.remove(index); return true; } return false; } void Index::setColumns(const IndexedColumnVector& columns) { clear(); m_columns = columns; } int Index::findColumn(const QString& name) const { for(int i=0;iname().compare(name, Qt::CaseInsensitive) == 0) return i; } return -1; } QStringList Index::columnSqlList() const { QStringList sl; foreach(const IndexedColumnPtr& c, m_columns) sl << c->toString(); return sl; } QString Index::sql() const { // Start CREATE (UNIQUE) INDEX statement QString sql = QString("CREATE %1INDEX %2 ON %3 (\n") .arg(m_unique ? QString("UNIQUE ") : QString("")) .arg(escapeIdentifier(m_name)) .arg(escapeIdentifier(m_table)); // Add column list sql += columnSqlList().join(",\n"); // Add partial index bit sql += QString("\n)"); if(!m_whereExpr.isEmpty()) sql += QString(" WHERE ") + m_whereExpr; return sql + ";"; } ObjectPtr Index::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.createindex(); CreateIndexWalker ctw(parser.getAST()); return ctw.index(); } catch(antlr::ANTLRException& ex) { qCritical() << "Sqlite parse error: " << QString::fromStdString(ex.toString()) << "(" << sSQL << ")"; } catch(...) { qCritical() << "Sqlite parse error: " << sSQL; //TODO } return IndexPtr(new Index("")); } IndexPtr CreateIndexWalker::index() { Index* index = new Index(""); index->setFullyParsed(true); if(m_root) // CREATE INDEX { antlr::RefAST s = m_root->getFirstChild(); // Skip to index name while(s->getType() != Sqlite3Lexer::ID && s->getType() != Sqlite3Lexer::QUOTEDID && s->getType() != Sqlite3Lexer::QUOTEDLITERAL && s->getType() != Sqlite3Lexer::STRINGLITERAL && s->getType() != sqlite3TokenTypes::KEYWORDASTABLENAME) { // Is this a unique index? if(s->getType() == Sqlite3Lexer::UNIQUE) index->setUnique(true); s = s->getNextSibling(); } // Extract and set index name index->setName(tablename(s)); // Get table name s = s->getNextSibling(); // ON s = s->getNextSibling(); // table name index->setTable(tablename(s)); s = s->getNextSibling(); // LPAREN s = s->getNextSibling(); // first column name antlr::RefAST column = s; // loop columndefs while(column != antlr::nullAST && column->getType() == sqlite3TokenTypes::INDEXCOLUMN) { parsecolumn(index, column->getFirstChild()); column = column->getNextSibling(); // COMMA or RPAREN column = column->getNextSibling(); // null or WHERE s = s->getNextSibling(); // COLUMNDEF s = s->getNextSibling(); // COMMA or RPAREN } // Now we are finished or it is a partial index if(s != antlr::nullAST) { // This should be a 'where' then if(s->getType() != sqlite3TokenTypes::WHERE) { // It is something else index->setFullyParsed(false); } else { s = s->getNextSibling(); // expr index->setWhereExpr(concatTextAST(s, true)); } } } return IndexPtr(index); } void CreateIndexWalker::parsecolumn(Index* index, antlr::RefAST c) { QString name; bool isExpression; QString order; // First count the number of nodes used for the name or the expression. We reach the end of the name nodes list when we either // get to the end of the list, get to a COMMA or a RPAREN, or get to the COLLATE keyword or get to the ASC/DESC keywords. // Then see how many items there are: if it's one it's a normal index column with only a column name. In this case get the identifier. // If it's more than one item it's an expression. In this case get all the items as they are. int number_of_name_items = 0; antlr::RefAST n = c; while(n != antlr::nullAST && n->getType() != sqlite3TokenTypes::COLLATE && n->getType() != sqlite3TokenTypes::ASC && n->getType() != sqlite3TokenTypes::DESC && n->getType() != sqlite3TokenTypes::COMMA && n->getType() != sqlite3TokenTypes::RPAREN) { number_of_name_items++; n = n->getNextSibling(); } if(number_of_name_items == 1) { name = identifier(c); isExpression = false; c = c->getNextSibling(); } else { for(int i=0;igetText().c_str() + QString(" "); c = c->getNextSibling(); } name.chop(1); isExpression = true; } // Parse the rest of the column definition while(c != antlr::nullAST) { switch(c->getType()) { case sqlite3TokenTypes::ASC: case sqlite3TokenTypes::DESC: order = c->getText().c_str(); break; default: // TODO Add support for COLLATE index->setFullyParsed(false); } c = c->getNextSibling(); } index->addColumn(IndexedColumnPtr(new IndexedColumn(name, isExpression, order))); } } //namespace sqlb