/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt
SPDX-License-Identifier: BSD-2-Clause-FreeBSD
SPDX-FileCopyrightText: 2021 The Libfossil Authors
SPDX-ArtifactOfProjectName: Libfossil
SPDX-FileType: Code
Heavily indebted to the Fossil SCM project (https://fossil-scm.org).
*/
/*************************************************************************
This file implements ticket-related parts of the library.
*/
#include "fossil-scm/fossil-internal.h"
#include <assert.h>
#include <string.h> /* memcmp() */
int fsl__cx_ticket_create_table(fsl_cx * const f){
fsl_db * const db = fsl_needs_repo(f);
int rc;
if(!db) return FSL_RC_NOT_A_REPO;
rc = fsl_cx_exec_multi(f,
"DROP TABLE IF EXISTS ticket;"
"DROP TABLE IF EXISTS ticketchng;"
);
if(!rc){
fsl_buffer * const buf = &f->cache.fileContent;
fsl_buffer_reuse(buf);
rc = fsl_cx_schema_ticket(f, buf);
if(!rc) rc = fsl_cx_exec_multi(f, "%b", buf);
}
return rc;
}
static int fsl_tkt_field_id(fsl_list const * jli, const char *zFieldName){
int i;
fsl_card_J const * jc;
for(i=0; i<(int)jli->used; ++i){
jc = (fsl_card_J const *)jli->list[i];
if( !fsl_strcmp(zFieldName, jc->field) ) return i;
}
return -1;
}
int fsl__cx_ticket_load_fields(fsl_cx * const f, bool forceReload){
fsl_stmt q = fsl_stmt_empty;
int i, rc = 0;
fsl_list * const li = &f->ticket.customFields;
fsl_card_J * jc;
if( !fsl_needs_repo(f) ){
return FSL_RC_NOT_A_REPO;
}else if(li->used){
if(!forceReload) return 0;
fsl__card_J_list_free(li, false);
/* Fall through and reload ... */
}
rc = fsl_cx_prepare(f, &q, "PRAGMA table_info(ticket)");
if(!rc) while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){
char const * zFieldName = fsl_stmt_g_text(&q, 1, NULL);
f->ticket.hasTicket = 1;
if( 0==memcmp(zFieldName,"tkt_", 4)){
if( 0==fsl_strcmp(zFieldName,"tkt_ctime")) f->ticket.hasCTime = 1;
continue;
}
jc = fsl_card_J_malloc(0, zFieldName, NULL);
if(!jc){
rc = FSL_RC_OOM;
break;
}
jc->flags = FSL_CARD_J_TICKET;
rc = fsl_list_append(li, jc);
if(rc){
fsl_card_J_free(jc);
break;
}
}
fsl_stmt_finalize(&q);
if(rc) goto end;
rc = fsl_cx_prepare(f, &q, "PRAGMA table_info(ticketchng)");
if(!rc) while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){
char const * zFieldName = fsl_stmt_g_text(&q, 1, NULL);
f->ticket.hasChng = 1;
if( 0==memcmp(zFieldName,"tkt_", 4)){
if( 0==fsl_strcmp(zFieldName,"tkt_rid")) f->ticket.hasChngRid = 1;
continue;
}
if( (i=fsl_tkt_field_id(li, zFieldName)) >= 0){
jc = (fsl_card_J*)li->list[i];
jc->flags |= FSL_CARD_J_CHNG;
continue;
}
jc = fsl_card_J_malloc(0, zFieldName, NULL);
if(!jc){
rc = FSL_RC_OOM;
break;
}
jc->flags = FSL_CARD_J_CHNG;
rc = fsl_list_append(li, jc);
if(rc){
fsl_card_J_free(jc);
break;
}
}
fsl_stmt_finalize(&q);
end:
if(!rc){
fsl_list_sort(li, fsl__qsort_cmp_J_cards);
}
return rc;
}
int fsl__ticket_rebuild(fsl_cx * const f, char const * zTktKCard){
int rc;
fsl_id_t tktRid;
fsl_id_t tagId;
fsl_db * const db = fsl_needs_repo(f);
fsl_stmt q = fsl_stmt_empty;
if(!db) return FSL_RC_NOT_A_REPO;
assert(!f->cache.isCrosslinking);
rc = fsl__cx_ticket_load_fields(f, false);
if(rc) goto end;
else if(!f->ticket.hasTicket) return 0;
if(f->flags & FSL_CX_F_SKIP_UNKNOWN_CROSSLINKS){
return 0;
}else{
char * const zTag = fsl_mprintf("tkt-%s", zTktKCard);
if(!zTag){
rc = FSL_RC_OOM;
goto end;
}
tagId = fsl_tag_id(f, zTag, true);
fsl_free(zTag);
}
if(tagId<0){
rc = f->error.code;
assert(0!=rc);
goto end;
}
tktRid = fsl_db_g_id(db, 0, "SELECT tkt_id FROM ticket "
"WHERE tkt_uuid=%Q", zTktKCard);
if(tktRid>0){
if(f->ticket.hasChng){
rc = fsl_cx_exec(f, "DELETE FROM ticketchng "
"WHERE tkt_id=%" FSL_ID_T_PFMT,
tktRid);
}
if(!rc) rc = fsl_cx_exec(f, "DELETE FROM ticket "
"WHERE tkt_id=%" FSL_ID_T_PFMT,
tktRid);
if(rc) goto end;
}
tktRid = 0;
rc = fsl_cx_prepare(f, &q, "SELECT rid FROM tagxref "
"WHERE tagid=%" FSL_ID_T_PFMT
" ORDER BY mtime", tagId);
while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&q)){
fsl_deck deck = fsl_deck_empty;
fsl_id_t const rid = fsl_stmt_g_id(&q, 0);
rc = fsl_deck_load_rid(f, &deck, rid, FSL_SATYPE_TICKET);
if(0==rc){
#if 0
/* TODOs... */
assert(deck.rid==rid);
rc = fsl__ticket_insert(f, &deck, rid, tktRid, &tgtRid)
/* See fossil(1) tkt.c:ticket_insert() */;
if(rc) goto outro;
rc = fsl__deck_ticket_event(&deck, createFlag, tagId)
/* See fossil(1) manifest.c:mainfest_ticket_event() */;
#else
rc = fsl_cx_err_set(f, FSL_RC_NYI,
"MISSING: a huge block of TICKET stuff from/via "
"manifest_crosslink(). It requires infrastructure "
"libfossil does not yet have.");
#endif
}
//outro:
fsl_deck_finalize(&deck);
}
end:
fsl_stmt_finalize(&q);
return rc;
}