From 172016e3d3b5cfe6f51470d43669f794b993da65 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Wed, 29 Aug 2018 00:14:51 -0500 Subject: declares XMLCodeGenerator in xmlgen.h In order for sqlite3gen to re-use parts of the XML generator, XMLCodeGenerator needs to be declared in a header file. I parroted how other generators handle this and have it working in both the XML and sqlite3 outputs, but I don't have any sense of whether this is otherwise "right". --- src/xmlgen.cpp | 233 +++++++++++++++++++++++++-------------------------------- src/xmlgen.h | 42 +++++++++++ 2 files changed, 144 insertions(+), 131 deletions(-) diff --git a/src/xmlgen.cpp b/src/xmlgen.cpp index e68c454..88350d3 100644 --- a/src/xmlgen.cpp +++ b/src/xmlgen.cpp @@ -222,146 +222,117 @@ class TextGeneratorXMLImpl : public TextGeneratorIntf /** Generator for producing XML formatted source code. */ -class XMLCodeGenerator : public CodeOutputInterface +void XMLCodeGenerator::codify(const char *text) { - public: - - XMLCodeGenerator(FTextStream &t) : m_t(t), m_lineNumber(-1), m_isMemberRef(FALSE), m_col(0), - m_insideCodeLine(FALSE), m_normalHLNeedStartTag(TRUE), m_insideSpecialHL(FALSE) {} - virtual ~XMLCodeGenerator() { } - - void codify(const char *text) - { - XML_DB(("(codify \"%s\")\n",text)); - if (m_insideCodeLine && !m_insideSpecialHL && m_normalHLNeedStartTag) - { - m_t << ""; - m_normalHLNeedStartTag=FALSE; - } - writeXMLCodeString(m_t,text,m_col); - } - void writeCodeLink(const char *ref,const char *file, - const char *anchor,const char *name, - const char *tooltip) - { - XML_DB(("(writeCodeLink)\n")); - if (m_insideCodeLine && !m_insideSpecialHL && m_normalHLNeedStartTag) - { - m_t << ""; - m_normalHLNeedStartTag=FALSE; - } - writeXMLLink(m_t,ref,file,anchor,name,tooltip); - m_col+=qstrlen(name); - } - void writeTooltip(const char *, const DocLinkInfo &, const char *, - const char *, const SourceLinkInfo &, const SourceLinkInfo & - ) - { - XML_DB(("(writeToolTip)\n")); - } - void startCodeLine(bool) - { - XML_DB(("(startCodeLine)\n")); - m_t << ""; - m_insideCodeLine=TRUE; - m_col=0; - } - void endCodeLine() - { - XML_DB(("(endCodeLine)\n")); - if (!m_insideSpecialHL && !m_normalHLNeedStartTag) - { - m_t << ""; - m_normalHLNeedStartTag=TRUE; - } - m_t << "" << endl; // non DocBook - m_lineNumber = -1; - m_refId.resize(0); - m_external.resize(0); - m_insideCodeLine=FALSE; - } - void startFontClass(const char *colorClass) + XML_DB(("(codify \"%s\")\n",text)); + if (m_insideCodeLine && !m_insideSpecialHL && m_normalHLNeedStartTag) + { + m_t << ""; + m_normalHLNeedStartTag=FALSE; + } + writeXMLCodeString(m_t,text,m_col); +} +void XMLCodeGenerator::writeCodeLink(const char *ref,const char *file, + const char *anchor,const char *name, + const char *tooltip) +{ + XML_DB(("(writeCodeLink)\n")); + if (m_insideCodeLine && !m_insideSpecialHL && m_normalHLNeedStartTag) + { + m_t << ""; + m_normalHLNeedStartTag=FALSE; + } + writeXMLLink(m_t,ref,file,anchor,name,tooltip); + m_col+=qstrlen(name); +} +void XMLCodeGenerator::writeTooltip(const char *, const DocLinkInfo &, const char *, + const char *, const SourceLinkInfo &, const SourceLinkInfo & + ) +{ + XML_DB(("(writeToolTip)\n")); +} +void XMLCodeGenerator::startCodeLine(bool) +{ + XML_DB(("(startCodeLine)\n")); + m_t << ""; - m_normalHLNeedStartTag=TRUE; + m_t << " refkind=\"member\""; } - m_t << ""; // non DocBook - m_insideSpecialHL=TRUE; - } - void endFontClass() - { - XML_DB(("(endFontClass)\n")); - m_t << ""; // non DocBook - m_insideSpecialHL=FALSE; - } - void writeCodeAnchor(const char *) - { - XML_DB(("(writeCodeAnchor)\n")); - } - void writeLineNumber(const char *extRef,const char *compId, - const char *anchorId,int l) - { - XML_DB(("(writeLineNumber)\n")); - // we remember the information provided here to use it - // at the start tag. - m_lineNumber = l; - if (compId) + else { - m_refId=compId; - if (anchorId) m_refId+=(QCString)"_1"+anchorId; - m_isMemberRef = anchorId!=0; - if (extRef) m_external=extRef; + m_t << " refkind=\"compound\""; } } - void setCurrentDoc(Definition *,const char *,bool) - { - } - void addWord(const char *,bool) + if (!m_external.isEmpty()) { + m_t << " external=\"" << m_external << "\""; } - - void finish() - { - if (m_insideCodeLine) endCodeLine(); - } - - private: - FTextStream &m_t; - QCString m_refId; - QCString m_external; - int m_lineNumber; - bool m_isMemberRef; - int m_col; - - bool m_insideCodeLine; - bool m_normalHLNeedStartTag; - bool m_insideSpecialHL; -}; - + } + m_t << ">"; + m_insideCodeLine=TRUE; + m_col=0; +} +void XMLCodeGenerator::endCodeLine() +{ + XML_DB(("(endCodeLine)\n")); + if (!m_insideSpecialHL && !m_normalHLNeedStartTag) + { + m_t << ""; + m_normalHLNeedStartTag=TRUE; + } + m_t << "" << endl; // non DocBook + m_lineNumber = -1; + m_refId.resize(0); + m_external.resize(0); + m_insideCodeLine=FALSE; +} +void XMLCodeGenerator::startFontClass(const char *colorClass) +{ + XML_DB(("(startFontClass)\n")); + if (m_insideCodeLine && !m_insideSpecialHL && !m_normalHLNeedStartTag) + { + m_t << ""; + m_normalHLNeedStartTag=TRUE; + } + m_t << ""; // non DocBook + m_insideSpecialHL=TRUE; +} +void XMLCodeGenerator::endFontClass() +{ + XML_DB(("(endFontClass)\n")); + m_t << ""; // non DocBook + m_insideSpecialHL=FALSE; +} +void XMLCodeGenerator::writeCodeAnchor(const char *) +{ + XML_DB(("(writeCodeAnchor)\n")); +} +void XMLCodeGenerator::writeLineNumber(const char *extRef,const char *compId, + const char *anchorId,int l) +{ + XML_DB(("(writeLineNumber)\n")); + // we remember the information provided here to use it + // at the start tag. + m_lineNumber = l; + if (compId) + { + m_refId=compId; + if (anchorId) m_refId+=(QCString)"_1"+anchorId; + m_isMemberRef = anchorId!=0; + if (extRef) m_external=extRef; + } +} +void XMLCodeGenerator::finish() +{ + if (m_insideCodeLine) endCodeLine(); +} static void writeTemplateArgumentList(ArgumentList *al, FTextStream &t, diff --git a/src/xmlgen.h b/src/xmlgen.h index 0447591..0555546 100644 --- a/src/xmlgen.h +++ b/src/xmlgen.h @@ -15,6 +15,48 @@ #ifndef XMLGEN_H #define XMLGEN_H +#include "outputgen.h" + +class XMLCodeGenerator : public CodeOutputInterface +{ + public: + + XMLCodeGenerator(FTextStream &t) : m_t(t), m_lineNumber(-1), m_isMemberRef(FALSE), m_col(0), + m_insideCodeLine(FALSE), m_normalHLNeedStartTag(TRUE), m_insideSpecialHL(FALSE) {} + virtual ~XMLCodeGenerator() { } + + void codify(const char *text); + void writeCodeLink(const char *ref,const char *file, + const char *anchor,const char *name, + const char *tooltip); + void writeTooltip(const char *, const DocLinkInfo &, const char *, + const char *, const SourceLinkInfo &, const SourceLinkInfo & + ); + void startCodeLine(bool); + void endCodeLine(); + void startFontClass(const char *colorClass); + void endFontClass(); + void writeCodeAnchor(const char *); + void writeLineNumber(const char *extRef,const char *compId, + const char *anchorId,int l); + void setCurrentDoc(Definition *,const char *,bool){} + void addWord(const char *,bool){} + + void finish(); + + private: + FTextStream &m_t; + QCString m_refId; + QCString m_external; + int m_lineNumber; + bool m_isMemberRef; + int m_col; + + bool m_insideCodeLine; + bool m_normalHLNeedStartTag; + bool m_insideSpecialHL; +}; + void generateXML(); #endif -- cgit v0.12 From 61cddaf2d440aff48868fc3a50185a2788917914 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Sun, 2 Sep 2018 22:14:57 -0500 Subject: Expand sqlite3gen's breadth, depth, and quality Generated sqlite3 databases are roughly ready to support tools and projects that require a significant fraction of what Doxygen collects. output quality: - collect primary info for most major entities and relationships - fix causes of junk data in some columns (i.e. const * / QCString / string / memory issues) - convert decription field markup (mix of markdown, HTML, and Doxygen commands) into XML - eliminate duplicate/overlapping data (both rows and columns) usability: - abort if database already exists; prompt user to archive/delete it - record Doxygen and sql schema versions so tools/clients can target/message appropriately - refine schema to support and simplify common queries addon/doxypysql: - adopt schema changes - py3 compat --- addon/doxypysql/search.py | 124 +-- src/sqlite3gen.cpp | 2157 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 1689 insertions(+), 592 deletions(-) diff --git a/addon/doxypysql/search.py b/addon/doxypysql/search.py index c185138..5f820de 100755 --- a/addon/doxypysql/search.py +++ b/addon/doxypysql/search.py @@ -17,19 +17,19 @@ import json import re class MemberType: - Define="0" - Function="1" - Variable="2" - Typedef="3" - Enumeration="4" - EnumValue="5" - Signal="6" - Slot="7" - Friend="8" - DCOP="9" - Property="10" - Event="11" - File="12" + Define="macro definition" + Function="function" + Variable="variable" + Typedef="typedef" + Enumeration="enumeration" + EnumValue="enumvalue" + Signal="signal" + Slot="slot" + Friend="friend" + DCOP="dcop" + Property="property" + Event="event" + File="file" class RequestType: References="9901" @@ -69,27 +69,27 @@ class Finder: def match(self,row): if self.row_type is int: - return " id=?" + return " rowid=?" else: if g_use_regexp == True: return " REGEXP (?,%s)" %row else: return " %s=?" %row - def fileName(self,id_file): - if self.cn.execute("SELECT COUNT(*) FROM files WHERE rowid=?",[id_file]).fetchone()[0] > 1: - print >>sys.stderr,"WARNING: non-uniq fileid [%s]. Considering only the first match." % id_file + def fileName(self,file_id): + if self.cn.execute("SELECT COUNT(*) FROM path WHERE rowid=?",[file_id]).fetchone()[0] > 1: + sys.stderr.write("WARNING: non-uniq fileid [%s]. Considering only the first match." % file_id) - for r in self.cn.execute("SELECT * FROM files WHERE rowid=?",[id_file]).fetchall(): + for r in self.cn.execute("SELECT * FROM path WHERE rowid=?",[file_id]).fetchall(): return r['name'] return "" def fileId(self,name): - if self.cn.execute("SELECT COUNT(*) FROM files WHERE"+self.match("name"),[name]).fetchone()[0] > 1: - print >>sys.stderr,"WARNING: non-uniq file name [%s]. Considering only the first match." % name + if self.cn.execute("SELECT COUNT(*) FROM path WHERE"+self.match("name"),[name]).fetchone()[0] > 1: + sys.stderr.write("WARNING: non-uniq file name [%s]. Considering only the first match." % name) - for r in self.cn.execute("SELECT rowid FROM files WHERE"+self.match("name"),[name]).fetchall(): + for r in self.cn.execute("SELECT rowid FROM path WHERE"+self.match("name"),[name]).fetchall(): return r[0] return -1 @@ -97,23 +97,24 @@ class Finder: def references(self): o=[] cur = self.cn.cursor() - cur.execute("SELECT refid FROM memberdef WHERE"+self.match("name"),[self.name]) - refids = cur.fetchall() + cur.execute("SELECT rowid FROM memberdef WHERE"+self.match("name"),[self.name]) + rowids = cur.fetchall() - if len(refids) == 0: + if len(rowids) == 0: return o - refid = refids[0]['refid'] + rowid = rowids[0]['rowid'] cur = self.cn.cursor() - #TODO:SELECT rowid from refids where refid=refid - for info in cur.execute("SELECT * FROM xrefs WHERE refid_dst LIKE '%"+refid+"%'"): + #TODO:SELECT rowid from refid where refid=refid + for info in cur.execute("SELECT * FROM xrefs WHERE dst_rowid=?", [rowid]): item={} cur = self.cn.cursor() - for i2 in cur.execute("SELECT * FROM memberdef WHERE refid=?",[info['src']]): + for i2 in cur.execute("SELECT * FROM memberdef WHERE rowid=?",[info['src_rowid']]): item['name']=i2['name'] - item['src']=info['src'] - item['file']=self.fileName(info['id_file']) - item['line']=info['line'] + item['src']=info['src_rowid'] + # Below no longer directly supported on this entry; can be found from either memberdef + #item['file']=self.fileName(info['file_id']) + #item['line']=info['line'] o.append(item) return o @@ -126,7 +127,7 @@ class Finder: item['name'] = r['name'] item['definition'] = r['definition'] item['argsstring'] = r['argsstring'] - item['file'] = self.fileName(r['id_file']) + item['file'] = self.fileName(r['file_id']) item['line'] = r['line'] item['detaileddescription'] = r['detaileddescription'] o.append(item) @@ -134,7 +135,7 @@ class Finder: ############################################################################### def file(self): o=[] - for r in self.cn.execute("SELECT rowid,* FROM files WHERE"+self.match("name"),[self.name]).fetchall(): + for r in self.cn.execute("SELECT rowid,name FROM local_file WHERE"+self.match("name"),[self.name]).fetchall(): item={} item['name'] = r['name'] item['id'] = r['rowid'] @@ -151,7 +152,7 @@ class Finder: if r['argsstring']: item['argsstring'] = r['argsstring'] item['definition'] = r['initializer'] - item['file'] = self.fileName(r['id_file']) + item['file'] = self.fileName(r['file_id']) item['line'] = r['line'] o.append(item) return o @@ -163,7 +164,7 @@ class Finder: item={} item['name'] = r['name'] item['definition'] = r['definition'] - item['file'] = self.fileName(r['id_file']) + item['file'] = self.fileName(r['file_id']) item['line'] = r['line'] o.append(item) return o @@ -175,16 +176,16 @@ class Finder: item={} item['name'] = r['name'] item['definition'] = r['definition'] - item['file'] = self.fileName(r['id_file']) + item['file'] = self.fileName(r['file_id']) item['line'] = r['line'] o.append(item) return o ############################################################################### def params(self): o=[] - c=self.cn.execute('SELECT id FROM memberdef WHERE'+self.match("name"),[self.name]) + c=self.cn.execute('SELECT rowid FROM memberdef WHERE'+self.match("name"),[self.name]) for r in c.fetchall(): - #a=("SELECT * FROM params where id=(SELECT id_param FROM memberdef_params where id_memberdef=?",[id_memberdef]) + #a=("SELECT * FROM param where id=(SELECT param_id FROM memberdef_param where memberdef_id=?",[memberdef_id]) item={} item['id'] = r['id'] o.append(item) @@ -202,20 +203,20 @@ class Finder: def includers(self): o=[] fid = self.fileId(self.name) - c=self.cn.execute('SELECT * FROM includes WHERE id_dst=?',[fid]) + c=self.cn.execute('SELECT * FROM includes WHERE dst_id=?',[fid]) for r in c.fetchall(): item={} - item['name'] = self.fileName(r['id_src']) + item['name'] = self.fileName(r['src_id']) o.append(item) return o ############################################################################### def includees(self): o=[] fid = self.fileId(self.name) - c=self.cn.execute('SELECT * FROM includes WHERE id_src=?',[fid]) + c=self.cn.execute('SELECT * FROM includes WHERE src_id=?',[fid]) for r in c.fetchall(): item={} - item['name'] = self.fileName(r['id_dst']) + item['name'] = self.fileName(r['dst_id']) o.append(item) return o ############################################################################### @@ -227,7 +228,7 @@ class Finder: item['name'] = r['name'] item['definition'] = r['definition'] item['argsstring'] = r['argsstring'] - item['file'] = self.fileName(r['id_file']) + item['file'] = self.fileName(r['file_id']) item['line'] = r['line'] #item['documentation'] = r['documentation'] o.append(item) @@ -235,19 +236,19 @@ class Finder: ############################################################################### def baseClasses(self): o=[] - c=self.cn.execute('SELECT base FROM basecompoundref WHERE'+self.match("derived"),[self.name]) + c=self.cn.execute('SELECT compounddef.name FROM compounddef JOIN compoundref ON compounddef.rowid=compoundref.base_rowid WHERE compoundref.derived_rowid IN (SELECT rowid FROM compounddef WHERE'+self.match("name")+')',[self.name]) for r in c.fetchall(): item={} - item['name'] = r['base'] + item['name'] = r['name'] o.append(item) return o ############################################################################### def subClasses(self): o=[] - c=self.cn.execute('SELECT derived FROM basecompoundref WHERE'+self.match("base"),[self.name]) + c=self.cn.execute('SELECT compounddef.name FROM compounddef JOIN compoundref ON compounddef.rowid=compoundref.derived_rowid WHERE compoundref.base_rowid IN (SELECT rowid FROM compounddef WHERE'+self.match("name")+')',[self.name]) for r in c.fetchall(): item={} - item['name'] = r['derived'] + item['name'] = r['name'] o.append(item) return o ############################################################################### @@ -268,21 +269,23 @@ def process(f,kind): } return request_processors[kind]() ############################################################################### + +# the -H option isn't documented. It's one of the more recent additions, but it's treating refids as if they would be a string. I'm just taking a stab at updating it for now, converting to use rowid, and making other edits necessary to get it to run. def processHref(cn,ref): j={} # is it in memberdef ? table="memberdef" - if ( cn.execute("SELECT count(*) from %s WHERE refid=?"%table,[ref] ).fetchone()[0] > 0 ): - for r in cn.execute("SELECT kind,id FROM %s WHERE refid='%s'" % (table,ref) ).fetchall(): - f=Finder(cn,r['id'],int) + if ( cn.execute("SELECT count(*) from %s WHERE rowid=?"%table,[ref] ).fetchone()[0] > 0 ): + for r in cn.execute("SELECT kind,rowid FROM %s WHERE rowid=?" % table,[ref]).fetchall(): + f=Finder(cn,r['rowid'],int) j=process(f,str(r['kind'])) # is it in compounddef ? table="compounddef" - if ( cn.execute("SELECT count(*) from %s WHERE refid=?"%table,[ref]).fetchone()[0] > 0 ): - for r in cn.execute("SELECT id FROM %s WHERE refid=?"%table,[ref] ).fetchall(): - f=Finder(cn,r['id'],int) + if ( cn.execute("SELECT count(*) from %s WHERE rowid=?"%table,[ref]).fetchone()[0] > 0 ): + for r in cn.execute("SELECT rowid FROM %s WHERE rowid=?"%table,[ref] ).fetchall(): + f=Finder(cn,r[0],int) j=process(f,RequestType.Struct) return j @@ -290,7 +293,7 @@ def processHref(cn,ref): def serveCgi(): import cgi - print 'Content-Type: application/json\n' + print('Content-Type: application/json\n') fieldStorage = cgi.FieldStorage() form = dict((key, fieldStorage.getvalue(key)) for key in fieldStorage.keys()) @@ -298,17 +301,17 @@ def serveCgi(): if 'href' in form: ref = form['href'] else: - print '{"result": null, "error": "no refid given"}' + print('{"result": null, "error": "no refid given"}') sys.exit(0) cn=openDb('doxygen_sqlite3.db') j = processHref(cn,ref) - print json.dumps({"result":j,"error":None}) + print(json.dumps({"result":j,"error":None})) ############################################################################### def usage(): - print >>sys.stderr,"""Usage: search.py [Options] + sys.stderr.write("""Usage: search.py [Options] Options: -h, --help -d Use database for queries. @@ -323,7 +326,7 @@ Options: -M Get all members of class . -S Get the sub classes of class . -R Consider the search to be a regex. -""" +""") ############################################################################### def serveCli(argv): try: @@ -362,6 +365,8 @@ def serveCli(argv): elif a in ('-f'): kind=MemberType.Function elif a in ('-F'): + # undocumented + # seems to fit with the lower case "search" patterns? kind=MemberType.File elif a in ('-m'): kind=MemberType.Define @@ -370,6 +375,7 @@ def serveCli(argv): elif a in ('-v'): kind=MemberType.Variable elif a in ('-H'): + # undocumented ref = o cn=openDb(dbname) @@ -378,7 +384,7 @@ def serveCli(argv): j=processHref(cn,ref) else: j=process(f,kind) - print json.dumps(j,indent=4) + print(json.dumps(j,indent=4)) def main(argv): diff --git a/src/sqlite3gen.cpp b/src/sqlite3gen.cpp index 6cd9581..8e236c1 100644 --- a/src/sqlite3gen.cpp +++ b/src/sqlite3gen.cpp @@ -23,11 +23,15 @@ #include "qtbc.h" #include "sqlite3gen.h" #include "doxygen.h" +#include "xmlgen.h" +#include "xmldocvisitor.h" #include "config.h" #include "util.h" +#include "outputlist.h" #include "docparser.h" #include "language.h" +#include "version.h" #include "dot.h" #include "arguments.h" #include "classlist.h" @@ -35,65 +39,129 @@ #include "namespacedef.h" #include "filename.h" #include "groupdef.h" +#include "membername.h" +#include "memberdef.h" #include "pagedef.h" #include "dirdef.h" +#include "section.h" +#include #include #include #include -//#define DBG_CTX(x) printf x -#define DBG_CTX(x) do { } while(0) +// enable to show general debug messages +// #define SQLITE3_DEBUG -const char * schema_queries[][2] = { +// enable to print all executed SQL statements. +// I recommend using the smallest possible input list. +// #define SQLITE3_DEBUG_SQL + +# ifdef SQLITE3_DEBUG +# define DBG_CTX(x) printf x +# else // SQLITE3_DEBUG +# define DBG_CTX(x) do { } while(0) +# endif + +# ifdef SQLITE3_DEBUG_SQL +// used by sqlite3_trace in generateSqlite3() +static void sqlLog(void *dbName, const char *sql){ + msg("SQL: '%s'\n", sql); +} +# endif + +const char * table_schema[][2] = { + /* TABLES */ + { "meta", + "CREATE TABLE IF NOT EXISTS meta (\n" + "\t-- Information about this db and how it was generated.\n" + "\t-- Doxygen info\n" + "\tdoxygen_version TEXT PRIMARY KEY NOT NULL,\n" + /* + Doxygen's version is likely to rollover much faster than the schema, and + at least until it becomes a core output format, we might want to make + fairly large schema changes even on minor iterations for Doxygen itself. + If these tools just track a predefined semver schema version that can + iterate independently, it *might* not be as hard to keep them in sync? + */ + "\tschema_version TEXT NOT NULL, -- Schema-specific semver\n" + "\t-- run info\n" + "\tgenerated_at TEXT NOT NULL,\n" + "\tgenerated_on TEXT NOT NULL,\n" + "\t-- project info\n" + "\tproject_name TEXT NOT NULL,\n" + "\tproject_number TEXT,\n" + "\tproject_brief TEXT\n" + ");" + }, { "includes", "CREATE TABLE IF NOT EXISTS includes (\n" "\t-- #include relations.\n" "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "\tlocal INTEGER NOT NULL,\n" - "\tid_src INTEGER NOT NULL, -- File id of the includer.\n" - "\tid_dst INTEGER NOT NULL -- File id of the includee.\n" - ");\n" - "CREATE UNIQUE INDEX idx_includes ON includes\n" - "\t(local, id_src, id_dst);" + "\tsrc_id INTEGER NOT NULL REFERENCES path, -- File id of the includer.\n" + "\tdst_id INTEGER NOT NULL REFERENCES path, -- File id of the includee.\n" + /* + In theory we could include name here to be informationally equivalent + with the XML, but I don't see an obvious use for it. + */ + "\tUNIQUE(local, src_id, dst_id) ON CONFLICT IGNORE\n" + ");" }, - { "innerclass", - "CREATE TABLE IF NOT EXISTS innerclass (\n" + { "contains", + "CREATE TABLE IF NOT EXISTS contains (\n" + "\t-- inner/outer relations (file, namespace, dir, class, group, page)\n" "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\trefid INTEGER NOT NULL,\n" - "\tprot INTEGER NOT NULL,\n" - "\tname TEXT NOT NULL\n" - ");" + "\tinner_rowid INTEGER NOT NULL REFERENCES compounddef,\n" + "\touter_rowid INTEGER NOT NULL REFERENCES compounddef\n" + ");" }, - { "files", - "CREATE TABLE IF NOT EXISTS files (\n" - "\t-- Names of source files and includes.\n" - "\tname TEXT PRIMARY KEY NOT NULL\n" - ");" + /* TODO: Path can also share rowids with refid/compounddef/def. (It could + * even collapse into that table...) + * + * I took a first swing at this by changing insertPath() to: + * - accept a FileDef + * - make its own call to insertRefid + * - return a refid struct. + * + * I rolled this back when I had trouble getting a FileDef for all types + * (PageDef in particular). + * + * Note: all colums referencing path would need an update. + */ + { "path", + "CREATE TABLE IF NOT EXISTS path (\n" + "\t-- Paths of source files and includes.\n" + "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + "\ttype INTEGER NOT NULL, -- 1:file 2:dir\n" + "\tlocal INTEGER NOT NULL,\n" + "\tfound INTEGER NOT NULL,\n" + "\tname TEXT NOT NULL\n" + ");" }, - { "refids", - "CREATE TABLE IF NOT EXISTS refids (\n" - "\trefid TEXT PRIMARY KEY NOT NULL\n" - ");" + { "refid", + "CREATE TABLE IF NOT EXISTS refid (\n" + "\t-- Distinct refid for all documented entities.\n" + "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + "\trefid TEXT NOT NULL UNIQUE\n" + ");" }, { "xrefs", "CREATE TABLE IF NOT EXISTS xrefs (\n" - "\t-- Cross reference relation.\n" + "\t-- Cross-reference relation\n" + "\t-- (combines xml and nodes).\n" "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\trefid_src INTEGER NOT NULL, -- referrer id.\n" - "\trefid_dst INTEGER NOT NULL, -- referee id.\n" - "\tid_file INTEGER NOT NULL, -- file where the reference is happening.\n" - "\tline INTEGER NOT NULL, -- line where the reference is happening.\n" - "\tcolumn INTEGER NOT NULL -- column where the reference is happening.\n" - ");\n" - "CREATE UNIQUE INDEX idx_xrefs ON xrefs\n" - "\t(refid_src, refid_dst, id_file, line, column);" + "\tsrc_rowid INTEGER NOT NULL REFERENCES refid, -- referrer id.\n" + "\tdst_rowid INTEGER NOT NULL REFERENCES refid, -- referee id.\n" + "\tcontext TEXT NOT NULL, -- inline, argument, initializer\n" + "\t-- Just need to know they link; ignore duplicates.\n" + "\tUNIQUE(src_rowid, dst_rowid, context) ON CONFLICT IGNORE\n" + ");\n" }, { "memberdef", "CREATE TABLE IF NOT EXISTS memberdef (\n" "\t-- All processed identifiers.\n" - "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\trefid INTEGER NOT NULL, -- see the refids table\n" + "\trowid INTEGER PRIMARY KEY NOT NULL,\n" "\tname TEXT NOT NULL,\n" "\tdefinition TEXT,\n" "\ttype TEXT,\n" @@ -107,7 +175,7 @@ const char * schema_queries[][2] = { "\tstatic INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\tconst INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\texplicit INTEGER DEFAULT 0, -- 0:no 1:yes\n" - "\tinline INTEGER DEFAULT 0, -- 0:no 1:yes\n" + "\tinline INTEGER DEFAULT 0, -- 0:no 1:yes 2:both (set after encountering inline and not-inline)\n" "\tfinal INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\tsealed INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\tnew INTEGER DEFAULT 0, -- 0:no 1:yes\n" @@ -138,55 +206,71 @@ const char * schema_queries[][2] = { "\taddable INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\tremovable INTEGER DEFAULT 0, -- 0:no 1:yes\n" "\traisable INTEGER DEFAULT 0, -- 0:no 1:yes\n" - /// @todo make a `kind' table - "\tkind INTEGER DEFAULT 0, -- 0:define 1:function 2:variable 3:typedef 4:enum 5:enumvalue 6:signal 7:slot 8:friend 9:DCOP 10:property 11:event\n" + "\tkind TEXT NOT NULL, -- 'macro definition' 'function' 'variable' 'typedef' 'enumeration' 'enumvalue' 'signal' 'slot' 'friend' 'dcop' 'property' 'event' 'interface' 'service'\n" "\tbodystart INTEGER DEFAULT 0, -- starting line of definition\n" "\tbodyend INTEGER DEFAULT 0, -- ending line of definition\n" - "\tid_bodyfile INTEGER DEFAULT 0, -- file of definition\n" - "\tid_file INTEGER NOT NULL, -- file where this identifier is located\n" + "\tbodyfile_id INTEGER REFERENCES path, -- file of definition\n" + "\tfile_id INTEGER NOT NULL REFERENCES path, -- file where this identifier is located\n" "\tline INTEGER NOT NULL, -- line where this identifier is located\n" "\tcolumn INTEGER NOT NULL, -- column where this identifier is located\n" - /// @todo make a `detaileddescription' table "\tdetaileddescription TEXT,\n" "\tbriefdescription TEXT,\n" - "\tinbodydescription TEXT\n" - ");" + "\tinbodydescription TEXT,\n" + "\tFOREIGN KEY (rowid) REFERENCES refid (rowid)\n" + ");" + }, + { "member", + "CREATE TABLE IF NOT EXISTS member (\n" + "\t-- Memberdef <-> containing compound relation.\n" + "\t-- Similar to XML listofallmembers.\n" + "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + "\tscope_rowid INTEGER NOT NULL REFERENCES compounddef,\n" + "\tmemberdef_rowid INTEGER NOT NULL REFERENCES memberdef,\n" + "\tprot INTEGER NOT NULL,\n" + "\tvirt INTEGER NOT NULL,\n" + "\tUNIQUE(scope_rowid, memberdef_rowid)\n" + ");" + }, + { "reimplements", + "CREATE TABLE IF NOT EXISTS reimplements (\n" + "\t-- Inherited member reimplmentation relations.\n" + "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + "\tmemberdef_rowid INTEGER NOT NULL REFERENCES memberdef, -- reimplementing memberdef id.\n" + "\treimplemented_rowid INTEGER NOT NULL REFERENCES memberdef, -- reimplemented memberdef id.\n" + "\tUNIQUE(memberdef_rowid, reimplemented_rowid) ON CONFLICT IGNORE\n" + ");\n" }, { "compounddef", "CREATE TABLE IF NOT EXISTS compounddef (\n" - "\t-- class/struct definitions.\n" - "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\tname TEXT NOT NULL,\n" - "\tkind TEXT NOT NULL,\n" - "\trefid INTEGER NOT NULL,\n" - "\tprot INTEGER NOT NULL,\n" - "\tid_file INTEGER NOT NULL,\n" - "\tline INTEGER NOT NULL,\n" - "\tcolumn INTEGER NOT NULL\n" - ");" - }, - { "basecompoundref", - "CREATE TABLE IF NOT EXISTS basecompoundref (\n" - "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\tbase TEXT NOT NULL,\n" - "\tderived TEXT NOT NULL,\n" - "\trefid INTEGER NOT NULL,\n" - "\tprot INTEGER NOT NULL,\n" - "\tvirt INTEGER NOT NULL\n" - ");" + "\t-- Class/struct definitions.\n" + "\trowid INTEGER PRIMARY KEY NOT NULL,\n" + "\tname TEXT NOT NULL,\n" + "\ttitle TEXT,\n" + // probably won't be empty '' or unknown, but the source *could* return them... + "\tkind TEXT NOT NULL, -- 'category' 'class' 'constants' 'dir' 'enum' 'example' 'exception' 'file' 'group' 'interface' 'library' 'module' 'namespace' 'package' 'page' 'protocol' 'service' 'singleton' 'struct' 'type' 'union' 'unknown' ''\n" + "\tprot INTEGER,\n" + "\tfile_id INTEGER NOT NULL REFERENCES path,\n" + "\tline INTEGER NOT NULL,\n" + "\tcolumn INTEGER NOT NULL,\n" + "\theader_id INTEGER REFERENCES path,\n" + "\tdetaileddescription TEXT,\n" + "\tbriefdescription TEXT,\n" + "\tFOREIGN KEY (rowid) REFERENCES refid (rowid)\n" + ");" }, - { "derivedcompoundref", - "CREATE TABLE IF NOT EXISTS derivedcompoundref (\n" - "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\tbase TEXT NOT NULL,\n" - "\tderived TEXT NOT NULL,\n" - "\trefid INTEGER NOT NULL,\n" - "\tprot INTEGER NOT NULL,\n" - "\tvirt INTEGER NOT NULL\n" - ");" + { "compoundref", + "CREATE TABLE IF NOT EXISTS compoundref (\n" + "\t-- Inheritance relation.\n" + "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + "\tbase_rowid INTEGER NOT NULL REFERENCES compounddef,\n" + "\tderived_rowid INTEGER NOT NULL REFERENCES compounddef,\n" + "\tprot INTEGER NOT NULL,\n" + "\tvirt INTEGER NOT NULL,\n" + "\tUNIQUE(base_rowid, derived_rowid)\n" + ");" }, - { "params", - "CREATE TABLE IF NOT EXISTS params (\n" + { "param", + "CREATE TABLE IF NOT EXISTS param (\n" "\t-- All processed parameters.\n" "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "\tattributes TEXT,\n" @@ -196,24 +280,221 @@ const char * schema_queries[][2] = { "\tarray TEXT,\n" "\tdefval TEXT,\n" "\tbriefdescription TEXT\n" - ");" - "CREATE UNIQUE INDEX idx_params ON params\n" + ");" + "CREATE UNIQUE INDEX idx_param ON param\n" "\t(type, defname);" }, - { "memberdef_params", - "CREATE TABLE IF NOT EXISTS memberdef_params (\n" + { "memberdef_param", + "CREATE TABLE IF NOT EXISTS memberdef_param (\n" "\t-- Junction table for memberdef parameters.\n" "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\tid_memberdef INTEGER NOT NULL,\n" - "\tid_param INTEGER NOT NULL\n" - ");" + "\tmemberdef_id INTEGER NOT NULL REFERENCES memberdef,\n" + "\tparam_id INTEGER NOT NULL REFERENCES param\n" + ");" }, - { "innernamespaces", - "CREATE TABLE IF NOT EXISTS innernamespaces (\n" - "\trowid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" - "\trefid INTEGER NOT NULL,\n" - "\tname TEXT NOT NULL\n" - ");" +}; + const char * view_schema[][2] = { + /* VIEWS * + We'll set these up AFTER we build the database, so that they can be indexed, + but so we don't have to pay a performance penalty for inserts as we build. + */ + { + /* + Makes all reference/relation tables easier to use. For example: + 1. query xrefs and join this view on either xrefs.dst_rowid=def.rowid or + xrefs.src_rowid=def.rowid + 2. get everything you need to output a list of references to/from an entity + + Also supports simple name search/lookup for both compound and member types. + + NOTES: + - summary for compounds generalizes title and briefdescription because + there's no single field that works as a quick introduction for both + pages and classes + - May be value in eventually extending this to fulltext or levenshtein + distance-driven lookup/search, but I'm avoiding these for now as it + takes some effort to enable them. + */ + "def", + "CREATE VIEW IF NOT EXISTS def (\n" + "\t-- Combined summary of all -def types for easier joins.\n" + "\trowid,\n" + "\trefid,\n" + "\tkind,\n" + "\tname,\n" + "\tsummary" + ")\n" + "as SELECT \n" + "\trefid.rowid,\n" + "\trefid.refid,\n" + "\tmemberdef.kind,\n" + "\tmemberdef.name,\n" + "\tmemberdef.briefdescription \n" + "FROM refid \n" + "JOIN memberdef ON refid.rowid=memberdef.rowid \n" + "UNION ALL \n" + "SELECT \n" + "\trefid.rowid,\n" + "\trefid.refid,\n" + "\tcompounddef.kind,\n" + "\tcompounddef.name,\n" + "\tCASE \n" + "\t\tWHEN briefdescription IS NOT NULL \n" + "\t\tTHEN briefdescription \n" + "\t\tELSE title \n" + "\tEND summary\n" + "FROM refid \n" + "JOIN compounddef ON refid.rowid=compounddef.rowid;" + }, + { + "local_file", + "CREATE VIEW IF NOT EXISTS local_file (\n" + "\t-- File paths found within the project.\n" + "\trowid,\n" + "\tfound,\n" + "\tname\n" + ")\n" + "as SELECT \n" + "\tpath.rowid,\n" + "\tpath.found,\n" + "\tpath.name\n" + "FROM path WHERE path.type=1 AND path.local=1 AND path.found=1;\n" + }, + { + "external_file", + "CREATE VIEW IF NOT EXISTS external_file (\n" + "\t-- File paths outside the project (found or not).\n" + "\trowid,\n" + "\tfound,\n" + "\tname\n" + ")\n" + "as SELECT \n" + "\tpath.rowid,\n" + "\tpath.name\n" + "FROM path WHERE path.type=1 AND path.local=0;\n" + }, + { + "inline_xrefs", + "CREATE VIEW IF NOT EXISTS inline_xrefs (\n" + "\t-- Crossrefs from inline member source.\n" + "\trowid,\n" + "\tsrc_rowid,\n" + "\tdst_rowid\n" + ")\n" + "as SELECT \n" + "\txrefs.rowid,\n" + "\txrefs.src_rowid,\n" + "\txrefs.dst_rowid\n" + "FROM xrefs WHERE xrefs.context='inline';\n" + }, + { + "argument_xrefs", + "CREATE VIEW IF NOT EXISTS argument_xrefs (\n" + "\t-- Crossrefs from member def/decl arguments\n" + "\trowid,\n" + "\tsrc_rowid,\n" + "\tdst_rowid\n" + ")\n" + "as SELECT \n" + "\txrefs.rowid,\n" + "\txrefs.src_rowid,\n" + "\txrefs.dst_rowid\n" + "FROM xrefs WHERE xrefs.context='argument';\n" + }, + { + "initializer_xrefs", + "CREATE VIEW IF NOT EXISTS initializer_xrefs (\n" + "\t-- Crossrefs from member initializers\n" + "\trowid,\n" + "\tsrc_rowid,\n" + "\tdst_rowid\n" + ")\n" + "as SELECT \n" + "\txrefs.rowid,\n" + "\txrefs.src_rowid,\n" + "\txrefs.dst_rowid\n" + "FROM xrefs WHERE xrefs.context='initializer';\n" + }, + { + "inner_outer", + "CREATE VIEW IF NOT EXISTS inner_outer\n" + "\t-- Joins 'contains' relations to simplify inner/outer 'rel' queries.\n" + "as SELECT \n" + "\tinner.*,\n" + "\touter.*\n" + "FROM def as inner\n" + "\tJOIN contains ON inner.rowid=contains.inner_rowid\n" + "\tJOIN def AS outer ON outer.rowid=contains.outer_rowid;\n" + }, + { + "rel", + "CREATE VIEW IF NOT EXISTS rel (\n" + "\t-- Boolean indicator of relations available for a given entity.\n" + "\t-- Join to (compound-|member-)def to find fetch-worthy relations.\n" + "\trowid,\n" + "\treimplemented,\n" + "\treimplements,\n" + "\tinnercompounds,\n" + "\toutercompounds,\n" + "\tinnerpages,\n" + "\touterpages,\n" + "\tinnerdirs,\n" + "\touterdirs,\n" + "\tinnerfiles,\n" + "\touterfiles,\n" + "\tinnerclasses,\n" + "\touterclasses,\n" + "\tinnernamespaces,\n" + "\touternamespaces,\n" + "\tinnergroups,\n" + "\toutergroups,\n" + "\tmembers,\n" + "\tcompounds,\n" + "\tsubclasses,\n" + "\tsuperclasses,\n" + "\tlinks_in,\n" + "\tlinks_out,\n" + "\targument_links_in,\n" + "\targument_links_out,\n" + "\tinitializer_links_in,\n" + "\tinitializer_links_out\n" + ")\n" + "as SELECT \n" + "\tdef.rowid,\n" + "\tEXISTS (SELECT rowid FROM reimplements WHERE reimplemented_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM reimplements WHERE memberdef_rowid=def.rowid),\n" + "\t-- rowid/kind for inner, [rowid:1/kind:1] for outer\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind='page'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1]='page'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind='dir'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1]='dir'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind='file'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1]='file'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind in (\n" + "'category','class','enum','exception','interface','module','protocol',\n" + "'service','singleton','struct','type','union'\n" + ")),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1] in (\n" + "'category','class','enum','exception','interface','module','protocol',\n" + "'service','singleton','struct','type','union'\n" + ")),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind='namespace'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1]='namespace'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE [rowid:1]=def.rowid AND kind='group'),\n" + "\tEXISTS (SELECT * FROM inner_outer WHERE rowid=def.rowid AND [kind:1]='group'),\n" + "\tEXISTS (SELECT rowid FROM member WHERE scope_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM member WHERE memberdef_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM compoundref WHERE base_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM compoundref WHERE derived_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM inline_xrefs WHERE dst_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM inline_xrefs WHERE src_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM argument_xrefs WHERE dst_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM argument_xrefs WHERE src_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM initializer_xrefs WHERE dst_rowid=def.rowid),\n" + "\tEXISTS (SELECT rowid FROM initializer_xrefs WHERE src_rowid=def.rowid)\n" + "FROM def ORDER BY def.rowid;" } }; @@ -224,193 +505,298 @@ struct SqlStmt { sqlite3 *db; }; ////////////////////////////////////////////////////// -SqlStmt incl_insert = { "INSERT INTO includes " - "( local, id_src, id_dst ) " - "VALUES " - "(:local,:id_src,:id_dst )" - ,NULL +/* If you add a new statement below, make sure to add it to + prepareStatements(). If sqlite3 is segfaulting (especially in + sqlite3_clear_bindings()), using an un-prepared statement may + be the cause. */ +SqlStmt meta_insert = { + "INSERT INTO meta " + "( doxygen_version, schema_version, generated_at, generated_on, project_name, project_number, project_brief )" + "VALUES " + "(:doxygen_version,:schema_version,:generated_at,:generated_on,:project_name,:project_number,:project_brief )" + ,NULL +}; +////////////////////////////////////////////////////// +SqlStmt incl_insert = { + "INSERT INTO includes " + "( local, src_id, dst_id ) " + "VALUES " + "(:local,:src_id,:dst_id )" + ,NULL }; -SqlStmt incl_select = { "SELECT COUNT(*) FROM includes WHERE " - "local=:local AND id_src=:id_src AND id_dst=:id_dst" - ,NULL +SqlStmt incl_select = { + "SELECT COUNT(*) FROM includes WHERE " + "local=:local AND src_id=:src_id AND dst_id=:dst_id" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt innerclass_insert={"INSERT INTO innerclass " - "( refid, prot, name )" - "VALUES " - "(:refid,:prot,:name )" - ,NULL +SqlStmt contains_insert={ + "INSERT INTO contains " + "( inner_rowid, outer_rowid )" + "VALUES " + "(:inner_rowid,:outer_rowid )" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt files_select = {"SELECT rowid FROM files WHERE name=:name" +SqlStmt path_select = { + "SELECT rowid FROM path WHERE name=:name" ,NULL }; -SqlStmt files_insert = {"INSERT INTO files " - "( name )" - "VALUES " - "(:name )" - ,NULL +SqlStmt path_insert = { + "INSERT INTO path " + "( type, local, found, name )" + "VALUES " + "(:type,:local,:found,:name )" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt refids_select = {"SELECT rowid FROM refids WHERE " - "refid=:refid" - ,NULL +SqlStmt refid_select = { + "SELECT rowid FROM refid WHERE refid=:refid" + ,NULL }; -SqlStmt refids_insert = {"INSERT INTO refids " - "( refid )" - "VALUES " +SqlStmt refid_insert = { + "INSERT INTO refid " + "( refid )" + "VALUES " "(:refid )" - ,NULL + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt xrefs_insert= {"INSERT INTO xrefs " - "( refid_src, refid_dst, id_file, line, column )" - "VALUES " - "(:refid_src,:refid_dst,:id_file,:line,:column )" - ,NULL +SqlStmt xrefs_insert= { + "INSERT INTO xrefs " + "( src_rowid, dst_rowid, context )" + "VALUES " + "(:src_rowid,:dst_rowid,:context )" + ,NULL +};////////////////////////////////////////////////////// +SqlStmt reimplements_insert= { + "INSERT INTO reimplements " + "( memberdef_rowid, reimplemented_rowid )" + "VALUES " + "(:memberdef_rowid,:reimplemented_rowid )" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt memberdef_insert={"INSERT INTO memberdef " - "(" - "refid," - "name," - "definition," - "type," - "argsstring," - "scope," - "initializer," - "bitfield," - "read," - "write," - "prot," - "static," - "const," - "explicit," - "inline," - "final," - "sealed," - "new," - "optional," - "required," - "volatile," - "virt," - "mutable," - "initonly," - "attribute," - "property," - "readonly," - "bound," - "constrained," - "transient," - "maybevoid," - "maybedefault," - "maybeambiguous," - "readable," - "writable," - "gettable," - "protectedsettable," - "protectedgettable," - "settable," - "privatesettable," - "privategettable," - "accessor," - "addable," - "removable," - "raisable," - "kind," - "bodystart," - "bodyend," - "id_bodyfile," - "id_file," - "line," - "column," - "detaileddescription," - "briefdescription," - "inbodydescription" - ")" - "VALUES " - "(" - ":refid," - ":name," - ":definition," - ":type," - ":argsstring," - ":scope," - ":initializer," - ":bitfield," - ":read," - ":write," - ":prot," - ":static," - ":const," - ":explicit," - ":inline," - ":final," - ":sealed," - ":new," - ":optional," - ":required," - ":volatile," - ":virt," - ":mutable," - ":initonly," - ":attribute," - ":property," - ":readonly," - ":bound," - ":constrained," - ":transient," - ":maybevoid," - ":maybedefault," - ":maybeambiguous," - ":readable," - ":writable," - ":gettable," - ":protectedsettable," - ":protectedgettable," - ":settable," - ":privatesettable," - ":privategettable," - ":accessor," - ":addable," - ":removable," - ":raisable," - ":kind," - ":bodystart," - ":bodyend," - ":id_bodyfile," - ":id_file," - ":line," - ":column," - ":detaileddescription," - ":briefdescription," - ":inbodydescription" - ")" - ,NULL +SqlStmt memberdef_exists={ + "SELECT EXISTS (SELECT * FROM memberdef WHERE rowid = :rowid)" + ,NULL +}; + +SqlStmt memberdef_incomplete={ + "SELECT EXISTS (" + "SELECT * FROM memberdef WHERE " + "rowid = :rowid AND inline != 2 AND inline != :new_inline" + ")" + ,NULL +}; + +SqlStmt memberdef_insert={ + "INSERT INTO memberdef " + "(" + "rowid," + "name," + "definition," + "type," + "argsstring," + "scope," + "initializer," + "bitfield," + "read," + "write," + "prot," + "static," + "const," + "explicit," + "inline," + "final," + "sealed," + "new," + "optional," + "required," + "volatile," + "virt," + "mutable," + "initonly," + "attribute," + "property," + "readonly," + "bound," + "constrained," + "transient," + "maybevoid," + "maybedefault," + "maybeambiguous," + "readable," + "writable," + "gettable," + "protectedsettable," + "protectedgettable," + "settable," + "privatesettable," + "privategettable," + "accessor," + "addable," + "removable," + "raisable," + "kind," + "bodystart," + "bodyend," + "bodyfile_id," + "file_id," + "line," + "column," + "detaileddescription," + "briefdescription," + "inbodydescription" + ")" + "VALUES " + "(" + ":rowid," + ":name," + ":definition," + ":type," + ":argsstring," + ":scope," + ":initializer," + ":bitfield," + ":read," + ":write," + ":prot," + ":static," + ":const," + ":explicit," + ":inline," + ":final," + ":sealed," + ":new," + ":optional," + ":required," + ":volatile," + ":virt," + ":mutable," + ":initonly," + ":attribute," + ":property," + ":readonly," + ":bound," + ":constrained," + ":transient," + ":maybevoid," + ":maybedefault," + ":maybeambiguous," + ":readable," + ":writable," + ":gettable," + ":protectedsettable," + ":protectedgettable," + ":settable," + ":privatesettable," + ":privategettable," + ":accessor," + ":addable," + ":removable," + ":raisable," + ":kind," + ":bodystart," + ":bodyend," + ":bodyfile_id," + ":file_id," + ":line," + ":column," + ":detaileddescription," + ":briefdescription," + ":inbodydescription" + ")" + ,NULL +}; +/* +We have a slightly different need than the XML here. The XML can have two +memberdef nodes with the same refid to document the declaration and the +definition. This doesn't play very nice with a referential model. It isn't a +big issue if only one is documented, but in case both are, we'll fall back on +this kludge to combine them in a single row... +*/ +SqlStmt memberdef_update_decl={ + "UPDATE memberdef SET " + "inline = :inline," + "file_id = :file_id," + "line = :line," + "column = :column," + "detaileddescription = 'Declaration: ' || :detaileddescription || 'Definition: ' || detaileddescription," + "briefdescription = 'Declaration: ' || :briefdescription || 'Definition: ' || briefdescription," + "inbodydescription = 'Declaration: ' || :inbodydescription || 'Definition: ' || inbodydescription " + "WHERE rowid = :rowid" + ,NULL +}; +SqlStmt memberdef_update_def={ + "UPDATE memberdef SET " + "inline = :inline," + "bodystart = :bodystart," + "bodyend = :bodyend," + "bodyfile_id = :bodyfile_id," + "detaileddescription = 'Declaration: ' || detaileddescription || 'Definition: ' || :detaileddescription," + "briefdescription = 'Declaration: ' || briefdescription || 'Definition: ' || :briefdescription," + "inbodydescription = 'Declaration: ' || inbodydescription || 'Definition: ' || :inbodydescription " + "WHERE rowid = :rowid" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt compounddef_insert={"INSERT INTO compounddef " - "( name, kind, prot, refid, id_file, line, column ) " - "VALUES " - "(:name,:kind,:prot,:refid,:id_file,:line,:column )" - ,NULL +SqlStmt member_insert={ + "INSERT INTO member " + "( scope_rowid, memberdef_rowid, prot, virt ) " + "VALUES " + "(:scope_rowid,:memberdef_rowid,:prot,:virt )" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt basecompoundref_insert={"INSERT INTO basecompoundref " - "( base, derived, refid, prot, virt ) " - "VALUES " - "(:base,:derived,:refid,:prot,:virt )" - ,NULL +SqlStmt compounddef_insert={ + "INSERT INTO compounddef " + "(" + "rowid," + "name," + "title," + "kind," + "prot," + "file_id," + "line," + "column," + "header_id," + "briefdescription," + "detaileddescription" + ")" + "VALUES " + "(" + ":rowid," + ":name," + ":title," + ":kind," + ":prot," + ":file_id," + ":line," + ":column," + ":header_id," + ":briefdescription," + ":detaileddescription" + ")" + ,NULL +}; +SqlStmt compounddef_exists={ + "SELECT EXISTS (" + "SELECT * FROM compounddef WHERE rowid = :rowid" + ")" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt derivedcompoundref_insert={"INSERT INTO derivedcompoundref " - "( refid, prot, virt, base, derived ) " - "VALUES " - "(:refid,:prot,:virt,:base,:derived )" - ,NULL +SqlStmt compoundref_insert={ + "INSERT INTO compoundref " + "( base_rowid, derived_rowid, prot, virt ) " + "VALUES " + "(:base_rowid,:derived_rowid,:prot,:virt )" + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt params_select = { "SELECT rowid FROM params WHERE " +SqlStmt param_select = { + "SELECT rowid FROM param WHERE " "(attributes IS NULL OR attributes=:attributes) AND " "(type IS NULL OR type=:type) AND " "(declname IS NULL OR declname=:declname) AND " @@ -418,27 +804,22 @@ SqlStmt params_select = { "SELECT rowid FROM params WHERE " "(array IS NULL OR array=:array) AND " "(defval IS NULL OR defval=:defval) AND " "(briefdescription IS NULL OR briefdescription=:briefdescription)" - ,NULL + ,NULL }; -SqlStmt params_insert = { "INSERT INTO params " - "( attributes, type, declname, defname, array, defval, briefdescription ) " - "VALUES " +SqlStmt param_insert = { + "INSERT INTO param " + "( attributes, type, declname, defname, array, defval, briefdescription ) " + "VALUES " "(:attributes,:type,:declname,:defname,:array,:defval,:briefdescription)" - ,NULL -}; -////////////////////////////////////////////////////// -SqlStmt memberdef_params_insert={ "INSERT INTO memberdef_params " - "( id_memberdef, id_param)" - "VALUES " - "(:id_memberdef,:id_param)" - ,NULL + ,NULL }; ////////////////////////////////////////////////////// -SqlStmt innernamespace_insert={"INSERT INTO innernamespaces " - "( refid, name)" - "VALUES " - "(:refid,:name)", - NULL +SqlStmt memberdef_param_insert={ + "INSERT INTO memberdef_param " + "( memberdef_id, param_id)" + "VALUES " + "(:memberdef_id,:param_id)" + ,NULL }; @@ -491,7 +872,7 @@ static bool bindIntParameter(SqlStmt &s,const char *name,int value) { int idx = sqlite3_bind_parameter_index(s.stmt, name); if (idx==0) { - msg("sqlite3_bind_parameter_index(%s)[%s] failed: %s\n", name, s.query, sqlite3_errmsg(s.db)); + msg("sqlite3_bind_parameter_index(%s)[%s] failed to find column: %s\n", name, s.query, sqlite3_errmsg(s.db)); return false; } int rv = sqlite3_bind_int(s.stmt, idx, value); @@ -508,7 +889,7 @@ static int step(SqlStmt &s,bool getRowId=FALSE, bool select=FALSE) int rc = sqlite3_step(s.stmt); if (rc!=SQLITE_DONE && rc!=SQLITE_ROW) { - msg("sqlite3_step: %s\n", sqlite3_errmsg(s.db)); + DBG_CTX(("sqlite3_step: %s (rc: %d)\n", sqlite3_errmsg(s.db), rc)); sqlite3_reset(s.stmt); sqlite3_clear_bindings(s.stmt); return -1; @@ -520,72 +901,118 @@ static int step(SqlStmt &s,bool getRowId=FALSE, bool select=FALSE) return rowid; } -static int insertFile(const char* name) +static int insertPath(QCString name, bool local=TRUE, bool found=TRUE, int type=1) { int rowid=-1; if (name==0) return rowid; - bindTextParameter(files_select,":name",name); - rowid=step(files_select,TRUE,TRUE); + name = stripFromPath(name); + + bindTextParameter(path_select,":name",name.data(),FALSE); + rowid=step(path_select,TRUE,TRUE); if (rowid==0) { - bindTextParameter(files_insert,":name",name); - rowid=step(files_insert,TRUE); + bindTextParameter(path_insert,":name",name.data(),FALSE); + bindIntParameter(path_insert,":type",type); + bindIntParameter(path_insert,":local",local?1:0); + bindIntParameter(path_insert,":found",found?1:0); + rowid=step(path_insert,TRUE); } return rowid; } -static int insertRefid(const char *refid) +static void recordMetadata() { - int rowid=-1; - if (refid==0) return rowid; + bindTextParameter(meta_insert,":doxygen_version",versionString); + bindTextParameter(meta_insert,":schema_version","0.2.0"); //TODO: this should be a constant somewhere; not sure where + bindTextParameter(meta_insert,":generated_at",dateToString(TRUE), FALSE); + bindTextParameter(meta_insert,":generated_on",dateToString(FALSE), FALSE); + bindTextParameter(meta_insert,":project_name",Config_getString(PROJECT_NAME)); + bindTextParameter(meta_insert,":project_number",Config_getString(PROJECT_NUMBER)); + bindTextParameter(meta_insert,":project_brief",Config_getString(PROJECT_BRIEF)); + step(meta_insert); +} - bindTextParameter(refids_select,":refid",refid); - rowid=step(refids_select,TRUE,TRUE); - if (rowid==0) +struct Refid { + int rowid; + const char *refid; + bool created; +}; + +struct Refid insertRefid(const char *refid) +{ + struct Refid ret; + ret.rowid=-1; + ret.refid=refid; + ret.created = FALSE; + if (refid==0) return ret; + + bindTextParameter(refid_select,":refid",refid); + ret.rowid=step(refid_select,TRUE,TRUE); + if (ret.rowid==0) { - bindTextParameter(refids_insert,":refid",refid); - rowid=step(refids_insert,TRUE); + bindTextParameter(refid_insert,":refid",refid); + ret.rowid=step(refid_insert,TRUE); + ret.created = TRUE; } - return rowid; + + return ret; +} + +static bool memberdefExists(struct Refid refid) +{ + bindIntParameter(memberdef_exists,":rowid",refid.rowid); + int test = step(memberdef_exists,TRUE,TRUE); + return test ? true : false; } +static bool memberdefIncomplete(struct Refid refid, const MemberDef* md) +{ + bindIntParameter(memberdef_incomplete,":rowid",refid.rowid); + bindIntParameter(memberdef_incomplete,":new_inline",md->isInline()); + int test = step(memberdef_incomplete,TRUE,TRUE); + return test ? true : false; +} -static bool insertMemberReference(int refid_src, int refid_dst, - int id_file, int line, int column) +static bool compounddefExists(struct Refid refid) { - if (id_file==-1||refid_src==-1||refid_dst==-1) + bindIntParameter(compounddef_exists,":rowid",refid.rowid); + int test = step(compounddef_exists,TRUE,TRUE); + return test ? true : false; +} + +static bool insertMemberReference(struct Refid src_refid, struct Refid dst_refid, const char *context) +{ + if (src_refid.rowid==-1||dst_refid.rowid==-1) return false; if ( - !bindIntParameter(xrefs_insert,":refid_src",refid_src) || - !bindIntParameter(xrefs_insert,":refid_dst",refid_dst) || - !bindIntParameter(xrefs_insert,":id_file",id_file) || - !bindIntParameter(xrefs_insert,":line",line) || - !bindIntParameter(xrefs_insert,":column",column) + !bindIntParameter(xrefs_insert,":src_rowid",src_refid.rowid) || + !bindIntParameter(xrefs_insert,":dst_rowid",dst_refid.rowid) ) { return false; } + else + { + bindTextParameter(xrefs_insert,":context",context); + } step(xrefs_insert); return true; } -static void insertMemberReference(const MemberDef *src, const MemberDef *dst) +static void insertMemberReference(const MemberDef *src, const MemberDef *dst, const char *context) { - QCString qrefid_dst = dst->getOutputFileBase() + "_1" + dst->anchor(); - QCString qrefid_src = src->getOutputFileBase() + "_1" + src->anchor(); - if (dst->getStartBodyLine()!=-1 && dst->getBodyDef()) - { - int refid_src = insertRefid(qrefid_src.data()); - int refid_dst = insertRefid(qrefid_dst.data()); - int id_file = insertFile("no-file"); // TODO: replace no-file with proper file - insertMemberReference(refid_src,refid_dst,id_file,dst->getStartBodyLine(),-1); - } + QCString qdst_refid = dst->getOutputFileBase() + "_1" + dst->anchor(); + QCString qsrc_refid = src->getOutputFileBase() + "_1" + src->anchor(); + + struct Refid src_refid = insertRefid(qsrc_refid); + struct Refid dst_refid = insertRefid(qdst_refid); + insertMemberReference(src_refid,dst_refid,context); } -static void insertMemberFunctionParams(int id_memberdef, const MemberDef *md, const Definition *def) +static void insertMemberFunctionParams(int memberdef_id, const MemberDef *md, const Definition *def) { ArgumentList *declAl = md->declArgumentList(); ArgumentList *defAl = md->argumentList(); @@ -600,8 +1027,8 @@ static void insertMemberFunctionParams(int id_memberdef, const MemberDef *md, co if (!a->attrib.isEmpty()) { - bindTextParameter(params_select,":attributes",a->attrib.data()); - bindTextParameter(params_insert,":attributes",a->attrib.data()); + bindTextParameter(param_select,":attributes",a->attrib); + bindTextParameter(param_insert,":attributes",a->attrib); } if (!a->type.isEmpty()) { @@ -612,57 +1039,56 @@ static void insertMemberFunctionParams(int id_memberdef, const MemberDef *md, co QCString *s; while ((s=li.current())) { - QCString qrefid_src = md->getOutputFileBase() + "_1" + md->anchor(); - int refid_src = insertRefid(qrefid_src.data()); - int refid_dst = insertRefid(s->data()); - int id_file = insertFile(stripFromPath(def->getDefFileName())); - insertMemberReference(refid_src,refid_dst,id_file,md->getDefLine(),-1); + QCString qsrc_refid = md->getOutputFileBase() + "_1" + md->anchor(); + struct Refid src_refid = insertRefid(qsrc_refid); + struct Refid dst_refid = insertRefid(s->data()); + insertMemberReference(src_refid,dst_refid, "argument"); ++li; } - bindTextParameter(params_select,":type",a->type.data()); - bindTextParameter(params_insert,":type",a->type.data()); + bindTextParameter(param_select,":type",a->type); + bindTextParameter(param_insert,":type",a->type); } if (!a->name.isEmpty()) { - bindTextParameter(params_select,":declname",a->name.data()); - bindTextParameter(params_insert,":declname",a->name.data()); + bindTextParameter(param_select,":declname",a->name); + bindTextParameter(param_insert,":declname",a->name); } if (defArg && !defArg->name.isEmpty() && defArg->name!=a->name) { - bindTextParameter(params_select,":defname",defArg->name.data()); - bindTextParameter(params_insert,":defname",defArg->name.data()); + bindTextParameter(param_select,":defname",defArg->name); + bindTextParameter(param_insert,":defname",defArg->name); } if (!a->array.isEmpty()) { - bindTextParameter(params_select,":array",a->array.data()); - bindTextParameter(params_insert,":array",a->array.data()); + bindTextParameter(param_select,":array",a->array); + bindTextParameter(param_insert,":array",a->array); } if (!a->defval.isEmpty()) { StringList l; linkifyText(TextGeneratorSqlite3Impl(l),def,md->getBodyDef(),md,a->defval); - bindTextParameter(params_select,":defval",a->defval.data()); - bindTextParameter(params_insert,":defval",a->defval.data()); + bindTextParameter(param_select,":defval",a->defval); + bindTextParameter(param_insert,":defval",a->defval); } if (defArg) ++defAli; - int id_param=step(params_select,TRUE,TRUE); - if (id_param==0) { - id_param=step(params_insert,TRUE); + int param_id=step(param_select,TRUE,TRUE); + if (param_id==0) { + param_id=step(param_insert,TRUE); } - if (id_param==-1) { - msg("error INSERT params failed\n"); + if (param_id==-1) { + DBG_CTX(("error INSERT params failed\n")); continue; } - bindIntParameter(memberdef_params_insert,":id_memberdef",id_memberdef); - bindIntParameter(memberdef_params_insert,":id_param",id_param); - step(memberdef_params_insert); + bindIntParameter(memberdef_param_insert,":memberdef_id",memberdef_id); + bindIntParameter(memberdef_param_insert,":param_id",param_id); + step(memberdef_param_insert); } } } -static void insertMemberDefineParams(int id_memberdef,const MemberDef *md, const Definition *def) +static void insertMemberDefineParams(int memberdef_id,const MemberDef *md, const Definition *def) { if (md->argumentList()->count()==0) // special case for "foo()" to // disguish it from "foo". @@ -675,20 +1101,34 @@ static void insertMemberDefineParams(int id_memberdef,const MemberDef *md, const Argument *a; for (ali.toFirst();(a=ali.current());++ali) { - bindTextParameter(params_insert,":defname",a->type.data()); - int id_param=step(params_insert,TRUE); - if (id_param==-1) { - msg("error INSERT param(%s) failed\n", a->type.data()); + bindTextParameter(param_insert,":defname",a->type); + int param_id=step(param_insert,TRUE); + if (param_id==-1) { continue; } - bindIntParameter(memberdef_params_insert,":id_memberdef",id_memberdef); - bindIntParameter(memberdef_params_insert,":id_param",id_param); - step(memberdef_params_insert); + bindIntParameter(memberdef_param_insert,":memberdef_id",memberdef_id); + bindIntParameter(memberdef_param_insert,":param_id",param_id); + step(memberdef_param_insert); } } } +static void associateMember(const MemberDef *md, struct Refid member_refid, struct Refid scope_refid) +{ + // TODO: skip EnumValue only to guard against recording refids and member records + // for enumvalues until we can support documenting them as entities. + if (md->memberType()==MemberType_EnumValue) return; + if (md->name().at(0)!='@') // skip anonymous members + { + bindIntParameter(member_insert, ":scope_rowid", scope_refid.rowid); + bindIntParameter(member_insert, ":memberdef_rowid", member_refid.rowid); + + bindIntParameter(member_insert, ":prot", md->protection()); + bindIntParameter(member_insert, ":virt", md->virtualness()); + step(member_insert); + } +} static void stripQualifiers(QCString &typeStr) { @@ -720,22 +1160,28 @@ static int prepareStatement(sqlite3 *db, SqlStmt &s) static int prepareStatements(sqlite3 *db) { if ( + -1==prepareStatement(db, meta_insert) || + -1==prepareStatement(db, memberdef_exists) || + -1==prepareStatement(db, memberdef_incomplete) || -1==prepareStatement(db, memberdef_insert) || - -1==prepareStatement(db, files_insert) || - -1==prepareStatement(db, files_select) || - -1==prepareStatement(db, refids_insert) || - -1==prepareStatement(db, refids_select) || + -1==prepareStatement(db, memberdef_update_def) || + -1==prepareStatement(db, memberdef_update_decl) || + -1==prepareStatement(db, member_insert) || + -1==prepareStatement(db, path_insert) || + -1==prepareStatement(db, path_select) || + -1==prepareStatement(db, refid_insert) || + -1==prepareStatement(db, refid_select) || -1==prepareStatement(db, incl_insert)|| -1==prepareStatement(db, incl_select)|| - -1==prepareStatement(db, params_insert) || - -1==prepareStatement(db, params_select) || + -1==prepareStatement(db, param_insert) || + -1==prepareStatement(db, param_select) || -1==prepareStatement(db, xrefs_insert) || - -1==prepareStatement(db, innerclass_insert) || + -1==prepareStatement(db, reimplements_insert) || + -1==prepareStatement(db, contains_insert) || + -1==prepareStatement(db, compounddef_exists) || -1==prepareStatement(db, compounddef_insert) || - -1==prepareStatement(db, basecompoundref_insert) || - -1==prepareStatement(db, derivedcompoundref_insert) || - -1==prepareStatement(db, memberdef_params_insert)|| - -1==prepareStatement(db, innernamespace_insert) + -1==prepareStatement(db, compoundref_insert) || + -1==prepareStatement(db, memberdef_param_insert) ) { return -1; @@ -763,15 +1209,35 @@ static void pragmaTuning(sqlite3 *db) sqlite3_exec(db, "PRAGMA temp_store = MEMORY;", NULL, NULL, &sErrMsg); } -static int initializeSchema(sqlite3* db) +static int initializeTables(sqlite3* db) +{ + int rc; + sqlite3_stmt *stmt = 0; + + msg("Initializing DB schema (tables)...\n"); + for (unsigned int k = 0; k < sizeof(table_schema) / sizeof(table_schema[0]); k++) + { + const char *q = table_schema[k][1]; + char *errmsg; + rc = sqlite3_exec(db, q, NULL, NULL, &errmsg); + if (rc != SQLITE_OK) + { + msg("failed to execute query: %s\n\t%s\n", q, errmsg); + return -1; + } + } + return 0; +} + +static int initializeViews(sqlite3* db) { int rc; sqlite3_stmt *stmt = 0; - msg("Initializing DB schema...\n"); - for (unsigned int k = 0; k < sizeof(schema_queries) / sizeof(schema_queries[0]); k++) + msg("Initializing DB schema (views)...\n"); + for (unsigned int k = 0; k < sizeof(view_schema) / sizeof(view_schema[0]); k++) { - const char *q = schema_queries[k][1]; + const char *q = view_schema[k][1]; char *errmsg; rc = sqlite3_exec(db, q, NULL, NULL, &errmsg); if (rc != SQLITE_OK) @@ -784,40 +1250,122 @@ static int initializeSchema(sqlite3* db) } //////////////////////////////////////////// -static void writeInnerClasses(const ClassSDict *cl) +/* TODO: +I collapsed all innerX tables into 'contains', which raises the prospect that +all of these very similar writeInnerX funcs could be refactored into a one, +or a small set of common parts. + +I think the hurdles are: +- picking a first argument that every call location can pass +- which yields a consistent iterator +- accommodates PageDef's slightly different rules for generating the + inner_refid (unless I'm missing a method that would uniformly return + the correct refid for all types). +*/ +static void writeInnerClasses(const ClassSDict *cl, struct Refid outer_refid) { if (!cl) return; ClassSDict::Iterator cli(*cl); - ClassDef *cd; + const ClassDef *cd; for (cli.toFirst();(cd=cli.current());++cli) { if (!cd->isHidden() && cd->name().find('@')==-1) // skip anonymous scopes { - int refid = insertRefid(cd->getOutputFileBase()); - bindIntParameter(innerclass_insert,":refid", refid); - bindIntParameter(innerclass_insert,":prot",cd->protection()); - bindTextParameter(innerclass_insert,":name",cd->name()); - step(innerclass_insert); + struct Refid inner_refid = insertRefid(cd->getOutputFileBase()); + + bindIntParameter(contains_insert,":inner_rowid", inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid", outer_refid.rowid); + step(contains_insert); } } } +static void writeInnerPages(const PageSDict *pl, struct Refid outer_refid) +{ + if (!pl) return; + + PageSDict::Iterator pli(*pl); + const PageDef *pd; + for (pli.toFirst();(pd=pli.current());++pli) + { + struct Refid inner_refid = insertRefid( + pd->getGroupDef() ? pd->getOutputFileBase()+"_"+pd->name() : pd->getOutputFileBase() + ); + + bindIntParameter(contains_insert,":inner_rowid", inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid", outer_refid.rowid); + step(contains_insert); -static void writeInnerNamespaces(const NamespaceSDict *nl) + } +} + +static void writeInnerGroups(const GroupList *gl, struct Refid outer_refid) +{ + if (gl) + { + GroupListIterator gli(*gl); + const GroupDef *sgd; + for (gli.toFirst();(sgd=gli.current());++gli) + { + struct Refid inner_refid = insertRefid(sgd->getOutputFileBase()); + + bindIntParameter(contains_insert,":inner_rowid", inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid", outer_refid.rowid); + step(contains_insert); + } + } +} + +static void writeInnerFiles(const FileList *fl, struct Refid outer_refid) +{ + if (fl) + { + QListIterator fli(*fl); + const FileDef *fd; + for (fli.toFirst();(fd=fli.current());++fli) + { + struct Refid inner_refid = insertRefid(fd->getOutputFileBase()); + + bindIntParameter(contains_insert,":inner_rowid", inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid", outer_refid.rowid); + step(contains_insert); + } + } +} + +static void writeInnerDirs(const DirList *dl, struct Refid outer_refid) +{ + if (dl) + { + QListIterator subdirs(*dl); + const DirDef *subdir; + for (subdirs.toFirst();(subdir=subdirs.current());++subdirs) + { + struct Refid inner_refid = insertRefid(subdir->getOutputFileBase()); + + bindIntParameter(contains_insert,":inner_rowid", inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid", outer_refid.rowid); + step(contains_insert); + } + } +} + +static void writeInnerNamespaces(const NamespaceSDict *nl, struct Refid outer_refid) { if (nl) { NamespaceSDict::Iterator nli(*nl); - NamespaceDef *nd; + const NamespaceDef *nd; for (nli.toFirst();(nd=nli.current());++nli) { - if (!nd->isHidden() && nd->name().find('@')==-1) // skip anonymouse scopes + if (!nd->isHidden() && nd->name().find('@')==-1) // skip anonymous scopes { - int refid = insertRefid(nd->getOutputFileBase()); - bindIntParameter(innernamespace_insert,":refid",refid); - bindTextParameter(innernamespace_insert,":name",nd->name(),FALSE); - step(innernamespace_insert); + struct Refid inner_refid = insertRefid(nd->getOutputFileBase()); + + bindIntParameter(contains_insert,":inner_rowid",inner_refid.rowid); + bindIntParameter(contains_insert,":outer_rowid",outer_refid.rowid); + step(contains_insert); } } } @@ -837,24 +1385,24 @@ static void writeTemplateArgumentList(const ArgumentList * al, if (!a->type.isEmpty()) { #warning linkifyText(TextGeneratorXMLImpl(t),scope,fileScope,0,a->type); - bindTextParameter(params_select,":type",a->type); - bindTextParameter(params_insert,":type",a->type); + bindTextParameter(param_select,":type",a->type); + bindTextParameter(param_insert,":type",a->type); } if (!a->name.isEmpty()) { - bindTextParameter(params_select,":declname",a->name); - bindTextParameter(params_insert,":declname",a->name); - bindTextParameter(params_select,":defname",a->name); - bindTextParameter(params_insert,":defname",a->name); + bindTextParameter(param_select,":declname",a->name); + bindTextParameter(param_insert,":declname",a->name); + bindTextParameter(param_select,":defname",a->name); + bindTextParameter(param_insert,":defname",a->name); } if (!a->defval.isEmpty()) { #warning linkifyText(TextGeneratorXMLImpl(t),scope,fileScope,0,a->defval); - bindTextParameter(params_select,":defval",a->defval); - bindTextParameter(params_insert,":defval",a->defval); + bindTextParameter(param_select,":defval",a->defval); + bindTextParameter(param_insert,":defval",a->defval); } - if (!step(params_select,TRUE,TRUE)) - step(params_insert); + if (!step(param_select,TRUE,TRUE)) + step(param_insert); } } } @@ -871,15 +1419,132 @@ static void writeTemplateList(const ClassDef *cd) { writeTemplateArgumentList(cd->templateArguments(),cd,0); } + +QCString getSQLDocBlock(const Definition *scope, + const Definition *def, + const QCString &doc, + const QCString &fileName, + int lineNr) +{ + QGString s; + if (doc.isEmpty()) return s.data(); + FTextStream t(&s); + DocNode *root = validatingParseDoc( + fileName, + lineNr, + const_cast(scope), + const_cast(reinterpret_cast(def)), + doc, + FALSE, + FALSE + ); + XMLCodeGenerator codeGen(t); + // create a parse tree visitor for XML + XmlDocVisitor *visitor = new XmlDocVisitor(t,codeGen); + root->accept(visitor); + delete visitor; + delete root; + QCString result = convertCharEntitiesToUTF8(s.data()); + return result.data(); +} + +static void getSQLDesc(SqlStmt &s,const char *col,const char *value,const Definition *def) +{ + bindTextParameter( + s, + col, + getSQLDocBlock( + def->getOuterScope(), + def, + value, + def->docFile(), + def->docLine() + ), + FALSE + ); +} //////////////////////////////////////////// +/* (updated Sep 01 2018) +DoxMemberKind and DoxCompoundKind (compound.xsd) gave me some +faulty assumptions about "kind" strings, so I compiled a reference + +The XML schema claims: + DoxMemberKind: (14) + dcop define enum event friend function interface property prototype + service signal slot typedef variable + + DoxCompoundKind: (17) + category class dir example exception file group interface module + namespace page protocol service singleton struct type union + +Member kind comes from MemberDef::memberTypeName() + types.h defines 14 MemberType_*s + _DCOP _Define _Enumeration _EnumValue _Event _Friend _Function _Interface + _Property _Service _Signal _Slot _Typedef _Variable + - xml doesn't include enumvalue here + (but renders enumvalue as) a sub-node of memberdef/templateparamlist + - xml includes 'prototype' that is unlisted here + vestigial? commented out in docsets.cpp and perlmodgen.cpp + MemberDef::memberTypeName() can return 15 strings: + (sorted by MemberType to match above; quoted because whitespace...) + "dcop" "macro definition" "enumeration" "enumvalue" "event" "friend" + "function" "interface" "property" "service" "signal" "slot" "typedef" + "variable" + + Above describes potential values for memberdef.kind + +Compound kind is more complex. *Def::compoundTypeString() + ClassDef kind comes from ::compoundTypeString() + classdef.h defines 9 compound types + Category Class Exception Interface Protocol Service Singleton Struct Union + But ClassDef::compoundTypeString() "could" return 13 strings + - default "unknown" shouldn't actually return + - other 12 can vary by source language; see method for specifics + category class enum exception interface module protocol service + singleton struct type union + + DirDef, FileDef, GroupDef have no method to return a string + tagfile/outputs hard-code kind to 'dir' 'file' or 'group' + + NamespaceDef kind comes from ::compoundTypeString() + NamespaceDef::compoundTypeString() "could" return 6 strings + - default empty ("") string + - other 5 differ by source language + constants library module namespace package + + PageDef also has no method to return a string + - some locations hard-code the kind to 'page' + - others conditionally output 'page' or 'example' + + All together, that's 23 potential strings (21 excl "" and unknown) + "" category class constants dir enum example exception file group + interface library module namespace package page protocol service singleton + struct type union unknown + + Above describes potential values for compounddef.kind + +For reference, there are 35 potential values of def.kind (33 excl "" and unknown): + "" "category" "class" "constants" "dcop" "dir" "enum" "enumeration" + "enumvalue" "event" "example" "exception" "file" "friend" "function" "group" + "interface" "library" "macro definition" "module" "namespace" "package" + "page" "property" "protocol" "service" "signal" "singleton" "slot" "struct" + "type" "typedef" "union" "unknown" "variable" + +This is relevant because the 'def' view generalizes memberdef and compounddef, +and two member+compound kind strings (interface and service) overlap. + +I have no grasp of whether a real user docset would include one or more +member and compound using the interface or service kind. +*/ + ////////////////////////////////////////////////////////////////////////////// -static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) +static void generateSqlite3ForMember(const MemberDef *md, struct Refid scope_refid, const Definition *def) { // + declaration/definition arg lists // + reimplements // + reimplementedBy - // + exceptions + // - exceptions // + const/volatile specifiers // - examples // + source definition @@ -893,18 +1558,102 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) // enum values are written as part of the enum if (md->memberType()==MemberType_EnumValue) return; if (md->isHidden()) return; - //if (md->name().at(0)=='@') return; // anonymous member - // group members are only visible in their group - //if (def->definitionType()!=Definition::TypeGroup && md->getGroupDef()) return; QCString memType; // memberdef QCString qrefid = md->getOutputFileBase() + "_1" + md->anchor(); - int refid = insertRefid(qrefid.data()); + struct Refid refid = insertRefid(qrefid); + + associateMember(md, refid, scope_refid); + + // compacting duplicate defs + if(!refid.created && memberdefExists(refid) && memberdefIncomplete(refid, md)) + { + /* + For performance, ideal to skip a member we've already added. + Unfortunately, we can have two memberdefs with the same refid documenting + the declaration and definition. memberdefIncomplete() uses the 'inline' + value to figure this out. Once we get to this point, we should *only* be + seeing the *other* type of def/decl, so we'll set inline to a new value (2), + indicating that this entry covers both inline types. + */ + struct SqlStmt memberdef_update; + + // definitions have bodyfile/start/end + if (md->getStartBodyLine()!=-1) + { + memberdef_update = memberdef_update_def; + int bodyfile_id = insertPath(md->getBodyDef()->absFilePath(),!md->getBodyDef()->isReference()); + if (bodyfile_id == -1) + { + sqlite3_clear_bindings(memberdef_update.stmt); + } + else + { + bindIntParameter(memberdef_update,":bodyfile_id",bodyfile_id); + bindIntParameter(memberdef_update,":bodystart",md->getStartBodyLine()); + bindIntParameter(memberdef_update,":bodyend",md->getEndBodyLine()); + } + } + // declarations don't + else + { + memberdef_update = memberdef_update_decl; + if (md->getDefLine() != -1) + { + int file_id = insertPath(md->getDefFileName(),!md->isReference()); + if (file_id!=-1) + { + bindIntParameter(memberdef_update,":file_id",file_id); + bindIntParameter(memberdef_update,":line",md->getDefLine()); + bindIntParameter(memberdef_update,":column",md->getDefColumn()); + } + } + } - bindIntParameter(memberdef_insert,":refid", refid); - bindIntParameter(memberdef_insert,":kind",md->memberType()); + bindIntParameter(memberdef_update, ":rowid", refid.rowid); + // value 2 indicates we've seen "both" inline types. + bindIntParameter(memberdef_update,":inline", 2); + + /* in case both are used, append/prepend descriptions */ + getSQLDesc(memberdef_update,":briefdescription",md->briefDescription(),md); + getSQLDesc(memberdef_update,":detaileddescription",md->documentation(),md); + getSQLDesc(memberdef_update,":inbodydescription",md->inbodyDocumentation(),md); + + step(memberdef_update,TRUE); + + // don't think we need to repeat params; should have from first encounter + + // + source references + // The cross-references in initializers only work when both the src and dst + // are defined. + MemberSDict *mdict = md->getReferencesMembers(); + if (mdict!=0) + { + MemberSDict::IteratorDict mdi(*mdict); + const MemberDef *rmd; + for (mdi.toFirst();(rmd=mdi.current());++mdi) + { + insertMemberReference(md,rmd, "inline"); + } + } + // + source referenced by + mdict = md->getReferencedByMembers(); + if (mdict!=0) + { + MemberSDict::IteratorDict mdi(*mdict); + const MemberDef *rmd; + for (mdi.toFirst();(rmd=mdi.current());++mdi) + { + insertMemberReference(rmd,md, "inline"); + } + } + return; + } + + bindIntParameter(memberdef_insert,":rowid", refid.rowid); + bindTextParameter(memberdef_insert,":kind",md->memberTypeName(),FALSE); bindIntParameter(memberdef_insert,":prot",md->protection()); bindIntParameter(memberdef_insert,":static",md->isStatic()); @@ -973,6 +1722,7 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) bindIntParameter(memberdef_insert,":settable",md->isSettable()); bindIntParameter(memberdef_insert,":privatesettable",md->isPrivateSettable()); bindIntParameter(memberdef_insert,":protectedsettable",md->isProtectedSettable()); + if (md->isAssign() || md->isCopy() || md->isRetain() || md->isStrong() || md->isWeak()) { @@ -995,6 +1745,18 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) bindIntParameter(memberdef_insert,":raisable",md->isRaisable()); } + const MemberDef *rmd = md->reimplements(); + if(rmd) + { + QCString qreimplemented_refid = rmd->getOutputFileBase() + "_1" + rmd->anchor(); + + struct Refid reimplemented_refid = insertRefid(qreimplemented_refid); + + bindIntParameter(reimplements_insert,":memberdef_rowid", refid.rowid); + bindIntParameter(reimplements_insert,":reimplemented_rowid", reimplemented_refid.rowid); + step(reimplements_insert,TRUE); + } + // + declaration/definition arg lists if (md->memberType()!=MemberType_Define && md->memberType()!=MemberType_Enumeration @@ -1008,9 +1770,9 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) stripQualifiers(typeStr); StringList l; linkifyText(TextGeneratorSqlite3Impl(l), def, md->getBodyDef(),md,typeStr); - if (typeStr.data()) + if (typeStr) { - bindTextParameter(memberdef_insert,":type",typeStr.data(),FALSE); + bindTextParameter(memberdef_insert,":type",typeStr,FALSE); } if (md->definition()) @@ -1029,7 +1791,7 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) // Extract references from initializer if (md->hasMultiLineInitializer() || md->hasOneLineInitializer()) { - bindTextParameter(memberdef_insert,":initializer",md->initializer().data()); + bindTextParameter(memberdef_insert,":initializer",md->initializer()); StringList l; linkifyText(TextGeneratorSqlite3Impl(l),def,md->getBodyDef(),md,md->initializer()); @@ -1044,11 +1806,10 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) s->data(), md->getBodyDef()->getDefFileName().data(), md->getStartBodyLine())); - QCString qrefid_src = md->getOutputFileBase() + "_1" + md->anchor(); - int refid_src = insertRefid(qrefid_src.data()); - int refid_dst = insertRefid(s->data()); - int id_file = insertFile(stripFromPath(md->getBodyDef()->getDefFileName())); - insertMemberReference(refid_src,refid_dst,id_file,md->getStartBodyLine(),-1); + QCString qsrc_refid = md->getOutputFileBase() + "_1" + md->anchor(); + struct Refid src_refid = insertRefid(qsrc_refid); + struct Refid dst_refid = insertRefid(s->data()); + insertMemberReference(src_refid,dst_refid, "initializer"); } ++li; } @@ -1056,34 +1817,35 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) if ( md->getScopeString() ) { - bindTextParameter(memberdef_insert,":scope",md->getScopeString().data(),FALSE); + bindTextParameter(memberdef_insert,":scope",md->getScopeString(),FALSE); } // +Brief, detailed and inbody description - bindTextParameter(memberdef_insert,":briefdescription",md->briefDescription(),FALSE); - bindTextParameter(memberdef_insert,":detaileddescription",md->documentation(),FALSE); - bindTextParameter(memberdef_insert,":inbodydescription",md->inbodyDocumentation(),FALSE); + getSQLDesc(memberdef_insert,":briefdescription",md->briefDescription(),md); + getSQLDesc(memberdef_insert,":detaileddescription",md->documentation(),md); + getSQLDesc(memberdef_insert,":inbodydescription",md->inbodyDocumentation(),md); // File location if (md->getDefLine() != -1) { - int id_file = insertFile(stripFromPath(md->getDefFileName())); - if (id_file!=-1) + int file_id = insertPath(md->getDefFileName(),!md->isReference()); + if (file_id!=-1) { - bindIntParameter(memberdef_insert,":id_file",id_file); + bindIntParameter(memberdef_insert,":file_id",file_id); bindIntParameter(memberdef_insert,":line",md->getDefLine()); bindIntParameter(memberdef_insert,":column",md->getDefColumn()); + // definitions also have bodyfile/start/end if (md->getStartBodyLine()!=-1) { - int id_bodyfile = insertFile(stripFromPath(md->getBodyDef()->absFilePath())); - if (id_bodyfile == -1) + int bodyfile_id = insertPath(md->getBodyDef()->absFilePath(),!md->getBodyDef()->isReference()); + if (bodyfile_id == -1) { sqlite3_clear_bindings(memberdef_insert.stmt); } else { - bindIntParameter(memberdef_insert,":id_bodyfile",id_bodyfile); + bindIntParameter(memberdef_insert,":bodyfile_id",bodyfile_id); bindIntParameter(memberdef_insert,":bodystart",md->getStartBodyLine()); bindIntParameter(memberdef_insert,":bodyend",md->getEndBodyLine()); } @@ -1091,16 +1853,16 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) } } - int id_memberdef=step(memberdef_insert,TRUE); + int memberdef_id=step(memberdef_insert,TRUE); if (isFunc) { - insertMemberFunctionParams(id_memberdef,md,def); + insertMemberFunctionParams(memberdef_id,md,def); } else if (md->memberType()==MemberType_Define && md->argsString()) { - insertMemberDefineParams(id_memberdef,md,def); + insertMemberDefineParams(memberdef_id,md,def); } // + source references @@ -1110,10 +1872,10 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) if (mdict!=0) { MemberSDict::IteratorDict mdi(*mdict); - MemberDef *rmd; + const MemberDef *rmd; for (mdi.toFirst();(rmd=mdi.current());++mdi) { - insertMemberReference(md,rmd);//,mdi.currentKey()); + insertMemberReference(md,rmd, "inline"); } } // + source referenced by @@ -1121,62 +1883,79 @@ static void generateSqlite3ForMember(const MemberDef *md, const Definition *def) if (mdict!=0) { MemberSDict::IteratorDict mdi(*mdict); - MemberDef *rmd; + const MemberDef *rmd; for (mdi.toFirst();(rmd=mdi.current());++mdi) { - insertMemberReference(rmd,md);//,mdi.currentKey()); + insertMemberReference(rmd,md, "inline"); } } } static void generateSqlite3Section( const Definition *d, const MemberList *ml, + struct Refid scope_refid, const char * /*kind*/, const char * /*header*/=0, const char * /*documentation*/=0) { if (ml==0) return; MemberListIterator mli(*ml); - MemberDef *md; - int count=0; + const MemberDef *md; + for (mli.toFirst();(md=mli.current());++mli) { + // TODO: necessary? just tracking what xmlgen does; xmlgen says: // namespace members are also inserted in the file scope, but // to prevent this duplication in the XML output, we filter those here. if (d->definitionType()!=Definition::TypeFile || md->getNamespaceDef()==0) { - count++; + generateSqlite3ForMember(md, scope_refid, d); } } - if (count==0) return; // empty list - for (mli.toFirst();(md=mli.current());++mli) +} + +static void associateAllClassMembers(const ClassDef *cd, struct Refid scope_refid) +{ + if (cd->memberNameInfoSDict()) { - // namespace members are also inserted in the file scope, but - // to prevent this duplication in the XML output, we filter those here. - //if (d->definitionType()!=Definition::TypeFile || md->getNamespaceDef()==0) + MemberNameInfoSDict::Iterator mnii(*cd->memberNameInfoSDict()); + MemberNameInfo *mni; + for (mnii.toFirst();(mni=mnii.current());++mnii) { - generateSqlite3ForMember(md,d); + MemberNameInfoIterator mii(*mni); + MemberInfo *mi; + for (mii.toFirst();(mi=mii.current());++mii) + { + MemberDef *md = mi->memberDef; + QCString qrefid = md->getOutputFileBase() + "_1" + md->anchor(); + associateMember(md, insertRefid(qrefid), scope_refid); + } } } } - +// many kinds: category class enum exception interface +// module protocol service singleton struct type union +// enum is Java only (and is distinct from enum memberdefs) static void generateSqlite3ForClass(const ClassDef *cd) { + // NOTE: Skeptical about XML's version of these + // 'x' marks missing items XML claims to include + + // + brief description + // + detailed description + // + template argument list(s) + // + include file + // + member groups + // x inheritance DOT diagram // + list of direct super classes // + list of direct sub classes - // + include file // + list of inner classes - // - template argument list(s) - // + member groups + // x collaboration DOT diagram // + list of all members - // - brief description - // - detailed description - // - inheritance DOT diagram - // - collaboration DOT diagram - // - user defined member sections - // - standard member sections - // - detailed member documentation + // x user defined member sections + // x standard member sections + // x detailed member documentation // - examples using the class if (cd->isReference()) return; // skip external references. @@ -1184,43 +1963,86 @@ static void generateSqlite3ForClass(const ClassDef *cd) if (cd->name().find('@')!=-1) return; // skip anonymous compounds. if (cd->templateMaster()!=0) return; // skip generated template instances. - msg("Generating Sqlite3 output for class %s\n",cd->name().data()); + struct Refid refid = insertRefid(cd->getOutputFileBase()); + + // can omit a class that already has a refid + if(!refid.created && compounddefExists(refid)){return;} + + bindIntParameter(compounddef_insert,":rowid", refid.rowid); bindTextParameter(compounddef_insert,":name",cd->name()); + bindTextParameter(compounddef_insert,":title",cd->title(), FALSE); bindTextParameter(compounddef_insert,":kind",cd->compoundTypeString(),FALSE); bindIntParameter(compounddef_insert,":prot",cd->protection()); - int refid = insertRefid(cd->getOutputFileBase()); - bindIntParameter(compounddef_insert,":refid", refid); - int id_file = insertFile(stripFromPath(cd->getDefFileName())); - bindIntParameter(compounddef_insert,":id_file",id_file); + int file_id = insertPath(cd->getDefFileName()); + bindIntParameter(compounddef_insert,":file_id",file_id); bindIntParameter(compounddef_insert,":line",cd->getDefLine()); bindIntParameter(compounddef_insert,":column",cd->getDefColumn()); + // + include file + /* + TODO: I wonder if this can actually be cut (just here) + + We were adding this "include" to the "includes" table alongside + other includes (from a FileDef). However, FileDef and ClassDef are using + "includes" nodes in very a different way: + - With FileDef, it means the file includes another. + - With ClassDef, it means you should include this file to use this class. + + Because of this difference, I added a column to compounddef, header_id, and + linked it back to the appropriate file. We could just add a nullable text + column that would hold a string equivalent to what the HTML docs include, + but the logic for generating it is embedded in + ClassDef::writeIncludeFiles(OutputList &ol). + + That said, at least on the handful of test sets I have, header_id == file_id, + suggesting it could be cut and clients might be able to reconstruct it from + other values if there's a solid heuristic for *when a class will + have a header file*. + */ + IncludeInfo *ii=cd->includeInfo(); + if (ii) + { + QCString nm = ii->includeName; + if (nm.isEmpty() && ii->fileDef) nm = ii->fileDef->docName(); + if (!nm.isEmpty()) + { + int header_id=insertPath(ii->fileDef->absFilePath(),!ii->fileDef->isReference()); + DBG_CTX(("-----> ClassDef includeInfo for %s\n", nm.data())); + DBG_CTX((" local : %d\n", ii->local)); + DBG_CTX((" imported : %d\n", ii->imported)); + DBG_CTX((" indirect : %d\n", ii->indirect)); + DBG_CTX(("header: %s\n", ii->fileDef->absFilePath().data())); + DBG_CTX((" file_id : %d\n", file_id)); + DBG_CTX((" header_id: %d\n", header_id)); + + if(header_id!=-1) + { + bindIntParameter(compounddef_insert,":header_id",header_id); + } + } + } + + getSQLDesc(compounddef_insert,":briefdescription",cd->briefDescription(),cd); + getSQLDesc(compounddef_insert,":detaileddescription",cd->documentation(),cd); + step(compounddef_insert); // + list of direct super classes if (cd->baseClasses()) { BaseClassListIterator bcli(*cd->baseClasses()); - BaseClassDef *bcd; + const BaseClassDef *bcd; for (bcli.toFirst();(bcd=bcli.current());++bcli) { - int refid = insertRefid(bcd->classDef->getOutputFileBase()); - bindIntParameter(basecompoundref_insert,":refid", refid); - bindIntParameter(basecompoundref_insert,":prot",bcd->prot); - bindIntParameter(basecompoundref_insert,":virt",bcd->virt); - - if (!bcd->templSpecifiers.isEmpty()) - { - bindTextParameter(basecompoundref_insert,":base",insertTemplateSpecifierInScope(bcd->classDef->name(),bcd->templSpecifiers),FALSE); - } - else - { - bindTextParameter(basecompoundref_insert,":base",bcd->classDef->displayName(),FALSE); - } - bindTextParameter(basecompoundref_insert,":derived",cd->displayName(),FALSE); - step(basecompoundref_insert); + struct Refid base_refid = insertRefid(bcd->classDef->getOutputFileBase()); + struct Refid derived_refid = insertRefid(cd->getOutputFileBase()); + bindIntParameter(compoundref_insert,":base_rowid", base_refid.rowid); + bindIntParameter(compoundref_insert,":derived_rowid", derived_refid.rowid); + bindIntParameter(compoundref_insert,":prot",bcd->prot); + bindIntParameter(compoundref_insert,":virt",bcd->virt); + step(compoundref_insert); } } @@ -1228,54 +2050,23 @@ static void generateSqlite3ForClass(const ClassDef *cd) if (cd->subClasses()) { BaseClassListIterator bcli(*cd->subClasses()); - BaseClassDef *bcd; + const BaseClassDef *bcd; for (bcli.toFirst();(bcd=bcli.current());++bcli) { - bindTextParameter(derivedcompoundref_insert,":base",cd->displayName(),FALSE); - if (!bcd->templSpecifiers.isEmpty()) - { - bindTextParameter(derivedcompoundref_insert,":derived",insertTemplateSpecifierInScope(bcd->classDef->name(),bcd->templSpecifiers),FALSE); - } - else - { - bindTextParameter(derivedcompoundref_insert,":derived",bcd->classDef->displayName(),FALSE); - } - int refid = insertRefid(bcd->classDef->getOutputFileBase()); - bindIntParameter(derivedcompoundref_insert,":refid", refid); - bindIntParameter(derivedcompoundref_insert,":prot",bcd->prot); - bindIntParameter(derivedcompoundref_insert,":virt",bcd->virt); - step(derivedcompoundref_insert); + struct Refid derived_refid = insertRefid(bcd->classDef->getOutputFileBase()); + struct Refid base_refid = insertRefid(cd->getOutputFileBase()); + bindIntParameter(compoundref_insert,":base_rowid", base_refid.rowid); + bindIntParameter(compoundref_insert,":derived_rowid", derived_refid.rowid); + bindIntParameter(compoundref_insert,":prot",bcd->prot); + bindIntParameter(compoundref_insert,":virt",bcd->virt); + step(compoundref_insert); } } - // + include file - IncludeInfo *ii=cd->includeInfo(); - if (ii) - { - QCString nm = ii->includeName; - if (nm.isEmpty() && ii->fileDef) nm = ii->fileDef->docName(); - if (!nm.isEmpty()) - { - int id_dst=insertFile(nm); - if (id_dst!=-1) { - bindIntParameter(incl_select,":local",ii->local); - bindIntParameter(incl_select,":id_src",id_file); - bindIntParameter(incl_select,":id_dst",id_dst); - int count=step(incl_select,TRUE,TRUE); - if (count==0) - { - bindIntParameter(incl_insert,":local",ii->local); - bindIntParameter(incl_insert,":id_src",id_file); - bindIntParameter(incl_insert,":id_dst",id_dst); - step(incl_insert); - } - } - } - } // + list of inner classes - writeInnerClasses(cd->getClassSDict()); + writeInnerClasses(cd->getClassSDict(),refid); - // - template argument list(s) + // + template argument list(s) writeTemplateList(cd); // + member groups @@ -1285,41 +2076,62 @@ static void generateSqlite3ForClass(const ClassDef *cd) MemberGroup *mg; for (;(mg=mgli.current());++mgli) { - generateSqlite3Section(cd,mg->members(),"user-defined",mg->header(), + generateSqlite3Section(cd,mg->members(),refid,"user-defined",mg->header(), mg->documentation()); } } - // + list of all members + // this is just a list of *local* members QListIterator mli(cd->getMemberLists()); MemberList *ml; for (mli.toFirst();(ml=mli.current());++mli) { if ((ml->listType()&MemberListType_detailedLists)==0) { - generateSqlite3Section(cd,ml,"user-defined");//g_xmlSectionMapper.find(ml->listType())); + generateSqlite3Section(cd,ml,refid,"user-defined"); } } + + // + list of all members + associateAllClassMembers(cd, refid); } +// kinds: constants library module namespace package static void generateSqlite3ForNamespace(const NamespaceDef *nd) { // + contained class definitions // + contained namespace definitions // + member groups // + normal members - // - brief desc - // - detailed desc - // - location + // + brief desc + // + detailed desc + // + location (file_id, line, column) // - files containing (parts of) the namespace definition if (nd->isReference() || nd->isHidden()) return; // skip external references + struct Refid refid = insertRefid(nd->getOutputFileBase()); + if(!refid.created && compounddefExists(refid)){return;} + bindIntParameter(compounddef_insert,":rowid", refid.rowid); + + bindTextParameter(compounddef_insert,":name",nd->name()); + bindTextParameter(compounddef_insert,":title",nd->title(), FALSE); + bindTextParameter(compounddef_insert,":kind","namespace",FALSE); + + int file_id = insertPath(nd->getDefFileName()); + bindIntParameter(compounddef_insert,":file_id",file_id); + bindIntParameter(compounddef_insert,":line",nd->getDefLine()); + bindIntParameter(compounddef_insert,":column",nd->getDefColumn()); + + getSQLDesc(compounddef_insert,":briefdescription",nd->briefDescription(),nd); + getSQLDesc(compounddef_insert,":detaileddescription",nd->documentation(),nd); + + step(compounddef_insert); // + contained class definitions - writeInnerClasses(nd->getClassSDict()); + writeInnerClasses(nd->getClassSDict(),refid); // + contained namespace definitions - writeInnerNamespaces(nd->getNamespaceSDict()); + writeInnerNamespaces(nd->getNamespaceSDict(),refid); // + member groups if (nd->getMemberGroupSDict()) @@ -1328,7 +2140,7 @@ static void generateSqlite3ForNamespace(const NamespaceDef *nd) MemberGroup *mg; for (;(mg=mgli.current());++mgli) { - generateSqlite3Section(nd,mg->members(),"user-defined",mg->header(), + generateSqlite3Section(nd,mg->members(),refid,"user-defined",mg->header(), mg->documentation()); } } @@ -1340,29 +2152,48 @@ static void generateSqlite3ForNamespace(const NamespaceDef *nd) { if ((ml->listType()&MemberListType_declarationLists)!=0) { - generateSqlite3Section(nd,ml,"user-defined");//g_xmlSectionMapper.find(ml->listType())); + generateSqlite3Section(nd,ml,refid,"user-defined"); } } } +// kind: file static void generateSqlite3ForFile(const FileDef *fd) { // + includes files // + includedby files - // - include graph - // - included by graph + // x include graph + // x included by graph // + contained class definitions // + contained namespace definitions // + member groups // + normal members - // - brief desc - // - detailed desc - // - source code - // - location + // + brief desc + // + detailed desc + // x source code + // + location (file_id, line, column) // - number of lines if (fd->isReference()) return; // skip external references + struct Refid refid = insertRefid(fd->getOutputFileBase()); + if(!refid.created && compounddefExists(refid)){return;} + bindIntParameter(compounddef_insert,":rowid", refid.rowid); + + bindTextParameter(compounddef_insert,":name",fd->name(),FALSE); + bindTextParameter(compounddef_insert,":title",fd->title(),FALSE); + bindTextParameter(compounddef_insert,":kind","file",FALSE); + + int file_id = insertPath(fd->getDefFileName()); + bindIntParameter(compounddef_insert,":file_id",file_id); + bindIntParameter(compounddef_insert,":line",fd->getDefLine()); + bindIntParameter(compounddef_insert,":column",fd->getDefColumn()); + + getSQLDesc(compounddef_insert,":briefdescription",fd->briefDescription(),fd); + getSQLDesc(compounddef_insert,":detaileddescription",fd->documentation(),fd); + + step(compounddef_insert); + // + includes files IncludeInfo *ii; if (fd->includeFileList()) @@ -1370,15 +2201,48 @@ static void generateSqlite3ForFile(const FileDef *fd) QListIterator ili(*fd->includeFileList()); for (ili.toFirst();(ii=ili.current());++ili) { - int id_src=insertFile(fd->absFilePath().data()); - int id_dst=insertFile(ii->includeName.data()); + int src_id=insertPath(fd->absFilePath(),!fd->isReference()); + int dst_id; + QCString dst_path; + + if(ii->fileDef) // found file + { + if(ii->fileDef->isReference()) + { + // strip tagfile from path + QCString tagfile = ii->fileDef->getReference(); + dst_path = ii->fileDef->absFilePath().copy(); + dst_path.stripPrefix(tagfile+":"); + } + else + { + dst_path = ii->fileDef->absFilePath(); + } + dst_id = insertPath(dst_path,ii->local); + } + else // can't find file + { + dst_id = insertPath(ii->includeName,ii->local,FALSE); + } + + DBG_CTX(("-----> FileDef includeInfo for %s\n", ii->includeName.data())); + DBG_CTX((" local: %d\n", ii->local)); + DBG_CTX((" imported: %d\n", ii->imported)); + DBG_CTX((" indirect: %d\n", ii->indirect)); + if(ii->fileDef) + { + DBG_CTX(("include: %s\n", ii->fileDef->absFilePath().data())); + } + DBG_CTX((" src_id : %d\n", src_id)); + DBG_CTX((" dst_id: %d\n", dst_id)); + bindIntParameter(incl_select,":local",ii->local); - bindIntParameter(incl_select,":id_src",id_src); - bindIntParameter(incl_select,":id_dst",id_dst); + bindIntParameter(incl_select,":src_id",src_id); + bindIntParameter(incl_select,":dst_id",dst_id); if (step(incl_select,TRUE,TRUE)==0) { bindIntParameter(incl_insert,":local",ii->local); - bindIntParameter(incl_insert,":id_src",id_src); - bindIntParameter(incl_insert,":id_dst",id_dst); + bindIntParameter(incl_insert,":src_id",src_id); + bindIntParameter(incl_insert,":dst_id",dst_id); step(incl_insert); } } @@ -1390,15 +2254,37 @@ static void generateSqlite3ForFile(const FileDef *fd) QListIterator ili(*fd->includedByFileList()); for (ili.toFirst();(ii=ili.current());++ili) { - int id_src=insertFile(ii->includeName); - int id_dst=insertFile(fd->absFilePath()); + int dst_id=insertPath(fd->absFilePath(),!fd->isReference()); + int src_id; + QCString src_path; + + if(ii->fileDef) // found file + { + if(ii->fileDef->isReference()) + { + // strip tagfile from path + QCString tagfile = ii->fileDef->getReference(); + src_path = ii->fileDef->absFilePath().copy(); + src_path.stripPrefix(tagfile+":"); + } + else + { + src_path = ii->fileDef->absFilePath(); + } + src_id = insertPath(src_path,ii->local); + } + else // can't find file + { + src_id = insertPath(ii->includeName,ii->local,FALSE); + } + bindIntParameter(incl_select,":local",ii->local); - bindIntParameter(incl_select,":id_src",id_src); - bindIntParameter(incl_select,":id_dst",id_dst); + bindIntParameter(incl_select,":src_id",src_id); + bindIntParameter(incl_select,":dst_id",dst_id); if (step(incl_select,TRUE,TRUE)==0) { bindIntParameter(incl_insert,":local",ii->local); - bindIntParameter(incl_insert,":id_src",id_src); - bindIntParameter(incl_insert,":id_dst",id_dst); + bindIntParameter(incl_insert,":src_id",src_id); + bindIntParameter(incl_insert,":dst_id",dst_id); step(incl_insert); } } @@ -1407,13 +2293,13 @@ static void generateSqlite3ForFile(const FileDef *fd) // + contained class definitions if (fd->getClassSDict()) { - writeInnerClasses(fd->getClassSDict()); + writeInnerClasses(fd->getClassSDict(),refid); } // + contained namespace definitions if (fd->getNamespaceSDict()) { - writeInnerNamespaces(fd->getNamespaceSDict()); + writeInnerNamespaces(fd->getNamespaceSDict(),refid); } // + member groups @@ -1423,7 +2309,7 @@ static void generateSqlite3ForFile(const FileDef *fd) MemberGroup *mg; for (;(mg=mgli.current());++mgli) { - generateSqlite3Section(fd,mg->members(),"user-defined",mg->header(), + generateSqlite3Section(fd,mg->members(),refid,"user-defined",mg->header(), mg->documentation()); } } @@ -1435,24 +2321,201 @@ static void generateSqlite3ForFile(const FileDef *fd) { if ((ml->listType()&MemberListType_declarationLists)!=0) { - generateSqlite3Section(fd,ml,"user-defined");//g_xmlSectionMapper.find(ml->listType())); + generateSqlite3Section(fd,ml,refid,"user-defined"); } } } +// kind: group static void generateSqlite3ForGroup(const GroupDef *gd) { -#warning WorkInProgress + // + members + // + member groups + // + files + // + classes + // + namespaces + // - packages + // + pages + // + child groups + // - examples + // + brief description + // + detailed description + + if (gd->isReference()) return; // skip external references. + + struct Refid refid = insertRefid(gd->getOutputFileBase()); + if(!refid.created && compounddefExists(refid)){return;} + bindIntParameter(compounddef_insert,":rowid", refid.rowid); + + bindTextParameter(compounddef_insert,":name",gd->name()); + bindTextParameter(compounddef_insert,":title",gd->groupTitle(), FALSE); + bindTextParameter(compounddef_insert,":kind","group",FALSE); + + int file_id = insertPath(gd->getDefFileName()); + bindIntParameter(compounddef_insert,":file_id",file_id); + bindIntParameter(compounddef_insert,":line",gd->getDefLine()); + bindIntParameter(compounddef_insert,":column",gd->getDefColumn()); + + getSQLDesc(compounddef_insert,":briefdescription",gd->briefDescription(),gd); + getSQLDesc(compounddef_insert,":detaileddescription",gd->documentation(),gd); + + step(compounddef_insert); + + // + files + writeInnerFiles(gd->getFiles(),refid); + + // + classes + writeInnerClasses(gd->getClasses(),refid); + + // + namespaces + writeInnerNamespaces(gd->getNamespaces(),refid); + + // + pages + writeInnerPages(gd->getPages(),refid); + + // + groups + writeInnerGroups(gd->getSubGroups(),refid); + + // + member groups + if (gd->getMemberGroupSDict()) + { + MemberGroupSDict::Iterator mgli(*gd->getMemberGroupSDict()); + MemberGroup *mg; + for (;(mg=mgli.current());++mgli) + { + generateSqlite3Section(gd,mg->members(),refid,"user-defined",mg->header(), + mg->documentation()); + } + } + + // + members + QListIterator mli(gd->getMemberLists()); + MemberList *ml; + for (mli.toFirst();(ml=mli.current());++mli) + { + if ((ml->listType()&MemberListType_declarationLists)!=0) + { + generateSqlite3Section(gd,ml,refid,"user-defined"); + } + } } +// kind: dir static void generateSqlite3ForDir(const DirDef *dd) { -#warning WorkInProgress + // + dirs + // + files + // + briefdescription + // + detaileddescription + // + location (below uses file_id, line, column; XML just uses file) + if (dd->isReference()) return; // skip external references + + struct Refid refid = insertRefid(dd->getOutputFileBase()); + if(!refid.created && compounddefExists(refid)){return;} + bindIntParameter(compounddef_insert,":rowid", refid.rowid); + + bindTextParameter(compounddef_insert,":name",dd->displayName()); + bindTextParameter(compounddef_insert,":kind","dir",FALSE); + + int file_id = insertPath(dd->getDefFileName(),TRUE,TRUE,2); + bindIntParameter(compounddef_insert,":file_id",file_id); + + /* + line and column are weird here, but: + - dir goes into compounddef with all of the others + - the semantics would be fine if we set them to NULL here + - but defining line and column as NOT NULL is an important promise + for other compounds, so I don't want to loosen it + + For reference, the queries return 1. + 0 or -1 make more sense, but I see that as a change for DirDef. + */ + bindIntParameter(compounddef_insert,":line",dd->getDefLine()); + bindIntParameter(compounddef_insert,":column",dd->getDefColumn()); + + getSQLDesc(compounddef_insert,":briefdescription",dd->briefDescription(),dd); + getSQLDesc(compounddef_insert,":detaileddescription",dd->documentation(),dd); + + step(compounddef_insert); + + // + files + writeInnerDirs(&dd->subDirs(),refid); + + // + files + writeInnerFiles(dd->getFiles(),refid); } +// kinds: page, example static void generateSqlite3ForPage(const PageDef *pd,bool isExample) { -#warning WorkInProgress + // + name + // + title + // + brief description + // + documentation (detailed description) + // + inbody documentation + // + sub pages + if (pd->isReference()) return; // skip external references. + + // TODO: do we more special handling if isExample? + + QCString qrefid = pd->getOutputFileBase(); + if (pd->getGroupDef()) + { + qrefid+=(QCString)"_"+pd->name(); + } + if (qrefid=="index") qrefid="indexpage"; // to prevent overwriting the generated index page. + + struct Refid refid = insertRefid(qrefid); + + // can omit a page that already has a refid + if(!refid.created && compounddefExists(refid)){return;} + + bindIntParameter(compounddef_insert,":rowid",refid.rowid); + // + name + bindTextParameter(compounddef_insert,":name",pd->name()); + + QCString title; + if (pd==Doxygen::mainPage) // main page is special + { + if (!pd->title().isEmpty() && pd->title().lower()!="notitle") + { + title = filterTitle(convertCharEntitiesToUTF8(Doxygen::mainPage->title())); + } + else + { + title = Config_getString(PROJECT_NAME); + } + } + else + { + SectionInfo *si = Doxygen::sectionDict->find(pd->name()); + if (si) + { + title = si->title; + } + + if(!title){title = pd->title();} + } + + // + title + bindTextParameter(compounddef_insert,":title",title,FALSE); + + bindTextParameter(compounddef_insert,":kind", isExample ? "example" : "page"); + + int file_id = insertPath(pd->getDefFileName()); + + bindIntParameter(compounddef_insert,":file_id",file_id); + bindIntParameter(compounddef_insert,":line",pd->getDefLine()); + bindIntParameter(compounddef_insert,":column",pd->getDefColumn()); + + // + brief description + getSQLDesc(compounddef_insert,":briefdescription",pd->briefDescription(),pd); + // + documentation (detailed description) + getSQLDesc(compounddef_insert,":detaileddescription",pd->documentation(),pd); + + step(compounddef_insert); + // + sub pages + writeInnerPages(pd->getSubPages(),refid); } @@ -1463,6 +2526,7 @@ static sqlite3* openDbConnection() QDir sqlite3Dir(outputDirectory); sqlite3 *db; int rc; + struct stat buf; rc = sqlite3_initialize(); if (rc != SQLITE_OK) @@ -1470,7 +2534,21 @@ static sqlite3* openDbConnection() msg("sqlite3_initialize failed\n"); return NULL; } - rc = sqlite3_open_v2(outputDirectory+"/doxygen_sqlite3.db", &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); + + + if (stat (outputDirectory+"/doxygen_sqlite3.db", &buf) == 0) + { + msg("doxygen_sqlite3.db already exists! aborting sqlite3 output generation!\n"); + msg("If you wish to re-generate the database, remove or archive the existing copy first.\n"); + return NULL; + } + + rc = sqlite3_open_v2( + outputDirectory+"/doxygen_sqlite3.db", + &db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + 0 + ); if (rc != SQLITE_OK) { sqlite3_close(db); @@ -1497,10 +2575,16 @@ void generateSqlite3() { return; } + +# ifdef SQLITE3_DEBUG + // debug: show all executed statements + sqlite3_trace(db, &sqlLog, NULL); +# endif + beginTransaction(db); pragmaTuning(db); - if (-1==initializeSchema(db)) + if (-1==initializeTables(db)) return; if ( -1 == prepareStatements(db) ) @@ -1509,9 +2593,11 @@ void generateSqlite3() return; } + recordMetadata(); + // + classes ClassSDict::Iterator cli(*Doxygen::classSDict); - ClassDef *cd; + const ClassDef *cd; for (cli.toFirst();(cd=cli.current());++cli) { msg("Generating Sqlite3 output for class %s\n",cd->name().data()); @@ -1520,7 +2606,7 @@ void generateSqlite3() // + namespaces NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict); - NamespaceDef *nd; + const NamespaceDef *nd; for (nli.toFirst();(nd=nli.current());++nli) { msg("Generating Sqlite3 output for namespace %s\n",nd->name().data()); @@ -1533,7 +2619,7 @@ void generateSqlite3() for (;(fn=fnli.current());++fnli) { FileNameIterator fni(*fn); - FileDef *fd; + const FileDef *fd; for (;(fd=fni.current());++fni) { msg("Generating Sqlite3 output for file %s\n",fd->name().data()); @@ -1543,7 +2629,7 @@ void generateSqlite3() // + groups GroupSDict::Iterator gli(*Doxygen::groupSDict); - GroupDef *gd; + const GroupDef *gd; for (;(gd=gli.current());++gli) { msg("Generating Sqlite3 output for group %s\n",gd->name().data()); @@ -1553,7 +2639,7 @@ void generateSqlite3() // + page { PageSDict::Iterator pdi(*Doxygen::pageSDict); - PageDef *pd=0; + const PageDef *pd=0; for (pdi.toFirst();(pd=pdi.current());++pdi) { msg("Generating Sqlite3 output for page %s\n",pd->name().data()); @@ -1563,7 +2649,7 @@ void generateSqlite3() // + dirs { - DirDef *dir; + const DirDef *dir; DirSDict::Iterator sdi(*Doxygen::directories); for (sdi.toFirst();(dir=sdi.current());++sdi) { @@ -1575,7 +2661,7 @@ void generateSqlite3() // + examples { PageSDict::Iterator pdi(*Doxygen::exampleSDict); - PageDef *pd=0; + const PageDef *pd=0; for (pdi.toFirst();(pd=pdi.current());++pdi) { msg("Generating Sqlite3 output for example %s\n",pd->name().data()); @@ -1590,6 +2676,11 @@ void generateSqlite3() generateSqlite3ForPage(Doxygen::mainPage,FALSE); } + // TODO: copied from initializeSchema; not certain if we should say/do more + // if there's a failure here? + if (-1==initializeViews(db)) + return; + endTransaction(db); } -- cgit v0.12 From eec2d3b6a9f80715fb590b0fb67b7fe87ba5fee7 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Sat, 13 Oct 2018 14:43:48 -0500 Subject: sqlite3: fix missing external_file view schema col --- src/sqlite3gen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sqlite3gen.cpp b/src/sqlite3gen.cpp index 8e236c1..a7d7f21 100644 --- a/src/sqlite3gen.cpp +++ b/src/sqlite3gen.cpp @@ -370,6 +370,7 @@ const char * table_schema[][2] = { ")\n" "as SELECT \n" "\tpath.rowid,\n" + "\tpath.found,\n" "\tpath.name\n" "FROM path WHERE path.type=1 AND path.local=0;\n" }, -- cgit v0.12 From e4f408eb4aad7fb035847616debe68fe115e2ae4 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Tue, 16 Oct 2018 19:40:13 -0500 Subject: sqlite3: require sqlite >= 3.9.0 The generated schema is malformed in versions below 3.9.0. --- CMakeLists.txt | 5 ++++- cmake/FindSQLite3.cmake | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e0fb55..45c2f2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,10 @@ find_package(BISON REQUIRED) find_package(Threads) if (sqlite3) - find_package(SQLite3 REQUIRED) + find_package(SQLite3 REQUIRED) + if (SQLITE3_VERSION VERSION_LESS 3.9.0) + message(SEND_ERROR "Doxygen requires at least sqlite3 version 3.9.0 (installed: ${SQLITE3_VERSION})") + endif() endif() find_package(Iconv REQUIRED) diff --git a/cmake/FindSQLite3.cmake b/cmake/FindSQLite3.cmake index 77b8eb4..45cc212 100644 --- a/cmake/FindSQLite3.cmake +++ b/cmake/FindSQLite3.cmake @@ -71,8 +71,16 @@ else (SQLITE3_LIBRARIES AND SQLITE3_INCLUDE_DIRS) endif (SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) if (SQLITE3_FOUND) + + # Extract version from header file + find_file(SQLITE3_HEADER "sqlite3.h" HINTS ${SQLITE3_INCLUDE_DIRS}) + if(SQLITE3_HEADER) + file(STRINGS "${SQLITE3_HEADER}" _DEF_TMP REGEX "^#define SQLITE_VERSION +\\\"[^\\\"]+\\\"") + string (REGEX REPLACE ".*\\\"(([0-9]+[.]?)+).*" "\\1" SQLITE3_VERSION "${_DEF_TMP}") + endif (SQLITE3_HEADER) + if (NOT Sqlite3_FIND_QUIETLY) - message(STATUS "Found Sqlite3: ${SQLITE3_LIBRARIES}") + message(STATUS "Found Sqlite3: ${SQLITE3_LIBRARIES} (found version \"${SQLITE3_VERSION}\")") endif (NOT Sqlite3_FIND_QUIETLY) else (SQLITE3_FOUND) if (Sqlite3_FIND_REQUIRED) -- cgit v0.12