SQL Syntax Reference
Complete grammar reference for Stategraph SQL (Structured Query Language).
Grammar Overview
Stategraph supports a subset of SQL for querying Terraform state data.
query := [cte] SELECT select_list FROM table [join ...] [WHERE expr] [GROUP BY exprs] [HAVING expr] [ORDER BY order_exprs] [LIMIT n] [UNION query]
join := [INNER | LEFT | RIGHT] JOIN table ON expr
SELECT Statement
Basic Syntax
SELECT columns FROM table
Select All Columns
SELECT * FROM instances
Select Specific Columns
SELECT address, type, provider FROM resources
Column Aliases
SELECT address AS resource_address, type AS resource_type FROM resources
FROM Clause
Single Table
SELECT * FROM instances
Table Aliases
Alias a table with AS, then qualify columns with the alias:
SELECT r.type, r.provider FROM resources AS r
JOINs
Combine tables with INNER JOIN, LEFT JOIN, or RIGHT JOIN. Use an ON clause to match rows:
SELECT i.address, r.type
FROM instances AS i
INNER JOIN resources AS r
ON i.resource_address = r.address
AND i.state_id = r.state_id
WHERE r.type = 'aws_instance'
Multiple joins in a single query are supported.
Available Tables
| Table | Description |
|---|---|
instances |
Resource instances (attributes, dependencies, state) |
resources |
Resource definitions (type, provider, module) |
states |
Terraform states |
providers |
Provider information |
outputs |
State outputs |
transactions |
Transaction history |
transaction_logs |
Transaction log entries |
tenants |
Tenant information |
users |
User information |
check_results |
Check results |
check_entries |
Check entries |
See Query (SQL) for complete column documentation.
WHERE Clause
Basic Filtering
SELECT * FROM resources WHERE type = 'aws_instance'
Multiple Conditions
SELECT * FROM resources
WHERE type = 'aws_instance'
AND provider = 'registry.terraform.io/hashicorp/aws'
Expressions
Literals
| Type | Examples |
|---|---|
| String | 'hello', 'aws_instance' |
| Integer | 42, -1, 0 |
| Float | 3.14, -0.5 |
| Boolean | true, false |
| Null | null |
Identifiers
Column and table names:
address
type
instances.address
Operators
Comparison Operators
| Operator | Description | Example |
|---|---|---|
= |
Equal | type = 'aws_instance' |
<> |
Not equal | type <> 'null_resource' |
< |
Less than | count < 10 |
> |
Greater than | count > 100 |
<= |
Less or equal | count <= 50 |
>= |
Greater or equal | count >= 5 |
Logical Operators
| Operator | Description | Example |
|---|---|---|
AND |
Logical AND | a = 1 AND b = 2 |
OR |
Logical OR | a = 1 OR b = 2 |
NOT |
Logical NOT | NOT a = 1 |
Arithmetic Operators
| Operator | Description | Example |
|---|---|---|
+ |
Addition | a + b |
- |
Subtraction | a - b |
* |
Multiplication | a * b |
/ |
Division | a / b |
- (unary) |
Negation | -a |
String Operators
| Operator | Description | Example |
|---|---|---|
\|\| |
Concatenation | 'hello' \|\| ' world' |
NULL Handling
| Expression | Description |
|---|---|
IS NULL |
Value is null |
IS NOT NULL |
Value is not null |
IS DISTINCT FROM |
NULL-safe not equal |
IS NOT DISTINCT FROM |
NULL-safe equal |
SELECT * FROM resources WHERE module IS NULL
SELECT * FROM resources WHERE module IS NOT NULL
Pattern Matching
IN
SELECT * FROM resources
WHERE type IN ('aws_instance', 'aws_ebs_volume', 'aws_eip')
LIKE / ILIKE
LIKE matches a pattern case-sensitively; ILIKE is case-insensitive. Use % for any sequence of characters and _ for a single character. Both support a negated form (NOT LIKE, NOT ILIKE).
-- Resource types starting with "aws_"
SELECT * FROM resources WHERE type LIKE 'aws_%'
-- Case-insensitive substring match
SELECT * FROM resources WHERE name ILIKE '%prod%'
-- Negated match
SELECT * FROM resources WHERE type NOT LIKE 'null_%'
Type Casting
SELECT count::text FROM summary
SELECT attributes::jsonb FROM instances
Syntax: expression::type
JSON Operators
Access JSON data stored in the attributes column.
Arrow Operators
| Operator | Description | Returns |
|---|---|---|
-> |
Get JSON value | JSON |
->> |
Get JSON as text | Text |
#>> |
Get nested path as text | Text |
Examples
-- Get JSON value
SELECT attributes->'tags' FROM instances
-- Get as text
SELECT attributes->>'instance_type' FROM instances
-- Nested path
SELECT attributes#>>'{tags,Name}' FROM instances
JSON Contains
-- Check if JSON contains value
SELECT * FROM instances
WHERE attributes->'tags' @> '{"Environment": "production"}'
Aggregation
COUNT
SELECT COUNT(*) FROM instances
SELECT COUNT(*) FROM resources
GROUP BY
SELECT type, COUNT(*) AS count
FROM resources
GROUP BY type
HAVING
SELECT type, COUNT(*) AS count
FROM resources
GROUP BY type
HAVING COUNT(*) > 10
Ordering
ORDER BY
SELECT * FROM resources ORDER BY type
SELECT * FROM resources ORDER BY type ASC
SELECT * FROM resources ORDER BY type DESC
Multiple Columns
SELECT * FROM resources ORDER BY type ASC, address DESC
Limiting Results
LIMIT
SELECT * FROM instances LIMIT 100
Common Table Expressions (CTEs)
WITH Clause
WITH aws_resources AS (
SELECT * FROM resources WHERE provider = 'registry.terraform.io/hashicorp/aws'
)
SELECT type, COUNT(*) FROM aws_resources GROUP BY type
Multiple CTEs
WITH
aws AS (SELECT * FROM resources WHERE provider = 'registry.terraform.io/hashicorp/aws'),
gcp AS (SELECT * FROM resources WHERE provider = 'registry.terraform.io/hashicorp/google')
SELECT 'aws' AS cloud, COUNT(*) FROM aws
UNION
SELECT 'gcp' AS cloud, COUNT(*) FROM gcp
Functions
Built-in Functions
Note: Function names are case-sensitive and must be lowercase.
| Function | Description | Example |
|---|---|---|
count(*) |
Count rows | count(*) |
sum(expr) |
Sum values | sum(count) |
avg(expr) |
Average values | avg(count) |
nullif(a, b) |
Return null if a = b | nullif(value, 0) |
to_char(expr, fmt) |
Format as string | to_char(created_at, 'YYYY-MM-DD') |
json_build_object(...) |
Build JSON object | json_build_object('key', value) |
Operator Precedence
From highest to lowest:
::(type cast)->,->>,#>>(JSON)-(unary negation)*,/+,-\|\|(concatenation)=,<>,<,>,<=,>=IS,IS NOT,IS DISTINCT FROM,IS NOT DISTINCT FROMIN,LIKE,ILIKENOTANDOR
Use parentheses to override precedence:
SELECT * FROM instances WHERE (a = 1 OR b = 2) AND c = 3
Reserved Keywords
AND, AS, ASC, BY, COUNT, DESC, DISTINCT, EXISTS, FALSE, FROM, GROUP,
HAVING, ILIKE, IN, INNER, IS, JOIN, LEFT, LIKE, LIMIT, MATERIALIZED, NOT,
NULL, ON, OR, ORDER, RIGHT, SELECT, TRUE, UNION, WHERE, WITH
Keywords are case-insensitive.
Comments
Stategraph SQL does not currently support comments in queries.
Query Examples
Basic Queries
-- All instances
SELECT * FROM instances
-- All resources
SELECT * FROM resources
-- Specific type (use resources table)
SELECT * FROM resources WHERE type = 'aws_instance'
-- Multiple conditions
SELECT * FROM resources
WHERE type = 'aws_instance'
AND module IS NOT NULL
Aggregations
-- Count by type
SELECT type, COUNT(*) AS count
FROM resources
GROUP BY type
ORDER BY count DESC
-- With minimum count
SELECT type, COUNT(*) AS count
FROM resources
GROUP BY type
HAVING COUNT(*) > 5
ORDER BY count DESC
JSON Queries
-- Get specific attribute
SELECT
address,
attributes->>'instance_type' AS instance_type
FROM instances
-- Filter by attribute
SELECT address
FROM instances
WHERE attributes->>'instance_type' = 't3.micro'
-- Check tags
SELECT address
FROM instances
WHERE attributes->'tags'->>'Environment' = 'production'
Complex Queries
-- Resources by type with counts
SELECT type, COUNT(*) AS count
FROM resources
WHERE provider = 'registry.terraform.io/hashicorp/aws'
GROUP BY type
ORDER BY count DESC
Error Handling
Syntax Errors
Invalid syntax returns an error with position information:
Error at line 1, column 15: Expected 'FROM'
Type Errors
Type mismatches return descriptive errors:
Error: Cannot compare string with integer
Unknown Columns
References to non-existent columns:
Error: Unknown column 'foo'