📄 subsql.cpp
字号:
//-< 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 "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",
"Unknown"
};
char* dbSubSql::prompt = ">> ";
dbSubSql::dbSubSql()
{
static struct {
char* name;
int tag;
} keywords[] = {
{"array", tkn_array},
{"autoincrement", tkn_autoincrement},
{"backup", tkn_backup},
{"bool", tkn_bool},
{"commit", tkn_commit},
{"compactify",tkn_compactify},
{"create", tkn_create},
{"delete", tkn_delete},
{"describe",tkn_describe},
{"drop", tkn_drop},
{"exit", tkn_exit},
{"hash", tkn_hash},
{"help", tkn_help},
{"index", tkn_index},
{"insert", tkn_insert},
{"int1", tkn_int1},
{"int2", tkn_int2},
{"int4", tkn_int4},
{"int8", tkn_int8},
{"into", tkn_into},
{"of", tkn_of},
{"on", tkn_on},
{"open", tkn_open},
{"reference",tkn_reference},
{"real4", tkn_real4},
{"real8", tkn_real8},
{"rollback",tkn_rollback},
{"select", tkn_select},
{"server", tkn_server},
{"set", tkn_set},
{"stop", tkn_stop},
{"show", tkn_show},
{"to", tkn_to},
{"table", tkn_table},
{"update", tkn_update},
{"values", tkn_values}
};
for (unsigned i = 0; i < items(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];
}
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::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;
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::createTable()
{
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(int8), sizeof(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;
int type = parseType();
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();
if (type == dbField::tpUnknown) {
break;
}
if (type == dbField::tpArray) {
error("Arrays of arrays are not supported by CLI");
break;
}
fields[nFields++].type = type;
} else if (type == dbField::tpReference) {
int len = strlen(buf);
fields[nFields-1].refTableName = new char[len+1];
strcpy(fields[nFields-1].refTableName, buf);
varyingLength += len;
}
tkn = scan();
}
if (tkn == tkn_rpar) {
beginTransaction(true);
modified = true;
if (findTable(name) != NULL) {
error("Table already exists");
return false;
}
oid_t oid = allocateRow(dbMetaTableId,
sizeof(dbTable) + sizeof(dbField)*nFields
+ varyingLength);
dbTable* table = (dbTable*)getRow(oid);
int offs = sizeof(dbTable) + sizeof(dbField)*nFields;
table->name.offs = offs;
table->name.size = strlen(name)+1;
strcpy((char*)table + offs, name);
offs += table->name.size;
size_t size = sizeof(dbRecord);
table->fields.offs = sizeof(dbTable);
dbField* field = (dbField*)((char*)table + table->fields.offs);
offs -= sizeof(dbTable);
bool arrayComponent = false;
for (int i = 0; i < nFields; i++) {
field->name.offs = offs;
field->name.size = strlen(fields[i].name) + 1;
strcpy((char*)field + offs, fields[i].name);
offs += field->name.size;
field->tableName.offs = offs;
if (fields[i].refTableName) {
field->tableName.size = strlen(fields[i].refTableName);
strcpy((char*)field + offs, fields[i].refTableName);
offs += field->tableName.size;
} else {
field->tableName.size = 1;
*((char*)field + offs++) = '\0';
}
field->inverse.offs = offs;
field->inverse.size = 1;
*((char*)field + offs++) = '\0';
field->type = fields[i].type;
field->size = typeDesc[fields[i].type].size;
if (!arrayComponent) {
size = DOALIGN(size, typeDesc[fields[i].type].alignment);
field->offset = size;
size += field->size;
} else {
field->offset = 0;
}
field->hashTable = 0;
field->tTree = 0;
arrayComponent = field->type == dbField::tpArray;
field += 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -