more flexible signals

Now there's room to add arbitrary configuration to signals and types.
Several things are no longer fixed columns/tables but instead within
the configuration types.
This commit is contained in:
Scott Lamb
2021-10-26 10:12:19 -07:00
parent 4a7f22723c
commit dad349840d
14 changed files with 464 additions and 258 deletions

View File

@@ -159,7 +159,7 @@ fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState
let video_samples: i32 = row.get(5)?;
let video_sync_samples: i32 = row.get(6)?;
let video_sample_entry_id: i32 = row.get(7)?;
let sample_file_uuid: db::FromSqlUuid = row.get(8)?;
let sample_file_uuid: db::SqlUuid = row.get(8)?;
let sample_file_sha1: Vec<u8> = row.get(9)?;
let video_index: Vec<u8> = row.get(10)?;
let old_id: i32 = row.get(11)?;

View File

@@ -335,7 +335,7 @@ fn verify_dir_contents(
let mut stmt = tx.prepare(r"select sample_file_uuid from recording_playback")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let uuid: crate::db::FromSqlUuid = row.get(0)?;
let uuid: crate::db::SqlUuid = row.get(0)?;
if !files.remove(&uuid.0) {
bail!(
"{} is missing from dir {}!",
@@ -349,7 +349,7 @@ fn verify_dir_contents(
let mut stmt = tx.prepare(r"select uuid from reserved_sample_files")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let uuid: crate::db::FromSqlUuid = row.get(0)?;
let uuid: crate::db::SqlUuid = row.get(0)?;
if files.remove(&uuid.0) {
// Also remove the garbage file. For historical reasons (version 2 was originally
// defined as not having a garbage table so still is), do this here rather than with

View File

@@ -5,7 +5,7 @@
/// Upgrades a version 2 schema to a version 3 schema.
/// Note that a version 2 schema is never actually used; so we know the upgrade from version 1 was
/// completed, and possibly an upgrade from 2 to 3 is half-finished.
use crate::db::{self, FromSqlUuid};
use crate::db::{self, SqlUuid};
use crate::dir;
use crate::schema;
use failure::Error;
@@ -19,8 +19,8 @@ use std::sync::Arc;
/// * there's only one dir.
/// * it has a last completed open.
fn open_sample_file_dir(tx: &rusqlite::Transaction) -> Result<Arc<dir::SampleFileDir>, Error> {
let (p, s_uuid, o_id, o_uuid, db_uuid): (String, FromSqlUuid, i32, FromSqlUuid, FromSqlUuid) =
tx.query_row(
let (p, s_uuid, o_id, o_uuid, db_uuid): (String, SqlUuid, i32, SqlUuid, SqlUuid) = tx
.query_row(
r#"
select
s.path, s.uuid, s.last_complete_open_id, o.uuid, m.uuid
@@ -65,7 +65,7 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let id = db::CompositeId(row.get(0)?);
let sample_file_uuid: FromSqlUuid = row.get(1)?;
let sample_file_uuid: SqlUuid = row.get(1)?;
let from_path = super::UuidPath::from(sample_file_uuid.0);
let to_path = crate::dir::CompositeIdPath::from(id);
if let Err(e) = nix::fcntl::renameat(

View File

@@ -6,7 +6,7 @@
///
/// This just handles the directory meta files. If they're already in the new format, great.
/// Otherwise, verify they are consistent with the database then upgrade them.
use crate::db::FromSqlUuid;
use crate::db::SqlUuid;
use crate::{dir, schema};
use cstr::cstr;
use failure::{bail, Error, Fail};
@@ -111,7 +111,7 @@ fn maybe_cleanup_garbage_uuids(dir: &dir::Fd) -> Result<bool, Error> {
}
pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> {
let db_uuid: FromSqlUuid =
let db_uuid: SqlUuid =
tx.query_row_and_then(r"select uuid from meta", params![], |row| row.get(0))?;
let mut stmt = tx.prepare(
r#"
@@ -129,9 +129,9 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
while let Some(row) = rows.next()? {
let path = row.get_ref(0)?.as_str()?;
info!("path: {}", path);
let dir_uuid: FromSqlUuid = row.get(1)?;
let dir_uuid: SqlUuid = row.get(1)?;
let open_id: Option<u32> = row.get(2)?;
let open_uuid: Option<FromSqlUuid> = row.get(3)?;
let open_uuid: Option<SqlUuid> = row.get(3)?;
let mut db_meta = schema::DirMeta::new();
db_meta.db_uuid.extend_from_slice(&db_uuid.0.as_bytes()[..]);
db_meta

View File

@@ -3,11 +3,138 @@
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
/// Upgrades a version 6 schema to a version 7 schema.
use failure::Error;
use failure::{format_err, Error};
use fnv::FnvHashMap;
use rusqlite::{named_params, params};
use std::convert::TryFrom;
use url::Url;
use uuid::Uuid;
use crate::{json::CameraConfig, FromSqlUuid};
use crate::{
json::{CameraConfig, GlobalConfig, SignalConfig, SignalTypeConfig},
SqlUuid,
};
fn copy_meta(tx: &rusqlite::Transaction) -> Result<(), Error> {
let mut stmt = tx.prepare("select uuid, max_signal_changes from old_meta")?;
let mut insert = tx.prepare("insert into meta (uuid, config) values (:uuid, :config)")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let uuid: SqlUuid = row.get(0)?;
let max_signal_changes: Option<i64> = row.get(1)?;
let config = GlobalConfig {
max_signal_changes: max_signal_changes
.map(|s| {
u32::try_from(s).map_err(|_| format_err!("max_signal_changes out of range"))
})
.transpose()?,
..Default::default()
};
insert.execute(named_params! {
":uuid": uuid,
":config": &config,
})?;
}
Ok(())
}
fn copy_signal_types(tx: &rusqlite::Transaction) -> Result<(), Error> {
let mut types_ = FnvHashMap::default();
let mut stmt = tx.prepare("select type_uuid, value, name from signal_type_enum")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let type_uuid: SqlUuid = row.get(0)?;
let value: i32 = row.get(1)?;
let name: Option<String> = row.get(2)?;
let type_ = types_
.entry(type_uuid.0)
.or_insert_with(SignalTypeConfig::default);
let value = u8::try_from(value).map_err(|_| format_err!("bad signal type value"))?;
let value_config = type_.values.entry(value).or_insert_with(Default::default);
if let Some(n) = name {
value_config.name = n;
}
}
let mut insert = tx.prepare("insert into signal_type (uuid, config) values (?, ?)")?;
for (&uuid, config) in &types_ {
insert.execute(params![SqlUuid(uuid), config])?;
}
Ok(())
}
struct Signal {
uuid: Uuid,
type_uuid: Uuid,
config: SignalConfig,
}
fn copy_signals(tx: &rusqlite::Transaction) -> Result<(), Error> {
let mut signals = FnvHashMap::default();
// Read from signal table.
{
let mut stmt =
tx.prepare("select id, source_uuid, type_uuid, short_name from old_signal")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let id: i32 = row.get(0)?;
let id = u32::try_from(id)?;
let source_uuid: SqlUuid = row.get(1)?;
let type_uuid: SqlUuid = row.get(2)?;
let short_name: String = row.get(3)?;
signals.insert(
id,
Signal {
uuid: source_uuid.0,
type_uuid: type_uuid.0,
config: SignalConfig {
short_name,
..Default::default()
},
},
);
}
}
// Read from the signal_camera table.
{
let mut stmt = tx.prepare("select signal_id, camera_id, type from signal_camera")?;
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let signal_id: i32 = row.get(0)?;
let signal_id = u32::try_from(signal_id)?;
let camera_id: i32 = row.get(1)?;
let type_: i32 = row.get(2)?;
let signal = signals.get_mut(&signal_id).unwrap();
signal.config.camera_associations.insert(
camera_id,
match type_ {
0 => "direct",
_ => "indirect",
}
.to_owned(),
);
}
}
let mut insert = tx.prepare(
r#"
insert into signal (id, uuid, type_uuid, config)
values (:id, :uuid, :type_uuid, :config)
"#,
)?;
for (&id, signal) in &signals {
insert.execute(named_params! {
":id": id,
":uuid": SqlUuid(signal.uuid),
":type_uuid": SqlUuid(signal.type_uuid),
":config": &signal.config,
})?;
}
Ok(())
}
fn copy_cameras(tx: &rusqlite::Transaction) -> Result<(), Error> {
let mut insert = tx.prepare(
@@ -34,7 +161,7 @@ fn copy_cameras(tx: &rusqlite::Transaction) -> Result<(), Error> {
let mut rows = stmt.query(params![])?;
while let Some(row) = rows.next()? {
let id: i32 = row.get(0)?;
let uuid: FromSqlUuid = row.get(1)?;
let uuid: SqlUuid = row.get(1)?;
let uuid_bytes = &uuid.0.as_bytes()[..];
let short_name: String = row.get(2)?;
let mut description: Option<String> = row.get(3)?;
@@ -134,6 +261,13 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
alter table user add preferences text;
alter table camera rename to old_camera;
alter table stream rename to old_stream;
alter table signal rename to old_signal;
alter table meta rename to old_meta;
create table meta (
uuid blob not null check (length(uuid) = 16),
config text
);
create table camera (
id integer primary key,
@@ -153,9 +287,25 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
cum_runs integer not null check (cum_runs >= 0),
unique (camera_id, type)
);
create table signal (
id integer primary key,
uuid blob unique not null check (length(uuid) = 16),
type_uuid blob not null references signal_type (uuid)
check (length(type_uuid) = 16),
config text
);
create table signal_type (
uuid blob primary key check (length(uuid) = 16),
config text
) without rowid;
"#,
)?;
copy_meta(tx)?;
copy_cameras(tx)?;
copy_signal_types(tx)?;
copy_signals(tx)?;
copy_streams(tx)?;
tx.execute_batch(
r#"
@@ -224,6 +374,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
drop table old_recording;
drop table old_stream;
drop table old_camera;
drop table old_meta;
drop table old_signal;
drop table signal_type_enum;
drop table signal_camera;
"#,
)?;
Ok(())