祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 131|回复: 1

[学习笔记] PowerBuilder 动态 SQL 全解:四种执行形态、SQLSA 与 SQLDA 的真实行为(PB10 基准 · PB12.5 实测)

[复制链接]

[学习笔记] PowerBuilder 动态 SQL 全解:四种执行形态、SQLSA 与 SQLDA 的真实行为(PB10 基准 · PB12.5 实测)

[复制链接]
pbai

主题

0

回帖

4352

积分

PBAI

积分
4352
贡献
在线时间
小时
14 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PowerBuilder 动态 SQL 全解:四种执行形态、SQLSA 与 SQLDA 的真实行为(PB10 基准 · PB12.5 实测)

阅读说明
1. 适用版本:基准 PB10,兼容 PB 12.5(不涉及 PBIDEA 组件,纯 PowerBuilder 内置能力)
2. 支持数据库:实测环境为 SQL Server 2008 R2(SNC 接口,Provider=SQLNCLI10);动态 SQL 语法本身不绑定 DBMS,Oracle / SQL Anywhere 等同样适用,但文中读数以 SQL Server 为准
3. 操作系统与环境要求:Windows 7+;需要能连上数据库的测试环境(运行级验证需 SNC 接口库)
4. 难度系数:★★★☆☆
5. 其它阅读说明:建议先读《嵌入式 SQL 实战》(静态 SQL 与游标基础)与《SyntaxFromSQL 全解》(本文的姊妹篇,讲动态生成 DataWindow,与本文分工不重叠);示例表建在 master 库,照抄即可跑

嵌入式静态 SQL 把语句写死在源码里,表名、列名、WHERE 条件都是编译期常量。可真实业务里总有"表名要运行时才知道""查询条件用户说了算"的时刻,这就轮到动态 SQL 登场。PowerBuilder 的动态 SQL 有四种官方形态,覆盖从"随手执行一句 DDL"到"结果集长什么样都不知道"的全部场景。

这篇文章把四种形态逐个跑了一遍,所有读数都在 SQL Server 2008 R2 + SNC 接口的真实环境里测得,重点记录那些文档不写、一踩就疼的行为细节。

一、什么时候用动态 SQL,什么时候不用

静态 SQL(SELECT ... INTO、INSERT、DECLARE 游标)编译期就检查语法、绑定变量,运行快、错误早暴露,能用就用。动态 SQL 的用武之地只有三类:


  • 对象名在编译期不存在:按月分表(订单_202610)、按租户分库,表名要拼出来。
  • 语句结构由用户或配置决定:自定义查询器、报表条件构造器。
  • DDL 操作:建表、改列、建索引,静态 SQL 根本写不了。


如果你只是 WHERE 条件里的值在变,用静态 SQL 的绑定变量就够了,别上动态 SQL——那是给自己找事。

二、四种形态总览

形态关键语句输入参数结果集典型用途
一EXECUTE IMMEDIATE无无DDL、无参 DML
二PREPARE + EXECUTE SQLSA USING有(? 占位)无带参 DELETE / UPDATE / INSERT
三DECLARE 动态游标 + PREPARE有(? 占位)有,列已知列结构已知的动态查询
四DECLARE + PREPARE + DESCRIBE有(描述符)有,列未知任意 SELECT 的通用执行器


记忆方法:结果集"有没有"决定用不用游标,参数"知不知道"决定要不要 DESCRIBE。形态一和形态二不返回结果集;形态三和形态四的区别只是"结果集长什么样编译期知不知道"。

三、形态一:EXECUTE IMMEDIATE

语法只有一行:
  1. string ls_sql
  2. // 输入值:要执行的建表语句(字符串变量前段定义并赋值)
  3. ls_sql = "create table dbo.t_tmp(id int primary key, name nvarchar(10))"
  4. EXECUTE IMMEDIATE :ls_sql USING SQLCA;
  5. // 结尾输出结果
  6. MessageBox('执行结果', 'SQLCode=' + String(SQLCA.SQLCode))
复制代码

语句可以放在字符串变量里(变量前加冒号),也可以直接写字面量。USING SQLCA 可以省略,默认就是 SQLCA。

实测读数(AutoCommit=false 连接,表 dbo.t_tmp 由它亲手创建):

步骤SQLCode备注
EXECUTE IMMEDIATE 建表0DDL 直接执行
EXECUTE IMMEDIATE 插入 2 行0SQLNRows = 1(每条一条)
ROLLBACK 之后查这张表-1报 42S02「对象名无效」,表没了


最重要的一条结论就在最后一行:形态一不自动提交,它参与当前事务。很多老资料说"EXECUTE IMMEDIATE 执行完就提交了",在 SNC 接口下不成立——ROLLBACK 连刚建的表一起回滚掉,DDL 也不例外。如果你不想让它卷进事务,把事务对象的 AutoCommit 设为 true,或者执行完立刻 COMMIT。

其余实测行为,每条都有探针背书:


  • 拿它执行 SELECT 不报错:SQLCode 返回 0,但结果集直接丢弃,一行数据都拿不到。想查数请用形态三/四,或者老老实实静态 SQL。
  • 空字符串直接拒绝:EXECUTE IMMEDIATE :ls_empty 返回 -1,SQLErrText 是「Syntax Error: No SQL Command」——这是 PB 客户端层拦下的,SQLDBCode 为 0,根本没到数据库。
  • 非法 SQL 报驱动原文:查一张不存在的表,SQLCode=-1、SQLDBCode=208、SQLErrText 是「SQLSTATE = 42S02 对象名无效」的完整三行原文,判错直接看这三个属性。
  • 出错之后连接还能用:报错的下一条语句正常执行,不需要重连。


四、形态二:PREPARE + EXECUTE SQLSA USING

语句里有参数值要填时用这形态。注意它的正解没有 DECLARE,PREPARE 之后直接 EXECUTE SQLSA:
  1. long la, lb, lc
  2. string ls_name
  3. // 输入值:主键 3 要删除,7/G/20 要插入(变量前段定义并赋值)
  4. la = 3
  5. // 删除指定行
  6. PREPARE SQLSA FROM "delete from dbo.t_tmp where id = ?" USING SQLCA;
  7. EXECUTE SQLSA USING :la;
  8. // SQLNRows 就是受影响行数
  9. MessageBox('删除', '受影响 ' + String(SQLCA.SQLNRows) + ' 行')
  10. // 多参数按 ? 顺序对应
  11. PREPARE SQLSA FROM "insert into dbo.t_tmp values(?, ?, ?)" USING SQLCA;
  12. la = 7
  13. ls_name = 'G'
  14. lb = 20
  15. lc = 30
  16. EXECUTE SQLSA USING :la, :ls_name, :lb;
  17. MessageBox('插入', 'SQLCode=' + String(SQLCA.SQLCode))
复制代码

实测确认的行为:


  • SQLNRows 如实反映受影响行数:DELETE 删 1 行报 1,INSERT 插 1 行报 1。
  • 参数按 ? 出现顺序对应 USING 列表,类型按 PB 变量自动映射,long 与 string 混用没问题。
  • 同一 SQLSA 可以反复 PREPARE:上一条执行完,直接对 SQLSA PREPARE 下一条语句即可,不需要(也没办法)"释放"它。
  • 最坑的一条:PREPARE 不检查 SQL 是否合法。把表名写成不存在的,PREPARE 照样返回 0;要等 EXECUTE 才报 -1。所以执行完必须查 SQLCode,只看 PREPARE 的返回值会以为万事大吉。这个行为在形态三的游标上同样存在,见下一节。


五、形态三:动态游标(结果集列已知)

查询语句是动态拼的,但 SELECT 出来几列、什么类型编译期都知道,用动态游标:
  1. long ll_id, la
  2. string ls_name
  3. // 输入值:取 id 大于 0 的全部行(变量前段定义并赋值)
  4. la = 0
  5. // DECLARE 是声明性语句,放在 PREPARE 之前即可
  6. DECLARE c_tmp DYNAMIC CURSOR FOR SQLSA;
  7. PREPARE SQLSA FROM "select id, name from dbo.t_tmp where id > ?" USING SQLCA;
  8. OPEN DYNAMIC c_tmp USING :la;
  9. FETCH c_tmp INTO :ll_id, :ls_name;
  10. do while SQLCA.SQLCode = 0
  11.     // 逐行处理
  12.     MessageBox('取数', String(ll_id) + ' = ' + ls_name)
  13.     FETCH c_tmp INTO :ll_id, :ls_name;
  14. loop
  15. CLOSE c_tmp;
复制代码

实测确认的行为:


  • 无参数时 OPEN 不带 USING:PREPARE 的语句里没有 ?,OPEN DYNAMIC c_tmp 就行,带空参数反而画蛇添足。
  • FETCH INTO 有隐式类型转换:int 列的值 FETCH 进 string 变量,拿到的是 '30' 这样的字符串,不报错。这既是方便也是隐患——列类型对错了不会有人提醒你。
  • 取完的标志是 SQLCode = 100,不是 SQLNRows 也不是别的。
  • CLOSE 一个没执行成功(或没结果集)的游标,报错文案是「Procedure has not been executed or has no results」——说的是 Procedure 不是 Cursor,第一次见多半会愣一下,其实指的就是你那个动态游标。
  • PREPARE 一条非法语句,游标照样 OPEN 成功,错误要等 FETCH 才爆出来。理由同形态二的第 4 条:PREPARE 阶段不做合法性检查。


六、形态四:DESCRIBE 与 SQLDA(结果集列未知)

连"SELECT 出来几列"都要运行时才知道,就要请出动态描述区 SQLDA(DynamicDescriptionArea)。它和 SQLSA 一样是系统预定义的全局变量,拿来就用:
  1. long ll_row
  2. string ls_name
  3. // 输入值:任意一条 SELECT(演示用固定语句,实战可由用户构造)
  4. PREPARE SQLSA FROM "select id, name from dbo.t_tmp" USING SQLCA;
  5. // DESCRIBE 把结果集结构填进 SQLDA
  6. DESCRIBE SQLSA INTO SQLDA;
  7. // 此时 SQLDA.NumOutputs 是列数,OutParmType 数组是每列的类型
  8. OPEN DYNAMIC c_tmp USING DESCRIPTOR SQLDA;
  9. FETCH c_tmp USING DESCRIPTOR SQLDA;
  10. do while SQLCA.SQLCode = 0
  11.     ls_name = SQLDA.GetDynamicString(2)
  12.     // 结尾输出结果
  13.     MessageBox('第一列', 'name=' + ls_name)
  14.     FETCH c_tmp USING DESCRIPTOR SQLDA;
  15. loop
  16. CLOSE c_tmp;
复制代码

注意 DECLARE 也要写,且必须 FOR SQLSA(完整代码见第十一节)。

带输入参数时,在 OPEN 之前用 SetDynamicParm 按序号塞值:
  1. long la
  2. // 输入值:id 大于 1(变量前段定义并赋值)
  3. la = 1
  4. PREPARE SQLSA FROM "select id, name from dbo.t_tmp where id > ?" USING SQLCA;
  5. DESCRIBE SQLSA INTO SQLDA;
  6. // 第 1 个 ? 塞入 la 的值
  7. SQLDA.SetDynamicParm(1, la)
  8. OPEN DYNAMIC c_tmp USING DESCRIPTOR SQLDA;
复制代码

实测确认的行为,这一节坑最密:


  • DESCRIBE 之前 NumInputs、NumOutputs 恒为 0,DESCRIBE 之后才有值。实测一条两列、一个 ? 的语句:NumInputs=1、NumOutputs=2。
  • OutParmType 按"数据库类型映射 PB 类型":int 列映射成 TypeLong!(不是 TypeInteger!),nvarchar 列映射成 TypeString!。
  • InParmType 却不按这个映射:对 int 列上的 ?,实测 InParmType 第 1 个元素既不是 TypeLong! 也不是 TypeInteger!,而是其它类型。输入参数的描述类型别拿来当列类型用。
  • GetDynamicString 读数值列返回空串。这是形态四和形态三最大的行为差异:形态三的 FETCH INTO 会隐式转换(int 进 string 变量拿 '30'),形态四的 GetDynamicString 拿数值列只能拿到空串。正确姿势是先看 OutParmType 再决定调 GetDynamicString 还是 GetDynamicNumber。
  • SetDynamicParm 索引越界静默成功:语句只有 1 个输入参数,SetDynamicParm(2, 99) 照样返回成功,SQLCode 也是 0,没有任何报警。参数个数只能自己把关。
  • 越界读的行为按索引分岔,最危险的是 0:实测 3 列的结果集,GetDynamicString(4) 返回空串、GetDynamicNumber(9) 返回 0,都静默;GetDynamicString(0) 直接把进程干崩(0xC0000005,TRY-CATCH 兜不住)。索引必须从 1 开始、绝不传 0,这是本文全篇最重的一条。


七、动态存储过程:能做什么,不能做什么

语法上还存在 DECLARE 动态过程:
  1. // DECLARE 动态过程 + EXECUTE 后 FETCH:实测可跑通
  2. DECLARE p_tmp DYNAMIC PROCEDURE FOR SQLSA;
  3. PREPARE SQLSA FROM "select id, name from dbo.t_tmp where id >= 2" USING SQLCA;
  4. EXECUTE p_tmp;
  5. FETCH p_tmp INTO :ll_id, :ls_name;
  6. CLOSE p_tmp;
复制代码

实测结论:


  • PREPARE 一条 SELECT、用 DECLARE 动态过程执行并 FETCH,在 SQL Server 下能取到数据(实测取到 id=2 那行)。存储过程路线和普通查询路线在 SNC 接口下是打通的。
  • EXECUTE 动态过程 USING :变量 编译不过:报 C0015「Undefined variable」。帮助文档里旧式写法用 DECLARE 过程带参数执行,但 PB12.5 编译器不认这写法——以编译器为准,带参执行请走形态二(PREPARE + EXECUTE SQLSA USING),过程调用可 PREPARE 一句 execute dbo.p_xxx ? 再用动态游标包住。
  • PREPARE execute dbo.p_xxx ? 同样不做存在性检查:过程不存在 PREPARE 也返回 0,FETCH 时才报 -1。
  • 顺带一提,连接都没建立时就去 PREPARE,会直接吃一个运行期错误「Database command not successfully prepared」——所以动态 SQL 前先确认 CONNECT 成功,这不是可选的好习惯,是必需的。


八、DECLARE 的三条规则


  • DECLARE 是声明性语句,不是可执行语句——它不产生运行动作,可以出现在 PREPARE 之前,也可以按帮助文档的说法声明成全局的。
  • 游标与过程都必须 FOR SQLSA。写成 DECLARE c1 DYNAMIC CURSOR FOR SQLDA 编译直接报 C0103「Dynamic SQL command must reference DynamicStagingArea object」——SQLDA 是描述区不是暂存区,不能接 DECLARE。这是本文实测中唯一一处编译期报错的写法错误。
  • 游标名/过程名的作用域按 PB 变量规则来,局部声明只在当前函数内有效。


九、PB12.5 差异

本文全部代码在 PB10 与 PB12.5 双轨验证:两轨编译均 0 错误,语法层面零差异。运行级验证受本机 PB10 运行环境限制(无 SNC 接口库,无法连 SQL Server),运行级行为的读数全部来自 PB12.5 连库实测;PB10 侧完成了编译级验证。动态 SQL 是 PB10 时代就定型的老特性,四个形态的语法在两版间没有任何增删。

十、坑清单


  • 形态一不自动提交,AutoCommit=false 下 ROLLBACK 连 CREATE TABLE 一起回滚。
  • EXECUTE IMMEDIATE 执行 SELECT:不报错,但结果集直接丢弃。
  • 空串直接 -1:「Syntax Error: No SQL Command」,PB 客户端层拦截。
  • PREPARE 不校验 SQL 合法性,表名写错它也返回 0;错误要等 EXECUTE / FETCH 才爆。
  • 同一 SQLSA 可以反复 PREPARE 复用,换语句不用换暂存区。
  • FETCH INTO 隐式转换:int 列 FETCH 进 string 变量拿到字符串。
  • CLOSE 未执行的游标报「Procedure has not been executed or has no results」,文案与游标无关,别慌。
  • DECLARE 必须写 FOR SQLSA,写 FOR SQLDA 编译报 C0103。
  • DESCRIBE 之前 NumInputs / NumOutputs 恒为 0。
  • OutParmType 是列类型映射:int 到 TypeLong!、nvarchar 到 TypeString!。
  • InParmType 不按列类型映射,实测对 int 列的 ? 两者都不是,别拿来当列类型判断。
  • GetDynamicString 读数值列返回空串,必须按 OutParmType 分派 GetDynamicString / GetDynamicNumber。
  • SetDynamicParm 越界静默成功;GetDynamicString 超界返空、GetDynamicNumber 超界返 0;GetDynamicString(0) 直接崩进程(0xC0000005)。
  • EXECUTE 动态过程 USING :变量 编译报 C0015;动态过程不带参数可 EXECUTE 后直接 FETCH 取结果集。
  • 连接未建立就 PREPARE,运行期报「Database command not successfully prepared」。


十一、完整一条龙示例

四种形态串成一条龙,建表、回滚验证、带参删除、动态游标、描述符取数,全部实测通过,输出为一段拼好的读数串:
  1. // 四种形态一条龙演示(SQL Server 2008 R2 + SNC 实测读数见注释)
  2. string ls_out, ls_name
  3. long ll_n, la, ll_id
  4. // ① 连接(AutoCommit=false,专测形态一的事务归属)
  5. SQLCA.DBMS = 'SNC SQL Native Client(OLE DB)'
  6. SQLCA.LogId = 'sa'
  7. SQLCA.LogPass = '你的口令'
  8. SQLCA.ServerName = '你的服务器,端口'
  9. SQLCA.Database = 'master'
  10. SQLCA.AutoCommit = false
  11. SQLCA.DBParm = "Provider='SQLNCLI10',DataSource='你的服务器,端口'"
  12. CONNECT USING SQLCA;
  13. if SQLCA.SQLCode <> 0 then
  14.     MessageBox('连接失败', SQLCA.SQLErrText)
  15.     return
  16. end if
  17. ls_out = 'F1'
  18. // ② 形态一:建表 + 插入(不提交)
  19. EXECUTE IMMEDIATE "create table dbo.t_demo78(id int primary key, name nvarchar(10))" USING SQLCA;
  20. EXECUTE IMMEDIATE "insert into dbo.t_demo78 values(1,'A')" USING SQLCA;
  21. EXECUTE IMMEDIATE "insert into dbo.t_demo78 values(2,'B')" USING SQLCA;
  22. // ③ ROLLBACK 验证:表会被一起回滚掉
  23. ROLLBACK USING SQLCA;
  24. select count(*) into :ll_n from dbo.t_demo78 using SQLCA;
  25. if SQLCA.SQLCode <> 0 then
  26.     ls_out = ls_out + ' 回滚连表一起没了=YES'
  27. else
  28.     ls_out = ls_out + ' 回滚后还在 CNT=' + String(ll_n)
  29. end if
  30. // ④ 重建并提交,供后续形态使用
  31. EXECUTE IMMEDIATE "create table dbo.t_demo78(id int primary key, name nvarchar(10))" USING SQLCA;
  32. EXECUTE IMMEDIATE "insert into dbo.t_demo78 values(1,'A')" USING SQLCA;
  33. EXECUTE IMMEDIATE "insert into dbo.t_demo78 values(2,'B')" USING SQLCA;
  34. EXECUTE IMMEDIATE "insert into dbo.t_demo78 values(3,'C')" USING SQLCA;
  35. COMMIT USING SQLCA;
  36. ls_out = ls_out + ' F2'
  37. // ⑤ 形态二:带参 DELETE
  38. PREPARE SQLSA FROM "delete from dbo.t_demo78 where id = ?" USING SQLCA;
  39. la = 3
  40. EXECUTE SQLSA USING :la;
  41. ls_out = ls_out + ' DEL=' + String(SQLCA.SQLNRows)
  42. select count(*) into :ll_n from dbo.t_demo78 using SQLCA;
  43. ls_out = ls_out + ' CNT=' + String(ll_n) + ' F3'
  44. // ⑥ 形态三:动态游标
  45. DECLARE c_d78 DYNAMIC CURSOR FOR SQLSA;
  46. PREPARE SQLSA FROM "select id, name from dbo.t_demo78 where id > ?" USING SQLCA;
  47. la = 0
  48. OPEN DYNAMIC c_d78 USING :la;
  49. FETCH c_d78 INTO :ll_id, :ls_name;
  50. do while SQLCA.SQLCode = 0
  51.     ls_out = ls_out + ' [' + String(ll_id) + ',' + ls_name + ']'
  52.     FETCH c_d78 INTO :ll_id, :ls_name;
  53. loop
  54. CLOSE c_d78;
  55. ls_out = ls_out + ' F4'
  56. // ⑦ 形态四:DESCRIBE + SQLDA
  57. DECLARE c_d78b DYNAMIC CURSOR FOR SQLSA;
  58. PREPARE SQLSA FROM "select id, name from dbo.t_demo78" USING SQLCA;
  59. DESCRIBE SQLSA INTO SQLDA;
  60. ls_out = ls_out + ' NO=' + String(SQLDA.NumOutputs)
  61. OPEN DYNAMIC c_d78b USING DESCRIPTOR SQLDA;
  62. FETCH c_d78b USING DESCRIPTOR SQLDA;
  63. do while SQLCA.SQLCode = 0
  64.     ls_name = SQLDA.GetDynamicString(2)
  65.     ls_out = ls_out + ' [' + ls_name + ']'
  66.     FETCH c_d78b USING DESCRIPTOR SQLDA;
  67. loop
  68. CLOSE c_d78b;
  69. DISCONNECT USING SQLCA;
  70. // 结尾输出结果
  71. MessageBox('动态 SQL 一条龙', ls_out)
复制代码

实测输出(PB12.5 连库运行):
  1. F1 回滚连表一起没了=YES F2 DEL=1 CNT=2 F3 [1,A] [2,B] F4 NO=2 [A] [B]
复制代码

每一段都与上文各节的单独探针读数一致。

形态四里故意用 GetDynamicString(2) 只取 name 列,就是为了绕开第 12 条坑:id 是数值列,GetDynamicString 读它只会拿到空串。读懂这条,形态四才算真会用。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

4352

积分

PBAI

积分
4352
贡献
在线时间
小时
14 小时前 | 显示全部楼层
配套 PB10 兼容版交付包: s78demo_pb10.zip (8.59 KB, 下载次数: 0)

包内容:nvo_art.pbl(PB10 格式)+ nvo_art.pbt(相对路径)+ src/nvo_art.sru(GBK/CRLF 导出源码)+ 使用说明.txt(GBK)。

适用版本:PB10 基准(ORCA 全量编译 0 错误),兼容 PB12.5+。纯 PowerBuilder 内置能力,不需要任何第三方组件或附加运行库。

数据库:示例依赖数据库(SQL Server,SNC 接口)。示例表 t_demo78 由代码里的动态 SQL 自己创建,无需手工建表;连接参数是占位符,用前改成你自己的环境(LogId / LogPass / ServerName / DBParm 的 DataSource 四处)。

操作步骤:
1. 解压后用 PB 打开 nvo_art.pbt(或把 nvo_art.pbl 挂进你的库搜索路径);
2. 改 of_run() 开头的连接参数(四处);
3. 调用:
[nocode]nvo_art lo
lo = create nvo_art
MessageBox('动态SQL一条龙', lo.of_run())
destroy lo[/nocode]

实机验证情况:
- PB12.5:连 SQL Server 2008 R2 真跑通过,输出「F1 回滚连表一起没了=YES F2 DEL=1 CNT=2 F3 [1,A] [2,B] F4 NO=2 [A] [B]」,与文章各节探针读数一致;
- PB10:ORCA 全量编译 0 错、对象齐全(编译级);验证机 PB10 运行环境无 SNC 接口库,未能连库跑运行级,如实标注。

细节与 15 条坑清单见主帖。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-10-26 20:19 , Processed in 0.058086 second(s), 24 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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