#define META_TABLE "meta"
#define OBJ_TABLE_PREFIX "objects"
#define ID_KEY "id"
+#define BUFSIZE 512
enum well_known_oids {
WK_OID_INVALID = 0,
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 {
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)