Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
:sectnums:
:sectnumlevels: 5

= 动态性能视图设计

== 目的

在 `ivorysql_ora` 中定义 `SYS.V$SESSION`、`SYS.V$PROCESS` 和 `SYS.V$PARAMETER`,将底层活动统计及配置参数映射为 Oracle 风格的视图列。实现复用现有数据源和统计函数,不增加采集进程或新的统计存储。

列定义及查询示例见 xref:master/oracle_compatibility/compat_dynamic_performance_view.adoc[动态性能视图]。

== 实现说明

=== 代码组织

[cols="3,4",options="header"]
|===
|文件 |职责
|`contrib/ivorysql_ora/src/sysview/sysview--1.0.sql` |三个动态性能视图的 SQL 定义
|`contrib/ivorysql_ora/ivorysql_ora_merge_sqls` |将系统视图脚本纳入扩展 SQL 合并流程
|`contrib/ivorysql_ora/sql/ora_sysview.sql` |视图结构和参数查询的回归用例
|`contrib/ivorysql_ora/expected/ora_sysview.out` |列类型及参数查询的期望结果
|===

[source,text]
----
SYS.V$SESSION ──→ pg_stat_activity ──→ 后端活动统计
└─→ pg_blocking_pids(pid) ──→ 阻塞 PID 列表首项
SYS.V$PROCESS ──→ pg_stat_activity ──→ 后端活动统计
SYS.V$PARAMETER → pg_settings ──→ 当前参数设置
----

三个对象均为普通 SQL 视图,通过投影、类型转换和 `CASE` 表达式提供兼容列。数据刷新及可见性沿用底层机制。

=== 会话信息映射

`V$SESSION` 从 `pg_stat_activity` 读取信息,将 `pid`、`usename` 和 `application_name` 分别映射为 `SID`、`USERNAME` 和 `PROGRAM`。主要转换规则如下:

* `state` 为 `active` 或 `fastpath function call` 时,`STATUS` 为 `ACTIVE`,其余分支为 `INACTIVE`,包括底层状态为空的情况。
* `backend_type` 为 `client backend` 时,`TYPE` 为 `USER`,其余分支为 `BACKGROUND`。
* `MACHINE` 使用 `COALESCE(client_hostname, host(client_addr), client_addr::text)`,优先显示主机名,否则使用地址。
* `backend_start`、`query_start` 和 `xact_start` 分别映射为登录、SQL 开始和事务开始时间,保留底层 `timestamptz` 类型。
* `query_id` 转为 `VARCHAR2(19)`,`query` 转为 `VARCHAR2(4000)`。查询标识生成及 SQL 文本采集仍由底层设置控制。
* `wait_event`、`wait_event_type` 和 `client_port` 分别映射为 `EVENT`、`WAIT_CLASS` 和 `PORT`。

阻塞信息通过以下表达式取得:

[source,sql]
----
(pg_catalog.pg_blocking_pids(pid))[1]::NUMBER AS blocking_session
----

它只保留数组首项,空数组取值为空。该函数与活动统计分别取得数据,不能将结果解释为始终一致的完整阻塞图。

`SID` 采用可复用的后端 PID,使用时需结合启动时间。`SQL_ID` 是底层查询标识的字符串表示,不能解释为 Oracle 的 SQL_ID 编码。

=== 进程信息映射

`V$PROCESS` 同样以 `pg_stat_activity` 为数据源,使用 `pid` 作为 `SPID`,使用 `usename` 作为数据库用户名,使用 `application_name` 作为 `PROGRAM`,并直接输出 `backend_type` 为 `PNAME`。

`BACKGROUND` 根据 `backend_type` 生成:客户端后端返回 `NULL`,其他分支返回字符串 `'1'`。由于 `SID` 和 `SPID` 均来自 `pid`,两个视图可以直接关联。该视图展示统计系统已报告的后端,而不是另行扫描操作系统进程。

=== 参数信息映射

`V$PARAMETER` 读取 `pg_settings`,将 `setting`、`boot_val` 和 `short_desc` 映射为当前值、默认值和描述。所有值均按视图声明转换为 `VARCHAR2` 或 `NUMBER`。

`TYPE` 依据 `vartype` 映射:`bool` 为 `1`,`integer` 为 `3`,包括实数、枚举和字符串在内的其他类型为 `2`。

`ISDEFAULT` 使用 `setting IS NOT DISTINCT FROM boot_val` 比较,因此相同字符串及两边均为空时返回 `TRUE`。它比较当前显示值与编译默认值,不依据参数来源判断。

修改属性按 `context` 映射:

[cols="2,2,2",options="header"]
|===
|底层 context |ISSES_MODIFIABLE |ISSYS_MODIFIABLE
|`user`、`superuser` |`TRUE` |`IMMEDIATE`
|`sighup` |`FALSE` |`IMMEDIATE`
|`postmaster`、`internal` |`FALSE` |`FALSE`
|其他上下文 |`FALSE` |`DEFERRED`
|===

这些映射是显示属性,不执行参数修改,也不替代参数权限、重载或重启检查。

=== 权限与统计可见性

三个视图的定义没有向 `PUBLIC` 授予 `SELECT`,由管理员控制对象访问权限。

视图授权与底层信息可见性分开处理。活动统计函数根据调用者角色决定会话详情的可见性;权限不足时,SQL 文本可显示 `<insufficient privilege>`,状态、时间及等待事件等底层字段可为空。视图中的 `CASE` 仍会对空值使用默认分支,因此可能得到 `INACTIVE`、`BACKGROUND` 或 `'1'`,不能据此推断被隐藏会话的真实状态。

参数视图沿用 `pg_settings` 的可见性限制。授予视图查询权限不会自动授予 `pg_read_all_stats`、`pg_read_all_settings`,也不会获得修改配置的权限。

== 测试验证

`ora_sysview` 回归用例通过 psql 的 `\d SYS.V$SESSION` 和 `\d SYS.V$PROCESS` 检查列名与类型,并查询 `V$PARAMETER` 中的 `listen_addresses`、`application_name`、`archive_command`、`archive_mode` 和 `block_size`,按参数名排序核对字段映射。

在已配置、编译并安装的 IvorySQL 源码目录中,可运行 `make -C contrib/ivorysql_ora oracle-installcheck ORA_REGRESS=ora_sysview`。目标实例需具备对应版本的 `ivorysql_ora`。

会话 PID、SQL 文本、等待事件和参数值随环境变化。检查运行结果时应验证数据来源和映射规则;跨用户可见性与实际阻塞关系需要结合角色和并发会话验证。
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
:sectnums:
:sectnumlevels: 5

= 动态性能视图

== 目的

IvorySQL 提供 `SYS.V$SESSION`、`SYS.V$PROCESS` 和 `SYS.V$PARAMETER`,用于查询后端会话、进程及配置参数信息。三个视图分别映射到底层活动统计和参数设置,便于使用 Oracle 风格的视图名称进行日常监控。

== 功能说明

[cols="2,4",options="header"]
|===
|视图 |用途
|`SYS.V$SESSION` |查询会话状态、SQL、等待事件及阻塞会话
|`SYS.V$PROCESS` |查询后端进程编号、应用名称和进程类型
|`SYS.V$PARAMETER` |查询当前参数值、默认值及修改属性
|===

=== V$SESSION

[cols="2,2,4",options="header"]
|===
|列名 |类型 |说明
|`SID` |`NUMBER` |后端进程 PID
|`USERNAME` |`VARCHAR2(128)` |数据库用户名
|`STATUS` |`VARCHAR2(40)` |底层状态为 `active` 或 `fastpath function call` 时为 `ACTIVE`,其他情况为 `INACTIVE`
|`MACHINE` |`VARCHAR2(128)` |客户端主机名,未提供时取客户端 IP 地址
|`PROGRAM` |`VARCHAR2(256)` |连接的 `application_name`
|`TYPE` |`VARCHAR2(30)` |`client backend` 映射为 `USER`,其他情况为 `BACKGROUND`
|`LOGON_TIME` |`timestamptz` |后端启动时间
|`SQL_EXEC_START` |`timestamptz` |当前 SQL 的开始时间;会话空闲时为最近一次 SQL 的开始时间
|`SQL_ID` |`VARCHAR2(19)` |底层 `query_id` 转换后的字符串
|`SQL_TEXT` |`VARCHAR2(4000)` |当前或最近一次 SQL 文本
|`BLOCKING_SESSION` |`NUMBER` |阻塞该后端的 PID 列表中的第一个值;无阻塞时为空
|`EVENT` |`VARCHAR2(64)` |底层等待事件名称
|`WAIT_CLASS` |`VARCHAR2(64)` |底层等待事件类型
|`TRANSACTION_START` |`timestamptz` |当前事务开始时间;没有活动事务时可为空
|`PORT` |`NUMBER` |客户端端口;Unix 域套接字连接为 `-1`
|===

`SID` 是操作系统进程编号,会在进程退出后被复用,识别会话时应结合 `LOGON_TIME`。`SQL_ID` 使用底层查询标识,不是 Oracle SQL_ID 编码;`compute_query_id` 关闭或未生成查询标识时,该列为空。

`EVENT` 和 `WAIT_CLASS` 使用底层事件分类。`STATUS = 'ACTIVE'` 的会话也可能正在等待资源。SQL 文本还受 `track_activities` 和 `track_activity_query_size` 的采集设置约束;空闲会话中的 SQL 文本不表示该语句仍在执行。

=== V$PROCESS

[cols="2,2,4",options="header"]
|===
|列名 |类型 |说明
|`SPID` |`NUMBER` |后端进程 PID,可与 `V$SESSION.SID` 关联
|`USERNAME` |`VARCHAR2(128)` |数据库用户名
|`PROGRAM` |`VARCHAR2(256)` |连接的 `application_name`
|`PNAME` |`VARCHAR2(30)` |底层 `backend_type`
|`BACKGROUND` |`VARCHAR2(1)` |客户端后端为空,其他后端为 `'1'`
|===

这里的 `USERNAME` 表示数据库用户,`PROGRAM` 表示应用设置的连接名称,应按此含义解读进程信息。

=== V$PARAMETER

[cols="2,2,4",options="header"]
|===
|列名 |类型 |说明
|`NAME` |`VARCHAR2(80)` |IvorySQL 参数名称
|`TYPE` |`NUMBER` |布尔参数为 `1`,整数参数为 `3`,其他参数为 `2`
|`VALUE` |`VARCHAR2(4000)` |当前连接可见的参数设置值
|`DEFAULT_VALUE` |`VARCHAR2(255)` |参数的编译默认值 `boot_val`
|`ISDEFAULT` |`VARCHAR2(5)` |当前值与编译默认值相同时为 `TRUE`,否则为 `FALSE`
|`ISSES_MODIFIABLE` |`VARCHAR2(5)` |底层参数上下文为 `user` 或 `superuser` 时为 `TRUE`,否则为 `FALSE`
|`ISSYS_MODIFIABLE` |`VARCHAR2(9)` |依据参数上下文返回 `IMMEDIATE`、`DEFERRED` 或 `FALSE`
|`DESCRIPTION` |`VARCHAR2(255)` |参数的简短说明
|===

参数名称和取值来自 IvorySQL 配置。修改属性列是对参数上下文的映射,不代表当前用户已获得修改权限;实际修改方式仍遵循相应参数的设置、重载或重启规则。

== 查询示例

以下示例在 Oracle 兼容模式下执行,使用 `SYS` 限定视图名称。会话、进程和参数结果随运行环境变化。

=== 查看当前会话和活动会话

[source,sql]
----
SELECT sid, username, status, program, logon_time
FROM sys.v$session
WHERE sid = pg_backend_pid();

SELECT sid, username, sql_id, sql_exec_start, sql_text
FROM sys.v$session
WHERE type = 'USER' AND status = 'ACTIVE'
ORDER BY sql_exec_start;
----

=== 查看等待及关联进程

[source,sql]
----
SELECT sid, blocking_session, event, wait_class
FROM sys.v$session
WHERE blocking_session IS NOT NULL;

SELECT s.sid, s.username, p.spid, p.pname, p.program
FROM sys.v$session s
JOIN sys.v$process p ON p.spid = s.sid
WHERE s.sid = pg_backend_pid();
----

`BLOCKING_SESSION` 只返回第一个阻塞 PID,不能据此判断全部阻塞关系。

=== 查看参数

[source,sql]
----
SELECT name, type, value, default_value, isdefault,
isses_modifiable, issys_modifiable
FROM sys.v$parameter
WHERE name IN ('application_name', 'archive_mode', 'block_size')
ORDER BY name;
----

== 查询权限与注意事项

视图定义不向 `PUBLIC` 授予 `SELECT`。管理员可为已有的监控角色按需授权,例如:

[source,sql]
----
GRANT USAGE ON SCHEMA sys TO monitor_user;
GRANT SELECT ON sys.v$session, sys.v$process, sys.v$parameter
TO monitor_user;
----

获得视图查询权限后,底层统计信息仍遵循调用者的可见性规则。查看其他角色的完整会话详情需要相应统计权限,例如 `pg_read_all_stats`;部分参数的可见性受 `pg_read_all_settings` 等权限约束。这些角色应由管理员按需授予,视图授权不会自动附带它们。

权限不足时,其他会话的部分列为空,`SQL_TEXT` 可能显示 `<insufficient privilege>`。`STATUS`、`TYPE` 和 `BACKGROUND` 仍按映射表达式生成,不应在缺少底层信息时仅凭这些值判断实际活动或进程类别。

在长事务中重复查询活动统计,可能读到同一份统计快照。需要重新采样时,可结束事务后查询,或先执行 `SELECT pg_stat_clear_snapshot();` 清除当前统计快照。不同查询之间的会话和阻塞关系可能发生变化。
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
:sectnums:
:sectnumlevels: 5

= Dynamic Performance Views Design

== Purpose

Define `SYS.V$SESSION`, `SYS.V$PROCESS`, and `SYS.V$PARAMETER` in `ivorysql_ora`, mapping underlying activity statistics and configuration parameters to Oracle-style view columns. The implementation reuses existing data sources and statistics functions without adding collector processes or statistics storage.

See xref:master/oracle_compatibility/compat_dynamic_performance_view.adoc[Dynamic Performance Views] for column definitions and query examples.

== Implementation

=== Code organization

[cols="3,4",options="header"]
|===
|File |Responsibility
|`contrib/ivorysql_ora/src/sysview/sysview--1.0.sql` |SQL definitions of the three dynamic performance views
|`contrib/ivorysql_ora/ivorysql_ora_merge_sqls` |Includes the system view script in extension SQL assembly
|`contrib/ivorysql_ora/sql/ora_sysview.sql` |Regression cases for view structure and parameter queries
|`contrib/ivorysql_ora/expected/ora_sysview.out` |Expected column types and parameter query results
|===

[source,text]
----
SYS.V$SESSION ──→ pg_stat_activity ──→ Backend activity statistics
└─→ pg_blocking_pids(pid) ──→ First blocking PID
SYS.V$PROCESS ──→ pg_stat_activity ──→ Backend activity statistics
SYS.V$PARAMETER → pg_settings ──→ Current parameter settings
----

All three objects are ordinary SQL views using projections, casts, and `CASE` expressions to provide compatible columns. Refresh behavior and visibility follow the underlying mechanisms.

=== Session mappings

`V$SESSION` reads `pg_stat_activity`, mapping `pid`, `usename`, and `application_name` to `SID`, `USERNAME`, and `PROGRAM`. The main conversion rules are:

* `STATUS` is `ACTIVE` for an underlying `state` of `active` or `fastpath function call`; all other branches return `INACTIVE`, including a NULL state.
* `TYPE` is `USER` when `backend_type` is `client backend`; all other branches return `BACKGROUND`.
* `MACHINE` uses `COALESCE(client_hostname, host(client_addr), client_addr::text)`, preferring a hostname and falling back to an address.
* `backend_start`, `query_start`, and `xact_start` map to login, SQL start, and transaction start times, retaining the underlying `timestamptz` type.
* `query_id` is cast to `VARCHAR2(19)` and `query` to `VARCHAR2(4000)`. Query identifier generation and SQL text collection remain controlled by underlying settings.
* `wait_event`, `wait_event_type`, and `client_port` map to `EVENT`, `WAIT_CLASS`, and `PORT`.

Blocking information comes from this expression:

[source,sql]
----
(pg_catalog.pg_blocking_pids(pid))[1]::NUMBER AS blocking_session
----

It retains only the first array element, returning NULL for an empty array. The function and activity statistics retrieve data separately, so the result is not a complete blocking graph guaranteed to be consistent at every moment.

`SID` is a reusable backend PID and should be considered together with the start time. `SQL_ID` is the underlying query identifier cast to a string, rather than Oracle's SQL_ID encoding.

=== Process mappings

`V$PROCESS` also reads `pg_stat_activity`, using `pid` as `SPID`, `usename` as the database username, and `application_name` as `PROGRAM`. It exposes `backend_type` as `PNAME`.

`BACKGROUND` is NULL for a client backend and the string `'1'` in every other branch. Because both `SID` and `SPID` come from `pid`, the views can be joined directly. The view exposes backends reported by the statistics system rather than scanning operating system processes separately.

=== Parameter mappings

`V$PARAMETER` reads `pg_settings`, mapping `setting`, `boot_val`, and `short_desc` to the current value, default value, and description. Values are cast to the declared `VARCHAR2` or `NUMBER` types.

`TYPE` maps `vartype` as follows: `bool` becomes `1`, `integer` becomes `3`, and all other types, including real, enum, and string, become `2`.

`ISDEFAULT` compares values with `setting IS NOT DISTINCT FROM boot_val`. Equal strings, or two NULLs, return `TRUE`. It compares the current displayed value with the compiled-in default rather than examining the setting's source.

Modification attributes map `context` as follows:

[cols="2,2,2",options="header"]
|===
|Underlying context |ISSES_MODIFIABLE |ISSYS_MODIFIABLE
|`user`, `superuser` |`TRUE` |`IMMEDIATE`
|`sighup` |`FALSE` |`IMMEDIATE`
|`postmaster`, `internal` |`FALSE` |`FALSE`
|Other contexts |`FALSE` |`DEFERRED`
|===

These mappings provide display attributes. They do not modify parameters or replace privilege, reload, or restart checks.

=== Privileges and statistics visibility

None of the three view definitions grants `SELECT` to `PUBLIC`. Administrators control access to the objects.

View access and underlying information visibility are separate. Activity statistics functions check the caller's role before exposing session details. With insufficient privileges, SQL text can show `<insufficient privilege>` and underlying state, time, and wait fields can be NULL. The view's `CASE` expressions still use their default branches for NULL values, potentially producing `INACTIVE`, `BACKGROUND`, or `'1'`. These values do not identify the actual state of a hidden session.

Parameter visibility follows `pg_settings` restrictions. Granting view access does not automatically grant `pg_read_all_stats`, `pg_read_all_settings`, or permission to change configuration.

== Verification

The `ora_sysview` regression cases use psql's `\d SYS.V$SESSION` and `\d SYS.V$PROCESS` to check column names and types. They also query `listen_addresses`, `application_name`, `archive_command`, `archive_mode`, and `block_size` in `V$PARAMETER`, ordered by parameter name, to verify field mappings.

From a configured, built, and installed IvorySQL source tree, run `make -C contrib/ivorysql_ora oracle-installcheck ORA_REGRESS=ora_sysview`. The target instance needs the matching version of `ivorysql_ora`.

Session PIDs, SQL text, wait events, and parameter values vary by environment. Validate their data sources and mapping rules when checking runtime results. Cross-user visibility and actual blocking relationships require verification with the relevant roles and concurrent sessions.
Loading
Loading