* TeMeL_SQLParser * FreeWare by eMeL



Version 1.03


C++ Source:

//---------------------------------------------------------------------------
//
//  SQL select statement parser and rebuilder
//  --- ------ --------- ------ --- ---------
//
//  FREEWARE by eMeL (Laszlo Moravecz, Hungary) (Moravecz László)
//
//  Language compliance: ANSI C++
//
//  news and updates: www.emel.hu/freeware/sqlparser   
//
//---------------------------------------------------------------------------
//  History:
//
//    2003.07.08  1.00  First publication
//    2003.07.09  1.01  "select A, (select B from BTBL where ID=ATBL.ID), D from ATBL" result correct field list
//    2003.07.09  1.02  Add HAVING clause
//    2003.07.09  1.03  Add JoinTag (data pick out from TableTag)
//
//---------------------------------------------------------------------------

#include "eMeL_SQLParser.h"

#include <string.h>
#include <stdlib.h>

#define sizeofarray(array)      (sizeof(array) / sizeof(array[0]))

//

char * TeMeL_SQLParser::sTokens[6] = {"select", "from", "where", "group by", "having", "order by"};
char * TeMeL_SQLParser::sVersion   = "1.03";

char * TeMeL_SQLParser::sJoinTokens[10] = {"join", "inner join",
                                           "left outer join", "left join",
                                           "right outer join", "right join",
                                           "full outer join", "full join",
                                           "union join", "cross join"};

//---------------------------------------------------------------------------

TeMeL_SQLParser::TeMeL_SQLParser()
{
  SQLText = NULL;

  Reset();
}
//---------------------------------------------------------------------------

TeMeL_SQLParser::~TeMeL_SQLParser()
{
  free(SQLText);
}
//---------------------------------------------------------------------------

void TeMeL_SQLParser::Reset(void)
{
  FieldTag  = NULL;
  TableTag  = NULL;
  JoinTag   = NULL;
  WhereTag  = NULL;
  GroupTag  = NULL;
  HavingTag = NULL;
  OrderTag  = NULL;

  free(SQLText);
  SQLText  = NULL;
}
//---------------------------------------------------------------------------

bool TeMeL_SQLParser::isSeparator(char cChr)
{
  return strchr(" \t\r\n", cChr);
}
//---------------------------------------------------------------------------

char * TeMeL_SQLParser::CompareToken(char * sSrc, char * sToken)
{ // now only exact compare, but syntax may found "group   by" against "group by"
  size_t iTokenLen = strlen(sToken);

  //

  if (strncmpi(sSrc, sToken, iTokenLen) == 0)
  { // Compares a portion, without case sensitivity : same
    char * sEndPos = sSrc + iTokenLen;

    if (isSeparator(*sEndPos))
    {
      return sEndPos;                   // Return true & Skip positions to after of token
    }
  }

  //

  return NULL;
}
//---------------------------------------------------------------------------

bool TeMeL_SQLParser::NextTokenEnd(void)
{
  int iParentheses = 0;

  //

  while (*SQLTextPtr)
  {
    if (*SQLTextPtr == '(')
    {
      iParentheses++;
    }
    else if (*SQLTextPtr == ')')
    {
      iParentheses--;

      if (iParentheses < 0)
      {
        /* TODO : throw if .... */
      }
    }

    //
    
    if (isSeparator(*SQLTextPtr) && (iParentheses == 0))
    {
      for (int iLoop = iActToken + 1; (iLoop <= tokOrderBy); iLoop++)
      { // Check all tokens after last found
        char * sEndPos = CompareToken(SQLTextPtr + 1, sTokens[iLoop]);

        if (sEndPos)
        {
          *SQLTextPtr = 0;                  // EOS to last char of previous tokens's text-parameter area
          iActToken   = static_cast<tTokens>(iLoop);
          SQLTextPtr  = sEndPos;

          return true;
        }
      }
    }

    //

    SQLTextPtr++;
  }

  //

  return false;                   // if don't found any
}
//---------------------------------------------------------------------------

bool TeMeL_SQLParser::isValidSQL(void)
{
  return ! isBlankStr(FieldTag) && ! isBlankStr(TableTag);
}
//---------------------------------------------------------------------------

bool TeMeL_SQLParser::isBlankStr(char * sStr)
{
  if (sStr == NULL)
  {
    return true;
  }

  //

  while (*sStr)
  {
    if (strchr(" \t\r\n", *sStr) == NULL)
    {
      return false;
    }

    sStr++;
  }

  //

  return true;
}
//---------------------------------------------------------------------------

char * TeMeL_SQLParser::TrimBothEnd(char * sStr)
{
  if (sStr == NULL)
  {
    return sStr;
  }

  //

  char * sRet;

  while (*sStr)
  { // removes leading spaces
    if (strchr(" \t\r\n", *sStr))
    {
      *sStr = 0;
    }
    else
    {
      break;
    }

    sStr++;
  }

  sRet = sStr;

  //
  
  if (*sStr)
  {
    char * sEnd = strchr(sStr, 0);              // to EOS
    sEnd--;

    while (sEnd >= sStr)
    { // removes trailing spaces
      if (strchr(" \t\r\n", *sEnd))
      {
        *sEnd = 0;
      }
      else
      {
        break;
      }

      sEnd--;
    }
  }

  //

  return sRet;
}
//---------------------------------------------------------------------------

bool TeMeL_SQLParser::ParseSQLText(char * pSelectSmt)
{
  Reset();

  SQLText = static_cast<char *>(malloc(strlen(pSelectSmt) + 16));

  strcpy(SQLText, " ");                 // for <blank><token><blank> rule
  strcat(SQLText, pSelectSmt);

  //

  SQLTextPtr = SQLText;

  iActToken = tokStart;

  //

  while ((iActToken < tokOrderBy) && NextTokenEnd())
  { // If last token (Order By) found, it's OK too
    switch (iActToken)                  // iActToken set by NextTokenEnd()
    {
      case tokSelect:
        FieldTag = SQLTextPtr;
        break;

      case tokFrom:
        TableTag = SQLTextPtr;
        break;

      case tokWhere:
        WhereTag = SQLTextPtr;
        break;

      case tokGroupBy:
        GroupTag = SQLTextPtr;
        break;

      case tokHaving:
        HavingTag = SQLTextPtr;
        break;

      case tokOrderBy:
        OrderTag = SQLTextPtr;
        break;
    }
  }

  //

  JoinOpener();

  FieldTag  = TrimBothEnd(FieldTag);
  TableTag  = TrimBothEnd(TableTag);
  JoinTag   = TrimBothEnd(JoinTag);
  WhereTag  = TrimBothEnd(WhereTag);
  GroupTag  = TrimBothEnd(GroupTag);
  HavingTag = TrimBothEnd(HavingTag);
  OrderTag  = TrimBothEnd(OrderTag);

  //

  return isValidSQL();
}
//---------------------------------------------------------------------------

void TeMeL_SQLParser::JoinOpener(void)
{
  if (isBlankStr(TableTag))
  {
    JoinTag = NULL;
    return;
  }

  //

  bool iCommaState = false;

  char * sText = TableTag;

  //

  while (*sText)
  {
    if (*sText == ',')
    {
      iCommaState = true;
    }
    else if (! isSeparator(*sText))
    {
      iCommaState = false;
    }

    //

    if (isSeparator(*sText) && ! iCommaState)
    {
      for (int iLoop = 0; (iLoop < sizeofarray(sJoinTokens)); iLoop++)
      { // Check all tokens
        if (CompareToken(sText + 1, sJoinTokens[iLoop]))
        {
          *sText  = 0;                    // EOS after last real tablename (new EOS of TableTag)
          JoinTag = sText + 1;            // standalone join partition

          return;
        }
      }
    }

    //

    sText++;
  }
}
//---------------------------------------------------------------------------

char * TeMeL_SQLParser::BuildSQLText(void)
{
  if (! isValidSQL())
  {
    return NULL;
  }

  //

  SQLText = static_cast<char *>(realloc(SQLText, strlen(FieldTag) + strlen(TableTag) + 1024));

  strcpy(SQLText, "select ");
  strcat(SQLText, FieldTag);
  strcat(SQLText, "\n from ");
  strcat(SQLText, TableTag);


  if (! isBlankStr(JoinTag))
  {
    SQLText = static_cast<char *>(realloc(SQLText, strlen(SQLText) + strlen(JoinTag) + 64));
    strcat(SQLText, "\n ");
    strcat(SQLText, JoinTag);
  }


  if (! isBlankStr(WhereTag))
  {
    SQLText = static_cast<char *>(realloc(SQLText, strlen(SQLText) + strlen(WhereTag) + 64));
    strcat(SQLText, "\n where ");
    strcat(SQLText, WhereTag);
  }


  if (! isBlankStr(GroupTag))
  {
    SQLText = static_cast<char *>(realloc(SQLText, strlen(SQLText) + strlen(GroupTag) + 64));
    strcat(SQLText, "\n group by ");
    strcat(SQLText, GroupTag);

    if (! isBlankStr(HavingTag))
    {
      SQLText = static_cast<char *>(realloc(SQLText, strlen(SQLText) + strlen(HavingTag) + 64));
      strcat(SQLText, "\n   having ");
      strcat(SQLText, HavingTag);
    }
  }


  if (! isBlankStr(OrderTag))
  {
    SQLText = static_cast<char *>(realloc(SQLText, strlen(SQLText) + strlen(OrderTag) + 64));
    strcat(SQLText, "\n order by ");
    strcat(SQLText, OrderTag);
  }

  return SQLText;
}
//---------------------------------------------------------------------------


(c) FreeWare from eMeL Bt. (Moravecz László)