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* 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 32 memfclose(MEMFILE* mf) { 33 if (mf->data) free(mf->data); 34 free(mf); 35 } 36 37 size_t 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* 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 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 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 79 static int my_disconnect(sqlite3_vtab *pVTab) { 80 sqlite3_free(pVTab); 81 return SQLITE_OK; 82 } 83 84 static int 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 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 143 my_close(cursor *c) { 144 delete c->rows; 145 sqlite3_free(c); 146 return SQLITE_OK; 147 } 148 149 static int 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 156 my_next(cursor *c) { 157 c->index++; 158 return SQLITE_OK; 159 } 160 161 static int 162 my_eof(cursor *c) { 163 return c->index >= c->rows->get<picojson::array>().size() ? 1 : 0; 164 } 165 166 static int 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 190 my_rowid(cursor *c, sqlite3_int64 *pRowid) { 191 *pRowid = c->index; 192 return SQLITE_OK; 193 } 194 195 static int 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 224 destructor(void *arg) { 225 return; 226 } 227 228 229 extern "C" { 230 231 EXPORT int 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