Skip to main content
Version: 0.4

Schema Reference

ROSQL expects telemetry data in the OpenTelemetry schema conventions for ROS2. This document defines the complete schema: table definitions, span attributes, field mappings, and schema profiles.

If you're building a ROS2-to-OTel bridge or setting up your own telemetry storage, implement these conventions to get full ROSQL feature support.

The table names are the same across all backends. What differs is the physical layout — column naming conventions for SQL databases, or directory structure for the Parquet backend.


Span attributes

ROS2 action executions

AttributeTypeExampleUsed by ROSQL for
ros.nodestring/bt_navigatorWHERE node = '...'
ros.action.namestring/navigate_to_poseWHERE action_name = '...'
ros.action.typestringnav2_msgs/action/NavigateToPoseAction type filtering
ros.action.goal_idstringUUIDGoal correlation
ros.action.statusstringsucceeded / aborted / canceledAction status filtering

ROS2 pub/sub tracing

AttributeTypeExampleUsed by ROSQL for
ros.topicstring/cmd_velTopic filtering, MESSAGE FLOW
ros.message_typestringgeometry_msgs/msg/TwistMessage type filtering
ros.publisher_nodestring/controller_serverPublisher attribution
ros.subscriber_nodestring/motor_driverSubscriber attribution
ros.session.idstringsess_abc123FOR SESSION scoping

Universal scope resource attributes

These resource attributes are set at the SDK/bridge level and enable FOR clause scoping across all query types.

AttributeTypeExampleUsed by ROSQL for
robot.idstringrobot_42FOR ROBOT scoping
service.versionstringv1.2.3FOR VERSION scoping
deployment.environmentstringproductionFOR ENVIRONMENT scoping
ros.session.idstringsess_abc123FOR SESSION scoping

ParentSpanId convention for TRACE

The publish span's SpanId must be set as the ParentSpanId of the corresponding subscribe span. This creates the causal chain that TRACE traverses recursively via a CTE.

Publisher span (SpanId: "abc")
└── Subscriber span (ParentSpanId: "abc", SpanId: "def")
└── Downstream span (ParentSpanId: "def", SpanId: "ghi")

Implementing this correctly requires middleware-level instrumentation — it cannot be done purely at the application level without code changes to every node.


Metric naming conventions

Metric nameUnitDescription
ros2.topic.message_rateHzTopic publish rate
ros2.topic.bandwidthB/sTopic bandwidth
ros2.topic.messages_receivedMessages received by subscribers
ros2.topic.messages_capturedMessages captured (e.g. by recorder)
ros2.topic.messages_filteredMessages filtered/dropped
ros2.action_servers.countActive action server count
ros2.services.countActive service count
ros2.action.queued_goalsQueued action goals
ros2.action.active_goalsActive action goals
ros2.action.completion_rateHzAction completion rate
system.cpu.utilization%Total CPU utilization
system.memory.utilization%Memory utilization
system.memory.usageBMemory usage in bytes
system.filesystem.utilization%Disk utilization
system.filesystem.usageBDisk usage in bytes
system.disk.ioB/sDisk I/O throughput
system.disk.operationsops/sDisk IOPS
system.network.ioB/sNetwork I/O throughput
system.network.packetspackets/sNetwork packet rate
system.network.latencymsNetwork latency
system.network.jittermsNetwork jitter
system.network.packet_loss%Packet loss rate
system.temperature°CSystem temperature
system.battery.charge%Battery state of charge
system.battery.voltageVBattery voltage
system.battery.currentABattery current draw
system.battery.temperature°CBattery temperature
process.cpu.utilization%Per-process CPU utilization
process.memory.usageBPer-process memory usage

Required tables

These three tables are required for basic ROSQL functionality.

otel_traces

Used by: FROM traces, TRACE, MESSAGE FLOW, DURING(), ACTION_SUCCESS_RATE, MOVING_AVG, DERIVATIVE, SHOW DEPLOYMENTS, SHOW SPAN SUMMARY, SHOW PLANS

CREATE TABLE otel_traces (
timestamp TIMESTAMPTZ NOT NULL,
trace_id TEXT NOT NULL,
span_id TEXT NOT NULL,
parent_span_id TEXT NOT NULL DEFAULT '',
span_name_col TEXT NOT NULL,
span_kind TEXT NOT NULL DEFAULT 'INTERNAL',
service_name TEXT NOT NULL DEFAULT '',
duration BIGINT NOT NULL, -- nanoseconds
status_code TEXT NOT NULL DEFAULT 'OK',
span_attributes JSONB NOT NULL DEFAULT '{}',
resource_attributes JSONB NOT NULL DEFAULT '{}'
);

Use --schema otel-postgres (default for PostgreSQL and MySQL).

otel_logs

Used by: FROM logs

CREATE TABLE otel_logs (
timestamp TIMESTAMPTZ NOT NULL,
trace_id TEXT NOT NULL DEFAULT '',
span_id TEXT NOT NULL DEFAULT '',
severity_text TEXT NOT NULL DEFAULT 'INFO',
severity_number INTEGER NOT NULL DEFAULT 9,
service_name TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL DEFAULT '',
resource_attributes JSONB NOT NULL DEFAULT '{}',
log_attributes JSONB NOT NULL DEFAULT '{}'
);

otel_metrics

Used by: FROM metrics, TOPIC_RATE

CREATE TABLE otel_metrics (
timestamp TIMESTAMPTZ NOT NULL,
metric_name TEXT NOT NULL,
value DOUBLE PRECISION NOT NULL,
attributes JSONB NOT NULL DEFAULT '{}',
service_name TEXT NOT NULL DEFAULT ''
);

Optional tables

These tables enable additional ROSQL features. If absent, ROSQL returns a clear DataSourceUnavailable error with guidance.

topic_messages

Used by: FROM topics, FROM odom (and other topic aliases)

CREATE TABLE topic_messages (
robot_id TEXT NOT NULL,
topic_name TEXT NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
fields JSONB NOT NULL DEFAULT '{}',
message_type TEXT NOT NULL DEFAULT ''
);

mcap_metadata

Used by: FROM recordings, FROM recordings WHERE topic = '...'

CREATE TABLE mcap_metadata (
robot_id TEXT NOT NULL,
session_id TEXT NOT NULL,
start_time TIMESTAMPTZ NOT NULL,
end_time TIMESTAMPTZ NOT NULL,
s3_key TEXT NOT NULL,
topics TEXT[] NOT NULL DEFAULT '{}',
message_types JSONB NOT NULL DEFAULT '{}' -- topic → message_type map
);

robot_joint_map (v0.4.3+)

Stores URDF-derived joint metadata per robot model. Required for SHOW JOINTS.

note

Not available in the Parquet backend — requires a persistent database table.

CREATE TABLE robot_joint_map (
robot_model TEXT NOT NULL,
valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
valid_to TIMESTAMPTZ, -- NULL = currently active
version TEXT NOT NULL DEFAULT '',
robot_ids TEXT[] NOT NULL DEFAULT '{}', -- robots using this model
joint_map JSONB NOT NULL DEFAULT '[]' -- array of joint descriptors
);

joint_map element schema:

{
"joint_name": "shoulder_pan_joint",
"joint_index": 0,
"joint_type": "revolute",
"lower_limit": -3.14159,
"upper_limit": 3.14159
}

Used by: SHOW JOINTS, JOINT DEVIATION


Additional tables (optional)

These tables power the additional data sources. If absent, ROSQL returns a DataSourceUnavailable error.

note

These tables are supported in PostgreSQL and MySQL only. They are not part of the standard Parquet directory layout.

ROSQL sourceTableDescription
FROM tftf_statesROS2 TF transform data
FROM heartbeatsrobot_heartbeatsRobot heartbeat / liveness data
FROM system_logssystem_logsSystem-level log events (OS/kernel)
FROM diagnosticsotel_metricsROS2 diagnostic messages (maps to metrics)
FROM eventsros2_eventsROS2 lifecycle and state machine events

ROSQL field mappings

ROSQL field names are resolved to physical column names based on the active schema profile. Use --schema otel-postgres (default) or --schema otel-clickhouse.

The Parquet backend uses otel-postgres column names.

From otel_traces

ROSQL fieldColumnStorage unit
trace_idtrace_id
span_idspan_id
parent_span_idparent_span_id
span_namespan_name_col
serviceservice_name
durationdurationnanoseconds
statusstatus_codeOK / ERROR
nodespan_attributes->>'ros.node'
action_namespan_attributes->>'ros.action.name'
action_statusspan_attributes->>'ros.action.status'
topicspan_attributes->>'ros.topic'
robot_idresource_attributes->>'robot.id'
versionresource_attributes->>'service.version'
environmentresource_attributes->>'deployment.environment'
session_idresource_attributes->>'ros.session.id'

From otel_logs

ROSQL fieldColumn
messagebody
severityseverity_text
severity_numberseverity_number

From otel_metrics

ROSQL fieldResolved asStorage unit
metric_namemetric_name
metric_valuevaluevaries
publish_ratevalue WHERE metric_name = 'ros2.topic.message_rate'Hz
bandwidthvalue WHERE metric_name = 'ros2.topic.bandwidth'B/s
messages_receivedvalue WHERE metric_name = 'ros2.topic.messages_received'
messages_capturedvalue WHERE metric_name = 'ros2.topic.messages_captured'
messages_filteredvalue WHERE metric_name = 'ros2.topic.messages_filtered'
action_servers_countvalue WHERE metric_name = 'ros2.action_servers.count'
services_countvalue WHERE metric_name = 'ros2.services.count'
queued_goalsvalue WHERE metric_name = 'ros2.action.queued_goals'
active_goalsvalue WHERE metric_name = 'ros2.action.active_goals'
completion_ratevalue WHERE metric_name = 'ros2.action.completion_rate'Hz
cpu_usagevalue WHERE metric_name = 'system.cpu.utilization'%
memory_usagevalue WHERE metric_name = 'system.memory.utilization'%
memory_bytesvalue WHERE metric_name = 'system.memory.usage'B
disk_usagevalue WHERE metric_name = 'system.filesystem.utilization'%
disk_bytesvalue WHERE metric_name = 'system.filesystem.usage'B
disk_iovalue WHERE metric_name = 'system.disk.io'B/s
disk_iopsvalue WHERE metric_name = 'system.disk.operations'ops/s
network_iovalue WHERE metric_name = 'system.network.io'B/s
network_packetsvalue WHERE metric_name = 'system.network.packets'packets/s
network_latencyvalue WHERE metric_name = 'system.network.latency'ms
network_jittervalue WHERE metric_name = 'system.network.jitter'ms
packet_lossvalue WHERE metric_name = 'system.network.packet_loss'%
temperaturevalue WHERE metric_name = 'system.temperature'°C
battery_chargevalue WHERE metric_name = 'system.battery.charge'%
battery_voltagevalue WHERE metric_name = 'system.battery.voltage'V
battery_currentvalue WHERE metric_name = 'system.battery.current'A
battery_temperaturevalue WHERE metric_name = 'system.battery.temperature'°C
process_cpuvalue WHERE metric_name = 'process.cpu.utilization'%
process_memoryvalue WHERE metric_name = 'process.memory.usage'B

From topic_messages (odom / position data)

ROSQL geospatial operators (WITHIN) and JOINT DEVIATION extract nested values from the fields JSONB column using these conventions:

ROSQL pathfields JSON pathDescription
position.latitudefields->>'position.latitude'GPS latitude (degrees)
position.longitudefields->>'position.longitude'GPS longitude (degrees)
pose.pose.position.xfields->>'pose.pose.position.x'Local frame X (metres)
pose.pose.position.yfields->>'pose.pose.position.y'Local frame Y (metres)
position[N]fields->'position'->>NJoint position array element N (radians)

These match the standard nav_msgs/Odometry and sensor_msgs/JointState ROS2 message layouts.


Topic aliases

ROSQL sourceResolves to
FROM odomFROM topics WHERE topic_name = '/odom'
FROM joint_statesFROM topics WHERE topic_name = '/joint_states'
FROM batteryFROM topics WHERE topic_name = '/battery_state'
FROM cmd_velFROM topics WHERE topic_name = '/cmd_vel'
FROM imuFROM topics WHERE topic_name = '/imu/data'