subsql.cpp

来自「FastDb是高效的内存数据库系统」· C++ 代码 · 共 3,037 行 · 第 1/5 页

CPP
3,037
字号
//-< SUBSQL.CPP >----------------------------------------------------*--------*
// FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
// (Main Memory Database Management System)                          *   /\|  *
//                                                                   *  /  \  *
//                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
//                          Last update: 10-Dec-98    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Interactive data manipulation language (subset of SQL)
//-------------------------------------------------------------------*--------*

#include <stdio.h>
#include <ctype.h>
#include "fastdb.h"
#include "compiler.h"
#include "wwwapi.h"
#include "subsql.h"
#include "symtab.h"
#include "hashtab.h"
#include "ttree.h"

#if THREADS_SUPPORTED
#include "server.h"
#endif

static char* typeMnem[] = {
                            "Boolean",
                            "Int1",
                            "Int2",
                            "Int4",
                            "Int8",
                            "Real4",
                            "Real8",
                            "String",
                            "Reference",
                            "Array",
                            "MethodBool",
                            "MethodInt1",
                            "MethodInt2",
                            "MethodInt4",
                            "MethodInt8",
                            "MethodReal4",
                            "MethodReal8",
                            "MethodString",
                            "MethodReference",
                            "Structure",
                            "RawBinary",
                            "StdString",
                            "Unknown"
                          };

char* dbSubSql::prompt = ">> ";


dbSubSql::dbSubSql(dbAccessType accessType)
    : dbDatabase(accessType)
{

  static struct
  {
    char* name;
    int   tag;
  }

  keywords[] = {
                 {"alter",   tkn_alter},
                 {"array",   tkn_array},
                 {"autocommit",   tkn_autocommit},
                 {"autoincrement",tkn_autoincrement},
                 {"backup",  tkn_backup},
                 {"bool",    tkn_bool},
                 {"commit",  tkn_commit},
                 {"compactify",tkn_compactify},
                 {"count",   tkn_count},
                 {"create",  tkn_create},
                 {"delete",  tkn_delete},
                 {"describe",tkn_describe},
                 {"drop",    tkn_drop},
                 {"exit",    tkn_exit},
                 {"export",  tkn_export},
                 {"hash",    tkn_hash},
                 {"help",    tkn_help},
                 {"http",    tkn_http},
                 {"import",  tkn_import},
                 {"index",   tkn_index},
                 {"int1",    tkn_int1},
                 {"int2",    tkn_int2},
                 {"int4",    tkn_int4},
                 {"int8",    tkn_int8},
                 {"inverse", tkn_inverse},
                 {"of",      tkn_of},
                 {"off",     tkn_off},
                 {"on",      tkn_on},
                 {"open",    tkn_open},
                 {"reference",tkn_reference},
                 {"real4",   tkn_real4},
                 {"real8",   tkn_real8},
                 {"rollback",tkn_rollback},
                 {"server",  tkn_server},
                 {"set",     tkn_set},
                 {"stop",    tkn_stop},
                 {"show",    tkn_show},
                 {"to",      tkn_to},
                 {"update",  tkn_update},
                 {"values",  tkn_values},
                 {"version", tkn_version}
               };

  for (unsigned i = 0; i < itemsof(keywords); i++)
  {

    dbSymbolTable::add
      (keywords[i].name, keywords[i].tag, FASTDB_CLONE_ANY_IDENTIFIER);
  }

  droppedTables = NULL;
  existedTables = NULL;
  opened = false;
  buflen = 1024;
  buf = new char[buflen];
  httpServerRunning = false;
  databaseName = NULL;
  historyUsed = historyCurr = 0;
  ungetToken = -1;
  autocommit = false;
}

dbSubSql::~dbSubSql()
{

  delete[] buf;
}


inline int strincmp(const char* p, const char* q, size_t n)
{
  while (n > 0)
  {
    int diff = toupper(*(unsigned char*)p) - toupper(*(unsigned char*)q);

    if (diff != 0)
    {
      return diff;
    }
    else if (*p == '\0')
    {
      return 0;
    }

    p += 1;
    q += 1;
    n -= 1;
  }

  return 0;
}

//
// Find one string within another, ignoring case
//

inline char* stristr(const char* haystack, const char* needle)
{
  nat4 i, hayLen, ndlLen;

  ndlLen = strlen(needle);
  hayLen = strlen(haystack);

  if (ndlLen > hayLen)
  {
    return NULL;
  }

  for (i = 0; i <= (hayLen - ndlLen); i++)
  {
    if (strincmp(&haystack[i], needle, ndlLen) == 0)
    {
      return (char*)&haystack[i];
    }
  }

  return NULL;
}


bool contains(dbUserFunctionArgument& arg1, dbUserFunctionArgument& arg2)
{
  assert(arg1.type == dbUserFunctionArgument::atString && arg2.type == dbUserFunctionArgument::atString);
  return stristr(arg1.u.strValue, arg2.u.strValue) != NULL;
}

USER_FUNC(contains);

int dbSubSql::get
  ()
{
  int ch = getc(in);

  if (ch == '\n')
  {
    pos = 0;
    line += 1;
  }
  else if (ch == '\t')
  {
    pos = DOALIGN(pos + 1, 8);
  }
  else
  {
    pos += 1;
  }

  return ch;
}

void dbSubSql::unget(int ch)
{
  if (ch != EOF)
  {
    if (ch != '\n')
    {
      pos -= 1;
    }
    else
    {
      line -= 1;
    }

    ungetc(ch, in);
  }
}

void dbSubSql::warning(char const* msg)
{
  fprintf(stderr, "%s at line %d position %d\n", msg, line, tknPos > 0 ? tknPos - 1 : 0);
}

void dbSubSql::error(char const* msg)
{
#ifdef THROW_EXCEPTION_ON_ERROR
  dbDatabaseThreadContext* ctx = threadContext.get();

  if (ctx != NULL)
  {
    ctx->interactive = true;
  }

  try
  {
    handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
  }
  catch(dbException)
  {}

#else

  dbDatabaseThreadContext* ctx = threadContext.get();

  if (ctx != NULL)
  {
    ctx->interactive = true;
    ctx->catched = true;

    if (setjmp(ctx->unwind) == 0)
    {
      handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
    }

    ctx->catched = false;
  }
  else
  {
    handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
  }

#endif
}


int dbSubSql::scan()
{
  int i, ch, digits;

  if (ungetToken >= 0)
  {
    int tkn = ungetToken;
    ungetToken = -1;
    return tkn;
  }

nextToken:

  do
  {
    if ((ch = get
                ()) == EOF)
      {
        return tkn_eof;
      }
  }
  while (isspace(ch));

  tknPos = pos;

  switch (ch)
  {

  case '*':
    return tkn_all;

  case '(':
    return tkn_lpar;

  case ')':
    return tkn_rpar;

  case ',':
    return tkn_comma;

  case '.':
    return tkn_dot;

  case ';':
    return tkn_semi;

  case '=':
    return tkn_eq;

  case '\'':
    i = 0;

    while (true)
    {

      ch = get
             ();

      if (ch == '\'')
      {
        if ((ch = get
                    ()) != '\'')
          {
            unget(ch);
            break;
          }
      }
      else if (ch == '\n' || ch == EOF)
      {
        unget(ch);
        error("New line within character constant");
        return tkn_error;
      }

      if (i+1 == buflen)
      {
        char* newbuf = new char[buflen*2];
        memcpy(newbuf, buf, buflen);
        delete[] buf;
        buf = newbuf;
        buflen *= 2;
      }

      buf[i++] = ch;
    }

    buf[i] = '\0';
    return tkn_sconst;

  case '-':

    if ((ch = get
                ()) == '-')
      {
        // ANSI comments

        while ((ch = get
                       ()) != EOF && ch != '\n');

        goto nextToken;
      }

    unget(ch);
    ch = '-';
    // no break

  case '0':

  case '1':

  case '2':

  case '3':

  case '4':

  case '5':

  case '6':

  case '7':

  case '8':

  case '9':

  case '+':
    i = 0;

    do
    {
      buf[i++] = ch;

      if (i == buflen)
      {
        error("Numeric constant too long");
        return tkn_error;
      }

      ch = get
             ();
    }
    while (ch != EOF
           && (isdigit(ch) || ch == '+' || ch == '-' || ch == 'e' ||
               ch == 'E' || ch == '.'));

    unget(ch);

    buf[i] = '\0';

    if (sscanf(buf, INT8_FORMAT "%n", &ival, &digits) != 1)
    {
      error("Bad integer constant");
      return tkn_error;
    }

    if (digits != i)
    {
      if (sscanf(buf, "%lf%n", &fval, &digits) != 1 || digits != i)
      {
        error("Bad float constant");
        return tkn_error;
      }

      return tkn_fconst;
    }

    return tkn_iconst;

  default:

    if (isalpha(ch) || ch == '$' || ch == '_')
    {
      i = 0;

      do
      {
        buf[i++] = ch;

        if (i == buflen)
        {
          error("Identifier too long");
          return tkn_error;
        }

        ch = get
               ();
      }
      while (ch != EOF && (isalnum(ch) || ch == '$' || ch == '_'));

      unget(ch);

      buf[i] = '\0';

      name = buf;

      return dbSymbolTable::add
               (name, tkn_ident);
    }
    else
    {
      error("Invalid symbol");
      return tkn_error;
    }
  }
}


bool dbSubSql::expect(char* expected, int token)
{
  int tkn = scan();

  if (tkn != token)
  {
    if (tkn != tkn_error)
    {
      char buf[256];
      sprintf(buf, "Token '%s' expected", expected);
      error(buf);
    }

    return false;
  }

  return true;
}


bool dbSubSql::updateTable(bool create)
{
  int tkn;

  if (!expect("table name", tkn_ident) || !expect("(", tkn_lpar))
  {
    return false;
  }

  char* name = this->name;
  int varyingLength = strlen(name)+1;

  static const struct
  {
    int size;
    int alignment;
  }

  typeDesc[] = {
                 { sizeof(bool), sizeof(bool) },
                 { sizeof(int1), sizeof(int1) },
                 { sizeof(int2), sizeof(int2) },
                 { sizeof(int4), sizeof(int4) },
                 { sizeof(db_int8), sizeof(db_int8) },
                 { sizeof(real4), sizeof(real4) },
                 { sizeof(real8), sizeof(real8) },
                 { sizeof(dbVarying), 4 },
                 { sizeof(oid_t), sizeof(oid_t) },
                 { sizeof(dbVarying), 4 }
               };

  const int maxFields = 256;
  tableField fields[maxFields];
  int nFields = 0;
  int nColumns = 0;
  tkn = tkn_comma;

  while (tkn == tkn_comma)
  {
    if (nFields+1 == maxFields)
    {
      error("Too many fields");
      break;
    }

    if (!expect("field name", tkn_ident))
    {
      break;
    }

    int nameLen = strlen(buf)+1;
    fields[nFields].name = new char[nameLen];
    strcpy(fields[nFields].name, buf);
    varyingLength += nameLen + 2;
    char* refTableName;
    char* inverseRefName;
    int type = parseType(refTableName, inverseRefName);
    fields[nFields++].type = type;

    if (type == dbField::tpUnknown)
    {
      break;
    }

    nColumns += 1;

    if (type == dbField::tpArray)
    {
      if (nFields+1 == maxFields)
      {
        error("Too many fields");
        break;
      }

      fields[nFields].name = new char[nameLen+2];
      sprintf(fields[nFields].name, "%s[]", fields[nFields-1].name);
      varyingLength += nameLen+2+2;
      type = parseType(refTableName, inverseRefName);

      if (type == dbField::tpUnknown)
      {
        break;
      }

      if (type == dbField::tpArray)
      {
        error("Arrays of arrays are not supported by CLI");
        break;
      }

      if (type == dbField::tpReference)
      {
        fields[nFields].refTableName = refTableName;
        varyingLength += strlen(refTableName);

        if (inverseRefName != NULL)
        {
          fields[nFields-1].inverseRefName = inverseRefName;
          varyingLength += strlen(inverseRefName);
        }
      }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?