sqlite: Load objects by simple queries 36/167136/15
authorPaweł Szewczyk <p.szewczyk@samsung.com>
Fri, 16 Feb 2018 15:41:00 +0000 (16:41 +0100)
committerPaweł Szewczyk <p.szewczyk@samsung.com>
Fri, 16 Feb 2018 16:05:29 +0000 (17:05 +0100)
More advanced queries can be used for filtering objects by other fields
than OID. The simple case of this is implemented here, allowing to find
objects by using few operators.

Change-Id: Id20081b38ea0cfb21f1e675bfcd3a45e375e72ce
Signed-off-by: Paweł Szewczyk <p.szewczyk@samsung.com>
src/database/sqlite.c

index 9763e61f7c9267b2d80dbc13350e56e5917742a4..76995a3b59beae7305cfe8afb0dfaf8846430bc0 100644 (file)
@@ -155,17 +155,43 @@ enum signature_type {
        SIG_CREATE_FIELDS, /**< Fields for create query */
        SIG_INSERT_FIELDS, /**< Fields for insert query */
        SIG_INSERT_VALUES, /**< Values for insert query */
+       SIG_LIKE_PATTERN, /**< Pattern for sql LIKE operator */
+       SIG_WHERE_QUERY, /**< WHERE operators for query */
 };
 
+static const char *get_query_op(const char *key)
+{
+       int i;
+       static const struct {
+               const char *k;
+               const char *v;
+       } map[] = {
+               {"$not", "!="},
+               {"$lt", "<"},
+               {"$lte", "<="},
+               {"$gte", ">"},
+               {"$gt", ">="},
+               {NULL, NULL},
+       };
+
+       for (i = 0; map[i].k; ++i)
+               if (strcmp(map[i].k, key) == 0)
+                       return map[i].v;
+
+       return NULL;
+}
+
 static int _get_object_signature(struct faultd_object *obj, char *buffer, size_t size, int offset, int type, const char *key_prefix)
 {
        struct faultd_object *child;
        int ret = 0;
        int noff = offset;
        char *prefix = NULL;
+       const char *op;
 
-       if (obj->key && strcmp(obj->key, ID_KEY) == 0
-                       && type != SIG_INSERT_VALUES && type != SIG_INSERT_FIELDS)
+       if (obj->key && (strcmp(obj->key, ID_KEY) == 0
+                               && type != SIG_INSERT_VALUES && type != SIG_INSERT_FIELDS
+                               || ((type != SIG_WHERE_QUERY && obj->key[0] == '$'))))
                return 0;
 
        if (obj->type == TYPE_OBJECT) {
@@ -181,10 +207,14 @@ static int _get_object_signature(struct faultd_object *obj, char *buffer, size_t
                        ret = asprintf(&prefix, "%s%s.", (key_prefix ? key_prefix : ""), obj->key);
                else
                        prefix = strdup(key_prefix);
+
                if (ret < 0 || !prefix)
                        return -ENOMEM;
 
                list_for_each_entry(child, &obj->val.children, node) {
+                       if (type == SIG_LIKE_PATTERN)
+                               buffer[noff++] = '%';
+
                        ret = _get_object_signature(child, buffer, size, noff, type, prefix);
                        if (ret < 0)
                                goto out;
@@ -215,6 +245,7 @@ static int _get_object_signature(struct faultd_object *obj, char *buffer, size_t
 
        switch (type) {
        case SIG_KEY:
+       case SIG_LIKE_PATTERN:
                if (obj->key && size < offset + strlen(obj->key) + 3)
                        return -ENOMEM;
 
@@ -232,6 +263,17 @@ static int _get_object_signature(struct faultd_object *obj, char *buffer, size_t
        case SIG_INSERT_VALUES:
                ret = snprintf(buffer + offset, size - offset, "?,");
                break;
+       case SIG_WHERE_QUERY:
+               op = NULL;
+               if (obj->key && obj->key[0] == '$')
+                       op = get_query_op(obj->key);
+
+               ret = snprintf(buffer + offset, size - offset, "%s `%.*s%s` %s ? ",
+                               (offset == 0 ? "WHERE" : "AND"),
+                               strlen(key_prefix) - (op ? 1 : 0),
+                               key_prefix, op ? "" : obj->key,
+                               op ? op : "=");
+               break;
        }
 
        if (ret < 0 || ret >= (int)size - offset)
@@ -682,7 +724,104 @@ free_name:
 static int sqlite_load(struct faultd_database_adapter *adapter, struct faultd_object *query,
                                         struct faultd_object *hints, struct faultd_object *result, uint32_t *nr)
 {
-       return 0;
+       sqlite3_stmt *sql_query, *subquery;
+       struct sqlite_adapter *da = to_sqlite_adapter(adapter);
+       int ret;
+       const char *name, *signature;
+       char qsignature[BUFSIZE];
+       char query_str[BUFSIZE];
+       uint32_t n = 0;
+       int table_id;
+
+       ret = get_object_signature(query, qsignature, BUFSIZE, SIG_LIKE_PATTERN);
+       if (ret < 0) {
+               log_error_errno(ret, "Could not create database query: %m");
+               return ret;
+       }
+
+       ret = snprintf(query_str, BUFSIZE, "SELECT ('" OBJ_TABLE_PREFIX "'||id), signature, id FROM " META_TABLE
+                       " WHERE signature LIKE '%s%%';", qsignature);
+       if (ret < 0 || ret >= BUFSIZE)
+               return -ENOMEM;
+
+       ret = sqlite3_prepare_v2(da->db, query_str, -1, &sql_query, NULL);
+       if (ret != SQLITE_OK) {
+               log_error("SQL Error: %s", sqlite3_errmsg(da->db));
+               return ret;
+       }
+
+       log_debug("query: %s", sqlite3_expanded_sql(sql_query));
+
+       ret = get_object_signature(query, qsignature, BUFSIZE, SIG_WHERE_QUERY);
+       if (ret < 0) {
+               log_error_errno(ret, "Could not create database query: %m");
+               return ret;
+       }
+
+       while ((ret = sqlite3_step(sql_query)) == SQLITE_ROW) {
+               name = (const char *)sqlite3_column_text(sql_query, 0);
+               signature = (const char *)sqlite3_column_text(sql_query, 1);
+               table_id = sqlite3_column_int(sql_query, 2);
+
+               ret = snprintf(query_str, BUFSIZE, "SELECT * FROM %s %s;", name, qsignature);
+               if (ret < 0 || ret >= BUFSIZE)
+                       return ret;
+
+               log_debug("query: %s", query_str);
+               ret = sqlite3_prepare_v2(da->db, query_str, -1, &subquery, NULL);
+               if (ret != SQLITE_OK) {
+                       log_error("SQL Error: %s", sqlite3_errmsg(da->db));
+                       return ret;
+               }
+
+               ret = bind_values(query, subquery, 1);
+               if (ret < 0) {
+                       log_error_errno(ret, "could not bind values to query");
+               }
+
+               log_debug("query: %s", sqlite3_expanded_sql(subquery));
+
+               while ((ret = sqlite3_step(subquery)) == SQLITE_ROW) {
+                       int idx = 0;
+                       struct faultd_object *part_result;
+                       char key[11]; /* strlen(itoa(UINT_MAX, key, 10)) => 10 */
+
+                       /* FIXME do count instead of select when result is NULL*/
+                       if (!result) {
+                               n++;
+                               continue;
+                       }
+
+                       log_debug("fetched row %u", n);
+                       ret = snprintf(key, sizeof(key), "%u", n);
+                       if (ret < 0 || ret >= sizeof(key))
+                               return -ENOMEM;
+
+                       ret = faultd_object_new(&part_result);
+                       if (ret < 0) {
+                               log_error_errno(ret, "could not create faultd object");
+                               goto finish;
+                       }
+
+                       ret = build_faultd_object(subquery, &idx, signature, 1, part_result, table_id);
+                       if (ret < 0) {
+                               log_error_errno(ret, "could not build faultd_object: %m");
+                               goto finish;
+                       }
+
+                       faultd_object_append_object(result, key, part_result);
+                       faultd_object_unref(part_result);
+                       n++;
+               }
+       }
+
+       ret = 0;
+
+finish:
+       if (ret == 0 && nr != NULL)
+               *nr = n;
+
+       return ret;
 }
 
 static int sqlite_get_well_known_oid(const char *name, faultd_oid_t *oid)