Database¶
Viper provides two database types for persistence:
Type |
Use Case |
History |
|---|---|---|
|
Simple key-value storage |
No |
|
Versioned data |
Yes (DAG) |
This page covers the simple Database. For versioned storage,
see CommitDatabase.
Creating a Database¶
In-memory and on-disk variants:
>>> db = Database.create_in_memory()
>>> db.in_memory()
True
>>> db = Database.create("data.vdb")
>>> db = Database.create("data.vdb", documentation="Cache for processed meshes")
>>> db = Database.open("data.vdb")
>>> db = Database.open("data.vdb", readonly=True)
Before opening, you can check if a file is a valid Database:
>>> Database.is_compatible("data.vdb")
True
Remote Access¶
Database supports network access through a server:
>>> Database.databases("server.local")
['cache', 'index']
>>> db = Database.connect("cache", "server.local")
>>> db = Database.connect_local("cache", "/tmp/project.sock")
See Server for deployment details.
Setting Up Definitions¶
>>> _ = db.extend_definitions(_tuto_defs)
>>> sorted(str(a).split()[-1] for a in db.definitions().attachments())
['Tuto::account', 'Tuto::avatar', 'Tuto::identity', 'Tuto::login', 'Tuto::portrait']
In your own code you would assemble definitions from your DSM model:
>>> builder = DSMBuilder.assemble("model.dsm")
>>> report, dsm_defs, defs = builder.parse()
>>> db.extend_definitions(defs)
>>> defs.inject()
CRUD Operations¶
Create a key and a document:
>>> key = TUTO_A_USER_LOGIN.create_key()
>>> login = TUTO_A_USER_LOGIN.create_document()
>>> login.nickname = "alice"
Write — all mutations require a transaction:
>>> db.begin_transaction()
>>> _ = db.set(TUTO_A_USER_LOGIN, key, login)
>>> db.commit()
Read:
>>> result = db.get(TUTO_A_USER_LOGIN, key)
>>> result.unwrap()
{nickname='alice', password=''}
Check existence:
>>> db.has(TUTO_A_USER_LOGIN, key)
True
Update:
>>> db.begin_transaction()
>>> login.password = "secret"
>>> _ = db.set(TUTO_A_USER_LOGIN, key, login)
>>> db.commit()
>>> db.get(TUTO_A_USER_LOGIN, key).unwrap()
{nickname='alice', password='secret'}
List keys for an attachment — keys() returns a ValueSet of ValueUUId:
>>> isinstance(db.keys(TUTO_A_USER_LOGIN), ValueSet)
True
>>> len(db.keys(TUTO_A_USER_LOGIN))
1
Delete:
>>> db.begin_transaction()
>>> _ = db.delete(TUTO_A_USER_LOGIN, key)
>>> db.commit()
>>> db.has(TUTO_A_USER_LOGIN, key)
False
Transactions¶
All write operations require a transaction:
>>> db.begin_transaction()
>>> db.in_transaction()
True
>>> db.commit()
>>> db.in_transaction()
False
A transaction can also be rolled back:
>>> db.begin_transaction()
>>> _ = db.set(TUTO_A_USER_LOGIN, key, login)
>>> db.rollback()
>>> db.has(TUTO_A_USER_LOGIN, key)
False
Safe transaction pattern — use try/finally to ensure cleanup:
db.begin_transaction()
try:
db.set(attachment, key, document)
db.commit()
except ViperError:
db.rollback()
raise
Lifecycle¶
Always close a database when done, especially for on-disk databases:
>>> tmp_db = Database.create_in_memory()
>>> tmp_db.close()
>>> tmp_db.is_closed()
True
Metadata¶
Database exposes a few metadata accessors:
>>> db.in_memory()
True
>>> db.path()
'InMemory'
>>> isinstance(db.uuid(), ValueUUId)
True
>>> db.codec_name()
'StreamBinary'
For an on-disk database, path() returns the file path and documentation()
returns the string passed to Database.create(..., documentation=...).
Choosing Between Database and CommitDatabase¶
Feature |
Database |
CommitDatabase |
|---|---|---|
Simple CRUD |
✓ |
✓ |
History |
✗ |
✓ |
Sync |
✗ |
✓ |
Embedded definitions |
✓ |
✓ |
Remote access |
✓ |
✓ |
Blob storage |
✓ |
✓ |
What’s Next¶
CommitDatabase - Versioned database with history
Blobs - Binary data storage