EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0
From: Ben Franksen via Core-talk <core-talk at aps.anl.gov>
To: mp+381272 at code.launchpad.net
Date: Thu, 26 Mar 2020 19:55:35 -0000
Ben Franksen has proposed merging ~bfrk/epics-base:write-filters into epics-base:7.0 with ~dirk.zimoch/epics-base:dbChannelForDBLinks as a prerequisite.

Requested reviews:
  EPICS Core Developers (epics-core)

For more details, see:
https://code.launchpad.net/~bfrk/epics-base/+git/epics-base/+merge/381272

This is just the initial refactor, no new features yet.
-- 
Your team EPICS Core Developers is requested to review the proposed merge of ~bfrk/epics-base:write-filters into epics-base:7.0.
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 9b149dd..1446ed5 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -338,7 +338,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
 	dbCommon	*pcommon;
 	char		*pbuffer = *poriginal;
 
-        if (!pfl || pfl->type == dbfl_type_rec)
+        if (!pfl)
             field_type = paddr->field_type;
         else
             field_type = pfl->field_type;
@@ -348,7 +348,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
 	if( (*options) & DBR_STATUS ) {
 	    unsigned short *pushort = (unsigned short *)pbuffer;
 
-            if (!pfl || pfl->type == dbfl_type_rec) {
+            if (!pfl) {
                 *pushort++ = pcommon->stat;
                 *pushort++ = pcommon->sevr;
             } else {
@@ -382,7 +382,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
 	if( (*options) & DBR_TIME ) {
 	    epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
 
-            if (!pfl || pfl->type == dbfl_type_rec) {
+            if (!pfl) {
                 *ptime++ = pcommon->time.secPastEpoch;
                 *ptime++ = pcommon->time.nsec;
             } else {
@@ -867,14 +867,14 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
     return 0;
 }
 
-long dbGetField(DBADDR *paddr,short dbrType,
-    void *pbuffer, long *options, long *nRequest, void *pflin)
+long dbGetField(DBADDR *paddr, short dbrType,
+    void *pbuffer, long *options, long *nRequest)
 {
     dbCommon *precord = paddr->precord;
     long status = 0;
 
     dbScanLock(precord);
-    status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
+    status = dbGet(paddr, dbrType, pbuffer, options, nRequest, NULL);
     dbScanUnlock(precord);
     return status;
 }
@@ -895,7 +895,7 @@ long dbGet(DBADDR *paddr, short dbrType,
     if (nRequest && *nRequest == 0)
         return 0;
 
-    if (!pfl || pfl->type == dbfl_type_rec) {
+    if (!pfl) {
         field_type = paddr->field_type;
         no_elements = capacity = paddr->no_elements;
 
@@ -911,7 +911,7 @@ long dbGet(DBADDR *paddr, short dbrType,
     } else {
         field_type = pfl->field_type;
         no_elements = capacity = pfl->no_elements;
-        offset = 0;
+        offset = pfl->offset;
     }
 
     if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
@@ -937,7 +937,7 @@ long dbGet(DBADDR *paddr, short dbrType,
     if (offset == 0 && (!nRequest || no_elements == 1)) {
         if (nRequest)
             *nRequest = 1;
-        if (!pfl || pfl->type == dbfl_type_rec) {
+        if (!pfl) {
             status = dbFastGetConvertRoutine[field_type][dbrType]
                 (paddr->pfield, pbuf, paddr);
         } else {
@@ -950,6 +950,7 @@ long dbGet(DBADDR *paddr, short dbrType,
 
             localAddr.field_type = pfl->field_type;
             localAddr.field_size = pfl->field_size;
+            /* not used by dbFastConvert: */
             localAddr.no_elements = pfl->no_elements;
             if (pfl->type == dbfl_type_val)
                 localAddr.pfield = (char *) &pfl->u.v.field;
@@ -981,14 +982,15 @@ long dbGet(DBADDR *paddr, short dbrType,
         }
         /* convert data into the caller's buffer */
         if (n <= 0) {
-            ;/*do nothing*/
-        } else if (!pfl || pfl->type == dbfl_type_rec) {
+            ;                           /*do nothing */
+        } else if (!pfl) {
             status = convert(paddr, pbuf, n, capacity, offset);
         } else {
             DBADDR localAddr = *paddr; /* Structure copy */
 
             localAddr.field_type = pfl->field_type;
             localAddr.field_size = pfl->field_size;
+            /* not used by dbConvert, it uses the passed capacity instead: */
             localAddr.no_elements = pfl->no_elements;
             if (pfl->type == dbfl_type_val)
                 localAddr.pfield = (char *) &pfl->u.v.field;
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index 805dfd4..4a95989 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -243,7 +243,7 @@ epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
 epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
 epicsShareFunc long dbGetField(
     struct dbAddr *,short dbrType,void *pbuffer,long *options,
-    long *nRequest,void *pfl);
+    long *nRequest);
 epicsShareFunc long dbGet(
     struct dbAddr *,short dbrType,void *pbuffer,long *options,
     long *nRequest,void *pfl);
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index a8bfbf1..e361f3b 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -49,14 +49,12 @@ typedef struct parseContext {
 
 static void *dbChannelFreeList;
 static void *chFilterFreeList;
-static void *dbchStringFreeList;
 
 void dbChannelExit(void)
 {
     freeListCleanup(dbChannelFreeList);
     freeListCleanup(chFilterFreeList);
-    freeListCleanup(dbchStringFreeList);
-    dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
+    dbChannelFreeList = chFilterFreeList = NULL;
 }
 
 void dbChannelInit (void)
@@ -66,7 +64,6 @@ void dbChannelInit (void)
 
     freeListInitPvt(&dbChannelFreeList,  sizeof(dbChannel), 128);
     freeListInitPvt(&chFilterFreeList,  sizeof(chFilter), 64);
-    freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
     db_init_event_freelists();
 }
 
@@ -446,28 +443,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
     return status;
 }
 
-/* Stolen from dbAccess.c: */
-static short mapDBFToDBR[DBF_NTYPES] =
-    {
-    /* DBF_STRING   => */DBR_STRING,
-    /* DBF_CHAR     => */DBR_CHAR,
-    /* DBF_UCHAR    => */DBR_UCHAR,
-    /* DBF_SHORT    => */DBR_SHORT,
-    /* DBF_USHORT   => */DBR_USHORT,
-    /* DBF_LONG     => */DBR_LONG,
-    /* DBF_ULONG    => */DBR_ULONG,
-    /* DBF_INT64    => */DBR_INT64,
-    /* DBF_UINT64   => */DBR_UINT64,
-    /* DBF_FLOAT    => */DBR_FLOAT,
-    /* DBF_DOUBLE   => */DBR_DOUBLE,
-    /* DBF_ENUM,    => */DBR_ENUM,
-    /* DBF_MENU,    => */DBR_ENUM,
-    /* DBF_DEVICE   => */DBR_ENUM,
-    /* DBF_INLINK   => */DBR_STRING,
-    /* DBF_OUTLINK  => */DBR_STRING,
-    /* DBF_FWDLINK  => */DBR_STRING,
-    /* DBF_NOACCESS => */DBR_NOACCESS };
-
 dbChannel * dbChannelCreate(const char *name)
 {
     const char *pname = name;
@@ -736,39 +711,6 @@ void dbChannelDelete(dbChannel *chan)
     freeListFree(dbChannelFreeList, chan);
 }
 
-static void freeArray(db_field_log *pfl) {
-    if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
-        freeListFree(dbchStringFreeList, pfl->u.r.field);
-    } else {
-        free(pfl->u.r.field);
-    }
-}
-
-void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
-{
-    void *p;
-    struct dbCommon *prec = dbChannelRecord(chan);
-
-    if (pfl->type != dbfl_type_rec) return;
-
-    pfl->type = dbfl_type_ref;
-    pfl->stat = prec->stat;
-    pfl->sevr = prec->sevr;
-    pfl->time = prec->time;
-    pfl->field_type  = chan->addr.field_type;
-    pfl->no_elements = chan->addr.no_elements;
-    pfl->field_size  = chan->addr.field_size;
-    pfl->u.r.dtor = freeArray;
-    pfl->u.r.pvt = pvt;
-    if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
-        p = freeListCalloc(dbchStringFreeList);
-    } else {
-        p = calloc(pfl->no_elements, pfl->field_size);
-    }
-    if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
-    pfl->u.r.field = p;
-}
-
 /* FIXME: Do these belong in a different file? */
 
 void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index fab9c66..6e98dee 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -64,8 +64,9 @@ typedef struct dbChannel {
 /* Prototype for the channel event function that is called in filter stacks
  *
  * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
- * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
- * data out of the associated record.
+ * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
+ * be called before accessing the data, as this indicates the data is owned by the
+ * record.
  *
  * This function has ownership of the field log pLog, if it wishes to discard
  * this update it should free the field log with db_delete_field_log() and
@@ -224,7 +225,6 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
 epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
 epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
 epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
-epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
 
 #ifdef __cplusplus
 }
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 466552d..8677add 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -181,7 +181,10 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
     if (ellCount(&chan->filters)) {
         db_field_log *pfl;
 
-        /* For the moment, empty arrays are not supported by EPICS */
+        /*
+         * For the moment, empty arrays are not supported by EPICS.
+         * See the remark in src/std/filters/arr.c for details.
+         */
         if (dbChannelFinalElements(chan) <= 0) /* empty array request */
             return S_db_badField;
         pfl = db_create_read_log(chan);
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index ed73529..532c2ba 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -667,27 +667,22 @@ int db_post_extra_labor (dbEventCtx ctx)
     return DB_EVENT_OK;
 }
 
-/*
- *  DB_CREATE_EVENT_LOG()
- *
- *  NOTE: This assumes that the db scan lock is already applied
- *        (as it copies data from the record)
- */
-db_field_log* db_create_event_log (struct evSubscrip *pevent)
+static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
 {
     db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
 
     if (pLog) {
-        struct dbChannel *chan = pevent->chan;
         struct dbCommon  *prec = dbChannelRecord(chan);
-        pLog->ctx = dbfl_context_event;
-        if (pevent->useValque) {
+        pLog->stat = prec->stat;
+        pLog->sevr = prec->sevr;
+        pLog->time = prec->time;
+        pLog->field_type  = dbChannelFieldType(chan);
+        pLog->field_size  = dbChannelFieldSize(chan);
+        pLog->no_elements = dbChannelElements(chan);
+        pLog->offset = 0;
+
+        if (use_val) {
             pLog->type = dbfl_type_val;
-            pLog->stat = prec->stat;
-            pLog->sevr = prec->sevr;
-            pLog->time = prec->time;
-            pLog->field_type  = dbChannelFieldType(chan);
-            pLog->no_elements = dbChannelElements(chan);
             /*
              * use memcpy to avoid a bus error on
              * union copy of char in the db at an odd
@@ -697,23 +692,62 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
                    dbChannelField(chan),
                    dbChannelFieldSize(chan));
         } else {
-            pLog->type = dbfl_type_rec;
+            rset *prset;
+
+            pLog->type = dbfl_type_ref;
+
+            if (dbChannelSpecial(chan) == SPC_DBADDR &&
+                (prset = dbGetRset(&chan->addr)) &&
+                prset->get_array_info)
+                /* This condition implies !use_val, see db_add_event */
+            {
+                void *pfieldsave = dbChannelField(chan);
+                prec = dbChannelRecord(chan);
+                prset->get_array_info(&chan->addr, &pLog->no_elements, &pLog->offset);
+                /* don't make a copy yet, just reference the field value */
+                pLog->u.r.field = dbChannelField(chan);
+                dbChannelField(chan) = pfieldsave;
+            }
+            else {
+                /* don't make a copy yet, just reference the field value */
+                pLog->u.r.field = dbChannelField(chan);
+            }
+            /* indicate field value still owned by record */
+            pLog->u.r.dtor = NULL;
+            /* no private data yet, may be set by a filter */
+            pLog->u.r.pvt = NULL;
         }
     }
     return pLog;
 }
 
 /*
+ *  DB_CREATE_EVENT_LOG()
+ *
+ *  NOTE: This assumes that the db scan lock is already applied
+ *        (as it calls rset->get_array_info)
+ */
+db_field_log* db_create_event_log (struct evSubscrip *pevent)
+{
+    db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
+    if (pLog) {
+        pLog->ctx  = dbfl_context_event;
+    }
+    return pLog;
+}
+
+/*
  *  DB_CREATE_READ_LOG()
  *
  */
 db_field_log* db_create_read_log (struct dbChannel *chan)
 {
-    db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
-
+    db_field_log *pLog = db_create_field_log(chan,
+        dbChannelElements(chan) == 1 &&
+        dbChannelSpecial(chan) != SPC_DBADDR &&
+        dbChannelFieldSize(chan) <= sizeof(union native_value));
     if (pLog) {
         pLog->ctx  = dbfl_context_read;
-        pLog->type = dbfl_type_rec;
     }
     return pLog;
 }
@@ -737,20 +771,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
     LOCKEVQUE (ev_que);
 
     /*
-     * if we have an event on the queue and both the last
-     * event on the queue and the current event are emtpy
-     * (i.e. of type dbfl_type_rec), simply ignore duplicate
-     * events (saving empty events serves no purpose)
-     */
-    if (pevent->npend > 0u &&
-        (*pevent->pLastLog)->type == dbfl_type_rec &&
-        pLog->type == dbfl_type_rec) {
-        db_delete_field_log(pLog);
-        UNLOCKEVQUE (ev_que);
-        return;
-    }
-
-    /*
      * add to task local event que
      */
 
diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
index e16ab4c..f0ab281 100644
--- a/modules/database/src/ioc/db/dbExtractArray.c
+++ b/modules/database/src/ioc/db/dbExtractArray.c
@@ -13,11 +13,12 @@
 /*
  *  Author: Ralph Lange <Ralph.Lange at bessy.de>
  *
- *  based on dbConvert.c
+ *  based on dbConvert.c, see copyNoConvert
  *  written by: Bob Dalesio, Marty Kraimer
  */
 
 #include <string.h>
+#include <assert.h>
 
 #include "epicsTypes.h"
 
@@ -25,61 +26,31 @@
 #include "dbAddr.h"
 #include "dbExtractArray.h"
 
-void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
-                           long nRequest, long no_elements, long offset, long increment)
+void dbExtractArray(const void *pfrom, void *pto,
+    short field_size, short field_type,
+    long nRequest, long no_elements, long offset, long increment)
 {
     char *pdst = (char *) pto;
-    char *psrc = (char *) paddr->pfield;
-    long nUpperPart;
-    int i;
-    short srcSize = paddr->field_size;
-    short dstSize = srcSize;
-    char isString = (paddr->field_type == DBF_STRING);
+    const char *psrc = (char *) pfrom;
 
-    if (nRequest > no_elements) nRequest = no_elements;
-    if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
+    /* assert preconditions */
+    assert(nRequest >= 0);
+    assert(no_elements >= 0);
+    assert(increment > 0);
+    assert(0 <= offset);
+    assert(offset < no_elements);
 
-    if (increment == 1 && dstSize == srcSize) {
-        nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
-        memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
+    if (increment == 1) {
+        long nUpperPart =
+            nRequest < no_elements - offset ? nRequest : no_elements - offset;
+        memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
         if (nRequest > nUpperPart)
-            memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
-        if (isString)
-            for (i = 1; i <= nRequest; i++)
-                pdst[dstSize*i-1] = '\0';
+            memcpy(pdst + (field_size * nUpperPart), psrc,
+                field_size * (nRequest - nUpperPart));
     } else {
-        for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
+        for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
             offset %= no_elements;
-            memcpy(pdst, &psrc[offset*srcSize], dstSize);
-            if (isString) pdst[dstSize-1] = '\0';
-        }
-    }
-}
-
-void dbExtractArrayFromBuf(const void *pfrom, void *pto,
-                           short field_size, short field_type,
-                           long nRequest, long no_elements, long offset, long increment)
-{
-    char *pdst = (char *) pto;
-    char *psrc = (char *) pfrom;
-    int i;
-    short srcSize = field_size;
-    short dstSize = srcSize;
-    char isString = (field_type == DBF_STRING);
-
-    if (nRequest > no_elements) nRequest = no_elements;
-    if (offset > no_elements - 1) offset = no_elements - 1;
-    if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
-
-    if (increment == 1) {
-        memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
-        if (isString)
-            for (i = 1; i <= nRequest; i++)
-                pdst[dstSize*i] = '\0';
-    } else {
-        for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
-            memcpy(pdst, &psrc[offset*srcSize], dstSize);
-            if (isString) pdst[dstSize] = '\0';
+            memcpy(pdst, psrc + (offset * field_size), field_size);
         }
     }
 }
diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
index 7ed3584..9680660 100644
--- a/modules/database/src/ioc/db/dbExtractArray.h
+++ b/modules/database/src/ioc/db/dbExtractArray.h
@@ -21,11 +21,22 @@
 extern "C" {
 #endif
 
-epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
-                                          long nRequest, long no_elements, long offset, long increment);
-epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
-                                          short field_size, short field_type,
-                                          long nRequest, long no_elements, long offset, long increment);
+/*
+ * This function does not do any conversion. This means we don't have to
+ * truncate the field_size to MAX_STRING_SIZE or add extra null terminators
+ * for string values. All of this is done by the dbConvert routines which
+ * will be called whether or not a filter is active.
+ *
+ * Checked preconditions:
+ * - nRequest >= 0, no_elements >= 0, increment > 0
+ * - 0 <= offset < no_elements
+ * Unchecked preconditions:
+ * - pto points to a buffer with at least field_size*nRequest bytes
+ * - pfrom points to a buffer with at least field_size*no_elements bytes
+ */
+epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
+    short field_size, short field_type,
+    long nRequest, long no_elements, long offset, long increment);
 
 #ifdef __cplusplus
 }
diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
index 1193954..206d6c9 100644
--- a/modules/database/src/ioc/db/dbTest.c
+++ b/modules/database/src/ioc/db/dbTest.c
@@ -341,14 +341,14 @@ long dbgf(const char *pname)
     no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
     if (addr.dbr_field_type == DBR_ENUM) {
         long status = dbGetField(&addr, DBR_STRING, pbuffer,
-            &options, &no_elements, NULL);
+            &options, &no_elements);
 
         printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
             no_elements, &msg_Buff, 10);
     }
     else {
         long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
-            &options, &no_elements, NULL);
+            &options, &no_elements);
 
         printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
             no_elements, &msg_Buff, 10);
@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
     ret_options = req_options;
     no_elements = 0;
     status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
-        &ret_options, &no_elements, NULL);
+        &ret_options, &no_elements);
     printBuffer(status, addr.dbr_field_type, pbuffer,
         req_options, ret_options, no_elements, pMsgBuff, tab_size);
 
@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
 
     dbr_type = DBR_STRING;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_CHAR;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_UCHAR;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_SHORT;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_USHORT;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_LONG;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_ULONG;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_INT64;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_UINT64;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_FLOAT;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_DOUBLE;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     dbr_type = DBR_ENUM;
     no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
-    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+    status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
     printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
 
     pmsg[0] = '\0';
@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
 
                 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
                 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
-                    &options, &no_elements, NULL);
+                    &options, &no_elements);
                 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
                     no_elements, pMsgBuff, tab_size);
             }
diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
index 6846ef5..458a28b 100644
--- a/modules/database/src/ioc/db/dbUnitTest.c
+++ b/modules/database/src/ioc/db/dbUnitTest.c
@@ -197,7 +197,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
         return;
     }
 
-    status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
+    status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
     if (status) {
         testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
         return;
@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
         return;
     }
 
-    status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
+    status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
     if (status) {
         testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
 
diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
index 1534517..4b3b82d 100644
--- a/modules/database/src/ioc/db/db_field_log.h
+++ b/modules/database/src/ioc/db/db_field_log.h
@@ -56,20 +56,31 @@ union native_value {
 struct db_field_log;
 typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
 
-/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
+/*
+ * A db_field_log has one of two types:
+ *
+ * dbfl_type_ref - Reference to value
+ *  Used for variable size (array) data types.  Meta-data
+ *  is stored in the field log, but value data is stored externally.
+ *  Only the dbfl_ref side of the data union is valid.
+ *
+ * dbfl_type_val - Internal value
+ *  Used to store small scalar data.  Meta-data and value are
+ *  present in this structure and no external references are used.
+ *  Only the dbfl_val side of the data union is valid.
+ */
 typedef enum dbfl_type {
-    dbfl_type_rec = 0,
     dbfl_type_val,
     dbfl_type_ref
 } dbfl_type;
 
 /* Context of db_field_log: event = subscription update, read = read reply */
 typedef enum dbfl_context {
-    dbfl_context_read = 0,
+    dbfl_context_read,
     dbfl_context_event
 } dbfl_context;
 
-#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
+#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
 
 struct dbfl_val {
     union native_value field; /* Field value */
@@ -81,6 +92,8 @@ struct dbfl_val {
  * db_delete_field_log().  Any code which changes a dbfl_type_ref
  * field log to another type, or to reference different data,
  * must explicitly call the dtor function.
+ * If the dtor is NULL, then this means the array data is still owned
+ * by a record.
  */
 struct dbfl_ref {
     dbfl_freeFunc     *dtor;  /* Callback to free filter-allocated resources */
@@ -88,8 +101,17 @@ struct dbfl_ref {
     void              *field; /* Field value */
 };
 
+/*
+ * Note: The offset member is understood to apply an implicit index mapping
+ *
+ *      i' = (i + offset) % no_elements
+ *
+ * of request index i. The resulting i' is used to to index into u.r.field.
+ *
+ * Also note that field_size may be larger than MAX_STRING_SIZE.
+ */
 typedef struct db_field_log {
-    unsigned int     type:2;  /* type (union) selector */
+    unsigned int     type:1;  /* type (union) selector */
     /* ctx is used for all types */
     unsigned int      ctx:1;  /* context (operation type) */
     /* the following are used for value and reference types */
@@ -97,37 +119,15 @@ typedef struct db_field_log {
     unsigned short     stat;  /* Alarm Status */
     unsigned short     sevr;  /* Alarm Severity */
     short        field_type;  /* DBF type of data */
-    short        field_size;  /* Data size */
-    long        no_elements;  /* No of array elements */
+    short        field_size;  /* Size of a single element */
+    long        no_elements;  /* No of valid array elements */
+    long             offset;  /* See above */
     union {
         struct dbfl_val v;
         struct dbfl_ref r;
     } u;
 } db_field_log;
 
-/*
- * A db_field_log will in one of three types:
- *
- * dbfl_type_rec - Reference to record
- *  The field log stores no data itself.  Data must instead be taken
- *  via the dbChannel* which must always be provided when along
- *  with the field log.
- *  For this type only the 'type' and 'ctx' members are used.
- *
- * dbfl_type_ref - Reference to outside value
- *  Used for variable size (array) data types.  Meta-data
- *  is stored in the field log, but value data is stored externally
- *  (see struct dbfl_ref).
- *  For this type all meta-data members are used.  The dbfl_ref side of the
- *  data union is used.
- *
- * dbfl_type_val - Internal value
- *  Used to store small scalar data.  Meta-data and value are
- *  present in this structure and no external references are used.
- *  For this type all meta-data members are used.  The dbfl_val side of the
- *  data union is used.
- */
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
index f91708a..b90335e 100644
--- a/modules/database/src/std/filters/arr.c
+++ b/modules/database/src/std/filters/arr.c
@@ -12,16 +12,13 @@
 
 #include <stdio.h>
 
-#include <freeList.h>
-#include <dbAccess.h>
-#include <dbExtractArray.h>
-#include <db_field_log.h>
-#include <dbLock.h>
-#include <recSup.h>
-#include <epicsExit.h>
-#include <special.h>
-#include <chfPlugin.h>
-#include <epicsExport.h>
+#include "chfPlugin.h"
+#include "dbExtractArray.h"
+#include "db_field_log.h"
+#include "dbLock.h"
+#include "epicsExit.h"
+#include "freeList.h"
+#include "epicsExport.h"
 
 typedef struct myStruct {
     epicsInt32 start;
@@ -45,6 +42,8 @@ static void * allocPvt(void)
     myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
     if (!my) return NULL;
 
+    /* defaults */
+    my->start = 0;
     my->incr = 1;
     my->end = -1;
     return (void *) my;
@@ -93,78 +92,61 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
 {
     myStruct *my = (myStruct*) pvt;
-    struct dbCommon *prec;
-    rset *prset;
+    int must_lock;
     long start = my->start;
     long end = my->end;
-    long nTarget = 0;
-    long offset = 0;
-    long nSource = dbChannelElements(chan);
-    long capacity = nSource;
-    void *pdst;
+    long nTarget;
+    void *pTarget;
+    /* initial values for the source array */
+    long offset = pfl->offset;
+    long nSource = pfl->no_elements;
 
     switch (pfl->type) {
     case dbfl_type_val:
-        /* Only filter arrays */
+        /* TODO Treat scalars as arrays with 1 element */
         break;
 
-    case dbfl_type_rec:
-        /* Extract from record */
-        if (dbChannelSpecial(chan) == SPC_DBADDR &&
-            nSource > 1 &&
-            (prset = dbGetRset(&chan->addr)) &&
-            prset->get_array_info)
-        {
-            void *pfieldsave = dbChannelField(chan);
-            prec = dbChannelRecord(chan);
-            dbScanLock(prec);
-            prset->get_array_info(&chan->addr, &nSource, &offset);
-            nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
-            pfl->type = dbfl_type_ref;
-            pfl->stat = prec->stat;
-            pfl->sevr = prec->sevr;
-            pfl->time = prec->time;
-            pfl->field_type = dbChannelFieldType(chan);
-            pfl->field_size = dbChannelFieldSize(chan);
-            pfl->no_elements = nTarget;
-            if (nTarget) {
-                pdst = freeListCalloc(my->arrayFreeList);
-                if (pdst) {
-                    pfl->u.r.dtor = freeArray;
-                    pfl->u.r.pvt = my->arrayFreeList;
-                    offset = (offset + start) % dbChannelElements(chan);
-                    dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity,
-                        offset, my->incr);
-                    pfl->u.r.field = pdst;
-                }
-            }
-            dbScanUnlock(prec);
-            dbChannelField(chan) = pfieldsave;
-        }
-        break;
-
-    /* Extract from buffer */
     case dbfl_type_ref:
-        pdst = NULL;
-        nSource = pfl->no_elements;
+        must_lock = !pfl->u.r.dtor;
+        if (must_lock)
+            dbScanLock(dbChannelRecord(chan));
         nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
-        pfl->no_elements = nTarget;
-        if (nTarget) {
-            /* Copy the data out */
-            void *psrc = pfl->u.r.field;
-
-            pdst = freeListCalloc(my->arrayFreeList);
-            if (!pdst) break;
-            offset = start;
-            dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
-                nTarget, nSource, offset, my->incr);
-        }
-        if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
-        if (nTarget) {
+        /*
+         * Side note: it would be nice if we could avoid the copying in
+         * the case of my->incr==1. This is currently not possible due to
+         * the way the offset member is interpreted (namely as shifting the
+         * array in a ring-buffer style).
+         */
+        if (nTarget > 0) {
+            /* copy the data */
+            void *pSource = pfl->u.r.field;
+            pTarget = freeListCalloc(my->arrayFreeList);
+            if (!pTarget) break;
+            offset = (offset + start) % nSource;
+            dbExtractArray(pSource, pTarget, pfl->field_size,
+                pfl->field_type, nTarget, nSource, offset, my->incr);
+            if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
+            pfl->u.r.field = pTarget;
             pfl->u.r.dtor = freeArray;
             pfl->u.r.pvt = my->arrayFreeList;
-            pfl->u.r.field = pdst;
         }
+        /* adjust offset and no_elements to refer to the new pTarget */
+        pfl->offset = 0;
+        /*
+         * Setting pfl->no_elements outside of the "if" clause above is
+         * done to make requests fail if nTarget is zero, that is, if all
+         * elements selected by the filter are outside the array bounds.
+         * TODO:
+         * It would be possible to lift this restriction by interpreting
+         * a request with *no* number of elements (NULL pointer) as scalar
+         * (meaning: fail if we get less than one element); in contrast,
+         * a request that explicitly specifies one element would be
+         * interpreted as an array request, for which zero elements would
+         * be a normal expected result.
+         */
+        pfl->no_elements = nTarget;
+        if (must_lock)
+            dbScanUnlock(dbChannelRecord(chan));
         break;
     }
     return pfl;
diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
index 5925b0b..56c9f5b 100644
--- a/modules/database/src/std/filters/ts.c
+++ b/modules/database/src/std/filters/ts.c
@@ -11,21 +11,39 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include <chfPlugin.h>
-#include <dbLock.h>
-#include <db_field_log.h>
-#include <epicsExport.h>
+#include "chfPlugin.h"
+#include "db_field_log.h"
+#include "dbLock.h"
+#include "epicsExport.h"
+
+/*
+ * The size of the data is different for each channel, and can even
+ * change at runtime, so a freeList doesn't make much sense here.
+ */
+static void freeArray(db_field_log *pfl) {
+    free(pfl->u.r.field);
+}
 
 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
     epicsTimeStamp now;
     epicsTimeGetCurrent(&now);
 
-    /* If string or array, must make a copy (to ensure coherence between time and data) */
-    if (pfl->type == dbfl_type_rec) {
-        dbScanLock(dbChannelRecord(chan));
-        dbChannelMakeArrayCopy(pvt, pfl, chan);
-        dbScanUnlock(dbChannelRecord(chan));
+    /* If reference and not already copied,
+       must make a copy (to ensure coherence between time and data) */
+    if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
+        void *pTarget = calloc(pfl->no_elements, pfl->field_size);
+        void *pSource = pfl->u.r.field;
+        if (pTarget) {
+            dbScanLock(dbChannelRecord(chan));
+            memcpy(pTarget, pSource, pfl->field_size * pfl->no_elements);
+            pfl->u.r.field = pTarget;
+            pfl->u.r.dtor = freeArray;
+            pfl->u.r.pvt = pvt;
+            dbScanUnlock(dbChannelRecord(chan));
+        }
     }
 
     pfl->time = now;
diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
index 8255fdc..ff74e01 100644
--- a/modules/database/test/ioc/db/dbChArrTest.cpp
+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
@@ -130,7 +130,7 @@ static void check(short dbr_type) {
     memset(buf, 0, sizeof(buf)); \
     (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
     pfl = db_create_read_log(pch); \
-    testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
+    testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
     testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
     testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
     if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
@@ -178,6 +178,7 @@ static void check(short dbr_type) {
     pfl->field_type = DBF_CHAR; \
     pfl->field_size = 1; \
     pfl->no_elements = 26; \
+    pfl->offset = 0; \
     pfl->u.r.dtor = freeArray; \
     pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
     testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
index 1ec16b3..08ba07b 100644
--- a/modules/database/test/std/filters/arrTest.cpp
+++ b/modules/database/test/std/filters/arrTest.cpp
@@ -56,25 +56,26 @@ const char *server_port = CA_SERVER_PORT;
 
 static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
     for (int i = 0; i < pfl1->no_elements; i++) {
+        int j = (i + pfl1->offset) % pfl1->no_elements;
         switch (type) {
         case DBR_DOUBLE:
-            if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+            if (((epicsFloat64*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
                 testDiag("at index=%d: field log has %g, should be %d",
-                         i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+                         i, ((epicsFloat64*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
                 return 0;
             }
             break;
         case DBR_LONG:
-            if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+            if (((epicsInt32*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
                 testDiag("at index=%d: field log has %d, should be %d",
-                         i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+                         i, ((epicsInt32*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
                 return 0;
             }
             break;
         case DBR_STRING:
-            if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
+            if (strtol(&((const char*)pfl1->u.r.field)[j*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
                 testDiag("at index=%d: field log has '%s', should be '%d'",
-                         i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
+                         i, &((const char*)pfl1->u.r.field)[j*pfl1->field_size], ((epicsInt32*)p2)[i]);
                 return 0;
             }
             break;
@@ -119,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") {
     off = Offset; \
     (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
     pfl = db_create_read_log(pch); \
-    testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
+    testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
     pfl2 = dbChannelRunPostChain(pch, pfl); \
     testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
     testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c
index 4d70f83..fd4a472 100644
--- a/modules/database/test/std/filters/dbndTest.c
+++ b/modules/database/test/std/filters/dbndTest.c
@@ -129,7 +129,7 @@ MAIN(dbndTest)
     dbEventCtx evtctx;
     int logsFree, logsFinal;
 
-    testPlan(77);
+    testPlan(72);
 
     testdbPrepare();
 
@@ -170,12 +170,9 @@ MAIN(dbndTest)
            "dbnd has one filter with argument in pre chain");
     testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
 
-    /* Field logs of type ref and rec: pass any update */
-
-    testHead("Field logs of type ref and rec");
-    fl1.type = dbfl_type_rec;
-    mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
+    /* Field logs of type ref: pass any update */
 
+    testHead("Field logs of type ref");
     fl1.type = dbfl_type_ref;
     mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
 

Replies:
Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
[Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk

Navigate by Date:
Prev: [Merge] ~bfrk/epics-base:write-filters-rebased into epics-base:7.0 Ben Franksen via Core-talk
Next: Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024 
Navigate by Thread:
Prev: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
Next: Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024 
ANJ, 31 Mar 2020 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·