Skip to content

Commit

Permalink
Merge pull request #4676 from cyrusimap/jmap_contacts_ietf_cache
Browse files Browse the repository at this point in the history
jmap_contact.c: use object cache for ContactCards
  • Loading branch information
ksmurchison authored Oct 5, 2023
2 parents 5f19469 + 2306434 commit 4566318
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 53 deletions.
51 changes: 35 additions & 16 deletions imap/carddav_db.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,13 @@ static const char *column_text_to_buf(const char *text, struct buf *buf)
" modseq, createdmodseq, NULL, NULL" \
" FROM vcard_objs"

#define CMD_GETFIELDS_JMAP \
#define CMD_GETFIELDS_JSCARD \
"SELECT vcard_objs.rowid, creationdate, mailbox, resource, imap_uid," \
" lock_token, lock_owner, lock_ownerid, lock_expire," \
" version, vcard_uid, kind, fullname, name, nickname, alive," \
" modseq, createdmodseq, jmapversion, jmapdata" \
" FROM vcard_objs LEFT JOIN vcard_jmapcache USING (rowid)"
" FROM vcard_objs LEFT JOIN jscard_cache" \
" ON (vcard_objs.rowid = jscard_cache.rowid AND jscard_cache.userid = :userid)"

static int read_cb(sqlite3_stmt *stmt, void *rock)
{
Expand Down Expand Up @@ -285,7 +286,7 @@ static int read_cb(sqlite3_stmt *stmt, void *rock)
column_text_to_buf((const char *) sqlite3_column_text(stmt, 14),
&db->bufs[8]);
cdata->jmapdata =
column_text_to_buf((const char *) sqlite3_column_text(stmt, 15),
column_text_to_buf((const char *) sqlite3_column_text(stmt, 19),
&db->bufs[9]);
}

Expand Down Expand Up @@ -889,7 +890,7 @@ static int carddav_write_groups(struct carddav_db *carddavdb, int rowid, const s
" nickname = :nickname" \
" WHERE rowid = :rowid;"

#define CMD_DELETE_JMAPCACHE "DELETE FROM vcard_jmapcache WHERE rowid = :rowid"
#define CMD_DELETE_JSCARDCACHE "DELETE FROM jscard_cache WHERE rowid = :rowid"

EXPORTED int carddav_write(struct carddav_db *carddavdb, struct carddav_data *cdata)
{
Expand All @@ -915,9 +916,7 @@ EXPORTED int carddav_write(struct carddav_db *carddavdb, struct carddav_data *cd
{ NULL, SQLITE_NULL, { .s = NULL } } };

if (cdata->dav.rowid) {
int r = sqldb_exec(carddavdb->db, CMD_DELETE_JMAPCACHE, bval, NULL, NULL);
if (r) return r;
r = sqldb_exec(carddavdb->db, CMD_UPDATE, bval, NULL, NULL);
int r = sqldb_exec(carddavdb->db, CMD_UPDATE, bval, NULL, NULL);
if (r) return r;
}
else {
Expand Down Expand Up @@ -985,30 +984,39 @@ EXPORTED int carddav_delmbox(struct carddav_db *carddavdb,
return 0;
}

#define CMD_INSERT_JMAPCACHE \
"INSERT INTO vcard_jmapcache ( rowid, jmapversion, jmapdata )" \
" VALUES ( :rowid, :jmapversion, :jmapdata );"
#define CMD_DELETE_JSCARDCACHE_USER \
"DELETE FROM jscard_cache" \
" WHERE rowid = :rowid AND userid = :userid"

#define CMD_INSERT_JSCARDCACHE_USER \
"INSERT INTO jscard_cache ( rowid, userid, jmapversion, jmapdata )" \
" VALUES ( :rowid, :userid, :jmapversion, :jmapdata );"

EXPORTED int carddav_write_jmapcache(struct carddav_db *carddavdb, int rowid, int version, const char *data)
EXPORTED int carddav_write_jscardcache(struct carddav_db *carddavdb,
int rowid, const char *userid,
int version, const char *data)
{
struct sqldb_bindval bval[] = {
{ ":rowid", SQLITE_INTEGER, { .i = rowid } },
{ ":userid", SQLITE_TEXT, { .s = userid } },
{ ":jmapversion", SQLITE_INTEGER, { .i = version } },
{ ":jmapdata", SQLITE_TEXT, { .s = data } },
{ NULL, SQLITE_NULL, { .s = NULL } } };
int r;

/* clean up existing records if any */
r = sqldb_exec(carddavdb->db, CMD_DELETE_JMAPCACHE, bval, NULL, NULL);
r = sqldb_exec(carddavdb->db, CMD_DELETE_JSCARDCACHE_USER, bval, NULL, NULL);
if (r) return r;

if (!data) return 0;

/* insert the cache record */
return sqldb_exec(carddavdb->db, CMD_INSERT_JMAPCACHE, bval, NULL, NULL);
return sqldb_exec(carddavdb->db, CMD_INSERT_JSCARDCACHE_USER, bval, NULL, NULL);
}

EXPORTED int carddav_get_cards(struct carddav_db *carddavdb,
const mbentry_t *mbentry,
const char *vcard_uid, int kind,
const char *userid, const char *vcard_uid, int kind,
int (*cb)(void *rock,
struct carddav_data *cdata),
void *rock)
Expand All @@ -1017,6 +1025,7 @@ EXPORTED int carddav_get_cards(struct carddav_db *carddavdb,
((carddavdb->db->version >= DB_MBOXID_VERSION) ?
mbentry->uniqueid : mbentry->name);
struct sqldb_bindval bval[] = {
{ ":userid", SQLITE_TEXT, { .s = userid } },
{ ":kind", SQLITE_INTEGER, { .i = kind } },
{ ":mailbox", SQLITE_TEXT, { .s = mailbox } },
{ ":vcard_uid", SQLITE_TEXT, { .s = vcard_uid } },
Expand All @@ -1026,7 +1035,7 @@ EXPORTED int carddav_get_cards(struct carddav_db *carddavdb,
struct read_rock rrock = { carddavdb, &cdata, 0, cb, rock };
struct buf sqlbuf = BUF_INITIALIZER;

buf_setcstr(&sqlbuf, CMD_GETFIELDS_JMAP);
buf_setcstr(&sqlbuf, CMD_GETFIELDS_JSCARD);
buf_appendcstr(&sqlbuf, " WHERE alive = 1");
if (kind != CARDDAV_KIND_ANY)
buf_appendcstr(&sqlbuf, " AND kind = :kind");
Expand Down Expand Up @@ -1481,7 +1490,17 @@ EXPORTED int carddav_writecard_x(struct carddav_db *carddavdb,
}
}

int r = carddav_write(carddavdb, cdata);
int r;
if (cdata->dav.rowid) {
/* clean up existing cache records */
struct sqldb_bindval bval[] = {
{ ":rowid", SQLITE_INTEGER, { .i = cdata->dav.rowid } },
{ NULL, SQLITE_NULL, { .s = NULL } } };

r = sqldb_exec(carddavdb->db, CMD_DELETE_JSCARDCACHE, bval, NULL, NULL);
if (r) return r;
}
r = carddav_write(carddavdb, cdata);
if (!r) r = carddav_write_emails(carddavdb,
cdata->dav.rowid, &emails, ispinned);
if (!r) r = carddav_write_groups(carddavdb, cdata->dav.rowid, &member_uids);
Expand Down
9 changes: 5 additions & 4 deletions imap/carddav_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ strarray_t *carddav_getgroup(struct carddav_db *carddavdb,
strarray_t *carddav_getuid_groups(struct carddav_db *carddavdb, const char *uid);

/* process each entry of type 'kind' for 'mailbox' in 'carddavdb' with cb() */
int carddav_get_cards(struct carddav_db *carddavdb,
const mbentry_t *mbentry, const char *vcard_uid, int kind,
int carddav_get_cards(struct carddav_db *carddavdb, const mbentry_t *mbentry,
const char *userid, const char *vcard_uid, int kind,
carddav_cb_t *cb, void *rock);

/* Process each entry for 'carddavdb' with a modseq higher than oldmodseq,
Expand All @@ -164,8 +164,9 @@ int carddav_foreach_sort(struct carddav_db *carddavdb, const mbentry_t *mbentry,
enum carddav_sort* sort, size_t nsort,
carddav_cb_t *cb, void *rock);

int carddav_write_jmapcache(struct carddav_db *carddavdb, int rowid,
int version, const char *data);
int carddav_write_jscardcache(struct carddav_db *carddavdb,
int rowid, const char *userid,
int version, const char *data);

/* update an entry in 'carddavdb' */
int carddav_update(struct carddav_db *carddavdb,
Expand Down
20 changes: 18 additions & 2 deletions imap/dav_db.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,23 @@
" PRIMARY KEY (rowid, ical_recurid, userid)" \
" FOREIGN KEY (rowid, ical_recurid) REFERENCES jscal_objs (rowid, ical_recurid) ON DELETE CASCADE );"

// dropped in version 16
#define CMD_CREATE_CARDCACHE \
"CREATE TABLE IF NOT EXISTS vcard_jmapcache (" \
" rowid INTEGER NOT NULL PRIMARY KEY," \
" jmapversion INTEGER NOT NULL," \
" jmapdata TEXT NOT NULL," \
" FOREIGN KEY (rowid) REFERENCES vcard_objs (rowid) ON DELETE CASCADE );"

#define CMD_CREATE_JSCARDCACHE \
"CREATE TABLE IF NOT EXISTS jscard_cache (" \
" rowid INTEGER NOT NULL," \
" userid TEXT NOT NULL," \
" jmapversion INTEGER NOT NULL," \
" jmapdata TEXT NOT NULL," \
" PRIMARY KEY (rowid, userid)" \
" FOREIGN KEY (rowid) REFERENCES vcard_objs (rowid) ON DELETE CASCADE );"

#define CMD_CREATE_SIEVE \
"CREATE TABLE IF NOT EXISTS sieve_scripts (" \
" rowid INTEGER PRIMARY KEY," \
Expand All @@ -221,7 +231,8 @@

#define CMD_CREATE CMD_CREATE_CAL CMD_CREATE_CARD CMD_CREATE_EM CMD_CREATE_GR \
CMD_CREATE_OBJS CMD_CREATE_CALCACHE CMD_CREATE_CARDCACHE \
CMD_CREATE_SIEVE CMD_CREATE_JSCALOBJS CMD_CREATE_JSCALCACHE
CMD_CREATE_SIEVE CMD_CREATE_JSCALOBJS CMD_CREATE_JSCALCACHE \
CMD_CREATE_JSCARDCACHE

/* leaves these unused columns around, but that's life. A dav_reconstruct
* will fix them */
Expand Down Expand Up @@ -274,6 +285,10 @@
"INSERT INTO jscal_objs" \
" SELECT rowid, '', modseq, createdmodseq, dtstart, dtend, alive, '' FROM ical_objs;"

#define CMD_DBUPGRADEv16 \
"DROP TABLE vcard_jmapcache;" \
CMD_CREATE_JSCARDCACHE

static int sievedb_upgrade(sqldb_t *db);

struct sqldb_upgrade davdb_upgrade[] = {
Expand All @@ -291,10 +306,11 @@ struct sqldb_upgrade davdb_upgrade[] = {
/* Don't upgrade to version 13. This was an intermediate Sieve DB version */
{ 14, CMD_DBUPGRADEv14, &sievedb_upgrade },
{ 15, CMD_DBUPGRADEv15, NULL },
{ 16, CMD_DBUPGRADEv16, NULL },
{ 0, NULL, NULL }
};

#define DB_VERSION 15
#define DB_VERSION 16

static sqldb_t *reconstruct_db;

Expand Down
63 changes: 32 additions & 31 deletions imap/jmap_contact.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ static int _json_to_card(struct jmap_req *req,

static int jmap_contact_getblob(jmap_req_t *req, jmap_getblob_context_t *ctx);

#define JMAPCACHE_CONTACTVERSION 1
#define JMAPCACHE_CARDVERSION 1

static jmap_method_t jmap_contact_methods_standard[] = {
#ifdef HAVE_LIBICALVCARD
Expand Down Expand Up @@ -490,12 +490,6 @@ static int getgroups_cb(void *rock, struct carddav_data *cdata)
return 0;
}

if (cdata->jmapversion == JMAPCACHE_CONTACTVERSION) {
json_error_t jerr;
obj = json_loads(cdata->jmapdata, 0, &jerr);
if (obj) goto gotvalue;
}

if (!crock->mailbox || strcmp(mailbox_uniqueid(crock->mailbox), cdata->dav.mailbox)) {
mailbox_close(&crock->mailbox);
r = mailbox_open_irl(mbentry->name, &crock->mailbox);
Expand All @@ -519,10 +513,6 @@ static int getgroups_cb(void *rock, struct carddav_data *cdata)

vparse_free_card(vcard);

hashu64_insert(cdata->dav.rowid, json_dumps(obj, 0), &crock->jmapcache);

gotvalue:

json_object_set_new(obj, "id", json_string(cdata->vcard_uid));
json_object_set_new(obj, "uid", json_string(cdata->vcard_uid));

Expand Down Expand Up @@ -719,13 +709,13 @@ static const jmap_property_t group_props[] = {

static void cachecards_cb(uint64_t rowid, void *payload, void *vrock)
{
const char *eventrep = payload;
const char *jscard = payload;
struct cards_rock *rock = vrock;

// there's no way to return errors, but luckily it doesn't matter if we
// fail to cache
carddav_write_jmapcache(rock->db, rowid,
JMAPCACHE_CONTACTVERSION, eventrep);
carddav_write_jscardcache(rock->db, rowid,
rock->req->userid, JMAPCACHE_CARDVERSION, jscard);
}

static int has_addressbooks_cb(const mbentry_t *mbentry, void *rock)
Expand Down Expand Up @@ -803,7 +793,7 @@ static int _contacts_get(struct jmap_req *req, carddav_cb_t *cb, int kind,
rock.rows = 0;
const char *id = json_string_value(jval);

r = carddav_get_cards(db, mbentry, id, kind, cb, &rock);
r = carddav_get_cards(db, mbentry, req->userid, id, kind, cb, &rock);
if (r || !rock.rows) {
json_array_append(get.not_found, jval);
}
Expand All @@ -812,7 +802,7 @@ static int _contacts_get(struct jmap_req *req, carddav_cb_t *cb, int kind,
}
else {
rock.rows = 0;
r = carddav_get_cards(db, mbentry, NULL, kind, cb, &rock);
r = carddav_get_cards(db, mbentry, req->userid, NULL, kind, cb, &rock);
if (r) goto done;
}

Expand Down Expand Up @@ -2705,12 +2695,6 @@ static int _contactsquery_cb(void *rock, struct carddav_data *cdata)
return 0;
}

if (cdata->jmapversion == JMAPCACHE_CONTACTVERSION) {
json_error_t jerr;
entry = json_loads(cdata->jmapdata, 0, &jerr);
if (entry) goto gotvalue;
}

/* Open mailbox. */
if (!crock->mailbox || strcmp(mailbox_name(crock->mailbox), mbentry->name)) {
mailbox_close(&crock->mailbox);
Expand Down Expand Up @@ -2743,9 +2727,6 @@ static int _contactsquery_cb(void *rock, struct carddav_data *cdata)
crock->mailbox, &record);
vparse_free_card(vcard);

gotvalue:


if (crock->filter) {
/* Match the contact against the filter */
struct contactsquery_filter_rock cfrock = {
Expand Down Expand Up @@ -7440,6 +7421,14 @@ static int getcards_cb(void *rock, struct carddav_data *cdata)
r = mailbox_find_index_record(crock->mailbox, cdata->dav.imap_uid, &record);
if (r) goto done;

if (!crock->args.disable_uri_as_blobid &&
cdata->jmapversion == JMAPCACHE_CARDVERSION) {
/* We only cache contacts with media as blobids */
json_error_t jerr;
obj = json_loads(cdata->jmapdata, 0, &jerr);
if (obj) goto gotvalue;
}

/* Load message containing the resource and parse vcard data */
vcardcomponent *vcard = record_to_vcard_x(crock->mailbox, &record);
if (!vcard) {
Expand All @@ -7458,6 +7447,13 @@ static int getcards_cb(void *rock, struct carddav_data *cdata)
crock->mailbox, &record, from_vcard_flags);
vcardcomponent_free(vcard);

if (!crock->args.disable_uri_as_blobid) {
/* Only cache contacts with media as blobids */
hashu64_insert(cdata->dav.rowid, json_dumps(obj, 0), &crock->jmapcache);
}

gotvalue:

jmap_filterprops(obj, crock->get->props);

if (jmap_wantprop(crock->get->props, "cyrusimap.org:href")) {
Expand Down Expand Up @@ -8162,13 +8158,13 @@ static int _cardquery_cb(void *rock, struct carddav_data *cdata)
mboxlist_entry_free(&mbentry);
return 0;
}
#if 0
if (cdata->jmapversion == JMAPCACHE_CONTACTVERSION) {

if (cdata->jmapversion == JMAPCACHE_CARDVERSION) {
json_error_t jerr;
entry = json_loads(cdata->jmapdata, 0, &jerr);
if (entry) goto gotvalue;
}
#endif

/* Open mailbox. */
if (!crock->mailbox || strcmp(mailbox_name(crock->mailbox), mbentry->name)) {
mailbox_close(&crock->mailbox);
Expand All @@ -8190,8 +8186,7 @@ static int _cardquery_cb(void *rock, struct carddav_data *cdata)
goto done;
}

//gotvalue:

gotvalue:

if (crock->filter) {
/* Match the contact against the filter */
Expand Down Expand Up @@ -11336,7 +11331,13 @@ static int _card_set_update(jmap_req_t *req, unsigned kind,
req->userid, req->authstate);
if (!r) r = annotate_state_store(state, annots);
if (!r) r = mailbox_rewrite_index_record(*mailbox, &record);
if (!r) *item = json_null();
if (!r) {
*item = json_null();

/* flush cached JSContactCard for this user */
carddav_write_jscardcache(db, cdata->dav.rowid,
req->userid, 0, NULL);
}
goto done;
}
}
Expand Down

0 comments on commit 4566318

Please sign in to comment.