1 //-< SUBSQL.CPP >----------------------------------------------------*--------*
2 // GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
3 // (Post Relational Database Management System)                      *   /\|  *
4 //                                                                   *  /  \  *
5 //                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
6 //                          Last update: 10-Dec-98    K.A. Knizhnik  * GARRET *
7 //-------------------------------------------------------------------*--------*
8 // Interactive data manipulation language (subset of SQL)
9 //-------------------------------------------------------------------*--------*
10 
11 #include "gigabase.h"
12 #include "compiler.h"
13 #include "wwwapi.h"
14 #include "symtab.h"
15 #include "hashtab.h"
16 #include "btree.h"
17 #include "rtree.h"
18 #include "subsql.h"
19 #include <locale.h>
20 
21 #if THREADS_SUPPORTED
22 #include "server.h"
23 #endif
24 
25 BEGIN_GIGABASE_NAMESPACE
26 
27 static char_t const* typeMnem[] = {
28         STRLITERAL("Boolean"),
29         STRLITERAL("Int1"),
30         STRLITERAL("Int2"),
31         STRLITERAL("Int4"),
32         STRLITERAL("Int8"),
33         STRLITERAL("Real4"),
34         STRLITERAL("Real8"),
35         STRLITERAL("String"),
36         STRLITERAL("Reference"),
37         STRLITERAL("Array"),
38         STRLITERAL("MethodBool"),
39         STRLITERAL("MethodInt1"),
40         STRLITERAL("MethodInt2"),
41         STRLITERAL("MethodInt4"),
42         STRLITERAL("MethodInt8"),
43         STRLITERAL("MethodReal4"),
44         STRLITERAL("MethodReal8"),
45         STRLITERAL("MethodString"),
46         STRLITERAL("MethodReference"),
47         STRLITERAL("Structure"),
48         STRLITERAL("RawBinary"),
49         STRLITERAL("StdString"),
50         STRLITERAL("MfcString"),
51         STRLITERAL("Rectangle"),
52         STRLITERAL("Unknown")
53 };
54 
55 #ifndef OID_FORMAT
56 #if dbDatabaseOidBits > 32
57 #define OID_FORMAT "#" INT8_FORMAT_PREFIX "x"
58 #else
59 #define OID_FORMAT "#%x"
60 #endif
61 #endif
62 
63 
64 
65 char const* dbSubSql::prompt = ">> ";
66 const int initBufSize = 4096;
67 
68 
69 static bool interactiveMode;
70 
71 
72 
dbSubSql(dbAccessType accessType,size_t pagePoolSize)73 dbSubSql::dbSubSql(dbAccessType accessType, size_t pagePoolSize)
74 : dbDatabase(accessType, pagePoolSize)
75 {
76     static struct {
77         char_t* name;
78         int     tag;
79     } keywords[] = {
80         {STRLITERAL("alter"),   tkn_alter},
81         {STRLITERAL("array"),   tkn_array},
82         {STRLITERAL("autocommit"),   tkn_autocommit},
83         {STRLITERAL("autoincrement"),tkn_autoincrement},
84         {STRLITERAL("backup"),  tkn_backup},
85         {STRLITERAL("bool"),    tkn_bool},
86         {STRLITERAL("commit"),  tkn_commit},
87         {STRLITERAL("compactify"),tkn_compactify},
88         {STRLITERAL("count"),   tkn_count},
89         {STRLITERAL("create"),  tkn_create},
90         {STRLITERAL("delete"),  tkn_delete},
91         {STRLITERAL("describe"),tkn_describe},
92         {STRLITERAL("drop"),    tkn_drop},
93         {STRLITERAL("exit"),    tkn_exit},
94         {STRLITERAL("export"),  tkn_export},
95         {STRLITERAL("hash"),    tkn_hash},
96         {STRLITERAL("help"),    tkn_help},
97         {STRLITERAL("http"),    tkn_http},
98         {STRLITERAL("import"),  tkn_import},
99         {STRLITERAL("incremental"),tkn_incremental},
100         {STRLITERAL("index"),   tkn_index},
101         {STRLITERAL("inverse"), tkn_inverse},
102         {STRLITERAL("int1"),    tkn_int1},
103         {STRLITERAL("int2"),    tkn_int2},
104         {STRLITERAL("int4"),    tkn_int4},
105         {STRLITERAL("int8"),    tkn_int8},
106         {STRLITERAL("memory"),  tkn_memory},
107         {STRLITERAL("of"),      tkn_of},
108         {STRLITERAL("off"),     tkn_off},
109         {STRLITERAL("on"),      tkn_on},
110         {STRLITERAL("open"),    tkn_open},
111         {STRLITERAL("profile"), tkn_profile},
112         {STRLITERAL("reference"),tkn_reference},
113         {STRLITERAL("real4"),   tkn_real4},
114         {STRLITERAL("real8"),   tkn_real8},
115         {STRLITERAL("rectangle"), tkn_rectangle},
116         {STRLITERAL("rename"),  tkn_rename},
117         {STRLITERAL("reorder"),  tkn_reorder},
118         {STRLITERAL("restore"), tkn_restore},
119         {STRLITERAL("rollback"),tkn_rollback},
120         {STRLITERAL("server"),  tkn_server},
121         {STRLITERAL("set"),     tkn_set},
122         {STRLITERAL("start"),   tkn_start},
123         {STRLITERAL("stop"),    tkn_stop},
124         {STRLITERAL("show"),    tkn_show},
125         {STRLITERAL("to"),      tkn_to},
126         {STRLITERAL("update"),  tkn_update},
127         {STRLITERAL("values"),  tkn_values},
128         {STRLITERAL("version"), tkn_version}
129     };
130     for (unsigned i = 0; i < itemsof(keywords); i++) {
131         dbSymbolTable::add(keywords[i].name, keywords[i].tag, GB_CLONE_ANY_IDENTIFIER);
132     }
133     buflen = initBufSize;
134     buf = new char_t[buflen];
135     droppedTables = NULL;
136     existedTables = NULL;
137     opened = false;
138     httpServerRunning = false;
139     databasePath = NULL;
140     historyUsed = historyCurr = 0;
141     autocommit = false;
142     ungetToken = -1;
143     dotIsPartOfIdentifier = false;
144 
145 #ifdef _WIN32_WCE
146     dateFormat = NULL;
147 #else
148     dateFormat = GETENV(STRLITERAL("SUBSQL_DATE_FORMAT"));
149 #endif
150 }
151 
~dbSubSql()152 dbSubSql::~dbSubSql() { delete[] buf; }
153 
154 
strincmp(const char_t * p,const char_t * q,size_t n)155 inline int strincmp(const char_t* p, const char_t* q, size_t n)
156 {
157     while (n > 0) {
158         int diff = TOUPPER(*(char_t*)p) - TOUPPER(*(char_t*)q);
159         if (diff != 0) {
160             return diff;
161         } else if (*p == '\0') {
162             return 0;
163         }
164         p += 1;
165         q += 1;
166         n -= 1;
167     }
168     return 0;
169 }
170 
171 //
172 // Find one string within another, ignoring case
173 //
174 
stristr(const char_t * haystack,const char_t * needle)175 inline char_t* stristr(const char_t* haystack, const char_t* needle)
176 {
177     size_t i, hayLen, ndlLen;
178 
179     ndlLen = STRLEN(needle);
180     hayLen = STRLEN(haystack);
181 
182     if (ndlLen > hayLen) {
183         return NULL;
184     }
185 
186     for (i = 0; i <= (hayLen - ndlLen); i++) {
187         if (strincmp(&haystack[i], needle, ndlLen) == 0) {
188             return (char_t*)&haystack[i];
189         }
190     }
191     return NULL;
192 }
193 
194 
contains(dbUserFunctionArgument & arg1,dbUserFunctionArgument & arg2)195 bool  __cdecl contains(dbUserFunctionArgument& arg1, dbUserFunctionArgument& arg2) {
196     assert(arg1.type == dbUserFunctionArgument::atString && arg2.type == dbUserFunctionArgument::atString);
197     return stristr(arg1.u.strValue, arg2.u.strValue) != NULL;
198 }
199 
200 USER_FUNC(contains);
201 
get()202 inline int dbSubSql::get()
203 {
204     int ch = GETC(in);
205     if (ch == '\n') {
206         pos = 0;
207         line += 1;
208     } else if (ch == '\t') {
209         pos = DOALIGN(pos + 1, 8);
210     } else {
211         pos += 1;
212     }
213     return ch;
214 }
215 
unget(int ch)216 inline void dbSubSql::unget(int ch) {
217     if (ch != T_EOF) {
218         if (ch != '\n') {
219             pos -= 1;
220         } else {
221             line -= 1;
222         }
223         UNGETC(ch, in);
224     }
225 }
226 
warning(char const * msg)227 void dbSubSql::warning(char const* msg)
228 {
229     fprintf(stderr, "%s at line %d position %d\n", msg, line, tknPos > 0 ? tknPos - 1 : 0);
230 }
231 
error(char const * msg)232 void dbSubSql::error(char const* msg)
233 {
234 #ifdef THROW_EXCEPTION_ON_ERROR
235     dbDatabaseThreadContext* ctx = threadContext.get();
236     if (ctx != NULL) {
237         ctx->interactive = true;
238     }
239     try {
240         handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
241     } catch(dbException) {}
242 #else
243     dbDatabaseThreadContext* ctx = threadContext.get();
244     if (ctx != NULL) {
245         ctx->interactive = true;
246         ctx->catched = true;
247         if (setjmp(ctx->unwind) == 0) {
248             handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
249         }
250         ctx->catched = false;
251     } else {
252         handleError(QueryError, msg, tknPos > 0 ? tknPos - 1 : 0);
253     }
254 #endif
255 }
256 
257 
scan()258 int dbSubSql::scan()
259 {
260     int i, ch, nextCh, digits;
261     char numbuf[64];
262 
263     if (ungetToken >= 0) {
264         int tkn = ungetToken;
265         ungetToken = -1;
266         return tkn;
267     }
268     bool dotIsIdentifierChar = dotIsPartOfIdentifier;
269     dotIsPartOfIdentifier = false;
270   nextToken:
271     do {
272         if ((ch = get()) == T_EOF) {
273             return tkn_eof;
274         }
275     } while (ch > 0 && ch <= ' ');
276 
277     tknPos = pos;
278     switch (ch) {
279       case '*':
280         return tkn_all;
281       case '(':
282         return tkn_lpar;
283       case ')':
284         return tkn_rpar;
285       case ',':
286         return tkn_comma;
287       case '.':
288         return tkn_dot;
289       case ';':
290         return tkn_semi;
291       case '=':
292         return tkn_eq;
293       case '\'':
294         i = 0;
295         while (true) {
296             ch = get();
297             if (ch == '\'') {
298                 if ((ch = get()) != '\'') {
299                     unget(ch);
300                     break;
301                 }
302             } else if (ch == '\n' || ch == T_EOF) {
303                 unget(ch);
304                 error("New line within character constant");
305                 return tkn_error;
306             }
307             if (i+1 == buflen) {
308                 char_t* newbuf = new char_t[buflen*2];
309                 memcpy(newbuf, buf, buflen*sizeof(char_t));
310                 delete[] buf;
311                 buf = newbuf;
312                 buflen *= 2;
313             }
314             buf[i++] = ch;
315         }
316         buf[i] = '\0';
317         return tkn_sconst;
318       case '-':
319         if ((ch = get()) == '-') {
320             // ANSI comments
321             while ((ch = get()) != T_EOF && ch != '\n');
322             goto nextToken;
323         }
324         unget(ch);
325         ch = '-';
326 
327         // no break
328       case '+':
329           nextCh = get(); unget(nextCh);    //< peek next char
330           if (!isdigit(nextCh))
331           {
332               if (ch == '-') return tkn_exclude;
333               if (ch == '+') return tkn_include;
334               error("Expected '+' or '-'"); return tkn_error;
335           }
336           // no break
337 
338       case '0': case '1': case '2': case '3': case '4':
339       case '5': case '6': case '7': case '8': case '9':
340         i = 0;
341         do {
342             if (i == sizeof(numbuf)) {
343                 error("Numeric constant too long");
344                 return tkn_error;
345             }
346             numbuf[i++] = (char)ch;
347             ch = get();
348         } while (ch != T_EOF
349                  && ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' ||
350                      ch == 'E' || ch == '.'));
351         unget(ch);
352         numbuf[i] = '\0';
353         if (sscanf(numbuf, INT8_FORMAT "%n", &ival, &digits) != 1) {
354             error("Bad integer constant");
355             return tkn_error;
356         }
357         if (digits != i) {
358             if (sscanf(numbuf, "%lf%n", &fval, &digits) != 1 || digits != i) {
359                 error("Bad float constant");
360                 return tkn_error;
361             }
362             return tkn_fconst;
363         }
364         return tkn_iconst;
365 
366       case '#':
367         ival = 0;
368         while (true) {
369             ch = get();
370             if (ch >= '0' && ch <= '9') {
371                 ival = (ival << 4) + ch-'0';
372             } else if (ch >= 'a' && ch <= 'f') {
373                 ival = (ival << 4) +  ch-'a'+10;
374             } else if (ch >= 'A' && ch <= 'F') {
375                 ival = (ival << 4) + ch-'A'+10;
376             } else {
377                 unget(ch);
378                 return tkn_iconst;
379             }
380         }
381       default:
382         if (ISALNUM(ch) || ch == '$' || ch == '_') {
383             i = 0;
384             do {
385                 if (i == buflen) {
386                     error("Identifier too long");
387                     return tkn_error;
388                 }
389                 buf[i++] = ch;
390                 ch = get();
391             } while (ch != T_EOF && (ISALNUM(ch) || ch == '$' || ch == '_' || (ch == '.' && dotIsIdentifierChar)));
392             unget(ch);
393             buf[i] = '\0';
394             name = buf;
395             return dbSymbolTable::add(name, tkn_ident);
396         } else {
397             error("Invalid symbol");
398             return tkn_error;
399         }
400     }
401 }
402 
403 
expect(char const * expected,int token)404 bool dbSubSql::expect(char const* expected, int token)
405 {
406     int tkn = scan();
407     if (tkn != token) {
408         if (tkn != tkn_error) {
409             char buf[256];
410             sprintf(buf, "Token '%s' expected", expected);
411             error(buf);
412         }
413         return false;
414     }
415     return true;
416 }
417 
418 
updateTable(bool create)419 bool dbSubSql::updateTable(bool create)
420 {
421     int tkn;
422     dotIsPartOfIdentifier = true;
423     if (!expect("table name", tkn_ident) || !expect("(", tkn_lpar)) {
424         return false;
425     }
426     char_t* name = this->name;
427     int varyingLength = (int)((STRLEN(name)+1)*sizeof(char_t));
428 
429     static const struct {
430         int size;
431         int alignment;
432     } typeDesc[] = {
433         { sizeof(bool), sizeof(bool) },
434         { sizeof(int1), sizeof(int1) },
435         { sizeof(int2), sizeof(int2) },
436         { sizeof(int4), sizeof(int4) },
437         { sizeof(db_int8), sizeof(db_int8) },
438         { sizeof(real4), sizeof(real4) },
439         { sizeof(real8), sizeof(real8) },
440         { sizeof(dbVarying), 4 },
441         { sizeof(oid_t), sizeof(oid_t) },
442         { sizeof(dbVarying), 4 },
443         {0}, // tpMethodBool,
444         {0}, // tpMethodInt1,
445         {0}, // tpMethodInt2,
446         {0}, // tpMethodInt4,
447         {0}, // tpMethodInt8,
448         {0}, // tpMethodReal4,
449         {0}, // tpMethodReal8,
450         {0}, // tpMethodString,
451         {0}, // tpMethodReference,
452         {0}, // tpStructure,
453         {0}, // tpRawBinary,
454         {0}, // tpStdString,
455         {0}, // tpMfcString,
456         { sizeof(rectangle), sizeof(coord_t) }, // tpRectangle,
457         {0} // tpUnknown
458     };
459 
460     const int maxFields = 256;
461     tableField fields[maxFields];
462     int nFields = 0;
463     int nColumns = 0;
464     tkn = tkn_comma;
465     while (tkn == tkn_comma) {
466         if (nFields+1 == maxFields) {
467             error("Too many fields");
468             break;
469         }
470         if (!expect("field name", tkn_ident)) {
471             break;
472         }
473         int nameLen = (int)(STRLEN(buf)+1);
474         fields[nFields].name = new char_t[nameLen];
475         STRCPY(fields[nFields].name, buf);
476         varyingLength += (nameLen + 2)*sizeof(char_t);
477         char_t* refTableName;
478         char_t* inverseRefName;
479         int type = parseType(refTableName, inverseRefName);
480         fields[nFields++].type = type;
481         if (type == dbField::tpUnknown) {
482             break;
483         }
484         nColumns += 1;
485         if (type == dbField::tpArray) {
486             if (nFields+1 == maxFields) {
487                 error("Too many fields");
488                 break;
489             }
490             fields[nFields].name = new char_t[nameLen+2];
491             SPRINTF(SPRINTF_BUFFER(fields[nFields].name), STRLITERAL("%s[]"), fields[nFields-1].name);
492             varyingLength += (nameLen+2+2)*sizeof(char_t);
493             type = parseType(refTableName, inverseRefName);
494             if (type == dbField::tpUnknown) {
495                 break;
496             }
497             if (type == dbField::tpArray) {
498                 error("Arrays of arrays are not supported by CLI");
499                 break;
500             }
501             if (type == dbField::tpReference) {
502                 fields[nFields].refTableName = refTableName;
503                 varyingLength += (int)(STRLEN(refTableName)*sizeof(char_t));
504                 if (inverseRefName != NULL) {
505                     fields[nFields-1].inverseRefName = inverseRefName;
506                     varyingLength += (int)(STRLEN(inverseRefName)*sizeof(char_t));
507                 }
508             }
509             fields[nFields++].type = type;
510         } else if (type == dbField::tpReference) {
511             fields[nFields-1].refTableName = refTableName;
512             varyingLength += (int)(STRLEN(refTableName)*sizeof(char_t));
513             if (inverseRefName != NULL) {
514                 fields[nFields-1].inverseRefName = inverseRefName;
515                 varyingLength += (int)(STRLEN(inverseRefName)*sizeof(char_t));
516             }
517         }
518         tkn = scan();
519     }
520     if (tkn == tkn_rpar) {
521         dbTableDescriptor* oldDesc = findTable(name);
522         if (oldDesc != NULL) {
523             if (create) {
524                 error("Table already exists");
525                 return false;
526             }
527         } else {
528             if (!create) {
529                 error("Table not found");
530                 return false;
531             }
532         }
533         beginTransaction(dbExclusiveLock);
534         dbPutTie putTie;
535         dbTable* table;
536         oid_t oid;
537 
538         if (create) {
539             modified = true;
540             oid = allocateRow(dbMetaTableId,
541                               sizeof(dbTable) + sizeof(dbField)*nFields + varyingLength);
542             table = (dbTable*)putRow(putTie, oid);
543         } else {
544             oid = oldDesc->tableId;
545             table = (dbTable*)new char[sizeof(dbTable) + sizeof(dbField)*nFields + varyingLength];
546         }
547         int offs = sizeof(dbTable) + sizeof(dbField)*nFields;
548         table->name.offs = offs;
549         table->name.size = (nat4)STRLEN(name)+1;
550         STRCPY((char_t*)((byte*)table + offs), name);
551         offs += table->name.size*sizeof(char_t);
552         size_t size = sizeof(dbRecord);
553         table->fields.offs = sizeof(dbTable);
554         dbField* field = (dbField*)((char*)table + table->fields.offs);
555         offs -= sizeof(dbTable);
556         bool arrayComponent = false;
557 
558         for (int i = 0; i < nFields; i++) {
559             field->name.offs = offs;
560             field->name.size = (nat4)(STRLEN(fields[i].name) + 1);
561             STRCPY((char_t*)((char*)field + offs), fields[i].name);
562             offs += field->name.size*sizeof(char_t);
563 
564             field->tableName.offs = offs;
565             if (fields[i].refTableName) {
566                 field->tableName.size = (nat4)(STRLEN(fields[i].refTableName) + 1);
567                 STRCPY((char_t*)((byte*)field + offs), fields[i].refTableName);
568                 offs += field->tableName.size*sizeof(char_t);
569             } else {
570                 field->tableName.size = 1;
571                 *(char_t*)((char*)field + offs) = '\0';
572                 offs += sizeof(char_t);
573             }
574 
575             field->inverse.offs = offs;
576             if (fields[i].inverseRefName) {
577                 field->inverse.size = (nat4)(STRLEN(fields[i].inverseRefName) + 1);
578                 STRCPY((char_t*)((byte*)field + offs), fields[i].inverseRefName);
579                 offs += field->inverse.size*sizeof(char_t);
580             } else {
581                 field->inverse.size = 1;
582                 *(char_t*)((char*)field + offs) = '\0';
583                 offs += sizeof(char_t);
584             }
585 
586             field->flags = 0;
587             field->type = fields[i].type;
588             field->size = typeDesc[fields[i].type].size;
589             if (!arrayComponent) {
590                 size = DOALIGN(size, typeDesc[fields[i].type].alignment);
591                 field->offset = (int4)size;
592                 size += field->size;
593             } else {
594                 field->offset = 0;
595             }
596             field->hashTable = 0;
597             field->bTree = 0;
598             arrayComponent = field->type == dbField::tpArray;
599             field += 1;
600             offs -= sizeof(dbField);
601         }
602         table->fields.size = nFields;
603         table->fixedSize = (nat4)size;
604         table->nRows = 0;
605         table->nColumns = nColumns;
606         table->firstRow = 0;
607         table->lastRow = 0;
608 
609         if (create) {
610             linkTable(new dbTableDescriptor(table), oid);
611         } else {
612             dbGetTie getTie;
613             dbTableDescriptor* newDesc = new dbTableDescriptor(table);
614             delete[] (char*)table;
615             dbTable* oldTable = (dbTable*)getRow(getTie, oid);
616             if (!newDesc->equal(oldTable)) {
617                 bool saveConfirmDeleteColumns = confirmDeleteColumns;
618                 confirmDeleteColumns = true;
619                 modified = true;
620                 schemeVersion += 1;
621                 unlinkTable(oldDesc);
622                 if (oldTable->nRows == 0) {
623                     updateTableDescriptor(newDesc, oid, oldTable);
624                 } else {
625                     reformatTable(oid, newDesc);
626                 }
627                 delete oldDesc;
628                 confirmDeleteColumns = saveConfirmDeleteColumns;
629                 addIndices(newDesc);
630             }
631         }
632         if (!completeDescriptorsInitialization()) {
633             warning("Reference to undefined table");
634         }
635     }
636     return tkn == tkn_rpar;
637 }
638 
parseType(char_t * & refTableName,char_t * & inverseRefName)639 int dbSubSql::parseType(char_t*& refTableName, char_t*& inverseRefName)
640 {
641     switch (scan()) {
642       case tkn_bool:
643         return dbField::tpBool;
644       case tkn_int1:
645         return dbField::tpInt1;
646       case tkn_int2:
647         return dbField::tpInt2;
648       case tkn_int4:
649         return dbField::tpInt4;
650       case tkn_int8:
651         return dbField::tpInt8;
652       case tkn_real4:
653         return dbField::tpReal4;
654       case tkn_real8:
655         return dbField::tpReal8;
656       case tkn_array:
657         return expect("of", tkn_of) ? dbField::tpArray : dbField::tpUnknown;
658       case tkn_string:
659         return dbField::tpString;
660       case tkn_reference:
661         if (expect("to", tkn_to) && expect("referenced table name", tkn_ident)) {
662             refTableName = new char_t[STRLEN(buf)+1];
663             STRCPY(refTableName, buf);
664             int tkn = scan();
665             if (tkn == tkn_inverse) {
666                 if (!expect("inverse reference field name", tkn_ident)) {
667                     return dbField::tpUnknown;
668                 }
669                 inverseRefName = new char_t[STRLEN(buf)+1];
670                 STRCPY(inverseRefName, buf);
671             } else {
672                 inverseRefName = NULL;
673                 ungetToken = tkn;
674             }
675             return dbField::tpReference;
676         } else {
677             return dbField::tpUnknown;
678         }
679       case tkn_rectangle:
680         return dbField::tpRectangle;
681       default:
682         error("Field type expected");
683     }
684     return dbField::tpUnknown;
685 }
686 
687 
readExpression()688 int dbSubSql::readExpression()
689 {
690     int i, ch;
691     int nesting = 0;
692     for (i = 0; (ch = get()) != ';' && (ch != ',' || nesting != 0)  && ch != EOF; i++) {
693         if (ch == '(') {
694             nesting += 1;
695         } else if (ch == ')') {
696             nesting -= 1;
697         }
698         if (i+1 >= buflen) {
699             char_t* newbuf = new char_t[buflen*2];
700             memcpy(newbuf, buf, buflen*sizeof(char_t));
701             delete[] buf;
702             buf = newbuf;
703             buflen *= 2;
704         }
705         buf[i] = ch;
706     }
707     buf[i] = '\0';
708     return ch;
709 }
710 
readCondition()711 bool dbSubSql::readCondition()
712 {
713     int i, ch;
714     for (i = 0; (ch = get()) != ';' && ch !=  T_EOF; i++) {
715         if (i+1 == buflen) {
716             char_t* newbuf = new char_t[buflen*2];
717             memcpy(newbuf, buf, buflen*sizeof(char_t));
718             delete[] buf;
719             buf = newbuf;
720             buflen *= 2;
721         }
722         buf[i] = ch;
723     }
724     buf[i] = '\0';
725     if (ch != ';') {
726         error("unexpected end of input");
727         return false;
728     }
729     return true;
730 }
731 
732 
dumpRecord(byte * base,dbFieldDescriptor * first)733 void dbSubSql::dumpRecord(byte* base, dbFieldDescriptor* first)
734 {
735     int i, n;
736     byte* elem;
737     dbFieldDescriptor* fd = first;
738     do {
739         if (fd != first) {
740             printf(", ");
741         }
742         switch (fd->type) {
743           case dbField::tpBool:
744             printf("%s", *(bool*)(base + fd->dbsOffs)
745                    ? "true" : "false");
746             continue;
747           case dbField::tpInt1:
748             printf("%d", *(int1*)(base + fd->dbsOffs));
749             continue;
750           case dbField::tpInt2:
751             printf("%d", *(int2*)(base + fd->dbsOffs));
752             continue;
753           case dbField::tpInt4:
754             printf("%d", *(int4*)(base + fd->dbsOffs));
755             continue;
756           case dbField::tpInt8:
757             printf(INT8_FORMAT, *(db_int8*)(base + fd->dbsOffs));
758             continue;
759           case dbField::tpReal4:
760             printf("%f", *(real4*)(base + fd->dbsOffs));
761             continue;
762           case dbField::tpReal8:
763             printf("%f", *(real8*)(base + fd->dbsOffs));
764             continue;
765           case dbField::tpRectangle:
766             {
767                 int i, sep = '(';
768                 rectangle& r = *(rectangle*)(base + fd->dbsOffs);
769                 for (i = 0; i < rectangle::dim*2; i++) {
770                     printf("%c%f", sep, (double)r.boundary[i]);
771                     sep = ',';
772                 }
773                 printf(")");
774             }
775             continue;
776           case dbField::tpString:
777             PRINTF(STRLITERAL("'%s'"),
778                    (char*)base+((dbVarying*)(base+fd->dbsOffs))->offs);
779             continue;
780           case dbField::tpReference:
781             printf(OID_FORMAT, *(oid_t*)(base + fd->dbsOffs));
782             continue;
783           case dbField::tpRawBinary:
784             n = (int)fd->dbsSize;
785             elem = base + fd->dbsOffs;
786             printf("(");
787             for (i = 0; i < n; i++) {
788                 if (i != 0) {
789                     printf(", ");
790                 }
791                 printf("%02x", *elem++);
792             }
793             printf(")");
794             continue;
795           case dbField::tpArray:
796             n = ((dbVarying*)(base + fd->dbsOffs))->size;
797             elem = base + ((dbVarying*)(base + fd->dbsOffs))->offs;
798             printf("(");
799             for (i = 0; i < n; i++) {
800                 if (i != 0) {
801                     printf(", ");
802                 }
803                 dumpRecord(elem, fd->components);
804                 elem += fd->components->dbsSize;
805             }
806             printf(")");
807             continue;
808           case dbField::tpStructure:
809             if (dateFormat != NULL
810                 && fd->components->next == fd->components
811                 && STRCMP(fd->components->name, STRLITERAL("stamp")) == 0)
812             {
813                 char_t buf[64];
814                 PRINTF(((dbDateTime*)(base + fd->components->dbsOffs))->asString(buf, itemsof(buf), dateFormat));
815                 continue;
816             }
817             printf("(");
818             dumpRecord(base, fd->components);
819             printf(")");
820         }
821     } while ((fd = fd->next) != first);
822 }
823 
calculateRecordSize(dbList * node,int offs,dbFieldDescriptor * first)824 int dbSubSql::calculateRecordSize(dbList* node, int offs,
825                                   dbFieldDescriptor* first)
826 {
827     dbFieldDescriptor* fd = first;
828     do {
829         if (node == NULL) {
830             return -1;
831         }
832         if (fd->type == dbField::tpArray) {
833             if (node->type != dbList::nTuple) {
834                 return -1;
835             }
836             int nElems = node->aggregate.nComponents;
837             offs = (int)(DOALIGN(offs, fd->components->alignment)
838                  + nElems*fd->components->dbsSize);
839             if (fd->attr & dbFieldDescriptor::HasArrayComponents) {
840                 dbList* component = node->aggregate.components;
841                 while (--nElems >= 0) {
842                     int d = calculateRecordSize(component,offs,fd->components);
843                     if (d < 0) return d;
844                     offs = d;
845                     component = component->next;
846                 }
847             }
848         } else if (fd->type == dbField::tpString) {
849             if (node->type != dbList::nString) {
850                 return -1;
851             }
852             offs += (int)((STRLEN(node->sval) + 1)*sizeof(char_t));
853         } else if (fd->type == dbField::tpRectangle) {
854             if (node->type != dbList::nTuple) {
855                 return -1;
856             }
857             int nCoords = node->aggregate.nComponents;
858             if (nCoords != rectangle::dim*2) {
859                 return -1;
860             }
861             dbList* component = node->aggregate.components;
862             while (--nCoords >= 0) {
863                 if (component->type != dbList::nInteger && component->type != dbList::nReal) {
864                     return -1;
865                 }
866                 component = component->next;
867             }
868         } else if (fd->type == dbField::tpRawBinary) {
869             if (node->type != dbList::nTuple) {
870                 return -1;
871             }
872             int nElems = node->aggregate.nComponents;
873             dbList* component = node->aggregate.components;
874             if (size_t(nElems) > fd->dbsSize) {
875                 return -1;
876             }
877             while (--nElems >= 0) {
878                 if (component->type != dbList::nInteger
879                     || (component->ival & ~0xFF) != 0)
880                 {
881                     return -1;
882                 }
883                 component = component->next;
884             }
885 #ifdef AUTOINCREMENT_SUPPORT
886         } else if (node->type == dbList::nAutoinc) {
887             if (fd->type != dbField::tpInt4) {
888                 return -1;
889             }
890 #endif
891         } else {
892             if (!((node->type == dbList::nBool && fd->type == dbField::tpBool)
893                   || (node->type == dbList::nInteger
894                       && (fd->type == dbField::tpInt1
895                           || fd->type == dbField::tpInt2
896                           || fd->type == dbField::tpInt4
897                           || fd->type == dbField::tpInt8
898                           || fd->type == dbField::tpReference))
899                   || (node->type == dbList::nReal
900                       && (fd->type == dbField::tpReal4
901                           || fd->type == dbField::tpReal8))
902                   || (node->type == dbList::nTuple
903                       && fd->type == dbField::tpStructure)
904                   || (node->type == dbList::nString && fd->type < dbField::tpString)))
905             {
906                 return -1;
907             }
908             if (fd->attr & dbFieldDescriptor::HasArrayComponents) {
909                 int d = calculateRecordSize(node->aggregate.components,
910                                             offs, fd->components);
911                 if (d < 0) return d;
912                 offs = d;
913             }
914         }
915         node = node->next;
916     } while ((fd = fd->next) != first);
917     return offs;
918 }
919 
isValidOid(oid_t oid)920 bool dbSubSql::isValidOid(oid_t oid)
921 {
922     return oid == 0 ||
923         (oid < currIndexSize
924          && (getPos(oid) & (dbFreeHandleFlag|dbPageObjectFlag)) == 0);
925 }
926 
initializeRecordFields(dbList * node,byte * dst,int offs,dbFieldDescriptor * first)927 int dbSubSql::initializeRecordFields(dbList* node, byte* dst, int offs,
928                                      dbFieldDescriptor* first)
929 {
930     dbFieldDescriptor* fd = first;
931     dbList* component;
932     byte* elem;
933     coord_t* coord;
934     int len, elemOffs, elemSize;
935 
936     do {
937         if (node->type == dbList::nString && fd->type != dbField::tpString) {
938             char_t* s = node->sval;
939             long  ival;
940             switch (fd->type) {
941               case dbField::tpBool:
942                 *(bool*)(dst+fd->dbsOffs) = *s == '1' || *s == 't' || *s == 'T';
943                 break;
944               case dbField::tpInt1:
945                 if (SSCANF(s, STRLITERAL("%ld"), &ival) != 1) {
946                     return -1;
947                 }
948                 *(int1*)(dst+fd->dbsOffs) = (int1)ival;
949               case dbField::tpInt2:
950                 if (SSCANF(s, STRLITERAL("%ld"), &ival) != 1) {
951                     return -1;
952                 }
953                 *(int2*)(dst+fd->dbsOffs) = (int2)ival;
954               case dbField::tpInt4:
955                 if (SSCANF(s, STRLITERAL("%ld"), &ival) != 1) {
956                     return -1;
957                 }
958                 *(int4*)(dst+fd->dbsOffs) = (int4)ival;
959               case dbField::tpInt8:
960                 if (SSCANF(s, STRLITERAL("%ld"), &ival) != 1) {
961                     return -1;
962                 }
963                 *(db_int8*)(dst+fd->dbsOffs) = ival;
964                 break;
965               case dbField::tpReal4:
966                 if (SSCANF(s, STRLITERAL("%f"), (real4*)(dst+fd->dbsOffs)) != 1) {
967                     return -1;
968                 }
969                 break;
970               case dbField::tpReal8:
971                 if (SSCANF(s, STRLITERAL("%lf"), (real8*)(dst+fd->dbsOffs)) != 1) {
972                     return -1;
973                 }
974                 break;
975             }
976 #ifdef AUTOINCREMENT_SUPPORT
977         } else if (node->type == dbList::nAutoinc) {
978             if (fd->type == dbField::tpInt4) {
979                 *(int4*)(dst+fd->dbsOffs) = fd->defTable->autoincrementCount;
980             } else {
981                 return -1;
982             }
983 #endif
984         } else {
985             switch (fd->type) {
986               case dbField::tpBool:
987                 *(bool*)(dst+fd->dbsOffs) = node->bval;
988                 break;
989               case dbField::tpInt1:
990                 *(int1*)(dst+fd->dbsOffs) = (int1)node->ival;
991                 break;
992               case dbField::tpInt2:
993                 *(int2*)(dst+fd->dbsOffs) = (int2)node->ival;
994                 break;
995               case dbField::tpInt4:
996                 *(int4*)(dst+fd->dbsOffs) = (int4)node->ival;
997                 break;
998               case dbField::tpInt8:
999                 *(db_int8*)(dst+fd->dbsOffs) = node->ival;
1000                 break;
1001               case dbField::tpReal4:
1002                 *(real4*)(dst+fd->dbsOffs) = (real4)node->fval;
1003                 break;
1004               case dbField::tpReal8:
1005                 *(real8*)(dst+fd->dbsOffs) = node->fval;
1006                 break;
1007               case dbField::tpReference:
1008                 if (isValidOid((oid_t)node->ival)) {
1009                     *(oid_t*)(dst+fd->dbsOffs) = (oid_t)node->ival;
1010                 } else {
1011                     return -1;
1012                 }
1013                 break;
1014               case dbField::tpString:
1015                 ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
1016                 len = (int)STRLEN(node->sval) + 1;
1017                 ((dbVarying*)(dst+fd->dbsOffs))->size = len;
1018                 memcpy(dst + offs, node->sval, len*sizeof(char_t));
1019                 offs += len*sizeof(char_t);
1020                 break;
1021               case dbField::tpRawBinary:
1022                 len = node->aggregate.nComponents;
1023                 component = node->aggregate.components;
1024                 elem = dst + fd->dbsOffs;
1025                 while (--len >= 0) {
1026                     *elem++ = (byte)component->ival;
1027                     component = component->next;
1028                 }
1029                 break;
1030               case dbField::tpRectangle:
1031                 len = node->aggregate.nComponents;
1032                 component = node->aggregate.components;
1033                 coord = (coord_t*)(dst + fd->dbsOffs);
1034                 assert(len == rectangle::dim*2);
1035                 while (--len >= 0) {
1036                     *coord++ = (component->type == dbList::nInteger)
1037                         ? (coord_t)component->ival : (coord_t)component->fval;
1038                     component = component->next;
1039                 }
1040                 break;
1041               case dbField::tpArray:
1042                 len = node->aggregate.nComponents;
1043                 elem = (byte*)DOALIGN(size_t(dst) + offs, fd->components->alignment);
1044                 offs = (int)(elem - dst);
1045                 ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
1046                 ((dbVarying*)(dst+fd->dbsOffs))->size = len;
1047                 elemSize = (int)fd->components->dbsSize;
1048                 elemOffs = len*elemSize;
1049                 offs += elemOffs;
1050                 component = node->aggregate.components;
1051                 while (--len >= 0) {
1052                     elemOffs = initializeRecordFields(component, elem, elemOffs,
1053                                                       fd->components);
1054                     if (elemOffs < 0) {
1055                         return elemOffs;
1056                     }
1057                     elemOffs -= elemSize;
1058                     elem += elemSize;
1059                     component = component->next;
1060                 }
1061                 offs += elemOffs;
1062                 break;
1063               case dbField::tpStructure:
1064                 offs = initializeRecordFields(node->aggregate.components,
1065                                               dst, offs, fd->components);
1066                 if (offs < 0) {
1067                     return offs;
1068                 }
1069             }
1070         }
1071         node = node->next;
1072     } while ((fd = fd->next) != first);
1073 
1074     return offs;
1075 }
1076 
1077 
insertRecord(dbList * list,dbTableDescriptor * desc)1078 bool dbSubSql::insertRecord(dbList* list, dbTableDescriptor* desc)
1079 {
1080     int size = calculateRecordSize(list, (int)desc->fixedSize, desc->columns);
1081     if (size < 0) {
1082         error("Incompatible types in insert statement");
1083         return false;
1084     }
1085     oid_t oid = allocateRow(desc->tableId, size, desc);
1086     dbPutTie tie;
1087     byte* dst = (byte*)putRow(tie, oid);
1088     if (initializeRecordFields(list, dst, (int)desc->fixedSize, desc->columns) < 0) {
1089         error("Conversion  error");
1090         return false;
1091     }
1092 
1093     dbFieldDescriptor* fd;
1094     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
1095         if ((fd->indexType & UNIQUE) != 0 && fd->type != dbField::tpRectangle) {
1096             if (!dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator)) {
1097                 for (dbFieldDescriptor* fdu = desc->indexedFields; fdu != fd; fdu = fdu->nextIndexedField) {
1098                     if ((fdu->indexType & UNIQUE) != 0 && fdu->type != dbField::tpRectangle) {
1099                         dbBtree::remove(this, fdu->bTree, oid, fdu->dbsOffs, fdu->comparator);
1100                     }
1101                 }
1102                 freeRow(desc->tableId, oid, desc);
1103                 error("Unique constraint violation");
1104                 return false;
1105             }
1106         }
1107     }
1108 
1109     size_t nRows = desc->nRows;
1110     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField){
1111         dbHashTable::insert(this, fd->hashTable, oid, fd->type, fd->dbsOffs, nRows);
1112     }
1113     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
1114         if (fd->type == dbField::tpRectangle) {
1115             dbRtree::insert(this, fd->bTree, oid, fd->dbsOffs);
1116         } else if ((fd->indexType & UNIQUE) == 0) {
1117             dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
1118         }
1119     }
1120     return true;
1121 }
1122 
readValues(dbList ** chain)1123 int dbSubSql::readValues(dbList** chain)
1124 {
1125     int i, n = 0;
1126     int tkn;
1127     dbList* node;
1128 
1129     while (true) {
1130         switch (scan()) {
1131           case tkn_lpar:
1132             node = new dbList(dbList::nTuple);
1133             node->aggregate.components = NULL;
1134             i = readValues(&node->aggregate.components);
1135             if (i < 0) {
1136                 return -1;
1137             }
1138             node->aggregate.nComponents = i;
1139             break;
1140           case tkn_rpar:
1141             return -n; // valid only in case of empty list
1142           case tkn_iconst:
1143             node = new dbList(dbList::nInteger);
1144             node->ival = ival;
1145             break;
1146           case tkn_true:
1147             node = new dbList(dbList::nBool);
1148             node->bval = true;
1149             break;
1150           case tkn_false:
1151             node = new dbList(dbList::nBool);
1152             node->bval = false;
1153             break;
1154           case tkn_fconst:
1155             node = new dbList(dbList::nReal);
1156             node->fval = fval;
1157             break;
1158           case tkn_sconst:
1159             node = new dbList(dbList::nString);
1160             node->sval = new char_t[STRLEN(buf)+1];
1161             STRCPY(node->sval, buf);
1162             break;
1163           case tkn_autoincrement:
1164             node = new dbList(dbList::nAutoinc);
1165             break;
1166           case tkn_error:
1167             return -1;
1168           default:
1169             error("Syntax error in insert list");
1170             return -1;
1171         }
1172         *chain = node;
1173         chain = &node->next;
1174         n += 1;
1175         if ((tkn = scan()) == tkn_rpar) {
1176             return n;
1177         }
1178         if (tkn != tkn_comma) {
1179             error("',' expected");
1180             return -1;
1181         }
1182     }
1183 }
1184 
1185 
readFieldName(int terminator)1186 dbFieldDescriptor* dbSubSql::readFieldName(int terminator)
1187 {
1188     int tkn;
1189 
1190     if (expect("table name", tkn_ident)) {
1191         dbTableDescriptor* desc;
1192         dbFieldDescriptor* fd;
1193         if ((desc = findTable(name)) == NULL) {
1194             error("No such table in database");
1195             return NULL;
1196         }
1197         if (expect(".", tkn_dot) && expect("field name", tkn_ident)) {
1198             if ((fd = desc->findSymbol(name)) == NULL) {
1199                 error("No such field in the table");
1200                 return NULL;
1201             } else if (fd->type == dbField::tpArray) {
1202                 error("Array components can not be indexed");
1203                 return NULL;
1204             }
1205         } else {
1206             return NULL;
1207         }
1208         while ((tkn = scan()) != terminator) {
1209             if (tkn != tkn_dot) {
1210                 error("'.' expected");
1211                 return NULL;
1212             }
1213             if (expect("field name", tkn_ident)) {
1214                 if ((fd = fd->findSymbol(name)) == NULL) {
1215                     error("No such field in the table");
1216                     return NULL;
1217                 } else if (fd->type == dbField::tpArray) {
1218                     error("Array components can not be indexed");
1219                     return NULL;
1220                 }
1221             } else {
1222                 return NULL;
1223             }
1224         }
1225         if (fd->type == dbField::tpStructure) {
1226             error("Structures can not be indexed");
1227             return NULL;
1228         }
1229         return fd;
1230     }
1231     return NULL;
1232 }
1233 
1234 
getListSize(dbExprNode * expr)1235 inline int getListSize(dbExprNode* expr)
1236 {
1237     int nElems;
1238     if (expr->type == tpList) {
1239         nElems = 0;
1240         if (expr->operand[0] != NULL) {
1241             do {
1242                 nElems += 1;
1243             } while ((expr = expr->operand[1]) != NULL);
1244         }
1245     } else {
1246         nElems = 1;
1247     }
1248     return nElems;
1249 }
1250 
updateFields(dbAnyCursor * cursor,dbUpdateElement * elems)1251 bool dbSubSql::updateFields(dbAnyCursor* cursor, dbUpdateElement* elems)
1252 {
1253      char_t buf[64], *src;
1254      dbInheritedAttribute iattr;
1255      dbSynthesizedAttribute sattr;
1256      iattr.db = this;
1257      iattr.oid = cursor->currId;
1258      iattr.table = cursor->table;
1259      iattr.record = cursor->tie.get();
1260      iattr.paramBase = (size_t)cursor->paramBase;
1261 
1262      do {
1263          dbExprNode* expr = elems->value;
1264          dbFieldDescriptor* fd = elems->field;
1265          execute(expr, iattr, sattr);
1266          byte* dst = cursor->record + fd->appOffs;
1267 
1268          switch (fd->type) {
1269            case dbField::tpArray:
1270            {
1271                int nElems = getListSize(expr);
1272                if (nElems != 0)
1273                {
1274                    switch (fd->components->type) {
1275                      case dbField::tpBool:
1276                      {
1277                          bool* arr = new bool[nElems];
1278                          switch (expr->type) {
1279                            case tpList:
1280                            {
1281                                int i = 0;
1282                                do {
1283                                    dbExprNode* elem = expr->operand[0];
1284                                    execute(elem, iattr, sattr);
1285                                    switch (elem->type) {
1286                                      case tpInteger:
1287                                        arr[i++] = sattr.ivalue != 0;
1288                                        continue;
1289                                      case tpBoolean:
1290                                        arr[i++] = sattr.bvalue;
1291                                        continue;
1292                                      case tpReal:
1293                                        arr[i++] = sattr.fvalue != 0;
1294                                        continue;
1295                                      case tpString:
1296                                        arr[i++] = *sattr.base == 'T' || *sattr.base == 't' || *sattr.base == '1';
1297                                        continue;
1298                                      default:
1299                                        return false;
1300                                    }
1301                                } while ((expr = expr->operand[1]) != NULL);
1302                                break;
1303                            }
1304                            case tpInteger:
1305                              arr[0] = sattr.ivalue != 0;
1306                              break;
1307                            case tpBoolean:
1308                              arr[0] = sattr.bvalue;
1309                              break;
1310                            case tpReal:
1311                              arr[0] = sattr.fvalue != 0;
1312                              break;
1313                            case tpString:
1314                              arr[0] = *sattr.base == 'T' || *sattr.base == 't' || *sattr.base == '1';
1315                              break;
1316                            default:
1317                              error("Invalid array element type");
1318                              return false;
1319                          }
1320                          ((dbArray<bool>*)dst)->assign(arr, nElems, false);
1321                          elems->strValue = (char_t*)arr;
1322                          continue;
1323                      }
1324                      case dbField::tpInt1:
1325                      {
1326                          int1* arr = new int1[nElems];
1327                          switch (expr->type) {
1328                            case tpList:
1329                            {
1330                                int i = 0;
1331                                do {
1332                                    dbExprNode* elem = expr->operand[0];
1333                                    execute(elem, iattr, sattr);
1334                                    switch (elem->type) {
1335                                      case tpInteger:
1336                                        arr[i++] = (int1)sattr.ivalue;
1337                                        continue;
1338                                      case tpBoolean:
1339                                        arr[i++] = sattr.bvalue ? 1 : 0;
1340                                        continue;
1341                                      case tpReal:
1342                                        arr[i++] = (int1)sattr.fvalue;
1343                                        continue;
1344                                      case tpString:
1345                                        arr[i++] = (int1)atoi((char*)sattr.base);
1346                                        continue;
1347                                      default:
1348                                        return false;
1349                                    }
1350                                } while ((expr = expr->operand[1]) != NULL);
1351                                break;
1352                            }
1353                            case tpInteger:
1354                              arr[0] = (int1)sattr.ivalue;
1355                              break;
1356                            case tpBoolean:
1357                              arr[0] = sattr.bvalue ? 1 : 0;
1358                              break;
1359                            case tpReal:
1360                              arr[0] = (int1)sattr.fvalue;
1361                              break;
1362                            case tpString:
1363                              arr[0] = (int1)atoi((char*)sattr.base);
1364                              break;
1365                            default:
1366                              error("Invalid array element type");
1367                              return false;
1368                          }
1369                          ((dbArray<int1>*)dst)->assign(arr, nElems, false);
1370                          elems->strValue = (char_t*)arr;
1371                          continue;
1372                      }
1373                      case dbField::tpInt2:
1374                      {
1375                          int2* arr = new int2[nElems];
1376                          switch (expr->type) {
1377                            case tpList:
1378                            {
1379                                int i = 0;
1380                                do {
1381                                    dbExprNode* elem = expr->operand[0];
1382                                    execute(elem, iattr, sattr);
1383                                    switch (elem->type) {
1384                                      case tpInteger:
1385                                        arr[i++] = (int2)sattr.ivalue;
1386                                        continue;
1387                                      case tpBoolean:
1388                                        arr[i++] = sattr.bvalue ? 1 : 0;
1389                                        continue;
1390                                      case tpReal:
1391                                        arr[i++] = (int2)sattr.fvalue;
1392                                        continue;
1393                                      case tpString:
1394                                        arr[i++] = (int2)atoi((char*)sattr.base);
1395                                        continue;
1396                                      default:
1397                                        return false;
1398                                    }
1399                                } while ((expr = expr->operand[1]) != NULL);
1400                                break;
1401                            }
1402                            case tpInteger:
1403                              arr[0] = (int2)sattr.ivalue;
1404                              break;
1405                            case tpBoolean:
1406                              arr[0] = sattr.bvalue ? 1 : 0;
1407                              break;
1408                            case tpReal:
1409                              arr[0] = (int2)sattr.fvalue;
1410                              break;
1411                            case tpString:
1412                              arr[0] = (int2)atoi((char*)sattr.base);
1413                              break;
1414                            default:
1415                              error("Invalid array element type");
1416                              return false;
1417                          }
1418                          ((dbArray<int2>*)dst)->assign(arr, nElems, false);
1419                          elems->strValue = (char_t*)arr;
1420                          continue;
1421                      }
1422                      case dbField::tpInt4:
1423                      {
1424                          int4* arr = new int4[nElems];
1425                          switch (expr->type) {
1426                            case tpList:
1427                            {
1428                                int i = 0;
1429                                do {
1430                                    dbExprNode* elem = expr->operand[0];
1431                                    execute(elem, iattr, sattr);
1432                                    switch (elem->type) {
1433                                      case tpInteger:
1434                                        arr[i++] = (int4)sattr.ivalue;
1435                                        continue;
1436                                      case tpBoolean:
1437                                        arr[i++] = sattr.bvalue ? 1 : 0;
1438                                        continue;
1439                                      case tpReal:
1440                                        arr[i++] = (int4)sattr.fvalue;
1441                                        continue;
1442                                      case tpString:
1443                                        arr[i++] = (int4)atoi((char*)sattr.base);
1444                                        continue;
1445                                      default:
1446                                        return false;
1447                                    }
1448                                } while ((expr = expr->operand[1]) != NULL);
1449                                break;
1450                            }
1451                            case tpInteger:
1452                              arr[0] = (int4)sattr.ivalue;
1453                              break;
1454                            case tpBoolean:
1455                              arr[0] = sattr.bvalue ? 1 : 0;
1456                              break;
1457                            case tpReal:
1458                              arr[0] = (int4)sattr.fvalue;
1459                              break;
1460                            case tpString:
1461                              arr[0] = (int4)atoi((char*)sattr.base);
1462                              break;
1463                            default:
1464                              error("Invalid array element type");
1465                              return false;
1466                          }
1467                          ((dbArray<int4>*)dst)->assign(arr, nElems, false);
1468                          elems->strValue = (char_t*)arr;
1469                          continue;
1470                      }
1471                      case dbField::tpInt8:
1472                      {
1473                          db_int8* arr = new db_int8[nElems];
1474                          switch (expr->type) {
1475                            case tpList:
1476                            {
1477                                int i = 0;
1478                                do {
1479                                    dbExprNode* elem = expr->operand[0];
1480                                    execute(elem, iattr, sattr);
1481                                    switch (elem->type) {
1482                                      case tpInteger:
1483                                        arr[i++] = sattr.ivalue;
1484                                        continue;
1485                                      case tpBoolean:
1486                                        arr[i++] = sattr.bvalue ? 1 : 0;
1487                                        continue;
1488                                      case tpReal:
1489                                        arr[i++] = (db_int8)sattr.fvalue;
1490                                        continue;
1491                                      case tpString:
1492                                        arr[i++] = atol((char*)sattr.base);
1493                                        continue;
1494                                      default:
1495                                        return false;
1496                                    }
1497                                } while ((expr = expr->operand[1]) != NULL);
1498                                break;
1499                            }
1500                            case tpInteger:
1501                              arr[0] = sattr.ivalue;
1502                              break;
1503                            case tpBoolean:
1504                              arr[0] = sattr.bvalue ? 1 : 0;
1505                              break;
1506                            case tpReal:
1507                              arr[0] = (db_int8)sattr.fvalue;
1508                              break;
1509                            case tpString:
1510                              arr[0] = atol((char*)sattr.base);
1511                              break;
1512                            default:
1513                              error("Invalid array element type");
1514                              return false;
1515                          }
1516                          ((dbArray<db_int8>*)dst)->assign(arr, nElems, false);
1517                          elems->strValue = (char_t*)arr;
1518                          continue;
1519                      }
1520                      case dbField::tpReal4:
1521                      {
1522                          real4* arr = new real4[nElems];
1523                          switch (expr->type) {
1524                            case tpList:
1525                            {
1526                                int i = 0;
1527                                do {
1528                                    dbExprNode* elem = expr->operand[0];
1529                                    execute(elem, iattr, sattr);
1530                                    switch (elem->type) {
1531                                      case tpInteger:
1532                                        arr[i++] = (real4)sattr.ivalue;
1533                                        continue;
1534                                      case tpBoolean:
1535                                        arr[i++] = (real4)(sattr.bvalue ? 1.0 : 0.0);
1536                                        continue;
1537                                      case tpReal:
1538                                        arr[i++] = (real4)sattr.fvalue;
1539                                        continue;
1540                                      case tpString:
1541                                        arr[i++] = (real4)atof((char*)sattr.base);
1542                                        continue;
1543                                      default:
1544                                        return false;
1545                                    }
1546                                } while ((expr = expr->operand[1]) != NULL);
1547                                break;
1548                            }
1549                            case tpInteger:
1550                              arr[0] = (real4)sattr.ivalue;
1551                              break;
1552                            case tpBoolean:
1553                              arr[0] = (real4)(sattr.bvalue ? 1.0 : 0.0);
1554                              break;
1555                            case tpReal:
1556                              arr[0] = (real4)sattr.fvalue;
1557                              break;
1558                            case tpString:
1559                              arr[0] = (real4)atof((char*)sattr.base);
1560                              break;
1561                            default:
1562                              error("Invalid array element type");
1563                              return false;
1564                          }
1565                          ((dbArray<real4>*)dst)->assign(arr, nElems, false);
1566                          elems->strValue = (char_t*)arr;
1567                          continue;
1568                      }
1569                      case dbField::tpReal8:
1570                      {
1571                          real8* arr = new real8[nElems];
1572                          switch (expr->type) {
1573                            case tpList:
1574                            {
1575                                int i = 0;
1576                                do {
1577                                    dbExprNode* elem = expr->operand[0];
1578                                    execute(elem, iattr, sattr);
1579                                    switch (elem->type) {
1580                                      case tpInteger:
1581                                        arr[i++] = (real8)sattr.ivalue;
1582                                        continue;
1583                                      case tpBoolean:
1584                                        arr[i++] = sattr.bvalue ? 1.0 : 0.0;
1585                                        continue;
1586                                      case tpReal:
1587                                        arr[i++] = sattr.fvalue;
1588                                        continue;
1589                                      case tpString:
1590                                        arr[i++] = atoi((char*)sattr.base);
1591                                        continue;
1592                                      default:
1593                                        return false;
1594                                    }
1595                                } while ((expr = expr->operand[1]) != NULL);
1596                                break;
1597                            }
1598                            case tpInteger:
1599                              arr[0] = (real8)sattr.ivalue;
1600                              break;
1601                            case tpBoolean:
1602                              arr[0] = sattr.bvalue ? 1.0 : 0.0;
1603                              break;
1604                            case tpReal:
1605                              arr[0] = sattr.fvalue;
1606                              break;
1607                            case tpString:
1608                              arr[0] = atof((char*)sattr.base);
1609                              break;
1610                            default:
1611                              error("Invalid array element type");
1612                              return false;
1613                          }
1614                          ((dbArray<real8>*)dst)->assign(arr, nElems, false);
1615                          elems->strValue = (char_t*)arr;
1616                          continue;
1617                      }
1618                      case dbField::tpReference:
1619                      {
1620                          oid_t* arr = new oid_t[nElems];
1621                          switch (expr->type) {
1622                            case tpList:
1623                            {
1624                                int i = 0;
1625                                do {
1626                                    dbExprNode* elem = expr->operand[0];
1627                                    execute(elem, iattr, sattr);
1628                                    switch (elem->type) {
1629                                      case tpInteger:
1630                                        arr[i++] = (oid_t)sattr.ivalue;
1631                                        continue;
1632                                      case tpReference:
1633                                        arr[i++] = sattr.oid;
1634                                        continue;
1635                                      case tpString:
1636                                        arr[i++] = (oid_t)atol((char*)sattr.base);
1637                                        continue;
1638                                      default:
1639                                        return false;
1640                                    }
1641                                } while ((expr = expr->operand[1]) != NULL);
1642                                break;
1643                            }
1644                            case tpInteger:
1645                              arr[0] = (oid_t)sattr.ivalue;
1646                              break;
1647                            case tpReference:
1648                              arr[0] = sattr.oid;
1649                              break;
1650                            case tpString:
1651                              arr[0] = (oid_t)atol((char*)sattr.base);
1652                              break;
1653                            default:
1654                              error("Invalid array element type");
1655                              return false;
1656                          }
1657                          ((dbArray<oid_t>*)dst)->assign(arr, nElems, false);
1658                          elems->strValue = (char_t*)arr;
1659                          continue;
1660                      }
1661                      default:
1662                        error("Usupported array type");
1663                        return false;
1664                    }
1665                } else {
1666                    dbAnyArray::arrayAllocator((dbAnyArray*)dst, NULL, 0);
1667                }
1668                continue;
1669            }
1670            case dbField::tpRectangle:
1671              if (expr->type != tpList) {
1672                  error("Array coordinates expected");
1673                  return false;
1674              } else {
1675                  rectangle& r = *(rectangle*)dst;
1676                  for (int i = 0; i < RECTANGLE_DIMENSION*2; i++) {
1677                      if (expr == NULL || expr->operand[0] == NULL) {
1678                          error("Bad rectangle constant");
1679                          return false;
1680                      }
1681                      dbExprNode* elem = expr->operand[0];
1682                      dbExprNode* tail = expr->operand[1];
1683                      if (elem->type == tpReal) {
1684                          r.boundary[i] = (coord_t)elem->fvalue;
1685                      } else if (elem->type == tpInteger) {
1686                          r.boundary[i] = (coord_t)elem->ivalue;
1687                      } else {
1688                          error("Bad rectangle constant");
1689                          return false;
1690                      }
1691                      expr = tail;
1692                  }
1693                  continue;
1694              }
1695            case dbField::tpBool:
1696              switch (expr->type) {
1697                case tpInteger:
1698                  *(bool*)dst = sattr.ivalue != 0;
1699                  continue;
1700                case tpBoolean:
1701                  *(bool*)dst = sattr.bvalue;
1702                  continue;
1703                case tpReal:
1704                  *(bool*)dst = sattr.fvalue != 0;
1705                  continue;
1706                case tpString:
1707                  *(bool*)dst = *sattr.base == 'T' || *sattr.base == 't' || *sattr.base == '1';
1708                  continue;
1709              }
1710              break;
1711            case dbField::tpInt1:
1712              switch (expr->type) {
1713                case tpInteger:
1714                  *(int1*)dst = (int1)sattr.ivalue;
1715                  continue;
1716                case tpBoolean:
1717                  *(int1*)dst = sattr.bvalue ? 1 : 0;
1718                  continue;
1719                case tpReal:
1720                  *(int1*)dst = (int1)sattr.fvalue;
1721                  continue;
1722                case tpString:
1723                  *(int1*)dst = (int1)atoi((char*)sattr.base);
1724                  continue;
1725              }
1726              break;
1727            case dbField::tpInt2:
1728              switch (expr->type) {
1729                case tpInteger:
1730                  *(int2*)dst = (int2)sattr.ivalue;
1731                  continue;
1732                case tpBoolean:
1733                  *(int2*)dst = sattr.bvalue ? 1 : 0;
1734                  continue;
1735                case tpReal:
1736                  *(int2*)dst = (int2)sattr.fvalue;
1737                  continue;
1738                case tpString:
1739                  *(int2*)dst = (int2)atoi((char*)sattr.base);
1740                  continue;
1741              }
1742              break;
1743            case dbField::tpInt4:
1744              switch (expr->type) {
1745                case tpInteger:
1746                  *(int4*)dst = (int4)sattr.ivalue;
1747                  continue;
1748                case tpBoolean:
1749                  *(int4*)dst = sattr.bvalue ? 1 : 0;
1750                  continue;
1751                case tpReal:
1752                  *(int4*)dst = (int4)sattr.fvalue;
1753                  continue;
1754                case tpString:
1755                  *(int4*)dst = (int1)atoi((char*)sattr.base);
1756                  continue;
1757              }
1758              break;
1759            case dbField::tpInt8:
1760              switch (expr->type) {
1761                case tpInteger:
1762                  *(db_int8*)dst = sattr.ivalue;
1763                  continue;
1764                case tpBoolean:
1765                  *(db_int8*)dst = sattr.bvalue ? 1 : 0;
1766                  continue;
1767                case tpReal:
1768                  *(db_int8*)dst = (db_int8)sattr.fvalue;
1769                  continue;
1770                case tpString:
1771                  *(db_int8*)dst = (db_int8)atoi((char*)sattr.base);
1772                  continue;
1773              }
1774              break;
1775            case dbField::tpReal4:
1776              switch (expr->type) {
1777                case tpInteger:
1778                  *(real4*)dst = (real4)sattr.ivalue;
1779                  continue;
1780                case tpBoolean:
1781                  *(real4*)dst = (real4)(sattr.bvalue ? 1.0 : 0.0);
1782                  continue;
1783                case tpReal:
1784                  *(real4*)dst = (real4)sattr.fvalue;
1785                  continue;
1786                case tpString:
1787                  *(real4*)dst = (real4)atof((char*)sattr.base);
1788                  continue;
1789              }
1790              break;
1791            case dbField::tpReal8:
1792              switch (expr->type) {
1793                case tpInteger:
1794                  *(real8*)dst = (real8)sattr.ivalue;
1795                  continue;
1796                case tpBoolean:
1797                  *(real8*)dst = sattr.bvalue ? 1.0 : 0.0;
1798                  continue;
1799                case tpReal:
1800                  *(real8*)dst = sattr.fvalue;
1801                  continue;
1802                case tpString:
1803                  *(real8*)dst = atof((char*)sattr.base);
1804                  continue;
1805              }
1806              break;
1807            case dbField::tpString:
1808              src = buf;
1809              switch (expr->type) {
1810                case tpInteger:
1811                  SPRINTF(SPRINTF_BUFFER(buf), T_INT8_FORMAT, sattr.ivalue);
1812                  break;
1813                case tpBoolean:
1814                  STRCPY(buf, sattr.bvalue ? STRLITERAL("t") : STRLITERAL("f"));
1815                  break;
1816                case tpReal:
1817                  SPRINTF(SPRINTF_BUFFER(buf), STRLITERAL("%f"), sattr.fvalue);
1818                  break;
1819                case tpString:
1820                  src = (char_t*)sattr.base;
1821                  break;
1822              }
1823              *(char_t**)dst = new char_t[STRLEN(src)+1];
1824              STRCPY(*(char_t**)dst, src);
1825              elems->strValue = *(char_t**)dst;
1826              continue;
1827            case dbField::tpReference:
1828              if (expr->type == tpReference) {
1829                  *(oid_t*)dst = sattr.oid;
1830                  continue;
1831              } else if (expr->type == tpInteger) {
1832                  *(oid_t*)dst = (oid_t)sattr.ivalue;
1833                  continue;
1834              }
1835          }
1836          error("Mismatched type of update expression");
1837          return false;
1838      } while ((elems = elems->next) != NULL);
1839 
1840      return true;
1841 }
1842 
deleteColumns(dbFieldDescriptor * columns)1843 void dbSubSql::deleteColumns(dbFieldDescriptor* columns)
1844 {
1845     if (columns != NULL) {
1846         dbFieldDescriptor *next, *fd = columns;
1847         do {
1848             next = fd->next;
1849             fd->type = dbField::tpUnknown;
1850             fd->longName = NULL;
1851             delete fd;
1852             fd = next;
1853         } while (next != columns);
1854     }
1855 }
1856 
profile()1857 void dbSubSql::profile()
1858 {
1859     printf("TABLES:\n");
1860     printf("   Fixed   Fields  Columns     Rows    Total  Aligned  TableName\n");
1861     printf("----------------------------------------------------------------\n");
1862     beginTransaction(dbSharedLock);
1863     for (dbTableDescriptor* desc=tables; desc != NULL; desc=desc->nextDbTable)
1864     {
1865         refreshTable(desc);
1866         size_t totalSize = 0;
1867         size_t totalAlignedSize = 0;
1868         oid_t oid = desc->firstRow;
1869         while (oid != 0) {
1870             dbRecord rec;
1871             getHeader(rec, oid);
1872             totalSize += rec.size;
1873             totalAlignedSize += DOALIGN(rec.size, dbAllocationQuantum);
1874             oid = rec.next;
1875         }
1876         printf("%8ld %8ld %8ld %8ld %8ld %8ld  %s\n",
1877                (long)desc->fixedSize,(long)desc->nFields, (long)desc->nColumns,
1878                (long)desc->nRows,  (long)totalSize, (long)totalAlignedSize,
1879                desc->name);
1880     }
1881 }
1882 
parse()1883 bool dbSubSql::parse()
1884 {
1885     dbTableDescriptor* desc;
1886     dbFieldDescriptor* fd;
1887     int tkn;
1888     bool outputOid, count;
1889     dbFieldDescriptor* columns = NULL;
1890 
1891     line = 1;
1892     pos = 0;
1893 
1894     while (true) {
1895         if (interactiveMode) {
1896             printf(prompt);
1897             tkn = scan();
1898             pos += (int)strlen(prompt);
1899         } else {
1900             tkn = scan();
1901         }
1902 
1903         switch (tkn) {
1904           case tkn_update:
1905             if (!opened) {
1906                 error("Database not opened");
1907                 continue;
1908             }
1909             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
1910                 error("Operation is not possible in read-only mode");
1911                 continue;
1912             }
1913             dotIsPartOfIdentifier = true;
1914             if (expect("table name", tkn_ident)) {
1915                 if ((desc = findTable(name)) == NULL) {
1916                     error("No such table in database");
1917                     continue;
1918                 }
1919                 if (!expect("set", tkn_set)) {
1920                     continue;
1921                 }
1922 
1923                 dbDatabaseThreadContext* ctx = threadContext.get();
1924                 byte *record = dbMalloc(desc->appSize);
1925                 memset(record, 0, desc->appSize);
1926                 ctx->interactive = true;
1927                 ctx->catched = true;
1928                 dbUpdateElement* elems = NULL;
1929                 if (!expect("field name", tkn_ident)) {
1930                     goto updateCleanup;
1931                 }
1932 
1933 #ifdef THROW_EXCEPTION_ON_ERROR
1934                 try {
1935 #else
1936                 if (setjmp(ctx->unwind) == 0) {
1937 #endif
1938 
1939                     char_t* condition = NULL;
1940                     int startPos = pos;
1941                     while (true) {
1942                         dbUpdateElement* elem = new dbUpdateElement;
1943                         dbFieldDescriptor* fd = desc->find(name);
1944                         if (fd == NULL) {
1945                             error("No such field in the table");
1946                             goto updateCleanup;
1947                         }
1948                         if (fd->type > dbField::tpArray && fd->type != dbField::tpRectangle) {
1949                             error("Field can not be updated");
1950                             goto updateCleanup;
1951                         }
1952                         elem->field = fd;
1953                         elem->next = elems;
1954                         elems = elem;
1955                         if (!expect("=", tkn_eq)) {
1956                             goto updateCleanup;
1957                         }
1958                         startPos = pos;
1959                         int ch = readExpression();
1960                         if (ch == EOF) {
1961                             error("unexpected end of input");
1962                             goto updateCleanup;
1963                         }
1964                         condition = stristr(buf, _T("where"));
1965                         if (condition != NULL) {
1966                             *condition = '\0';
1967                         }
1968                         dbExprNode* expr = ctx->compiler.compileExpression(desc, buf, startPos);
1969                         if (expr == NULL) {
1970                             goto updateCleanup;
1971                         }
1972                        if (expr->type > tpReference && expr->type != tpList) {
1973                             error("Invalid expression type");
1974                             goto updateCleanup;
1975                         }
1976                         elem->value = expr;
1977                         if (condition == NULL && ch == ',') {
1978                             if (!expect("field name", tkn_ident)) {
1979                                 goto updateCleanup;
1980                             }
1981                         } else {
1982                             break;
1983                         }
1984                     }
1985                     dbAnyCursor cursor(*desc, dbCursorForUpdate, record);
1986                     cursor.reset();
1987 
1988                     if (condition != NULL) {
1989                         query.pos = startPos + (int)(condition - buf) + 5;
1990                         query = condition + 5;
1991                         select(&cursor, query);
1992                         if (query.compileError()) {
1993                             goto updateCleanup;
1994                         }
1995                     } else {
1996                         select(&cursor);
1997                     }
1998                     if (cursor.gotoFirst()) {
1999                         do {
2000                             cursor.fetch();
2001                             if (!updateFields(&cursor, elems)) {
2002                                 goto updateCleanup;
2003                             }
2004                             if (!cursor.update()) {
2005                                 error("Unique constrain violation");
2006                                 goto updateCleanup;
2007                             }
2008                         } while (cursor.gotoNext());
2009                     }
2010                     printf("\n\t%d records updated\n", cursor.getNumberOfRecords());
2011 #ifdef THROW_EXCEPTION_ON_ERROR
2012                 } catch(dbException const&) {}
2013 #else
2014                 } else {
2015                     if (query.mutexLocked) {
2016                         query.mutexLocked = false;
2017                         query.mutex.unlock();
2018                     }
2019                 }
2020 #endif
2021               updateCleanup:
2022                 query.reset();
2023                 while (elems != NULL) {
2024                     dbUpdateElement* elem = elems;
2025                     elems = elems->next;
2026                     delete elem;
2027                 }
2028                 dbExprNodeAllocator::instance.reset();
2029                 ctx->catched = false;
2030                 dbFree(record);
2031             }
2032             break;
2033 
2034           case tkn_select:
2035             if (!opened) {
2036                 error("Database not opened");
2037                 continue;
2038             }
2039             outputOid = true;
2040             count = false;
2041             if ((tkn = scan()) == tkn_all) {
2042                 outputOid = false;
2043                 tkn = scan();
2044             } else if (tkn == tkn_count) {
2045                 if (!expect("'('", tkn_lpar)
2046                     || !expect("'*'", tkn_all)
2047                     || !expect("')'", tkn_rpar))
2048                 {
2049                     continue;
2050                 }
2051                 count = true;
2052                 tkn = scan();
2053             }
2054             columns = NULL;
2055             if (tkn != tkn_from) {
2056                 while (true) {
2057                     if (tkn != tkn_ident) {
2058                         error("Field name or 'from' expected");
2059                     }
2060                     dbFieldDescriptor* column = new dbFieldDescriptor(name);
2061                     if (columns != NULL) {
2062                         column->next = columns;
2063                         column->prev = columns->prev;
2064                         column->prev->next = column;
2065                         columns->prev = column;
2066                     } else {
2067                         columns = column;
2068                         column->prev = column->next = column;
2069                     }
2070                     tkn = scan();
2071                     if (tkn != tkn_comma) {
2072                         break;
2073                     }
2074                     tkn = scan();
2075                 }
2076             }
2077             if (tkn != tkn_from) {
2078                 deleteColumns(columns);
2079                 error("FROM expected");
2080                 continue;
2081             }
2082             dotIsPartOfIdentifier = true;
2083             if (scan() != tkn_ident) {
2084                 deleteColumns(columns);
2085                 error("Table name expected");
2086                 continue;
2087             }
2088             if ((desc = findTable(name)) != NULL) {
2089                 dbAnyCursor cursor(*desc, dbCursorViewOnly, NULL);
2090                 query.pos = pos;
2091                 dbDatabaseThreadContext* ctx = threadContext.get();
2092                 ctx->interactive = true;
2093                 ctx->catched = true;
2094 #ifdef THROW_EXCEPTION_ON_ERROR
2095                 try {
2096 #else
2097                 if (setjmp(ctx->unwind) == 0) {
2098 #endif
2099                     if (readCondition()) {
2100                         query = buf;
2101                         cursor.reset();
2102                         select(&cursor, query);
2103                         if (query.compileError()) {
2104                             deleteColumns(columns);
2105                             dbExprNodeAllocator::instance.reset();
2106                             ctx->catched = false;
2107                             break;
2108                         }
2109                     } else {
2110                         ctx->catched = false;
2111                         deleteColumns(columns);
2112                         break;
2113                     }
2114                     if (count) {
2115                         printf("%d records selected\n",
2116                                cursor.getNumberOfRecords());
2117                     } else {
2118                         if (cursor.gotoFirst()) {
2119                             dbGetTie tie;
2120                             dbFieldDescriptor* columnList;
2121                             if (columns != NULL) {
2122                                 columnList = columns;
2123                                 dbFieldDescriptor* cc = columns;
2124                                 do {
2125                                     dbFieldDescriptor* next = cc->next;
2126                                     dbFieldDescriptor* fd = desc->columns;
2127                                     do {
2128                                         if (cc->name == fd->name) {
2129                                             *cc = *fd;
2130                                             cc->next = next;
2131                                             goto Found;
2132                                         }
2133                                     } while ((fd = fd->next) != desc->columns);
2134 #ifdef UNICODE
2135                                     char columnName[128];
2136                                     char buf[256];
2137                                     wcstombs(columnName, cc->name, sizeof columnName);
2138                                     sprintf(buf, "Column '%s' is not found\n", columnName);
2139                                     error(buf);
2140 #else
2141                                     char buf[256];
2142                                     sprintf(buf, "Column '%s' is not found\n", cc->name);
2143                                     error(buf);
2144 #endif
2145                                   Found:
2146                                     PRINTF(STRLITERAL("%s "), cc->name);
2147                                     cc = next;
2148                                 } while (cc != columns);
2149                             } else {
2150                                 columnList = desc->columns;
2151                                 dbFieldDescriptor* fd = columnList;
2152                                 do {
2153                                     PRINTF(STRLITERAL("%s "), fd->name);
2154                                 } while ((fd = fd->next) != columnList);
2155                             }
2156                             if (outputOid) {
2157                                 printf("\n" OID_FORMAT ": (", cursor.currId);
2158                             } else {
2159                                 printf("\n(");
2160                             }
2161                             dumpRecord((byte*)getRow(tie, cursor.currId), columnList);
2162                             printf(")");
2163                             while (cursor.gotoNext()) {
2164                                 if (outputOid) {
2165                                     printf(",\n" OID_FORMAT ": (", cursor.currId);
2166                                 } else {
2167                                     printf(",\n(");
2168                                 }
2169                                 dumpRecord((byte*)getRow(tie, cursor.currId), columnList);
2170                                 printf(")");
2171                             }
2172                             printf("\n\t%d records selected\n",
2173                                    cursor.getNumberOfRecords());
2174                         } else {
2175                             fprintf(stderr, "No records selected\n");
2176                         }
2177                     }
2178 #ifdef THROW_EXCEPTION_ON_ERROR
2179                 } catch(dbException const&) {}
2180 #else
2181                 } else {
2182                     if (query.mutexLocked) {
2183                         query.mutexLocked = false;
2184                         query.mutex.unlock();
2185                     }
2186                 }
2187 #endif
2188                 deleteColumns(columns);
2189                 ctx->catched = false;
2190             } else {
2191                 error("No such table in database");
2192             }
2193             break;
2194 
2195           case tkn_open:
2196             if (expect("database file name", tkn_sconst)) {
2197                 if (opened) {
2198                     delete[] databasePath;
2199                     close();
2200                     while (droppedTables != NULL) {
2201                         dbTableDescriptor* next = droppedTables->nextDbTable;
2202                         delete droppedTables;
2203                         droppedTables = next;
2204                     }
2205                     opened = false;
2206                 }
2207                 time_t transactionCommitDelay = 0;
2208                 char* delay = getenv("GIGABASE_COMMIT_DELAY");
2209                 if (delay != NULL) {
2210                     transactionCommitDelay = atoi(delay);
2211                 }
2212                 if (!open(buf, transactionCommitDelay)) {
2213                     fprintf(stderr, "Database not opened\n");
2214                 } else {
2215                     databasePath = new char_t[STRLEN(buf) + 1];
2216                     STRCPY(databasePath, buf);
2217                     opened = true;
2218                     metatable = loadMetaTable();
2219                     existedTables = tables;
2220                     char* backupName = getenv("GIGABASE_BACKUP_NAME");
2221                     if (backupName != NULL) {
2222                         char* backupPeriod = getenv("GIGABASE_BACKUP_PERIOD");
2223                         time_t period = 60*60*24; // one day
2224                         if (backupPeriod != NULL) {
2225                             period = atoi(backupPeriod);
2226                         }
2227 #ifdef UNICODE
2228                         char_t backupFilePath[1024];
2229                         mbstowcs(backupFilePath, backupName, (sizeof backupFilePath)/sizeof(wchar_t));
2230 #else
2231                         char* backupFilePath = backupName;
2232 #endif
2233                         PRINTF(_T("Schedule backup to file %s each %u seconds\n"),
2234                                backupFilePath, (unsigned)period);
2235                         scheduleBackup(backupFilePath, period);
2236                     }
2237                 }
2238             }
2239             break;
2240 
2241           case tkn_drop:
2242             if (!opened) {
2243                 error("Database not opened");
2244                 continue;
2245             }
2246             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2247                 error("Operation is not possible in read-only mode");
2248                 continue;
2249             }
2250             switch (scan()) {
2251               case tkn_table:
2252                 dotIsPartOfIdentifier = true;
2253                 if (expect("table name", tkn_ident)) {
2254                     desc = findTable(name);
2255                     if (desc == NULL) {
2256                         error("No such table in database");
2257                     } else {
2258                         dropTable(desc);
2259                         if (desc == existedTables) {
2260                             existedTables = desc->nextDbTable;
2261                         }
2262                         unlinkTable(desc);
2263                         desc->nextDbTable = droppedTables;
2264                         droppedTables = desc;
2265                     }
2266                 }
2267                 break;
2268               case tkn_hash:
2269               case tkn_index:
2270                 fd = readFieldName();
2271                 if (fd != NULL) {
2272                     if (fd->bTree == 0) {
2273                         error("There is no index for this field");
2274                     } else {
2275                         dropIndex(fd);
2276                     }
2277                 }
2278                 break;
2279               default:
2280                 error("Expecting 'table', 'hash' or 'index' keyword");
2281                 continue;
2282             }
2283             break;
2284 
2285           case tkn_rename:
2286             if (!opened) {
2287                 error("Database not opened");
2288                 continue;
2289             }
2290             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2291                 error("Operation is not possible in read-only mode");
2292                 continue;
2293             }
2294             fd = readFieldName(tkn_to);
2295             if (fd != NULL) {
2296                 if (expect("new field name", tkn_ident) && expect(";", tkn_semi)) {
2297                     beginTransaction(dbExclusiveLock);
2298                     fd->name = name;
2299                     delete[] fd->longName;
2300                     fd->longName = new char_t[STRLEN(name)+1];
2301                     STRCPY(fd->longName, name);
2302                     size_t newSize = sizeof(dbTable) + desc->nFields*sizeof(dbField)
2303                         + fd->defTable->totalNamesLength()*sizeof(char_t);
2304                     dbPutTie tie;
2305                     dbTable* table = (dbTable*)putRow(tie, fd->defTable->tableId, newSize);
2306                     fd->defTable->storeInDatabase(table);
2307                 }
2308             }
2309             break;
2310 
2311           case tkn_backup:
2312             if (!opened) {
2313                 error("Database not opened");
2314                 continue;
2315             } else {
2316                 int backupFlags = 0;
2317                 tkn = scan();
2318                 if (tkn == tkn_compactify) {
2319                     backupFlags |= BCK_COMPACTIFY;
2320                     tkn = scan();
2321                 }
2322                 if (tkn == tkn_incremental) {
2323                     backupFlags |= BCK_INCREMENTAL;
2324                     tkn = scan();
2325                 }
2326                 if (tkn == tkn_reorder) {
2327                     backupFlags |= BCK_REORDER;
2328                     tkn = scan();
2329                 }
2330                 if (tkn != tkn_sconst) {
2331                     error("Backup file name expected");
2332                 } else {
2333                     if (!backup(buf, backupFlags)) {
2334                         printf("Backup failed\n");
2335                     } else {
2336                         while (droppedTables != NULL) {
2337                             dbTableDescriptor* next = droppedTables->nextDbTable;
2338                             delete droppedTables;
2339                             droppedTables = next;
2340                         }
2341                         commit();
2342                         existedTables = tables;
2343                     }
2344                 }
2345             }
2346             continue;
2347 
2348           case tkn_restore:
2349             if (opened) {
2350                 error("Can not restore online database");
2351                 continue;
2352             }
2353             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2354                 error("Operation is not possible in read-only mode");
2355                 continue;
2356             }
2357             if (expect("backup file name", tkn_sconst)) {
2358                 char_t bckName[initBufSize];
2359                 STRCPY(bckName, buf);
2360                 if (expect("database file name", tkn_sconst)) {
2361                     if (!restore(bckName, buf)) {
2362                         printf("Restore failed\n");
2363                     }
2364                 }
2365             }
2366             break;
2367 
2368           case tkn_alter:
2369             if (!opened) {
2370                 error("Database not opened");
2371                 continue;
2372             }
2373             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2374                 error("Operation is not possible in read-only mode");
2375                 continue;
2376             }
2377             switch (scan()) {
2378               case tkn_table:
2379                 updateTable(false);
2380                 break;
2381               default:
2382                 error("Expecting 'table' keyword");
2383                 continue;
2384             }
2385             break;
2386 
2387 
2388           case tkn_create:
2389             if (!opened) {
2390                 error("Database not opened");
2391                 continue;
2392             }
2393             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2394                 error("Operation is not possible in read-only mode");
2395                 continue;
2396             }
2397             switch (scan()) {
2398               case tkn_hash:
2399               case tkn_index:
2400                 if (!expect("on", tkn_on)) {
2401                     continue;
2402                 }
2403                 fd = readFieldName();
2404                 if (fd != NULL) {
2405                     if (fd->bTree != 0) {
2406                         error("Index already exists");
2407                     } else {
2408                         createIndex(fd);
2409                     }
2410                 }
2411                 break;
2412 
2413               case tkn_table:
2414                 updateTable(true);
2415                 break;
2416 
2417               default:
2418                 error("Expecting 'table', 'hash' or 'index' keyword");
2419                 continue;
2420             }
2421             break;
2422 
2423           case tkn_insert:
2424             if (!opened) {
2425                 error("Database not opened");
2426                 continue;
2427             }
2428             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2429                 error("Operation is not possible in read-only mode");
2430                 continue;
2431             }
2432             if (expect("into", tkn_into)) {
2433                 dotIsPartOfIdentifier = true;
2434                 if (expect("table name", tkn_ident)) {
2435                     if ((desc = findTable(name)) == NULL) {
2436                         error("No such table in database");
2437                         continue;
2438                     }
2439                     if (!expect("values", tkn_values)) {
2440                         continue;
2441                     }
2442                     beginTransaction(dbExclusiveLock);
2443                     modified = true;
2444                     while (expect("(", tkn_lpar)) {
2445                         dbList* list = NULL;
2446                         int n = readValues(&list);
2447                         if (n <= 0 || !insertRecord(list, desc)) {
2448                             if (n == 0) {
2449                                 error("Empty fields list");
2450                             }
2451                             tkn = tkn_semi; // just avoid extra error messages
2452                         } else {
2453                             tkn = scan();
2454                         }
2455                         while (list != NULL) {
2456                             dbList* tail = list->next;
2457                             delete list;
2458                             list = tail;
2459                         }
2460                         if (tkn == tkn_semi) {
2461                             break;
2462                         } else if (tkn != tkn_comma) {
2463                             error("';' or ',' expected");
2464                         }
2465                     }
2466                 }
2467             }
2468             break;
2469 
2470           case tkn_delete:
2471             if (!opened) {
2472                 error("Database not opened");
2473                 continue;
2474             }
2475             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2476                 error("Operation is not possible in read-only mode");
2477                 continue;
2478             }
2479             if (expect("from", tkn_from)) {
2480                 dotIsPartOfIdentifier = true;
2481                 if (expect("table name", tkn_ident)) {
2482                     if ((desc = findTable(name)) == NULL) {
2483                         error("No such table in database");
2484                         continue;
2485                     }
2486                     dbAnyCursor cursor(*desc, dbCursorForUpdate, NULL);
2487                     dbDatabaseThreadContext* ctx = threadContext.get();
2488                     ctx->interactive = true;
2489                     ctx->catched = true;
2490 
2491 #ifdef THROW_EXCEPTION_ON_ERROR
2492                     try {
2493 #else
2494                         if (setjmp(ctx->unwind) == 0) {
2495 #endif
2496                             if (readCondition()) {
2497                                 query = buf;
2498                                 cursor.reset();
2499                                 select(&cursor, query);
2500                                 if (query.compileError()) {
2501                                     dbExprNodeAllocator::instance.reset();
2502                                     ctx->catched = false;
2503                                     break;
2504                                 }
2505                             } else {
2506                                 ctx->catched = false;
2507                                 break;
2508                             }
2509                             int n_deleted = cursor.getNumberOfRecords();
2510                             cursor.removeAllSelected();
2511                             printf("\n\t%d records deleted\n", n_deleted);
2512 #ifdef THROW_EXCEPTION_ON_ERROR
2513                         } catch(dbException const&) {}
2514 #else
2515                     } else {
2516                         if (query.mutexLocked) {
2517                             query.mutexLocked = false;
2518                             query.mutex.unlock();
2519                         }
2520                     }
2521 #endif
2522                     ctx->catched = false;
2523                 }
2524             }
2525             break;
2526 
2527           case tkn_commit:
2528             if (!opened) {
2529                 error("Database not opened");
2530             } else {
2531                 while (droppedTables != NULL) {
2532                     dbTableDescriptor* next = droppedTables->nextDbTable;
2533                     delete droppedTables;
2534                     droppedTables = next;
2535                 }
2536                 commit();
2537                 existedTables = tables;
2538             }
2539             continue;
2540 
2541           case tkn_rollback:
2542             if (!opened) {
2543                 error("Database not opened");
2544             } else {
2545                 while (droppedTables != NULL) {
2546                     dbTableDescriptor* next = droppedTables->nextDbTable;
2547                     linkTable(droppedTables, droppedTables->tableId);
2548                     droppedTables = next;
2549                 }
2550                 rollback();
2551                 while (tables != existedTables) {
2552                     dbTableDescriptor* table = tables;
2553                     unlinkTable(table);
2554                     delete table;
2555                 }
2556             }
2557             continue;
2558 
2559           case tkn_memory:
2560             if (!opened) {
2561                 error("Database not opened");
2562             } else {
2563                 dbMemoryStatistic stat;
2564                 beginTransaction(dbSharedLock);
2565                 getMemoryStatistic(stat);
2566                 printf("Used memory: %ld\nFree memory: %ld\nNumber of holes: %ld\nMaximal hole size: %ld\nMinimal hole size: %ld\nAverage hole size: %ld\n\n",
2567                        (long)stat.used,
2568                        (long)stat.free,
2569                        (long)stat.nHoles,
2570                        (long)stat.maxHoleSize,
2571                        (long)stat.minHoleSize,
2572                        (long)(stat.nHoles != 0 ? (stat.free / stat.nHoles) : 0));
2573                 for (int i = 0; i < dbDatabaseOffsetBits; i++) {
2574                     if (stat.nHolesOfSize[i] != 0) {
2575                         printf("Number of holes of size [%ld...%ld): %ld\n", 1L << i, 1L << (i+1), (long)stat.nHolesOfSize[i]);
2576                     }
2577                 }
2578             }
2579             break;
2580 
2581           case tkn_profile:
2582             if (!opened) {
2583                 error("Database not opened");
2584             } else {
2585                 profile();
2586             }
2587             break;
2588 
2589           case tkn_show:
2590             if (!opened) {
2591                 error("Database not opened");
2592                 continue;
2593             } else {
2594                 beginTransaction(dbSharedLock);
2595                 printf("GigaBASE version  :  %d.%02d\n"
2596                        "Database version  :  %d.%02d\n"
2597                        "Database file size: " INT8_FORMAT " Kb\n"
2598                        "Used database size: " INT8_FORMAT " Kb\n"
2599                        "Object index size : " INT8_FORMAT " handles\n"
2600                        "Used part of index: " INT8_FORMAT " handles\n",
2601                        GIGABASE_MAJOR_VERSION, GIGABASE_MINOR_VERSION,
2602                        header->versionMajor, header->versionMinor,
2603                        db_int8(header->root[1-curr].size / 1024),
2604                        db_int8(used() / 1024),
2605                        db_int8(header->root[1-curr].indexSize),
2606                        db_int8(header->root[1-curr].indexUsed)
2607                        );
2608                 printf("\nTABLES:\n");
2609                 printf("OID       FixedSize   Fields  Columns Rows     TableName\n");
2610                 printf("---------------------------------------------------------\n");
2611                 for (dbTableDescriptor* desc=tables; desc != NULL; desc=desc->nextDbTable)
2612                 {
2613                     PRINTF(STRLITERAL("0x%06x  %8ld %8ld %8ld %8ld  %s\n"),
2614                            (int)desc->tableId, (long)desc->fixedSize,
2615                            (long)desc->nFields, (long)desc->nColumns, (long)desc->nRows, desc->name);
2616                 }
2617             }
2618             break;
2619 
2620           case tkn_describe:
2621             if (!opened) {
2622                 error("Database not opened");
2623                 continue;
2624             }
2625             dotIsPartOfIdentifier = true;
2626             if (expect("table name", tkn_ident)) {
2627                 if ((desc = findTable(name)) == NULL) {
2628                     error("No such table in database");
2629                     continue;
2630                 }
2631                 PRINTF(_T("\nOID=0x%06x, TableName=%s\n"), (int)desc->tableId, desc->name);
2632                 printf("No Index FieldType        RefTableName     FieldName        InverseFieldName Flg\n");
2633                 printf("--------------------------------------------------------------------------------\n");
2634                 dbFieldDescriptor* fd = desc->columns;
2635                 for (int i = (int)desc->nColumns; --i >= 0;) {
2636                     PRINTF(STRLITERAL("%-2d %-5s %-16s %-16s %-16s %-16s %x\n"),
2637                            fd->fieldNo,
2638                            fd->bTree != 0 ? "+" : "-",
2639                            typeMnem[fd->type],
2640                            fd->refTableName != NULL
2641                                ? fd->refTableName
2642                                : (fd->type == dbField::tpArray && fd->components->refTableName != NULL)
2643                                   ? fd->components->refTableName
2644                                   : _T("(null)"),
2645                            fd->name,
2646                            (fd->inverseRefName != NULL ? fd->inverseRefName : _T("(null)")),
2647                            fd->indexType
2648                         );
2649                     fd = fd->next;
2650                 }
2651             }
2652             continue;
2653 
2654           case tkn_export:
2655             if (!opened) {
2656                 error("Database not opened");
2657                 continue;
2658             }
2659             if (expect("xml file name", tkn_sconst)) {
2660                 FILE* f;
2661                 if (STRCMP(buf, _T("-")) == 0) {
2662                     f = stdout;
2663                 } else {
2664 #ifdef UNICODE
2665 #if defined(_WIN32)
2666                     f = _wfopen(buf, _T("w"));
2667 #else
2668                     char filePath[1024];
2669                     wcstombs(filePath, buf, sizeof filePath);
2670                     f = fopen(filePath, "w");
2671 #endif
2672 #else
2673                     f = fopen(buf, "w");
2674 #endif
2675                 }
2676                 if (f != NULL) {
2677 
2678                     dbArray<char_t*> tables;
2679                     SelectionMethod  method = sel_all;
2680 
2681                     if (parseExportTables(tables, method)) {
2682                         exportDatabaseToXml(f, tables.get(), tables.length(), method);
2683                     }
2684                     fclose(f);
2685 
2686                 } else {
2687                     error("Failed to open output file");
2688                 }
2689             }
2690             break;
2691 
2692           case tkn_import:
2693             if (!opened) {
2694                 error("Database not opened");
2695                 continue;
2696             }
2697             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2698                 error("Operation is not possible in read-only mode");
2699                 continue;
2700             }
2701             if (expect("xml file name", tkn_sconst)) {
2702                 FILE* f;
2703                 if (STRCMP(buf, _T("-")) == 0) {
2704                     f = stdin;
2705                 } else {
2706 #ifdef UNICODE
2707 #if defined(_WIN32)
2708                     f = _wfopen(buf, _T("r"));
2709 #else
2710                     char filePath[1024];
2711                     wcstombs(filePath, buf, sizeof filePath);
2712                     f = fopen(filePath, "r");
2713 #endif
2714 #else
2715                     f = fopen(buf, "r");
2716 #endif
2717                 }
2718                 if (f != NULL) {
2719                     if (!importDatabaseFromXml(f)) {
2720                         error("Import from XML file failed: incorrect file format");
2721                     }
2722                     fclose(f);
2723                 } else {
2724                     error("Failed to open input file");
2725                 }
2726             }
2727             break;
2728 
2729           case tkn_autocommit:
2730             switch (scan()) {
2731               case tkn_on:
2732                 autocommit = true;
2733                 break;
2734                case tkn_off:
2735                 autocommit = false;
2736                 break;
2737               default:
2738                 error("ON or OFF expected");
2739             }
2740             continue;
2741 
2742           case tkn_help:
2743             fprintf(stderr, "SubSQL commands:\n\n\
2744 open 'database-file-name' ';'\n\
2745 select ('*') from <table-name> where <condition> ';'\n\
2746 create table <table-name> '('<field-name> <field-type> {',' <field-name> <field-type>}')' ';' \n\
2747 alter table <table-name> '('<field-name> <field-type> {',' <field-name> <field-type>}')' ';' \n\
2748 rename <table-name> '.' <old-field-name> 'to' <new-field-name> ';' \n\
2749 update <table-name> set <field-name> '=' <expression> {',' <field-name> '=' <expression>} where <condition> ';'\n\
2750 delete from <table-name>\n\
2751 drop table <table-name>\n\
2752 drop index <table-name> {'.' <field-name>} ';'\n\
2753 create index on <table-name> {'.' <field-name>} ';'\n\
2754 drop hash <table-name> {'.' <field-name>};\n\
2755 create hash on <table-name> {'.' <field-name>}field> ';'\n\
2756 insert into <table-name> values '(' <value>{',' <value>} ')' ';'\n\
2757 backup [compactify] 'backup-file-name'\n\
2758 restore 'backup-file-name' 'database-file-name'\n\
2759 start server URL number-of-threads\n\
2760 stop server URL\n\
2761 start http server URL\n\
2762 stop http server\n\
2763 describe <table-name>\n\
2764 import 'xml-file-name'\n\
2765 export 'xml-file-name' [ ('+' | '-') <table-name> ( ',' <table-name>)* ] ';'\n\
2766 commit\n\
2767 rollback\n\
2768 autocommit (on|off)\n\
2769 show\n\
2770 profile\n\
2771 exit\n\
2772 help\n\n");
2773             continue;
2774           case tkn_start:
2775             if (!opened) {
2776                 error("Database not opened");
2777             } else {
2778                 commit(); // allow server threads to process
2779                 existedTables = tables;
2780                 tkn = scan();
2781                 if (tkn == tkn_http) {
2782                     if (expect("server", tkn_server)
2783                         && expect("HTTP server URL", tkn_sconst))
2784                     {
2785 #if !THREADS_SUPPORTED
2786                         error("Database was build without pthread support");
2787 #else
2788                         startHttpServer(buf);
2789 #endif
2790                     }
2791                 } else if (tkn == tkn_server && expect("server URL", tkn_sconst)) {
2792 #if !THREADS_SUPPORTED
2793                     error("Database was build without pthread support");
2794 #else
2795                     dbServer* server = dbServer::find(buf);
2796                     if (server == NULL) {
2797                         char_t* serverURL = new char_t[STRLEN(buf)+1];
2798                         STRCPY(serverURL, buf);
2799                         if (expect("number of threads", tkn_iconst)) {
2800                             server = new dbServer(this, serverURL, (int)ival);
2801                             PRINTF(_T("Server started for URL %s\n"), serverURL);
2802                         }
2803                         delete[] serverURL;
2804                     }
2805                     if (server != NULL) {
2806                         server->start();
2807                     }
2808 #endif
2809                 } else {
2810                     error("Token 'server' expected");
2811                 }
2812             }
2813             continue;
2814           case tkn_stop:
2815             tkn = scan();
2816             if (tkn == tkn_http) {
2817                 if (expect("server", tkn_server) && expect("HTTP server URL", tkn_sconst))
2818                 {
2819 #if !THREADS_SUPPORTED
2820                     error("Database was build without pthread support");
2821 #else
2822                     stopHttpServer(buf);
2823 #endif
2824                 }
2825             } else if (tkn == tkn_server) {
2826                 if (expect("server URL", tkn_sconst))
2827                 {
2828 #if !THREADS_SUPPORTED
2829                     error("Database was build without pthread support");
2830 #else
2831                     dbServer* server = dbServer::find(buf);
2832                     if (server != NULL) {
2833                         server->stop();
2834                         PRINTF(_T("Server stopped for URL %s\n"), buf);
2835                     } else {
2836                         FPRINTF(stderr, _T("No server was started for URL %s\n"), buf);
2837                     }
2838 #endif
2839                 }
2840             } else {
2841                 error("Token 'server' expected");
2842             }
2843             continue;
2844           case tkn_semi:
2845             putchar('\n');
2846             // no break
2847           case tkn_error:
2848             continue;
2849           case tkn_exit:
2850             return false;
2851           case tkn_version:
2852             printf("GigaBASE version %d.%02d\n", GIGABASE_MAJOR_VERSION, GIGABASE_MINOR_VERSION);
2853             continue;
2854           case tkn_eof:
2855             return true;
2856           default:
2857             error("Unexpected token");
2858             continue;
2859         }
2860         if (autocommit || !modified) {
2861             while (droppedTables != NULL) {
2862                 dbTableDescriptor* next = droppedTables->nextDbTable;
2863                 delete droppedTables;
2864                 droppedTables = next;
2865             }
2866             commit();
2867             existedTables = tables;
2868         }
2869     }
2870 }
2871 
parseExportTables(dbArray<char_t * > & tables,SelectionMethod & method)2872 bool dbSubSql::parseExportTables(dbArray<char_t*>/*OUT*/ &tables, SelectionMethod /*OUT*/ &method)
2873 {
2874     int tk = scan();
2875 
2876     switch(tk)
2877     {
2878     /* semi-colon: end of SQL statement. Exports all tables */
2879     case tkn_semi:        method = sel_all;            return true;
2880 
2881     /**    '+' or '-' : select tables.
2882      *    Reads next token: first tablename.
2883      */
2884     case tkn_include:    method = sel_named_only;    tk = scan(); break;
2885     case tkn_exclude:    method = sel_all_except;    tk = scan(); break;
2886 
2887     /**    Convenience: if filename is followed by tablename an implicit '+' is assumed.
2888      *    The first tablename alread read, no need to call scan()
2889      */
2890     case tkn_ident:        method = sel_named_only;    break;
2891 
2892     default:
2893         error("Table selection method ('+' or '-' ) or ';' expected ");
2894         return false;
2895     }
2896 
2897     /* List of tablenames [table1, table2 ,table3] */
2898     for(;;)
2899     {
2900         if (tk != tkn_ident)
2901         {
2902             error("Expected table name");
2903             return false;
2904         }
2905 
2906         if (findTable(name) == NULL)
2907         {
2908             error("No such table in database");
2909             return false;
2910         }
2911         tables.append(name);
2912 
2913         /* ';' = end of list, ',' = seperator, else fail */
2914         int tk = scan();
2915         if (tk == tkn_semi) break;
2916         if (tk != tkn_comma)
2917         {
2918             error("Comma expected");
2919             return false;
2920         }
2921 
2922         /* prepare: next token */
2923         tk = scan();
2924     }
2925 
2926     return true;
2927 }
2928 
2929 
handleError(dbErrorClass error,char const * msg,int arg)2930 void dbSubSql::handleError(dbErrorClass error, char const* msg, int arg)
2931 {
2932     dbDatabaseThreadContext* ctx = threadContext.get();
2933     if (ctx == NULL || ctx->interactive) {
2934         const int screenWidth = 80;
2935         int col;
2936         switch (error) {
2937           case QueryError:
2938             col = arg % screenWidth;
2939             if (interactiveMode) {
2940                 while (--col >= 0) putc('-', stderr);
2941                 fprintf(stderr, "^\n%s\n", msg);
2942             } else {
2943                 fprintf(stderr, "%s at line %d position %d\n", msg, line, arg);
2944             }
2945             break;
2946           case ArithmeticError:
2947             fprintf(stderr, "%s\n", msg);
2948             break;
2949           case IndexOutOfRangeError:
2950             fprintf(stderr, "Index %d is out of range\n", arg);
2951             break;
2952           case NullReferenceError:
2953             fprintf(stderr, "Null object reference is accessed\n");
2954             break;
2955           case DatabaseOpenError:
2956             return;
2957           default:
2958             dbDatabase::handleError(error, msg, arg);
2959         }
2960         //
2961         // Recovery
2962         //
2963         if (interactiveMode) {
2964             int ch;
2965             while ((ch = get()) != '\n' && ch != T_EOF);
2966         } else {
2967             fseek(in, 0, SEEK_END);
2968         }
2969     }
2970     switch (error) {
2971       case DatabaseOpenError:
2972       case InconsistentInverseReference:
2973         fprintf(stderr, "%s\n", msg);
2974         break;
2975       default:
2976 #ifdef THROW_EXCEPTION_ON_ERROR
2977         if (msg == NULL) {
2978             msg = errorMessage[error];
2979         }
2980         throw dbException(error, msg, arg);
2981 #else
2982         if (ctx != NULL) {
2983             if (ctx->catched) {
2984                 longjmp(ctx->unwind, error);
2985             } else {
2986                 abort();
2987             }
2988         }
2989 #endif
2990     }
2991 }
2992 
run(int argc,char * argv[])2993 void dbSubSql::run(int argc, char* argv[])
2994 {
2995     int i;
2996     bool daemon = false;
2997     for (i = 1; i < argc; i++) {
2998         if (strcmp(argv[i], "-") == 0) {
2999             break;
3000         }
3001         if (strcmp(argv[i], "-daemon") == 0) {
3002             daemon = true;
3003             continue;
3004         }
3005         in = fopen(argv[i], "r");
3006         if (in == NULL) {
3007             fprintf(stderr, "Failed to open '%s' file\n", argv[i]);
3008         } else {
3009             if (!parse()) {
3010                 if (opened) {
3011                     delete[] databasePath;
3012                     close();
3013                 }
3014 #if THREADS_SUPPORTED
3015                 dbServer::cleanup();
3016 #endif
3017                 return;
3018             }
3019         }
3020     }
3021     if (i == argc) {
3022         printf("SubSQL interactive utility for GigaBASE v. %d.%02d\n"
3023                "Type 'help' for more information\n",
3024                GIGABASE_MAJOR_VERSION, GIGABASE_MINOR_VERSION);
3025         interactiveMode = true;
3026         dbDatabaseThreadContext* ctx = threadContext.get();
3027         if (ctx != NULL) {
3028             ctx->interactive = true;
3029         }
3030     }
3031     if (daemon) {
3032         dbMutex mutex;
3033         dbCriticalSection cs(mutex);
3034         daemonTerminationEvent.open();
3035         daemonTerminationEvent.wait(mutex);
3036         daemonTerminationEvent.close();
3037     } else {
3038         in = stdin;
3039         parse();
3040     }
3041     if (opened) {
3042         delete[] databasePath;
3043         close();
3044     }
3045 #if THREADS_SUPPORTED
3046     dbServer::cleanup();
3047 #endif
3048 }
3049 
3050 #define HTML_HEAD "Content-type: text/html\r\n\r\n\
3051 <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\"><HTML><HEAD>\
3052 <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/>"
3053 
3054 #define BODY "<BODY BGCOLOR=\"#c0c0c0\">"
3055 
3056 #define EMPTY_LIST "<OPTION>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</OPTION></SELECT><BR>"
3057 
3058 
httpQueryError(WWWconnection & con,char const * msg,char const * table)3059 void httpQueryError(WWWconnection& con, char const* msg, char const* table)
3060 {
3061     con << TAG <<
3062         HTML_HEAD "<TITLE>BUGDB error</TITLE></HEAD>"
3063         BODY
3064         "<CENTER><FONT SIZE=+2 COLOR=\"#FF0000\">"
3065         << msg << "</FONT></CENTER><P><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3066         "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3067         "<INPUT TYPE=hidden NAME=table VALUE=\"" << table << "\">"
3068         "<INPUT TYPE=hidden NAME=page VALUE=queryPage>"
3069         "<CENTER><INPUT TYPE=submit VALUE=\"Ok\"></CENTER></FORM></BODY></HTML>";
3070 }
3071 
3072 
httpError(WWWconnection & con,char const * msg)3073 void httpError(WWWconnection& con, char const* msg)
3074 {
3075     con << TAG <<
3076         HTML_HEAD "<TITLE>BUGDB error</TITLE></HEAD>"
3077         BODY
3078         "<CENTER><FONT SIZE=+2 COLOR=\"#FF0000\">"
3079         << msg << "</FONT></CENTER><P><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3080         "<INPUT TYPE=HIDDEN NAME=\"socket\" VALUE=\"" << con.getAddress() << "\">"
3081         "<INPUT TYPE=hidden NAME=\"page\" VALUE=defaultPage>"
3082         "<CENTER><INPUT TYPE=submit VALUE=\"Ok\"></CENTER></FORM></BODY></HTML>";
3083 }
3084 
3085 
defaultPage(WWWconnection & con)3086 bool defaultPage(WWWconnection& con)
3087 {
3088     ((dbSubSql*)con.userData)->defaultPage(con);
3089     return true;
3090 }
3091 
3092 
defaultPage(WWWconnection & con)3093 void dbSubSql::defaultPage(WWWconnection& con)
3094 {
3095     con << TAG <<
3096         HTML_HEAD "<TITLE>Database browser</TITLE></HEAD>"
3097         BODY
3098         "<TABLE><TR><TH align=left>Database path</TH><TD>" << databasePath << "</TD></TR>"
3099         "<TR><TH align=left>GigaBASE version</TH><TD>" << GIGABASE_MAJOR_VERSION << "." << GIGABASE_MINOR_VERSION << "</TD></TR>"
3100         "<TR><TH align=left>Database version</TH><TD>" << header->versionMajor << "." << header->versionMinor << "</TD></TR>"
3101         "<TR><TH align=left>Database size</TH><TD>" << (db_int8)(header->root[1-curr].size / 1024) << "Kb</TD></TR>"
3102         "</TABLE><P>"
3103         "<H2>Tables:</H2><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3104         "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3105         "<INPUT TYPE=hidden NAME=page VALUE=queryPage>"
3106         "<SELECT SIZE=10 NAME=\"table\">";
3107     if (tables != NULL && tables->nextDbTable != NULL) {
3108         for (dbTableDescriptor* desc=tables; desc != NULL; desc=desc->nextDbTable)
3109         {
3110             if (STRCMP(desc->name, STRLITERAL("Metatable")) != 0) {
3111                 con << TAG << "<OPTION VALUE=\"" << desc->name << "\">" << desc->name << "</OPTION>";
3112             }
3113         }
3114     } else {
3115         con << TAG << EMPTY_LIST;
3116     }
3117     con << TAG << "</SELECT><P><INPUT TYPE=submit VALUE=Query></FORM></BODY></HTML>";
3118 }
3119 
queryPage(WWWconnection & con)3120 bool queryPage(WWWconnection& con)
3121 {
3122     ((dbSubSql*)con.userData)->queryPage(con);
3123     return true;
3124 }
3125 
queryPage(WWWconnection & con)3126 void dbSubSql::queryPage(WWWconnection& con)
3127 {
3128     char* tableName = con.get("table");
3129     if (tableName == NULL) {
3130         httpError(con, "Table not selected");
3131         return;
3132     }
3133     dbTableDescriptor* desc;
3134 #ifdef UNICODE
3135     char_t buf[1024];
3136     mbstowcs(buf, tableName, itemsof(buf));
3137     desc = findTableByName(buf);
3138 #else
3139     desc = findTableByName(tableName);
3140 #endif
3141     if (desc == NULL) {
3142         httpError(con, "No such table");
3143         return;
3144     }
3145     char* history = con.get("history");
3146     if (history == NULL) {
3147         history = (char*)"";
3148     }
3149     con << TAG <<
3150         HTML_HEAD "<TITLE>Table query</TITLE></HEAD>"
3151         BODY
3152         "<TABLE><TR><TH align=left>Table name</TH><TD>" << tableName << "</TD></TR>"
3153         "<TR><TH align=left>Number of rows</TH><TD>" << (int)desc->nRows << "</TD></TR>"
3154         "</TABLE><P>"
3155         "<TABLE BORDER><TR><TH>Field name</TH><TH>Field type</TH><TH>Referenced table</TH><TH>Inverse reference</TH><TH>Indexed</TH></TR>";
3156     dbFieldDescriptor* fd = desc->columns;
3157     for (int i = (int)desc->nColumns; --i >= 0;) {
3158         con << TAG << "<TR><TD>" << fd->name << "</TD><TD>" << typeMnem[fd->type] << "</TD><TD>"
3159             << (fd->refTableName ? fd->refTableName : _T(" "))  << "</TD><TD>"
3160             << (fd->refTableName != NULL
3161                 ? fd->refTableName
3162                 : (fd->type == dbField::tpArray && fd->components->refTableName != NULL)
3163                   ? fd->components->refTableName
3164                   : _T(" "))
3165             << "</TD><TD align=center>"
3166             << ((fd->bTree != 0) ? _T("+") : _T(" ")) << "</TD></TR>";
3167         fd = fd->next;
3168     }
3169     con << TAG << "</TABLE><P><TABLE>"
3170         "<FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3171         "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3172         "<INPUT TYPE=hidden NAME=page VALUE=selectionPage>"
3173         "<INPUT TYPE=hidden NAME=table VALUE=\"" << tableName << "\">"
3174         "<TR><TD>SELECT FROM <B>" << tableName << "</B> WHERE</TD>"
3175         "<TD><INPUT TYPE=text NAME=query VALUE=\""
3176         << history << "\" SIZE=40></TD>"
3177         "<TD><INPUT type=submit value=Select></TD></TR></FORM>";
3178     if (historyUsed != 0) {
3179         con << TAG << "<FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3180             "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3181             "<INPUT TYPE=hidden NAME=page VALUE=queryPage>"
3182             "<INPUT TYPE=hidden NAME=table VALUE=\"" << tableName << "\">"
3183             "<TR><TD align=right>Query history</TD>"
3184             "<TD><SELECT SIZE=1 NAME=history>";
3185         for (unsigned i = historyCurr, j = historyUsed; j != 0; j -= 1) {
3186             char* h = queryHistory[--i % MAX_HISTORY_SIZE];
3187             con << TAG << "<OPTION VALUE=\"" << h << "\">" << h << "</OPTION>";
3188         }
3189         con << TAG << "</TD><TD><INPUT type=submit value=Edit></TD></TR></FORM>";
3190     }
3191     con << TAG << "</TABLE></FORM>"
3192         "<P><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3193         "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3194         "<INPUT TYPE=hidden NAME=page VALUE=defaultPage>"
3195         "<INPUT TYPE=submit VALUE=\"Another table\"></FORM></BODY></HTML>";
3196 }
3197 
3198 
3199 enum ComponentType {
3200     RecordComponent,
3201     ArrayComponent,
3202     StructureComponent
3203 };
3204 
httpDumpRecord(WWWconnection & con,byte * base,dbFieldDescriptor * first,ComponentType componentType,int & buttonCounter,const char * condition,int buttonIndex)3205 void httpDumpRecord(WWWconnection& con, byte* base, dbFieldDescriptor* first, ComponentType componentType,
3206                     int &buttonCounter, const char *condition, int buttonIndex)
3207 {
3208     int i, n;
3209     byte* elem;
3210     // only maxDumpArrayLength entries will be shown for an array.
3211     // To turn off this feature, set maxDumpArrayLength to zero.
3212     const int maxDumpArrayLength = 20;
3213     dbFieldDescriptor* fd = first;
3214     do {
3215         if (componentType == StructureComponent) {
3216             con << TAG << "<TR><TH>" << fd->name << "</TH><TD>";
3217         } else if (componentType == RecordComponent) {
3218             con << TAG << "<TD>";
3219         }
3220         switch (fd->type) {
3221           case dbField::tpBool:
3222             con << TAG << (*(bool*)(base + fd->dbsOffs) ? "true" : "false");
3223             break;
3224           case dbField::tpInt1:
3225             con << TAG << *(int1*)(base + fd->dbsOffs);
3226             break;
3227           case dbField::tpInt2:
3228             con << TAG << *(int2*)(base + fd->dbsOffs);
3229             break;
3230           case dbField::tpInt4:
3231             con << TAG << *(int4*)(base + fd->dbsOffs);
3232             break;
3233           case dbField::tpInt8:
3234             con << TAG << *(db_int8*)(base + fd->dbsOffs);
3235             break;
3236           case dbField::tpReal4:
3237             con << TAG << *(real4*)(base + fd->dbsOffs);
3238             break;
3239           case dbField::tpReal8:
3240             con << TAG << *(real8*)(base + fd->dbsOffs);
3241             break;
3242           case dbField::tpRectangle:
3243             {
3244                 rectangle& r = *(rectangle*)(base + fd->dbsOffs);
3245                 con << TAG << "<TABLE BORDER><TR>";
3246                 for (i = 0; i < rectangle::dim; i++) {
3247                     con << TAG << "<TD>" << r.boundary[i] << "</TD>";
3248                 }
3249                 con << TAG << "</TR><TR>";
3250                 for (i = 0; i < rectangle::dim; i++) {
3251                     con << TAG << "<TD>" << r.boundary[rectangle::dim+i] << "</TD>";
3252                 }
3253                 con << TAG << "</TR></TABLE>";
3254             }
3255             break;
3256           case dbField::tpString:
3257             con << TAG << "\"" << (char_t*)((char*)base+((dbVarying*)(base+fd->dbsOffs))->offs) << "\"";
3258             break;
3259           case dbField::tpReference:
3260             {
3261                 oid_t oid = *(oid_t*)(base + fd->dbsOffs);
3262                 if (oid == 0) {
3263                     con << TAG << "null";
3264                 } else {
3265                     con << TAG << "<A HREF=\"" <<  con.getStub() << "?socket="
3266                         << con.getAddress() << "&page=selectionPage&table=" << URL << fd->refTableName <<  "&query="
3267                         << URL << "current=" << oid << TAG << "\">@" << oid << "</A>";
3268                 }
3269             }
3270             break;
3271           case dbField::tpRawBinary:
3272             n = (int)fd->dbsSize;
3273             elem = base + fd->dbsOffs;
3274             con << TAG << "\"";
3275             for (i = 0; i < n; i++) {
3276                 char buf[8];
3277                 sprintf(buf, "\\x%02x", *elem++);
3278                 con << TAG << buf;
3279             }
3280             con << TAG << "\"";
3281             break;
3282           case dbField::tpArray:
3283             {
3284                 n = ((dbVarying*)(base + fd->dbsOffs))->size;
3285                 elem = base + ((dbVarying*)(base + fd->dbsOffs))->offs;
3286                 con << TAG << "<OL>";
3287 
3288                 // limit array size to maxDumpArrayLength
3289                 bool bigList = false;
3290                 bool showButton = false;
3291                 int oldn = n;
3292                 if (maxDumpArrayLength > 0 && n > maxDumpArrayLength) {
3293                     bigList = true; }
3294 
3295                 if (bigList && buttonCounter != buttonIndex) {
3296                     n = maxDumpArrayLength;
3297                     showButton = true;
3298                 }
3299 
3300                 for (i = 0; i < n; i++) {
3301                     con << TAG << "<LI>";
3302                     httpDumpRecord(con, elem, fd->components, ArrayComponent, buttonCounter, condition, buttonIndex);
3303                     elem += fd->components->dbsSize;
3304                 }
3305                 con << TAG << "</OL>";
3306 
3307                 if (showButton)
3308                 {
3309                     con << TAG << "<P align=\"center\"><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3310                                   "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3311                                   "<INPUT TYPE=hidden NAME=page VALUE=selectionPage>"
3312                                   "<INPUT TYPE=hidden NAME=table VALUE=\"" << first->defTable->getName() << "\">"
3313                                   "<INPUT TYPE=hidden NAME=query VALUE=\"" << condition << "\">"
3314                                   "<INPUT TYPE=hidden NAME=button VALUE=\"" << buttonCounter << "\">"
3315                                   "<INPUT TYPE=submit VALUE=\"Full list (" << oldn << ")\"></FORM></P>";
3316                 }
3317 
3318                 if (bigList)
3319                    buttonCounter++;
3320 
3321             } // case
3322             break;
3323           case dbField::tpStructure:
3324             con << TAG << "<TABLE BORDER>";
3325             httpDumpRecord(con, base, fd->components,RecordComponent, buttonCounter, condition, buttonIndex);
3326             con << TAG << "</TABLE>";
3327         }
3328         if (componentType == StructureComponent) {
3329             con << TAG << "</TD></TR>";
3330         } else if (componentType == RecordComponent) {
3331             con << TAG << "</TD>";
3332         }
3333     } while ((fd = fd->next) != first);
3334 }
3335 
selectionPage(WWWconnection & con)3336 bool selectionPage(WWWconnection& con)
3337 {
3338     ((dbSubSql*)con.userData)->selectionPage(con);
3339     return true;
3340 }
3341 
selectionPage(WWWconnection & con)3342 void dbSubSql::selectionPage(WWWconnection& con)
3343 {
3344     char const* tableName = con.get("table");
3345     char const* condition = con.get("query");
3346     char const* buttonNr  = con.get("button");
3347 
3348     int buttonIndex = -1;
3349     if (buttonNr) {
3350         buttonIndex = atoi(buttonNr); }
3351 
3352 
3353     dbTableDescriptor* desc;
3354 #ifdef UNICODE
3355     char_t buf[1024];
3356     mbstowcs(buf, tableName, itemsof(buf));
3357     desc = findTableByName(buf);
3358 #else
3359     desc = findTableByName(tableName);
3360 #endif
3361     if (desc == NULL) {
3362         httpError(con, "No such table");
3363         return;
3364     }
3365     if (condition == NULL) {
3366         httpError(con, "Condition was not specified");
3367         return;
3368     }
3369     if (strlen(condition) > 0
3370         && (historyUsed == 0
3371             || strcmp(condition, queryHistory[unsigned(historyCurr-1)%MAX_HISTORY_SIZE]) != 0))
3372     {
3373         char* h = new char[strlen(condition)+1];
3374         strcpy(h, condition);
3375         if (historyCurr == historyUsed) {
3376             historyUsed += 1;
3377         } else {
3378             delete[] queryHistory[historyCurr];
3379         }
3380         queryHistory[historyCurr] = h;
3381         if (++historyCurr == MAX_HISTORY_SIZE) {
3382             historyCurr = 0;
3383         }
3384     }
3385     dbAnyCursor cursor(*desc, dbCursorViewOnly, NULL);
3386     query.pos = pos;
3387     dbDatabaseThreadContext* ctx = threadContext.get();
3388     ctx->interactive = false;
3389     ctx->catched = true;
3390 #ifdef THROW_EXCEPTION_ON_ERROR
3391     try {
3392 #else
3393     if (setjmp(ctx->unwind) == 0) {
3394 #endif
3395 #ifdef UNICODE
3396         mbstowcs(buf, condition, sizeof(buf)/sizeof(wchar_t));
3397         query = buf;
3398 #else
3399         query = condition;
3400 #endif
3401         cursor.reset();
3402         select(&cursor, query);
3403         if (query.compileError()) {
3404             dbExprNodeAllocator::instance.reset();
3405             ctx->catched = false;
3406             httpQueryError(con, "query syntax error", tableName);
3407             return;
3408         }
3409         con << TAG <<
3410             HTML_HEAD "<TITLE>Selection</TITLE></HEAD>"
3411             BODY
3412             "<H2>Selection from table " << tableName << "</H2>"
3413             "<TABLE BORDER><TR><TH>OID</TH>";
3414         dbFieldDescriptor* fd = desc->columns;
3415         do {
3416             con << TAG << "<TH>" << fd->name << "</TH>";
3417         } while ((fd = fd->next) != desc->columns);
3418         con << TAG << "</TR>";
3419 
3420         int nSelected = 0;
3421         dbGetTie tie;
3422         int buttonCounter = 0;
3423         if (cursor.gotoFirst()) {
3424             do {
3425                 nSelected += 1;
3426                 con << TAG << "<TR><TD>@" << cursor.currId << "</TD>";
3427                 httpDumpRecord(con, (byte*)getRow(tie, cursor.currId),
3428                                cursor.table->columns, RecordComponent,
3429                                buttonCounter, condition, buttonIndex);
3430                 con << TAG << "</TR>";
3431             } while (cursor.gotoNext());
3432             con << TAG << "</TABLE>";
3433             if (nSelected > 1) {
3434                 con << TAG << "<P>" << nSelected << " records selected";
3435             }
3436         } else {
3437             con << TAG << "</TABLE><P>No records selected";
3438         }
3439 #ifdef THROW_EXCEPTION_ON_ERROR
3440     } catch(dbException const& x) {
3441         httpQueryError(con, x.getMsg(), tableName);
3442         ctx->catched = false;
3443         commit(); // release locks
3444         return;
3445     }
3446 #else
3447     } else {
3448         httpQueryError(con, "Query error", tableName);
3449         if (query.mutexLocked) {
3450             query.mutexLocked = false;
3451             query.mutex.unlock();
3452         }
3453         ctx->catched = false;
3454         commit(); // release locks
3455         return;
3456     }
3457 #endif
3458     ctx->catched = false;
3459     commit(); // release locks
3460 
3461     con << TAG <<
3462         "<P><FORM METHOD=POST ACTION=\"" << con.getStub() << "\">"
3463         "<INPUT TYPE=HIDDEN NAME=socket VALUE=\"" << con.getAddress() << "\">"
3464         "<INPUT TYPE=hidden NAME=table VALUE=\"" << tableName << "\">"
3465         "<INPUT TYPE=hidden NAME=page VALUE=queryPage>"
3466         "<INPUT TYPE=submit VALUE=\"New query\"></FORM></BODY></HTML>";
3467 }
3468 
3469 
3470 WWWapi::dispatcher dispatchTable[] = {
3471     {"defaultPage", defaultPage},
3472     {"queryPage", queryPage},
3473     {"selectionPage", selectionPage}
3474 };
3475 
3476 
3477 
3478 
startHttpServer(char_t const * address)3479 void dbSubSql::startHttpServer(char_t const* address)
3480 {
3481     if (httpServerRunning) {
3482         error("HTTP server already started");
3483     } else {
3484         httpServer = new HTTPapi(*this, itemsof(dispatchTable), dispatchTable);
3485         char const* socketAddress;
3486 #ifdef UNICODE
3487         char buf[1024];
3488         wcstombs(buf, address, sizeof buf);
3489         socketAddress = buf;
3490 #else
3491         socketAddress = address;
3492 #endif
3493         if (!httpServer->open(socketAddress, socket_t::sock_global_domain)) {
3494             delete httpServer;
3495             error("Failed to open HTTP session");
3496         } else {
3497             httpServerRunning = true;
3498             httpServerThread.create(httpServerThreadProc, this);
3499         }
3500     }
3501 }
3502 
stopHttpServer(char_t const *)3503 void dbSubSql::stopHttpServer(char_t const*)
3504 {
3505     if (!httpServerRunning) {
3506         error("HTTP server was not started");
3507     } else {
3508         httpServerRunning = false;
3509         httpServer->cancel();
3510     }
3511 }
3512 
httpServerThreadProc(void * arg)3513 void thread_proc dbSubSql::httpServerThreadProc(void* arg)
3514 {
3515     ((dbSubSql*)arg)->httpServerLoop();
3516 }
3517 
httpServerLoop()3518 void  dbSubSql::httpServerLoop()
3519 {
3520     WWWconnection con;
3521     con.userData = this;
3522     attach();
3523     while (httpServer->connect(con) && httpServerRunning && httpServer->serve(con));
3524     delete httpServer;
3525     detach();
3526     httpServerRunning = false;
3527 }
3528 
3529 END_GIGABASE_NAMESPACE
3530 
3531 #ifdef _WINCE
3532 
3533 #define MAX_ARGS 32
3534 
3535 #ifdef WINONLY
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)3536 int WINAPI WinMain(
3537                    HINSTANCE hInstance,
3538                    HINSTANCE hPrevInstance,
3539                    LPSTR lpCmdLine,
3540                    int nCmdShow)
3541 {
3542     char* line = m_cmdline;
3543 #else
3544 int WINAPI WinMain(
3545                    HINSTANCE hInstance,
3546                    HINSTANCE hPrevInstance,
3547                    LPTSTR lpCmdLine,
3548                    int nCmdShow)
3549 {
3550     int len = wcslen(lpCmdLine);
3551     char* line   = new char[len+1];
3552     wcstombs(line, lpCmdLine, len);
3553 #endif
3554     int argc = 1;
3555     char* argv[MAX_ARGS];
3556     argv[0] = NULL;
3557     if (*line != '\0') {
3558         while (true) {
3559             if (*line == '\"') {
3560                 argv[argc++] = ++line;
3561                 while (*++line != '\"' && *line != '\0');
3562             } else {
3563                 argv[argc++] = line;
3564                 while (*++line != ' ' && *line != '\0');
3565             }
3566             if (*line == '\0') {
3567                 break;
3568             }
3569             *line++ = '\0';
3570         }
3571     }
3572     argv[argc] = NULL;
3573 #else
3574 int __cdecl main(int argc, char* argv[])
3575 {
3576 #endif
3577     GB_NS::dbDatabase::dbAccessType accessType;
3578     if (getenv("SUBSQL_MULTICLIENT") != NULL) {
3579         accessType = (getenv("SUBSQL_READONLY") != NULL)
3580             ?  GB_NS::dbDatabase::dbMulticlientReadOnly :GB_NS:: dbDatabase::dbMulticlientReadWrite;
3581     } else {
3582         accessType = (getenv("SUBSQL_READONLY") != NULL)
3583             ?  GB_NS::dbDatabase::dbReadOnly : GB_NS::dbDatabase::dbAllAccess;
3584     }
3585     char* locale = getenv("SUBSQL_LOCALE");
3586     if (locale != NULL) {
3587         setlocale(LC_COLLATE, locale);
3588         setlocale(LC_CTYPE, locale);
3589     }
3590     size_t pagePoolSize = 0;
3591     char* pagePoolSizeStr = getenv("SUBSQL_POOL_SIZE");
3592     if (pagePoolSizeStr != NULL) {
3593         pagePoolSize = atoi(pagePoolSizeStr);
3594     }
3595     GB_NS::dbSubSql db(accessType, pagePoolSize);
3596     db.run(argc, argv);
3597     return 0;
3598 }
3599 
3600