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> </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