From be7b2ebfdd38a301eeae64287cd6127fe0697377 Mon Sep 17 00:00:00 2001 From: himmel Date: Tue, 21 Oct 2025 16:49:47 +0800 Subject: [PATCH 1/2] Add documentation for nested subfunctions feature - Provide implementation details and use cases for Oracle-compatible nested subfunctions --- CN/modules/ROOT/nav.adoc | 2 + CN/modules/ROOT/pages/master/6.3.7.adoc | 113 ++++++++++++++++++++++++ CN/modules/ROOT/pages/master/7.19.adoc | 83 +++++++++++++++++ EN/modules/ROOT/nav.adoc | 4 +- EN/modules/ROOT/pages/master/6.3.7.adoc | 113 ++++++++++++++++++++++++ EN/modules/ROOT/pages/master/7.19.adoc | 83 +++++++++++++++++ 6 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 CN/modules/ROOT/pages/master/6.3.7.adoc create mode 100644 CN/modules/ROOT/pages/master/7.19.adoc create mode 100644 EN/modules/ROOT/pages/master/6.3.7.adoc create mode 100644 EN/modules/ROOT/pages/master/7.19.adoc diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index e3c8c3c6..f087cf2a 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -26,6 +26,7 @@ **** xref:master/6.3.2.adoc[OUT 参数] **** xref:master/6.3.4.adoc[%TYPE、%ROWTYPE] **** xref:master/6.3.5.adoc[NLS 参数] +**** xref:master/6.3.7.adoc[嵌套子函数] *** xref:master/6.4.adoc[国标GB18030] ** Oracle兼容功能列表 *** xref:master/7.1.adoc[1、框架设计] @@ -45,6 +46,7 @@ *** xref:master/7.15.adoc[15、OUT 参数] *** xref:master/7.16.adoc[16、%TYPE、%ROWTYPE] *** xref:master/7.17.adoc[17、NLS 参数] +*** xref:master/7.19.adoc[19、嵌套子函数] ** IvorySQL贡献指南 *** xref:master/8.1.adoc[社区贡献指南] *** xref:master/8.2.adoc[asciidoc语法快速参考] diff --git a/CN/modules/ROOT/pages/master/6.3.7.adoc b/CN/modules/ROOT/pages/master/6.3.7.adoc new file mode 100644 index 00000000..31d9f72b --- /dev/null +++ b/CN/modules/ROOT/pages/master/6.3.7.adoc @@ -0,0 +1,113 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += 嵌套子函数 + +== 目的 + +- 嵌套子函数:定义在函数、存储过程或匿名块内部的函数或存储过程,也称为 subproc 或 inner 函数。 +- 父函数:承载嵌套函数的外层函数、存储过程或匿名块,执行过程中负责实际触发子函数调用。 + +== 实现说明 + +=== 一、嵌套子函数语法识别 + +==== 1. 识别嵌套写法 + +当 `DECLARE` 块里出现 `function ... is/as begin ... end` 结构时,`pl_gram.y` 会调用 `plisql_build_subproc_function()`(对应创建普通函数,这一阶段相当于在 `pg_proc` 中更新 catalog 中的注册信息): + +. 在 `PLiSQL_function` 的 `subprocfuncs[]` 数组中创建 `PLiSQL_subproc_function` 结构,记录名称、参数、返回类型、属性等,获得一个下标 `fno` 作为该子函数的标识。 +. 调用 `plisql_check_subprocfunc_properties()` 校验声明与定义的合法组合。 + +==== 2. 数据项保存 + +保存到父函数的 `datum` 表:编译期的 `PLiSQL_function->datums` 描述子函数里的变量、记录字段,`PLiSQL_execstate->datums` 保存着执行过程中的变量。 + +==== 3. 保存多态函数模板 + +如果子函数里使用了多态参数,在语法阶段保存到 `subprocfunc->src`,同时将 `has_poly_argument` 设成 `true`,执行时按不同实参类型重新编译。 + +=== 二、父函数重新编译 + +. 父函数的 `PLiSQL_function` 结构多了一个 `subprocfuncs` 数组,里面每个元素就是刚才创建的 `PLiSQL_subproc_function`。 +. 子函数结构体 `PLiSQL_subproc_function` 有一个哈希表指针 `HTAB *poly_tab`,默认为空。当子函数里使用了多态函数时,`has_poly_argument` 为 `true`,则会在初次编译时初始化 `poly_tab`。`poly_tab` 的 key 是 `PLiSQL_func_hashkey`,记录着子函数的 `fno`、输入参数类型等; value 是编译好的 `PLiSQL_function *`(`plisql` 函数的执行上下文)。 + +=== 三、调用时解析 + +. 编译过程中,`pg` 解析器会生成一个 `ParseState` 结构,`plisql_subprocfunc_ref()` 会通过 `ParseState->p_subprocfunc_hook()` 找到父函数的 `PLiSQL_function`,调用 `plisql_ns_lookup()` 找到所有同名子函数的 `fno`,根据参数个数、类型找到最合适的多态函数。 +. `FuncExpr` 结构构造时会对子函数进行标记,方便后期执行阶段识别:`function_from = FUNC_FROM_SUBPROCFUNC`,`parent_func` 指向父级 `PLiSQL_function`,`funcid = fno`。 +. `plisql_call_handler()` 当 `function_from == FUNC_FROM_SUBPROCFUNC`,会用 `parent_func + fno` 找到对应的 `PLiSQL_subproc_function`: +.. 如果不是多态:直接复用 `subprocfunc->function` 里的动作树。 +.. 如果是多态:先在 `poly_tab` 查有没有编译结果;没有就调用 `plisql_dynamic_compile_subproc()` 编译,放进 `poly_tab` 缓存。 +. 子函数开始执行之前,`plisql_init_subprocfunc_globalvar()` 会把父函数的 `datum` 表中有关的变量 fork 一份,这样子函数可以获取到父函数的变量值,也不会污染父函数的变量空间;执行后由 `plisql_assign_out_subprocfunc_globalvar()` 把需要回写的变量更新到父函数的 `datum` 表。 + +== 模块设计 + +=== PL/iSQL 语法扩展 + +- `pl_gram.y` 新增子过程声明、嵌套定义的产生式,并在创建过程中记录 `lastoutvardno`、子过程信息等元数据。 +- 支持在子函数内引用父过程变量、子过程以及自定义类型。 + +当 DECLARE 块内出现 `function ... is/as begin ... end` 结构时,`pl_gram.y` 会调用 `plisql_build_subproc_function()` 进行编译: + +. 在 `PLiSQL_function` 的 `subprocfuncs[]` 中创建 `PLiSQL_subproc_function` 结构,记录名称、参数、返回类型和属性,分配下标 `fno` 作为子函数标识。 +. 调用 `plisql_check_subprocfunc_properties()` 校验声明与定义属性组合是否合法,防止重复或缺失声明造成的语义错误。 + +=== 数据项保存 + +父函数的 Datum 表在编译期和执行期分别缓存子函数能访问的变量: + +. `PLiSQL_function->datums` 保存子函数编译阶段可见的变量与记录字段信息。 +. `PLiSQL_execstate->datums` 在执行阶段持有实时的变量数值,实现运行期访问。 + +=== 多态函数模板 + +若子函数包含多态参数,语法阶段会: + +. 将子函数源文本拷贝到 `subprocfunc->src`。 +. 设置 `has_poly_argument = true`,为后续按实参类型动态编译做好准备。 + +=== 父函数重新编译 + +- 父函数的 `PLiSQL_function` 结构新增 `subprocfuncs` 数组,每个元素对应一个 `PLiSQL_subproc_function`。 +- `PLiSQL_subproc_function` 持有 `HTAB *poly_tab` 指针;当 `has_poly_argument` 为 `true` 时,在首次编译时初始化该缓存,键为 `PLiSQL_func_hashkey`(子函数 `fno` + 实参类型),值为编译后的 `PLiSQL_function`。 + +=== 解析器钩子 + +编译期间 PostgreSQL 解析器会构造 `ParseState`,`plisql_subprocfunc_ref()` 通过 `ParseState->p_subprocfunc_hook()` 连接父函数,调用 `plisql_ns_lookup()` 找到同名子函数的全部 `fno`,并依据参数个数与类型挑选最佳候选,实现重载分发。 + +=== FuncExpr 标记 + +构造 `FuncExpr` 时会标记嵌套调用信息,便于执行阶段识别: + +- `function_from = FUNC_FROM_SUBPROCFUNC`。 +- `parent_func` 指向父级 `PLiSQL_function`。 +- `funcid = fno`,用于快速定位子函数定义。 + +=== 嵌套函数查找机制 + +- `plisql_subprocfunc_ref()` 作为 `ParseState->p_subprocfunc_hook` 实现入口,复用名称空间查询逻辑。 +- `plisql_get_subprocfunc_detail()` 依据参数数量、类型与命名匹配规则挑选最优候选,是嵌套函数重载的关键。 + +=== 执行路径 + +. `plisql_call_handler()` 判断 `function_from` 后,通过 `parent_func + fno` 找到目标 `PLiSQL_subproc_function`。 +. 对普通子函数,直接复用 `subprocfunc->function` 缓存; +. 对多态子函数,先查询 `poly_tab`,未命中时调用 `plisql_dynamic_compile_subproc()` 动态编译并写入缓存。 + +=== 变量同步 + +- `plisql_init_subprocfunc_globalvar()` 在子函数执行前拷贝父函数 Datum 表中的相关变量,保证子函数读取到外层最新状态。 +- `plisql_assign_out_subprocfunc_globalvar()` 在返回前回写 OUT/INOUT 变量,确保父子函数数据一致性且互不污染。 + +=== PSQL 端语句发送 + +- `psqlscan.l` 调整 `proc_func_define_level` 和 `begin_depth` 的入栈/出栈逻辑,确保嵌套函数体整体发送至 SQL 端。 +- 只有当嵌套层级回到 0 且遇到分号时,才触发发送,避免子函数块被拆分。 + +=== SQL 层返回值获取 + +- 普通函数通过 `funcid` 访问 `pg_proc`;嵌套函数依赖 `FuncExpr.parent_func` 承载的 `PLiSQL_function`。 +- 为此实现一组函数指针(`plisql_register_internal_func()` 注册)供 SQL 层回调,按需获取嵌套函数名称、返回类型与 OUT 参数信息。 diff --git a/CN/modules/ROOT/pages/master/7.19.adoc b/CN/modules/ROOT/pages/master/7.19.adoc new file mode 100644 index 00000000..ff2f028b --- /dev/null +++ b/CN/modules/ROOT/pages/master/7.19.adoc @@ -0,0 +1,83 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += 兼容Oracle 嵌套子函数 + +== 1.目的 + +- 在 IvorySQL 中使用 Oracle 风格嵌套子函数。 + +== 2.功能说明 + +- 支持在匿名块、函数或过程内部声明与调用子函数/子过程,作用域限定在父块内。 +- 子函数可读取及更新父级变量,也可定义自身局部变量;父级无法直接访问子函数内部状态。 +- 支持重载解析机制,按参数个数、类型或命名区分同名子程序。 + +== 3.测试用例 + +[source,sql] +---- +DO $$ +DECLARE + v_result integer; + FUNCTION inner_square(p_value integer) RETURN integer IS + BEGIN + RAISE NOTICE 'inner_square called'; + RETURN p_value * p_value; + END; +BEGIN + v_result := inner_square(10); + RAISE NOTICE 'result=%', v_result; +END; +$$ LANGUAGE plisql; +---- + +[source,sql] +---- +DO $$ +DECLARE + v_base_multiplier integer := 20; + v_audit_counter integer := 0; + v_result integer; + FUNCTION inner_square(p_value integer) RETURN integer IS + BEGIN + RAISE NOTICE 'inner_square called'; + v_audit_counter := v_audit_counter + 1; + RETURN v_base_multiplier * p_value; + END; +BEGIN + v_result := inner_square(10); + RAISE NOTICE 'result=%', v_result; + RAISE NOTICE 'v_audit_counter=%', v_audit_counter; +END; +$$ LANGUAGE plisql; +---- + +[source,sql] +---- +-- Polymorphic nested function specializing on argument type +DO $$ +DECLARE + v_last_notice text := 'none'; + + FUNCTION describe_value(p_input anyelement) RETURN text IS + BEGIN + v_last_notice := format('polymorphic dispatch with %s', pg_typeof(p_input)); + RETURN v_last_notice; + END; + + FUNCTION describe_value(p_input anyarray, p_element anyelement) RETURN text IS + BEGIN + v_last_notice := format('array dispatch with %s', pg_typeof(p_input)::text); + RETURN v_last_notice; + END; +BEGIN + RAISE NOTICE '%', describe_value(100); + RAISE NOTICE '%', describe_value('IvorySQL'::text); -- explicit cast avoids ambiguous literal + RAISE NOTICE '%', describe_value(ARRAY[1,2,3], NULL::int); -- extra arg guides anyarray resolution + RAISE NOTICE 'last notice=%', v_last_notice; +END; +$$ LANGUAGE plisql; +---- diff --git a/EN/modules/ROOT/nav.adoc b/EN/modules/ROOT/nav.adoc index 3d42ed18..d31a07d4 100644 --- a/EN/modules/ROOT/nav.adoc +++ b/EN/modules/ROOT/nav.adoc @@ -11,7 +11,7 @@ ** xref:master/4.3.adoc[Developer] ** xref:master/4.4.adoc[Operation Management] ** xref:master/4.5.adoc[Migration] -* IvorySQL Ecosystem +* IvorySQL Ecosystem ** xref:master/5.1.adoc[PostGIS] ** xref:master/5.2.adoc[pgvector] * IvorySQL Architecture Design @@ -25,6 +25,7 @@ *** xref:master/6.3.2.adoc[OUT Parameter] *** xref:master/6.3.4.adoc[%Type & %Rowtype] *** xref:master/6.3.5.adoc[NLS Parameters] +*** xref:master/6.3.7.adoc[Nested Subfunctions] ** xref:master/6.4.adoc[GB18030 Character Set] * List of Oracle compatible features ** xref:master/7.1.adoc[1、Ivorysql frame design] @@ -44,6 +45,7 @@ ** xref:master/7.15.adoc[15、OUT Parameter] ** xref:master/7.16.adoc[16、%Type & %Rowtype] ** xref:master/7.17.adoc[17、NLS Parameters] +** xref:master/7.19.adoc[19、Nested Subfunctions] * xref:master/8.adoc[Community contribution] * xref:master/9.adoc[Tool Reference] * xref:master/10.adoc[FAQ] diff --git a/EN/modules/ROOT/pages/master/6.3.7.adoc b/EN/modules/ROOT/pages/master/6.3.7.adoc new file mode 100644 index 00000000..c81957aa --- /dev/null +++ b/EN/modules/ROOT/pages/master/6.3.7.adoc @@ -0,0 +1,113 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += Nested Subfunctions + +== Objective + +- Nested subfunctions refer to functions or procedures defined inside another function, stored procedure, or anonymous block; they are also called subprocs or inner functions. +- Parent functions are the outer functions, stored procedures, or anonymous blocks that host nested subfunctions and are responsible for invoking them during execution. + +== Implementation Notes + +=== 1. Syntax Recognition for Nested Subfunctions + +==== 1. Detecting Nested Definitions + +When a `DECLARE` block contains a `function ... is/as begin ... end` construct, `pl_gram.y` calls `plisql_build_subproc_function()` (similar to creating a regular function and updating the entry in `pg_proc`): + +. Create a `PLiSQL_subproc_function` entry in the parent `PLiSQL_function`'s `subprocfuncs[]` array to store the name, arguments, return type, and other attributes, and record the index `fno` as the identifier of this subfunction. +. Call `plisql_check_subprocfunc_properties()` to validate the combination of declaration and definition attributes. + +==== 2. Storing Datum Entries + +Nested subfunctions share the parent's datum table. During compilation, `PLiSQL_function->datums` describes variables and record fields inside the subfunction, while `PLiSQL_execstate->datums` keeps the runtime values. + +==== 3. Preserving Polymorphic Templates + +If the subfunction uses polymorphic parameters, the parser stores its source code in `subprocfunc->src` and sets `has_poly_argument` to `true` so that the executor can recompile it for each distinct argument type. + +=== 2. Recompiling the Parent Program + +. The parent `PLiSQL_function` gains a `subprocfuncs` array, each element being the `PLiSQL_subproc_function` created earlier. +. Each `PLiSQL_subproc_function` has a `HTAB *poly_tab` pointer that is initialized on the first compilation when `has_poly_argument` is `true`. The hash key is `PLiSQL_func_hashkey`, which records the subfunction's `fno` and input argument types; the value is the compiled `PLiSQL_function *` execution context. + +=== 3. Name Resolution During Invocation + +. PostgreSQL builds a `ParseState` structure during compilation. `plisql_subprocfunc_ref()` locates the parent `PLiSQL_function` through `ParseState->p_subprocfunc_hook()` and calls `plisql_ns_lookup()` to gather all `fno` values for subfunctions sharing the same name, then selects the best match based on argument count and types. +. When `FuncExpr` nodes are created, the subfunction call is tagged for later execution: `function_from = FUNC_FROM_SUBPROCFUNC`, `parent_func` points to the parent `PLiSQL_function`, and `funcid = fno`. +. In `plisql_call_handler()`, when `function_from == FUNC_FROM_SUBPROCFUNC`, the runtime fetches the appropriate `PLiSQL_subproc_function` via the pair `(parent_func, fno)`: +.. For non-polymorphic subfunctions, reuse the precompiled action tree stored in `subprocfunc->function`. +.. For polymorphic subfunctions, probe `poly_tab`; if there is no cached plan, call `plisql_dynamic_compile_subproc()` to compile one and store it in the cache. +. Before execution, `plisql_init_subprocfunc_globalvar()` forks relevant entries from the parent's datum table so the subfunction can access the latest parent variables without polluting the parent scope. After execution, `plisql_assign_out_subprocfunc_globalvar()` writes back the necessary variables. + +== Module Design + +=== PL/iSQL Grammar Extensions + +- `pl_gram.y` adds productions for subprocedure declarations and nested definitions, and records metadata such as `lastoutvardno` and subprocedure descriptors. +- Nested subfunctions can reference variables from the parent scope, other subprocedures, and user-defined types. + +Whenever a `function ... is/as begin ... end` construct is seen inside a `DECLARE` block, `pl_gram.y` invokes `plisql_build_subproc_function()`: + +. Insert a `PLiSQL_subproc_function` entry into the parent `PLiSQL_function->subprocfuncs[]`, storing the name, arguments, return type, and other attributes, and assign an index `fno`. +. Call `plisql_check_subprocfunc_properties()` to verify that declarations and definitions are consistent and to prevent duplicate or missing declarations from introducing semantic errors. + +=== Datum Storage + +The parent program's datum tables hold the variables accessible to nested subfunctions during compilation and execution: + +. `PLiSQL_function->datums` preserves the variable and record metadata visible during compilation. +. `PLiSQL_execstate->datums` carries the live values at runtime. + +=== Polymorphic Templates + +When a subfunction contains polymorphic arguments, the parser will: + +. Copy the subfunction source text into `subprocfunc->src`. +. Set `has_poly_argument = true` to prepare for dynamic recompilation based on actual argument types. + +=== Parent Recompilation + +- The parent `PLiSQL_function` includes a `subprocfuncs` array, with each element corresponding to a `PLiSQL_subproc_function`. +- Each `PLiSQL_subproc_function` maintains an optional `HTAB *poly_tab`; when `has_poly_argument` is `true`, the cache is initialized on the first compile. Keys are `PLiSQL_func_hashkey` (subfunction `fno` plus argument types), and values are the compiled `PLiSQL_function` plans. + +=== Parser Hooks + +During compilation, PostgreSQL creates a `ParseState`. `plisql_subprocfunc_ref()` plugs into `ParseState->p_subprocfunc_hook`, reusing the namespace lookup logic to gather candidates. `plisql_get_subprocfunc_detail()` then chooses the best match based on argument count, types, and named parameters, enabling overloaded dispatch. + +=== FuncExpr Annotation + +When constructing `FuncExpr` nodes, the compiler attaches metadata so the executor can recognize nested calls: + +- `function_from = FUNC_FROM_SUBPROCFUNC`. +- `parent_func` references the owning `PLiSQL_function`. +- `funcid = fno`, enabling direct lookup of the subfunction definition. + +=== Nested Subfunction Lookup + +- `plisql_subprocfunc_ref()` implements `ParseState->p_subprocfunc_hook` and reuses the namespace search to find nested subfunctions. +- `plisql_get_subprocfunc_detail()` applies matching rules for argument count, type, and naming to pick the optimal overload. + +=== Execution Path + +. `plisql_call_handler()` checks `function_from`; if it is a nested subfunction, the handler locates `PLiSQL_subproc_function` via `(parent_func, fno)`. +. For regular subfunctions, reuse the cached plan stored in `subprocfunc->function`. +. For polymorphic subfunctions, consult `poly_tab`; on a miss, call `plisql_dynamic_compile_subproc()` to build and cache a specialized plan. + +=== Variable Synchronization + +- `plisql_init_subprocfunc_globalvar()` copies the relevant entries from the parent datum table before the subfunction runs to expose the latest state. +- `plisql_assign_out_subprocfunc_globalvar()` writes back OUT/INOUT variables after execution to keep parent and child scopes consistent without mutual pollution. + +=== Statement Dispatch in psql + +- `psqlscan.l` adjusts the push/pop logic of `proc_func_define_level` and `begin_depth` so the nested subfunction body is transmitted to the SQL engine as a whole. +- Statements are sent only when the nesting depth returns to zero and a semicolon is reached, avoiding partial dispatch of subfunction blocks. + +=== Retrieving Return Information on the SQL Side + +- Regular functions obtain metadata via `funcid` from `pg_proc`; nested subfunctions rely on `FuncExpr.parent_func`, which holds the parent `PLiSQL_function`. +- A set of callback pointers (registered through `plisql_register_internal_func()`) allows the SQL layer to fetch nested subfunction names, return types, and OUT parameter information on demand. diff --git a/EN/modules/ROOT/pages/master/7.19.adoc b/EN/modules/ROOT/pages/master/7.19.adoc new file mode 100644 index 00000000..9be50d74 --- /dev/null +++ b/EN/modules/ROOT/pages/master/7.19.adoc @@ -0,0 +1,83 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += Oracle-Compatible Nested Subfunctions + +== 1. Objective + +- Using Oracle-style nested subfunctions in IvorySQL. + +== 2. Feature Description + +- Allows declaring and invoking subfunctions or subprocedures inside anonymous blocks, functions, or procedures, with scope limited to the parent block. +- Nested subfunctions can read and update variables defined in the parent scope while introducing their own local variables; the parent scope cannot directly access the subfunction's internal state. +- Supports overloading resolution that distinguishes homonymous subfunctions by argument count, data type, or named parameters. + +== 3. Test Cases + +[source,sql] +---- +DO $$ +DECLARE + v_result integer; + FUNCTION inner_square(p_value integer) RETURN integer IS + BEGIN + RAISE NOTICE 'inner_square called'; + RETURN p_value * p_value; + END; +BEGIN + v_result := inner_square(10); + RAISE NOTICE 'result=%', v_result; +END; +$$ LANGUAGE plisql; +---- + +[source,sql] +---- +DO $$ +DECLARE + v_base_multiplier integer := 20; + v_audit_counter integer := 0; + v_result integer; + FUNCTION inner_square(p_value integer) RETURN integer IS + BEGIN + RAISE NOTICE 'inner_square called'; + v_audit_counter := v_audit_counter + 1; + RETURN v_base_multiplier * p_value; + END; +BEGIN + v_result := inner_square(10); + RAISE NOTICE 'result=%', v_result; + RAISE NOTICE 'v_audit_counter=%', v_audit_counter; +END; +$$ LANGUAGE plisql; +---- + +[source,sql] +---- +-- Polymorphic nested function specializing on argument type +DO $$ +DECLARE + v_last_notice text := 'none'; + + FUNCTION describe_value(p_input anyelement) RETURN text IS + BEGIN + v_last_notice := format('polymorphic dispatch with %s', pg_typeof(p_input)); + RETURN v_last_notice; + END; + + FUNCTION describe_value(p_input anyarray, p_element anyelement) RETURN text IS + BEGIN + v_last_notice := format('array dispatch with %s', pg_typeof(p_input)::text); + RETURN v_last_notice; + END; +BEGIN + RAISE NOTICE '%', describe_value(100); + RAISE NOTICE '%', describe_value('IvorySQL'::text); -- explicit cast avoids ambiguous literal + RAISE NOTICE '%', describe_value(ARRAY[1,2,3], NULL::int); -- extra arg guides anyarray resolution + RAISE NOTICE 'last notice=%', v_last_notice; +END; +$$ LANGUAGE plisql; +---- From a1471ef7e24146c2629eb5e08d6037b84948b984 Mon Sep 17 00:00:00 2001 From: himmel Date: Wed, 22 Oct 2025 09:11:03 +0800 Subject: [PATCH 2/2] Remove numbered headings from nested subfunction documentation The numbered headings (1., 2., 3.) in section titles were redundant and inconsistent with the overall documentation style. This change replaces them with clean hierarchical headings while preserving all technical content. --- CN/modules/ROOT/pages/master/6.3.7.adoc | 12 ++++++------ CN/modules/ROOT/pages/master/7.19.adoc | 6 +++--- EN/modules/ROOT/pages/master/6.3.7.adoc | 12 ++++++------ EN/modules/ROOT/pages/master/7.19.adoc | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CN/modules/ROOT/pages/master/6.3.7.adoc b/CN/modules/ROOT/pages/master/6.3.7.adoc index 31d9f72b..63284050 100644 --- a/CN/modules/ROOT/pages/master/6.3.7.adoc +++ b/CN/modules/ROOT/pages/master/6.3.7.adoc @@ -12,29 +12,29 @@ == 实现说明 -=== 一、嵌套子函数语法识别 +=== 嵌套子函数语法识别 -==== 1. 识别嵌套写法 +==== 识别嵌套写法 当 `DECLARE` 块里出现 `function ... is/as begin ... end` 结构时,`pl_gram.y` 会调用 `plisql_build_subproc_function()`(对应创建普通函数,这一阶段相当于在 `pg_proc` 中更新 catalog 中的注册信息): . 在 `PLiSQL_function` 的 `subprocfuncs[]` 数组中创建 `PLiSQL_subproc_function` 结构,记录名称、参数、返回类型、属性等,获得一个下标 `fno` 作为该子函数的标识。 . 调用 `plisql_check_subprocfunc_properties()` 校验声明与定义的合法组合。 -==== 2. 数据项保存 +==== 数据项保存 保存到父函数的 `datum` 表:编译期的 `PLiSQL_function->datums` 描述子函数里的变量、记录字段,`PLiSQL_execstate->datums` 保存着执行过程中的变量。 -==== 3. 保存多态函数模板 +==== 保存多态函数模板 如果子函数里使用了多态参数,在语法阶段保存到 `subprocfunc->src`,同时将 `has_poly_argument` 设成 `true`,执行时按不同实参类型重新编译。 -=== 二、父函数重新编译 +=== 父函数重新编译 . 父函数的 `PLiSQL_function` 结构多了一个 `subprocfuncs` 数组,里面每个元素就是刚才创建的 `PLiSQL_subproc_function`。 . 子函数结构体 `PLiSQL_subproc_function` 有一个哈希表指针 `HTAB *poly_tab`,默认为空。当子函数里使用了多态函数时,`has_poly_argument` 为 `true`,则会在初次编译时初始化 `poly_tab`。`poly_tab` 的 key 是 `PLiSQL_func_hashkey`,记录着子函数的 `fno`、输入参数类型等; value 是编译好的 `PLiSQL_function *`(`plisql` 函数的执行上下文)。 -=== 三、调用时解析 +=== 调用时解析 . 编译过程中,`pg` 解析器会生成一个 `ParseState` 结构,`plisql_subprocfunc_ref()` 会通过 `ParseState->p_subprocfunc_hook()` 找到父函数的 `PLiSQL_function`,调用 `plisql_ns_lookup()` 找到所有同名子函数的 `fno`,根据参数个数、类型找到最合适的多态函数。 . `FuncExpr` 结构构造时会对子函数进行标记,方便后期执行阶段识别:`function_from = FUNC_FROM_SUBPROCFUNC`,`parent_func` 指向父级 `PLiSQL_function`,`funcid = fno`。 diff --git a/CN/modules/ROOT/pages/master/7.19.adoc b/CN/modules/ROOT/pages/master/7.19.adoc index ff2f028b..b35a5786 100644 --- a/CN/modules/ROOT/pages/master/7.19.adoc +++ b/CN/modules/ROOT/pages/master/7.19.adoc @@ -5,17 +5,17 @@ = 兼容Oracle 嵌套子函数 -== 1.目的 +== 目的 - 在 IvorySQL 中使用 Oracle 风格嵌套子函数。 -== 2.功能说明 +== 功能说明 - 支持在匿名块、函数或过程内部声明与调用子函数/子过程,作用域限定在父块内。 - 子函数可读取及更新父级变量,也可定义自身局部变量;父级无法直接访问子函数内部状态。 - 支持重载解析机制,按参数个数、类型或命名区分同名子程序。 -== 3.测试用例 +== 测试用例 [source,sql] ---- diff --git a/EN/modules/ROOT/pages/master/6.3.7.adoc b/EN/modules/ROOT/pages/master/6.3.7.adoc index c81957aa..5ea1eb7c 100644 --- a/EN/modules/ROOT/pages/master/6.3.7.adoc +++ b/EN/modules/ROOT/pages/master/6.3.7.adoc @@ -12,29 +12,29 @@ == Implementation Notes -=== 1. Syntax Recognition for Nested Subfunctions +=== Syntax Recognition for Nested Subfunctions -==== 1. Detecting Nested Definitions +==== Detecting Nested Definitions When a `DECLARE` block contains a `function ... is/as begin ... end` construct, `pl_gram.y` calls `plisql_build_subproc_function()` (similar to creating a regular function and updating the entry in `pg_proc`): . Create a `PLiSQL_subproc_function` entry in the parent `PLiSQL_function`'s `subprocfuncs[]` array to store the name, arguments, return type, and other attributes, and record the index `fno` as the identifier of this subfunction. . Call `plisql_check_subprocfunc_properties()` to validate the combination of declaration and definition attributes. -==== 2. Storing Datum Entries +==== Storing Datum Entries Nested subfunctions share the parent's datum table. During compilation, `PLiSQL_function->datums` describes variables and record fields inside the subfunction, while `PLiSQL_execstate->datums` keeps the runtime values. -==== 3. Preserving Polymorphic Templates +==== Preserving Polymorphic Templates If the subfunction uses polymorphic parameters, the parser stores its source code in `subprocfunc->src` and sets `has_poly_argument` to `true` so that the executor can recompile it for each distinct argument type. -=== 2. Recompiling the Parent Program +=== Recompiling the Parent Program . The parent `PLiSQL_function` gains a `subprocfuncs` array, each element being the `PLiSQL_subproc_function` created earlier. . Each `PLiSQL_subproc_function` has a `HTAB *poly_tab` pointer that is initialized on the first compilation when `has_poly_argument` is `true`. The hash key is `PLiSQL_func_hashkey`, which records the subfunction's `fno` and input argument types; the value is the compiled `PLiSQL_function *` execution context. -=== 3. Name Resolution During Invocation +=== Name Resolution During Invocation . PostgreSQL builds a `ParseState` structure during compilation. `plisql_subprocfunc_ref()` locates the parent `PLiSQL_function` through `ParseState->p_subprocfunc_hook()` and calls `plisql_ns_lookup()` to gather all `fno` values for subfunctions sharing the same name, then selects the best match based on argument count and types. . When `FuncExpr` nodes are created, the subfunction call is tagged for later execution: `function_from = FUNC_FROM_SUBPROCFUNC`, `parent_func` points to the parent `PLiSQL_function`, and `funcid = fno`. diff --git a/EN/modules/ROOT/pages/master/7.19.adoc b/EN/modules/ROOT/pages/master/7.19.adoc index 9be50d74..601766cf 100644 --- a/EN/modules/ROOT/pages/master/7.19.adoc +++ b/EN/modules/ROOT/pages/master/7.19.adoc @@ -5,17 +5,17 @@ = Oracle-Compatible Nested Subfunctions -== 1. Objective +== Objective - Using Oracle-style nested subfunctions in IvorySQL. -== 2. Feature Description +== Feature Description - Allows declaring and invoking subfunctions or subprocedures inside anonymous blocks, functions, or procedures, with scope limited to the parent block. - Nested subfunctions can read and update variables defined in the parent scope while introducing their own local variables; the parent scope cannot directly access the subfunction's internal state. - Supports overloading resolution that distinguishes homonymous subfunctions by argument count, data type, or named parameters. -== 3. Test Cases +== Test Cases [source,sql] ----