CS9 never takes a database configuration as an argument. It reads the
CS9_DBCONFIG_* environment variables on load, builds one
configuration per access level, and hands each one to csdb.
Choosing a backend is therefore choosing a set of environment variables,
and nothing else in your surveillance system changes.
CS9 speaks to two backends: PostgreSQL and SQLite. This vignette puts the two environments next to each other and lists which variables each backend reads and which it ignores.
Nothing here runs. The blocks are written to be copied into
.Renviron.
vignette("backends", package = "csdb") is the companion
at the R level: the same comparison in terms of dbconfig
lists, primary keys, field types and row counts.
The two environments
PostgreSQL needs a running server, a database, a schema per access level, and credentials.
# CS9 Core Configuration
CS9_AUTO=0
CS9_PATH=/home/myuser/cs9
# Database Connection Settings
CS9_DBCONFIG_ACCESS=config/anon
CS9_DBCONFIG_DRIVER=PostgreSQL Unicode
CS9_DBCONFIG_PORT=5432
CS9_DBCONFIG_SERVER=localhost
CS9_DBCONFIG_USER=cs9_user
CS9_DBCONFIG_PASSWORD=yourStrongPassword100
CS9_DBCONFIG_SSLMODE=prefer
CS9_DBCONFIG_ROLE_CREATE_TABLE=x
# Database Schema Configuration
CS9_DBCONFIG_SCHEMA_CONFIG=config
CS9_DBCONFIG_DB_CONFIG=cs9_surveillance
CS9_DBCONFIG_SCHEMA_ANON=anon
CS9_DBCONFIG_DB_ANON=cs9_surveillanceSQLite needs a file path per access level. There is nothing to install, nothing to start and nothing to authenticate against.
# CS9 Core Configuration
CS9_AUTO=0
CS9_PATH=/home/myuser/cs9
# One SQLite file per access level
CS9_DBCONFIG_ACCESS=config/anon
CS9_DBCONFIG_DRIVER=SQLite
CS9_DBCONFIG_DB_CONFIG=/home/myuser/cs9/config.sqlite
CS9_DBCONFIG_DB_ANON=/home/myuser/cs9/anon.sqliteCS9 creates each file, and any missing parent directory, on the first connection.
CS9_PATH must not be empty in either block.
check_environment_setup() treats an empty value as a
missing one and reports an error, so give it a real directory.
CS9_DBCONFIG_ROLE_CREATE_TABLE=x is in the PostgreSQL
block on purpose, and x is the sentinel meaning “take no
role”. Leaving the variable out is not the same as leaving it unset in
R. Sys.getenv() returns "" for an unset
variable, csdb substitutes the x sentinel only
for a NULL, and its PostgreSQL create_table
then tests role_create_table != "x", which is true for
"". The result is a SET ROLE "" in front of
the CREATE TABLE. Set the variable to a real role, or to
x. SQLite ignores it either way.
Which variable each backend reads
CS9 records every CS9_DBCONFIG_*
variable in the table below into its configuration list, whatever the
driver is. The configuration has one shape for every backend, and the
backend then uses the fields that apply to it. So “recorded but ignored”
is the accurate description, not “not read”: setting
CS9_DBCONFIG_SERVER under SQLite is recorded and has no
effect, rather than rejected.
“Required” below means check_environment_setup() reports
an error without it.
| Variable | PostgreSQL | SQLite |
|---|---|---|
CS9_DBCONFIG_ACCESS |
Required. A /-separated list of access levels. |
Required, and identical in meaning. |
CS9_DBCONFIG_DRIVER |
Required. PostgreSQL Unicode, matched exactly, because
it must equal an odbcinst.ini entry. |
Required. SQLite, matched case-insensitively. |
CS9_DBCONFIG_SERVER |
Required, and used to open the connection. | Not required. Recorded by CS9, ignored by the SQLite backend. |
CS9_DBCONFIG_PORT |
Required, and used to open the connection. | Not required. Recorded, ignored. |
CS9_DBCONFIG_USER |
Required, and used to authenticate. | Not required. Recorded, ignored. |
CS9_DBCONFIG_PASSWORD |
Required, and used to authenticate. | Not required. Recorded, ignored. |
CS9_DBCONFIG_TRUSTED_CONNECTION |
Not required. Recorded, and the PostgreSQL connection does not read it. | Not required. Recorded, ignored. |
CS9_DBCONFIG_SSLMODE |
Not required. Recorded, and used: require selects the
encrypted connection. |
Not required. Recorded, ignored. |
CS9_DBCONFIG_ROLE_CREATE_TABLE |
Not required, but see the SET ROLE "" trap above.
Recorded, and taken with SET ROLE before
CREATE TABLE. |
Not required. Recorded, ignored. SQLite has no roles. |
CS9_DBCONFIG_SCHEMA_<ACCESS> |
Required for the config and anon access
levels, which the validator names one by one. Identifiers become
schema.table. |
Not required. Recorded, ignored. SQLite has no schemas, so identifiers are the bare table name. |
CS9_DBCONFIG_DB_<ACCESS> |
Required for config and anon. The database
name. |
Required for every access level in CS9_DBCONFIG_ACCESS.
The path to the file. |
CS9_AUTO and CS9_PATH are required by both
and neither is a database setting. CS9_PATH must be a
non-empty directory path: check_environment_setup() counts
an empty value as missing.
The two backends derive the required DB and
SCHEMA variables differently, and it shows in the last two
rows. The PostgreSQL tier names CS9_DBCONFIG_SCHEMA_CONFIG,
CS9_DBCONFIG_DB_CONFIG,
CS9_DBCONFIG_SCHEMA_ANON and
CS9_DBCONFIG_DB_ANON literally, so an access level outside
config/anon is not checked for. The SQLite tier builds
CS9_DBCONFIG_DB_<ACCESS> from whatever
CS9_DBCONFIG_ACCESS holds, so every access level in the
list is checked.
What check_environment_setup() asks for
cs9::check_environment_setup() validates in tiers, and
the driver decides which tiers apply.
- Always:
CS9_AUTO,CS9_PATH,CS9_DBCONFIG_ACCESS,CS9_DBCONFIG_DRIVER. - Every driver except SQLite:
CS9_DBCONFIG_SERVERandCS9_DBCONFIG_PORT. - SQLite: one
CS9_DBCONFIG_DB_<ACCESS>per access level, andconfigamong the access levels. -
PostgreSQL Unicode:CS9_DBCONFIG_USER,CS9_DBCONFIG_PASSWORD,CS9_DBCONFIG_SCHEMA_CONFIG,CS9_DBCONFIG_DB_CONFIG,CS9_DBCONFIG_SCHEMA_ANONandCS9_DBCONFIG_DB_ANON.
config is required among the access levels because CS9
builds its four configuration tables from that access unconditionally.
An access list without it fails later, and obscurely.
Setting the variables from another package
CS9 is a dependency, so it loads first and has read the environment
before your own package’s .onLoad() runs. A package that
sets its own CS9_DBCONFIG_* values there must tell CS9 to
read them again.
.onLoad <- function(libname, pkgname) {
if (Sys.getenv("CS9_DBCONFIG_ACCESS") == "") {
Sys.setenv(
CS9_AUTO = "0",
CS9_PATH = tempdir(),
CS9_DBCONFIG_ACCESS = "config/anon",
CS9_DBCONFIG_DRIVER = "SQLite",
CS9_DBCONFIG_DB_CONFIG = fs::path(tempdir(), "cs9", "config.sqlite"),
CS9_DBCONFIG_DB_ANON = fs::path(tempdir(), "cs9", "anon.sqlite")
)
cs9::reload_db_config()
}
}The chunk does not run when the vignette is built, because defining
.onLoad() in a vignette would do nothing useful. The body
inside the if does run: paste it at the console in a fresh
session and cs9::config$dbconfigs is rebuilt against those
two files.
The guard matters. It leaves a real .Renviron in charge
whenever the user has written one, and supplies a working default only
when nobody has.
reload_db_config() rebuilds the configuration and the
table objects. It opens nothing: CS9 creates a table on the first
$connect(), not on load.
Which one to use
Use SQLite for a local install, for examples, for tests, and for a single-writer pipeline on one machine. It is a file.
Use PostgreSQL for a production surveillance system: several processes writing at once, data that outgrows one machine, and access levels that different people are allowed to read.
Switching between them is an edit to .Renviron and a
restart of R. The table definitions, the tasks and the action functions
do not change.
