Mini SQL API 와 웹 인터페이스

     

     

 

0.  시작하는 말.

    점점 날씨가 쌀쌀해 지는 것이 느껴진다. 나무 가지에는 무성하던 나뭇잎이 떨어져서 서서히 겨울을 준비하는 모습들이 보이는 것 같다. 여러분들도 겨울 준비가 되어 가는지 모르겠다.

    지난 시간에는 간단하게 RDBMS와 E-R Diagram에 대한 내용들을 예를 들어서 테이블과 같은 것들을 만들어 보았고, mSQL에서 지원되고 있는 SQL 문과 데이터베이스 시스템 유틸리티들을 알아 보았다. 아마도 지난번 시간의 예들이 현제 새로운 데이터 베이스를 작성하는데 많은 도움이 되리라고 생각된다.

    이번 시간에는 우리들이 가장 바라고 실무적인 것들을 다루어 보기로 한다. API(Application Programming Interface)는 대부분의 계발 툴이나 언어에서 지원하는 하나의 계발자를 위한 라이브러리로, 대부분의 계발자들은 API를 사용하여 프로그램을 만들어 내고 있다. 그밖에 mSQL에서는 Minerva 대학에서 계발한 ESL이라는 C언어와 비슷한 스크립트 언어를 가지고, 웹과 연동을 쉽게 할 수 있도록 지원하고 있다. W3-mSQL은 아주 편리한 WWW Interface 패키지로 초기에는 따로 웹 사이트를 찾아서 받아 사용해야 했지만, 2.0.0버전부터는 함께 계발이 되고 있는 상황으로 보다 쉬운 웹 인터페이스 접근을 할 수 있다.

    우리의 최대의 강적(?)인 Windows 계열에서의 웹 인터페이스에 대한 비교를 하지 않을 수 없을 것이다. 여러분들이 주목해야 할 점은 Windows NT와 IIS 서버에서 지원하고 있는 ISAPI, VBScript와 같이 아주 쉬운 방법들을 여러분들은 알고 있을 것이다. 그러나 그것 보다도 더 쉽고 유지 보수가 쉽다는 점이다. 거의 Perl 스크립트와 같은 문장을 사용하기만 해도 웹페이지를 쉽게 접근할 수 잇다. 이제 여러분들도 구미가 당길 때도 된 듯 싶다.

 

1.  mSQL API

    여러분들은 몇 가지의 기본적인 API를 접하게 될 것이다. mSQL API는 초기에 매우 기초적인 것으로 시작을 하고 있으나, 최근 2.0.0버전에서는 매우 강력하게 지원이 되고 있는 것을 쉽게 알 수 있다. 가장 중요한 서버 제어에 관한 API가 추가 된 것이다. 하지만, 이러한 것 보다도 더 중요한 것이 바로 기본적인 데이터베이스와 접속하는 것이다. 데이터 베이스와의 접속 그리고 상호 통신에 관한 간단한 API부터 시작해 보도록 하자.

    mSQL 데이터베이스 배포판에는 데이터베이스와의 통신을 위한 API 라이브러리라는 것을 함께 배포한다. 여러분들이 mSQL데이터베이스를 설치했다면, Hughes 디렉토리에 lib와 Include 디렉토리가 존재할 것이다. 다음과 같은 파일이 존재하는지를 확인해 보자. 존재하지 않는다면, 다시 mSQL을 설치해야 한다. 필자는 mSQL라이브러리와 헤더 파일을 /usr/local/lib, /usr/local/include 등의 디렉토리에 하이퍼 링크를 시켜서 사용하고 있다.

    <그림1>  라이브러리 파일.

    <그림2>  Mini SQL데이터베이스 관련 헤더 파일들.

    위와 같이 모든 파일들이 잘 있다면, 시작해도 좋다. 다음은 우리가 알아야 할 함수들을 Hughes에서 정의 해놓은 것들이다. 여러분들은 이들 중에서 일부만 맛을 보게 될 것 같다.

      /*
      **      msql.h  -
      **
      **
      ** Copyright (c) 1993-95  David J. Hughes
      ** Copyright (c) 1995  Hughes Technologies Pty Ltd
      **
      ** Permission to use, copy, and distribute for non-commercial purposes,
      ** is hereby granted without fee, providing that the above copyright
      ** notice appear in all copies and that both the copyright notice and this
      ** permission notice appear in supporting documentation.
      **
      ** This software is provided "as is" without any expressed or implied warranty.
      **
      ** ID = "$Id:"
      **
      */

      // 다음 부분은 새로 추가된 부분으로 텀파일이 잘 안될 경우에 해주면 된다.
      #include <time.h>
      // 삽입 끝..

      #ifndef MSQL_H
      #define MSQL_H

      #if defined(__STDC__) || defined(__cplusplus)
      #  define __ANSI_PROTO(x)       x
      #else
      #  define __ANSI_PROTO(x)       ()
      #endif

      #ifdef __cplusplus
      extern "C" {
      #endif

      typedef char    ** m_row;

      typedef struct field_s {
              char    *name,
                      *table;
              int     type,
                      length,
                      flags;
      } m_field;

      typedef struct  m_seq_s {
              int     step,
                      value;
      } m_seq;

      typedef struct  m_data_s {
              int     width;
              m_row   data;
              struct  m_data_s *next;
      } m_data;

      typedef struct m_fdata_s {
              m_field field;
              struct m_fdata_s *next;
      } m_fdata;

      typedef struct result_s {
              m_data  *queryData,
                      *cursor;
              m_fdata *fieldData,
                      *fieldCursor;
              int     numRows,
                      numFields;
      } m_result;

      #define msqlNumRows(res) res->numRows
      #define msqlNumFields(res) res->numFields

      #define INT_TYPE        1
      #define CHAR_TYPE       2
      #define REAL_TYPE       3
      #define IDENT_TYPE      4
      #define NULL_TYPE       5
      #define TEXT_TYPE       6
      #define DATE_TYPE       7
      #define UINT_TYPE       8
      #define MONEY_TYPE      9
      #define TIME_TYPE       10
      #define LAST_REAL_TYPE  10
      #define IDX_TYPE        253
      #define SYSVAR_TYPE     254
      #define ANY_TYPE        255

      #define NOT_NULL_FLAG   1
      #define UNIQUE_FLAG     2

      #define IS_UNIQUE(n)    (n & UNIQUE_FLAG)
      #define IS_NOT_NULL(n)  (n & NOT_NULL_FLAG)

      static char msqlTypeNames[][12] =
              {"???", "int", "char","real","ident","null","text","date","uint",
              "money","time","???"};

      /*
      ** Pre-declarations for the API library functions
      ** 본격적으로 API 함수가 정의되는 부분입니다.
      */
      #ifndef _MSQL_SERVER_SOURCE
              extern  char msqlErrMsg[];
              int     msqlConnect __ANSI_PROTO((char *));
              int     msqlSelectDB __ANSI_PROTO((int, char*));
              int     msqlQuery __ANSI_PROTO((int, char*));
              int     msqlCreateDB __ANSI_PROTO((int, char*));
              int     msqlDropDB __ANSI_PROTO((int, char*));
              int     msqlShutdown __ANSI_PROTO((int));
              int     msqlGetProtoInfo __ANSI_PROTO((void));
              int     msqlReloadAcls __ANSI_PROTO((int));
              int     msqlGetServerStats __ANSI_PROTO((int));
              char    *msqlGetServerInfo __ANSI_PROTO((void));
              char    *msqlGetHostInfo __ANSI_PROTO((void));
              char    *msqlUnixTimeToDat

      e __ANSI_PROTO((time_t));
              char    *msqlUnixTimeToTime __ANSI_PROTO((time_t));
              void    msqlClose __ANSI_PROTO((int));
              void    msqlDataSeek __ANSI_PROTO((m_result*, int));
              void    msqlFieldSeek __ANSI_PROTO((m_result*, int));
              void    msqlFreeResult __ANSI_PROTO((m_result*));
                 m_row   msqlFetchRow __ANSI_PROTO((m_result*));
              m_seq   *msqlGetSequenceInfo __ANSI_PROTO((int, char*));
              m_field *msqlFetchField __ANSI_PROTO((m_result *));
              m_result *msqlListDBs __ANSI_PROTO((int));
              m_result *msqlListTables __ANSI_PROTO((int));
              m_result *msqlListFields __ANSI_PROTO((int, char*));
              m_result *msqlListIndex __ANSI_PROTO((int, char*, char*));
              m_result *msqlStoreResult __ANSI_PROTO((void));
              time_t  msqlDateToUnixTime

      __ANSI_PROTO((char *));
              time_t  msqlTimeToUnixTime __ANSI_PROTO((char *));
      #endif

      int     msqlLoadConfigFile __ANSI_PROTO((char *));
      int     msqlGetIntConf __ANSI_PROTO((char *, char *));
      char    *msqlGetCharConf __ANSI_PROTO((char *, char*));

      #ifdef __cplusplus
              }
      #endif
      #endif /*MSQL_H*/

    <리스트1> msql.h 헤더 파일 리스트.

    모든 함수들을 모두 알아야 할 필요는 없으며, 데이터 베이스와 통신을 하는데 있어서 거의 필요하지 않는 함수들이 있다. 우리는 가장 많이 사용되고 있는 함수들을 알아 볼 것이다. 또한 mSQL DBMS 초보자들이 쉽게 놓칠 수 있는 숨어 있는 작은 소스들을 서버에서 찾아서 함께 분석하도록 한다. 필자가 특정 API를 공부할 때 먼저 하는 것은 헤더 파일을 먼저 보는 것이다. API헤더 파일을 먼저 보는 이유는 쉽게 데이터 베이스에 어떤 API가 있으며, 어떤 기능을 할 수 있을 것이라고 하는 것들을 추측할 수 있기 때문이다.

    다음은 mSQL API로 데이터베이스 관련 프로그램을 작성할 때 기본적으로 사용되는 함수들을 순서대로 나열하였다. 대충의 함수 사용 순서는 다음과 같다.

      #include <stdio.h>
      #include <string.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include "msql.h"

      int main(argc,argv)
      int     argc;
      char    *argv[];
      {
              // 소켓 식별자를 선언 한다.
      int sock;

      // 데이터 베이스로부터 퀘리의 결과를 저장하는 곳을 선언한다.
      // 헤더 파일에 m_result형을 선언해 놓았으니 참고하기 바란다.
              m_result *rs;

              // Mini SQL과 접속을 하는 부분, 에러가 나면, 에러 메시지 출력.
              if ((sock = msqlConnect(NULL)) < 0)  {
                      printf("Couldn't connect to engine!\n%s\n\n", msqlErrMsg);
                      perror("");
                      exit(1);
              }

              // Mini SQL에서 관리하는 데이터 베이스에서 선택하여 접속한다.
              if (msqlSelectDB(sock,"데이터베이스명") < 0)    {
                      printf("데이터베이스와 접속할 수 없습니다, %s\n",,msqlErrMsg);
              }

              // 쿼리를 실행한다.
              msqlQuery(sock,qbuf)

              // 데이터를 m_result형에 저장한다.(배열에 저장한다고 생각하면 편하다)
              rs=msqlstoreResult()

              // 저장된 데이터 들을 적당한 처리를 해주어서 프로그램의 목적을 달성한다.

              // 데이터들을 자유롭게 해제시켜 준다.
              MsqlFreeresult(rs);

              // 데이터 베이스와 접속을 해제한다.
              msqlClose(sock);
              exit(0);
      }

    <리스트3> 기본적인 프로그래밍 골격.

    다음은 함수에 대한 세부적인 내용을 다루도록 해보자. 대충 어떻게 사용되는지는 쉽게 알  있으리라 생각되지만, 각각의 함수들의 기능들과 옵션들을 상세하게 알아 보자. 대부분의 API들은 msql이라는 단어가 함수의 맨 앞에 붙어서 다른 함수들과의 구분을 해준다. 또한 모든 함수들을 사용하는데 있어서 발생하는 모든 에러들은 MsqlErrMsg[]라는 배열에 저장이 되므로 에러 제어를 하려고 할때에 에러를 표시하게 위하여 Postgres95에서 사용하는 플래그 체크는 할 필요가 없다. 단지 함수의 명령 수행의 성공 및 실패의 여부만 if문으로 판단하여 그에 대응되는 메시지를 출력해주기만 하면 된다. 대부분의 함수들은 다음과 같은 상태 값을 return해준다.

            Flag 값 ---------  성공   0
                        |
                        |
                        |
                        |
                       ---------  실패   -1

1-1. 데이터베이스 접속 및 쿼리와 관련된 함수

    * int msqlConnect (char *host)
    이 함수는 MSQL 데이터 베이스 엔진과의 접속을 형성하는 역할을 하며, 프로그램의 초기에 이 함수를 사용하는 경우가 많다. 기본값으로 호스트 명이 NULL인 경우에는 Localhost로 간주하여 접속하게 된다. 이 함수는 위의 간단한 예제에서 보았듯이 sock이라는 변수에 socket descriptor를 넘겨준다. 따라서 이 함수를 사용할 때에는 위의 예제와 같이 사용해야 한다. 이 값은 데이터베이스와 연결하여 계속적으로 함수에 이 값을 넣어야 한다.

    * int msqlSelectDB(int sockdes, char *dbname)
    MsqlConnect가 성공적으로 이루어졌다면, 다음은 해당 서버에서 관리를 하고 있는 데이터 베이스 중에 접근하려는 데이터베이스를 선택하도록 하는 함수로 Mini SQL 2.0 버전 부터는 다중으로 여러 개의 데이터베이스를 접근하도록 한다. sock은 데이터베이스와 접속을 하기 위하여 사용했던 msqlconnect()함수가 리턴하는 sockdes값을 넣어 주면 된다. 그리고 dbname에서는 문자형으로 데이터 베이스의 이름을 넣어 준다.

    * int msqlCreateDB (int sockdes, Char *dbname)
    데이터베이스를 생성하는데 마치 지난 달의 데이터베이스 유틸리티에서 msqladmin과같은 역할을 하는 것이다. 사용자들이 이 함수를 사용하는 경우는 거의 드물며, 데이터베이스 유틸리티를 만들려고 하지 않는 한 사용하지 않는 함수이다.

    * int msqlDropDB (Int sockdes, char *dbname)
    데이터베이스를 모두 지우기 위한 함수로 msqladmin에서 사용되는 함수이다.

    * int msqlQuery (int sockdes, char *query)
    데이터베이스에 접근하여 아주 자주 사용되고, 중요한 함수로 프로그래머가 만든 쿼리를 직접 데이터 베이스로 전송하여 주는 함수이다. 이 함수를 사용하여 프로그래머가 원하는 데이터들을 여러 가지 쿼리문에 조건(where, order by)을 이용하여 얻어낼 수 있다. query라는 변수는 주로 malloc이나 배열로 선언되어서 그 문자열 내에 SELECT, UPDATE, DELETE 등의 쿼리문들이 삽입이 되기 때문에 충분한 양의 메모리를 할당해주는 것이 좋고, 그렇게 하지 않는다면, 직접 쿼리문을 입력할 수 있다. 이 함수가 실행이 되면, MsqlStroreResult() 함수를 실행하여 프로그램에 선언한 m_result형 rs 변수에 저장하도록 해야 실제로 우리가 쉽게 쓸수 있는 배열의 형태가 된다. m_result형은 구조가 약간 복잡하기는 하지만, 데이터베이스 구조를 알려고 하지 않는 한 이 자료형에 대하여 너무 자세하게 알려고 하거나, 어렵게 생각하지 않아도 된다. 프로그래머가 사용하는 방법에 대하여서는 아주 쉽게 사용할 수 있도록 되어 있다.

    * m_result *msqlStoreResult ()
    이 함수는 앞에서도 말했듯이 msqlQuery()함수와 함께 사용되어져서 그 쿼리의 결과 값을 m_result 형의 rs 변수에 저장하도록 하는 함수이다. 유감스럽게도 중요한 역할을 하지만, 특별한 변수를 가지지 않는 함수이다.

    * Void msqlFreeResult(m_result  *rs)
    더 이상 쿼리를 한 m_result 변수형으로 선언한 rs 변수 결과값이 필요하지 않다면, rs값을 인수로 넣어 주면 결과 자유롭게 만들어 주는 역할을 한다.

    * void msqlDataSeek(m_result *rs, int pos),
      void msqlFieldSeek(m_result *rs, int pos)

    m_result 구조체에는 데이터의 다음의 row 데이터 값에 대한 정보를 가지는 client측의 커서라는 것이 포함한다.  MsqlDataseek 함수를 호출하면 데이터의 위치 정보를 이동할 수 있다. pos의 처음 시작 값은 0으로 시작하고, 퀘리로 선택되어진 데이터중 맨 끝에 있는 데이터를 얻기를 원할 때에는 msqlNumrows() 함수로 선택되오진 데이터의 개수를 얻은 후에 -1값을 해주어 pos 값에 넘겨 주고, msqlFetchrows() 나 msqlFetchfield()함수를 사용하면 얻을 수 있다.

     

    // 행 단위 데이터를 얻는다.
    m_row mrow;
    // 열단위의 데이터를 얻는다.
    m_field mfield;
              for(int i; I<= msqlNumRows(rs); i++) {
                          mrow=msqlFetchRows(rs);

     

      각의 행에 대하여 매번 한번씩 접근을 한다.
      마찬가지로 m_field형도 같은 방법을 사용한다.

                      ▼    ▼    ▼

      번    호

      이    름

      나   이

      mrow[0]

      mrow[1]

      mrow[2]

      mrow[0]

      mrow[1]

      mrow[2]

      :

      :

      :

    }

     

    * m_row msqlFetchRow(m_result *rs) ,
      m_field *msqlFetchField(m_result *rs)

    두 함수는 실제로 데이터를 저장하는 함수로 m_row형으로 저장하여 프로그래머가 검색된 데이터의 row열에 대하여 쉽게 접근이 가능하도록 한다. 거의 모든 함수들이 이 함수를 이용하여 데이터를 받아 들이는데 사용된다. 물론 msqlFetchField() 함수를 사용하려면, m_field형으로 선언한 변수가 있어야 한다.

    * int msqlNumRows(m_result *rs),
      int msqlNumFields(m_result *rs)

    msqlQuery()를 실행하고나서 result 값을 rs에 저장하였다면, 우리는 몇 개의 데이터들이 현제 쿼리로 선택되었는지 알아야 할때가 아주 많아 특히 비비에스 게시판이나, 검색을 원할 때에는 몇번의 루프를 돌려야 할지 모를때에 아주 유용하게 사용된다. rows와 Field는 쉽게 알 수 있듯이 row는 row의 개수를 field함수는 field의 개수를 출력하여 준다.

    * void  msqlClose(int sock)
    mSQL 데이터베이스 엔진과의 모든 통신을 종료하기 위하여 이 함수를 사용한다. 대부분의 경우에는 프로그램의 맨 마지막에서 이 함수를 사용한다.

1-2. 데이터 베이스 스키마와 관련된 함수

    다음은 데이터 베이스의 스키마와 관련된 함수들을 소개할 것이다. 여러분들은  msqladmin이라는 프로그램을 이용하여 데이터베이스의 스키마를 설계할 수 있다.  그리고 Relshow, msqldump라는 프로그램을 이용하여 데이터 베이스 스키마을 볼 것이다. 이렇게해서 데이터 베이스의 스키마를 보는 방법도 있겠지만, 좀더 본인의 취향에 맞게 이러한 프로그램들을 계발할 수 있다. 다음의 함수들을 그것을 만드는데 아주 유용하게 사용될 것 같다.

    * m_result *msqlListDBs(Int sock)
    mSQL 데이터베이스 엔진에서 관리하고 있는 데이터 베이스의 모든 List를 보여준다. 이에 대한 데이터들은 m_result 형으로 저장이 되는데,  이전에 알아보았던 msqlfetchrow()함수를 이용하여 데이터 베이스들의 이름들을 알아낼 수 있다.

    * m_result *msqlListTables(int sock)
    msqlListDBs() 함수와 마찬가지로 데이터베이스 테이블을 m_result형으로 저장한다.

    * m_result *msqlListFields (int sock, char *tableNAME)
    특정 데이터베이스의 테이블에서의 모든 필드에 대한 정보를 저장한다.

    * m_result *msqlListIndex(int sock, char tableNAME, char *index)
    테이블중에서 index 테이블의 구조를 알 수 있게 한다.

    mSQL API에 대하여 자세하게 설명만을 더한다는 것은 왠(~)지 실무적인 것 같지 않아 필자를 싫어한다. 실제적인 예제를 주로 다루는 것이 필자의 스타일이다. 다음은 실제로 컴파일이 가능하도록 만들어진 간단한 프로그램 예제이다. 아래의 프로그램 예제를 보기 전에 컴파일하는 방법을 먼저 알아야 될 것 같다.

1-3. 컴파일하는 방법

    컴파일하는 방법 여러분이 Gcc와 같은 성능 좋고, 편리하며, 뛰어난 컴파일러가 있다면 모두 가능하다.

    컴파일하는 방법은 여러 가지가 될 수 있다. 자신의 시스템에 맞게 다음을 고치면 대부분 성공적으로 컴파일을 할 수 있으리라 생각한다.

     

    cc -c -I/usr/local/Hughes/include msqlapp.c
    cc -c msqlapp msqlapp.c -L/usr/local/Hughes/lib -lmsql
    cc -c msqlapp msqlapp.c -L/usr/local/Hughes/lib -I/usr/local/Hughes/include -lmsql -lnsl -lsocket

      

      /*
        mSQL 2.0 release C API demonstration1
        Programmed by Kyung-Ho, Kim
      */

      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      #include "msql.h"

      void printErrMsg(void);

      void main(void)
      {
        char key, *hostname;
        int sockdes, i, usn;
        m_result *rs;
        m_row mrow;

        printf("mSQL 2.0 접속 프로그램\n");
        printf("접속할 mSQL 2.0 서버를 입력하세요\n");
        hostname=malloc(30 * sizeof(char));
        gets(hostname);

        printf("지금 <%s>에 접속을 시도합니다.\n", hostname);
        sockdes=msqlConnect(hostname);
        printf("소켓 번호 : %d  번으로 연결에 성공했읍니다.\n", sockdes);

        msqlClose(sockdes);
        printf("접속을 종료했읍니다. 여기가 프로그램의 끝입니다\n");

        free(hostname);
      }

      void printErrMsg(void)
      {
        puts("mSQL 2.0 rel : 에러가 발생했읍니다.\n");
        puts(msqlErrMsg);
      }

    <예제1> msqlapp.c 프로그램 예제

    쉽게 보아서는 데이터 베이스에 접근하여 아무 일도 하지 않고 접속을 종료한다는 것을 알 수 있을 것이다. 다음은 여러분들의 데이터 베이스 소스 디렉토리를 뒤질 차례이다. 다음과 같은 경로에 서버와 관련이 되어 있는 소스와 관련이 없는 몇 개의 소스를 볼 수 있다. Insert_test.c 파일과 select_test.c 파일을 쉽게 찾을 수 있으며, sample.msql 파일도 함께 찾을 수 있다. 이 파일들은 여러분들을 위한 파일이다. 두개의 파일 대하여 여러분들이 직접 소스를 분석해 보는 것도 많은 도움이 될 것이다.

    <그림 3> mSQL 서버 디렉토리

    여러분들은 sample.msql을 먼저 데이터베이스로 만들어야 한다. 그리고 2개의 테스트 프로그램을 컴파일하여 실행해 보기 바란다. 그리고 relshow.c, msqladmin.c, msqldump.c msqlimport.c msqlexport.c 등등의 파일도 함께 보이는데 이러한 파일들을 분석하는 것이 가장 좋은 방법이다.

 

2.  mSQL Tool Version 0.7

    이제는 간단한 예제를 다루어 보았으니 몸을 풀 시간이 된 것 같다. 금년 여름에 만들었던 프로그램을 한번 소개해 볼까한다. 그래픽 함수도 사용되지 않았으며, 여러분들이 쉽게 고쳐서 다시 새롭게 만들기도 쉬울 것으로 보이고, 전체적인 mSQL API 동작을 알 수 있으리라 생각된다. 잘 만들어 졌다고 이 프로그램을 소개하는 것이 아니라는 것을 알아 주었으면 한다.  필자는 여러 개의 데이터 베이스를 사용하고 있는데 때때로 데이터 베이스 스키마를 보고 싶거나 데이터들이 잘 들어 갔는지를 알아 보고 싶을 때가 있다. 이때 불편한 것을 다른 사람이 만든 툴을 이용하지 않고, 직접 만들었다는 것 뿐이다.

    다음은 프로그램을 시행하면, 데이터 베이스의 접속을 원하는 호스트의 이름을 넣기를 원한다.

    <그림 3> msqltool 0.7 실행

    여기에 mSQL 데이터 베이스 엔진이 동작하는 워크스테이션이나 리눅스 서버의 IP나 Domain name 을 입력하면 바로 접속을 한다. 만약 아무 입력도 하지 않고 Enter를 하면, localhost로 접속하도록 했다.  이 프로그램의 주요 부분을 살펴보면 다음과 같다.

1. 데이터 베이스 서버에 접속하기

    //
    // msqlConnect() 함수를 사용해서 mSQL 2.0과의 접속 여부 확인
    //
    printf("지금 <%s>에 접속을 시도합니다.\n", hostname);
    if((sockdes=msqlConnect(hostname)) < 0){
        printErrMsg();
        printf("연결에 실패했읍니다. %s 서버에 mSQL 2.0 DBMS가 동작하는지 확인하세요.\n",             hostname);
        msqlClose(sockdes);
        exit(1);
      }

2. 접속한 호스트의 모든 데이터 베이스들을 알아내기

    do{
        //
        // 접속된 데이타베이스시스템의 모든 데이타 베이스를 알아낸다
        //
        printf("\n지금 %s 에서 관리하고 있는 데이터 베이스입니다. \n", hostname);
        if(!(rs=msqlListDBs(sockdes))){
          printErrMsg();
          printf("관리하고 있는 데이타 베이스가 없는 것 같읍니다.\n\n");
          exit(1);
        }
        else{
          totaldb=msqlNumRows(rs);
          printf("현제 총 %d 개의 데이타 베이스를 관리하고 있읍니다. \n", totaldb);
          // 각 데이타 베이스의 이름을 얻는다.
          for(counter=0; counter<totaldb; counter++){
            mrow=msqlFetchRow(rs);
            dblist[counter]=*mrow;
          }

3. 데이터 베이스의 테이블 스키마 출력

    if(utb!=0){
                 //
                 // 테이블내에 필드 정보를 알아낸다.
                 //
                 printf("입력하신 숫자는 %d 번입니다. \n", utb);
                 strcpy(tbname, tblist[utb-1]);
                 printf("지금 %s 테이블을 접근 중 입니다.\n\n", tbname);
                 if(!(rs=msqlListFields(sockdes, tbname))){
                   printErrMsg();
                   exit(1);
                 }
                 else{
                   counter=0;
                   printf("\n");

    printf("-----------------------------------------------\n");
                   printf("  현제 접근 중인 데이타 베이스 : %s\n", dbname);
                   printf("  현제 접근 중인 테이블        : %s\n", tbname);

    printf("-----------------------------------------------\n");
                   printf("  번 호   필드이름   필드형   필드길이   필수키   Unique 인덱스 \n");

    printf("------------------------------------------------\n");
                   while((mfield=msqlFetchField(rs))){
                   printf(" %3d   %-13.13s %-7.7s   %6d    %s\n", counter+1, mfield->name,\  fieldtype(mfield->type), mfield->length, keytype(mfield->type, mfield->flags));
                     counter++;
                   }
    printf("------------------------------------------------\n");
                   }
                   printf("엔터키를 입력하시면 데이타 베이스 메뉴로 돌아 갑니다...\n\n");
                        }
                else
                        break;
                   }while(tolower(getc(stdin))!= 0x1C);
    }

    각 부분별로 특별히 설명할 것은 없을 것 같다. 독자 여러분들이 API의 설명을 보면서 동작이 어떻게 되는지 쉽게 알 수 있다.

    <그림 4> msqltool에서 데이터 베이스의 테이블 스키마

 

4. Full Source Code List

    /*
      mSQL 2.0 release Database Schema Browser.
      Msqltools  version 0.7
      Programmed by Kyung-Ho, Kim

      GNU General Public License.
      Copyrighted by GPL.
      This Program is FREE
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <ctype.h>
    #include "msql.h"
    #define  DEBUG     printf("여기까지는 에러가 나지 않았읍니다\n");

    extern int errno;
    void printErrMsg(void);
    char *fieldtype(int type);
    char *keytype(int type, int flag);

    //
    // 에러가 발생하면 에러를 출력한다.
    //
    void printErrMsg(void)
    {
      char *buffer;
      buffer = (char *)malloc( 30 * sizeof(char));
      puts("--------------------------------------------------------\n");
      puts("mSQL 2.0 rel : 에러가 발생했읍니다.\n");
      puts(msqlErrMsg);
      sprintf(buffer, "에러 발생");
      perror(buffer);
      puts("--------------------------------------------------------\n");
      free(buffer);
    }

    char *fieldtype(int type)
    {
      char *buffer;

      buffer=(char *)malloc(10*sizeof(char));

      switch(type){
      case INT_TYPE:
        strcpy(buffer, "정수형");
        break;
      case UINT_TYPE:
        strcpy(buffer, "배정수형");
        break;
      case DATE_TYPE:
        strcpy(buffer, "날  짜");
        break;
      case TIME_TYPE:
        strcpy(buffer, "시  간");
        break;
      case MONEY_TYPE:
        strcpy(buffer, "  돈  ");
        break;
      case CHAR_TYPE:
        strcpy(buffer,"문  자");
        break;
      case TEXT_TYPE:
        strcpy(buffer, "텍스트");
        break;
      case REAL_TYPE:
        strcpy(buffer, "실  수");
        break;
      case IDX_TYPE:
        strcpy(buffer,"인덱스");
        break;
      default:
        strcpy(buffer, "없  음");
        break;
      }
      return b

    uffer;
      free(buffer);
    }

    char *keytype(int type, int flag)
    {
      char *buffer;

      buffer=(char *)malloc(17*sizeof(char));

      if(type !=IDX_TYPE)
        sprintf(buffer, "%s", IS_NOT_NULL(flag)? " Y           N/A" : " N           N/A");
      else
        sprintf(buffer, "%s", IS_UNIQUE(flag)?   "N/A           Y " : "N/A           N ");

      return buffer;
    }


    void main(void)
    {

      char *hostname, dbname[20], tbname[20];
      int sockdes, counter, udb=1, utb, totaldb, totaltb, totalfd;

      m_result *rs;
      m_row mrow;
      m_field *mfield;
      char **mmrow, **dblist, **tblist;


      //
      // 프로그램 시작-접속할 서버의 주소를 입력 받는다.
      //
      printf("------------------------------------------------------\n");
      printf("         mSQL 2.0 브라우저 v.0.7 -- 제작자: 김.경호(zesus:Hitel)\n");
      printf("------------------------------------------------------\n");
      printf("접속할 mSQL 2.0 서버를 입력하세요\n");
      hostname=malloc(30 * sizeof(char));
      printf("원격 호스트 (Enter=localhost): ");
      gets(hostname);
      //scanf("%s",hostname);
      //getchar();
      if(strcmp(hostname,"\n")<0)
        strcpy(hostname,"localhost");

      //
      // msqlConnect() 함수를 사용해서 mSQL 2.0과의 접속 여부 확인
      //
      printf("지금 <%s>에 접속을 시도합니다.\n", hostname);
      if((sockdes=msqlConnect(hostname)) < 0){
        printErrMsg();
        printf("연결에 실패했읍니다. %s 서버에 mSQL 2.0 DBMS가 동작하는지 확인하세요.\n", hostname);
        msqlClose(sockdes);
        exit(1);
      }
      else{
        printf("소켓 번호 : %d  번으로 연결에 성공했읍니다.\n", sockdes);
        printf("%s : mSQL 2.0 서버에 성공적으로 접속을 했읍니다.\n", hostname);
        printf("----------------------------------------------------\n\n");
      }

      do{
        //
        // 접속된 데이타베이스시스템의 모든 데이타 베이스를 알아낸다
        //
        printf("\n지금 %s 에서 관리하고 있는 데이터 베이스 입니다.\n", hostname);
        if(!(rs=msqlListDBs(sockdes))){
          printErrMsg();
          printf("관리하고 있는 데이타 베이스가 없는 것 같읍니다.\n\n");
          exit(1);
        }
        else{
          totaldb=msqlNumRows(rs);
          printf("현제 총 %d 개의 데이타 베이스를 관리하고 있읍니다. \n", totaldb);
          // 각 데이타 베이스의 이름을 얻는다.
          for(counter=0; counter<totaldb; counter++){
            mrow=msqlFetchRow(rs);
            dblist[counter]=*mrow;
          }

          // 각 데이타 베이스의 이름을 출력한다.
          printf("----------------------------------------------------\n");
          printf("   번  호          데이타 베이스\n");
          printf("----------------------------------------------------\n");
          for(counter=0; counter<totaldb; counter++)
            printf("   %3d.            %-16.16s\n", counter+1, dblist[counter]);

          // 관리하고 있는 데이타 베이스를 사용자의 선택을 입력 받음.
          printf("------------------------------------------------------\n");
          printf("데이타 베이스를 선택하세요[번호,0(종료)] : ");
          scanf("%d",&udb);
          getc(stdin);
        }
        if(udb!=0){
        //
        // 사용자의 요구에 따라 선택된 데이타 베이스에 접근한다.
        //
        printf("입력하신 숫자는 %d 번입니다. \n\n", udb);
        strcpy(dbname, dblist[udb-1]);
        printf("%s 데이타 베이스를 접근합니다.\n", dbname);
        if(msqlSelectDB(sockdes, dbname)==-1)
          printErrMsg();
        else
          printf("%s 데이타 베이스에 성공적으로 접근했습니다.\n\n", dbname);

        do{
             //
             // 데이타 베이스내의 테이블 정보를 보여 준다.
             //
             if(!(rs=msqlListTables(sockdes))){
               printErrMsg();
               exit(1);
             }
             else{
               totaltb=msqlNumRows(rs);
               for(counter=0; counter < totaltb; counter++){
               mrow=msqlFetchRow(rs);
               tblist[counter]=*mrow;
             }
             printf("\n");
             printf("----------------------------------------------------\n");
             printf("  현제 접근 중인 데이타 베이스 : %s\n", dbname);
             printf("  존재하는 테이블의 수         : %d\n", totaltb);
             printf("----------------------------------------------------\n");
             printf("  번 호               테이블 이름\n");
           

      printf("--------------------------------------------------------\n");
             for(counter=0; counter<totaltb; counter++)
               printf("  %3d                 %s\n", counter+1, tblist[counter]);

             printf("-----------------------------------------------------\n");
             printf("접근할 테이블을 입력하세요[번호,0(전메뉴))] : ");
             scanf("%d", &utb);
             getchar();
           }
           

           if(utb!=0){
                 //
                 // 테이블내에 필드 정보를 알아낸다.
                 //
                 printf("입력하신 숫자는 %d 번입니다. \n", utb);
                 strcpy(tbname, tblist[utb-1]);
                 printf("지금 %s 테이블을 접근 중 입니다.\n\n", tbname);
                 if(!(rs=msqlListFields(sockdes, tbname))){
                   printErrMsg();
                   exit(1);
                 }
                 else{
                   counter=0;
                   printf("\n");
                   printf("-------------------------------------------------\n");
                   printf("  현제 접근 중인 데이타 베이스 : %s\n", dbname);
                   printf("  현제 접근 중인 테이블        : %s\n", tbname);
                   printf("-------------------------------------------------\n");
       

                printf("  번 호   필드 이름     필드형     필드길이     필수키    Unique 인덱스 \n");
                   printf("-------------------------------------------------\n");
                   while((mfield=msqlFetchField(rs))){
                     printf(" %3d  %-13.13s %-7.7s   %6d   %s\n", counter+1, mfield->name, \
                            fieldtype(mfield->type), mfield->length, keytype(mfield->type, mfield->flags));
                     counter++;
                   }
                   printf("-------------------------------------------------\n");
                 }
                 printf("엔터키를 입력하시면 데이타 베이스 메뉴로 돌아 갑니다...\n\n");
               }
               else
                 break;
            }while(tolower(getc(stdin))!= 0x1C);
        }
      }while(udb!=0);
      

      //
      // mSQL 2.0 데이타 베이스 시스템과의 접속을 종료한다.
      //
      msqlClose(sockdes);
      printf("%s : mSQL 2.0 서버에서 접속을 종료 했읍니다.\n", hostname);
      free(hostname);

      printf("\n\n");
      printf("-------------------------------------------------------\n"); 
      printf("       mSQL 2.0 브라우저 v0.7 을 사용해 주셔서 대단히 감사합니다.\n");
      printf("-------------------------------------------------------\n"); 
      printf("\n\n\n");
    }

 

3.  마치며..

    이 번호에서는 mSQL의 API에 대하여 알아 보았다. 많은 sample파일들을 실어 실제적으로 이해를 높이려고 했으나 부족한 면이 많았던 것으로 생각이 든다. 다음 호에서는 Web과 연동하는 간단한 예제를 보이기로 한다.




▲ top

home으로...