找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5849

积分

0

好友

750

主题
发表于 3 天前 | 查看: 4| 回复: 0

本文首发时间 2026-08-21。

背景

这个问题最早是 2023 年 11 月在客户现场的 openGauss 发行版上发现的。当时有一个数据库连接驱动,会去执行一条查询 information_schema.parameters 视图的 SQL:

select parameter_mode, parameter_name, pg_type.oid
from information_schema.parameters
join pg_catalog.pg_type on pg_type.typname = udt_name
where upper(specific_name) = upper('%')
and upper(specific_schema) = upper('%')
order by ordinal_position;

当参数值为 null 时,这条 SQL 会报错:

ERROR:  failed to find plan for subquery ss

分析与临时解决

当时这套应用已经上线生产环境了,生产环境没报错,测试环境却报错。客户对比了两个环境的数据库参数,发现有一个参数不一样: track_stmt_stat_level 在生产环境是 OFF,L0 ,测试环境是 OFF,L1

当时我和内核研发一起定位,分析出了原因: information_schema.parameters 这个视图里有一个子查询,由于「任何值 = null 恒为 false」,子查询实际上不再需要生成执行计划,本可以被直接裁剪掉。但子查询里一个嵌套列被外部引用为目标列,当反解析目标列名称时,发现找不到这个子查询的计划,于是报错。

原本的视图长这样:

CREATE OR REPLACE VIEW information_schema.parameters AS
SELECT current_database()::information_schema.sql_identifier AS specific_catalog,
       ss.n_nspname::information_schema.sql_identifier AS specific_schema,
       ((ss.proname::text || '_'::text) || ss.p_oid::text)::information_schema.sql_identifier AS specific_name,
       (ss.x).n::information_schema.cardinal_number AS ordinal_position,
       CASE
           WHEN ss.proargmodes IS NULL THEN 'IN'::text
           WHEN ss.proargmodes[(ss.x).n] = 'i'::"char" THEN 'IN'::text
           WHEN ss.proargmodes[(ss.x).n] = 'o'::"char" THEN 'OUT'::text
           WHEN ss.proargmodes[(ss.x).n] = 'b'::"char" THEN 'INOUT'::text
           WHEN ss.proargmodes[(ss.x).n] = 'v'::"char" THEN 'IN'::text
           WHEN ss.proargmodes[(ss.x).n] = 't'::"char" THEN 'OUT'::text
           ELSE NULL::text
       END::information_schema.character_data AS parameter_mode,
       'NO'::character varying::information_schema.yes_or_no AS is_result,
       'NO'::character varying::information_schema.yes_or_no AS as_locator,
       NULLIF(ss.proargnames[(ss.x).n], NULL::text)::information_schema.sql_identifier AS parameter_name,
       CASE
           WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text
           WHEN nt.nspname = 'pg_catalog'::name THEN format_type(t.oid, NULL::integer)
           ELSE 'USER-DEFINED'::text
       END::information_schema.character_data AS data_type,
       NULL::integer::information_schema.cardinal_number AS character_maximum_length,
       NULL::integer::information_schema.cardinal_number AS character_octet_length,
       NULL::character varying::information_schema.sql_identifier AS character_set_catalog,
       NULL::character varying::information_schema.sql_identifier AS character_set_schema,
       NULL::character varying::information_schema.sql_identifier AS character_set_name,
       NULL::character varying::information_schema.sql_identifier AS collation_catalog,
       NULL::character varying::information_schema.sql_identifier AS collation_schema,
       NULL::character varying::information_schema.sql_identifier AS collation_name,
       NULL::integer::information_schema.cardinal_number AS numeric_precision,
       NULL::integer::information_schema.cardinal_number AS numeric_precision_radix,
       NULL::integer::information_schema.cardinal_number AS numeric_scale,
       NULL::integer::information_schema.cardinal_number AS datetime_precision,
       NULL::character varying::information_schema.character_data AS interval_type,
       NULL::integer::information_schema.cardinal_number AS interval_precision,
       current_database()::information_schema.sql_identifier AS udt_catalog,
       nt.nspname::information_schema.sql_identifier AS udt_schema,
       t.typname::information_schema.sql_identifier AS udt_name,
       NULL::character varying::information_schema.sql_identifier AS scope_catalog,
       NULL::character varying::information_schema.sql_identifier AS scope_schema,
       NULL::character varying::information_schema.sql_identifier AS scope_name,
       NULL::integer::information_schema.cardinal_number AS maximum_cardinality,
       (ss.x).n::information_schema.sql_identifier AS dtd_identifier
FROM pg_type t,
     pg_namespace nt,
     (SELECT n.nspname AS n_nspname,
             p.proname,
             p.oid AS p_oid,
             p.proargnames,
             p.proargmodes,
             information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x
      FROM pg_namespace n,
           pg_proc p
      WHERE n.oid = p.pronamespace
        AND (pg_has_role(p.proowner, 'USAGE'::text)
             OR has_function_privilege(p.oid, 'EXECUTE'::text))) ss
WHERE t.oid = (ss.x).x
  AND t.typnamespace = nt.oid;

关键就在 (ss.x).n 的引用和 ss 这个子查询。我当时的紧急方案是修改这个视图,解掉一层嵌套,把 (ss.x).n 转换成 ss.n ,修改后的视图如下:

CREATE OR REPLACE VIEW information_schema.parameters AS
SELECT current_database()::information_schema.sql_identifier AS specific_catalog,
       ss.n_nspname::information_schema.sql_identifier AS specific_schema,
       ((ss.proname::text || '_'::text) || ss.p_oid::text)::information_schema.sql_identifier AS specific_name,
       ss.n::information_schema.cardinal_number AS ordinal_position,
       CASE
           WHEN ss.proargmodes IS NULL THEN 'IN'::text
           WHEN ss.proargmodes[ss.n] = 'i'::"char" THEN 'IN'::text
           WHEN ss.proargmodes[ss.n] = 'o'::"char" THEN 'OUT'::text
           WHEN ss.proargmodes[ss.n] = 'b'::"char" THEN 'INOUT'::text
           WHEN ss.proargmodes[ss.n] = 'v'::"char" THEN 'IN'::text
           WHEN ss.proargmodes[ss.n] = 't'::"char" THEN 'OUT'::text
           ELSE NULL::text
       END::information_schema.character_data AS parameter_mode,
       'NO'::character varying::information_schema.yes_or_no AS is_result,
       'NO'::character varying::information_schema.yes_or_no AS as_locator,
       NULLIF(ss.proargnames[ss.n], NULL::text)::information_schema.sql_identifier AS parameter_name,
       CASE
           WHEN t.typelem <> 0::oid AND t.typlen = (-1) THEN 'ARRAY'::text
           WHEN nt.nspname = 'pg_catalog'::name THEN format_type(t.oid, NULL::integer)
           ELSE 'USER-DEFINED'::text
       END::information_schema.character_data AS data_type,
       NULL::integer::information_schema.cardinal_number AS character_maximum_length,
       NULL::integer::information_schema.cardinal_number AS character_octet_length,
       NULL::character varying::information_schema.sql_identifier AS character_set_catalog,
       NULL::character varying::information_schema.sql_identifier AS character_set_schema,
       NULL::character varying::information_schema.sql_identifier AS character_set_name,
       NULL::character varying::information_schema.sql_identifier AS collation_catalog,
       NULL::character varying::information_schema.sql_identifier AS collation_schema,
       NULL::character varying::information_schema.sql_identifier AS collation_name,
       NULL::integer::information_schema.cardinal_number AS numeric_precision,
       NULL::integer::information_schema.cardinal_number AS numeric_precision_radix,
       NULL::integer::information_schema.cardinal_number AS numeric_scale,
       NULL::integer::information_schema.cardinal_number AS datetime_precision,
       NULL::character varying::information_schema.character_data AS interval_type,
       NULL::integer::information_schema.cardinal_number AS interval_precision,
       current_database()::information_schema.sql_identifier AS udt_catalog,
       nt.nspname::information_schema.sql_identifier AS udt_schema,
       t.typname::information_schema.sql_identifier AS udt_name,
       NULL::character varying::information_schema.sql_identifier AS scope_catalog,
       NULL::character varying::information_schema.sql_identifier AS scope_schema,
       NULL::character varying::information_schema.sql_identifier AS scope_name,
       NULL::integer::information_schema.cardinal_number AS maximum_cardinality,
       ss.n::information_schema.sql_identifier AS dtd_identifier
FROM pg_type t,
     pg_namespace nt,
     (SELECT n.nspname AS n_nspname,
             p.proname,
             p.oid AS p_oid,
             p.proargnames,
             p.proargmodes,
             information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[])) AS x,
             (information_schema._pg_expandarray(COALESCE(p.proallargtypes, p.proargtypes::oid[]))).n AS n
      FROM pg_namespace n,
           pg_proc p
      WHERE n.oid = p.pronamespace
        AND (pg_has_role(p.proowner, 'USAGE'::text)
             OR has_function_privilege(p.oid, 'EXECUTE'::text))) ss
WHERE t.oid = (ss.x).x
  AND t.typnamespace = nt.oid;

简化用例

随后我简化出了两个用例,不需要调整参数即可复现。

第一个用例如下:

MogDB=# select * from information_schema.parameters where specific_name = null ;
 specific_catalog | specific_schema | specific_name | ordinal_position | parameter_mode | is_result | as_locator | parameter_name | data_type | character_maximum_length | character_octet_length | character_set_catalog | character_set_schema | character_set_name | collation_catalog | collation_schema | collation_name | numeric_precision | numeric_precision_radix | numeric_scale | datetime_precision | interval_type | interval_precision | udt_catalog | udt_schema | udt_name | scope_catalog | scope_schema | scope_name | maximum_cardinality | dtd_identifier
------------------+-----------------+---------------+------------------+----------------+-----------+------------+----------------+-----------+--------------------------+------------------------+-----------------------+----------------------+--------------------+-------------------+------------------+----------------+-------------------+-------------------------+---------------+--------------------+---------------+--------------------+-------------+------------+----------+---------------+-------------+------------+---------------------+----------------
(0 rows)

MogDB=# explain select * from information_schema.parameters where specific_name = null ;
                QUERY PLAN
------------------------------------------
 Result  (cost=0.00..0.06 rows=1 width=0)
   One-Time Filter: false
(2 rows)

MogDB=# explain analyze select * from information_schema.parameters where specific_name = null ;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Result  (cost=0.00..0.06 rows=1 width=0) (actual time=0.009..0.009 rows=0 loops=1)
   One-Time Filter: false
 Total runtime: 1.893 ms
(3 rows)

MogDB=# explain performance select * from information_schema.parameters where specific_name = null ;
ERROR:  failed to find plan for subquery ss
MogDB=#

第二个简化用例则更聚焦于语句本身:

explain performance
SELECT (ss.x).n AS ordinal_position
FROM pg_type t,
     (SELECT information_schema._pg_expandarray(p.proallargtypes) AS x
      FROM pg_proc p
      WHERE proname = 'aclexplode') AS ss
WHERE t.oid = (ss.x).x
  and 1<>1;

上面这两个 SQL,都是直接执行不报错、 plain EXPLAIN 不报错、 EXPLAIN ANALYZE 不报错,只在 explain performance 时报错。由于当时项目里有优先级更高的问题,这个有规避方案的问题就被暂时搁置了,后续也没持续跟踪。

GaussDB / openGauss / PostgreSQL

几年后的今天(2026 年 8 月 18 日),我和客户讨论 GaussDB 为什么不在 statement_history 里记录更详细的逐步开销,而是只记一个参考值。我解释记详细开销对性能影响更大,而且 explain performance 和实际执行走的逻辑本来就有差异,正好举出本文的例子:同一个 SQL,直接查询不报错,explain performance 却报错。

提到这里,我顺便在 GaussDB 最新的 507 版本上测试了一下,发现该问题在 507 版本上依然存在:

gaussdb=# EXPLAIN PERFOrMANCE
SELECT (ss.x).n AS ordinal_position
FROM pg_type t,
     (SELECT information_schema._pg_expandarray(p.proallargtypes) AS x
      FROM pg_proc p
      WHERE proname = 'aclexplode') AS ss
WHERE t.oid = (ss.x).x
  and 1<>1;
ERROR:  failed to find plan for subquery ss

gaussdb=# SELECT (ss.x).n AS ordinal_position
gaussdb-# FROM pg_type t,
gaussdb-#       (SELECT information_schema._pg_expandarray(p.proallargtypes) AS x
gaussdb(# FROM pg_proc p
gaussdb(# WHERE proname = 'aclexplode') AS ss
gaussdb-# WHERE t.oid = (ss.x).x
  and 1<>1;
 ordinal_position
------------------
(0 rows)

之后又在最新的 openGauss 7.0 上测了下,问题也还在。于是我想让 AI 基于 openGauss 源码分析一下这个问题。

这次用了 copilotcli 接 opencode go 的 kimi k3,输出相当慢。在 AI 思考过程中,我看到它联想到了原生 PostgreSQL 似乎有类似问题。虽然 explain performance 是 openGauss 独有的,原生 PostgreSQL 不支持,但原生 PG 支持 verbose ,那么 explain verbose 会不会也有这个问题?这里要提一句,kimi k3 没有搞混 openGauss 和 PostgreSQL,在没使用网络搜索的情况下连 performance 这类语法差异细节都注意到了,这点值得好评。

于是我没等 AI 分析完,直接用 dbfiddle 在线逐个 PG 版本验证了一遍:从 PG12 开始就不报错了,PG9.3 到 PG11 的报错和 openGauss 一模一样。这大概率就是一个已知的原生 PG 缺陷,后续版本已经修掉了。

随后我开了个临时免费的 mimo claw,让 AI 去找这个问题在原生 PG 里是哪个 commit 被修复的。AI 分析过程中,我「土法」搜索了一下,发现 PG 社区近年还在讨论这个问题。2024 年,有人在 PG16.3 上执行类似 SQL 也报了同样的错——BUG #18576:

Bug reference: 18576
Logged by: Vasya B
Email address: vasiliy.boytsov@gmail.com
PostgreSQL version: 16.3
Operating system: Ubuntu 24.04
Description:
From a clean DB, one can execute: EXPLAIN (VERBOSE) SELECT FROM information_schema.element_types WHERE object_type = 'TABLE';
Which returns: ERROR: failed to find plan for subquery ss
While the expected result was a working query. W/O VERBOSE this query works.

与此同时,mimo claw 的分析结果也出来了,和我土法搜索得到的 BUG 是同一个。下面是第三轮对话产出的完整报告。

PostgreSQL EXPLAIN (VERBOSE) 报错 failed to find plan for subquery 完整分析

一、问题复现

EXPLAIN (VERBOSE)
SELECT (ss.x).n AS ordinal_position
FROM pg_type t,
     (SELECT information_schema._pg_expandarray(p.proallargtypes) AS x
      FROM pg_proc p
      WHERE proname = 'aclexplode') AS ss
WHERE t.oid = (ss.x).x
  AND 1<>1;

在多个 PG 版本上执行均报错:

ERROR:  failed to find plan for subquery ss

二、错误代码位置

src/backend/utils/adt/ruleutils.cget_name_for_var_field() 函数。

当 EXPLAIN 尝试反解 (ss.x).n 这种「对子查询 RECORD 类型输出的字段引用」时,需要找到子查询的执行计划来确定字段的真实类型。核心逻辑如下:

/*
 * We're deparsing a Plan tree so we don't have complete
 * RTE entries (in particular, rte->subquery is NULL). But
 * the only place we'd see a Var directly referencing a
 * SUBQUERY RTE is in a SubqueryScan plan node, and we can
 * look into the child plan's tlist instead.
 */
if (!dpns->inner_plan)          /* PG 9.3 用 inner_planstate */
    elog(ERROR, "failed to find plan for subquery %s",
         rte->eref->aliasname);

设计假设:引用 SUBQUERY RTE 的 Var 一定出现在 SubqueryScan 计划节点中。当优化器以任何方式消除了 SubqueryScan 节点, inner_plan 为 NULL,就会触发报错。

三、错误引入时间线

版本 是否存在该 elog 说明
PG 8.2 get_name_for_var_field 尚未依赖 SubqueryScan 节点
PG 8.3 Tom Lane 于 2007-02-23 引入(CVS r1.251)
PG 8.4 ~ PG 9.5 持续存在,但特定查询模式下未必触发
PG 9.6 3fc6e2d7f 引入新的触发路径(见下文)
PG 10 ~ PG 11 同上
PG 12+ ✅→修复 2024-08-09 修复并 back-patch

引入 commit(2007-02-23,Tom Lane,开发版本 8.3):

Now that plans have flat rangetable lists, it's a lot easier to get EXPLAIN to drill down into subplan targetlists... Along the way, fix an EXPLAIN bug I introduced by suppressing subqueries from execution-time range tables: get_name_for_var_field() assumed it could look at rte->subquery to find out the real type of a RECORD var. That doesn't work anymore, but instead we can look at the input plan of the SubqueryScan plan node.

这次重构把 get_name_for_var_field() 从「读取 RTE 的 subquery 字段」改为「读取 SubqueryScan 子计划的 targetlist」。当 SubqueryScan 节点不存在时,这个错误就产生了。

四、后续恶化的触发路径

commit 3fc6e2d7f5b652b417fa6937c34de2438d60fa9f(2016-03-07,Tom Lane,PG 9.6)

Make the upper part of the planner work by generating and comparing Paths.

这是一次大规模的优化器重构。在此之前,常量 FALSE 条件下的子查询会被优化为 Result -> Result(子 Result 保留了原始 targetlist), get_name_for_var_field 仍能从中获取字段信息。重构之后,优化器直接去掉了子 Result 节点,只保留一个空的父 Result,导致该函数在更多场景下触发报错。

Tom Lane 对简化用例的 bisection 结果:9.5 正常,9.6 开始失败。

五、不同查询模式触发同一错误

查询模式 触发版本 触发机制
简单 SRF + 常量 FALSE(Tom 的测试用例) ≥ 9.6 3fc6e2d7f 后优化器直接消除子 Result
Join + SRF + 常量 FALSE(你的查询) ≥ 8.3 Join 消除/约束推导等优化消除 SubqueryScan
information_schema.element_types(Bug #18576) ≥ 9.6 同 Tom 用例机制

你的查询在 PG9.3 就报错,是因为 join 场景下优化器通过不同于 3fc6e2d7f 的路径(可能是 join 消除或约束推导)去掉了 SubqueryScan 节点。这条路径从 PG8.3 起就存在。

六、修复

commit 120dd033761dbd70ad6267ff7255430bc8a989ed(2024-08-09)

  • 作者:Richard Guo & Tom Lane
  • Bug:#18576
  • 修改文件: src/backend/utils/adt/ruleutils.csrc/backend/optimizer/plan/createplan.c

修复逻辑:当发现子查询已被优化为空的 Result 节点(无 inner_plan )时,不再 elog(ERROR) ,而是 fallback 返回 "fN"(N 为字段序号)作为字段名:

if (!dpns->inner_plan)
{
    char *dummy_name = palloc(32);
    Assert(IsA(dpns->plan, Result));
    snprintf(dummy_name, 32, "f%d", fieldno);
    return dummy_name;
}

Back-patch 情况

版本 EOL 时间 是否收到修复
PG 11 2023-11-09 ❌ 已 EOL,未修复
PG 12 2024-11-14
PG 13 2025-11-13
PG 14+ 更晚

七、总结

该错误的本质是 ruleutils.c 中一个存在了 17 年的设计缺陷(2007—2024): get_name_for_var_field() 假设引用子查询的 Var 一定有对应的 SubqueryScan 计划节点,但优化器可以在多种场景下消除该节点。2016 年的优化器重构( 3fc6e2d7f )扩大了触发面,2024 年的修复( 120dd033761dbd70ad6267ff7255430bc8a989ed )通过 fallback 策略彻底解决了问题。PG11 因已 EOL 未收到修复,PG12 及以上版本均已修复。也就是说,这个修复并不是在 PG12 上直接做的,而是 2024 年 8 月修的,只是回溯到了 PG12。

kimi k3 的 openGauss 源码分析结果也出来了:

分析完成。所有环节均已在这台运行实例(同版本 3700f950)上复现验证。

结论:这是 EXPLAIN 反解析(deparse)的一个缺陷,不是查询本身的问题

触发需同时满足 4 个条件:

  1. WHERE 含常量假条件( 1<>1 ),整个查询被优化成无子节点的 dummy plan
  2. 子查询无法被展平消除
  3. 目标列引用了子查询输出中 RECORD 类型字段的成员 (ss.x).n
  4. EXPLAIN 需要打印 Output 列表(VERBOSE,或 PERFORMANCE——explain.cpp:414 强制 verbose=true

完整因果链(源码级):

  1. 1<>1 经常量折叠成 FALSE, make_join_relrestriction_is_constant_false() 命中 → mark_dummy_rel()(joinrels.cpp:677),joinrel 得到无子路径的 dummy AppendPath
  2. create_append_plan 对空 subpaths 生成无子节点的 ResultOne-Time Filter: false ,createplan.cpp:1586)。但该 Result 的 targetlist 仍保留原表达式 (ss.x).n ——一个指向 RTE ss 的 RECORD 型 Var 的 FieldSelect
  3. set_plan_references 展平 rangetable 时清空了 newrte->subquery = NULL(setrefs.cpp:236),最终计划里 RTE 已不带子查询 Query 树
  4. VERBOSE 打印 "Output:" 时 → get_rule_expr 处理 FieldSelect(ruleutils.cpp:10379)→ get_name_for_var_field 需要下钻解析 record 字段名
  5. RTE_SUBQUERY 分支中 rte->subquery == NULL ,只能走「plan-tree」分支,期望通过 SubqueryScan 节点的 inner_planstate 找子计划——但计划树里根本没有 SubqueryScan 节点(顶层是无子节点 Result)→ ruleutils.cpp:9402 报错

验证过的边界情况:

场景 结果
原查询直接执行 / plain EXPLAIN / EXPLAIN ANALYZE 正常(不反解析 Output)
EXPLAIN VERBOSE / PERFORMANCE 报错
去掉 1<>1 或改 1=1 正常(SubqueryScan 存在)
只输出 ss.x(不取 .n 字段)+ 常假 正常(走 get_variable 简单路径)
子查询可展平时(如 case C) 正常(Var 被内联,不再引用 RTE ss)

本质:优化器可以生成彻底消除 SubqueryScan 节点的 dummy plan,但反解析器的 plan-tree 回退分支假设「凡引用 RTE_SUBQUERY 必有对应 SubqueryScan 节点」,两者不匹配。修复方向:ruleutils.cpp:9402 处在 inner_planstate == NULL 时,对无法解析的 record 字段降级处理(如用 rte->eref 中的别名兜底),而非直接 ereport。

临时规避:用 plain EXPLAIN 或 EXPLAIN ANALYZE;或去掉常量假条件;或避免在可能被整体短路的查询中对子查询 record 列取字段。

总结

简单来说,这个 BUG 的表现就是:特定的一些 SQL,直接执行不报错,但 explain performance 或者 explain verbose 会报错;或者把 track_stmt_stat_level 第二个值开到 L1 时再执行 SQL 也会报错。

当年遇到的一个小问题,如今回过头来细查,竟然翻出了一个在 PostgreSQL 上持续了 17 年的 BUG。更准确地说,我这个 BUG 场景其实没有被其他人报告过:2007 年被 Tom Lane 引入,2016 年又被 Tom Lane 做了另一条 BUG 路径的错误修复,引来了 2024 年 Richard Guo 的再次修复,巧合之下把我遇到的这个场景也一起修掉了。

回想起来,我发现这个问题时是 2023 年,当时正在做一个非常重要的项目,非常忙,文章都写得少了(全年只发布 10 篇),也没往原生 PG 上想。要不然这个问题至少报告人就是我了。有意思的是,修复人 Richard Guo(郭峰)是中国人,也是 PG 的 Major Contributor 之一。

至于 openGauss 里这个 BUG 要不要去修,我暂时没心情。如果有谁看到了这篇文章想去修的话,可以在 issue 里顺便提一下本文。这类底层缺陷排查起来确实费神,有空的话也可以到云栈社区翻翻同类的避坑记录。


参考链接

[1] https://dbfiddle.uk/j7FJ4LYX

[2] https://postgrespro.ru/list/thread-id/2708652




上一篇:Python数据可视化实战:用Seaborn绘制广告支出与销售额线性回归图
下一篇:Claude Code 给 DeepSeek V4 Pro 接视觉:claude-vision-skill 安装与使用全记录
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-26 01:07 , Processed in 2.476537 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表