Skip to main content

Oracle

Pull

Synopsis

Director connects outbound to an Oracle service on a schedule and runs operator-defined read-only queries, emitting one record per returned row. Each query runs on its own cron schedule with its own state, and supports incremental (high-watermark) collection as well as contiguous time-window collection. Cursor and window positions are checkpointed persistently, so collection resumes where it left off after a restart.

Schema

- id: <numeric>
name: <string>
description: <string>
type: oracle
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
host: <string>
port: <numeric>
username: <string>
password: <string>
database: <string>
connect_timeout: <numeric>
encrypt: <boolean>
insecure_skip_verify: <boolean>
ca_name: <string>
connection_string: <string>
definitions:
- name: oracle_custom_query_collector
status: <boolean>
inputs: <input[]>

Configuration

The following fields are used to define the device:

Device

FieldRequiredDefaultDescription
idY-Unique numeric identifier
nameY-Device name
descriptionN-Optional description
typeY-Must be oracle
tagsN-Optional tags
pipelinesN-Optional preprocessing pipelines
statusNtrueEnable/disable the device

Connection

FieldRequiredDefaultDescription
hostY-Hostname or IP address of the Oracle server.
portN1521Listener port. See Service name and listener port.
usernameY-Login used to authenticate.
passwordN-Password for username.
databaseN-Oracle service name to connect to.
connect_timeoutN30Connection dial and ping timeout, in seconds.
connection_stringN-Raw DSN passthrough. When set it is authoritative and the structured connection properties are ignored. See Connection string.

TLS

FieldRequiredDefaultDescription
encryptNtrueEncrypt the connection.
insecure_skip_verifyNfalseAccept the server certificate without verifying it.
ca_nameN-Custom CA used to verify the server certificate. Ignored when insecure_skip_verify is true.
note

TLS material fields (cert_name, key_name, ca_name, client_ca_name) accept any of the following:

  • File name — resolved relative to the service root directory. Nested paths such as certs/prod/server.pem are supported.
  • Absolute path — honored only if it resolves inside the service root. Any path that escapes the root is refused.
  • Inline PEM content — used verbatim when the value contains -----BEGIN.
  • Environment variable${ENV_VAR}.
  • Vault reference$secret{id=...} or $secret{store=...,ref=...}.

Custom Query Collector

Queries for this device are declared under the collector definition named oracle_custom_query_collector.

Queries are defined under the device's definitions: block. The definition name must match the device's collector constant, and each entry under inputs: is one query running on its own schedule with its own state.

Input properties

Inputs share the standard dataset input frame (id, name, status) plus the following properties:

FieldRequiredTypeDefaultDescription
queryYstringThe SQL statement to run. May contain the binding tokens described below.
cronNstring5-field cron expression. Empty or "0" runs the query on every stats interval tick.
timeoutNint120Query timeout in seconds. A zero or negative value falls back to the default.
validate_queryNbooleantrueEnforce that the query is a single read-only statement. See Query validation.
max_rowsNint0Per-run row cap for bounded, chunked backfills; the remainder resumes on the next run. 0 means no cap. Ignored when the query uses window tokens.
max_retriesNint1How many times a failed run retries on the next collection cycle before waiting for its next cron slot.
skippableNbooleantrueWhen true, a backlog of missed cron slots collapses into a single catch-up run. false replays every missed slot, one per cycle, until the schedule catches up.
pipeline_nameNstringRoute the collected records to a specific preprocessing pipeline by name.
tracking_columnNstringColumn whose maximum value becomes the next cursor. See Incremental collection.
tracking_column_typeNstringnumericnumeric, timestamp, or string.
tracking_initial_valueNstringFirst-run starting point. Defaults to 0, the Unix epoch, or the empty string per type.
tracking_rescan_marginNint0Re-scan this far behind the watermark on each run to catch late-committing rows. Numeric units, or seconds for timestamps.
window_formatNstringdatetimeHow window bounds are bound: datetime, unix, unix_ms, or rfc3339. See Time-window collection.
initial_lookbackNint0First-run backfill in seconds for window queries. 0 collects forward only.

Binding tokens

Three tokens may appear in a query. Each is rewritten to the database driver's own placeholder syntax and its value is passed as a bound parameter, so a token value can never alter the shape of the statement:

TokenBound value
{{cursor}}The last-seen value of the tracking column.
{{earliest}}Lower bound of this run's time window.
{{latest}}Upper bound of this run's time window.

The {{...}} spelling is the same on every database. Do not substitute a driver-native placeholder such as :cursor or ? — those are not recognized as tokens and reach the driver unbound.

Incremental collection

Setting tracking_column turns an input into a high-watermark collector: each run collects only rows past the largest value seen so far. The watermark is persisted after every successful run and survives restarts and device-ownership moves within a cluster.

query: "SELECT id, event_time, message FROM audit_log WHERE id > {{cursor}} ORDER BY id ASC"
tracking_column: id
tracking_column_type: numeric

Always ORDER BY the tracking column so that runs capped by max_rows, or interrupted partway, stay contiguous.

caution

Configuring tracking_column without placing {{cursor}} in the query is accepted but collects nothing incrementally: the watermark advances while the full result set is re-read on every run. A warning is logged when this combination is detected.

High-watermark tracking assumes the column is monotonic in commit order. A row that commits late with an already-passed value falls behind the watermark and is skipped. tracking_rescan_margin re-collects a safety margin behind the watermark on each run to catch those rows, at the cost of re-emitting the margin — deduplicate downstream when enabling it.

Time-window collection

A query referencing {{earliest}} or {{latest}} collects a contiguous time slice per run. Consecutive windows tile exactly, with no gap and no overlap: this run's upper bound becomes the next run's lower bound, persisted the same way as a cursor.

query: "SELECT * FROM login_events WHERE event_time >= {{earliest}} AND event_time < {{latest}}"
window_format: datetime
initial_lookback: 3600

Use >= on the lower bound and < on the upper bound so that a row landing exactly on a boundary is collected by exactly one window.

window_format must match how the column stores time. The default datetime binds a native timestamp; unix and unix_ms bind integer epoch values; rfc3339 binds a string.

max_rows is ignored for window queries, because a capped window would silently drop its remainder.

Query validation

With validate_query left at its default, a query must be a single statement beginning with SELECT, WITH, SHOW, EXPLAIN, DESCRIBE, or DESC. Multiple statements are rejected, as is a WITH or EXPLAIN statement containing INSERT, UPDATE, DELETE, or MERGE — and EXPLAIN ANALYZE, which executes its target rather than describing it. String literals, quoted identifiers, and comments are excluded from the check, so a semicolon or keyword inside them does not trigger a rejection. One trailing semicolon is stripped.

Set validate_query: false only for stored procedures or vendor read syntax the validator cannot prove read-only.

warning

Query validation guards against configuration accidents; it is not a security boundary. Pair it with a database login that has read-only permissions on the objects being queried.

Collected records

Each row becomes one JSON record whose fields are the query's column names, carrying native JSON types rather than stringified values:

Column typeRendered as
Numeric, booleanJSON number or boolean
Exact numeric (DECIMAL, NUMERIC, MONEY)Unquoted JSON number with full precision preserved
Date and timeISO-8601 string in UTC, millisecond precision
JSON, JSONBNested JSON object
BinaryBase64 string
NULLnull

A top-level @timestamp field is added to every record, which is what lets it flow through pipelines and into targets as a first-class document. If the query itself returns a column named @timestamp, that column is kept as-is and nothing is added.

Details

Service name and listener port

The database property holds the Oracle service name, not a schema or catalog name. It is named database for parity with the other database devices.

The default port 1521 addresses the conventional non-TLS TNS listener. TLS/TCPS listeners conventionally run on a different port — typically 2484 — which is not derived from encrypt and must be set explicitly on port.

Identifier casing

Record field names are the column names the driver reports. Oracle folds unquoted identifiers to uppercase, so a query written as SELECT sid, username FROM v$session produces the fields SID and USERNAME. Reference them in that form from downstream pipelines, or quote the identifiers in the query to fix the casing at the source.

tracking_column is matched against the returned columns case-insensitively, so tracking_column: id resolves against a column reported as ID.

Numeric precision

Oracle NUMBER columns are collected as full-precision numbers rather than being rounded to a floating-point approximation, so identifiers and monetary values survive collection intact.

Connection string

Setting connection_string switches the device to raw DSN passthrough. The string is handed to the driver's own parser, and every structured property — host, port, username, password, database, and the TLS properties — is ignored. Transport security is then expressed inside the DSN:

connection_string: "oracle://user:pass@oracle01:1521/ORCLPDB1?SSL=true"

Checkpoints

Each input's cursor and window position is stored in Director's persistent state, keyed per input. After a restart the input resumes from its stored position rather than re-reading from the beginning, and the same state follows the device when ownership moves to another node in a cluster.

Examples

Basic Collection

Connecting to an Oracle service and collecting user sessions every five minutes...

- id: 930000003
name: oracle-prod
type: oracle
status: true
properties:
host: oracle01.example.com
username: vmetric_reader
password: "<password>"
database: ORCLPDB1
definitions:
- name: oracle_custom_query_collector
status: true
inputs:
- id: 3001
name: Active Sessions
status: true
properties:
query: "SELECT sid, serial#, username, status FROM v$session WHERE type = 'USER'"
cron: "*/5 * * * *"

Each returned row becomes one record, with the query's column names as fields and an added @timestamp...

{
"@timestamp": "2026-07-21T10:35:00.000Z",
"SID": 412,
"SERIAL#": 38177,
"USERNAME": "APP_RW",
"STATUS": "ACTIVE"
}

Incremental Collection

Collecting only audit rows added since the last run, using a sequence-backed column as the high-watermark...

- id: 930000003
name: oracle-audit
type: oracle
status: true
properties:
host: oracle01.example.com
username: vmetric_reader
password: "<password>"
database: ORCLPDB1
definitions:
- name: oracle_custom_query_collector
status: true
inputs:
- id: 3002
name: New Audit Rows
status: true
properties:
query: "SELECT id, event_time, os_username, action_name FROM audit_log WHERE id > {{cursor}} ORDER BY id ASC"
tracking_column: id
tracking_column_type: numeric
max_rows: 50000
cron: "*/1 * * * *"

Time-Window Collection

Collecting login events in contiguous time slices over a TCPS listener, backfilling the first hour on the initial run...

- id: 930000003
name: oracle-logins
type: oracle
status: true
properties:
host: oracle01.example.com
port: 2484
username: vmetric_reader
password: "<password>"
database: ORCLPDB1
encrypt: true
ca_name: certs/oracle-ca.pem
definitions:
- name: oracle_custom_query_collector
status: true
inputs:
- id: 3003
name: Login Events Window
status: true
properties:
query: "SELECT * FROM login_events WHERE event_time >= {{earliest}} AND event_time < {{latest}}"
window_format: datetime
initial_lookback: 3600
cron: "*/5 * * * *"