Skip to content

Commit 6cce7fc

Browse files
committed
Merge branch 'main' into new-integration-test
2 parents a77b9f4 + ef53e96 commit 6cce7fc

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: build, test and release sqlite-sync
22
on:
33
push:
4+
workflow_dispatch:
45

56
permissions:
67
contents: write
@@ -173,7 +174,7 @@ jobs:
173174
VERSION=$(grep -oP '#define CLOUDSYNC_VERSION\s+"\K[^"]+' "$FILE")
174175
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
175176
LATEST=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.name')
176-
if [[ "$VERSION" != "$LATEST" ]]; then
177+
if [[ "$VERSION" != "$LATEST" || "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
177178
echo "version=$VERSION" >> $GITHUB_OUTPUT
178179
else
179180
echo "::warning file=src/cloudsync.h::To release a new version, please update the CLOUDSYNC_VERSION in src/cloudsync.h to be different than the latest $LATEST"

src/cloudsync.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,7 @@ void *cloudsync_context_create (void) {
14901490
}
14911491

14921492
void cloudsync_context_free (void *ptr) {
1493-
DEBUG_SETTINGS("cloudsync_context_free %p", data);
1493+
DEBUG_SETTINGS("cloudsync_context_free %p", ptr);
14941494
if (!ptr) return;
14951495

14961496
cloudsync_context *data = (cloudsync_context*)ptr;

src/cloudsync.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "sqlite3.h"
1717
#endif
1818

19-
#define CLOUDSYNC_VERSION "0.7.8"
19+
#define CLOUDSYNC_VERSION "0.8.0"
2020

2121
int sqlite3_cloudsync_init (sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
2222

src/utils.c

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SQLITE_EXTENSION_INIT3
2929

3030
#define FNV_OFFSET_BASIS 0xcbf29ce484222325ULL
3131
#define FNV_PRIME 0x100000001b3ULL
32+
#define HASH_CHAR(_c) do { h ^= (uint8_t)(_c); h *= FNV_PRIME; h_final = h;} while (0)
3233

3334
// MARK: UUIDv7 -
3435

@@ -201,15 +202,84 @@ char *cloudsync_string_replace_prefix(const char *input, char *prefix, char *rep
201202
return (char *)input;
202203
}
203204

204-
uint64_t fnv1a_hash(const char *data, size_t len) {
205-
uint64_t hash = FNV_OFFSET_BASIS;
206-
for (size_t i = 0; i < len; ++i) {
207-
hash ^= (uint8_t)data[i];
208-
hash *= FNV_PRIME;
205+
/*
206+
Compute a normalized hash of a SQLite CREATE TABLE statement.
207+
208+
* Normalization:
209+
* - Skips comments (-- and / * )
210+
* - Skips non-printable characters
211+
* - Collapses runs of whitespace to single space
212+
* - Case-insensitive outside quotes
213+
* - Preserves quoted string content exactly
214+
* - Handles escaped quotes
215+
* - Trims trailing spaces and semicolons from the effective hash
216+
*/
217+
uint64_t fnv1a_hash (const char *data, size_t len) {
218+
uint64_t h = FNV_OFFSET_BASIS;
219+
int q = 0; // quote state: 0 / '\'' / '"'
220+
int cmt = 0; // comment state: 0 / 1=line / 2=block
221+
int last_space = 1; // prevent leading space
222+
uint64_t h_final = h; // hash state after last non-space, non-semicolon char
223+
224+
for (size_t i = 0; i < len; i++) {
225+
int c = data[i];
226+
int next = (i + 1 < len) ? data[i + 1] : 0;
227+
228+
// detect start of comments
229+
if (!q && !cmt && c == '-' && next == '-') {cmt = 1; i += 1; continue;}
230+
if (!q && !cmt && c == '/' && next == '*') {cmt = 2; i += 1; continue;}
231+
232+
// skip comments
233+
if (cmt == 1) {if (c == '\n') cmt = 0; continue;}
234+
if (cmt == 2) {if (c == '*' && next == '/') { cmt = 0; i += 1; } continue;}
235+
236+
// handle quotes
237+
if (c == '\'' || c == '"') {
238+
if (q == c) {
239+
if (next == c) {HASH_CHAR(c); i += 1; continue;}
240+
q = 0;
241+
} else if (!q) q = c;
242+
HASH_CHAR(c);
243+
last_space = 0;
244+
continue;
245+
}
246+
247+
// inside quote → hash exactly
248+
if (q) {HASH_CHAR(c); last_space = 0; continue;}
249+
250+
// skip non-printable
251+
if (!isprint((unsigned char)c)) continue;
252+
253+
// whitespace normalization
254+
if (isspace((unsigned char)c)) {
255+
// look ahead to next non-space, non-comment char
256+
size_t j = i + 1;
257+
while (j < len && isspace((unsigned char)data[j])) j++;
258+
259+
int next_c = (j < len) ? data[j] : 0;
260+
261+
// if next char is punctuation where space is irrelevant → skip space
262+
if (next_c == '(' || next_c == ')' || next_c == ',' || next_c == ';' || next_c == 0) {
263+
// skip inserting space
264+
last_space = 1;
265+
continue;
266+
}
267+
268+
// else, insert one space
269+
if (!last_space) {HASH_CHAR(' '); last_space = 1;}
270+
continue;
271+
}
272+
273+
// skip semicolons at end
274+
if (c == ';') {last_space = 1; continue;}
275+
276+
// normal visible char
277+
HASH_CHAR(tolower(c));
278+
last_space = 0;
209279
}
210-
return hash;
280+
281+
return h_final;
211282
}
212-
213283
// MARK: - CRDT algos -
214284

215285
table_algo crdt_algo_from_name (const char *algo_name) {

0 commit comments

Comments
 (0)