Parascope Docs

ParaQL Language Reference

Complete syntax reference for ParaQL including SELECT, FROM, WHERE, aggregation, traversal, temporal queries, and built-in functions

This page is the definitive syntax reference for ParaQL. Every clause, operator, function, and special statement is documented here.

Basic SELECT

SELECT <columns>
FROM <source>
[WHERE <conditions>]
[GROUP BY <columns>]
[HAVING <conditions>]
[ORDER BY <column> [ASC|DESC]]
[LIMIT <n>]
[OFFSET <n>]

SELECT Columns

SyntaxDescription
*All universal columns
nameUniversal field
config.phaseConfig-namespace field (type-specific)
metrics.cpu_percentMetrics-namespace field
raw.metadata.labelsRaw JSONB deep path access
COUNT(*) AS totalAggregate with alias
config.ram_mb / 1024 AS ram_gbArithmetic expression

DISTINCT

SELECT DISTINCT namespace
FROM kubernetes.pod

Removes duplicate rows from the result set.

FROM Clause

CI Types

Query a specific CI type using the source.type format:

FROM kubernetes.pod
FROM openstack.instance
FROM ceph.osd

Use SHOW TABLES to list all available CI types.

Wildcard (All Types)

FROM *

Queries across all CI types. Useful for cross-system searches. Note that this carries a higher complexity cost.

Special Tables

TableDescription
changesGlobal change history (ci_name, change_type, changed_at, field details)
relationshipsRelationship records (source_ci_id, target_ci_id, relationship_type)
SELECT ci_name, change_type, changed_at
FROM changes
WHERE changed_at > NOW() - INTERVAL '24 hours'
ORDER BY changed_at DESC

Table Aliases

SELECT p.name, p.config.phase
FROM kubernetes.pod p
WHERE p.config.phase = 'Running'

WHERE Clause

Comparison Operators

OperatorExample
=WHERE status = 'active'
!=WHERE config.phase != 'Running'
>WHERE config.vcpus > 4
>=WHERE config.ram_mb >= 1024
<WHERE criticality_score < 50
<=WHERE config.disk_gb <= 100

Pattern Matching

OperatorDescriptionExample
LIKECase-sensitive pattern (% = any chars, _ = single char)WHERE name LIKE 'web-%'
ILIKECase-insensitive patternWHERE name ILIKE '%nginx%'

Set Operators

WHERE config.phase IN ('Pending', 'Failed', 'Unknown')
WHERE source NOT IN ('kubernetes', 'ceph')

Range

WHERE changed_at BETWEEN NOW() - INTERVAL '48 hours' AND NOW()
WHERE config.vcpus BETWEEN 2 AND 8

NULL Checks

WHERE namespace IS NULL
WHERE criticality IS NOT NULL

Staleness

Check if a CI hasn't been seen recently:

WHERE last_seen IS STALE BY INTERVAL '2 hours'

Logical Operators

Combine conditions with AND, OR, and NOT. Use parentheses for grouping:

WHERE (config.phase = 'Running' OR config.phase = 'Succeeded')
  AND namespace != 'kube-system'
  AND NOT name LIKE 'test-%'

Automatic Active Filter

ParaQL automatically adds status = 'active' to CI queries unless you explicitly reference status in your WHERE clause. This means:

-- Implicitly filtered to active CIs only
SELECT name FROM kubernetes.pod

-- No auto-filter because status is referenced
SELECT name, status FROM kubernetes.pod WHERE status = 'inactive'

Column Namespaces

Every CI has three data namespaces:

NamespaceAccessDescription
Universalname, ci_type, status, source, created_at, last_seen, namespace, criticality, criticality_scoreCommon fields shared by all CI types
Configconfig.phase, config.vcpusType-specific configuration data (tracked in change history)
Metricsmetrics.cpu_percentType-specific runtime metrics (not tracked in changes)
Rawraw.metadata.labelsDeep JSONB access into the original collected data

Use DESCRIBE kubernetes.pod to see all available columns for a CI type, grouped by namespace.

Aggregation

GROUP BY

SELECT source, COUNT(*) AS count
FROM *
WHERE status = 'active'
GROUP BY source
ORDER BY count DESC

Aggregate Functions

FunctionDescription
COUNT(*)Count rows
COUNT(column)Count non-null values
SUM(column)Sum numeric values
AVG(column)Average
MIN(column)Minimum value
MAX(column)Maximum value

HAVING

Filter aggregated results (applied after GROUP BY):

SELECT namespace, COUNT(*) AS pod_count
FROM kubernetes.pod
GROUP BY namespace
HAVING COUNT(*) > 5
ORDER BY pod_count DESC

Ordering and Pagination

ORDER BY name ASC                    -- ascending (default)
ORDER BY criticality_score DESC      -- descending
ORDER BY namespace, name             -- multi-column sort
LIMIT 50                             -- max rows returned
OFFSET 20                            -- skip first N rows

When no LIMIT is specified, ParaQL applies a default limit of 1000 rows to prevent accidental full-table scans.

Scalar Functions

FunctionDescriptionExample
COALESCE(a, b)First non-null valueCOALESCE(namespace, 'none')
NULLIF(a, b)Returns null if a = bNULLIF(status, 'unknown')
UPPER(col)Uppercase stringUPPER(name)
LOWER(col)Lowercase stringLOWER(ci_type)
TRIM(col)Strip whitespaceTRIM(name)
CONCAT(a, b, ...)Concatenate stringsCONCAT(source, '.', ci_type)
LENGTH(col)String lengthLENGTH(name)
CAST(col AS type)Type castCAST(config.vcpus AS float)
DATE_TRUNC(unit, col)Truncate timestampDATE_TRUNC('day', created_at)
NOW()Current timestampWHERE created_at > NOW() - INTERVAL '7 days'

Computed Virtual Columns

These are ParaQL-specific functions that compute derived values from CI metadata:

FunctionReturnsDescription
age()DurationTime since CI was first created
stale_for()DurationTime since CI was last seen by a collector
last_updated()TimestampMost recent change timestamp
change_count()IntegerTotal number of changes for the CI
change_count(INTERVAL '7 days')IntegerChanges within the specified window
relationship_count()IntegerTotal relationships (inbound + outbound)
inbound_count()IntegerRelationships pointing to this CI
outbound_count()IntegerRelationships from this CI to others
SELECT name, ci_type,
       age() AS ci_age,
       stale_for() AS staleness,
       change_count(INTERVAL '7 days') AS weekly_changes,
       relationship_count() AS rels
FROM *
WHERE status = 'active'
ORDER BY weekly_changes DESC
LIMIT 20

Tip: Always alias computed columns (e.g., age() AS ci_age) for readable column headers in results.

Arithmetic

Standard arithmetic operators work on numeric columns:

SELECT name,
       config.vcpus,
       config.ram_mb / 1024 AS ram_gb,
       config.vcpus * 2 AS double_vcpus
FROM openstack.instance
WHERE status = 'active'
ORDER BY config.vcpus DESC

Supported operators: +, -, *, /, % (modulo).

Intervals

Time intervals are used with temporal functions and comparisons:

INTERVAL '7 days'
INTERVAL '24 hours'
INTERVAL '30 minutes'
INTERVAL '2 hours'
NOW() - INTERVAL '7 days'

Relationship Traversal

Walk the infrastructure graph using arrow syntax:

SELECT dep.name, dep.ci_type,
       pod.name, pod.ci_type
FROM kubernetes.deployment dep
  -[manages]-> kubernetes.pod pod

Arrow Syntax

SyntaxDirectionMeaning
-[type]->ForwardSource runs/contains/manages target
<-[type]-ReverseTarget runs/contains/manages source
-[*]->Wildcard typeAny relationship type
-[*1..3]->Variable depth1 to 3 hops of any type

Multi-Hop Traversal

Chain multiple hops to walk deep into the infrastructure stack:

SELECT node.name, node.ci_type,
       vm.name, vm.ci_type,
       os.name, os.ci_type
FROM proxmox.node node
  <-[runs_on]- proxmox.vm vm
  <-[runs_on]- os.linux os

Wildcard Targets

Match any CI type at the other end:

SELECT src.name, src.ci_type,
       tgt.name, tgt.ci_type
FROM kubernetes.deployment src
  -[*]-> * tgt
LIMIT 30

Variable-Depth Traversal

Explore relationships up to N hops deep:

SELECT src.name, src.ci_type,
       tgt.name, tgt.ci_type
FROM proxmox.node src
  -[*1..3]-> * tgt
LIMIT 50

Maximum depth is 10 hops. Cycle detection prevents infinite loops.

Filtering Traversal Results

Apply WHERE conditions to any aliased table in the traversal:

SELECT dep.name, dep.ci_type,
       pod.name, pod.ci_type
FROM kubernetes.deployment dep
  -[manages]-> kubernetes.pod pod
WHERE pod.config.phase = 'Running'

LINEAGE (Infrastructure Stack)

Discover the full infrastructure stack above and below a CI — from physical hardware up through virtualization to application containers:

SELECT name, ci_type, tier, depth, direction
FROM LINEAGE(kubernetes.pod, name LIKE '%api%')
ColumnDescription
nameCI name
ci_typeCI type
tierInfrastructure tier (hardware, os, virtual, platform, application, service)
depthHops from the seed CI
direction"up" (toward hardware) or "down" (toward applications)
relationship_typeType of relationship connecting this hop

LINEAGE respects the tier ordering defined in the tier registry to produce a coherent stack view.

-- Lineage for a specific node
SELECT name, ci_type, tier, depth, direction, relationship_type
FROM LINEAGE(proxmox.node)

IMPACT (Blast Radius)

Determine what would be affected if a CI goes down:

SELECT name, ci_type, depth, path, criticality, criticality_score
FROM IMPACT(proxmox.node, name LIKE '%pve%')
ORDER BY depth, criticality_score DESC
ColumnDescription
nameAffected CI name
ci_typeAffected CI type
depthHops from the failed CI
pathRelationship path from source to this CI
criticalityCriticality tier (Critical, High, Medium, Low)
criticality_scoreNumeric criticality score (0-100)

Impact analysis follows all relationships outward from the seed CI, building a dependency tree of everything that would break.

-- Storage failure: what depends on this pool?
SELECT name, ci_type, depth, path, criticality, criticality_score
FROM IMPACT(ceph.pool)
ORDER BY criticality_score DESC
LIMIT 50

ORPHANS

Find CIs with no relationships — potential configuration drift or missing discovery:

SELECT name, ci_type, source, age() AS ci_age, last_seen
FROM ORPHANS()
WHERE status = 'active'
ORDER BY ci_age DESC
LIMIT 30

You can optionally filter orphans to a specific CI type:

SELECT COUNT(*) AS orphaned_pods
FROM ORPHANS(kubernetes.pod)

DIFF (Temporal Comparison)

Compare infrastructure state between two points in time:

DIFF kubernetes.pod
BETWEEN NOW() - INTERVAL '7 days' AND NOW()

Results include three categories:

Diff TypeMeaning
AddedCIs that were created in the time window
RemovedCIs that were deleted in the time window
ModifiedCIs with field-level changes

DIFF supports WHERE, ORDER BY, and LIMIT:

DIFF openstack.instance
BETWEEN NOW() - INTERVAL '30 days' AND NOW()
ORDER BY name
LIMIT 100

AS OF (Point-in-Time Snapshot)

View infrastructure state as it existed at a specific point in the past:

SELECT name, config.phase, config.node_name
FROM kubernetes.pod
AS OF NOW() - INTERVAL '7 days'
WHERE config.phase = 'Running'

AS OF reconstructs CI state by walking the change history backwards from the specified timestamp. This works with any standard SELECT query.

Schema Introspection

StatementDescription
SHOW TABLESList all CI types
SHOW TABLES FROM kubernetesCI types from a specific source
DESCRIBE kubernetes.podColumn names, types, and namespaces
SHOW RELATIONSHIPSAll relationship types with source/target
SHOW RELATIONSHIPS FOR proxmox.nodeRelationships involving a specific type
SHOW SOURCESAll configured data sources
SHOW TIERSInfrastructure tier hierarchy

EXPLAIN

Preview the execution plan without running the query:

EXPLAIN SELECT name, config.phase
FROM kubernetes.pod
WHERE config.phase = 'Running'

The explain view shows the query execution plan, complexity score, and complexity breakdown. Useful for understanding query cost and optimizing performance.

Complexity System

Every query is scored for complexity before execution. This protects the database from accidentally expensive queries.

TierScore RangeBehavior
Immediate0-15Executed immediately
Warning16-25Executed with a performance warning
Elevated26-40Requires elevated API key privileges
Reject41+Rejected — simplify the query

Factors that increase complexity:

  • FROM * (wildcard across all types)
  • Relationship traversal (per hop)
  • Variable-depth traversal
  • IMPACT, LINEAGE, ORPHANS
  • DIFF, AS OF
  • Missing LIMIT clause
  • Leading-wildcard ILIKE (ILIKE '%term%')

Use EXPLAIN to inspect the complexity breakdown of any query.