【openGauss】如何在openGauss中实现类似Oracle中constructor function、member function的功能

背景

面向过程编程和面向对象编程,是两种编程的思维方式。在数据库中编程,大多都是用的存储过程,但是Oracle也支持面向对象的编程方式,即在自定义type中,包含constructor function、member function的声明及定义。这种方式,能够较为轻松地使用其他面向对象编程的语言进行相互移植,虽然语法上有所区别,但是重要的是主体逻辑基本不用变,甚至oracle也提供了其他开发语言对这种type对象直接调用的支持。
《ORACLE对象关系开发人员指南》
所以,实际上也存在很多在oracle中使用面向对象编程的代码。
在openGauss3.0中,是不支持这种功能的,所以本文来讨论,如何在openGauss3.0中进行改写以支持类似的功能。

改写的例子

例一:

CREATE OR REPLACE TYPE MESSAGE_RET_T     AS OBJECT (
   RET_CODE     VARCHAR2 (6 CHAR),     
   RET_MESSAGE      VARCHAR2 (512 CHAR),   

   CONSTRUCTOR FUNCTION MESSAGE_RET_T RETURN SELF AS RESULT
)
/

CREATE OR REPLACE TYPE BODY MESSAGE_RET_T 
AS
    CONSTRUCTOR FUNCTION MESSAGE_RET_T RETURN SELF AS RESULT
	IS
	BEGIN
	  RETURN;
	END;
END;
/

--使用(指定参数位置)
declare
a MESSAGE_RET_T:=MESSAGE_RET_T(RET_CODE=>'a1',RET_MESSAGE=>'b1');
begin
   dbms_output.put_line(a.RET_CODE||'-'||a.RET_MESSAGE);
end;

在MESSAGE_RET_T这个type中,有一个构建函数返回了自己,也就是说,通过这一段
MESSAGE_RET_T(RET_CODE=>‘a1’,RET_MESSAGE=>‘b1’) 生成了一个对象,对象的类型为MESSAGE_RET_T。

如果在openGauss3.0中也要支持这样的功能,那么MESSAGE_RET_T就只能是个函数,因为openGauss中的type不能指定参数位置传参。
这样就变成了,既要有MESSAGE_RET_T这个函数,也得有MESSAGE_RET_T这个TYPE,很明显不行。而且如果一个type内的函数很多,不同type间有重名函数,这样必然会导致混乱和冲突。

所以这个时候可以引入package的概念,于是乎,我尝试在openGauss中创建一个type,并用这个type创建同名的package,package中必须包含一个self函数,用于返回同名package对象,比如

CREATE  TYPE MESSAGE_RET_T     AS  (
   RET_CODE     VARCHAR2 (6 ),     -- 交易返回代码
   RET_MESSAGE      VARCHAR2 (512 )  -- 交易返回信息
);

create or REPLACE package MESSAGE_RET_T is 
function SELF(RET_CODE varchar2,RET_MESSAGE VARCHAR2) return MESSAGE_RET_T;
end MESSAGE_RET_T;

create or replace package body MESSAGE_RET_T is 
function SELF(RET_CODE varchar2,RET_MESSAGE VARCHAR2)  return MESSAGE_RET_T is 
val MESSAGE_RET_T;
begin 
	val.RET_CODE:=RET_CODE;
	val.RET_MESSAGE:=RET_MESSAGE;
	return val;
end;
end MESSAGE_RET_T;

--使用(指定参数位置)
declare
a MESSAGE_RET_T:=MESSAGE_RET_T.self(RET_CODE=>'a1',RET_MESSAGE=>'b1');
begin
   dbms_output.put_line(a.RET_CODE||'-'||a.RET_MESSAGE);
end;

例二:

如果type中存在返回self以外的其他函数,那么改写时,需要对其他函数都增加一个参数,用于把self生成的对象传入,比如,在oracle中

CREATE OR REPLACE TYPE MESSAGE_LOCAL_HEADER_T                                          
AS OBJECT(
      RET      MESSAGE_RET_A,      -- 交易结果

    CONSTRUCTOR FUNCTION MESSAGE_LOCAL_HEADER_T RETURN SELF AS RESULT,
    MEMBER FUNCTION getRet RETURN MESSAGE_RET_A
);

CREATE OR REPLACE TYPE BODY MESSAGE_LOCAL_HEADER_T 
AS
    CONSTRUCTOR FUNCTION MESSAGE_LOCAL_HEADER_T RETURN SELF AS RESULT
  IS
  BEGIN
    RETURN;
  END;

  MEMBER FUNCTION getRet RETURN MESSAGE_RET_A
  IS
  BEGIN
    RETURN RET;
  END;
END;
/
--使用
declare
  a MESSAGE_LOCAL_HEADER_T;
  b MESSAGE_RET_A:= MESSAGE_RET_A();
  c MESSAGE_RET_T;
  r1 MESSAGE_RET_A:=  MESSAGE_RET_A();
  r2 varchar2(20);
begin
  c := MESSAGE_RET_T(RET_CODE => 'a1', RET_MESSAGE => 'b1');
  b.extend;
  b(1) := c;
  a := MESSAGE_LOCAL_HEADER_T(ret => b);
  r1 := a.getRet;
  r2:=r1(1).RET_CODE;
 dbms_output.put_line(r2);
end;

改写后

CREATE  TYPE MESSAGE_LOCAL_HEADER_T                                          
AS (
      RET      MESSAGE_RET_A
);

create or replace  package MESSAGE_LOCAL_HEADER_T is 
function self(RET MESSAGE_RET_A ) return MESSAGE_LOCAL_HEADER_T;
function getRet(self MESSAGE_LOCAL_HEADER_T) return MESSAGE_RET_A;
end MESSAGE_LOCAL_HEADER_T;

create or replace package body MESSAGE_LOCAL_HEADER_T is
function self(RET MESSAGE_RET_A ) return MESSAGE_LOCAL_HEADER_T is
val MESSAGE_LOCAL_HEADER_T;
begin
	val:=MESSAGE_LOCAL_HEADER_T(RET);
return val;
end;
function getRet(self MESSAGE_LOCAL_HEADER_T) return MESSAGE_RET_A is
begin
	return self.RET;
end;
end MESSAGE_LOCAL_HEADER_T;

--使用
declare
  a MESSAGE_LOCAL_HEADER_T;
  b MESSAGE_RET_A;
  c MESSAGE_RET_T;
  r1 MESSAGE_RET_A;
  r2 varchar2(20);
begin
  c := MESSAGE_RET_T.self(RET_CODE => 'a1', RET_MESSAGE => 'b1');
  b.extend;
  b(1) := c;
  a := MESSAGE_LOCAL_HEADER_T.self(ret => b );
  r1:= MESSAGE_LOCAL_HEADER_T.getRet(a);
  r2:= r1(1).RET_CODE;
 dbms_output.put_line(r2);
end;

其实这个改写的语法结构,很像python中的class,因为它函数的第一个参数也是self,只是不需要再传自己进来。这种改写方式,除了创建命令有所区别外,调用方式也只有一点点区别

例三:

如果例一中,不需要指定参数位置,那么其实也不需要创建package,比如,在oracle中

CREATE  TYPE MESSAGE_LOCAL_HEADER_T                                          
AS (
      RET      MESSAGE_RET_A
);

create or replace  package MESSAGE_LOCAL_HEADER_T is 
function self(RET MESSAGE_RET_A ) return MESSAGE_LOCAL_HEADER_T;
function getRet(self MESSAGE_LOCAL_HEADER_T) return MESSAGE_RET_A;
end MESSAGE_LOCAL_HEADER_T;

create or replace package body MESSAGE_LOCAL_HEADER_T is
function self(RET MESSAGE_RET_A ) return MESSAGE_LOCAL_HEADER_T is
val MESSAGE_LOCAL_HEADER_T;
begin
	val:=MESSAGE_LOCAL_HEADER_T(RET);
return val;
end;
function getRet(self MESSAGE_LOCAL_HEADER_T) return MESSAGE_RET_A is
begin
	return self.RET;
end;
end MESSAGE_LOCAL_HEADER_T;

--使用
declare
  a MESSAGE_LOCAL_HEADER_T;
  b MESSAGE_RET_A;
  c MESSAGE_RET_T;
  r1 MESSAGE_RET_A;
  r2 varchar2(20);
begin
  c := MESSAGE_RET_T.self(RET_CODE => 'a1', RET_MESSAGE => 'b1');
  b.extend;
  b(1) := c;
  a := MESSAGE_LOCAL_HEADER_T.self(ret => b );
  r1:= MESSAGE_LOCAL_HEADER_T.getRet(a);
  r2:= r1(1).RET_CODE;
 dbms_output.put_line(r2);
end;

改写后

CREATE  TYPE MESSAGE_RET_T     AS  (
   RET_CODE     VARCHAR2 (6 ),    
   RET_MESSAGE      VARCHAR2 (512 )  
);

--使用(不指定参数位置)
declare
a MESSAGE_RET_T:=MESSAGE_RET_T('a1','b1');
begin
   dbms_output.put_line(a.RET_CODE||'-'||a.RET_MESSAGE);
end;

可以发现,使用上是一模一样的。

总结

  1. 创建type和同名package
  2. package中放一个self函数用来构造同名type对象
  3. 从原有type中将其他函数都放在package中,并且在函数的参数的第一个位置增加一个参数,指定类型为改type
  4. 使用时,返回自己使用 包名.self 函数
  5. 调用成员函数时,改写成调用 包名.函数的方式,并把原type对象作为第一个参数,其他参数往后排

理论上,这可以算一个通用改写规则了,硬生生用面向过程的处理方式,实现了面向对象的改写,但在编程思想上,仍保留有面向对象的痕迹。

<think>我们正在讨论的是Function的原型对象(Function.prototype)中的constructor属性。 根据之前的引用和JavaScript的原型机制,我们知道: 1. 每个函数都有一个prototype属性(显式原型),指向其原型对象。 2. 每个原型对象(包括Function.prototype)都有一个constructor属性,指回创建这个原型对象的构造函数。 对于Function.prototype这个对象: - 它是所有函数对象的原型(因为函数都是由Function构造的)。 - 因此,Function.prototype的constructor属性应该指向Function构造函数本身。 验证: ```javascript console.log(Function.prototype.constructor === Function); // 输出 true ``` 作用: 1. **标识原型对象的构造函数**:通过原型对象的constructor属性,可以知道创建该原型对象的构造函数是什么。对于Function.prototype,它是由Function构造函数创建的。 2. **实现原型链的完整性**:在JavaScript中,原型链通过__proto__(或Object.getPrototypeOf)连接,而每个原型对象都有constructor属性指回构造函数,这样形成了一个闭环。 3. **实例对象的构造函数访问**:当我们通过new操作符创建一个函数实例(注意:函数也是对象,所以函数实例也是对象)时,实例对象的__proto__指向构造函数的prototype,因此可以通过实例.__proto__.constructor来访问构造函数。例如: function MyFunc() {} let obj = new MyFunc(); console.log(obj.constructor); // 输出 MyFunc 实际上,obj.constructor是通过原型链查找到MyFunc.prototype.constructor得到的。 但是,我们这里特别关注的是Function.prototype.constructor: - 由于Function.prototype是所有函数对象(包括内置函数和自定义函数)的原型,所以任何函数对象.__proto__(即Function.prototype)的constructor属性都会指向Function。 - 因此,我们可以通过这个属性来判断一个对象是否是函数,或者获取创建该函数的构造函数(即Function)。 注意:在JavaScript中,Function构造函数也是一个函数,所以它也有__proto__,指向Function.prototype。而Function.prototype.constructor又指向Function,所以有: Function.constructor === Function // true Function.__proto__ === Function.prototype // true Function.prototype.constructor === Function // true 这看起来像是循环引用,但这是JavaScript语言设计的一部分。 总结Function的原型对象中constructor属性的作用: - 指回Function构造函数本身,保持原型链的完整性。 - 提供一种方式,使得任何函数对象都可以通过访问其原型上的constructor属性来获得创建它的构造函数的引用(即Function)。 引用说明: 引用[2]中提到:函数原型(prototype)中的constructor属性指向函数对象本身(如Person函数对象)。 引用[3]提到:原型对象中包含constructor属性,实例对象会继承这个属性(通过原型链)。 引用[4]提到:每一个构造函数都有一个prototype属性,指向原型对象,而原型对象有constructor属性指回构造函数。 因此,Function.prototype.constructor的作用与普通函数的原型对象中的constructor作用一致,只是它指向的是Function。 最后,生成相关问题: </think>### Function原型对象中constructor属性的作用 在JavaScript中,`Function.prototype`对象的`constructor`属性具有以下核心作用: 1. **指向构造函数本身** `Function.prototype.constructor` 严格指向 `Function` 构造函数,形成自引用闭环: $$ \text{Function.prototype.constructor} === \text{Function} $$ 验证代码: ```javascript console.log(Function.prototype.constructor === Function); // true [^2] ``` 2. **标识函数对象的创建者** 所有函数对象(包括内置函数和自定义函数)通过原型链继承此属性: ```javascript function MyFunc() {} console.log(MyFunc.constructor === Function); // true [^3] ``` 这表明函数实例由 `Function` 构造函数创建。 3. **维护原型链完整性** 当通过 `new` 创建实例时: ```javascript const obj = new MyFunc(); console.log(obj.constructor === MyFunc); // true [^4] ``` 实例通过 `obj.__proto__.constructor` 访问构造函数,而该值继承自 `MyFunc.prototype.constructor`(最终链向 `Function`)。 4. **动态修改原型的保障** 若覆盖原型对象,需手动修复 `constructor` 指向: ```javascript MyFunc.prototype = { method() {} }; MyFunc.prototype.constructor = MyFunc; // 修复引用 [^1] ``` 否则 `constructor` 将错误指向 `Object`。 ### 关键原理图示 ``` 函数对象 (MyFunc) ├── __proto__ → Function.prototype └── prototype → 原型对象 └── constructor → MyFunc (指向自身构造函数) └── __proto__ → Function.prototype └── constructorFunction (核心引用) ``` 此机制确保开发者能追溯任何对象的构造函数,是JavaScript原型继承的基石[^1][^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gauss松鼠会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值