node running, runtime version awareness

This commit is contained in:
2026-02-25 23:24:07 -06:00
parent e89b5991ec
commit 495b81236a
54 changed files with 4308 additions and 246 deletions

View File

@@ -29,6 +29,7 @@ pub struct CreateActionInput {
pub description: String,
pub entrypoint: String,
pub runtime: Option<Id>,
pub runtime_version_constraint: Option<String>,
pub param_schema: Option<JsonSchema>,
pub out_schema: Option<JsonSchema>,
pub is_adhoc: bool,
@@ -41,6 +42,7 @@ pub struct UpdateActionInput {
pub description: Option<String>,
pub entrypoint: Option<String>,
pub runtime: Option<Id>,
pub runtime_version_constraint: Option<Option<String>>,
pub param_schema: Option<JsonSchema>,
pub out_schema: Option<JsonSchema>,
}
@@ -54,7 +56,8 @@ impl FindById for ActionRepository {
let action = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE id = $1
"#,
@@ -76,7 +79,8 @@ impl FindByRef for ActionRepository {
let action = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE ref = $1
"#,
@@ -98,7 +102,8 @@ impl List for ActionRepository {
let actions = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
ORDER BY ref ASC
"#,
@@ -133,10 +138,11 @@ impl Create for ActionRepository {
let action = sqlx::query_as::<_, Action>(
r#"
INSERT INTO action (ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_adhoc)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
runtime, runtime_version_constraint, param_schema, out_schema, is_adhoc)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
"#,
)
.bind(&input.r#ref)
@@ -146,6 +152,7 @@ impl Create for ActionRepository {
.bind(&input.description)
.bind(&input.entrypoint)
.bind(input.runtime)
.bind(&input.runtime_version_constraint)
.bind(&input.param_schema)
.bind(&input.out_schema)
.bind(input.is_adhoc)
@@ -213,6 +220,15 @@ impl Update for ActionRepository {
has_updates = true;
}
if let Some(runtime_version_constraint) = &input.runtime_version_constraint {
if has_updates {
query.push(", ");
}
query.push("runtime_version_constraint = ");
query.push_bind(runtime_version_constraint);
has_updates = true;
}
if let Some(param_schema) = &input.param_schema {
if has_updates {
query.push(", ");
@@ -240,7 +256,7 @@ impl Update for ActionRepository {
query.push(", updated = NOW() WHERE id = ");
query.push_bind(id);
query.push(" RETURNING id, ref, pack, pack_ref, label, description, entrypoint, runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated");
query.push(" RETURNING id, ref, pack, pack_ref, label, description, entrypoint, runtime, runtime_version_constraint, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated");
let action = query
.build_query_as::<Action>()
@@ -279,7 +295,8 @@ impl ActionRepository {
let actions = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE pack = $1
ORDER BY ref ASC
@@ -300,7 +317,8 @@ impl ActionRepository {
let actions = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE runtime = $1
ORDER BY ref ASC
@@ -322,7 +340,8 @@ impl ActionRepository {
let actions = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1
ORDER BY ref ASC
@@ -343,7 +362,8 @@ impl ActionRepository {
let actions = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE is_workflow = true
ORDER BY ref ASC
@@ -366,7 +386,8 @@ impl ActionRepository {
let action = sqlx::query_as::<_, Action>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
FROM action
WHERE workflow_def = $1
"#,
@@ -393,7 +414,8 @@ impl ActionRepository {
SET is_workflow = true, workflow_def = $2, updated = NOW()
WHERE id = $1
RETURNING id, ref, pack, pack_ref, label, description, entrypoint,
runtime, param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
runtime, runtime_version_constraint,
param_schema, out_schema, is_workflow, workflow_def, is_adhoc, created, updated
"#,
)
.bind(action_id)

View File

@@ -40,6 +40,7 @@ pub mod pack_test;
pub mod queue_stats;
pub mod rule;
pub mod runtime;
pub mod runtime_version;
pub mod trigger;
pub mod workflow;
@@ -57,6 +58,7 @@ pub use pack_test::PackTestRepository;
pub use queue_stats::QueueStatsRepository;
pub use rule::RuleRepository;
pub use runtime::{RuntimeRepository, WorkerRepository};
pub use runtime_version::RuntimeVersionRepository;
pub use trigger::{SensorRepository, TriggerRepository};
pub use workflow::{WorkflowDefinitionRepository, WorkflowExecutionRepository};

View File

@@ -0,0 +1,447 @@
//! Repository for runtime version operations
//!
//! Provides CRUD operations and specialized queries for the `runtime_version`
//! table, which stores version-specific execution configurations for runtimes.
use crate::error::Result;
use crate::models::{Id, RuntimeVersion};
use crate::repositories::{Create, Delete, FindById, List, Repository, Update};
use sqlx::{Executor, Postgres, QueryBuilder};
/// Repository for runtime version database operations
pub struct RuntimeVersionRepository;
impl Repository for RuntimeVersionRepository {
type Entity = RuntimeVersion;
fn table_name() -> &'static str {
"runtime_version"
}
}
/// Input for creating a new runtime version
#[derive(Debug, Clone)]
pub struct CreateRuntimeVersionInput {
pub runtime: Id,
pub runtime_ref: String,
pub version: String,
pub version_major: Option<i32>,
pub version_minor: Option<i32>,
pub version_patch: Option<i32>,
pub execution_config: serde_json::Value,
pub distributions: serde_json::Value,
pub is_default: bool,
pub available: bool,
pub meta: serde_json::Value,
}
/// Input for updating an existing runtime version
#[derive(Debug, Clone, Default)]
pub struct UpdateRuntimeVersionInput {
pub version: Option<String>,
pub version_major: Option<Option<i32>>,
pub version_minor: Option<Option<i32>>,
pub version_patch: Option<Option<i32>>,
pub execution_config: Option<serde_json::Value>,
pub distributions: Option<serde_json::Value>,
pub is_default: Option<bool>,
pub available: Option<bool>,
pub verified_at: Option<Option<chrono::DateTime<chrono::Utc>>>,
pub meta: Option<serde_json::Value>,
}
const SELECT_COLUMNS: &str = r#"
id, runtime, runtime_ref, version,
version_major, version_minor, version_patch,
execution_config, distributions,
is_default, available, verified_at, meta,
created, updated
"#;
#[async_trait::async_trait]
impl FindById for RuntimeVersionRepository {
async fn find_by_id<'e, E>(executor: E, id: i64) -> Result<Option<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let row = sqlx::query_as::<_, RuntimeVersion>(&format!(
"SELECT {} FROM runtime_version WHERE id = $1",
SELECT_COLUMNS
))
.bind(id)
.fetch_optional(executor)
.await?;
Ok(row)
}
}
#[async_trait::async_trait]
impl List for RuntimeVersionRepository {
async fn list<'e, E>(executor: E) -> Result<Vec<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let rows = sqlx::query_as::<_, RuntimeVersion>(&format!(
"SELECT {} FROM runtime_version ORDER BY runtime_ref ASC, version ASC",
SELECT_COLUMNS
))
.fetch_all(executor)
.await?;
Ok(rows)
}
}
#[async_trait::async_trait]
impl Create for RuntimeVersionRepository {
type CreateInput = CreateRuntimeVersionInput;
async fn create<'e, E>(executor: E, input: Self::CreateInput) -> Result<RuntimeVersion>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let row = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
INSERT INTO runtime_version (
runtime, runtime_ref, version,
version_major, version_minor, version_patch,
execution_config, distributions,
is_default, available, meta
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING {}
"#,
SELECT_COLUMNS
))
.bind(input.runtime)
.bind(&input.runtime_ref)
.bind(&input.version)
.bind(input.version_major)
.bind(input.version_minor)
.bind(input.version_patch)
.bind(&input.execution_config)
.bind(&input.distributions)
.bind(input.is_default)
.bind(input.available)
.bind(&input.meta)
.fetch_one(executor)
.await?;
Ok(row)
}
}
#[async_trait::async_trait]
impl Update for RuntimeVersionRepository {
type UpdateInput = UpdateRuntimeVersionInput;
async fn update<'e, E>(executor: E, id: i64, input: Self::UpdateInput) -> Result<RuntimeVersion>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let mut query: QueryBuilder<Postgres> = QueryBuilder::new("UPDATE runtime_version SET ");
let mut has_updates = false;
if let Some(version) = &input.version {
query.push("version = ");
query.push_bind(version);
has_updates = true;
}
if let Some(version_major) = &input.version_major {
if has_updates {
query.push(", ");
}
query.push("version_major = ");
query.push_bind(*version_major);
has_updates = true;
}
if let Some(version_minor) = &input.version_minor {
if has_updates {
query.push(", ");
}
query.push("version_minor = ");
query.push_bind(*version_minor);
has_updates = true;
}
if let Some(version_patch) = &input.version_patch {
if has_updates {
query.push(", ");
}
query.push("version_patch = ");
query.push_bind(*version_patch);
has_updates = true;
}
if let Some(execution_config) = &input.execution_config {
if has_updates {
query.push(", ");
}
query.push("execution_config = ");
query.push_bind(execution_config);
has_updates = true;
}
if let Some(distributions) = &input.distributions {
if has_updates {
query.push(", ");
}
query.push("distributions = ");
query.push_bind(distributions);
has_updates = true;
}
if let Some(is_default) = input.is_default {
if has_updates {
query.push(", ");
}
query.push("is_default = ");
query.push_bind(is_default);
has_updates = true;
}
if let Some(available) = input.available {
if has_updates {
query.push(", ");
}
query.push("available = ");
query.push_bind(available);
has_updates = true;
}
if let Some(verified_at) = &input.verified_at {
if has_updates {
query.push(", ");
}
query.push("verified_at = ");
query.push_bind(*verified_at);
has_updates = true;
}
if let Some(meta) = &input.meta {
if has_updates {
query.push(", ");
}
query.push("meta = ");
query.push_bind(meta);
has_updates = true;
}
if !has_updates {
// Nothing to update — just fetch the current row
return Self::find_by_id(executor, id)
.await?
.ok_or_else(|| crate::Error::not_found("runtime_version", "id", id.to_string()));
}
query.push(" WHERE id = ");
query.push_bind(id);
query.push(&format!(" RETURNING {}", SELECT_COLUMNS));
let row = query
.build_query_as::<RuntimeVersion>()
.fetch_optional(executor)
.await?
.ok_or_else(|| crate::Error::not_found("runtime_version", "id", id.to_string()))?;
Ok(row)
}
}
#[async_trait::async_trait]
impl Delete for RuntimeVersionRepository {
async fn delete<'e, E>(executor: E, id: i64) -> Result<bool>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let result = sqlx::query("DELETE FROM runtime_version WHERE id = $1")
.bind(id)
.execute(executor)
.await?;
Ok(result.rows_affected() > 0)
}
}
/// Specialized queries
impl RuntimeVersionRepository {
/// Find all versions for a given runtime ID.
///
/// Returns versions ordered by major, minor, patch descending
/// (newest version first).
pub async fn find_by_runtime<'e, E>(executor: E, runtime_id: Id) -> Result<Vec<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let rows = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
SELECT {}
FROM runtime_version
WHERE runtime = $1
ORDER BY version_major DESC NULLS LAST,
version_minor DESC NULLS LAST,
version_patch DESC NULLS LAST
"#,
SELECT_COLUMNS
))
.bind(runtime_id)
.fetch_all(executor)
.await?;
Ok(rows)
}
/// Find all versions for a given runtime ref (e.g., "core.python").
pub async fn find_by_runtime_ref<'e, E>(
executor: E,
runtime_ref: &str,
) -> Result<Vec<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let rows = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
SELECT {}
FROM runtime_version
WHERE runtime_ref = $1
ORDER BY version_major DESC NULLS LAST,
version_minor DESC NULLS LAST,
version_patch DESC NULLS LAST
"#,
SELECT_COLUMNS
))
.bind(runtime_ref)
.fetch_all(executor)
.await?;
Ok(rows)
}
/// Find all available versions for a given runtime ID.
///
/// Only returns versions where `available = true`.
pub async fn find_available_by_runtime<'e, E>(
executor: E,
runtime_id: Id,
) -> Result<Vec<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let rows = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
SELECT {}
FROM runtime_version
WHERE runtime = $1 AND available = TRUE
ORDER BY version_major DESC NULLS LAST,
version_minor DESC NULLS LAST,
version_patch DESC NULLS LAST
"#,
SELECT_COLUMNS
))
.bind(runtime_id)
.fetch_all(executor)
.await?;
Ok(rows)
}
/// Find the default version for a given runtime ID.
///
/// Returns `None` if no version is marked as default.
pub async fn find_default_by_runtime<'e, E>(
executor: E,
runtime_id: Id,
) -> Result<Option<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let row = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
SELECT {}
FROM runtime_version
WHERE runtime = $1 AND is_default = TRUE
LIMIT 1
"#,
SELECT_COLUMNS
))
.bind(runtime_id)
.fetch_optional(executor)
.await?;
Ok(row)
}
/// Find a specific version by runtime ID and version string.
pub async fn find_by_runtime_and_version<'e, E>(
executor: E,
runtime_id: Id,
version: &str,
) -> Result<Option<RuntimeVersion>>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let row = sqlx::query_as::<_, RuntimeVersion>(&format!(
r#"
SELECT {}
FROM runtime_version
WHERE runtime = $1 AND version = $2
"#,
SELECT_COLUMNS
))
.bind(runtime_id)
.bind(version)
.fetch_optional(executor)
.await?;
Ok(row)
}
/// Clear the `is_default` flag on all versions for a runtime.
///
/// Useful before setting a new default version.
pub async fn clear_default_for_runtime<'e, E>(executor: E, runtime_id: Id) -> Result<u64>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let result = sqlx::query(
"UPDATE runtime_version SET is_default = FALSE WHERE runtime = $1 AND is_default = TRUE",
)
.bind(runtime_id)
.execute(executor)
.await?;
Ok(result.rows_affected())
}
/// Mark a version's availability and update the verification timestamp.
pub async fn set_availability<'e, E>(executor: E, id: Id, available: bool) -> Result<bool>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let result = sqlx::query(
"UPDATE runtime_version SET available = $1, verified_at = NOW() WHERE id = $2",
)
.bind(available)
.bind(id)
.execute(executor)
.await?;
Ok(result.rows_affected() > 0)
}
/// Delete all versions for a given runtime ID.
pub async fn delete_by_runtime<'e, E>(executor: E, runtime_id: Id) -> Result<u64>
where
E: Executor<'e, Database = Postgres> + 'e,
{
let result = sqlx::query("DELETE FROM runtime_version WHERE runtime = $1")
.bind(runtime_id)
.execute(executor)
.await?;
Ok(result.rows_affected())
}
}

View File

@@ -518,6 +518,7 @@ pub struct CreateSensorInput {
pub entrypoint: String,
pub runtime: Id,
pub runtime_ref: String,
pub runtime_version_constraint: Option<String>,
pub trigger: Id,
pub trigger_ref: String,
pub enabled: bool,
@@ -533,6 +534,7 @@ pub struct UpdateSensorInput {
pub entrypoint: Option<String>,
pub runtime: Option<Id>,
pub runtime_ref: Option<String>,
pub runtime_version_constraint: Option<Option<String>>,
pub trigger: Option<Id>,
pub trigger_ref: Option<String>,
pub enabled: Option<bool>,
@@ -549,7 +551,8 @@ impl FindById for SensorRepository {
let sensor = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
WHERE id = $1
@@ -572,7 +575,8 @@ impl FindByRef for SensorRepository {
let sensor = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
WHERE ref = $1
@@ -595,7 +599,8 @@ impl List for SensorRepository {
let sensors = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
ORDER BY ref ASC
@@ -619,11 +624,13 @@ impl Create for SensorRepository {
let sensor = sqlx::query_as::<_, Sensor>(
r#"
INSERT INTO sensor (ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
"#,
)
@@ -635,6 +642,7 @@ impl Create for SensorRepository {
.bind(&input.entrypoint)
.bind(input.runtime)
.bind(&input.runtime_ref)
.bind(&input.runtime_version_constraint)
.bind(input.trigger)
.bind(&input.trigger_ref)
.bind(input.enabled)
@@ -711,6 +719,15 @@ impl Update for SensorRepository {
has_updates = true;
}
if let Some(runtime_version_constraint) = &input.runtime_version_constraint {
if has_updates {
query.push(", ");
}
query.push("runtime_version_constraint = ");
query.push_bind(runtime_version_constraint);
has_updates = true;
}
if let Some(trigger) = input.trigger {
if has_updates {
query.push(", ");
@@ -754,7 +771,7 @@ impl Update for SensorRepository {
query.push(", updated = NOW() WHERE id = ");
query.push_bind(id);
query.push(" RETURNING id, ref, pack, pack_ref, label, description, entrypoint, runtime, runtime_ref, trigger, trigger_ref, enabled, param_schema, config, created, updated");
query.push(" RETURNING id, ref, pack, pack_ref, label, description, entrypoint, runtime, runtime_ref, runtime_version_constraint, trigger, trigger_ref, enabled, param_schema, config, created, updated");
let sensor = query.build_query_as::<Sensor>().fetch_one(executor).await?;
@@ -786,7 +803,8 @@ impl SensorRepository {
let sensors = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
WHERE trigger = $1
@@ -808,7 +826,8 @@ impl SensorRepository {
let sensors = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
WHERE enabled = true
@@ -829,7 +848,8 @@ impl SensorRepository {
let sensors = sqlx::query_as::<_, Sensor>(
r#"
SELECT id, ref, pack, pack_ref, label, description, entrypoint,
runtime, runtime_ref, trigger, trigger_ref, enabled,
runtime, runtime_ref, runtime_version_constraint,
trigger, trigger_ref, enabled,
param_schema, config, created, updated
FROM sensor
WHERE pack = $1