CURIA(3)

๐Ÿ’– CURIA(3) ๐Ÿ’– โ€” the Quick Database Manager extended-API glow-up ๐Ÿฆ‹

okay bestie, sit down, hydrate, and let me tell you about CURIA(3). it's the extended API of QDBM and it's literally the moment. ๐Ÿ’…


๐ŸŽ€ NAME ๐ŸŽ€

Curia โ€” the extended API of QDBM. that's the whole vibe. ๐Ÿ’–

๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

#include <depot.h>
#include <curia.h>
#include <stdlib.h>

CURIA *cropen(const char *name, int omode, int bnum, int dnum);

int crclose(CURIA *curia);

int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf,
int vsiz, int dmode);

int crout(CURIA *curia, const char *kbuf, int ksiz);

char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int
max, int *sp);

int crgetwb(CURIA *curia, const char *kbuf, int ksiz, int start, int
max, char *vbuf);

int crvsiz(CURIA *curia, const char *kbuf, int ksiz);

int criterinit(CURIA *curia);

char *criternext(CURIA *curia, int *sp);

int crsetalign(CURIA *curia, int align);

int crsetfbpsiz(CURIA *curia, int size);

int crsync(CURIA *curia);

int croptimize(CURIA *curia, int bnum);

char *crname(CURIA *curia);

int crfsiz(CURIA *curia);

double crfsizd(CURIA *curia);

int crbnum(CURIA *curia);

int crbusenum(CURIA *curia);

int crrnum(CURIA *curia);

int crwritable(CURIA *curia);

int crfatalerror(CURIA *curia);

int crinode(CURIA *curia);

time_t crmtime(CURIA *curia);

int crremove(const char *name);

int crrepair(const char *name);

int crexportdb(CURIA *curia, const char *name);

int crimportdb(CURIA *curia, const char *name);

char *crsnaffle(const char *name, const char *kbuf, int ksiz, int *sp);

int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf,
int vsiz, int dmode);

int croutlob(CURIA *curia, const char *kbuf, int ksiz);

char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int
max, int *sp);

int crgetlobfd(CURIA *curia, const char *kbuf, int ksiz);

int crvsizlob(CURIA *curia, const char *kbuf, int ksiz);

int crrnumlob(CURIA *curia);

โœจ DESCRIPTION โœจ

okay so. Curia is the extended API of QDBM, and what it does is give you routines for managing multiple database files in a directory. ๐Ÿ—‚๏ธ

why tho? because some file systems have this restriction where the size of each file is limited (rude, honestly), and Curia escapes that by dividing a database file into two or more. and if those database files deploy on multiple devices, the scalability is improved. it's giving upgrade. ๐Ÿ”ฅ


๐ŸŒธ the whole directory situation ๐ŸŒธ

here's the tea, because this is where Curia is different from Depot:

  • Depot creates a database with a file name. Curia creates a database with a directory name. ๐Ÿ’…
  • A database file named as depot is placed in the specified directory.
  • That file keeps the attribute of the database, but it does not keep the entities of the records.
  • Besides, sub directories are created by the number of division of the database, and they're named with 4 digits.
  • The database files are placed in the subdirectories.
  • The entities of the records are stored in the database file.

so, for example โ€” in the case that a database directory is named as casket and the number of division is 3, you get:

casket/depot
casket/0001/depot
casket/0002/depot
casket/0003/depot

no error occurs even if the namesake directory exists when creating a database. so if sub directories exist and some devices are mounted on the sub directories, the database files deploy on the multiple devices. ๐ŸŽ€ and yes, it is possible for the database files to deploy on multiple file servers using NFS and so on. iconic.


๐Ÿ’œ large objects (lob) ๐Ÿ’œ

Curia features managing large objects. here's how it works: although usual records are stored in some database files, records of large objects are stored in individual files. because the files of large objects are deployed in different directories named with the hash values, the access speed is part-way robust โ€” although it is slower than the speed of usual records, ngl.

so the move is: large and not often accessed data should be secluded as large objects. by doing this, the access speed of usual records is improved. โœจ

the directory hierarchies of large objects are placed in the directory named as lob in the sub directories of the database. and because the key spaces of the usual records and the large objects are different, the operations keep out of each other โ€” no crossover drama, we love that. ๐Ÿ™…๐Ÿ’–


๐Ÿซถ how to actually use this thing

in order to use Curia, you should include depot.h, curia.h and stdlib.h in the source files. usually, the following description will be near the beginning of a source file:

#include <depot.h>
#include <curia.h>
#include <stdlib.h>

a pointer to CURIA is used as a database handle. it's like that some file I/O routines of stdio.h use a pointer to FILE. ๐Ÿงš a database handle is opened with the function cropen and closed with crclose. and bestie โ€” you should not refer directly to any member of the handle. hands off the internals, fr fr.

๐Ÿ’€ if a fatal error occurs in a database, any access method via the handle except crclose will not work and return error status.

a process is allowed to use multiple database handles at the same time, but handles of the same database directory should not be used. noted, bestie.

Curia also assign the external variable dpecode with the error code. the function dperrmsg is used in order to get the message of the error code. ๐Ÿ’Œ


๐Ÿ”‘ cropen

the function cropen is used in order to get a database handle.

CURIA *cropen(const char *name, int omode, int bnum, int dnum);
  • name โ€” specifies the name of a database directory.
  • omode โ€” specifies the connection mode: CR_OWRITER as a writer, CR_OREADER as a reader. more on the add-ons below ๐Ÿ‘‡
    • if the mode is CR_OWRITER, the following may be added by bitwise or: CR_OCREAT, which means it creates a new database if not exist; CR_OTRUNC, which means it creates a new database regardless if one exists.
    • both of CR_OREADER and CR_OWRITER can be added to by bitwise or: CR_ONOLCK, which means it opens a database directory without file locking; or CR_OLCKNB, which means locking is performed without blocking.
    • CR_OCREAT can be added to by bitwise or: CR_OSPARSE, which means it creates database files as sparse files.
  • bnum โ€” specifies the number of elements of each bucket array. if it is not more than 0, the default value is specified. the size of each bucket array is determined on creating, and can not be changed except for by optimization of the database. suggested size of each bucket array is about from 0.5 to 4 times of the number of all records to store.
  • dnum โ€” specifies the number of division of the database. if it is not more than 0, the default value is specified. the number of division can not be changed from the initial value. the max number of division is 512.

the return value is the database handle or NULL if it is not successful.

while connecting as a writer, an exclusive lock is invoked to the database directory. while connecting as a reader, a shared lock is invoked to the database directory. the thread blocks until the lock is achieved. ๐Ÿซ  if CR_ONOLCK is used, the application is responsible for exclusion control โ€” that one's on you, bestie.


๐Ÿ”’ crclose

the function crclose is used in order to close a database handle.

int crclose(CURIA *curia);
  • curia โ€” specifies a database handle.

if successful, the return value is true, else, it is false. because the region of a closed handle is released, it becomes impossible to use the handle. updating a database is assured to be written when the handle is closed.

๐Ÿ˜ญ if a writer opens a database but does not close it appropriately, the database will be broken. like, actually broken. do not do this. close your handles, periodt.

โœ๏ธ crput

the function crput is used in order to store a record.

int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf,
int vsiz, int dmode);
  • curia โ€” specifies a database handle connected as a writer.
  • kbuf โ€” specifies the pointer to the region of a key.
  • ksiz โ€” specifies the size of the region of the key. if it is negative, the size is assigned with strlen(kbuf).
  • vbuf โ€” specifies the pointer to the region of a value.
  • vsiz โ€” specifies the size of the region of the value. if it is negative, the size is assigned with strlen(vbuf).
  • dmode โ€” specifies behavior when the key overlaps, by the following values: CR_DOVER, which means the specified value overwrites the existing one; CR_DKEEP, which means the existing value is kept; CR_DCAT, which means the specified value is concatenated at the end of the existing value.

if successful, the return value is true, else, it is false.


๐Ÿ—‘๏ธ crout

the function crout is used in order to delete a record.

int crout(CURIA *curia, const char *kbuf, int ksiz);
  • curia โ€” specifies a database handle connected as a writer.
  • kbuf โ€” specifies the pointer to the region of a key.
  • ksiz โ€” specifies the size of the region of the key. if it is negative, the size is assigned with strlen(kbuf).

if successful, the return value is true, else, it is false. false is returned when no record corresponds to the specified key. (so if you delete something that isn't there, that's a false, bestie. the void said no. ๐Ÿ•ณ๏ธ)


๐Ÿ” crget

the function crget is used in order to retrieve a record.

char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int
max, int *sp);
  • curia โ€” specifies a database handle.
  • kbuf โ€” specifies the pointer to the region of a key.
  • ksiz โ€” specifies the size of the region of the key. if it is negative, the size is assigned with strlen(kbuf).
  • start โ€” specifies the offset address of the beginning of the region of the value to be read.
  • max โ€” specifies the max size to be read. if it is negative, the size to read is unlimited.
  • sp โ€” specifies the pointer to a variable to which the size of the region of the return value is assigned. if it is NULL, it is not used.

if successful, the return value is the pointer to the region of the value of the corresponding record, else, it is NULL. NULL is returned when no record corresponds to the specified key or the size of the value of the corresponding record is less than start.

because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. โœจ

๐Ÿง  because the region of the return value is allocated with the malloc call, it should be released with the free call if it is no longer in use. free your memory, bestie, it is the law. ๐Ÿ’…

๐Ÿ“ฅ crgetwb

the function crgetwb is used in order to retrieve a record and write the value into a buffer.

So like, picking right back up, this next chunk of functions? It's a lot, but every single one is here, bestie โ€” nothing skipped, nothing merged, nothing invented. Let's go. ๐Ÿ’–


int crgetwb(CURIA *curia, const char *kbuf, int ksiz, int start, int max, char *vbuf);

This one reads a slice of a value โ€” not the whole thing, just a lil piece of it, very โœจ targeted โœจ.

  • curia โ€” the database handle.
  • kbuf โ€” the pointer to the region of a key.
  • ksiz โ€” the size of the region of the key. And plot twist: if it's negative, the size gets assigned with strlen(kbuf). ๐Ÿซถ
  • start โ€” the offset address of the beginning of the region of the value you wanna read.
  • max โ€” the max size to be read. It should be equal to or less than the size of the writing buffer. Do not get cute with this, okay?
  • vbuf โ€” the pointer to a buffer where the value of the corresponding record gets written.

If it goes well, the return value is the size of the written data. If not, it's -1. And -1 shows up when no record corresponds to the specified key, or when the size of the value of the corresponding record is less than start. Periodt.

Major heads-up bestie: no additional zero code is appended at the end of the region of the writing buffer. So please, I am begging, do NOT treat that buffer like a character string. ๐Ÿ˜ญ

And when you just wanna know how big a value is? That's literally what crvsiz is for.

int crvsiz(CURIA *curia, const char *kbuf, int ksiz);

  • curia โ€” the database handle.
  • kbuf โ€” the pointer to the region of a key.
  • ksiz โ€” the size of the region of the key. Negative? Then it's assigned with strlen(kbuf), same as before. ๐Ÿ’…

If it works, the return value is the size of the value of the corresponding record. If not, -1. And here's the slay: because this function does not read the entity of a record, it's faster than crget. Efficient queen behavior. ๐Ÿ‘‘

int criterinit(CURIA *curia);

This one initializes the iterator of a database handle โ€” and the iterator is the whole reason you can walk through the key of every record stored in a database. ๐ŸŒˆ

  • curia โ€” the database handle.

If it works, the return value is true. If not, it's false.

char *criternext(CURIA *curia, int *sp);

Grab the next key from the iterator. Simple as that. โœจ

  • curia โ€” the database handle.
  • sp โ€” the pointer to a variable that the size of the region of the return value gets assigned to. And if it's NULL, it's just not used. No stress.

If it works, the return value is the pointer to the region of the next key. If not, it's NULL. And NULL comes back when there's no record to be get out of the iterator.

Good news: because an additional zero code is appended at the end of the region of the return value, you can treat the return value as a character string. ๐ŸŽ€

But listen: because the region of the return value is allocated with the malloc call, it should be released with the free call once it's no longer in use. Free your mallocs, bestie, or leak city. ๐Ÿ’€

You can totally access every record by iteration of calling this function. However โ€” and this is the fine print โ€” it is not assured if updating the database is occurred while the iteration. Also, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access. So don't come for me about ordering. ๐Ÿ™ˆ

int crsetalign(CURIA *curia, int align);

Sets the alignment of a database handle.

  • curia โ€” a database handle connected as a writer.
  • align โ€” the size of alignment.

If it works, the return value is true. If not, it's false. If alignment is set to a database, the efficiency of overwriting values is improved. The size of alignment is suggested to be the average size of the values of the records to be stored.

If alignment is positive, padding whose size is a multiple number of the alignment is placed. If alignment is negative, as vsiz is the size of a value, the size of padding is calculated with (vsiz / pow(2, abs(align) - 1)). Math is mathing. ๐Ÿงฎ

Because alignment setting is not saved in a database, you should specify alignment every opening a database. Every. Single. Time. ๐Ÿ˜ค

int crsetfbpsiz(CURIA *curia, int size);

Sets the size of the free block pool of a database handle.

  • curia โ€” a database handle connected as a writer.
  • size โ€” the size of the free block pool of a database.

If it works, the return value is true. If not, it's false. The default size of the free block pool is 16. And if the size is greater, the space efficiency of overwriting values is improved โ€” but the time efficiency gets sacrificed. Trade-offs, bestie, they're everywhere. ๐Ÿฆ‹

int crsync(CURIA *curia);

Synchronizes the updating contents with the files and the devices.

  • curia โ€” a database handle connected as a writer.

If it works, the return value is true. If not, it's false. This function is useful when another process uses the connected database directory. ๐Ÿค

int croptimize(CURIA *curia, int bnum);

Optimizes a database. Glow-up time. โœจ

  • curia โ€” a database handle connected as a writer.
  • bnum โ€” the number of the elements of each bucket array. If it's not more than 0, the default value is specified.

In an alternating succession of deleting and storing with overwrite or concatenate, dispensable regions accumulate. This function is useful to do away with them. So like, clean your room, basically. ๐Ÿงน

char *crname(CURIA *curia);

Gets the name of a database.

  • curia โ€” the database handle.

If it works, the return value is the pointer to the region of the name of the database. If not, it's NULL.

Because the region of the return value is allocated with the malloc call, it should be released with the free call if it is no longer in use. (Yes, again. This is a recurring theme. Free it. ๐Ÿ’…)

int crfsiz(CURIA *curia);

Gets the total size of database files.

  • curia โ€” the database handle.

If it works, the return value is the total size of the database files. If not, it's -1. And warning: if the total size is more than 2GB, the return value overflows. ๐Ÿ˜ญ So if your DB is a chonky girl, use the next one.

double crfsizd(CURIA *curia);

Same energy, but the total size of database files as a double-precision floating-point number.

  • curia โ€” the database handle.

If it works, the return value is the total size of the database files. If not, it's -1.0.

int crbnum(CURIA *curia);

Gets the total number of the elements of each bucket array.

  • curia โ€” the database handle.

If it works, the return value is the total number of the elements of each bucket array. If not, it's -1.

int crbusenum(CURIA *curia);

Gets the total number of the used elements of each bucket array.

  • curia โ€” the database handle.

If it works, the return value is the total number of the used elements of each bucket array. If not, it's -1.

Ngl, this function is inefficient because it accesses all elements of each bucket array. So like, use it, but know what you're signing up for. ๐Ÿซ 

int crrnum(CURIA *curia);

Gets the number of the records stored in a database.

  • curia โ€” the database handle.

If it works, the return value is the number of the records stored in the database. If not, it's -1.

int crwritable(CURIA *curia);

Vibe check: is this database handle a writer or not? ๐Ÿ‘‘

  • curia โ€” the database handle.

The return value is true if the handle is a writer, false if not.

int crfatalerror(CURIA *curia);

Checks whether a database has a fatal error or not. The dramatic one. ๐Ÿ’€

  • curia โ€” the database handle.

The return value is true if the database has a fatal error, false if not.

int crinode(CURIA *curia);

Gets the inode number of a database directory.

  • curia โ€” the database handle.

The return value is the inode number of the database directory.

time_t crmtime(CURIA *curia);

Gets the last modified time of a database.

  • curia โ€” the database handle.

The return value is the last modified time of the database. โฐ

int crremove(const char *name);

Removes a database directory. Bye bestie. ๐Ÿ‘‹

  • name โ€” the name of a database directory.

If it works, the return value is true. If not, it's false.

int crrepair(const char *name);

Repairs a broken database directory. Healing era. ๐Ÿฉน

  • name โ€” the name of a database directory.

If it works, the return value is true. If not, it's false.

Real talk though: there is no guarantee that all records in a repaired database directory correspond to the original or expected state. So like, it's giving best-effort, not it's giving perfect. Manage expectations. ๐Ÿ˜ญ

int crexportdb(CURIA *curia, const char *name);

Dumps all records as endian independent data.

  • curia โ€” the database handle.
  • name โ€” the name of an output directory.

If it works, the return value is true. If not, it's false. And note that large objects are ignored. ๐Ÿ™ˆ

int crimportdb(CURIA *curia, const char *name);

Loads all records from endian independent data.

  • curia โ€” a database handle connected as a writer. The database of the handle must be empty. No sneaky non-empty DBs, okay?
  • name โ€” the name of an input directory.

If it works, the return value is true. If not, it's false. And again, note that large objects are ignored. ๐ŸŒธ

crsnaffle

So crsnaffle is used in order to retrieve a record directly from a database directoryโ€ฆ ๐Ÿ‘€

๐Ÿฆ‹ crsnaffle โ€” the iconic "grab a record" bestie ๐Ÿ’…

char *crsnaffle(const char *name, const char *kbuf, int ksiz, int *sp);

name is the name of a database directory, periodt. kbuf points at the region of a key, and ksiz is the size of that key region. If ksiz is negative, the size gets assigned with strlen(kbuf) โ€” yes ma'am. sp points to a variable where the size of the return value's region gets assigned. If sp is NULL, it's simply not used, no biggie.

If it succeeds, the return value is the pointer to the region of the value of the corresponding record; otherwise it's NULL. NULL is returned when no record corresponds to the specified key, sad but true. Because an additional zero code is appended at the end of the return value's region, you can treat the return value as a character string โœจ. Because the return value's region is allocated with the malloc call, it should be released with the free call if it is no longer in use โ€” free your faves, bestie. Although this function can be used even while the database directory is locked by another process, it is not assured that recent updated is reflected. (Yes, the original says "recent updated" โ€” we don't fix, we just vibe.)

๐ŸŽ€ crputlob โ€” storing a large object, main character energy ๐Ÿ‘‘

The function crputlob is used in order to store a large object.

int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf,
int vsiz, int dmode);

curia specifies a database handle connected as a writer. kbuf points to the region of a key. ksiz is the size of the key region; if it's negative, the size is assigned with strlen(kbuf). vbuf points to the region of a value. vsiz is the size of the value region; if it's negative, the size is assigned with strlen(vbuf). dmode specifies behavior when the key overlaps, by the following values:

  • CR_DOVER โ€” the specified value overwrites the existing one. Slay, out with the old.
  • CR_DKEEP โ€” the existing value is kept. Protect the archives, queen.
  • CR_DCAT โ€” the specified value is concatenated at the end of the existing value. Stitch them together, it's giving collage.

If successful, the return value is true; else, it is false. No cap.

๐Ÿ’… croutlob โ€” deleting a large object ๐Ÿ—‘๏ธ

The function croutlob is used in order to delete a large object.

int croutlob(CURIA *curia, const char *kbuf, int ksiz);

curia specifies a database handle connected as a writer. kbuf points to the region of a key. ksiz is the size of the key region; if it's negative, the size is assigned with strlen(kbuf). If successful, the return value is true; else, it is false. false is returned when no large object corresponds to the specified key.

๐ŸŒธ crgetlob โ€” retrieving a large object ๐Ÿฆ‹

The function crgetlob is used in order to retrieve a large object.

char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int
max, int *sp);

curia specifies a database handle. kbuf points to the region of a key. ksiz is the size of the key region; if it's negative, the size is assigned with strlen(kbuf). start specifies the offset address of the beginning of the region of the value to be read. max specifies the max size to be read; if it's negative, the size to read is unlimited. sp points to a variable to which the size of the region of the return value is assigned. If sp is NULL, it is not used. If successful, the return value is the pointer to the region of the value of the corresponding large object; else, it is NULL. NULL is returned when no large object corresponds to the specified key or the size of the value of the corresponding large object is less than start. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the malloc call, it should be released with the free call if it is no longer in use.

๐Ÿฆ‹ crgetlobfd โ€” getting the file descriptor of a large object ๐Ÿ”ฅ

The function crgetlobfd is used in order to get the file descriptor of a large object.

int crgetlobfd(CURIA *curia, const char *kbuf, int ksiz);

curia specifies a database handle. kbuf points to the region of a key. ksiz is the size of the key region; if it's negative, the size is assigned with strlen(kbuf). If successful, the return value is the file descriptor of the corresponding large object; else, it is -1. -1 is returned when no large object corresponds to the specified key. The returned file descriptor is opened with the open call. If the database handle was opened as a writer, the descriptor is writable (O_RDWR); else, it is not writable (O_RDONLY). The descriptor should be closed with the close call if it is no longer in use.

โœจ crvsizlob โ€” getting the size of the value of a large object ๐Ÿ“

The function crvsizlob is used in order to get the size of the value of a large object.

int crvsizlob(CURIA *curia, const char *kbuf, int ksiz);

curia specifies a database handle. kbuf points to the region of a key. ksiz is the size of the key region; if it's negative, the size is assigned with strlen(kbuf). If successful, the return value is the size of the value of the corresponding large object; else, it is -1. Because this function does not read the entity of a large object, it is faster than crgetlob. It's giving speed, no cap.

๐Ÿ‘‘ crrnumlob โ€” getting the number of large objects in a database ๐Ÿ’–

The function crrnumlob is used in order to get the number of the large objects stored in a database.

int crrnumlob(CURIA *curia);

curia specifies a database handle. If successful, the return value is the number of the large objects stored in the database; else, it is -1.

๐Ÿงต Threading tea โ˜•

If QDBM was built with POSIX thread enabled, the global variable dpecode is treated as thread specific data, and functions of Curia are reentrant. In that case, they are thread-safe as long as a handle is not accessed by threads at the same time, on the assumption that errno, malloc, and so on are thread-safe. That's the boundary, bestie.


๐ŸŒˆ SEE ALSO

qdbm(3), depot(3), relic(3), hovel(3), cabin(3), villa(3), odeum(3), ndbm(3), gdbm(3)


okay that's the whole thing, bestie. this was CURIA(3) from the Man Page, dated 2004-04-22. go free your mallocs and live your truth ๐Ÿ’–โœจ