コード例 #1
0
ファイル: listmgr_recov.c プロジェクト: karig/robinhood
int ListMgr_RecovGetNext( struct lmgr_iterator_t *p_iter,
                          entry_id_t * p_id,
                          attr_set_t * p_info,
                          recov_status_t * last_status )
{
    int            rc = 0;
    char          *result_tab[2+RECOV_FIELD_COUNT]; /* +2 for id and recov_status */
    DEF_PK(pk);
    int entry_disappeared = FALSE;

    do
    {
        entry_disappeared = FALSE;

        rc = db_next_record( &p_iter->p_mgr->conn, &p_iter->select_result,
                             result_tab, RECOV_FIELD_COUNT+2 );

        if ( rc )
            return rc;
        if ( result_tab[0] == NULL ) /* no id? */
            return DB_REQUEST_FAILED;

        if ( sscanf( result_tab[0], SPK, PTR_PK(pk) ) != 1 )
            return DB_REQUEST_FAILED;

        if ( result_tab[1] == NULL ) { /* no status */
            if (last_status)
                *last_status = -1;
        }
        else if (last_status)
            *last_status = str2int(result_tab[1]);

        /* retrieve entry id (except validator) */
        rc = pk2entry_id( p_iter->p_mgr, pk, p_id );

        /* /!\ If the entry disappeared from DB, we must go to next record */
        if ( rc == DB_NOT_EXISTS )
            entry_disappeared = TRUE;
        else if ( rc )
            return rc;

    }
    while ( entry_disappeared );        /* goto next record if entry desappered */

    return result2attrset( T_RECOV, result_tab + 2, RECOV_FIELD_COUNT, p_info );
}
コード例 #2
0
ファイル: listmgr_get.c プロジェクト: cea-hpc/robinhood
/**
 *  Retrieve entry attributes from its primary key
 */
int listmgr_get_by_pk( lmgr_t * p_mgr, PK_ARG_T pk, attr_set_t * p_info )
{
    int             rc;
    char           *first_table = NULL;
    GString        *req, *from;
    /* attribute count is up to 1 per bit (8 per byte).
     * x2 for bullet proofing */
    char           *result_tab[2*8*sizeof(p_info->attr_mask)];
    result_handle_t result;
    bool            checkmain   = true;
    int             main_count  = 0,
                    annex_count = 0,
                    name_count  = 0;
    attr_mask_t     gen = gen_fields(p_info->attr_mask);

    if (p_info == NULL)
        return 0;

    /* init entry info */
    memset(&p_info->attr_values, 0, sizeof(entry_info_t));
    req = g_string_new("SELECT ");
    from = g_string_new(" FROM ");

    /* retrieve source info for generated fields (only about std fields)*/
    add_source_fields_for_gen(&p_info->attr_mask.std);

    /* don't get fields that are not in main, names, annex, stripe...
     * This allows the caller to set all bits 'on' to get everything.
     * Note: this also clear generated fields. They will be restored after.
     */
    supported_bits_only(&p_info->attr_mask);

    /* get info from main table (if asked) */
    main_count = attrmask2fieldlist(req, p_info->attr_mask, T_MAIN, "", "", 0);
    if (main_count < 0)
    {
        rc = -main_count;
        goto free_str;
    }
    else if (main_count > 0)
    {
        checkmain = false;
        first_table = MAIN_TABLE;
        g_string_append(from, MAIN_TABLE);
    }

    annex_count = attrmask2fieldlist(req, p_info->attr_mask, T_ANNEX, "", "",
                                     first_table != NULL ? AOF_LEADING_SEP : 0);
    if (annex_count < 0)
    {
        rc = -annex_count;
        goto free_str;
    }
    else if (annex_count > 0)
    {
        if (first_table != NULL)
            g_string_append_printf(from, " LEFT JOIN "ANNEX_TABLE" ON %s.id="
                                   ANNEX_TABLE".id", first_table);
        else
        {
            first_table = ANNEX_TABLE;
            g_string_append(from, ANNEX_TABLE);
        }
    }

    name_count = attrmask2fieldlist(req, p_info->attr_mask, T_DNAMES, "", "",
                                    first_table != NULL ? AOF_LEADING_SEP : 0);
    if (name_count < 0)
    {
        rc = -name_count;
        goto free_str;
    }
    else if (name_count > 0)
    {
        if (first_table)
            /* it's OK to JOIN with NAMES table here even if there are multiple paths,
             * as we only take one result record. The important thing is to return
             * consistent values for parent_id, name and fullpath. */
            g_string_append_printf(from, " LEFT JOIN "DNAMES_TABLE" ON %s.id="
                                   DNAMES_TABLE".id", first_table);
        else
        {
            first_table = DNAMES_TABLE;
            g_string_append(from, DNAMES_TABLE);
        }
    }

    if (first_table != NULL)
    {
        int shift = 0;

        g_string_append_printf(req, "%s WHERE %s.id="DPK, from->str,
                               first_table, pk);

        rc = db_exec_sql(&p_mgr->conn, req->str, &result);
        if (rc)
            goto free_str;

        rc = db_next_record(&p_mgr->conn, &result, result_tab,
                            main_count + annex_count + name_count);
        /* END_OF_LIST means it does not exist */
        if (rc == DB_END_OF_LIST)
        {
            clean_std_table_bits(&p_info->attr_mask);

            /* not found, but did not check MAIN yet */
            if (checkmain)
                goto next_table;

            rc = DB_NOT_EXISTS;
        }
        if (rc)
            goto free_res;

        /* set info from result */
        if (main_count)
        {
            rc = result2attrset(T_MAIN, result_tab + shift, main_count, p_info);
            shift += main_count;
            if (rc)
                goto free_res;
        }
        if (annex_count)
        {
            rc = result2attrset(T_ANNEX, result_tab + shift, annex_count,
                                p_info);
            shift += annex_count;
            if (rc)
                goto free_res;
        }
        if (name_count)
        {
            rc = result2attrset(T_DNAMES, result_tab + shift, name_count,
                                p_info);
            shift += name_count;
            if (rc)
                goto free_res;
        }

next_table:
        db_result_free(&p_mgr->conn, &result);
    }

    /* remove stripe info if it is not a file */
    if (stripe_fields(p_info->attr_mask) && ATTR_MASK_TEST(p_info, type)
        && strcmp(ATTR(p_info, type), STR_TYPE_FILE) != 0)
    {
        p_info->attr_mask = attr_mask_and_not(&p_info->attr_mask, &stripe_attr_set);
    }

    /* get stripe info if asked */
#ifdef _LUSTRE
    if (stripe_fields(p_info->attr_mask))
    {
        rc = get_stripe_info(p_mgr, pk, &ATTR(p_info, stripe_info),
                             ATTR_MASK_TEST(p_info, stripe_items)?
                                &ATTR(p_info, stripe_items) : NULL);
        if (rc == DB_ATTR_MISSING || rc == DB_NOT_EXISTS)
        {
            /* stripe info is in std mask */
            p_info->attr_mask.std &= ~ATTR_MASK_stripe_info;

            if (ATTR_MASK_TEST(p_info, stripe_items))
                p_info->attr_mask.std &= ~ATTR_MASK_stripe_items;
        }
        else if (rc)
            goto free_str;
        else
            checkmain = false; /* entry exists */
    }
#else
    /* POSIX: always clean stripe bits */
    p_info->attr_mask = attr_mask_and_not(&p_info->attr_mask, &stripe_attr_set);
#endif

    /* special field dircount */
    if (dirattr_fields(p_info->attr_mask))
    {
        if (listmgr_get_dirattrs(p_mgr, pk, p_info))
        {
            DisplayLog(LVL_MAJOR, LISTMGR_TAG, "listmgr_get_dirattrs failed for "DPK, pk);
            p_info->attr_mask = attr_mask_and_not(&p_info->attr_mask, &dir_attr_set);
        }
    }

    if (checkmain)
    {
        /* verify it exists in main table */
        g_string_printf(req, "SELECT id FROM " MAIN_TABLE " WHERE id="DPK, pk);

        /* execute the request */
        rc = db_exec_sql(&p_mgr->conn, req->str, &result);
        if (rc)
            goto free_str;

        rc = db_next_record(&p_mgr->conn, &result, result_tab, 1);
        db_result_free(&p_mgr->conn, &result);
        if (rc)
        {
            rc = DB_NOT_EXISTS;
            goto free_str;
        }
    }

    /* restore generated fields in attr mask */
    p_info->attr_mask = attr_mask_or(&p_info->attr_mask, &gen);
    /* generate them */
    generate_fields(p_info);

    /* update operation stats */
    p_mgr->nbop[OPIDX_GET]++;

    rc = DB_SUCCESS;
    goto free_str;

  free_res:
    db_result_free(&p_mgr->conn, &result);
  free_str:
    g_string_free(req, TRUE);
    g_string_free(from, TRUE);
    return rc;
} /* listmgr_get_by_pk */
コード例 #3
0
ファイル: listmgr_ns.c プロジェクト: karig/robinhood
/**
 * Get the list of children of a given parent (or list of parents).
 * \param parent_list       [in]  list of parents to get the child of
 * \param parent_count      [in]  number of ids in parent list
 * \param attr_mask         [in]  required attributes for children
 * \param child_id_list     [out] ptr to array of child ids
 * \param child_attr_list   [out] ptr to array of child attrs
 * \param child_count       [out] number of returned children
 */
int ListMgr_GetChild( lmgr_t * p_mgr, const lmgr_filter_t * p_filter,
                      const wagon_t * parent_list, unsigned int parent_count,
                      int attr_mask,
                      wagon_t ** child_id_list, attr_set_t ** child_attr_list,
                      unsigned int * child_count)
{
    result_handle_t result;
    char *curr;
    int  filter_main = 0;
    int  filter_annex = 0;
    int main_attrs = 0;
    int dnames_attrs = 0;
    int annex_attrs = 0;
    char query[4096];
    char fieldlist_main[1024] = "";
    char fieldlist_dnames[1024] = "";
    char fieldlist_annex[1024] = "";
    char filter_str_main[1024] = "";
    char filter_str_annex[1024] = "";
    char tmp[2048];
    char *path = NULL;
    int path_len;
    char * pc;
    int rc, i;

    /* TODO: querying children from several parent cannot work, since
     * we need to get the paths of the children. Or we could do a
     * lookup into parent_list to find the right one. In the meantime,
     * try not to mess up the code. */
    if (parent_count != 1)
        RBH_BUG("cannot get children for several parent simultaneously");

    /* always request for name to build fullpath in wagon */
    attr_mask |= ATTR_MASK_name;

    /* request is always on the MAIN table (which contains [parent_id, id] relationship */

    /* /!\ possible cases:
     * - simplest: the fields of the filter and the attributes to be retrieved are in the MAIN table
     * - harder: the fields of the filter and attributes are in a different table
     */

    /* 1) location of filters */
    if ( p_filter )
    {
        char           dummy_str[1024];
        unsigned int   dummy_uint;
        if (dir_filter(p_mgr, dummy_str, p_filter, &dummy_uint) != FILTERDIR_NONE)
        {
            DisplayLog( LVL_MAJOR, LISTMGR_TAG, "Directory filter not supported in %s()", __func__ );
            return DB_NOT_SUPPORTED;
        }
        else if (func_filter(p_mgr, dummy_str, p_filter, T_MAIN, FALSE, FALSE))
        {
            DisplayLog( LVL_MAJOR, LISTMGR_TAG, "Function filter not supported in %s()", __func__ );
            return DB_NOT_SUPPORTED;
        }

        /* There is always a filter on T_DNAMES, which is the parent condition.
         * Look for optional filters:
         */
        filter_main = filter2str( p_mgr, filter_str_main, p_filter, T_MAIN,
                                  FALSE, TRUE );

        if ( annex_table )
            filter_annex = filter2str( p_mgr, filter_str_annex, p_filter,
                                       T_ANNEX, FALSE, TRUE );
        else
            filter_annex = 0;

        /* @TODO to be implemented */
#if 0
        filter_stripe_info =
            filter2str( p_mgr, filter_str_stripe_info, p_filter, T_STRIPE_INFO,
                        ( filter_main > 0 ) || ( filter_annex > 0 ), TRUE );

        filter_stripe_items =
            filter2str( p_mgr, filter_str_stripe_items, p_filter, T_STRIPE_ITEMS,
                        ( filter_main > 0 ) || ( filter_annex > 0 )
                        || ( filter_stripe_info > 0 ), TRUE );
#endif
    }

    /* 2) location of requested attributes */
    if (attr_mask)
    {
        /* retrieve source info for generated fields */
        add_source_fields_for_gen( &attr_mask );

        main_attrs = attrmask2fieldlist( fieldlist_main, attr_mask, T_MAIN,
                                         /* leading comma */ TRUE, /* for update */ FALSE,
                                         /* prefix */ MAIN_TABLE".", /* postfix */ "" );

        dnames_attrs += attrmask2fieldlist( fieldlist_dnames, attr_mask, T_DNAMES,
                                            /* leading comma */ TRUE, /* for update */ FALSE,
                                            /* prefix */ DNAMES_TABLE".", /* postfix */ "" );

        if ( annex_table )
            annex_attrs = attrmask2fieldlist( fieldlist_annex, attr_mask, T_ANNEX,
                                             /* leading comma */ TRUE, /* for update */ FALSE,
                                             /* prefix */ ANNEX_TABLE".", /* postfix */ "" );
        else
            annex_attrs = 0;
    }
    else
    {
        /* no returned attrs */
        if (child_attr_list)
            *child_attr_list = NULL;
    }
    pc = parent_cond(p_mgr, tmp, sizeof(tmp), parent_list, parent_count, DNAMES_TABLE".");
    if (!pc)
        return DB_BUFFER_TOO_SMALL;

    curr = query;

    /* SELECT clause */
    /* id + dname fields */
    curr += sprintf(curr, "SELECT "DNAMES_TABLE".id%s", fieldlist_dnames);
    /* main attrs */
    if (main_attrs)
        curr += sprintf(curr, "%s", fieldlist_main);
    /* annex attrs */
    if (annex_attrs)
        curr += sprintf(curr, "%s", fieldlist_annex);

    /* FROM clause */
    curr += sprintf(curr, " FROM "DNAMES_TABLE);
    if (main_attrs || filter_main)
        curr += sprintf(curr, " LEFT JOIN "MAIN_TABLE
                              " ON "DNAMES_TABLE".id="MAIN_TABLE".id");
    if (annex_attrs || filter_annex)
        curr += sprintf(curr, " LEFT JOIN "ANNEX_TABLE
                              " ON "DNAMES_TABLE".id="ANNEX_TABLE".id");

    /* WHERE clause */
    curr += sprintf(curr, " WHERE %s", pc);
    if (filter_main)
        curr += sprintf(curr, " AND %s", filter_str_main);
    if (filter_annex)
        curr += sprintf(curr, " AND %s", filter_str_annex);

retry:
    rc = db_exec_sql(&p_mgr->conn, query, &result);
    if (lmgr_delayed_retry(p_mgr, rc))
        goto retry;
    else if (rc)
        return rc;

    /* copy result to output structures */
    *child_count = db_result_nb_records(&p_mgr->conn, &result);

    /* allocate entry_id array */
    *child_id_list = MemCalloc(*child_count, sizeof(wagon_t));
    if (*child_id_list == NULL)
        return DB_NO_MEMORY;

    if (child_attr_list)
    {
        *child_attr_list = MemCalloc(*child_count, sizeof(attr_set_t));
        if (*child_attr_list == NULL)
        {
            rc = DB_NO_MEMORY;
            goto array_free;
        }
    }

    /* Allocate a string long enough to contain the parent path and a
     * child name. */
    path_len = strlen(parent_list[0].fullname) + RBH_NAME_MAX + 2;
    path = malloc(path_len);
    if (!path) {
        DisplayLog( LVL_MAJOR, LISTMGR_TAG, "Can't alloc enough memory (%d bytes)",
                    path_len );
        rc = DB_NO_MEMORY;
        goto array_free;
    }

    for (i = 0; i < *child_count; i++)
    {
        char *res[128]; /* 128 fields per row is large enough */
        rc = db_next_record(&p_mgr->conn, &result, res, 128);
        if ( rc )
            goto array_free;

        /* copy id to array */
        pk2entry_id(p_mgr, res[0], &((*child_id_list)[i].id));

        /* copy attributes to array */
        if (child_attr_list)
        {
            (*child_attr_list)[i].attr_mask = attr_mask;

            /* first id, then dnames attrs, then main attrs, then annex attrs */
            if (dnames_attrs)
            {
                /* shift of 1 for id */
                rc = result2attrset( T_DNAMES, res + 1, dnames_attrs, &((*child_attr_list)[i]) );
                if ( rc )
                    goto array_free;
            }

            if (main_attrs)
            {
                /* first id, then main attrs, then annex attrs */
                /* shift of 1 for id */
                rc = result2attrset( T_MAIN, res + dnames_attrs + 1, main_attrs, &((*child_attr_list)[i]) );
                if ( rc )
                    goto array_free;
            }

            if (annex_attrs)
            {
                /* shift of main_attrs count */
                rc = result2attrset( T_ANNEX, res + dnames_attrs + main_attrs + 1, annex_attrs,
                                     &((*child_attr_list)[i]) );
                if ( rc )
                    goto array_free;
            }

#ifdef _LUSTRE
            if (stripe_fields(attr_mask))
            {
                if (get_stripe_info( p_mgr, res[0], &ATTR(&(*child_attr_list)[i], stripe_info),
                                     &ATTR(&(*child_attr_list)[i], stripe_items) ))
                {
                    ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_info);
                    ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_items);
                }
            }
#endif

            generate_fields(&((*child_attr_list)[i]));

            /* Note: path is properly sized already to not overflow. */
            sprintf(path, "%s/%s", parent_list[0].fullname,
                    (*child_attr_list)[i].attr_values.name);
            (*child_id_list)[i].fullname = strdup(path);
        }
    }

    if (path)
        free(path);
    db_result_free( &p_mgr->conn, &result );
    return 0;

array_free:
    if (path)
        free(path);
    if (child_attr_list && *child_attr_list)
    {
        MemFree(*child_attr_list);
        *child_attr_list = NULL;
    }
    MemFree(*child_id_list);
    *child_id_list = NULL;
    return rc;
}
コード例 #4
0
ファイル: listmgr_get.c プロジェクト: karig/robinhood
/**
 *  Retrieve entry attributes from its primary key
 */
int listmgr_get_by_pk( lmgr_t * p_mgr, PK_ARG_T pk, attr_set_t * p_info )
{
    int            rc;
    char           fieldlist[4096] = "";
    char          *first_table = NULL;
    char           from[1024] = "";
    char           query[4096] = "";
    /* we assume there is not more than 128 fields */
    char          *result_tab[128];
    result_handle_t result;
    int checkmain = 1;
    int main_count = 0, annex_count = 0, name_count = 0;

    if (p_info == NULL)
        return 0;

    /* init entry info */
    memset( &p_info->attr_values, 0, sizeof( entry_info_t ) );
    fieldlist[0] = '\0';

    /* retrieve source info for generated fields */
    add_source_fields_for_gen( &p_info->attr_mask );

    /* get info from main table (if asked) */
    main_count = attrmask2fieldlist(fieldlist, p_info->attr_mask, T_MAIN, FALSE,
                                    FALSE, "", "");
    if (main_count < 0)
        return -main_count;
    else if (main_count > 0)
    {
        checkmain = 0;
        first_table = MAIN_TABLE;
        sprintf(from, MAIN_TABLE);
    }

    annex_count = attrmask2fieldlist(fieldlist + strlen(fieldlist),
                                     p_info->attr_mask, T_ANNEX,
                                     first_table != NULL, FALSE, "", "");
    if (annex_count < 0)
        return -annex_count;
    else if (annex_count > 0)
    {
        if (first_table)
            sprintf(from + strlen(from), " LEFT JOIN "ANNEX_TABLE" ON %s.id="
                    ANNEX_TABLE".id", first_table);
        else
        {
            first_table = ANNEX_TABLE;
            sprintf(from, ANNEX_TABLE);
        }
    }

    name_count = attrmask2fieldlist(fieldlist + strlen(fieldlist),
                                    p_info->attr_mask, T_DNAMES,
                                    first_table != NULL, FALSE, "", "");
    if (name_count < 0)
        return -name_count;
    else if (name_count > 0)
    {
        if (first_table)
            /* it's OK to JOIN with NAMES table here even if there are multiple paths,
             * as we only take one result record. The important thing is to return
             * consistent values for parent_id, name and fullpath. */
            sprintf(from + strlen(from), " LEFT JOIN "DNAMES_TABLE" ON %s.id="
                    DNAMES_TABLE".id", first_table);
        else
        {
            first_table = DNAMES_TABLE;
            sprintf(from, DNAMES_TABLE);
        }
    }

    if (first_table != NULL)
    {
        int shift = 0;
        sprintf(query, "SELECT %s FROM %s WHERE %s.id="DPK, fieldlist, from,
                first_table, pk);

        rc = db_exec_sql(&p_mgr->conn, query, &result);
        if (rc)
            return rc;

        rc = db_next_record(&p_mgr->conn, &result, result_tab,
                            main_count + annex_count + name_count);
        /* END_OF_LIST means it does not exist */
        if (rc == DB_END_OF_LIST)
            rc = DB_NOT_EXISTS;
        if (rc)
            goto free_res;

        /* set info from result */
        if (main_count)
        {
            rc = result2attrset(T_MAIN, result_tab + shift, main_count, p_info);
            shift += main_count;
            if (rc)
                goto free_res;
        }
        if (annex_count)
        {
            rc = result2attrset(T_ANNEX, result_tab + shift, annex_count,
                                p_info);
            shift += annex_count;
            if (rc)
                goto free_res;
        }
        if (name_count)
        {
            rc = result2attrset(T_DNAMES, result_tab + shift, name_count,
                                p_info);
            shift += name_count;
            if (rc)
                goto free_res;
        }

        db_result_free(&p_mgr->conn, &result);
    }

    /* remove stripe info if it is not a file */
    if (stripe_fields(p_info->attr_mask) && ATTR_MASK_TEST(p_info, type)
        && strcmp(ATTR(p_info, type), STR_TYPE_FILE) != 0)
    {
        p_info->attr_mask &= ~stripe_attr_set;
    }

    /* get stripe info if asked */
#ifdef _LUSTRE
    if (stripe_fields( p_info->attr_mask ))
    {
        rc = get_stripe_info( p_mgr, pk, &ATTR( p_info, stripe_info ),
                              ATTR_MASK_TEST( p_info, stripe_items ) ? &ATTR( p_info,
                                                                              stripe_items ) :
                              NULL );
        if ( rc == DB_ATTR_MISSING || rc == DB_NOT_EXISTS )
        {
            p_info->attr_mask &= ~ATTR_MASK_stripe_info;

            if ( ATTR_MASK_TEST( p_info, stripe_items ) )
                p_info->attr_mask &= ~ATTR_MASK_stripe_items;
        }
        else if ( rc )
            return rc;
        else
            checkmain = 0; /* entry exists */
    }
#else
    /* always clean them */
    p_info->attr_mask &= ~(ATTR_MASK_stripe_info | ATTR_MASK_stripe_items);
#endif

    /* special field dircount */
    if (dirattr_fields( p_info->attr_mask ))
    {
        if (listmgr_get_dirattrs(p_mgr, pk, p_info))
        {
            DisplayLog( LVL_MAJOR, LISTMGR_TAG, "listmgr_get_dirattrs failed for "DPK, pk );
            p_info->attr_mask &= ~dir_attr_set;
        }
    }

    if (checkmain)
    {
        /* verify it exists in main table */
        sprintf( query, "SELECT id FROM " MAIN_TABLE " WHERE id="DPK, pk );

        /* execute the request */
        rc = db_exec_sql( &p_mgr->conn, query, &result );
        if ( rc )
            return rc;

        rc = db_next_record( &p_mgr->conn, &result, result_tab, 1 );
        db_result_free( &p_mgr->conn, &result );
        if (rc)
            return DB_NOT_EXISTS;
    }

    /* compute generated fields if asked */
    generate_fields( p_info );

    p_mgr->nbop[OPIDX_GET]++;

    return DB_SUCCESS;

  free_res:
    db_result_free( &p_mgr->conn, &result );
    return rc;
}                               /* listmgr_get_by_pk */
コード例 #5
0
ファイル: listmgr_ns.c プロジェクト: cea-hpc/robinhood
/**
 * Get the list of children of a given parent (or list of parents).
 * \param parent_list       [in]  list of parents to get the child of
 * \param parent_count      [in]  number of ids in parent list
 * \param attr_mask         [in]  required attributes for children
 * \param child_id_list     [out] ptr to array of child ids
 * \param child_attr_list   [out] ptr to array of child attrs
 * \param child_count       [out] number of returned children
 */
int ListMgr_GetChild(lmgr_t *p_mgr, const lmgr_filter_t *p_filter,
                     const wagon_t *parent_list, unsigned int parent_count,
                     attr_mask_t attr_mask,
                     wagon_t **child_id_list, attr_set_t **child_attr_list,
                     unsigned int *child_count)
{
    result_handle_t result;
    char *path = NULL;
    int path_len;
    int                rc, i;
    GString           *req = NULL;
    GString           *fields = NULL;
    GString           *from = NULL;
    GString           *where = NULL;
    struct field_count field_cnt = {0};
    struct field_count filter_cnt = {0};
    table_enum         query_tab = T_DNAMES;
    bool               distinct = false;
    int                retry_status;

    /* XXX: querying children from several parent cannot work, since
     * we need to get the paths of the children. Or we could do a
     * lookup into parent_list to find the right one. In the meantime,
     * try not to mess up the code. */
    if (unlikely(parent_count != 1))
        RBH_BUG("cannot get children for several parent simultaneously");

    /* always request for name to build fullpath in wagon */
    attr_mask_set_index(&attr_mask, ATTR_INDEX_name);

    fields = g_string_new(NULL);

    /* append fields for all tables */
    if (!attr_mask_is_null(attr_mask))
    {
        /* retrieve source info for generated fields */
        add_source_fields_for_gen(&attr_mask.std);

        field_cnt.nb_names = attrmask2fieldlist(fields, attr_mask, T_DNAMES,
                                                DNAMES_TABLE".", "",
                                                AOF_LEADING_SEP);

        field_cnt.nb_main = attrmask2fieldlist(fields, attr_mask, T_MAIN,
                                               MAIN_TABLE".", "",
                                               AOF_LEADING_SEP);

        field_cnt.nb_annex = attrmask2fieldlist(fields, attr_mask, T_ANNEX,
                                                ANNEX_TABLE".", "",
                                                AOF_LEADING_SEP);
    }
    else
    {
        /* no returned attrs */
        if (child_attr_list != NULL)
            *child_attr_list = NULL;
    }

    where = g_string_new(NULL);

    /* starts with condition on parent */
    rc = append_parent_cond(p_mgr, where, parent_list, parent_count, DNAMES_TABLE".");
    if (rc != DB_SUCCESS)
        goto free_str;

    /* check filters on other tables */
    if (!no_filter(p_filter))
    {
        if (unlikely(dir_filter(p_mgr, NULL, p_filter, NULL, NULL) != FILTERDIR_NONE))
        {
            DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Directory filter not supported in %s()", __func__);
            rc = DB_NOT_SUPPORTED;
            goto free_str;
        }
        else if (unlikely(func_filter(p_mgr, NULL, p_filter, T_MAIN, 0)))
        {
            DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Function filter not supported in %s()", __func__);
            rc = DB_NOT_SUPPORTED;
            goto free_str;
        }

        /* There is always a filter on T_DNAMES, which is the parent condition.
         * Look for optional filters.
         */
        filter_where(p_mgr, p_filter, &filter_cnt, where,
                     AOF_LEADING_SEP | AOF_SKIP_NAME);
        /** @FIXME process other filters on NAMES */
    }

    from = g_string_new(DNAMES_TABLE);

    /* add filter_count + field_count to build the FROM clause.
     * Preserve field count which is needed to interpret the result.
     */
    filter_cnt.nb_main += field_cnt.nb_main;
    filter_cnt.nb_annex += field_cnt.nb_annex;
    filter_cnt.nb_names += field_cnt.nb_names;
    /* query tab is DNAMES, skip_name=true, is_first_tab=T_DNAMES */
    filter_from(p_mgr, &filter_cnt, from, &query_tab, &distinct,
                AOF_LEADING_SEP | AOF_SKIP_NAME);

    /* request is always on the DNAMES table (which contains [parent_id, id] relationship */
    if (distinct)
        req = g_string_new("SELECT DISTINCT("DNAMES_TABLE".id) as id");
    else
        req = g_string_new("SELECT "DNAMES_TABLE".id as id");

    /* build the whole request */
    g_string_append_printf(req, "%s FROM %s WHERE %s", fields->str, from->str, where->str);

retry:
    rc = db_exec_sql(&p_mgr->conn, req->str, &result);
    retry_status = lmgr_delayed_retry(p_mgr, rc);
    if (retry_status == 1)
        goto retry;
    else if (retry_status == 2) {
        rc = DB_RBH_SIG_SHUTDOWN;
        goto free_str;
    } else if (rc)
        goto free_str;

    /* copy result to output structures */
    *child_count = db_result_nb_records(&p_mgr->conn, &result);

    /* allocate entry_id array */
    *child_id_list = MemCalloc(*child_count, sizeof(wagon_t));
    if (*child_id_list == NULL)
    {
        rc = DB_NO_MEMORY;
        goto free_str;
    }

    if (child_attr_list)
    {
        *child_attr_list = MemCalloc(*child_count, sizeof(attr_set_t));
        if (*child_attr_list == NULL)
        {
            rc = DB_NO_MEMORY;
            goto array_free;
        }
    }

    /* Allocate a string long enough to contain the parent path and a
     * child name. */
    path_len = strlen(parent_list[0].fullname) + RBH_NAME_MAX + 2;
    path = malloc(path_len);
    if (!path) {
        DisplayLog(LVL_MAJOR, LISTMGR_TAG, "Can't alloc enough memory (%d bytes)",
                    path_len);
        rc = DB_NO_MEMORY;
        goto array_free;
    }

    for (i = 0; i < *child_count; i++)
    {
        char *res[128]; /* 128 fields per record is large enough */

        rc = db_next_record(&p_mgr->conn, &result, res, sizeof(res)/sizeof(*res));
        if (rc)
            goto array_free;

        /* copy id to array */
        pk2entry_id(p_mgr, res[0], &((*child_id_list)[i].id));

        /* copy attributes to array */
        if (child_attr_list)
        {
            unsigned int shift = 1; /* first was NAMES.id */

            (*child_attr_list)[i].attr_mask = attr_mask;

            /* first id, then dnames attrs, then main attrs, then annex attrs */
            if (field_cnt.nb_names > 0)
            {
                /* shift of 1 for id */
                rc = result2attrset(T_DNAMES, res + shift, field_cnt.nb_names, &((*child_attr_list)[i]));
                if (rc)
                    goto array_free;
                shift += field_cnt.nb_names;
            }

            if (field_cnt.nb_main > 0)
            {
                /* first id, then main attrs, then annex attrs */
                /* shift of 1 for id */
                rc = result2attrset(T_MAIN, res + shift, field_cnt.nb_main, &((*child_attr_list)[i]));
                if (rc)
                    goto array_free;
                shift += field_cnt.nb_main;
            }

            if (field_cnt.nb_annex > 0)
            {
                /* shift of main_attrs count */
                rc = result2attrset(T_ANNEX, res + shift, field_cnt.nb_annex,
                                     &((*child_attr_list)[i]));
                if (rc)
                    goto array_free;
                shift += field_cnt.nb_annex;
            }

#ifdef _LUSTRE
            if (stripe_fields(attr_mask))
            {
                if (get_stripe_info(p_mgr, res[0], &ATTR(&(*child_attr_list)[i], stripe_info),
                                     &ATTR(&(*child_attr_list)[i], stripe_items)))
                {
                    ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_info);
                    ATTR_MASK_UNSET(&(*child_attr_list)[i], stripe_items);
                }
            }
#endif

            generate_fields(&((*child_attr_list)[i]));

            /* Note: path is properly sized already to not overflow. */
            snprintf(path, path_len, "%s/%s", parent_list[0].fullname,
                     (*child_attr_list)[i].attr_values.name);
            (*child_id_list)[i].fullname = strdup(path);
        }
    }

    if (path)
        free(path);

    db_result_free(&p_mgr->conn, &result);
    g_string_free(req, TRUE);
    g_string_free(fields, TRUE);
    g_string_free(from, TRUE);
    g_string_free(where, TRUE);
    return 0;

array_free:
    if (path)
        free(path);
    if (child_attr_list && *child_attr_list)
    {
        MemFree(*child_attr_list);
        *child_attr_list = NULL;
    }
    MemFree(*child_id_list);
    *child_id_list = NULL;
free_str:
    g_string_free(req, TRUE);
    g_string_free(fields, TRUE);
    g_string_free(from, TRUE);
    g_string_free(where, TRUE);
    return rc;
}