Changed dbChannel management API to Test/Create/Delete
This commit is contained in:
committed by
Michael Davidsaver
parent
4324ffd274
commit
f4d55f1249
@@ -210,6 +210,27 @@ static const yajl_callbacks chf_callbacks =
|
||||
static const yajl_parser_config chf_config =
|
||||
{ 0, 1 }; /* allowComments = NO , checkUTF8 = YES */
|
||||
|
||||
static long json_validate(const char *json)
|
||||
{
|
||||
yajl_handle yh = yajl_alloc(NULL, &chf_config, NULL, NULL);
|
||||
size_t jlen = strlen(json);
|
||||
yajl_status ys;
|
||||
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
if (ys == yajl_status_insufficient_data)
|
||||
ys = yajl_parse_complete(yh);
|
||||
|
||||
yajl_free(yh);
|
||||
|
||||
if (ys == yajl_status_ok)
|
||||
return 0;
|
||||
|
||||
return S_db_notFound;
|
||||
}
|
||||
|
||||
static long chf_parse(dbChannel *chan, const char *json)
|
||||
{
|
||||
parseContext parser =
|
||||
@@ -251,6 +272,68 @@ static long chf_parse(dbChannel *chan, const char *json)
|
||||
return status;
|
||||
}
|
||||
|
||||
static long pvNameLookup(DBENTRY *pdbe, const char **ppname)
|
||||
{
|
||||
long status;
|
||||
|
||||
dbInitEntry(pdbbase, pdbe);
|
||||
|
||||
status = dbFindRecordPart(pdbe, ppname);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (**ppname == '.')
|
||||
++*ppname;
|
||||
|
||||
status = dbFindFieldPart(pdbe, ppname);
|
||||
if (status == S_dbLib_fieldNotFound)
|
||||
status = dbGetAttributePart(pdbe, ppname);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbChannelTest(const char *pname)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
long status;
|
||||
|
||||
if (!pname || !*pname || !pdbbase)
|
||||
return S_db_notFound;
|
||||
|
||||
status = pvNameLookup(&dbEntry, &pname);
|
||||
if (status)
|
||||
goto finish;
|
||||
|
||||
/* Test field modifiers */
|
||||
while (*pname) {
|
||||
switch (*pname) {
|
||||
case '$':
|
||||
switch (dbEntry.pflddes->field_type) {
|
||||
case DBF_STRING:
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
break;
|
||||
default:
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
status = json_validate(pname);
|
||||
goto finish;
|
||||
default:
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
pname++;
|
||||
}
|
||||
|
||||
finish:
|
||||
dbFinishEntry(&dbEntry);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Stolen from dbAccess.c: */
|
||||
static short mapDBFToDBR[DBF_NTYPES] =
|
||||
{
|
||||
@@ -271,41 +354,29 @@ static short mapDBFToDBR[DBF_NTYPES] =
|
||||
/* DBF_FWDLINK => */DBR_STRING,
|
||||
/* DBF_NOACCESS => */DBR_NOACCESS };
|
||||
|
||||
long dbChannelFind(dbChannel *chan, const char *pname)
|
||||
dbChannel * dbChannelCreate(const char *name)
|
||||
{
|
||||
DBADDR *paddr;
|
||||
DBENTRY dbEntry;
|
||||
dbChannel *chan = NULL;
|
||||
DBADDR *paddr;
|
||||
dbFldDes *pflddes;
|
||||
const char *pname;
|
||||
long status;
|
||||
short dbfType;
|
||||
|
||||
if (!chan || !pname || !*pname || !pdbbase)
|
||||
return S_db_notFound;
|
||||
if (!name || !*name || !pdbbase)
|
||||
return NULL;
|
||||
pname = name;
|
||||
|
||||
if (chan->magic == DBCHANNEL_MAGIC) {
|
||||
chFilter *filter;
|
||||
|
||||
while ((filter = (chFilter *) ellGet(&chan->filters))) {
|
||||
filter->fif->channel_close(filter);
|
||||
free(filter);
|
||||
}
|
||||
} else {
|
||||
ellInit(&chan->filters);
|
||||
chan->magic = DBCHANNEL_MAGIC;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, &dbEntry);
|
||||
status = dbFindRecordPart(&dbEntry, &pname);
|
||||
status = pvNameLookup(&dbEntry, &pname);
|
||||
if (status)
|
||||
goto finish;
|
||||
|
||||
if (*pname == '.')
|
||||
++pname;
|
||||
status = dbFindFieldPart(&dbEntry, &pname);
|
||||
if (status == S_dbLib_fieldNotFound)
|
||||
status = dbGetAttributePart(&dbEntry, &pname);
|
||||
if (status)
|
||||
goto finish;
|
||||
// FIXME: Use free-list
|
||||
chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate");
|
||||
chan->magic = DBCHANNEL_MAGIC;
|
||||
chan->name = strdup(name); // FIXME?
|
||||
ellInit(&chan->filters);
|
||||
|
||||
paddr = &chan->addr;
|
||||
pflddes = dbEntry.pflddes;
|
||||
@@ -350,8 +421,13 @@ long dbChannelFind(dbChannel *chan, const char *pname)
|
||||
pname++;
|
||||
}
|
||||
|
||||
finish: dbFinishEntry(&dbEntry);
|
||||
return status;
|
||||
finish:
|
||||
dbFinishEntry(&dbEntry);
|
||||
if (status && chan) {
|
||||
dbChannelDelete(chan);
|
||||
chan = NULL;
|
||||
}
|
||||
return chan;
|
||||
}
|
||||
|
||||
long dbChannelOpen(dbChannel *chan)
|
||||
@@ -392,6 +468,9 @@ void dbChannelReport(dbChannel *chan, int level)
|
||||
if (chan->magic != DBCHANNEL_MAGIC)
|
||||
return;
|
||||
|
||||
printf("Channel '%s': %d filter(s)\n", chan->name,
|
||||
ellCount(&chan->filters));
|
||||
|
||||
filter = (chFilter *) ellFirst(&chan->filters);
|
||||
while (filter) {
|
||||
filter->fif->channel_report(filter, level);
|
||||
@@ -399,7 +478,7 @@ void dbChannelReport(dbChannel *chan, int level)
|
||||
}
|
||||
}
|
||||
|
||||
long dbChannelClose(dbChannel *chan)
|
||||
long dbChannelDelete(dbChannel *chan)
|
||||
{
|
||||
chFilter *filter;
|
||||
|
||||
@@ -410,7 +489,10 @@ long dbChannelClose(dbChannel *chan)
|
||||
filter->fif->channel_close(filter);
|
||||
free(filter);
|
||||
}
|
||||
free(chan->name); // FIXME?
|
||||
chan->magic = 0;
|
||||
free(chan); // FIXME: Use free-list
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
/* A dbChannel points to a record field, and can have multiple filters */
|
||||
typedef struct dbChannel {
|
||||
epicsUInt32 magic;
|
||||
const char *name;
|
||||
dbAddr addr;
|
||||
ELLLIST filters;
|
||||
} dbChannel;
|
||||
@@ -85,10 +86,11 @@ struct chFilter {
|
||||
void *puser;
|
||||
};
|
||||
|
||||
epicsShareFunc long dbChannelFind(dbChannel *chan, const char *pname);
|
||||
epicsShareFunc long dbChannelTest(const char *pname);
|
||||
epicsShareFunc dbChannel * dbChannelCreate(const char *pname);
|
||||
epicsShareFunc long dbChannelOpen(dbChannel *chan);
|
||||
epicsShareFunc void dbChannelReport(dbChannel *chan, int level);
|
||||
epicsShareFunc long dbChannelClose(dbChannel *chan);
|
||||
epicsShareFunc long dbChannelDelete(dbChannel *chan);
|
||||
|
||||
epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif);
|
||||
epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len);
|
||||
|
||||
@@ -130,72 +130,85 @@ chFilterIf testIf =
|
||||
|
||||
MAIN(dbChannelTest)
|
||||
{
|
||||
dbChannel ch;
|
||||
dbChannel *pch;
|
||||
|
||||
testPlan(59);
|
||||
testPlan(64);
|
||||
|
||||
testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL));
|
||||
testOk(!!pdbbase, "pdbbase was set");
|
||||
|
||||
r = e = 0;
|
||||
testOk1(!dbChannelFind(&ch, "x.NAME$"));
|
||||
testOk1(ch.addr.no_elements> 1);
|
||||
/* Regular record and field names */
|
||||
testOk1(!dbChannelTest("x"));
|
||||
testOk1(!dbChannelTest("x.NAME"));
|
||||
|
||||
testOk1(!dbChannelFind(&ch, "x.{}"));
|
||||
/* Long string field modifier */
|
||||
testOk1(!dbChannelTest("x.NAME$"));
|
||||
testOk1(!!(pch = dbChannelCreate("x.NAME$")));
|
||||
testOk1(pch->addr.no_elements > 1);
|
||||
testOk1(!dbChannelDelete(pch));
|
||||
|
||||
testOk1(dbChannelFind(&ch, "y"));
|
||||
testOk1(dbChannelFind(&ch, "x.{\"none\":null}"));
|
||||
/* JSON field modifier validation */
|
||||
testOk1(!dbChannelTest("x.{\"json\":true}"));
|
||||
|
||||
/* Ensure bad PVs get rejected */
|
||||
testOk(dbChannelTest("y"), "Test nonexistent record");
|
||||
testOk(dbChannelCreate("y") == NULL, "Create nonexistent record");
|
||||
|
||||
testOk1(dbChannelTest("x.{not-json}"));
|
||||
testOk1(!dbChannelCreate("x.{\"none\":null}"));
|
||||
|
||||
dbRegisterFilter("any", &testIf);
|
||||
|
||||
/* Parser event rejection by filter */
|
||||
e = e_start;
|
||||
testOk1(dbChannelFind(&ch, "x.{\"any\":null}"));
|
||||
testOk1(!dbChannelCreate("x.{\"any\":null}"));
|
||||
|
||||
r = e_start;
|
||||
e = e_start | e_null | e_abort;
|
||||
testOk1(dbChannelFind(&ch, "x.{\"any\":null}"));
|
||||
testOk1(!dbChannelCreate("x.{\"any\":null}"));
|
||||
|
||||
r = e_start | e_null;
|
||||
e = e_start | e_null | e_end;
|
||||
testOk1(dbChannelFind(&ch, "x.{\"any\":null}"));
|
||||
testOk1(!dbChannelCreate("x.{\"any\":null}"));
|
||||
|
||||
/* Successful parsing... */
|
||||
r = r_any;
|
||||
e = e_start | e_null | e_end;
|
||||
testOk1(!dbChannelFind(&ch, "x.{\"any\":null}"));
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}")));
|
||||
e = e_close;
|
||||
testOk1(!dbChannelClose(&ch));
|
||||
testOk1(!dbChannelDelete(pch));
|
||||
|
||||
dbRegisterFilter("scalar", &testIf);
|
||||
|
||||
e = e_start | e_null | e_end;
|
||||
testOk1(!dbChannelFind(&ch, "x.{\"scalar\":null}"));
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}")));
|
||||
|
||||
e = e_report;
|
||||
dbChannelReport(&ch, 0);
|
||||
dbChannelReport(pch, 0);
|
||||
|
||||
e = e_close;
|
||||
testOk1(!dbChannelClose(&ch));
|
||||
testOk1(!dbChannelDelete(pch));
|
||||
|
||||
e = e_start | e_start_array | e_boolean | e_integer | e_end_array
|
||||
| e_end;
|
||||
testOk1(!dbChannelFind(&ch, "x.{\"any\":[true,1]}"));
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}")));
|
||||
e = e_close;
|
||||
testOk1(!dbChannelClose(&ch));
|
||||
testOk1(!dbChannelDelete(pch));
|
||||
|
||||
e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map
|
||||
| e_end;
|
||||
testOk1(!dbChannelFind(&ch, "x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}"));
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")));
|
||||
e = e_close;
|
||||
testOk1(!dbChannelDelete(pch));
|
||||
|
||||
/* More event rejection */
|
||||
r = r_scalar;
|
||||
e = e_close | e_start | e_start_array | e_abort;
|
||||
testOk1(dbChannelFind(&ch, "x.{\"scalar\":[null]}"));
|
||||
e = 0;
|
||||
testOk1(!dbChannelClose(&ch));
|
||||
e = e_start | e_start_array | e_abort;
|
||||
testOk1(!dbChannelCreate("x.{\"scalar\":[null]}"));
|
||||
|
||||
e = e_start | e_start_map | e_abort;
|
||||
testOk1(dbChannelFind(&ch, "x.{\"scalar\":{}}"));
|
||||
e = 0;
|
||||
testOk1(!dbChannelClose(&ch));
|
||||
testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
|
||||
|
||||
dbFreeBase(pdbbase);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user