Tue, 09 Dec 2025 18:24:48 +0100
implement json primitives serialization
| 23 | 1 | /* |
| 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | |
| 3 | * | |
| 4 | * Copyright 2025 Olaf Wintermann. All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions are met: | |
| 8 | * | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * | |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 13 | * notice, this list of conditions and the following disclaimer in the | |
| 14 | * documentation and/or other materials provided with the distribution. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 26 | * POSSIBLIITY OF SUCH DAMAGE. | |
| 27 | */ | |
| 28 | ||
| 29 | #include "database.h" | |
| 30 | ||
| 31 | #include <stdio.h> | |
| 32 | #include <stdlib.h> | |
| 33 | #include <errno.h> | |
| 34 | #include <unistd.h> | |
| 35 | ||
| 36 | #include <cx/buffer.h> | |
| 37 | #include <cx/streams.h> | |
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
38 | #include <cx/mempool.h> |
| 23 | 39 | |
| 40 | #define TEST_DB "test.db" | |
| 41 | #define TEST_DATA_FILE "testdata.sql" | |
| 42 | ||
| 43 | ||
| 44 | // create test.db and execute testdata script | |
| 45 | int init_test_db(void) { | |
| 46 | sqlite3 *db; | |
| 47 | char *err_msg = NULL; | |
| 48 | ||
| 49 | FILE *f = fopen(TEST_DATA_FILE, "r"); | |
| 50 | if(!f) { | |
| 51 | fprintf(stderr, "Cannot open test data file %s: %s\n", TEST_DATA_FILE, strerror(errno)); | |
| 52 | } | |
| 53 | ||
| 54 | if(unlink(TEST_DB)) { | |
| 55 | if(errno != ENOENT) { | |
| 56 | fprintf(stderr, "Cannot unlink %s: %s\n", TEST_DB, strerror(errno)); | |
| 57 | fclose(f); | |
| 58 | return 1; | |
| 59 | } | |
| 60 | } | |
| 61 | if(sqlite3_open(TEST_DB, &db) != SQLITE_OK) { | |
| 62 | fprintf(stderr, "Cannot open database %s: %s\n", TEST_DB, sqlite3_errmsg(db)); | |
| 63 | fclose(f); | |
| 64 | return 1; | |
| 65 | } | |
| 66 | ||
| 67 | CxBuffer *buf = cxBufferCreate(NULL, 2048, NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS); | |
| 68 | cx_stream_copy(f, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); | |
| 69 | int err = 0; | |
| 70 | if(buf->size > 0) { | |
| 71 | cxBufferTerminate(buf); | |
| 72 | ||
| 73 | if(sqlite3_exec(db, buf->space, 0, 0, &err_msg) != SQLITE_OK) { | |
| 74 | fprintf(stderr, "SQL error: %s\n", err_msg); | |
| 75 | sqlite3_free(err_msg); | |
| 76 | err = 1; | |
| 77 | } | |
| 78 | } else { | |
| 79 | fprintf(stderr, "Error: no file content\n"); | |
| 80 | err = 1; | |
| 81 | } | |
| 82 | cxBufferFree(buf); | |
| 83 | sqlite3_close(db); | |
| 84 | ||
| 85 | return err; | |
| 86 | } | |
| 87 | ||
| 88 | ||
| 89 | ||
| 90 | static DBUContext *ctx; | |
| 91 | static DBUConnection *conn; | |
| 92 | ||
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
93 | static DBUClass *address; |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
94 | static DBUClass *person; |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
95 | static DBUClass *role; |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
96 | |
| 23 | 97 | typedef struct Address { |
| 98 | int64_t address_id; | |
| 99 | ||
| 100 | cxmutstr street; | |
| 101 | cxmutstr zip; | |
| 102 | cxmutstr city; | |
| 103 | } Address; | |
| 104 | ||
| 105 | typedef struct Person { | |
| 106 | int64_t person_id; | |
| 107 | ||
| 108 | cxmutstr name; | |
| 109 | cxmutstr email; | |
| 110 | int age; | |
| 111 | bool iscustomer; | |
| 112 | uint64_t hash; | |
| 113 | ||
| 114 | Address *address; | |
| 115 | ||
| 116 | CxList *roles; | |
| 117 | } Person; | |
| 118 | ||
| 119 | typedef struct Role { | |
| 120 | int64_t role_id; | |
| 121 | int64_t person_id; | |
| 122 | cxmutstr name; | |
| 123 | } Role; | |
| 124 | ||
| 125 | int init_db_tests(void) { | |
| 126 | ctx = dbuContextCreate(); | |
| 127 | ||
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
128 | address = dbuRegisterClass(ctx, "address", Address, address_id); |
| 23 | 129 | dbuClassAdd(address, Address, street); |
| 130 | dbuClassAdd(address, Address, zip); | |
| 131 | dbuClassAdd(address, Address, city); | |
| 132 | ||
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
133 | role = dbuRegisterClass(ctx, "role", Role, role_id); |
| 23 | 134 | |
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
135 | person = dbuRegisterClass(ctx, "person", Person, person_id); |
| 23 | 136 | dbuClassAdd(person, Person, name); |
| 137 | dbuClassAdd(person, Person, email); | |
| 138 | dbuClassAdd(person, Person, age); | |
| 139 | dbuClassAdd(person, Person, iscustomer); | |
| 140 | dbuClassAdd(person, Person, hash); | |
| 141 | dbuClassAddObj(person, "address_id", offsetof(Person, address), address); | |
| 142 | dbuClassAddCxLinkedList(person, "person_id", offsetof(Person, roles), role); | |
| 143 | ||
| 144 | dbuClassAddForeignKey(role, Role, person_id, person); | |
| 145 | dbuClassAdd(role, Role, name); | |
| 146 | ||
| 147 | return 0; | |
| 148 | } | |
| 149 | ||
| 150 | void cleanup_db_tests(void) { | |
| 151 | if(conn) { | |
| 152 | dbuConnectionFree(conn); | |
| 153 | } | |
| 154 | dbuContextFree(ctx); | |
| 155 | } | |
| 156 | ||
| 157 | CX_TEST(testSqliteConnection) { | |
| 158 | CX_TEST_DO { | |
| 159 | conn = dbuSQLiteConnection(TEST_DB); | |
| 160 | CX_TEST_ASSERT(conn); | |
| 161 | CX_TEST_ASSERT(dbuConnectionIsActive(conn)); | |
| 162 | } | |
| 163 | } | |
| 164 | ||
| 165 | CX_TEST(testConnectionExec) { | |
| 166 | CX_TEST_DO { | |
| 167 | CX_TEST_ASSERT(conn); | |
| 168 | ||
| 169 | int t1 = dbuConnectionExec(conn, "create table ExecTest1(a int);"); | |
| 170 | CX_TEST_ASSERT(t1 == 0); | |
| 171 | ||
| 172 | int t2 = dbuConnectionExec(conn, "insert into ExecTest1(a) values (1);"); | |
| 173 | CX_TEST_ASSERT(t2 == 0); | |
| 174 | ||
| 175 | int t3 = dbuConnectionExec(conn, "drop table ExecTest1;"); | |
| 176 | CX_TEST_ASSERT(t3 == 0); | |
| 177 | ||
| 178 | int fail = dbuConnectionExec(conn, "select * from Fail;"); | |
| 179 | CX_TEST_ASSERT(fail != 0); | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | CX_TEST(testSingleValueQuery) { | |
| 184 | CX_TEST_DO { | |
| 185 | CX_TEST_ASSERT(conn); | |
| 186 | ||
| 187 | DBUQuery *q = dbuConnectionQuery(conn, NULL); | |
| 188 | CX_TEST_ASSERT(q); | |
| 189 | CX_TEST_ASSERT(dbuQuerySetSQL(q, "select 12;") == 0); | |
| 190 | CX_TEST_ASSERT(dbuQueryExec(q) == 0); | |
| 191 | ||
| 192 | DBUResult *r = dbuQueryGetResult(q); | |
| 193 | CX_TEST_ASSERT(r); | |
| 194 | int value; | |
| 195 | CX_TEST_ASSERT(dbuResultAsValue(r, &value) == 0); | |
| 196 | CX_TEST_ASSERT(value == 12); | |
| 197 | ||
| 198 | dbuQueryFree(q); | |
| 199 | ||
| 200 | } | |
| 201 | } | |
|
24
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
202 | |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
203 | CX_TEST(testSqlExec) { |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
204 | CX_TEST_DO { |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
205 | CX_TEST_ASSERT(conn); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
206 | |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
207 | DBUResult *r = dbuSqlExec(conn, NULL, "select email from person where name = 'alice';"); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
208 | CX_TEST_ASSERT(r); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
209 | cxmutstr value; |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
210 | CX_TEST_ASSERT(dbuResultAsValue(r, &value) == 0); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
211 | CX_TEST_ASSERT(!cx_strcmp(value, "alice@example.com")); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
212 | free(value.ptr); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
213 | } |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
214 | } |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
215 | |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
216 | CX_TEST(testSqlExecParam) { |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
217 | CX_TEST_DO { |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
218 | CX_TEST_ASSERT(conn); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
219 | |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
220 | DBUResult *r = dbuSqlExecParam(conn, NULL, "select email from person where name = ?;", "alice"); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
221 | CX_TEST_ASSERT(r); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
222 | cxmutstr value; |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
223 | CX_TEST_ASSERT(dbuResultAsValue(r, &value) == 0); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
224 | CX_TEST_ASSERT(!cx_strcmp(value, "alice@example.com")); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
225 | free(value.ptr); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
226 | |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
227 | r = dbuSqlExecParam(conn, NULL, "select name from person where hash = ?;", 987654321); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
228 | CX_TEST_ASSERT(r); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
229 | CX_TEST_ASSERT(dbuResultAsValue(r, &value) == 0); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
230 | CX_TEST_ASSERT(!cx_strcmp(value, "bob")); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
231 | free(value.ptr); |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
232 | } |
|
df671b62538e
add dbuSqlExec API
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
23
diff
changeset
|
233 | } |
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
234 | |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
235 | CX_TEST(testSingleTableQuery) { |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
236 | CxMempool *mp = cxMempoolCreateSimple(64); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
237 | |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
238 | CX_TEST_DO { |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
239 | DBUQuery *query = dbuQueryCreate(conn, mp->allocator, "select * from address order by street;"); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
240 | DBUObjectBuilder *builder = dbuObjectBuilder(address, query, mp->allocator); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
241 | CxList *adr = dbuObjectBuilderGetList(builder); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
242 | |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
243 | CX_TEST_ASSERT(adr); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
244 | CX_TEST_ASSERT(cxListSize(adr) == 2); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
245 | |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
246 | Address *a0 = cxListAt(adr, 0); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
247 | Address *a1 = cxListAt(adr, 1); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
248 | CX_TEST_ASSERT(a0); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
249 | CX_TEST_ASSERT(a1); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
250 | CX_TEST_ASSERT(!cx_strcmp(a0->street, "street 1")); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
251 | CX_TEST_ASSERT(!cx_strcmp(a1->street, "street 2")); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
252 | CX_TEST_ASSERT(!cx_strcmp(a0->zip, "12343")); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
253 | CX_TEST_ASSERT(!cx_strcmp(a1->zip, "23456")); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
254 | CX_TEST_ASSERT(!cx_strcmp(a0->city, "city 17")); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
255 | CX_TEST_ASSERT(!cx_strcmp(a1->city, "city 18")); |
|
26
dc36aa437249
implement json primitives serialization
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
25
diff
changeset
|
256 | |
|
dc36aa437249
implement json primitives serialization
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
25
diff
changeset
|
257 | dbuObjectBuilderDestroy(builder); |
|
25
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
258 | } |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
259 | |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
260 | cxMempoolFree(mp); |
|
0bb91d1f9bba
add single table query test
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
24
diff
changeset
|
261 | } |