文档中心MogDBMogDB StackUqbar
v5.0

文档:v5.0

支持的版本:

其他版本:

支持嵌入式SQL预处理器(ECPG)

可获得性

本特性自MogDB 5.0.0版本开始引入。

特性简介

支持C语言中嵌入式SQL程序。

客户价值

可以相对容易地将为其他SQL数据库编写的嵌入式SQL程序移植到MogDB,保证用户业务快速迁移。

特性描述

嵌入式SQL程序由一种普通编程语言编写的代码(在这里是C)和位于特殊标记的小节中的SQL命令混合组成。要构建该程序,源代码(.pgc)首先会通过嵌入式SQL预处理器,它会将源代码转换成一个普通C程序(.c),并且后来它能够被一个C编译器所处理。转换过的 ECPG 应用会通过嵌入式 SQL 库(ecpglib)调用libpq库中的函数,并且与MogDB服务器使用普通的前端/后端协议通信。 嵌入式SQL在为 C 代码处理SQL命令方面比起其他方法来具有优势。

  1. 它处理了C程序中变量之间冗长的信息传递;
  2. 其次,程序中的SQL代码在编译时就会被检查以保证语法正确性;
  3. 在C语言中嵌入的SQL是在SQL标准中指定的并且受到很多其他SQL数据库系统的支持。

特性增强

为兼容Oracle Pro*C,平滑使用ECPG替换Pro*C实现业务逻辑,MogDB 5.0.8实现了如下功能:

  1. 支持EXEC SQL FOR FETCH取出多行结果到SQLDA结构体
  2. 支持EXEC SQL EXECUTE IMMEDIATE {:host_string}
  3. 支持动态SQL PREPARE主机变量
  4. 宿主变量声明与共享
  5. 数组形式指示符变量
  6. 兼容了SQLDA的DESCRIPTOR
  7. 兼容了DESCRIBE SELECT LIST FOR和DESCRIBE BIND VARIABLES FOR
  8. 兼容Pro*C格式的建立连接方式
  9. 数据类型转换与兼容
  10. 结构体数组处理多行数据
  11. for子句限制处理行数
  12. 事务处理语法commit relase rollback release
  13. 匿名块语法支持
  14. 兼容了EXECUTE IMMEDIATE string_literal
  15. 兼容了PREPARE FROM [SelectStmt|UpdateStmt|InsertStmt|DeleteStmt|MergeStmt]

特性约束

  1. 当前已经实现了部分OCI类型,但MogOCI库不成熟禁止使用
  2. 在sqlda中绑定这些数据类型的变量,其类型代码与ORACLE保持一致
  3. 与PRO*C一致,用户需要在预编译代码中自行实现绑定变量、查询时输出列类型处理
  4. 对于EXECUTE IMMEDIATE,仅支持hoststring,不支持string_literal
  5. PREPARE FROM仅支持主机变量,不支持SELECT语法
  6. 使用SQLDA接收数据时,仅当列给出长度限制,如:char[10],才能正确获取变长字符串

依赖关系

无。

使用方法

ECPG编译使用如下命令:

  1. ecpg testecpg.ecpg -o testecpg.c

  2. gcc -l$GAUSSHOME/include/postgresql/server/ -l$GAUSSHOME/include -L$GAUSSHOME/lib -lpq -lecpg -o testecpg testecpg.c

示例

#include <stdlib.h>
#include <stdio.h>

/* error handlers for the whole program */
EXEC SQL WHENEVER NOT FOUND DO BREAK;


int main(int argc, char **argv)
{
    EXEC SQL BEGIN DECLARE SECTION;
    int v_id, v_name_ind;
    char v_name[32];
    char *url="tcp:postgresql://127.0.0.1:5432/postgres";
    char *username="ecpg";
    char *password="Test@123";
    EXEC SQL END DECLARE SECTION;

    EXEC SQL DECLARE c CURSOR FOR
        SELECT id, name
        FROM test_ecpg_tab
        ORDER BY 1;

    /* connect to the database */
    EXEC SQL CONNECT TO :url USER :username USING :password;

    /* open a cursor */
    EXEC SQL OPEN c;

    /* loop will be left if the cursor is done */
    for(;;)
    {
        /* get the next result row */
        EXEC SQL FETCH NEXT FROM c INTO :v_id, :v_name :v_name_ind;

        printf(
            "id = %d, name = %s\n",
            v_id,
            v_name_ind ? "(null)" : v_name
        );
    }

    EXEC SQL CLOSE c;
    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT;

    return 0;
}
//基础的增删改

//查询
EXEC SQL SELECT ename,job,sal +2000 into :emp_name , :job_title,:salary from emp where empno = :emp_number;

// 插入
EXEC SQL INSERT INTO emp (empno,ename,sal,deptno) VALUES (:emp_number,:emp_name,:salary,:dept_number);
 
// 更新
EXEC SQL UPDATE emp SET sal = :salary , comm = :commission WHERE empno =:emp_number;

// 删除
EXEC SQL DELETE FROM emo WHERE deptno = :dept_number;
//对于动态 SQL 其中一种方法为使用 SQLDA 储存数据结构,SQLDA 结构定义于 sqlda.h 头文件中。在此将介绍如何使用 SQLDA
#include <sqlda.h>
SQLDA *bind_dp;
SQLDA *select_dp;

//该 SQLDA 结构体初始化可以使用如下方式
bind_dp = SQLSQLDAAlloc(runtime_context,size,name_lenght,ind_name_length);

//在 ORACLE 早期版本中使用 sqlald()函数来分配描述符
EXEC SQL FETCH ... USING DESCRIPTOR ...
EXEC SQL OPEN ... USING DESCRIPTOR ...

构建的SQLDA结构体含有若干成员,用户需要了解其各成员语义,并自行构建填充该SQLDA描述符。具体如下:

  • N变量:可以Describe的select-list或占位符的最大值

  • V变量:指向储存select-list或绑定变量值的数据缓冲区地址数组的指针

    在使用select-list或绑定变量前需要分配V对应空间,并声明

  • L变量:指向数据缓冲区的select-list或绑定变量值长度数组

  • T变量:指向数据缓冲区的select-list或绑定变量值数据类型代码数组

  • I变量:指向指示符变量的数据缓冲区

  • F变量:DESCRIBE实际找到的select-list或占位符数量

  • S变量:指向数据缓冲区的select-list或占位符的名称

  • M变量:select-list或占位符名称的长度

  • C变量:当前长度的select-list或占位符名称数组

  • X变量:储存指示符变量名称的数组

  • Y变量:指示符变量名称的最大长度数组

  • Z变量:指示变量名称的当前长度

用户需要了解如上SQLDA的部分实现细节,因为用户需要在其预编译的C代码中自行处理如何使用SQLDA。具体流程如下:

  1. Declare Section中声明一个主机字符串以保存查询文本
  2. 声明select和bind的SQLDA
  3. 为select和bind描述符分配存储空间
  4. 设置可以Describe的选择列表和占位符的最大数量
  5. 将查询文本放在主机字符串中
  6. 从主机字符串准备查询
  7. 为查询声明游标
  8. 将待绑定的变量DESCRIBE到绑定描述符中
  9. 将占位符的数量重置为DESCRIBE实际找到的数量
  10. 获取值并为DESCRIBE找到的绑定变量分配空间
  11. 使用bind描述符来打开游标
  12. 将select-list DESCRIBE到描述符中
  13. 将select-list的数量重置为DESCRIBE实际找到的数量
  14. 重置每个select-list列的长度和数据类型以进行显示
  15. 将数据库中的一行FETCH到select描述符指向的已分配数据缓冲区中
  16. 处理FETCH返回的select-list值
  17. 释放用于选择列表项目、占位符、指示符变量和描述符的存储空间
  18. 关闭游标
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//定义列和绑定变量的最大个数
#define MAX_ITEMS 40
//定义列名的最大值
#define MAX_VNAME_LEN 30  
#define MAX_INAME_LEN 30

int alloc_descriptor(int size,int max_vname_len,int max_iname_len);   
void set_bind_v();    
void set_select_v();
void free_da();   
void sql_error(char *msg);  

EXEC SQL INCLUDE sqlca;   
EXEC SQL INCLUDE sqlda; 
EXEC SQL INCLUDE sqlcpr;

//宿主变量定义:
EXEC SQL BEGIN DECLARE SECTION;
float f1 = 12.34;  
VARCHAR f2[64];
char sql_statement[256]= "select * from test_ora";   
char type_statement[256]="select f1,f2 from test_ora where f1<? and f2=:f2";   
int i;   
EXEC SQL END DECLARE SECTION;  

SQLDA *bind_p;   
SQLDA *select_p;  

int main()    
{   
    //ECPGdebug(1, stderr);

    exec sql connect to REGRESSDB1;
    printf("\n [OK Connected!] \n\n");
    EXEC SQL WHENEVER SQLERROR DO sql_error("<ERROR>");

    //给sqlda类型 分配数据
    alloc_descriptor(MAX_ITEMS,MAX_VNAME_LEN,MAX_INAME_LEN);   

    //建表语句
    EXEC SQL DROP TABLE IF EXISTS TEST_ORA;
    EXEC SQL CREATE TABLE TEST_ORA(f1 float, f2 text);
    EXEC SQL INSERT INTO TEST_ORA VALUES(12.34,'abcd123');
    EXEC SQL INSERT INTO TEST_ORA VALUES(12,'e234d');
    EXEC SQL INSERT INTO TEST_ORA VALUES(12.34,'abcd123');
    EXEC SQL INSERT INTO TEST_ORA VALUES(333.33,'abcd');
    EXEC SQL commit;
    //prepare语句
    EXEC SQL PREPARE S from :type_statement;   
    EXEC SQL DECLARE C1 CURSOR FOR S;    
    set_bind_v();  

    strcpy(f2.arr,"abcd123");
    f2.len = strlen(f2.arr);
    f2.arr[f2.len] = '\0';

    bind_p->L[0] = sizeof(float);
    bind_p->V[0] = (char*)malloc(bind_p->L[0]);
    memcpy(bind_p->V[0], &f1, sizeof(float));
    bind_p->T[0] = 4;   /* EXTERNAL_PROC_FLOAT */
    bind_p->L[1] = sizeof(char) * 64;
    bind_p->V[1] = (char*)malloc(bind_p->L[1] + 1);
    memcpy(bind_p->V[1], &f2, sizeof(char) * 64);
    bind_p->T[1] = 1;   /* EXTERNAL_PROC_VARCHAR2 */

    EXEC SQL OPEN C1 USING DESCRIPTOR bind_p;   
    EXEC SQL DESCRIBE SELECT LIST for S INTO select_p;

    set_select_v();
    printf("f1\t\tf2\n");   
    printf("----------------------------------------------------------\n");   
    for(;;)   
    {    
        EXEC SQL WHENEVER NOT FOUND DO break;   
        EXEC SQL FETCH C1 USING DESCRIPTOR select_p;  

        for(i = 0;i<select_p->F;i++){   
            printf("%s ",select_p->V[i]);   
        }   
        printf("\n");  
    }   
    free_da();   
    EXEC SQL CLOSE C1;   
    printf("\n-----------------------------------------------------\n");   
    alloc_descriptor(MAX_ITEMS,MAX_VNAME_LEN,MAX_INAME_LEN);   
    EXEC SQL PREPARE S from :sql_statement;   
    EXEC SQL DECLARE C CURSOR FOR S;    
    set_bind_v();  
    EXEC SQL OPEN C USING DESCRIPTOR bind_p;   
    EXEC SQL DESCRIBE SELECT LIST for S INTO select_p;  
    set_select_v();   
    EXEC SQL WHENEVER NOT FOUND DO break; 
    for (;;) {        
        EXEC SQL FETCH C USING DESCRIPTOR select_p; 
        for(i = 0;i<select_p->F;i++){
            printf("%s ",select_p->V[i]);   
        }
        printf("\n");
    }
    free_da();   
    EXEC SQL CLOSE C;          
    EXEC SQL DROP TABLE TEST_ORA;
    EXEC SQL COMMIT WORK RELEASE;   
    exit(0);   
}   
//分配描述符空间:
int alloc_descriptor(int size,int max_vname_len,int max_iname_len)   
{   
    if((bind_p=sqlald(size,max_vname_len,max_iname_len))==(SQLDA*)0)   
    {   
        printf("can't allocate memory for bind_p.");   
        return -1;   
    }   

    if((select_p=sqlald(size,max_vname_len,max_iname_len))==(SQLDA*)0)   
    {   
        printf("can't allocate memory for select_p.");   
        return -1;   
    }   

    return 0;   
}   
//绑定变量的设置:
void set_bind_v()   
{   
    unsigned int i;   
    EXEC SQL WHENEVER SQLERROR DO sql_error("<ERROR>");   
    bind_p ->N = MAX_ITEMS;   
    EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_p; 
 
    if(bind_p->F<0)   
    {   
        printf("Too Many bind varibles");   
        return;   
    }   
    bind_p->N = bind_p->F;   
    for(i=0;i<bind_p->N;i++)   
    {
        bind_p->T[i] = 1;   
    }   
}

//选择列处理
void set_select_v()   
{   
    unsigned int i;
    int null_ok,precision,scale;   
    EXEC SQL DESCRIBE SELECT LIST for S INTO select_p;  

    if(select_p->F<0)   
    {   
        printf("Too Many column varibles");   
        return;   
    }   
    select_p->N = select_p->F;   
    //对格式作处理
    for(i = 0;i<select_p->N;i++)   
    {   
        sqlnul((short unsigned int*)&(select_p->T[i]), (short unsigned int*)&(select_p->T[i]), &null_ok);//检查类型是否为空
        switch (select_p->T[i])
        {   
            case 1://VARCHAR2   
                break;   
            case 2://NUMBER   
                sqlprc(&(select_p->L[i]), &precision, &scale);   
                if (precision == 0)   
                    precision = 40;   
                select_p->L[i] = precision + 2;  
                break;   
            case 8://LONG   
                    select_p->L[i] = 240;   
                    break;   
            case 11://ROWID   
                    select_p->L[i] = 18;   
                    break;   
            case 12://DATE   
                    select_p->L[i] = 9;   
                    break;   
            case 23://RAW   
                    break;   
            case 24://LONGRAW   
                    select_p->L[i] = 240;   
                break;   
        }   
        select_p->V[i] = (char *)realloc(select_p->V[i], select_p->L[i]+1);   
        select_p->V[i][select_p->L[i]] ='\0';//加上终止符
        select_p->T[i] = 1;//把所有类型转换为字符型
    }   
} 
//释放内存SQLDA的函数:
void free_da()   
{    
    sqlclu(bind_p);   
    sqlclu(select_p);   
}   

//错误处理
void sql_error(char *msg)   
{   
    printf("\n%s %s\n", msg,(char *)sqlca.sqlerrm.sqlerrmc);   
    EXEC SQL WHENEVER SQLERROR CONTINUE; 
    EXEC SQL ROLLBACK RELEASE;   
    exit(0);   
}  
  1. 创建数据库用户

    create user ecpg identified by 'Test@123';
  2. 创建测试表

    drop table if exists ecpg.test_ecpg_tab;
    create table ecpg.test_ecpg_tab as select id , ' name '||id name from generate_series(1,20) id;
Copyright © 2011-2025 www.enmotech.com All rights reserved.