很多时候我们要把执行的SQL脚本以重定向的方式放在SQLPLUS中执行,并且这种脚本需要执行很长时间.例如:
sqlplus -s scott/tiger@orcl << !
delcare
v_count integer;
begin
loop
exit when v_count >= 100;
insert into xx values(v_count);
--and place other statement here
v_count = v_count + 1;
end;
/
commit
/
exit;
!
这样在执行的时候通过'ps -ef|grep sqlplus'就可以看到明文密码.存在安全隐患.
于是出现了另外一种办法,用sqlplus -s /nolog打开sqlplus然后在脚本中使用conn命令连接数据库.示例如下:
sqlplus -s /nolog << !
conn scott/tiger@orcl
delcare
v_count integer;
begin
loop
exit when v_count >= 100;
insert into xx values(v_count);
--and place other statement here
v_count = v_count + 1;
end;
/
commit
/
exit;
!
但是这样同样存在极大的安全隐患.
所以需要进行对连接数据库的部分进行封装,以下是我通过管道技术对sqlplus进行的封装,可以保证密码不可见
/*************************************
** 文件名:ConDB.c                                          *
** 作用: 被SHELL调用,执行输入重定向中SQL脚本       *
** 支持: 1.DDL、DML、DCL语句;                         *
**       2.SQL语句中应用SHELL变量                      *
** 作者: sunzx                                               *
** 时间: 2008-03-19                                       *
** 参考: APUE中关于管道的使用;                          *
**************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LEN   2048
int main (int argc,char *argv[])
{
    int n,fd[2];
    pid_t pid;
    char line[MAX_LEN];
    char conSql[100];
    strcpy(conSql,"conn scott/tiger@orcl
");
    if(pipe(fd) < 0)
    {
        fprintf(stderr,"pipe error
");
        exit (-1);
    }
    pid=fork();
    if (pid < 0)
        printf("error in fork!
");
    else if (pid == 0) /*child*/
    {
        close(fd[1]); /*close write end*/
        if(fd[0] != STDIN_FILENO)
        {
            if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
            {
                fprintf(stderr,"dup2 error to stdin
");
                exit (-1);
            }
            close(fd[0]); /*don not need this after dup2*/
        }
        if( execlp("sqlplus","sqlplus","-s","/nolog",(char *)0) < 0)
        {
            fprintf(stderr,"execlp error for sqlplus
");
            exit (-1);
        }
    }
    else              /*parent*/
    {
        close(fd[0]); /*close read end */
        write(fd[1],conSql,strlen(conSql));
        /* get data from stdin, and write into fd[1]*/
        while ( fgets(line,MAX_LEN, stdin) != NULL)
        {
            n = strlen(line);
            if( write(fd[1], line, n) != n)
            {
                fprintf(stderr,"write error to pipe
");
                exit (-1);
            }
        }
        if(ferror(stdin))
        {
            fprintf(stderr,"fgets error
");
            exit (-1);
        }
        close(fd[1]); /* close write end of pipe for reader */
        if(waitpid(pid,NULL,0) < 0)
        {
        }
        exit(0);
    }
}
在此C语言示例中,密码通过strcpy(conSql,"conn scott/tiger@orcl
");把用户名,密码,数据库实例名连接串拷贝到conSql中,再把conSql串发送给sqlplus子进程,这样就成功连接了数据库,剩下的工作交给fgets去得到SQL脚本吧.
这里稍做修改可以实现数据库连接串的配置化,方便操作.当然在父进程中还可以做一些其他工作,比如记录日志.等等.
该程序已经在AIX5.3下编译通过,编译器cc