/* * return the type of the object * * returns: * -EINVAL: invalid arg, ignore value of obj_type * OSD_ERROR: any other error, ignore value of obj_type * OSD_OK: success in determining the type, either valid or invalid. * obj_types set to the determined type. */ int obj_get_type(void *ohandle, uint64_t pid, uint64_t oid, uint8_t *obj_type) { struct db_context *dbc = ((struct handle*)ohandle)->dbc; int ret = 0; int bound = 0; *obj_type = ILLEGAL_OBJ; assert(dbc && dbc->db && dbc->obj && dbc->obj->gettype); repeat: ret = 0; ret |= sqlite3_bind_int64(dbc->obj->gettype, 1, pid); ret |= sqlite3_bind_int64(dbc->obj->gettype, 2, oid); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", __func__); goto out_reset; } while ((ret = sqlite3_step(dbc->obj->gettype)) == SQLITE_BUSY); if (ret == SQLITE_ROW) { *obj_type = sqlite3_column_int(dbc->obj->gettype, 0); } else if (ret == SQLITE_DONE) { osd_debug("%s: object (%llu %llu) doesn't exist", __func__, llu(pid), llu(oid)); } out_reset: ret = db_reset_stmt(dbc, dbc->obj->gettype, bound, __func__); if (ret == OSD_REPEAT) goto repeat; out: return ret; }
/* * returns: * -EINVAL: invalid arg, cid is not set * OSD_ERROR: in case of any error, cid is not set * OSD_OK: success, cid is set to proper collection id */ int coll_get_cid(void *ohandle, uint64_t pid, uint64_t oid, uint32_t number, uint64_t *cid) { struct db_context *dbc = ((struct handle*)ohandle)->dbc; int ret = 0; int bound = 0; assert(dbc && dbc->db && dbc->coll && dbc->coll->getcid); repeat: ret = 0; ret |= sqlite3_bind_int64(dbc->coll->getcid, 1, pid); ret |= sqlite3_bind_int64(dbc->coll->getcid, 2, oid); ret |= sqlite3_bind_int64(dbc->coll->getcid, 3, number); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", __func__); goto out_reset; } while ((ret = sqlite3_step(dbc->coll->getcid)) == SQLITE_BUSY); if (ret == SQLITE_ROW) *cid = sqlite3_column_int64(dbc->coll->getcid, 0); out_reset: ret = db_reset_stmt(dbc, dbc->coll->getcid, bound, __func__); if (ret == OSD_REPEAT) goto repeat; out: return ret; }
/* * tests whether partition is empty. * * returns: * -EINVAL: invalid arg * OSD_ERROR: in case of other errors, ignore value of *isempty * OSD_OK: success, *isempty set to: * ==1: if partition is empty or absent or in case of sqlite error * ==0: if partition is not empty */ int obj_isempty_pid(void *ohandle, char *root, uint64_t pid, int *isempty) { struct db_context *dbc = ((struct handle*)ohandle)->dbc; int ret = 0; int bound = 0; *isempty = 0; assert(dbc && dbc->db && dbc->obj && dbc->obj->emptypid); repeat: ret = sqlite3_bind_int64(dbc->obj->emptypid, 1, pid); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", __func__); goto out_reset; } while ((ret = sqlite3_step(dbc->obj->emptypid)) == SQLITE_BUSY); if (ret == SQLITE_ROW) *isempty = (0 == sqlite3_column_int(dbc->obj->emptypid, 0)); out_reset: ret = db_reset_stmt(dbc, dbc->obj->emptypid, bound, __func__); if (ret == OSD_REPEAT) goto repeat; out: return ret; }
/* * return the max object pointer for a particular pid,cid * * returns: * -EINVAL: invalid arg, ignore value of isempty * OSD_ERROR: in case of other errors, ignore value of isempty * OSD_OK: success, isempty is set to: * ==1: if collection is empty or absent * ==0: if not empty */ int coll_max_pointer(struct db_context *dbc, uint64_t pid, uint64_t cid, uint32_t *number) { int ret = 0; int bound = 0; *number = 0; assert(dbc && dbc->db && dbc->coll && dbc->coll->max); repeat: ret = 0; ret |= sqlite3_bind_int64(dbc->coll->max, 1, pid); ret |= sqlite3_bind_int64(dbc->coll->max, 2, cid); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", __func__); goto out_reset; } while ((ret = sqlite3_step(dbc->coll->max)) == SQLITE_BUSY); if (ret == SQLITE_ROW) *number = (0 == sqlite3_column_int(dbc->coll->max, 0)); out_reset: ret = db_reset_stmt(dbc, dbc->coll->max, bound, __func__); if (ret == OSD_REPEAT) goto repeat; out: return ret; }
/* * tests whether collection is empty. * * returns: * -EINVAL: invalid arg, ignore value of isempty * OSD_ERROR: in case of other errors, ignore value of isempty * OSD_OK: success, isempty is set to: * ==1: if collection is empty or absent * ==0: if not empty */ int coll_isempty_cid(struct db_context *dbc, uint64_t pid, uint64_t cid, int *isempty) { int ret = 0; int bound = 0; *isempty = 0; assert(dbc && dbc->db && dbc->coll && dbc->coll->emptycid); repeat: ret = 0; ret |= sqlite3_bind_int64(dbc->coll->emptycid, 1, pid); ret |= sqlite3_bind_int64(dbc->coll->emptycid, 2, cid); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", __func__); goto out_reset; } while ((ret = sqlite3_step(dbc->coll->emptycid)) == SQLITE_BUSY); if (ret == SQLITE_ROW) *isempty = (0 == sqlite3_column_int(dbc->coll->emptycid, 0)); out_reset: ret = db_reset_stmt(dbc, dbc->coll->emptycid, bound, __func__); if (ret == OSD_REPEAT) goto repeat; out: return ret; }
/* * this function executes id retrieval statement. Only the functions * retireiving a list of oids, cids or pids may use this function * * returns: * OSD_ERROR: in case of any error * OSD_OK: on success * OSD_REPEAT: in case of sqlite_schema error, statements are prepared again, * hence values need to be bound again. */ int db_exec_id_rtrvl_stmt(struct db_context *dbc, sqlite3_stmt *stmt, int ret, const char *func, uint64_t alloc_len, uint8_t *outdata, uint64_t *used_outlen, uint64_t *add_len, uint64_t *cont_id) { uint64_t len = 0; int bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", func); goto out_reset; } len = 0; *add_len = 0; *cont_id = 0; *used_outlen = 0; while (1) { ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { if ((alloc_len - len) >= 8) { set_htonll(outdata, sqlite3_column_int64(stmt, 0)); outdata += 8; len += 8; } else if (*cont_id == 0) { *cont_id = sqlite3_column_int64(stmt, 0); } /* handle overflow: osd2r01 Sec 6.14.2 */ if (*add_len + 8 > *add_len) { *add_len += 8; } else { /* terminate since add_len overflew */ *add_len = (uint64_t) -1; break; } } else if (ret == SQLITE_BUSY) { continue; } else { break; } } out_reset: ret = db_reset_stmt(dbc, stmt, bound, func); if (ret == OSD_OK) *used_outlen = len; return ret; }
/* * exec_dms: executes prior prepared and bound data manipulation statement. * Only SQL statements with 'insert' or 'delete' may call this helper * function. * * returns: * OSD_ERROR: in case of error * OSD_OK: on success * OSD_REPEAT: in case of sqlite_schema error, statements are prepared again, * hence values need to be bound again. */ int db_exec_dms(struct db_context *dbc, sqlite3_stmt *stmt, int ret, const char *func) { int bound; TICK_TRACE(db_exec_dms); bound = (ret == SQLITE_OK); if (!bound) { error_sql(dbc->db, "%s: bind failed", func); goto out_reset; } do { TICK_TRACE(sqlite3_step); ret = sqlite3_step(stmt); } while (ret == SQLITE_BUSY); out_reset: ret = db_reset_stmt(dbc, stmt, bound, func); TICK_TRACE(db_exec_dms); return ret; }
/* * returns: * -EINVAL: invalid arg or misaligned buffer * -ENOENT: empty result set * OSD_ERROR: some other error * OSD_OK: success, used_outlen modified */ static int exec_attr_rtrvl_stmt(struct db_context *dbc, sqlite3_stmt *stmt, int ret, const char *func, uint64_t oid, uint64_t page, int rtrvl_type, uint64_t outlen, uint8_t *outdata, uint8_t listfmt, uint32_t *used_outlen) { uint32_t len = 0; uint8_t found = 0; uint8_t bound = (ret == SQLITE_OK); uint8_t inval = 0; if (!bound) { error_sql(dbc->db, "%s: bind failed", func); goto out_reset; } *used_outlen = 0; while (1) { ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { if (rtrvl_type == GATHER_VAL) { ret = attr_gather_val(stmt, outdata, outlen); } else if (rtrvl_type == GATHER_ATTR) { ret = attr_gather_attr(stmt, outdata, outlen, oid, listfmt); } else if (rtrvl_type == GATHER_DIR_PAGE) { ret = attr_gather_dir_page(stmt, outlen, outdata, oid, page, listfmt); } else { ret = -EINVAL; } if (ret > 0) { len += ret; outlen -= ret; outdata += ret; if (!found) found = 1; } else { if (ret == -EINVAL) inval = 1; break; } } else if (ret == SQLITE_BUSY) { continue; } else { break; } } out_reset: ret = db_reset_stmt(dbc, stmt, bound, func); if (inval) { ret = -EINVAL; } else if (ret == OSD_OK) { *used_outlen = len; if (!found) ret = -ENOENT; } return ret; }
/* * set attributes on members of the give collection * * return values: * -EINVAL: invalid argument * -EIO: prepare or some other sqlite function failed * OSD_ERROR: some other error * OSD_OK: success */ int mtq_set_member_attrs(struct db_context *dbc, uint64_t pid, uint64_t cid, struct setattr_list *set_attr) { int ret = 0; int factor = 1; uint32_t i = 0; char *cp = NULL; char *SQL = NULL; size_t sqlen = 0; sqlite3_stmt *stmt = NULL; const char *attr = attr_getname(dbc); assert(dbc && dbc->db && set_attr && attr); if (set_attr->sz == 0) { ret = 0; goto out; } SQL = Malloc(MAXSQLEN*factor); if (!SQL) { ret = -ENOMEM; goto out; } cp = SQL; sqlen = 0; sprintf(SQL, "INSERT OR REPLACE INTO %s ", attr); sqlen += strlen(SQL); cp += sqlen; for (i = 0; i < set_attr->sz; i++) { sprintf(cp, " SELECT %llu, oid, %u, %u, ? FROM %s " " WHERE pid = %llu AND page = %u AND value = %llu", llu(pid), set_attr->le[i].page, set_attr->le[i].number, attr, llu(pid), USER_COLL_PG, llu(cid)); if (i < (set_attr->sz - 1)) cp = strcat(cp, " UNION ALL "); sqlen += strlen(cp); if (sqlen > (MAXSQLEN*factor - 200)) { factor *= 2; SQL = realloc(SQL, MAXSQLEN*factor); if (!SQL) { ret = -ENOMEM; goto out; } } cp = SQL + sqlen; } cp = strcat(cp, " ;"); ret = sqlite3_prepare(dbc->db, SQL, strlen(SQL)+1, &stmt, NULL); if (ret != SQLITE_OK) { ret = -EIO; error_sql(dbc->db, "%s: sqlite3_prepare", __func__); goto out; } /* bind values */ for (i = 0; i < set_attr->sz; i++) { ret = sqlite3_bind_blob(stmt, i+1, set_attr->le[i].cval, set_attr->le[i].len, SQLITE_TRANSIENT); if (ret != SQLITE_OK) { ret = -EIO; error_sql(dbc->db, "%s: blob @ %u", __func__, i+1); goto out_finalize; } } /* execute the statement */ while ((ret = sqlite3_step(stmt)) == SQLITE_BUSY); if (ret != SQLITE_DONE) { error_sql(dbc->db, "%s: sqlite3_step", __func__); ret = -EIO; goto out_finalize; } ret = OSD_OK; out_finalize: if (sqlite3_finalize(stmt) != SQLITE_OK) error_sql(dbc->db, "%s: finalize", __func__); out: free(SQL); return ret; }
/* * return values: * -EINVAL: invalid argument * -EIO: prepare or some other sqlite function failed * OSD_ERROR: some other error * OSD_OK: success */ int mtq_run_query(struct db_context *dbc, uint64_t pid, uint64_t cid, struct query_criteria *qc, void *outdata, uint32_t alloc_len, uint64_t *used_outlen, uint64_t matches_cid) { int ret = 0; int pos = 0; char *cp = NULL; char *SQL = NULL; uint8_t *p = NULL; uint32_t i = 0; uint32_t sqlen = 0; uint32_t number; uint32_t factor = 2; /* this query fills space quickly */ uint64_t len = 0; const char *op = NULL; sqlite3_stmt *stmt = NULL; char select_stmt[MAXSQLEN]; const char *coll = coll_getname(dbc); const char *attr = attr_getname(dbc); assert(dbc && dbc->db && qc && outdata && used_outlen && coll && attr); if (qc->query_type == 0) { op = " UNION "; } else if (qc->query_type == 1) { op = " INTERSECT "; } else { ret = -EINVAL; goto out; } SQL = Malloc(MAXSQLEN*factor); if (SQL == NULL) { ret = -ENOMEM; goto out; } cp = SQL; sqlen = 0; /* * XXX:SD the spec does not mention whether min or max values have to * in tested with '<' or '<='. We assume the tests are inclusive of * boundaries, i.e. use '<=' for comparison. */ /* build the SQL statment */ if (matches_cid != 0) { ret = coll_max_pointer(dbc, pid, cid, &number); if (ret != SQLITE_OK) { goto out; } sprintf(select_stmt, "SELECT coll.pid, %llu, attr.oid, %d " "FROM %s AS coll, %s AS attr WHERE " "coll.pid=coll.pid AND coll.oid=attr.oid " "AND coll.pid = %llu AND coll.cid = %llu ", llu(matches_cid), number, coll, attr, llu(pid), llu(cid)); } else { sprintf(select_stmt, "SELECT attr.oid FROM %s AS coll, " "%s AS attr WHERE coll.pid = coll.pid AND " "coll.oid = attr.oid AND coll.pid = %llu " "AND coll.cid = %llu ", coll, attr, llu(pid), llu(cid)); } strncpy(cp, select_stmt, MAXSQLEN*factor); sqlen += strlen(cp); cp += sqlen; for (i = 0; i < qc->qc_cnt; i++) { sprintf(cp, " AND attr.page = %u AND attr.number = %u ", qc->page[i], qc->number[i]); if (qc->min_len[i] > 0) cp = strcat(cp, " AND ? <= attr.value "); if (qc->max_len[i] > 0) cp = strcat(cp, " AND attr.value <= ? "); if ((i+1) < qc->qc_cnt) { cp = strcat(cp, op); cp = strcat(cp, select_stmt); } sqlen += strlen(cp); if (sqlen >= (MAXSQLEN*factor - 400)) { factor *= 2; SQL = realloc(SQL, MAXSQLEN*factor); if (!SQL) { ret = -ENOMEM; goto out; } } cp = SQL + sqlen; } cp = strcat(cp, " GROUP BY attr.oid ORDER BY 1;"); if (matches_cid != 0) { char *SQL2 = Malloc(strlen(SQL) + 100); if (SQL2 == NULL) { ret = -ENOMEM; goto out; } sprintf(SQL2, "INSERT INTO %s %s", coll, SQL); free(SQL); SQL = SQL2; } ret = sqlite3_prepare(dbc->db, SQL, strlen(SQL)+1, &stmt, NULL); if (ret != SQLITE_OK) { error_sql(dbc->db, "%s: sqlite3_prepare", __func__); ret = -EIO; goto out; } repeat: /* bind the values */ pos = 1; for (i = 0; i < qc->qc_cnt; i++) { if (qc->min_len[i] > 0) { ret = sqlite3_bind_blob(stmt, pos, qc->min_val[i], qc->min_len[i], SQLITE_TRANSIENT); if (ret != SQLITE_OK) { ret = -EIO; error_sql(dbc->db, "%s: bind min_val @ %d", __func__, pos); goto out_finalize; } pos++; } if (qc->max_len[i] > 0) { ret = sqlite3_bind_blob(stmt, pos, qc->max_val[i], qc->max_len[i], SQLITE_TRANSIENT); if (ret != SQLITE_OK) { ret = -EIO; error_sql(dbc->db, "%s: bind max_val @ %d", __func__, pos); goto out_finalize; } pos++; } } if (matches_cid != 0) { ret = db_exec_dms(dbc, stmt, ret, __func__); if (ret == OSD_REPEAT) goto repeat; goto out_finalize; } /* execute the query */ p = outdata; p += ML_ODL_OFF; len = ML_ODL_OFF - 8; /* subtract len of addition_len */ *used_outlen = ML_ODL_OFF; while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { if ((alloc_len - len) > 8) { /* * TODO: query is a multi-object command, so delete * the objects from the collection, once they are * selected */ set_htonll(p, sqlite3_column_int64(stmt, 0)); *used_outlen += 8; } p += 8; /* handle overflow: osd2r01 Sec 6.18.3 */ if (len != (uint64_t) -1 && (len + 8) > len) { len += 8; } else { len = (uint64_t) -1; } } if (ret != SQLITE_DONE) { error_sql(dbc->db, "%s: sqlite3_step", __func__); ret = -EIO; goto out_finalize; } set_htonll(outdata, len); ret = OSD_OK; out_finalize: if (sqlite3_finalize(stmt) != SQLITE_OK) error_sql(dbc->db, "%s: finalize", __func__); out: free(SQL); return ret; }
/* * returns list of objects along with requested attributes * * return values: * -EINVAL: invalid argument * -EIO: prepare or some other sqlite function failed * OSD_ERROR: some other error * OSD_OK: success */ int mtq_list_oids_attr(struct db_context *dbc, uint64_t pid, uint64_t initial_oid, struct getattr_list *get_attr, uint64_t alloc_len, void *outdata, uint64_t *used_outlen, uint64_t *add_len, uint64_t *cont_id) { int ret = 0; char *cp = NULL; char *SQL = NULL; uint32_t i = 0; uint32_t factor = 2; /* this query uses space fast */ uint32_t attr_list_len = 0; /*XXX:SD see below */ uint32_t sqlen = 0; uint64_t oid = 0; uint32_t page; uint32_t number; uint16_t len; const void *val = NULL; sqlite3_stmt *stmt = NULL; uint8_t *head = NULL, *tail = NULL; const char *select_stmt = NULL; const char *obj = obj_getname(dbc); const char *attr = attr_getname(dbc); assert(dbc && dbc->db && get_attr && outdata && used_outlen && add_len && obj && attr); if (get_attr->sz == 0) { ret = -EINVAL; goto out; } SQL = Malloc(MAXSQLEN*factor); if (!SQL) { ret = -ENOMEM; goto out; } /* * For each attribute requested, create a select statement, * which will try to index into the attr table with a full key rather * than just (pid, oid) prefix key. Analogous to loop unrolling, we * unroll each requested attribute into its own select statement. */ select_stmt = "SELECT obj.oid as myoid, attr.page, " " attr.number, attr.value FROM %s as obj, %s as attr " " WHERE obj.pid = attr.pid AND obj.oid = attr.oid AND " " obj.pid = %llu AND obj.type = %u AND " " attr.page = %u AND attr.number = %u AND obj.oid >= %llu "; cp = SQL; sqlen = 0; sprintf(SQL, select_stmt, obj, attr, llu(pid), USEROBJECT, USER_TMSTMP_PG, 0, llu(initial_oid)); SQL = strcat(SQL, " UNION ALL "); sqlen += strlen(SQL); cp += sqlen; for (i = 0; i < get_attr->sz; i++) { sprintf(cp, select_stmt, obj, attr, llu(pid), USEROBJECT, get_attr->le[i].page, get_attr->le[i].number, llu(initial_oid)); if (i < (get_attr->sz - 1)) cp = strcat(cp, " UNION ALL "); sqlen += strlen(cp); if (sqlen > (MAXSQLEN*factor - 400)) { factor *= 2; SQL = realloc(SQL, MAXSQLEN*factor); if (!SQL) { ret = -ENOMEM; goto out; } } cp = SQL + sqlen; } sprintf(cp, " ORDER BY myoid; "); ret = sqlite3_prepare(dbc->db, SQL, strlen(SQL)+1, &stmt, NULL); if (ret != SQLITE_OK) { error_sql(dbc->db, "%s: sqlite3_prepare", __func__); goto out; } /* execute the statement */ head = tail = outdata; attr_list_len = 0; while(1) { ret = sqlite3_step(stmt); if (ret == SQLITE_BUSY) { continue; } else if (ret != SQLITE_ROW) { break; } /* for the rest of loop body ret == SQLITE_ROW */ /* * XXX:SD The spec is inconsistent in applying padding and * alignment rules. Here we make changes to the spec. In our * case object descriptor format header (table 79) is 16B * instead of 12B, and attributes list length field is 4B * instead of 2B as defined in spec, and starts at byte 12 * in the header (not 10). ODE is object descriptor entry. */ oid = sqlite3_column_int64(stmt, 0); page = sqlite3_column_int64(stmt, 1); number = sqlite3_column_int64(stmt, 2); len = sqlite3_column_bytes(stmt, 3); if (page == USER_TMSTMP_PG && number == 0) { /* look ahead in the buf to see if there is space */ if (alloc_len >= 16) { /* start attr list of 'this' ODE */ set_htonll(tail, oid); memset(tail + 8, 0, 4); /* reserved */ if (head != tail) { /* fill attr_list_len of prev ODE */ set_htonl(head, attr_list_len); head = tail; attr_list_len = 0; } alloc_len -= 16; tail += 16; head += 12; /* points to attr-list-len */ *used_outlen += 16; } else { if (head != tail) { /* fill attr_list_len of prev ODE */ set_htonl(head, attr_list_len); head = tail; attr_list_len = 0; } if (*cont_id == 0) *cont_id = oid; } /* handle overflow: osd2r01 Sec 6.14.2 */ if (*add_len + 16 > *add_len) { *add_len += 16; } else { /* terminate since add_len overflew */ *add_len = (uint64_t) -1; ret = SQLITE_DONE; break; } continue; } if (alloc_len >= 16) { /* osd_debug("%s: oid %llu, page %u, number %u, len %u", __func__, llu(oid), page, number, len); */ val = sqlite3_column_blob(stmt, 3); ret = le_pack_attr(tail, alloc_len, page, number, len, val); assert (ret != -EOVERFLOW); if (ret > 0) { alloc_len -= ret; tail += ret; attr_list_len += ret; *used_outlen += ret; if (alloc_len < 16){ set_htonl(head, attr_list_len); head = tail; attr_list_len = 0; if (*cont_id == 0) *cont_id = oid; } } else { goto out_finalize; } } else { if (head != tail) { /* fill attr_list_len of this ODE */ set_htonl(head, attr_list_len); head = tail; attr_list_len = 0; if (*cont_id == 0) *cont_id = oid; } } /* handle overflow: osd2r01 Sec 6.14.2 */ if ((*add_len + roundup8(4+4+2+len)) > *add_len) { *add_len += roundup8(4+4+2+len); } else { /* terminate since add_len overflew */ *add_len = (uint64_t) -1; ret = SQLITE_DONE; break; } } if (ret != SQLITE_DONE) { error_sql(dbc->db, "%s: query execution failed. SQL %s, " " add_len %llu attr_list_len %u", __func__, SQL, llu(*add_len), attr_list_len); goto out_finalize; } if (head != tail) { set_htonl(head, attr_list_len); head += (4 + attr_list_len); assert(head == tail); } ret = OSD_OK; /* success */ out_finalize: if (sqlite3_finalize(stmt) != SQLITE_OK) { ret = -EIO; error_sql(dbc->db, "%s: finalize", __func__); } out: free(SQL); return ret; }