Cdb_get_objects() returns only one object

I have a 200+ records long list. Each list item has 13 leaves. According to documentation, I can get several items at once using something like this:

// ds : data session socket;
// cpath: simple path to the whole list;
// N=13; start=0; limit=10;
// tmp: confd_value_t *, allocated N*limit*sizeof(confd_value_t) bytes

rescount = cdb_get_objects(ds, tmp, N, start, limit, cpath)

// rescount=13;

No errors or exceptions, I just always get one object.
It correctly processes the “start” variable: I get one object of given position.
What I’m doing wrong?

ConfD 7.3

You can use the confd_cmd tool implementation as a reference.
See $CONFD_DIR/src/confd/tools/confd_cmd.c

$ cat $CONFD_DIR/src/confd/tools/confd_cmd.c |grep -B8 -A12 ' cdb_get_objects'
static void do_cdb_get_objects(char *argv[]) /* <start> <num> <key> */
{
    int ix = atoi(argv[0]);
    int nobj = atoi(argv[1]);
    int max_elems = max_object_elems(confd_cs_node_cd(NULL, argv[2]));
    confd_value_t val[nobj*max_elems];
    char tmpbuf[BUFSIZ]; int o, i, ret;

    ret = cdb_get_objects(cs, val, max_elems, ix, nobj, argv[2]);
    assert(ret > 0 && ret <= max_elems);
    for (o = 0; o < nobj; o++) {
        /* we assume that we have the same number of values in each object */
        for (i = 0; i < ret; i++) {
            confd_pp_value(tmpbuf, BUFSIZ, &val[o*max_elems+i]);
            printf("%s\n", tmpbuf);
            confd_free_value(&val[o*max_elems+i]);
        }
        if (o < nobj - 1)
            printf("\n");
    }
}

Demo:

$ cd examples.confd/restconf/yang-patch
$ make all start
$ confd_cmd -dd -c 'get_objects 0 8 "/jukebox/library/artist{Foo\ Fighters}/album{Wasting\ Light}/song"'
get_objects "0" "8" "/jukebox/library/artist{Foo\ Fighters}/album{Wasting\ Light}/song"
TRACE Connected (maapi) to ConfD
TRACE MAAPI_LOAD_ALL_NS
TRACE MAAPI_LOAD_MNS_MAPS
TRACE MAAPI_LOAD_HASH_DB
TRACE Connected (cdb) to ConfD
TRACE CDB_NEW_SESSION  --> CONFD_OK
TRACE Established new CDB session to ConfD
TRACE CDB_GET_OBJECTS /jukebox/library/artist{Foo\ Fighters}/album{Wasting\ Light}/song --> CONFD_OK
A Matter Of Time
a_matter_of_time.mp3
NOEXISTS
276

Arlandria
arlandria.mp3
NOEXISTS
267

Back And Forth
back_and_forth.mp3
NOEXISTS
231

Bridge Burning
bridge_burning.mp3
NOEXISTS
286

I Should Have Known
i_should_have_known.mp3
NOEXISTS
255

These Days
these_days.mp3
NOEXISTS
298

Walk
walk.mp3
NOEXISTS
255

White Limo
white_limo.mp3
NOEXISTS
202

According to this code and some trace messages, cdb_get_objects() returns only the length of one object. But it is already known before calling this function. I’m confused by that logic.
I think, the more comfortable behavior should be:
If ix+nobj-1 is outside the range of actually existing list entries, fill in maximum available confd_value_t entries and return actual number of objects (instead of CONFD_ERR_NOEXISTS or just length of one object).

Anyway, thanks. Now the direction to dig into is more clear to me.

From the confd_lib_cdb(3) man page regarding the return value from cdb_get_objects():
“On success, the highest actual number of values in any of the list entries read is returned.”

Using the confd_cmd.c implementation as an example, it checks the schema, max_object_elems(), to get the highest number of values in the list entry / table row / object. But the list may not have all of those values set in any of the list entries so the return value will tell you what the max actually is.

Yes, this is what I got wrong. Thanks. My code is working now.
But that logic is very strange. “The highest actual number of values in any of the list entries” is known before this function call (in order to allocate memory for example) and even passed as an argument.

Right, but the return value is the “highest number of values set in a list entry”. Say the highest number of possible values is 13, as passed to cdb_get_objects(), but no list entries have values set for more than 10 values. cdb_get_objects() will then return 10.

So… If some fields are not mandatory, and some of them are not set in selected set, I’ll get 10 instead of 13?
It means that I’ll get non-uniform objects?

Let’s say I have a list of objects with A(mandatory), B(optional) and C(optional) fields.
And I have a subset with A and B, or A and C fields set.
Does it mean, that I’ll have 2 as return value and random set of A,B and A,C sets from cdb_get_objects()?

What da… mess!
Very surprising and disgusting. Weird logic.

Well, perhaps surprising, but I would not use drama words like “disgusting” and “weird” to describe it. You will of course get 13 with a minimum of 3 entries set as “NOEXISTS”. The returned 10 number is the max number of values set in a list entry. Someone needed that info once upon a time so it is provided too.

Oh wait…
You say that an example with A, B and C will still return 3 and objects will be uniform with “NOEXISTS” as confd_value_t.xmltag.tag for unset values? Ok, I can handle this if objects are uniform.
I just need to know exactly how this function returns data. I’m very confused for now.

Yes, that was a bit confusing I admit. Let’s try again.

int cdb_get_objects(int sock, confd_value_t *values, int n, int ix, int nobj, const char *fmt, ...);

Say you do not check or know the max number of values each list entry can have. I.e. you don’t check the schema and you do not have access to the info from the YANG model.
Now you set n to 100, i.e. you have space for max 100 values, but the max number of possible values is 5. cdb_get_objects() will return that info incase you did not know that already.

That’s the point. I always check the schema and aware of YANG model.
And I prefer to have a real number of retrieved objects than something I already know via cdb_num_instances() or so.

But let’s return to our ABC example.

A is mandatory. B and C are optional.
There are 3 list records in CDB:
1: A=1 B=1
2: A=2 C=1
3: A=3 B=1 C=1

Code:
confd_value_t tmp[3*2];
int ret = cdb_get_objects(ds, &tmp, 3, 0, 2, path);

What will be ret and what structure to expect in tmp?

Option 1:
ret == 2
tmp: [1, 1, 2, 1 , null, null]

Option 2:
ret == 3
tmp: [1, 1, NOEXISTS, 2, NOEXISTS, 1]

Everyone’s application are/was not aware of the YANG model, especially back in the CLI+SNMP centric stone age. Loading the schema is optional.

cdb_num_instances() give you the number of list entries / table rows / objects in a list. The cdb_get_objects() return value is the max number of values returned for each list entry / table row / object.
You already know the number of retrieved objects/list entries/rows from the list, which is nobj.
nobj cannot be larger than the number of entries in the list or cdb_get_objects() will return an error.

Option 2.
The original list of objects.
cdb_num_instances() = 3
max_object_elems() = 3

   A B C
   - - -
1: 1 1 x
2: 2 x 1
3: 3 1 1

ret = cdb_get_objects(…, &tmp, n = 3, ix = 0, nobj = 2, …)
ret = 3;
tmp:[1,1,NOEXISTS,2,NOEXISTS,1]

tmp visualized as a table:

   A B C
   - - -
1: 1 1 x
2: 2 x 1

Thanks, it’s very helpful.

But I still think that cdb_get_objects() behavior can be improved.

Yes, cdb_num_instances() return the number of objects. I use confd_max_object_size() to determine the object size. So cdb_get_objects() return value is predictable and almost useless. I thought this function is more intelligent. It could fetch less than “nobj” objects and return the actual number of fetched objects (or actual number of filled structs).

Any application can call cdb_num_instances() to find out the number of objects in the list. But only applications that have loaded the schema information from the ConfD daemon will be able to use the confd_max_object_size() function to get the object size.