From: Paweł Szewczyk Date: Mon, 15 Jan 2018 15:55:53 +0000 (+0100) Subject: sqlite: Implement storing objects X-Git-Tag: submit/tizen/20180222.151354~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=01fa0cd8a57c79c3471dce661965cd6536761d48;p=platform%2Fcore%2Fsystem%2Ffaultd.git sqlite: Implement storing objects This commit introduces the form of object signatures. They have similar structure to json. Note that the singature building function is re-purposed for building other object-specific strings, including sql queries. Change-Id: I9b268fdb46380fa311abc304116808729e5e1b06 Signed-off-by: Paweł Szewczyk --- diff --git a/src/database/sqlite.c b/src/database/sqlite.c index 6c43c31..e6f8648 100644 --- a/src/database/sqlite.c +++ b/src/database/sqlite.c @@ -28,6 +28,7 @@ #define META_TABLE "meta" #define OBJ_TABLE_PREFIX "objects" #define ID_KEY "id" +#define BUFSIZE 512 enum well_known_oids { WK_OID_INVALID = 0, @@ -37,11 +38,15 @@ enum well_known_oids { enum prepared_stmts { STMT_GET_ALL_TABLES = 0, + STMT_FIND_TABLE, + STMT_INSERT_TABLE, STMT_NUM, }; const char *prepared_stmts_str[STMT_NUM] = { [STMT_GET_ALL_TABLES] = "SELECT ('" OBJ_TABLE_PREFIX "'||id), signature FROM " META_TABLE ";", + [STMT_FIND_TABLE] = "SELECT ('" OBJ_TABLE_PREFIX "'||id) FROM " META_TABLE " WHERE signature = ?;", + [STMT_INSERT_TABLE] = "INSERT INTO " META_TABLE " (signature) VALUES (?);", }; struct sqlite_adapter { @@ -115,9 +120,334 @@ close_db: return ret; } +static char type_id_map[] = { + [TYPE_OBJECT] = 'O', + [TYPE_STRING] = 's', + [TYPE_OID] = 'o', + [TYPE_INT] = 'i', + [TYPE_TIMESPEC] = 'T', + [TYPE_TIME_T] = 't', + [TYPE_UUID] = 'u', +}; + +static const char *type_sql_map[] = { + [TYPE_OBJECT] = "NULL", + [TYPE_STRING] = "TEXT", + [TYPE_OID] = "INTEGER", + [TYPE_INT] = "INTEGER", + [TYPE_TIMESPEC] = "BLOB", + [TYPE_TIME_T] = "INTEGER", + [TYPE_UUID] = "BLOB", +}; + +enum signature_type { + SIG_KEY, /**< Signature stored in database */ + SIG_CREATE_FIELDS, /**< Fields for create query */ + SIG_INSERT_FIELDS, /**< Fields for insert query */ + SIG_INSERT_VALUES, /**< Values for insert query */ +}; + +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; + + if (obj->key && strcmp(obj->key, ID_KEY) == 0 + && type != SIG_INSERT_VALUES && type != SIG_INSERT_FIELDS) + return 0; + + if (obj->type == TYPE_OBJECT) { + if (type == SIG_KEY) { + ret = snprintf(buffer + noff, size - noff, "%s:{", (obj->key ? obj->key : "")); + if (ret < 0 || ret >= (int)size - noff) + return -ENOMEM; + + noff += ret; + } + + if (obj->key) + 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) { + ret = _get_object_signature(child, buffer, size, noff, type, prefix); + if (ret < 0) + goto out; + + noff += ret; + } + + free(prefix); + prefix = NULL; + + if (type == SIG_KEY) { + if (buffer[noff - 1] == ',') + noff--; + + ret = snprintf(buffer + noff, size - noff, "},"); + if (ret < 0 || ret >= (int)size - noff) + return -ENOMEM; + + noff += ret; + } + + ret = noff - offset; + if (ret < 0) + return 0; + + return ret; + } + + switch (type) { + case SIG_KEY: + if (obj->key && size < offset + strlen(obj->key) + 3) + return -ENOMEM; + + ret = snprintf(buffer + offset, size - offset, + "%s:%c,", (obj->key ? obj->key : ""), type_id_map[obj->type]); + break; + case SIG_CREATE_FIELDS: + ret = snprintf(buffer + offset, size - offset, + "`%s%s` %s,", key_prefix, obj->key, type_sql_map[obj->type]); + break; + case SIG_INSERT_FIELDS: + ret = snprintf(buffer + offset, size - offset, + "`%s%s`,", key_prefix, obj->key); + break; + case SIG_INSERT_VALUES: + ret = snprintf(buffer + offset, size - offset, "?,"); + break; + } + + if (ret < 0 || ret >= (int)size - offset) + ret = -ENOMEM; + +out: + free(prefix); + return ret; +} + +static inline int get_object_signature(struct faultd_object *obj, char *buffer, int size, int type) +{ + int ret; + int off = 0; + + buffer[0] = '\0'; + if (type == SIG_CREATE_FIELDS) { + ret = snprintf(buffer, size, "id INTEGER PRIMARY KEY,"); + if (ret < 0 || ret >= size) + return -ENOMEM; + + off = ret; + } + + ret = _get_object_signature(obj, buffer, size, off, type, ""); + if (ret < 0) + return ret; + + if (off + ret > 0) + buffer[off + ret - 1] = '\0'; + return 0; +} + +static int get_table_match(struct faultd_database_adapter *adapter, struct faultd_object *obj, char *signature, char **name, int *id) +{ + sqlite3_stmt *query; + int ret; + char *nname; + struct sqlite_adapter *da = to_sqlite_adapter(adapter); + char query_str[BUFSIZE]; + char buf[BUFSIZE]; + char *sqlite_err; + int _id; + + query = da->stmts[STMT_FIND_TABLE]; + sqlite3_reset(query); + sqlite3_bind_text(query, 1, signature, -1, SQLITE_TRANSIENT); + log_debug("query: %s", sqlite3_expanded_sql(query)); + ret = sqlite3_step(query); + if (ret == SQLITE_ROW) { + log_debug("found matching row: %s", sqlite3_column_text(query, 0)); + *name = strdup((const char *)sqlite3_column_text(query, 0)); + *id = sqlite3_column_int(query, 1); + return 0; + } + + log_debug("Did not found matching table. Creating new table."); + + query = da->stmts[STMT_INSERT_TABLE]; + sqlite3_reset(query); + sqlite3_bind_text(query, 1, signature, -1, SQLITE_TRANSIENT); + log_debug("query: %s", sqlite3_expanded_sql(query)); + ret = sqlite3_step(query); + if (ret == SQLITE_ERROR) { + log_error("SQL Error: %s", sqlite3_errmsg(da->db)); + return ret; + } + + _id = sqlite3_last_insert_rowid(da->db); + ret = asprintf(&nname, OBJ_TABLE_PREFIX "%d", _id); + if (ret < 0) + return -ENOMEM; + + get_object_signature(obj, buf, BUFSIZE, SIG_CREATE_FIELDS); + ret = snprintf(query_str, BUFSIZE, "CREATE TABLE %s (%s);", nname, buf); + if (ret < 0 || ret >= BUFSIZE) + goto free_name; + + log_debug("creating table:\n%s", query_str); + ret = sqlite3_exec(da->db, query_str, NULL, 0, &sqlite_err); + if (ret != SQLITE_OK) { + log_error("SQL Error: %s", sqlite3_errmsg(da->db)); + goto free_name; + } + + ret = snprintf(query_str, BUFSIZE, "CREATE UNIQUE INDEX %s_oid_idx on %s (%s);", nname, nname, ID_KEY); + if (ret < 0 || ret >= BUFSIZE) + goto free_name; + + ret = sqlite3_exec(da->db, query_str, NULL, 0, &sqlite_err); + if (ret != SQLITE_OK) { + log_error("SQL Error: %s", sqlite3_errmsg(da->db)); + goto free_name; + } + + *name = nname; + if (id) + *id = _id; + + return 0; + +free_name: + free(nname); + return ret; +} + +static inline uint64_t pack_oid(faultd_oid_t *oid) +{ + return (uint64_t)oid->sqlite.table << 32 | oid->sqlite.obj; +} + +static int bind_values(struct faultd_object *obj, sqlite3_stmt *query, int n) +{ + struct faultd_object *child; + int ret; + int nn; + uint64_t packed; + + switch (obj->type) { + case TYPE_OBJECT: + nn = n; + list_for_each_entry(child, &obj->val.children, node) { + ret = bind_values(child, query, nn); + if (ret < 0) + return ret; + + nn += ret; + } + ret = nn - n; + break; + case TYPE_STRING: + ret = sqlite3_bind_text(query, n, obj->val.s, -1, SQLITE_TRANSIENT); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + case TYPE_OID: + packed = pack_oid(&obj->val.oid); + ret = sqlite3_bind_int64(query, n, packed); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + case TYPE_INT: + ret = sqlite3_bind_int(query, n, obj->val.i); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + case TYPE_TIMESPEC: + ret = sqlite3_bind_blob(query, n, (const void *)&obj->val.ts, sizeof(obj->val.ts), SQLITE_TRANSIENT); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + case TYPE_TIME_T: + ret = sqlite3_bind_int(query, n, obj->val.time); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + case TYPE_UUID: + ret = sqlite3_bind_blob(query, n, (const void *)&obj->val.uuid, sizeof(obj->val.uuid), SQLITE_TRANSIENT); + ret = (ret == SQLITE_OK ? 1 : -ret); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int sqlite_store(struct faultd_database_adapter *adapter, struct faultd_object *obj, faultd_oid_t *oid) { + sqlite3_stmt *query; + struct sqlite_adapter *da = to_sqlite_adapter(adapter); + char fields_str[BUFSIZE]; + char values_str[BUFSIZE]; + char query_str[BUFSIZE]; + char *name = NULL; + int ret; + int table_id = 0; + + get_object_signature(obj, fields_str, BUFSIZE, SIG_KEY); + ret = get_table_match(adapter, obj, fields_str, &name, &table_id); + if (ret < 0) + return ret; + + log_debug("storing: %s in %s", fields_str, name); + ret = get_object_signature(obj, fields_str, BUFSIZE, SIG_INSERT_FIELDS); + if (ret < 0) { + log_error("Could not create query (signature: %s)", fields_str); + goto err; + } + + ret = get_object_signature(obj, values_str, BUFSIZE, SIG_INSERT_VALUES); + if (ret < 0) { + log_error("Could not create query (signature: %s)", values_str); + goto err; + } + + ret = snprintf(query_str, BUFSIZE, "INSERT OR REPLACE INTO %s (%s) VALUES (%s);", name, fields_str, values_str); + if (ret < 0 || ret >= BUFSIZE) + goto err; + + ret = sqlite3_prepare_v2(da->db, query_str, -1, &query, NULL); + if (ret != SQLITE_OK) { + log_error("SQL Error: %s", sqlite3_errmsg(da->db)); + goto err; + } + + ret = bind_values(obj, query, 1); + if (ret < 0) { + log_error("Could not bind values to sql query"); + return ret; + } + + log_debug("query: %s", sqlite3_expanded_sql(query)); + ret = sqlite3_step(query); + if (ret == SQLITE_ERROR) { + log_error("SQL Error: %s", sqlite3_errmsg(da->db)); + ret = -1; + goto err; + } + + if (oid) { + oid->sqlite.table = table_id; + oid->sqlite.obj = sqlite3_last_insert_rowid(da->db); + } + return 0; +err: + + free(name); + return ret; } static int sqlite_get_by_oid(struct faultd_database_adapter *adapter, faultd_oid_t *oid, struct faultd_object *result)