1 #define UNICODE
2 #define PY_SSIZE_T_CLEAN
3 #include <Python.h>
4 
5 
6 #include <stdlib.h>
7 
8 #include <sqlite3ext.h>
9 SQLITE_EXTENSION_INIT1
10 
11 #ifdef _MSC_VER
12 #define MYEXPORT __declspec(dllexport)
13 #else
14 #define MYEXPORT __attribute__ ((visibility ("default")))
15 #endif
16 
17 // sortconcat {{{
18 
19 typedef struct {
20     unsigned char *val;
21     int index;
22     int length;
23 } SortConcatItem;
24 
25 typedef struct {
26     SortConcatItem **vals;
27     int count;
28     int length;
29 } SortConcatList;
30 
sort_concat_step(sqlite3_context * context,int argc,sqlite3_value ** argv)31 static void sort_concat_step(sqlite3_context *context, int argc, sqlite3_value **argv) {
32     const unsigned char *val;
33     int idx, sz;
34     SortConcatList *list;
35 
36     assert(argc == 2);
37 
38     list = (SortConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
39     if (list == NULL) return;
40 
41     if (list->vals == NULL) {
42         list->vals = (SortConcatItem**)calloc(100, sizeof(SortConcatItem*));
43         if (list->vals == NULL) return;
44         list->length = 100;
45         list->count = 0;
46     }
47 
48     if (list->count == list->length) {
49         list->vals = (SortConcatItem**)realloc(list->vals, sizeof(SortConcatItem*)*(list->length + 100));
50         if (list->vals == NULL) return;
51         list->length = list->length + 100;
52     }
53 
54     list->vals[list->count] = (SortConcatItem*)calloc(1, sizeof(SortConcatItem));
55     if (list->vals[list->count] == NULL) return;
56 
57     idx = sqlite3_value_int(argv[0]);
58     val = sqlite3_value_text(argv[1]);
59     sz  = sqlite3_value_bytes(argv[1]);
60     if (idx == 0 || val == NULL || sz == 0) {free(list->vals[list->count]); return;}
61 
62 
63 
64     list->vals[list->count]->val = (unsigned char*)calloc(sz, sizeof(unsigned char));
65     if (list->vals[list->count]->val == NULL)
66         {free(list->vals[list->count]); return;}
67     list->vals[list->count]->index = idx;
68     list->vals[list->count]->length = sz;
69     memcpy(list->vals[list->count]->val, val, sz);
70     list->count = list->count + 1;
71 
72 }
73 
sort_concat_free(SortConcatList * list)74 static void sort_concat_free(SortConcatList *list) {
75     int i;
76     if (list == NULL) return;
77     for (i = 0; i < list->count; i++) {
78         free(list->vals[i]->val);
79         free(list->vals[i]);
80     }
81     free(list->vals);
82 }
83 
sort_concat_cmp(const void * a_,const void * b_)84 static int sort_concat_cmp(const void *a_, const void *b_) {
85     return (*((SortConcatItem**)a_))->index - (*((SortConcatItem**)b_))->index;
86 }
87 
sort_concat_do_finalize(SortConcatList * list,const unsigned char join)88 static unsigned char* sort_concat_do_finalize(SortConcatList *list, const unsigned char join) {
89     unsigned char *ans, *pos;
90     unsigned int sz = 0, i;
91 
92     for (i = 0; i < list->count; i++) {
93         sz += list->vals[i]->length;
94     }
95     sz += list->count;
96 
97     ans = (unsigned char *) calloc(sz, sizeof(unsigned char));
98     if (ans == NULL) return ans;
99 
100     pos = ans;
101     for (i = 0; i < list->count; i++) {
102         if (list->vals[i]->length > 0) {
103             memcpy(pos, list->vals[i]->val, list->vals[i]->length);
104             pos += list->vals[i]->length;
105             if (i < list->count -1) { *pos = join; pos += 1; }
106         }
107     }
108 
109     return ans;
110 
111 }
112 
sort_concat_finalize(sqlite3_context * context)113 static void sort_concat_finalize(sqlite3_context *context) {
114     SortConcatList *list;
115     unsigned char *ans;
116 
117     list = (SortConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
118 
119     if (list != NULL && list->vals != NULL && list->count > 0) {
120         qsort(list->vals, list->count, sizeof(list->vals[0]), sort_concat_cmp);
121         ans = sort_concat_do_finalize(list, ',');
122         if (ans != NULL) sqlite3_result_text(context, (char*)ans, -1, SQLITE_TRANSIENT);
123         free(ans);
124         sort_concat_free(list);
125     }
126 }
127 
sort_concat_finalize2(sqlite3_context * context)128 static void sort_concat_finalize2(sqlite3_context *context) {
129     SortConcatList *list;
130     unsigned char *ans;
131 
132     list = (SortConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
133 
134     if (list != NULL && list->vals != NULL && list->count > 0) {
135         qsort(list->vals, list->count, sizeof(list->vals[0]), sort_concat_cmp);
136         ans = sort_concat_do_finalize(list, '|');
137         if (ans != NULL) sqlite3_result_text(context, (char*)ans, -1, SQLITE_TRANSIENT);
138         free(ans);
139         sort_concat_free(list);
140     }
141 
142 }
143 
sort_concat_finalize3(sqlite3_context * context)144 static void sort_concat_finalize3(sqlite3_context *context) {
145     SortConcatList *list;
146     unsigned char *ans;
147 
148     list = (SortConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
149 
150     if (list != NULL && list->vals != NULL && list->count > 0) {
151         qsort(list->vals, list->count, sizeof(list->vals[0]), sort_concat_cmp);
152         ans = sort_concat_do_finalize(list, '&');
153         if (ans != NULL) sqlite3_result_text(context, (char*)ans, -1, SQLITE_TRANSIENT);
154         free(ans);
155         sort_concat_free(list);
156     }
157 
158 }
159 
160 // }}}
161 
162 // identifiers_concat {{{
163 
164 typedef struct {
165     char *val;
166     size_t length;
167 } IdentifiersConcatItem;
168 
169 typedef struct {
170     IdentifiersConcatItem **vals;
171     size_t count;
172     size_t length;
173 } IdentifiersConcatList;
174 
identifiers_concat_step(sqlite3_context * context,int argc,sqlite3_value ** argv)175 static void identifiers_concat_step(sqlite3_context *context, int argc, sqlite3_value **argv) {
176     const char *key, *val;
177     size_t len = 0;
178     IdentifiersConcatList *list;
179 
180     assert(argc == 2);
181 
182     list = (IdentifiersConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
183     if (list == NULL) return;
184 
185     if (list->vals == NULL) {
186         list->vals = (IdentifiersConcatItem**)calloc(100, sizeof(IdentifiersConcatItem*));
187         if (list->vals == NULL) return;
188         list->length = 100;
189         list->count = 0;
190     }
191 
192     if (list->count == list->length) {
193         list->vals = (IdentifiersConcatItem**)realloc(list->vals, sizeof(IdentifiersConcatItem*)*(list->length + 100));
194         if (list->vals == NULL) return;
195         list->length = list->length + 100;
196     }
197 
198     list->vals[list->count] = (IdentifiersConcatItem*)calloc(1, sizeof(IdentifiersConcatItem));
199     if (list->vals[list->count] == NULL) return;
200 
201     key = (char*) sqlite3_value_text(argv[0]);
202     val = (char*) sqlite3_value_text(argv[1]);
203     if (key == NULL || val == NULL) {return;}
204     len = strlen(key) + strlen(val) + 1;
205 
206     list->vals[list->count]->val = (char*)calloc(len+1, sizeof(char));
207     if (list->vals[list->count]->val == NULL) return;
208     snprintf(list->vals[list->count]->val, len+1, "%s:%s", key, val);
209     list->vals[list->count]->length = len;
210 
211     list->count = list->count + 1;
212 
213 }
214 
215 
identifiers_concat_finalize(sqlite3_context * context)216 static void identifiers_concat_finalize(sqlite3_context *context) {
217     IdentifiersConcatList *list;
218     IdentifiersConcatItem *item;
219     char *ans, *pos;
220     size_t sz = 0, i;
221 
222     list = (IdentifiersConcatList*) sqlite3_aggregate_context(context, sizeof(*list));
223     if (list == NULL || list->vals == NULL || list->count < 1) return;
224 
225     for (i = 0; i < list->count; i++) {
226         sz += list->vals[i]->length;
227     }
228     sz += list->count; // Space for commas
229     ans = (char*)calloc(sz+2, sizeof(char));
230     if (ans == NULL) return;
231 
232     pos = ans;
233 
234     for (i = 0; i < list->count; i++) {
235         item = list->vals[i];
236         if (item == NULL || item->val == NULL) continue;
237         memcpy(pos, item->val, item->length);
238         pos += item->length;
239         *pos = ',';
240         pos += 1;
241         free(item->val);
242         free(item);
243     }
244     *(pos-1) = 0; // Remove trailing comma
245     sqlite3_result_text(context, ans, -1, SQLITE_TRANSIENT);
246     free(ans);
247     free(list->vals);
248 }
249 
250 // }}}
251 
sqlite3_extension_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)252 MYEXPORT int sqlite3_extension_init(
253     sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi){
254   SQLITE_EXTENSION_INIT2(pApi);
255   sqlite3_create_function(db, "sortconcat", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize);
256   sqlite3_create_function(db, "sortconcat_bar", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize2);
257   sqlite3_create_function(db, "sortconcat_amper", 2, SQLITE_UTF8, NULL, NULL, sort_concat_step, sort_concat_finalize3);
258   sqlite3_create_function(db, "identifiers_concat", 2, SQLITE_UTF8, NULL, NULL, identifiers_concat_step, identifiers_concat_finalize);
259   return 0;
260 }
261 
262 static PyObject *
sqlite_custom_init_funcs(PyObject * self,PyObject * args)263 sqlite_custom_init_funcs(PyObject *self, PyObject *args) {
264     Py_RETURN_NONE;
265 }
266 
267 static char sqlite_custom_doc[] = "Implementation of custom sqlite methods in C for speed.";
268 
269 static PyMethodDef sqlite_custom_methods[] = {
270     {"init_funcs", sqlite_custom_init_funcs, METH_VARARGS,
271         "init_funcs()\n\nInitialize module."
272     },
273 
274     {NULL, NULL, 0, NULL}
275 };
276 
277 static int
exec_module(PyObject * module)278 exec_module(PyObject *module) { return 0; }
279 
280 static PyModuleDef_Slot slots[] = { {Py_mod_exec, exec_module}, {0, NULL} };
281 
282 static struct PyModuleDef module_def = {
283     .m_base     = PyModuleDef_HEAD_INIT,
284     .m_name     = "sqlite_custom",
285     .m_doc      = sqlite_custom_doc,
286     .m_methods  = sqlite_custom_methods,
287     .m_slots    = slots,
288 };
289 
PyInit_sqlite_custom(void)290 CALIBRE_MODINIT_FUNC PyInit_sqlite_custom(void) { return PyModuleDef_Init(&module_def); }
291