1 #include <string>
2 #include <sstream>
3 #include <sqlite3.h>
4 #include <sqlite3ext.h>
5 #include <curl/curl.h>
6 #include "picojson.h"
7
8 #ifdef _WIN32
9 # define EXPORT __declspec(dllexport)
10 #else
11 # define EXPORT
12 #endif
13
14 SQLITE_EXTENSION_INIT1;
15
16 typedef struct {
17 char* data; // response data from server
18 size_t size; // response size of data
19 } MEMFILE;
20
21 MEMFILE*
memfopen()22 memfopen() {
23 MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
24 if (mf) {
25 mf->data = NULL;
26 mf->size = 0;
27 }
28 return mf;
29 }
30
31 void
memfclose(MEMFILE * mf)32 memfclose(MEMFILE* mf) {
33 if (mf->data) free(mf->data);
34 free(mf);
35 }
36
37 size_t
memfwrite(char * ptr,size_t size,size_t nmemb,void * stream)38 memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
39 MEMFILE* mf = (MEMFILE*) stream;
40 int block = size * nmemb;
41 if (!mf) return block; // through
42 if (!mf->data)
43 mf->data = (char*) malloc(block);
44 else
45 mf->data = (char*) realloc(mf->data, mf->size + block);
46 if (mf->data) {
47 memcpy(mf->data + mf->size, ptr, block);
48 mf->size += block;
49 }
50 return block;
51 }
52
53 char*
memfstrdup(MEMFILE * mf)54 memfstrdup(MEMFILE* mf) {
55 char* buf;
56 if (mf->size == 0) return NULL;
57 buf = (char*) malloc(mf->size + 1);
58 memcpy(buf, mf->data, mf->size);
59 buf[mf->size] = 0;
60 return buf;
61 }
62
63 static int
my_connect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVTab,char ** c)64 my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) {
65 std::stringstream ss;
66 ss << "CREATE TABLE " << argv[0]
67 << "(id int, full_name text, description text, html_url text)";
68 int rc = sqlite3_declare_vtab(db, ss.str().c_str());
69 *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
70 memset(*ppVTab, 0, sizeof(sqlite3_vtab));
71 return rc;
72 }
73
74 static int
my_create(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVTab,char ** c)75 my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) {
76 return my_connect(db, pAux, argc, argv, ppVTab, c);
77 }
78
my_disconnect(sqlite3_vtab * pVTab)79 static int my_disconnect(sqlite3_vtab *pVTab) {
80 sqlite3_free(pVTab);
81 return SQLITE_OK;
82 }
83
84 static int
my_destroy(sqlite3_vtab * pVTab)85 my_destroy(sqlite3_vtab *pVTab) {
86 sqlite3_free(pVTab);
87 return SQLITE_OK;
88 }
89
90 typedef struct {
91 sqlite3_vtab_cursor base;
92 int index;
93 picojson::value* rows;
94 } cursor;
95
96 static int
my_open(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)97 my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
98 MEMFILE* mf;
99 CURL* curl;
100 char* json;
101 CURLcode res = CURLE_OK;
102 char error[CURL_ERROR_SIZE] = {0};
103 char* cert_file = getenv("SSL_CERT_FILE");
104
105 mf = memfopen();
106 curl = curl_easy_init();
107 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
108 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
109 curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.29.0");
110 curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repositories");
111 if (cert_file)
112 curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
113 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
114 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
115 curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
116 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
117 res = curl_easy_perform(curl);
118 curl_easy_cleanup(curl);
119 if (res != CURLE_OK) {
120 std::cerr << error << std::endl;
121 return SQLITE_FAIL;
122 }
123
124 picojson::value* v = new picojson::value;
125 std::string err;
126 picojson::parse(*v, mf->data, mf->data + mf->size, &err);
127 memfclose(mf);
128
129 if (!err.empty()) {
130 delete v;
131 std::cerr << err << std::endl;
132 return SQLITE_FAIL;
133 }
134
135 cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor));
136 c->rows = v;
137 c->index = 0;
138 *ppCursor = &c->base;
139 return SQLITE_OK;
140 }
141
142 static int
my_close(cursor * c)143 my_close(cursor *c) {
144 delete c->rows;
145 sqlite3_free(c);
146 return SQLITE_OK;
147 }
148
149 static int
my_filter(cursor * c,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)150 my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
151 c->index = 0;
152 return SQLITE_OK;
153 }
154
155 static int
my_next(cursor * c)156 my_next(cursor *c) {
157 c->index++;
158 return SQLITE_OK;
159 }
160
161 static int
my_eof(cursor * c)162 my_eof(cursor *c) {
163 return c->index >= c->rows->get<picojson::array>().size() ? 1 : 0;
164 }
165
166 static int
my_column(cursor * c,sqlite3_context * ctxt,int i)167 my_column(cursor *c, sqlite3_context *ctxt, int i) {
168 picojson::value v = c->rows->get<picojson::array>()[c->index];
169 picojson::object row = v.get<picojson::object>();
170 const char* p = NULL;
171 switch (i) {
172 case 0:
173 p = row["id"].to_str().c_str();
174 break;
175 case 1:
176 p = row["full_name"].to_str().c_str();
177 break;
178 case 2:
179 p = row["description"].to_str().c_str();
180 break;
181 case 3:
182 p = row["html_url"].to_str().c_str();
183 break;
184 }
185 sqlite3_result_text(ctxt, strdup(p), strlen(p), free);
186 return SQLITE_OK;
187 }
188
189 static int
my_rowid(cursor * c,sqlite3_int64 * pRowid)190 my_rowid(cursor *c, sqlite3_int64 *pRowid) {
191 *pRowid = c->index;
192 return SQLITE_OK;
193 }
194
195 static int
my_bestindex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)196 my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
197 return SQLITE_OK;
198 }
199
200 static const sqlite3_module module = {
201 0,
202 my_create,
203 my_connect,
204 my_bestindex,
205 my_disconnect,
206 my_destroy,
207 my_open,
208 (int (*)(sqlite3_vtab_cursor *)) my_close,
209 (int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter,
210 (int (*)(sqlite3_vtab_cursor *)) my_next,
211 (int (*)(sqlite3_vtab_cursor *)) my_eof,
212 (int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column,
213 (int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid,
214 NULL, // my_update
215 NULL, // my_begin
216 NULL, // my_sync
217 NULL, // my_commit
218 NULL, // my_rollback
219 NULL, // my_findfunction
220 NULL, // my_rename
221 };
222
223 static void
destructor(void * arg)224 destructor(void *arg) {
225 return;
226 }
227
228
229 extern "C" {
230
231 EXPORT int
sqlite3_extension_init(sqlite3 * db,char ** errmsg,const sqlite3_api_routines * api)232 sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
233 SQLITE_EXTENSION_INIT2(api);
234 sqlite3_create_module_v2(db, "github", &module, NULL, destructor);
235 return 0;
236 }
237
238 }
239