Skip to content

Commit a988ef9

Browse files
committed
Merge conflicts
2 parents 136f352 + 7c1e08b commit a988ef9

7 files changed

+307
-102
lines changed

Diff for: CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_library(clangql
1515
src/Index.pb.cc
1616
src/Service.grpc.pb.cc
1717
src/Service.pb.cc
18+
src/RefsTable.cc
1819
src/RelationsTable.cc
1920
src/SymbolsTable.cc
2021
src/Module.cc)

Diff for: README.md

+27-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Afterwards, you can connect to a codebase by instantiating the various virtual t
1717
sqlite> CREATE VIRTUAL TABLE llvm_symbols USING clangql (symbols, clangd-index.llvm.org:5900);
1818
sqlite> CREATE VIRTUAL TABLE llvm_base_of USING clangql (base_of, clangd-index.llvm.org:5900);
1919
sqlite> CREATE VIRTUAL TABLE llvm_overridden_by USING clangql (overridden_by, clangd-index.llvm.org:5900);
20+
sqlite> CREATE VIRTUAL TABLE llvm_refs USING clangql (refs, clangd-index.llvm.org:5900);
2021

2122
You can then query the codebase as if it was a regular table (some caveats apply, read the last point to learn more):
2223

@@ -54,13 +55,25 @@ As another example, searching all the subclasses of a particular class:
5455
MCAsmInfoELF llvm:: llvm/include/llvm/MC/MCAsmInfoELF.h
5556
MCAsmInfoCOFF llvm:: llvm/include/llvm/MC/MCAsmInfoCOFF.h
5657

57-
In general, for each codebase three different virtual tables can be queried: a `symbols` table will contain information about every symbol in the codebase, a `base_of` table will contain information about what symbols are base classes of what symbols, and a `overridden_by` table will contain information about what symbols are overridden by what symbols.
58+
Searching for all declarations inside of the `std` namespace:
59+
60+
sqlite> SELECT decl.Name FROM llvm_symbols AS decl INNER JOIN llvm_refs AS ref ON ref.SymbolId = decl.Id WHERE decl.Scope = "std::" AND ref.Declaration = 1;
61+
Name
62+
---------------------
63+
align_val_t
64+
__unexpected
65+
is_execution_policy
66+
__terminate
67+
is_execution_policy_v
68+
69+
In general, for each codebase four different virtual tables can be queried: a `symbols` table will contain information about every symbol in the codebase, a `base_of` table will contain information about what symbols are base classes of what symbols, a `overridden_by` table will contain information about what symbols are overridden by what symbols, and a `refs` table will contain information about symbol references.
5870

5971
The syntax for instantiating the tables is the following:
6072

6173
CREATE VIRTUAL TABLE my_symbols USING clangql (symbols, host:port);
6274
CREATE VIRTUAL TABLE my_base_of USING clangql (base_of, host:port);
6375
CREATE VIRTUAL TABLE my_overridden_by USING clangql (overridden_by, host:port);
76+
CREATE VIRTUAL TABLE my_refs USING clangql (refs, host:port);
6477

6578
`my_*` names are not important and can be anything, the first parameter to the creation of the virtual tables is important and must be left as-is, the second parameter is the connection string. Currently, only unencrypted gRPC connections are supported.
6679

@@ -82,6 +95,15 @@ The meaning is as follows: if a row `(S, O)` is present in `base_of`, then `S` i
8295

8396
Please note that it is only possible to query these two tables by their `Subject`, querying by `Object` is not possible due to limitations in the clangd protocol.
8497

98+
The schema of `refs` tables is equivalent to
99+
100+
CREATE TABLE vtable(SymbolId TEXT, Declaration INT,
101+
Definition INT, Reference INT, Spelled INT,
102+
Path TEXT, StartLine INT, StartCol INT,
103+
EndLine INT, EndCol INT)
104+
105+
Please note that querying `refs` without a `SymbolId` will return 0 rows.
106+
85107
## How do I build it?
86108

87109
ClangQL uses CMake, Protocol Buffers and gRPC. On Windows I used vcpkg to manage the two dependencies. I'm afraid I'm not knowledgeable enough with Linux and/or macOS to give precise indications on how to build it there, but I'm guessing that as long as you have the correct development packages installed and visible on your system, CMake will be able to locate them.
@@ -90,7 +112,7 @@ Once the repository is cloned, run:
90112

91113
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DVCPKG_TARGET_TRIPLET=x64-windows-static-md -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake
92114

93-
to configure the build. Adjust `CMAKE_BUILD_TYPE`, `VCPKG_TARGET_TRIPLET`, `CMAKE_TOOLCHAIN_FILE` and the generator type to suit your system and needs the best. Please not that the `sqlite` CLI tool and the extension must have the same bitness, at least on Windows. A 32-bit CLI (such as the precompiled one from SQLite.org) _will not_ load a 64-bit extension.
115+
to configure the build. Adjust `CMAKE_BUILD_TYPE`, `VCPKG_TARGET_TRIPLET`, `CMAKE_TOOLCHAIN_FILE` and the generator type to suit your system and needs the best. Please note that the `sqlite` CLI tool and the extension must have the same bitness, at least on Windows. A 32-bit CLI (such as the precompiled one from SQLite.org) _will not_ load a 64-bit extension.
94116

95117
Once configured, run:
96118

@@ -108,6 +130,8 @@ Also, there is currently no way to i.e. obtain all possible relations between tw
108130

109131
Not all queries are equally fast: querying on symbol id, name or scope is fast, everything else needs to happen client side and is potentially slow.
110132

111-
Similarly, when querying the `base_of` or `overridden_by` relations, only one of the two directions is possible, the other is not currently possible due to protocol limitations.
133+
Similarly, when querying the `base_of` or `overridden_by` relations, only one of the two directions is possible, the other is not currently possible due to protocol limitations. Also, not specifying a `Subject` will result in 0 rows being returned.
134+
135+
Querying `refs` without specifying a `SymbolId` will result in 0 rows being produced. Specifying any one of `Definition`, `Declaration`, `Reference` or `Spelled` will generate more specific requests to the clangd server, all other fields are scanned client side.
112136

113137
Error checking is nonexistant. This is not ready for production use and was mostly made for fun, to explore to what extent the clangd interface was suitable for use with SQLite, and to learn about the SQLite virtual table system.

Diff for: src/ClangQLModule.cc

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
SQLITE_EXTENSION_INIT3
33
#include "SymbolsTable.hpp"
44
#include "RelationsTable.hpp"
5+
#include "RefsTable.hpp"
56
#include <grpcpp/grpcpp.h>
67

78
#include <string>
@@ -20,6 +21,8 @@ static std::unique_ptr<VirtualTable> InitTable(sqlite3* db, int argc, const char
2021
return std::make_unique<RelationsTable>(db, SymbolIndex::NewStub(channel), BaseOf);
2122
} else if(table_type == "overridden_by") {
2223
return std::make_unique<RelationsTable>(db, SymbolIndex::NewStub(channel), OverriddenBy);
24+
} else if(table_type == "refs") {
25+
return std::make_unique<RefsTable>(db, SymbolIndex::NewStub(channel));
2326
} else {
2427
return nullptr;
2528
}

Diff for: src/RefsTable.cc

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#include "RefsTable.hpp"
2+
#include "IResultStream.hpp"
3+
#include "sqlite3ext.h"
4+
SQLITE_EXTENSION_INIT3
5+
#include "VirtualTableCursor.hpp"
6+
7+
#include <string>
8+
#include <vector>
9+
10+
using namespace clang::clangd::remote;
11+
using clang::clangd::remote::v1::SymbolIndex;
12+
13+
enum RefKind {
14+
Kind_Unknown = 0,
15+
Kind_Declaration = 1 << 0,
16+
Kind_Definition = 1 << 1,
17+
Kind_Reference = 1 << 2,
18+
Kind_Spelled = 1 << 3,
19+
Kind_All = Kind_Declaration | Kind_Definition | Kind_Reference | Kind_Spelled
20+
};
21+
22+
class RefStream final : public IResultStream<Ref> {
23+
grpc::ClientContext m_ctx;
24+
std::unique_ptr<grpc::ClientReader<RefsReply>> m_replyReader;
25+
RefsRequest m_req;
26+
RefsReply m_reply;
27+
public:
28+
RefStream(SymbolIndex::Stub& stub, RefsRequest& req)
29+
: m_replyReader(stub.Refs(&m_ctx, req))
30+
, m_req(req) { }
31+
32+
const Ref& Current() override {
33+
return m_reply.stream_result();
34+
}
35+
36+
bool Next() override {
37+
return m_replyReader->Read(&m_reply) && m_reply.has_stream_result();
38+
}
39+
40+
const std::string& Id() {
41+
return m_req.ids(0);
42+
}
43+
};
44+
45+
enum {
46+
CONSTR_ID = 1,
47+
CONSTR_DEF = 2,
48+
CONSTR_DECL = 4,
49+
CONSTR_REF = 8,
50+
CONSTR_SPE = 16
51+
};
52+
53+
class RefsCursor final : public VirtualTableCursor {
54+
SymbolIndex::Stub& m_stub;
55+
bool m_eof = false;
56+
std::unique_ptr<RefStream> m_stream = nullptr;
57+
public:
58+
RefsCursor(SymbolIndex::Stub& stub)
59+
: m_stub(stub) {}
60+
61+
int Eof() override { return m_eof; }
62+
int Next() override {
63+
m_eof = !m_stream->Next();
64+
return SQLITE_OK;
65+
}
66+
int Close() override {
67+
return SQLITE_OK;
68+
}
69+
sqlite3_int64 RowId() override { return 0; }
70+
int Column(sqlite3_context *ctx, int idxCol) override {
71+
auto& Current = m_stream->Current();
72+
switch(idxCol) {
73+
#define SET_RES2(field1, field2) do { \
74+
if(Current.has_ ## field1() && Current.field1().has_ ## field2()) { \
75+
sqlite3_result_text(ctx, Current.field1().field2().c_str(), -1, SQLITE_TRANSIENT); \
76+
} else { sqlite3_result_null(ctx); } } while(0)
77+
#define SET_RES3(field1, field2, field3) do { \
78+
if(Current.has_ ## field1() && Current.field1().has_ ## field2() && Current.field1().field2().has_ ## field3()) { \
79+
sqlite3_result_int(ctx, Current.field1().field2().field3()); \
80+
} else { sqlite3_result_null(ctx); } } while(0)
81+
82+
case 0: sqlite3_result_text(ctx, m_stream->Id().c_str(), -1, SQLITE_TRANSIENT); break;
83+
case 1: sqlite3_result_int(ctx, (Current.kind() & Kind_Declaration) == Kind_Declaration); break;
84+
case 2: sqlite3_result_int(ctx, (Current.kind() & Kind_Definition) == Kind_Definition); break;
85+
case 3: sqlite3_result_int(ctx, (Current.kind() & Kind_Reference) == Kind_Reference); break;
86+
case 4: sqlite3_result_int(ctx, (Current.kind() & Kind_Spelled) == Kind_Spelled); break;
87+
case 5: SET_RES2(location, file_path); break;
88+
case 6: SET_RES3(location, start, line); break;
89+
case 7: SET_RES3(location, start, column); break;
90+
case 8: SET_RES3(location, end, line); break;
91+
case 9: SET_RES3(location, end, column); break;
92+
#undef SET_RES2
93+
#undef SET_RES3
94+
}
95+
return SQLITE_OK;
96+
}
97+
int Filter(int idxNum, const char *idxStr, int argc, sqlite3_value **argv) override {
98+
if(idxNum) {
99+
RefsRequest req;
100+
int argvIndex = 0;
101+
int kind = Kind_All;
102+
103+
if(idxNum & CONSTR_ID) {
104+
req.add_ids((const char*)sqlite3_value_text(argv[argvIndex++]));
105+
}
106+
107+
if(idxNum & CONSTR_DEF) {
108+
if(!sqlite3_value_int(argv[argvIndex++])) {
109+
kind &= !Kind_Definition;
110+
}
111+
}
112+
113+
if(idxNum & CONSTR_DECL) {
114+
if(!sqlite3_value_int(argv[argvIndex++])) {
115+
kind &= !Kind_Declaration;
116+
}
117+
}
118+
119+
if(idxNum & CONSTR_REF) {
120+
if(!sqlite3_value_int(argv[argvIndex++])) {
121+
kind &= !Kind_Reference;
122+
}
123+
}
124+
125+
if(idxNum & CONSTR_SPE) {
126+
if(!sqlite3_value_int(argv[argvIndex++])) {
127+
kind &= !Kind_Spelled;
128+
}
129+
}
130+
131+
req.set_filter(kind);
132+
m_stream = std::make_unique<RefStream>(m_stub, req);
133+
return Next();
134+
} else {
135+
m_eof = true;
136+
return SQLITE_OK;
137+
}
138+
}
139+
};
140+
141+
constexpr const char* schema = R"cpp(
142+
CREATE TABLE vtable(SymbolId TEXT, Declaration INT,
143+
Definition INT, Reference INT, Spelled INT,
144+
Path TEXT, StartLine INT, StartCol INT,
145+
EndLine INT, EndCol INT,
146+
PRIMARY KEY (
147+
SymbolId, Declaration, Definition, Reference, Spelled,
148+
Path, StartLine, StartCol, EndLine, EndCol))
149+
WITHOUT ROWID)cpp";
150+
151+
RefsTable::RefsTable(sqlite3* db, std::unique_ptr<SymbolIndex::Stub> stub)
152+
: m_stub(std::move(stub)) {
153+
int err = sqlite3_declare_vtab(db, schema);
154+
if(err != SQLITE_OK) {
155+
auto errmsg = sqlite3_errmsg(db);
156+
throw std::exception(errmsg);
157+
}
158+
}
159+
160+
int RefsTable::BestIndex(sqlite3_index_info* info) {
161+
int argvIndex = 0;
162+
163+
// Check for id
164+
for(int i = 0; i < info->nConstraint; i++) {
165+
auto& constraint = info->aConstraint[i];
166+
if(!constraint.usable || constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) continue;
167+
if(constraint.iColumn == 0) {
168+
info->aConstraintUsage[i].argvIndex = ++argvIndex;
169+
info->idxNum |= CONSTR_ID;
170+
info->estimatedCost = 1;
171+
break;
172+
}
173+
}
174+
175+
// Check for def
176+
for(int i = 0; i < info->nConstraint; i++) {
177+
auto& constraint = info->aConstraint[i];
178+
if(!constraint.usable || constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) continue;
179+
if(constraint.iColumn == 1) {
180+
info->aConstraintUsage[i].argvIndex = ++argvIndex;
181+
info->idxNum |= CONSTR_DEF;
182+
break;
183+
}
184+
}
185+
186+
// Check for decl
187+
for(int i = 0; i < info->nConstraint; i++) {
188+
auto& constraint = info->aConstraint[i];
189+
if(!constraint.usable || constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) continue;
190+
if(constraint.iColumn == 2) {
191+
info->aConstraintUsage[i].argvIndex = ++argvIndex;
192+
info->idxNum |= CONSTR_DEF;
193+
break;
194+
}
195+
}
196+
197+
// Check for ref
198+
for(int i = 0; i < info->nConstraint; i++) {
199+
auto& constraint = info->aConstraint[i];
200+
if(!constraint.usable || constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) continue;
201+
if(constraint.iColumn == 3) {
202+
info->aConstraintUsage[i].argvIndex = ++argvIndex;
203+
info->idxNum |= CONSTR_REF;
204+
break;
205+
}
206+
}
207+
208+
// Check for spelled
209+
for(int i = 0; i < info->nConstraint; i++) {
210+
auto& constraint = info->aConstraint[i];
211+
if(!constraint.usable || constraint.op != SQLITE_INDEX_CONSTRAINT_EQ) continue;
212+
if(constraint.iColumn == 4) {
213+
info->aConstraintUsage[i].argvIndex = ++argvIndex;
214+
info->idxNum |= CONSTR_SPE;
215+
break;
216+
}
217+
}
218+
219+
return SQLITE_OK;
220+
}
221+
222+
std::unique_ptr<VirtualTableCursor> RefsTable::Open() {
223+
return std::make_unique<RefsCursor>(*m_stub);
224+
}
225+
226+
int RefsTable::FindFunction(int nArg, const std::string& name, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg) { return 0; }
227+
228+
int RefsTable::Disconnect() {
229+
return SQLITE_OK;
230+
}
231+
232+
int RefsTable::Destroy() {
233+
return SQLITE_OK;
234+
}

Diff for: src/RefsTable.hpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef REFTABLE_HPP
2+
#define REFTABLE_HPP
3+
#include "sqlite3ext.h"
4+
#include "VirtualTable.hpp"
5+
#include "Service.grpc.pb.h"
6+
7+
class RefsTable : public VirtualTable {
8+
std::unique_ptr<clang::clangd::remote::v1::SymbolIndex::Stub> m_stub;
9+
10+
public:
11+
RefsTable(sqlite3* db, std::unique_ptr<clang::clangd::remote::v1::SymbolIndex::Stub> stub);
12+
13+
virtual int BestIndex(sqlite3_index_info* info) override;
14+
virtual std::unique_ptr<VirtualTableCursor> Open() override;
15+
virtual int FindFunction(int nArg, const std::string& name, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg) override;
16+
virtual int Disconnect() override;
17+
virtual int Destroy() override;
18+
};
19+
20+
#endif

Diff for: src/RelationsTable.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ SQLITE_EXTENSION_INIT3
66

77
#include <string>
88
#include <vector>
9+
#include <chrono>
910

1011
using namespace clang::clangd::remote;
1112
using clang::clangd::remote::v1::SymbolIndex;
@@ -81,8 +82,7 @@ class RelationsCursor final : public VirtualTableCursor {
8182
}
8283

8384
m_stream = std::make_unique<RelationStream>(m_stub, req);
84-
Next();
85-
return SQLITE_OK;
85+
return Next();
8686
}
8787
};
8888

0 commit comments

Comments
 (0)