Results of a query are always d_stores themselves, so you can query them again. You can also run set operations on stores, like getting the difference, or the union.
In d_store a row has an index, and rows can be referenced like that. A column is called a field. What you store in each field is a d_value, which is how you put in and get back the data from d_store. A field contains either NULL or a d_value. Non existing fields always contain NULL.
As a programmers interface, d_store tries to elleviate memory management from the user.
d_value, if using the D_STRING or D_INT macros, the values you get are oneshot, don't reuse them but you also don't have too (may not) free them. Any value you create using d_value_create_from_string() or alike, you can keep and use as much as you want, but you need to free these in the end.
d_values you get from the d_store, you can only use locally, there is no guarentee the value will remain valid if you modify the d_store in any way. Same goes for the data you get from d_value_get_string() or equavalent. If you want to keep on to the values use d_value_copy, or copy the data directly.
As a convenience, all d_store functions with variable arguments can take a string directly as a d_value. Except for the empty string "".
Unless you never put a newly created d_row in a store, don't free the rows; instead use d_row_delete. Notice the difference between deleting a row, and d_store_remove, which just removes the row from one d_store (so from only the set of rows that one d_store represents)!
Any d_row you receive from the store you can only use locally, same as the d_values. But d_row's never become invalid unless somewhere you call d_row_delete from somewhere.
d_stores you always have to free, regardless of how you got them. Freeing d_stores never deletes any data, rows (and therefor the d_values they contain) will always stay until you free the last d_store, or you use d_row_delete.
Freeing the last d_store will free all other housekeeping associated with it, even if that store represents only a small subset of all the rows available. After freeing the last d_store, don't reference any d_values or d_rows again, they will be invalid.
It is essential to understand that though most d_stores will represent a subset of all rows, they do have a reference to all rows. So when you use d_store_create(store), you can use any d_store as an argument, the result is a new d_store that will represent all rows available in that database. Use d_store_create_empty(store) if you want to create a new d_store but with zero rows, or use d_store_copy(store) if you want to copy a specific d_store. If the argument is NULL for any of these cases, then a new database is created.
Notice that it is perfectly valid to put a d_value from one database into a row of another database. However, you cannot add rows to a d_store that belongs to a different database.
A query for a field being equal to a value (except for the NULL value) is an efficient operation (using d_select()). Any other query will need to iterate the d_store you query. So as a rule, start queries by equality queries, then run other needed queries on the resulting d_store.
Same goes for the sorter or nodup, only set a sorter on the final result d_store, the later you set a sorter, the more efficient. After you set a sorter, modifying rows, or adding or removing rows is more efficient then making a new store with the same sorter. So use the mutating d_store_sub(), instead of d_sub().
D_store fits in between berkely and SQL. As d_store uses set operations, you can model any SQL query with d_store. But d_store is a lot more dynamic then SQL. This comes at a cost of-course, d_store needs to keep all its data in memory to operate well. This means, depending your application, you are limited by your physical memory. So don't use d_store when gigabytes of storage is required.
If you can live with that size limit, then d_store will perform better then SQL in most situations. Because the data is more shared, more indexes are available to d_store. Also d_store is a very direct interface to the C programming language or any language binding. Which removes the need for a some layer inbetween the data model and the programmer.
1.5.1