1 //-< DATABASE.CPP >--------------------------------------------------*--------*
2 // GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
3 // (Post Relational Database Management System)                      *   /\|  *
4 //                                                                   *  /  \  *
5 //                          Created:     20-Nov-1998  K.A. Knizhnik  * / [] \ *
6 //                          Last update: 23-Nov-2001  K.A. Knizhnik  * GARRET *
7 //-------------------------------------------------------------------*--------*
8 // Database memory management, query execution, scheme evaluation
9 //-------------------------------------------------------------------*--------*
10 
11 #define INSIDE_GIGABASE
12 
13 #include "gigabase.h"
14 #include "compiler.h"
15 #include "hashtab.h"
16 #include "btree.h"
17 #include "rtree.h"
18 #include "symtab.h"
19 #include <math.h>
20 #ifndef _WINCE
21 #include <sys/stat.h>
22 #endif
23 
24 BEGIN_GIGABASE_NAMESPACE
25 
26 dbNullReference null;
27 
28 char_t const* const dbMetaTableName = STRLITERAL("Metatable");
29 
30 unsigned dbDatabase::dbParallelScanThreshold = 1000;
31 
distance(rectangle const & r,rectangle const & q)32 coord_t GIGABASE_DLL_ENTRY distance(rectangle const& r, rectangle const& q)
33 {
34     if (r & q) {
35         return 0;
36     }
37     coord_t d = 0;;
38     for (int i = 0; i < rectangle::dim; i++) {
39         if (r.boundary[i] > q.boundary[rectangle::dim+i]) {
40             coord_t di = r.boundary[i] - q.boundary[rectangle::dim+i];
41             d += di*di;
42         } else if (q.boundary[i] > r.boundary[rectangle::dim+i]) {
43             coord_t di = q.boundary[i] - r.boundary[rectangle::dim+i];
44             d += di*di;
45         }
46     }
47     return (coord_t)sqrt((double)d);
48 }
49 
convertIntToString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr)50 inline void convertIntToString(dbInheritedAttribute&   iattr,
51                                dbSynthesizedAttribute& sattr)
52 {
53     char_t buf[32];
54     iattr.allocateString(sattr, buf,
55                          SPRINTF(SPRINTF_BUFFER(buf), T_INT8_FORMAT, sattr.ivalue) + 1);
56 }
57 
convertRealToString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr)58 inline void convertRealToString(dbInheritedAttribute&   iattr,
59                                 dbSynthesizedAttribute& sattr)
60 {
61     char_t buf[32];
62     iattr.allocateString(sattr, buf,
63                          SPRINTF(SPRINTF_BUFFER(buf), STRLITERAL("%f"), sattr.fvalue) + 1);
64 }
65 
concatenateStrings(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)66 static void concatenateStrings(dbInheritedAttribute&   iattr,
67                                dbSynthesizedAttribute& sattr,
68                                dbSynthesizedAttribute& sattr2)
69 {
70     if (sattr2.array.size == 1) { // first string is empty
71         iattr.free(sattr2);
72         return;
73     }
74     int len = sattr.array.size + sattr2.array.size - 1;
75     if (iattr.sp + len*sizeof(char_t) > sizeof(iattr.stack)) {
76         char_t* s = new char_t[len];
77         memcpy(s, sattr.array.base, (sattr.array.size - 1)*sizeof(char_t));
78         memcpy(s + sattr.array.size - 1, sattr2.array.base,
79                sattr2.array.size*sizeof(char_t));
80         iattr.free(sattr2);
81         iattr.free(sattr);
82         iattr.makeDynamic(sattr, s);
83         sattr.array.base = (char*)s;
84         sattr.array.size = len;
85     } else {
86         if (sattr2.osClass == dbSynthesizedAttribute::osStack) {
87             iattr.sp = sattr2.os.sp;
88         }
89         if (sattr.osClass == dbSynthesizedAttribute::osStack) {
90             memcpy(iattr.stack + iattr.sp - 1, sattr2.array.base,
91                    sattr2.array.size*sizeof(char_t));
92             iattr.sp += (sattr2.array.size-1)*sizeof(char_t);
93             if (sattr2.osClass != dbSynthesizedAttribute::osStack) {
94                 iattr.free(sattr2);
95             }
96         } else {
97             if (sattr2.osClass == dbSynthesizedAttribute::osStack) {
98                 assert(sattr2.array.base == (char*)iattr.stack + iattr.sp);
99                 memmove(sattr2.array.base + (sattr.array.size - 1)*sizeof(char_t),
100                         sattr2.array.base, sattr2.array.size*sizeof(char_t));
101                 memcpy(sattr2.array.base, sattr.array.base,
102                        (sattr.array.size-1)*sizeof(char_t));
103             } else {
104                 memcpy(iattr.stack + iattr.sp, sattr.array.base,
105                        (sattr.array.size - 1)*sizeof(char_t));
106                 memcpy(iattr.stack + iattr.sp + (sattr.array.size - 1)*sizeof(char_t),
107                        sattr2.array.base, sattr2.array.size*sizeof(char_t));
108                 iattr.free(sattr2);
109             }
110             iattr.free(sattr);
111             sattr.osClass = dbSynthesizedAttribute::osStack;
112             sattr.os.sp = iattr.sp;
113             iattr.sp += len*sizeof(char_t);
114         }
115         sattr.array.base = (char*)iattr.stack + sattr.os.sp;
116         sattr.array.size = len;
117     }
118 }
119 
compareStringsForEquality(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr1,dbSynthesizedAttribute & sattr2)120 inline int compareStringsForEquality(dbInheritedAttribute&   iattr,
121                                      dbSynthesizedAttribute& sattr1,
122                                      dbSynthesizedAttribute& sattr2)
123 {
124     int result = STRCMP((char_t*)sattr1.array.base, (char_t*)sattr2.array.base);
125     iattr.free(sattr2);
126     iattr.free(sattr1);
127     return result;
128 }
129 
compareRawBinary(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr1,dbSynthesizedAttribute & sattr2,int size,void * func)130 inline int compareRawBinary(dbInheritedAttribute&   iattr,
131                             dbSynthesizedAttribute& sattr1,
132                             dbSynthesizedAttribute& sattr2,
133                             int size, void* func)
134 {
135     dbUDTComparator comparator = (dbUDTComparator)func;
136     int result = comparator(sattr1.raw, sattr2.raw, size);
137     iattr.free(sattr2);
138     iattr.free(sattr1);
139     return result;
140 }
141 
compareStrings(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr1,dbSynthesizedAttribute & sattr2)142 inline int compareStrings(dbInheritedAttribute&   iattr,
143                           dbSynthesizedAttribute& sattr1,
144                           dbSynthesizedAttribute& sattr2)
145 {
146 #ifdef USE_LOCALE_SETTINGS
147     int result = STRCOLL((char_t*)sattr1.array.base, (char_t*)sattr2.array.base);
148 #else
149     int result = STRCMP((char_t*)sattr1.array.base, (char_t*)sattr2.array.base);
150 #endif
151     iattr.free(sattr2);
152     iattr.free(sattr1);
153     return result;
154 }
155 
156 
matchStrings(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr1,dbSynthesizedAttribute & sattr2,char_t escapeChar)157 inline bool matchStrings(dbInheritedAttribute&   iattr,
158                          dbSynthesizedAttribute& sattr1,
159                          dbSynthesizedAttribute& sattr2,
160                          char_t escapeChar)
161 {
162     char_t* str = (char_t*)sattr1.array.base;
163     char_t* pattern = (char_t*)sattr2.array.base;
164     char_t* wildcard = NULL;
165     char_t* strpos = NULL;
166     bool value;
167     while (true) {
168         if (*pattern == dbMatchAnySubstring) {
169             wildcard = ++pattern;
170             strpos = str;
171         } else if (*str == '\0') {
172             value = (*pattern == '\0');
173             break;
174         } else if (*pattern == escapeChar && pattern[1] == *str) {
175             str += 1;
176             pattern += 2;
177         } else if (*pattern != escapeChar
178                    && (*str == *pattern || *pattern == dbMatchAnyOneChar))
179         {
180             str += 1;
181             pattern += 1;
182         } else if (wildcard) {
183             str = ++strpos;
184             pattern = wildcard;
185         } else {
186             value = false;
187             break;
188         }
189     }
190     iattr.free(sattr2);
191     iattr.free(sattr1);
192     return value;
193 }
194 
matchStrings(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr1,dbSynthesizedAttribute & sattr2)195 inline bool matchStrings(dbInheritedAttribute&   iattr,
196                          dbSynthesizedAttribute& sattr1,
197                          dbSynthesizedAttribute& sattr2)
198 {
199     char_t* str = (char_t*)sattr1.array.base;
200     char_t* pattern = (char_t*)sattr2.array.base;
201     char_t* wildcard = NULL;
202     char_t* strpos = NULL;
203     bool    value;
204     while (true) {
205         if (*pattern == dbMatchAnySubstring) {
206             wildcard = ++pattern;
207             strpos = str;
208         } else if (*str == '\0') {
209             value = (*pattern == '\0');
210             break;
211         } else if (*str == *pattern || *pattern == dbMatchAnyOneChar) {
212             str += 1;
213             pattern += 1;
214         } else if (wildcard) {
215             str = ++strpos;
216             pattern = wildcard;
217         } else {
218             value = false;
219             break;
220         }
221     }
222     iattr.free(sattr2);
223     iattr.free(sattr1);
224     return value;
225 }
226 
227 
lowercaseString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr)228 inline void lowercaseString(dbInheritedAttribute&   iattr,
229                             dbSynthesizedAttribute& sattr)
230 {
231     char_t* src = (char_t*)sattr.array.base;
232     if (sattr.osClass == dbSynthesizedAttribute::osStack ||
233         sattr.osClass == dbSynthesizedAttribute::osDynamic)
234     {
235         char_t* dst = src;
236         while ((*dst++ = TOLOWER(*src++)) != '\0');
237     } else {
238         char_t* dst;
239         if (iattr.sp + sattr.array.size*sizeof(char_t) > sizeof(iattr.stack)) {
240             dst = new char_t[sattr.array.size];
241             sattr.array.base = (char*)dst;
242             while ((*dst++ = TOLOWER(*src++)) != '\0');
243             iattr.free(sattr);
244             iattr.makeDynamic(sattr, (char_t*)sattr.array.base);
245         } else {
246             dst = (char_t*)(iattr.stack + iattr.sp);
247             sattr.array.base = (char*)dst;
248             while ((*dst++ = TOLOWER(*src++)) != '\0');
249             iattr.free(sattr);
250             sattr.osClass = dbSynthesizedAttribute::osStack;
251             sattr.os.sp = iattr.sp;
252             iattr.sp += sattr.array.size*sizeof(char_t);
253         }
254     }
255 }
256 
uppercaseString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr)257 inline void uppercaseString(dbInheritedAttribute&   iattr,
258                             dbSynthesizedAttribute& sattr)
259 {
260     char_t* src = (char_t*)sattr.array.base;
261     if (sattr.osClass == dbSynthesizedAttribute::osStack ||
262         sattr.osClass == dbSynthesizedAttribute::osDynamic)
263     {
264         char_t* dst = src;
265         while ((*dst++ = TOUPPER(*src++)) != '\0');
266     } else {
267         char_t* dst;
268         if (iattr.sp + sattr.array.size*sizeof(char_t) > sizeof(iattr.stack)) {
269             dst = new char_t[sattr.array.size];
270             sattr.array.base = (char*)dst;
271             while ((*dst++ = TOUPPER(*src++)) != '\0');
272             iattr.free(sattr);
273             iattr.makeDynamic(sattr, (char_t*)sattr.array.base);
274         } else {
275             dst = (char_t*)(iattr.stack + iattr.sp);
276             sattr.array.base = (char*)dst;
277             while ((*dst++ = TOUPPER(*src++)) != '\0');
278             iattr.free(sattr);
279             sattr.osClass = dbSynthesizedAttribute::osStack;
280             sattr.os.sp = iattr.sp;
281             iattr.sp += sattr.array.size*sizeof(char_t);
282         }
283     }
284 }
285 
copyString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,char_t * str)286 inline void copyString(dbInheritedAttribute&   iattr,
287                        dbSynthesizedAttribute& sattr, char_t* str)
288 {
289     iattr.allocateString(sattr, str);
290 }
291 
searchArrayOfBool(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)292 inline void searchArrayOfBool(dbInheritedAttribute&   iattr,
293                               dbSynthesizedAttribute& sattr,
294                               dbSynthesizedAttribute& sattr2)
295 {
296     bool *p = (bool*)sattr2.array.base;
297     int   n = sattr2.array.size;
298     bool  v = (bool)sattr.bvalue;
299     sattr.bvalue = false;
300     while (--n >= 0) {
301         if (v == *p++) {
302             sattr.bvalue = true;
303             break;
304         }
305     }
306     iattr.free(sattr2);
307 }
308 
searchArrayOfInt1(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)309 inline void searchArrayOfInt1(dbInheritedAttribute&   iattr,
310                               dbSynthesizedAttribute& sattr,
311                               dbSynthesizedAttribute& sattr2)
312 {
313     int1 *p = (int1*)sattr2.array.base;
314     int   n = sattr2.array.size;
315     int1  v = (int1)sattr.ivalue;
316     sattr.bvalue = false;
317     while (--n >= 0) {
318         if (v == *p++) {
319             sattr.bvalue = true;
320             break;
321         }
322     }
323     iattr.free(sattr2);
324 }
325 
searchArrayOfInt2(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)326 inline void searchArrayOfInt2(dbInheritedAttribute&   iattr,
327                               dbSynthesizedAttribute& sattr,
328                               dbSynthesizedAttribute& sattr2)
329 {
330     int2 *p = (int2*)sattr2.array.base;
331     int   n = sattr2.array.size;
332     int2  v = (int2)sattr.ivalue;
333     sattr.bvalue = false;
334     while (--n >= 0) {
335         if (v == *p++) {
336             sattr.bvalue = true;
337             break;
338         }
339     }
340     iattr.free(sattr2);
341 }
342 
searchArrayOfInt4(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)343 inline void searchArrayOfInt4(dbInheritedAttribute&   iattr,
344                               dbSynthesizedAttribute& sattr,
345                               dbSynthesizedAttribute& sattr2)
346 {
347     int4 *p = (int4*)sattr2.array.base;
348     int   n = sattr2.array.size;
349     int4  v = (int4)sattr.ivalue;
350     sattr.bvalue = false;
351     while (--n >= 0) {
352         if (v == *p++) {
353             sattr.bvalue = true;
354             break;
355         }
356     }
357     iattr.free(sattr2);
358 }
359 
searchArrayOfInt8(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)360 inline void searchArrayOfInt8(dbInheritedAttribute&   iattr,
361                               dbSynthesizedAttribute& sattr,
362                               dbSynthesizedAttribute& sattr2)
363 {
364     db_int8 *p = (db_int8*)sattr2.array.base;
365     int   n = sattr2.array.size;
366     db_int8  v = sattr.ivalue;
367     sattr.bvalue = false;
368     while (--n >= 0) {
369         if (v == *p) {
370             sattr.bvalue = true;
371             break;
372         }
373         p += 1;
374     }
375     iattr.free(sattr2);
376 }
377 
searchArrayOfReal4(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)378 inline void searchArrayOfReal4(dbInheritedAttribute&   iattr,
379                                dbSynthesizedAttribute& sattr,
380                                dbSynthesizedAttribute& sattr2)
381 {
382     real4* p = (real4*)sattr2.array.base;
383     int    n = sattr2.array.size;
384     real4  v = (real4)sattr.fvalue;
385     sattr.bvalue = false;
386     while (--n >= 0) {
387         if (v == *p++) {
388             sattr.bvalue = true;
389             break;
390         }
391     }
392     iattr.free(sattr2);
393 }
394 
searchArrayOfReal8(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)395 inline void searchArrayOfReal8(dbInheritedAttribute&   iattr,
396                                dbSynthesizedAttribute& sattr,
397                                dbSynthesizedAttribute& sattr2)
398 {
399     real8 *p = (real8*)sattr2.array.base;
400     int    n = sattr2.array.size;
401     real8  v = sattr.fvalue;
402     sattr.bvalue = false;
403     while (--n >= 0) {
404         if (v == *p) {
405             sattr.bvalue = true;
406             break;
407         }
408         p += 1;
409     }
410     iattr.free(sattr2);
411 }
412 
searchArrayOfReference(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)413 inline void searchArrayOfReference(dbInheritedAttribute&   iattr,
414                                    dbSynthesizedAttribute& sattr,
415                                    dbSynthesizedAttribute& sattr2)
416 {
417     oid_t *p = (oid_t*)sattr2.array.base;
418     int    n = sattr2.array.size;
419     oid_t  v = sattr.oid;
420     sattr.bvalue = false;
421     while (--n >= 0) {
422         if (v == *p) {
423             sattr.bvalue = true;
424             break;
425         }
426         p += 1;
427     }
428     iattr.free(sattr2);
429 }
430 
searchArrayOfRectangle(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)431 inline void searchArrayOfRectangle(dbInheritedAttribute&   iattr,
432                                    dbSynthesizedAttribute& sattr,
433                                    dbSynthesizedAttribute& sattr2)
434 {
435     rectangle *p = (rectangle*)sattr2.array.base;
436     int        n = sattr2.array.size;
437     rectangle  v = sattr.rvalue;
438     sattr.bvalue = false;
439     while (--n >= 0) {
440         if (v == *p) {
441             sattr.bvalue = true;
442             break;
443         }
444         p += 1;
445     }
446     iattr.free(sattr2);
447 }
448 
searchArrayOfString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)449 inline void searchArrayOfString(dbInheritedAttribute&   iattr,
450                                 dbSynthesizedAttribute& sattr,
451                                 dbSynthesizedAttribute& sattr2)
452 {
453     dbVarying *p = (dbVarying*)sattr2.array.base;
454     int        n = sattr2.array.size;
455     char_t*    str = (char_t*)sattr.array.base;
456     sattr.bvalue = false;
457     while (--n >= 0) {
458         if (STRCMP((char_t*)((char*)p + p->offs), str) == 0) {
459             sattr.bvalue = true;
460             break;
461         }
462         p += 1;
463     }
464     iattr.free(sattr2);
465     iattr.free(sattr);
466 }
467 
searchInString(dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr,dbSynthesizedAttribute & sattr2)468 inline void searchInString(dbInheritedAttribute&   iattr,
469                            dbSynthesizedAttribute& sattr,
470                            dbSynthesizedAttribute& sattr2)
471 {
472     if (sattr.array.size > sattr2.array.size) {
473         sattr.bvalue = false;
474 #ifndef UNICODE
475     } else if (sattr2.array.size > dbBMsearchThreshold) {
476         int len = sattr.array.size - 2;
477         int n = sattr2.array.size - 1;
478         int i, j, k;
479         int shift[256];
480         byte* pattern = (byte*)sattr.array.base;
481         byte* str = (byte*)sattr2.array.base;
482         for (i = 0; i < (int)itemsof(shift); i++) {
483             shift[i] = len+1;
484         }
485         for (i = 0; i < len; i++) {
486             shift[pattern[i]] = len-i;
487         }
488         for (i = len; i < n; i += shift[str[i]]) {
489             j = len;
490             k = i;
491             while (pattern[j] == str[k]) {
492                 k -= 1;
493                 if (--j < 0) {
494                     sattr.bvalue = true;
495                     iattr.free(sattr2);
496                     iattr.free(sattr);
497                     return;
498                 }
499             }
500         }
501         sattr.bvalue = false;
502 #endif
503     } else {
504         sattr.bvalue = STRSTR((char_t*)sattr2.array.base, (char_t*)sattr.array.base) != NULL;
505     }
506     iattr.free(sattr2);
507     iattr.free(sattr);
508 }
509 
powerIntInt(db_int8 x,db_int8 y)510 inline db_int8 powerIntInt(db_int8 x, db_int8 y)
511 {
512     db_int8 res = 1;
513 
514     if (y < 0) {
515         x = 1/x;
516         y = -y;
517     }
518     while (y != 0) {
519         if (y & 1) {
520             res *= x;
521         }
522         x *= x;
523         y >>= 1;
524     }
525     return res;
526 }
527 
powerRealInt(real8 x,db_int8 y)528 inline real8 powerRealInt(real8 x, db_int8 y)
529 {
530     real8 res = 1.0;
531 
532     if (y < 0) {
533         x = 1/x;
534         y = -y;
535     }
536     while (y != 0) {
537         if (y & 1) {
538             res *= x;
539         }
540         x *= x;
541         y >>= 1;
542     }
543     return res;
544 }
545 
546 
operator =(dbSearchContext const & sc)547 void dbSearchContext::operator =(dbSearchContext const& sc)
548 {
549     memcpy(this, &sc, sizeof(sc));
550     if ((void*)sc.firstKey == (void*)&sc.literal[0]) {
551         firstKey = (char_t*)&literal[0];
552     }
553     if ((void*)sc.lastKey == (void*)&sc.literal[0]) {
554         lastKey = (char_t*)&literal[0];
555     } else if ((void*)sc.lastKey == (void*)&sc.literal[1]) {
556         lastKey = (char_t*)&sc.literal[1];
557     }
558 }
559 
560 
evaluateBoolean(dbExprNode * expr,oid_t oid,dbTableDescriptor * table,dbAnyCursor * cursor)561 bool dbDatabase::evaluateBoolean(dbExprNode*        expr,
562                                  oid_t              oid,
563                                  dbTableDescriptor* table,
564                                  dbAnyCursor*       cursor)
565 {
566     dbInheritedAttribute iattr;
567     dbSynthesizedAttribute sattr1;
568     dbSynthesizedAttribute sattr2;
569     iattr.db = this;
570     iattr.oid = oid;
571     iattr.table = table;
572     sattr1.oid = oid;
573     iattr.load(sattr1);
574     iattr.record = sattr1.base;
575     iattr.paramBase = (size_t)cursor->paramBase;
576     execute(expr, iattr, sattr2);
577     iattr.free(sattr1);
578     return sattr2.bvalue != 0;
579 }
580 
evaluate(dbExprNode * expr,oid_t oid,dbTableDescriptor * table,dbSynthesizedAttribute & result)581 void dbDatabase::evaluate(dbExprNode*             expr,
582                           oid_t                   oid,
583                           dbTableDescriptor*      table,
584                           dbSynthesizedAttribute& result)
585 {
586     dbInheritedAttribute iattr;
587     dbSynthesizedAttribute sattr1;
588     iattr.db = this;
589     iattr.oid = oid;
590     iattr.table = table;
591     sattr1.oid = oid;
592     iattr.load(sattr1);
593     iattr.record = sattr1.base;
594     iattr.paramBase = 0;
595     execute(expr, iattr, result);
596     iattr.free(sattr1);
597 }
598 
599 
evaluateString(dbExprNode * expr,oid_t oid,dbTableDescriptor * table,char_t * buf,size_t bufSize)600 size_t dbDatabase::evaluateString(dbExprNode*             expr,
601                                   oid_t                   oid,
602                                   dbTableDescriptor*      table,
603                                   char_t*                 buf,
604                                   size_t                  bufSize)
605 {
606     dbInheritedAttribute iattr;
607     dbSynthesizedAttribute sattr1;
608     dbSynthesizedAttribute sattr2;
609     iattr.db = this;
610     iattr.oid = oid;
611     iattr.table = table;
612     sattr1.oid = oid;
613     iattr.load(sattr1);
614     iattr.record = sattr1.base;
615     iattr.paramBase = 0;
616     execute(expr, iattr, sattr2);
617     STRNCPY(buf, (char_t*)sattr2.array.base, bufSize);
618     iattr.free(sattr2);
619     iattr.free(sattr1);
620     return sattr2.array.size-1;
621 }
622 
623 
execute(dbExprNode * expr,dbInheritedAttribute & iattr,dbSynthesizedAttribute & sattr)624 void _fastcall dbDatabase::execute(dbExprNode*             expr,
625                                    dbInheritedAttribute&   iattr,
626                                    dbSynthesizedAttribute& sattr)
627 {
628     dbSynthesizedAttribute sattr2, sattr3;
629     char* tmp;
630 
631     switch (expr->cop) {
632       case dbvmVoid:
633         sattr.bvalue = true; // empty condition
634         return;
635       case dbvmCurrent:
636         sattr.oid = iattr.oid;
637         return;
638       case dbvmFirst:
639         sattr.oid = iattr.table->firstRow;
640         return;
641       case dbvmLast:
642         sattr.oid = iattr.table->lastRow;
643         return;
644       case dbvmLoadBool:
645         execute(expr->operand[0], iattr, sattr);
646         sattr.bvalue = *(bool*)(sattr.base+expr->offs);
647         iattr.free(sattr);
648         return;
649       case dbvmLoadInt1:
650         execute(expr->operand[0], iattr, sattr);
651         sattr.ivalue = *(int1*)(sattr.base+expr->offs);
652         iattr.free(sattr);
653         return;
654       case dbvmLoadInt2:
655         execute(expr->operand[0], iattr, sattr);
656         sattr.ivalue = *(int2*)(sattr.base+expr->offs);
657         iattr.free(sattr);
658         return;
659       case dbvmLoadInt4:
660         execute(expr->operand[0], iattr, sattr);
661         sattr.ivalue = *(int4*)(sattr.base+expr->offs);
662         iattr.free(sattr);
663         return;
664       case dbvmLoadInt8:
665         execute(expr->operand[0], iattr, sattr);
666         sattr.ivalue = *(db_int8*)(sattr.base+expr->offs);
667         iattr.free(sattr);
668         return;
669       case dbvmLoadReal4:
670         execute(expr->operand[0], iattr, sattr);
671         sattr.fvalue = *(real4*)(sattr.base+expr->offs);
672         iattr.free(sattr);
673         return;
674       case dbvmLoadReal8:
675         execute(expr->operand[0], iattr, sattr);
676         sattr.fvalue = *(real8*)(sattr.base+expr->offs);
677         iattr.free(sattr);
678         return;
679       case dbvmLoadReference:
680         execute(expr->operand[0], iattr, sattr);
681         sattr.oid = *(oid_t*)(sattr.base+expr->offs);
682         iattr.free(sattr);
683         return;
684       case dbvmLoadRectangle:
685         execute(expr->operand[0], iattr, sattr);
686         sattr.rvalue = *(rectangle*)(sattr.base+expr->offs);
687         iattr.free(sattr);
688         return;
689       case dbvmLoadArray:
690       case dbvmLoadString:
691         execute(expr->operand[0], iattr, sattr);
692         tmp = (char*)sattr.base
693             + ((dbVarying*)(sattr.base + expr->offs))->offs;
694         sattr.array.size = ((dbVarying*)(sattr.base + expr->offs))->size;
695         sattr.array.base = tmp;
696         return;
697       case dbvmLoadRawBinary:
698         execute(expr->operand[0], iattr, sattr);
699         sattr.raw = (void*)(sattr.base+expr->offs);
700         return;
701 
702       case dbvmLoadSelfBool:
703         sattr.bvalue = *(bool*)(iattr.record+expr->offs);
704         return;
705       case dbvmLoadSelfInt1:
706         sattr.ivalue = *(int1*)(iattr.record+expr->offs);
707         return;
708       case dbvmLoadSelfInt2:
709         sattr.ivalue = *(int2*)(iattr.record+expr->offs);
710         return;
711       case dbvmLoadSelfInt4:
712         sattr.ivalue = *(int4*)(iattr.record+expr->offs);
713         return;
714       case dbvmLoadSelfInt8:
715         sattr.ivalue = *(db_int8*)(iattr.record+expr->offs);
716         return;
717       case dbvmLoadSelfReal4:
718         sattr.fvalue = *(real4*)(iattr.record+expr->offs);
719         return;
720       case dbvmLoadSelfReal8:
721         sattr.fvalue = *(real8*)(iattr.record+expr->offs);
722         return;
723       case dbvmLoadSelfReference:
724         sattr.oid = *(oid_t*)(iattr.record+expr->offs);
725         return;
726       case dbvmLoadSelfRectangle:
727         sattr.rvalue = *(rectangle*)(iattr.record+expr->offs);
728         return;
729       case dbvmLoadSelfArray:
730       case dbvmLoadSelfString:
731         sattr.array.base = (char*)iattr.record +
732             ((dbVarying*)(iattr.record + expr->offs))->offs;
733         sattr.array.size = ((dbVarying*)(iattr.record + expr->offs))->size;
734         sattr.osClass = dbSynthesizedAttribute::osSelf;
735         return;
736       case dbvmLoadSelfRawBinary:
737         sattr.raw = (void*)(iattr.record+expr->offs);
738         return;
739 
740       case dbvmInvokeMethodBool:
741       {
742           bool val;
743           execute(expr->ref.base, iattr, sattr);
744           expr->ref.field->method->invoke(sattr.base, &val);
745           sattr.bvalue = val;
746           iattr.free(sattr);
747           return;
748       }
749       case dbvmInvokeMethodInt1:
750       {
751           int1 val;
752           execute(expr->ref.base, iattr, sattr);
753           expr->ref.field->method->invoke(sattr.base, &val);
754           sattr.ivalue = val;
755           iattr.free(sattr);
756           return;
757       }
758       case dbvmInvokeMethodInt2:
759       {
760           int2 val;
761           execute(expr->ref.base, iattr, sattr);
762           expr->ref.field->method->invoke(sattr.base, &val);
763           sattr.ivalue = val;
764           iattr.free(sattr);
765           return;
766       }
767       case dbvmInvokeMethodInt4:
768       {
769           int4 val;
770           execute(expr->ref.base, iattr, sattr);
771           expr->ref.field->method->invoke(sattr.base, &val);
772           sattr.ivalue = val;
773           iattr.free(sattr);
774           return;
775       }
776       case dbvmInvokeMethodInt8:
777         execute(expr->ref.base, iattr, sattr);
778         expr->ref.field->method->invoke(sattr.base, &sattr.ivalue);
779         iattr.free(sattr);
780         return;
781       case dbvmInvokeMethodReal4:
782       {
783           real4 val;
784           execute(expr->ref.base, iattr, sattr);
785           expr->ref.field->method->invoke(sattr.base, &val);
786           sattr.fvalue = val;
787           iattr.free(sattr);
788           return;
789       }
790       case dbvmInvokeMethodReal8:
791         execute(expr->ref.base, iattr, sattr);
792         expr->ref.field->method->invoke(sattr.base, &sattr.fvalue);
793         iattr.free(sattr);
794         return;
795       case dbvmInvokeMethodReference:
796         execute(expr->ref.base, iattr, sattr);
797         expr->ref.field->method->invoke(sattr.base, &sattr.oid);
798         iattr.free(sattr);
799         return;
800       case dbvmInvokeMethodString:
801         execute(expr->ref.base, iattr, sattr);
802         expr->ref.field->method->invoke(sattr.base, &sattr.array.base);
803         iattr.free(sattr);
804         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
805         iattr.makeDynamic(sattr, sattr.array.base);
806         return;
807 
808       case dbvmInvokeSelfMethodBool:
809       {
810           bool val;
811           expr->ref.field->method->invoke(iattr.record, &val);
812           sattr.bvalue = val;
813           return;
814       }
815       case dbvmInvokeSelfMethodInt1:
816       {
817           int1 val;
818           expr->ref.field->method->invoke(iattr.record, &val);
819           sattr.ivalue = val;
820           return;
821       }
822       case dbvmInvokeSelfMethodInt2:
823       {
824           int2 val;
825           expr->ref.field->method->invoke(iattr.record, &val);
826           sattr.ivalue = val;
827           return;
828       }
829       case dbvmInvokeSelfMethodInt4:
830       {
831           int4 val;
832           expr->ref.field->method->invoke(iattr.record, &val);
833           sattr.ivalue = val;
834           return;
835       }
836       case dbvmInvokeSelfMethodInt8:
837         expr->ref.field->method->invoke(iattr.record, &sattr.ivalue);
838         return;
839       case dbvmInvokeSelfMethodReal4:
840       {
841           real4 val;
842           expr->ref.field->method->invoke(iattr.record, &val);
843           sattr.fvalue = *(real4*)&sattr.fvalue;
844           return;
845       }
846       case dbvmInvokeSelfMethodReal8:
847         expr->ref.field->method->invoke(iattr.record, &sattr.fvalue);
848         return;
849       case dbvmInvokeSelfMethodReference:
850         expr->ref.field->method->invoke(iattr.record, &sattr.oid);
851         return;
852       case dbvmInvokeSelfMethodString:
853         expr->ref.field->method->invoke(iattr.record, &sattr.array.base);
854         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
855         iattr.makeDynamic(sattr, sattr.array.base);
856         return;
857 
858       case dbvmLength:
859         execute(expr->operand[0], iattr, sattr);
860         sattr.ivalue = sattr.array.size;
861         iattr.free(sattr);
862         return;
863       case dbvmStringLength:
864         execute(expr->operand[0], iattr, sattr);
865         sattr.ivalue = sattr.array.size - 1;
866         iattr.free(sattr);
867         return;
868 
869       case dbvmGetAt:
870         execute(expr->operand[0], iattr, sattr);
871         execute(expr->operand[1], iattr, sattr2);
872         if ((nat8)sattr2.ivalue >= (nat8)sattr.array.size) {
873             if (expr->operand[1]->cop == dbvmVariable) {
874                 iattr.unwind(expr->operand[1]->offs);
875             }
876             iattr.cleanup();
877             iattr.db->handleError(IndexOutOfRangeError, NULL,
878                                   int(sattr2.ivalue));
879         }
880         sattr.base = (byte*)sattr.array.base + int(sattr2.ivalue)*expr->offs;
881         return;
882       case dbvmRectangleCoord:
883         execute(expr->operand[0], iattr, sattr);
884         execute(expr->operand[1], iattr, sattr2);
885         if ((nat8)sattr2.ivalue >= rectangle::dim*2) {
886             if (expr->operand[1]->cop == dbvmVariable) {
887                 iattr.unwind(expr->operand[1]->offs);
888             }
889             iattr.cleanup();
890             iattr.db->handleError(IndexOutOfRangeError, NULL,
891                                   int(sattr2.ivalue));
892         }
893         sattr.fvalue = sattr.rvalue.boundary[int(sattr2.ivalue)];
894         iattr.free(sattr);
895         return;
896       case dbvmCharAt:
897         execute(expr->operand[0], iattr, sattr);
898         execute(expr->operand[1], iattr, sattr2);
899         if ((nat8)sattr2.ivalue >= (nat8)(sattr.array.size-1)) {
900             if (expr->operand[1]->cop == dbvmVariable) {
901                 iattr.unwind(expr->operand[1]->offs);
902             }
903             iattr.cleanup();
904             iattr.db->handleError(IndexOutOfRangeError, NULL,
905                                   int(sattr2.ivalue));
906         }
907         sattr.ivalue = *((char_t*)sattr.array.base + int(sattr2.ivalue)) & ((1 << 8*sizeof(char_t))-1);
908         iattr.free(sattr);
909         return;
910 
911       case dbvmExists:
912         iattr.exists_iterator[expr->offs].index = 0;
913         iattr.exists_iterator[expr->offs].sp = (int)iattr.sp;
914         iattr.exists_iterator[expr->offs].dynChain = iattr.dynChain;
915 #ifdef DO_NOT_USE_SETJMP
916         try
917 #else
918         if (setjmp(iattr.exists_iterator[expr->offs].unwind) == 0)
919 #endif
920         {
921             do {
922                 execute(expr->operand[0], iattr, sattr);
923                 iattr.exists_iterator[expr->offs].index += 1;
924             } while (!sattr.bvalue);
925         }
926 #ifdef DO_NOT_USE_SETJMP
927         catch (OutOfBoundsException&)
928 #else
929         else
930 #endif
931         {
932             sattr.bvalue = false;
933         }
934         return;
935 
936       case dbvmVariable:
937         sattr.ivalue = iattr.exists_iterator[expr->offs].index;
938         return;
939 
940       case dbvmLoadVarBool:
941         sattr.bvalue = *(bool*)((char*)expr->var + iattr.paramBase);
942         return;
943       case dbvmLoadVarInt1:
944         sattr.ivalue = *(int1*)((char*)expr->var + iattr.paramBase);
945         return;
946       case dbvmLoadVarInt2:
947         sattr.ivalue = *(int2*)((char*)expr->var + iattr.paramBase);
948         return;
949       case dbvmLoadVarInt4:
950         sattr.ivalue = *(int4*)((char*)expr->var + iattr.paramBase);
951         return;
952       case dbvmLoadVarInt8:
953         sattr.ivalue = *(db_int8*)((char*)expr->var + iattr.paramBase);
954         return;
955       case dbvmLoadVarReal4:
956         sattr.fvalue = *(real4*)((char*)expr->var + iattr.paramBase);
957         return;
958       case dbvmLoadVarReal8:
959         sattr.fvalue = *(real8*)((char*)expr->var + iattr.paramBase);
960         return;
961       case dbvmLoadVarReference:
962         sattr.oid = *(oid_t*)((char*)expr->var + iattr.paramBase);
963         return;
964       case dbvmLoadVarRectangle:
965         sattr.rvalue = *(rectangle*)((char*)expr->var + iattr.paramBase);
966         return;
967       case dbvmLoadVarRectanglePtr:
968         sattr.rvalue = **(rectangle**)((char*)expr->var + iattr.paramBase);
969         return;
970       case dbvmLoadVarString:
971         sattr.array.base = (char*)((char*)expr->var + iattr.paramBase);
972         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
973         sattr.osClass = dbSynthesizedAttribute::osSelf;
974         return;
975       case dbvmLoadVarStringPtr:
976         sattr.array.base = *(char**)((char*)expr->var + iattr.paramBase);
977         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
978         sattr.osClass = dbSynthesizedAttribute::osSelf;
979         return;
980       case dbvmLoadVarArrayOfOid:
981       case dbvmLoadVarArrayOfInt4:
982       case dbvmLoadVarArrayOfInt8:
983         sattr.array.base = (char*)((dbAnyArray*)((char*)expr->var + iattr.paramBase))->base();
984         sattr.array.size = (int)((dbAnyArray*)((char*)expr->var + iattr.paramBase))->length();
985         sattr.osClass = dbSynthesizedAttribute::osSelf;
986         return;
987       case dbvmLoadVarArrayOfOidPtr:
988       case dbvmLoadVarArrayOfInt4Ptr:
989       case dbvmLoadVarArrayOfInt8Ptr:
990         {
991             dbAnyArray* arr = *(dbAnyArray**)((char*)expr->var + iattr.paramBase);
992             sattr.array.base = (char*)arr->base();
993             sattr.array.size = (int)arr->length();
994             sattr.osClass = dbSynthesizedAttribute::osSelf;
995             return;
996         }
997       case dbvmLoadVarRawBinary:
998         sattr.raw = (void*)((char*)expr->var + iattr.paramBase);
999         return;
1000       case dbvmLoadVarRawBinaryPtr:
1001         sattr.raw = *(void**)((char*)expr->var + iattr.paramBase);
1002         return;
1003 #ifdef USE_MFC_STRING
1004       case dbvmLoadVarMfcString:
1005         sattr.array.base = (char*)(MFC_STRING::PCXSTR)*(MFC_STRING*)((char*)expr->var + iattr.paramBase);
1006         sattr.array.size = ((MFC_STRING*)((char*)expr->var + iattr.paramBase))->GetLength() + 1;
1007         sattr.osClass = dbSynthesizedAttribute::osSelf;
1008         return;
1009 #endif
1010 #ifdef USE_STD_STRING
1011       case dbvmLoadVarStdString:
1012         sattr.array.base = (char*)((STD_STRING*)((char*)expr->var + iattr.paramBase))->c_str();
1013         sattr.array.size = (int)((STD_STRING*)((char*)expr->var + iattr.paramBase))->length() + 1;
1014         sattr.osClass = dbSynthesizedAttribute::osSelf;
1015         return;
1016 #endif
1017       case dbvmLoadTrue:
1018         sattr.bvalue = true;
1019         return;
1020       case dbvmLoadFalse:
1021         sattr.bvalue = false;
1022         return;
1023       case dbvmLoadNull:
1024         sattr.oid = 0;
1025         return;
1026       case dbvmLoadIntConstant:
1027         sattr.ivalue = expr->ivalue;
1028         return;
1029       case dbvmLoadRealConstant:
1030         sattr.fvalue = expr->fvalue;
1031         return;
1032       case dbvmLoadRectangleConstant:
1033         sattr.rvalue = expr->rvalue;
1034         return;
1035       case dbvmLoadStringConstant:
1036         sattr.array.base = (char*)expr->svalue.str;
1037         sattr.array.size = expr->svalue.len;
1038         sattr.osClass = dbSynthesizedAttribute::osSelf;
1039         return;
1040 
1041       case dbvmOrBool:
1042         execute(expr->operand[0], iattr, sattr);
1043         if (sattr.bvalue == 0) {
1044             execute(expr->operand[1], iattr, sattr);
1045         }
1046         return;
1047       case dbvmAndBool:
1048         execute(expr->operand[0], iattr, sattr);
1049         if (sattr.bvalue != 0) {
1050             execute(expr->operand[1], iattr, sattr);
1051         }
1052         return;
1053       case dbvmNotBool:
1054         execute(expr->operand[0], iattr, sattr);
1055         sattr.bvalue = !sattr.bvalue;
1056         return;
1057 
1058       case dbvmIsNull:
1059         execute(expr->operand[0], iattr, sattr);
1060         sattr.bvalue = sattr.oid == 0;
1061         return;
1062 
1063       case dbvmAddRectangle:
1064         execute(expr->operand[0], iattr, sattr);
1065         execute(expr->operand[1], iattr, sattr2);
1066         sattr.rvalue += sattr2.rvalue;
1067         return;
1068 
1069       case dbvmNegInt:
1070         execute(expr->operand[0], iattr, sattr);
1071         sattr.ivalue = -sattr.ivalue;
1072         return;
1073       case dbvmAddInt:
1074         execute(expr->operand[0], iattr, sattr);
1075         execute(expr->operand[1], iattr, sattr2);
1076         sattr.ivalue += sattr2.ivalue;
1077         return;
1078       case dbvmSubInt:
1079         execute(expr->operand[0], iattr, sattr);
1080         execute(expr->operand[1], iattr, sattr2);
1081         sattr.ivalue -= sattr2.ivalue;
1082         return;
1083       case dbvmMulInt:
1084         execute(expr->operand[0], iattr, sattr);
1085         execute(expr->operand[1], iattr, sattr2);
1086         sattr.ivalue *= sattr2.ivalue;
1087         return;
1088       case dbvmDivInt:
1089         execute(expr->operand[0], iattr, sattr);
1090         execute(expr->operand[1], iattr, sattr2);
1091         if (sattr2.ivalue == 0) {
1092             iattr.cleanup();
1093             iattr.db->handleError(ArithmeticError, "Division by zero");
1094         } else {
1095             sattr.ivalue /= sattr2.ivalue;
1096         }
1097         return;
1098       case dbvmAndInt:
1099         execute(expr->operand[0], iattr, sattr);
1100         execute(expr->operand[1], iattr, sattr2);
1101         sattr.ivalue &= sattr2.ivalue;
1102         return;
1103       case dbvmOrInt:
1104         execute(expr->operand[0], iattr, sattr);
1105         execute(expr->operand[1], iattr, sattr2);
1106         sattr.ivalue |= sattr2.ivalue;
1107         return;
1108       case dbvmNotInt:
1109         execute(expr->operand[0], iattr, sattr);
1110         sattr.ivalue = ~sattr.ivalue;
1111         return;
1112       case dbvmAbsInt:
1113         execute(expr->operand[0], iattr, sattr);
1114         if (sattr.ivalue < 0) {
1115             sattr.ivalue = -sattr.ivalue;
1116         }
1117         return;
1118       case dbvmPowerInt:
1119         execute(expr->operand[0], iattr, sattr);
1120         execute(expr->operand[1], iattr, sattr2);
1121         if (sattr.ivalue == 2) {
1122             sattr.ivalue = sattr2.ivalue < 64
1123                 ? (nat8)1 << (int)sattr2.ivalue : 0;
1124         } else if (sattr.ivalue == 0 && sattr2.ivalue < 0) {
1125             iattr.cleanup();
1126             iattr.db->handleError(ArithmeticError,
1127                                   "Raise zero to negative power");
1128         } else {
1129             sattr.ivalue = powerIntInt(sattr.ivalue, sattr2.ivalue);
1130         }
1131         return;
1132 
1133 
1134       case dbvmEqInt:
1135         execute(expr->operand[0], iattr, sattr);
1136         execute(expr->operand[1], iattr, sattr2);
1137         sattr.bvalue = sattr.ivalue == sattr2.ivalue;
1138         return;
1139       case dbvmNeInt:
1140         execute(expr->operand[0], iattr, sattr);
1141         execute(expr->operand[1], iattr, sattr2);
1142         sattr.bvalue = sattr.ivalue != sattr2.ivalue;
1143         return;
1144       case dbvmGtInt:
1145         execute(expr->operand[0], iattr, sattr);
1146         execute(expr->operand[1], iattr, sattr2);
1147         sattr.bvalue = sattr.ivalue > sattr2.ivalue;
1148         return;
1149       case dbvmGeInt:
1150         execute(expr->operand[0], iattr, sattr);
1151         execute(expr->operand[1], iattr, sattr2);
1152         sattr.bvalue = sattr.ivalue >= sattr2.ivalue;
1153         return;
1154       case dbvmLtInt:
1155         execute(expr->operand[0], iattr, sattr);
1156         execute(expr->operand[1], iattr, sattr2);
1157         sattr.bvalue = sattr.ivalue < sattr2.ivalue;
1158         return;
1159       case dbvmLeInt:
1160         execute(expr->operand[0], iattr, sattr);
1161         execute(expr->operand[1], iattr, sattr2);
1162         sattr.bvalue = sattr.ivalue <= sattr2.ivalue;
1163         return;
1164       case dbvmBetweenInt:
1165         execute(expr->operand[0], iattr, sattr);
1166         execute(expr->operand[1], iattr, sattr2);
1167         if (sattr.ivalue < sattr2.ivalue) {
1168             sattr.bvalue = false;
1169         } else {
1170             execute(expr->operand[2], iattr, sattr2);
1171             sattr.bvalue = sattr.ivalue <= sattr2.ivalue;
1172         }
1173         return;
1174 
1175       case dbvmEqRectangle:
1176         execute(expr->operand[0], iattr, sattr);
1177         execute(expr->operand[1], iattr, sattr2);
1178         sattr.bvalue = sattr.rvalue == sattr2.rvalue;
1179         return;
1180       case dbvmNeRectangle:
1181         execute(expr->operand[0], iattr, sattr);
1182         execute(expr->operand[1], iattr, sattr2);
1183         sattr.bvalue = sattr.rvalue != sattr2.rvalue;
1184         return;
1185       case dbvmGtRectangle:
1186         execute(expr->operand[0], iattr, sattr);
1187         execute(expr->operand[1], iattr, sattr2);
1188         sattr.bvalue = sattr.rvalue > sattr2.rvalue;
1189         return;
1190       case dbvmGeRectangle:
1191         execute(expr->operand[0], iattr, sattr);
1192         execute(expr->operand[1], iattr, sattr2);
1193         sattr.bvalue = sattr.rvalue >= sattr2.rvalue;
1194         return;
1195       case dbvmLtRectangle:
1196         execute(expr->operand[0], iattr, sattr);
1197         execute(expr->operand[1], iattr, sattr2);
1198         sattr.bvalue = sattr.rvalue < sattr2.rvalue;
1199         return;
1200       case dbvmLeRectangle:
1201         execute(expr->operand[0], iattr, sattr);
1202         execute(expr->operand[1], iattr, sattr2);
1203         sattr.bvalue = sattr.rvalue <= sattr2.rvalue;
1204         return;
1205 
1206       case dbvmOverlapsRectangle:
1207         execute(expr->operand[0], iattr, sattr);
1208         execute(expr->operand[1], iattr, sattr2);
1209         sattr.bvalue = sattr.rvalue & sattr2.rvalue;
1210         return;
1211 
1212       case dbvmRectangleArea:
1213         execute(expr->operand[0], iattr, sattr);
1214         sattr.fvalue = (double)area(sattr.rvalue);
1215         return;
1216 
1217       case dbvmNegReal:
1218         execute(expr->operand[0], iattr, sattr);
1219         sattr.fvalue = -sattr.fvalue;
1220         return;
1221       case dbvmAddReal:
1222         execute(expr->operand[0], iattr, sattr);
1223         execute(expr->operand[1], iattr, sattr2);
1224         sattr.fvalue += sattr2.fvalue;
1225         return;
1226       case dbvmSubReal:
1227         execute(expr->operand[0], iattr, sattr);
1228         execute(expr->operand[1], iattr, sattr2);
1229         sattr.fvalue -= sattr2.fvalue;
1230         return;
1231       case dbvmMulReal:
1232         execute(expr->operand[0], iattr, sattr);
1233         execute(expr->operand[1], iattr, sattr2);
1234         sattr.fvalue *= sattr2.fvalue;
1235         return;
1236       case dbvmDivReal:
1237         execute(expr->operand[0], iattr, sattr);
1238         execute(expr->operand[1], iattr, sattr2);
1239         if (sattr2.fvalue == 0.0) {
1240             iattr.cleanup();
1241             iattr.db->handleError(ArithmeticError, "Division by zero");
1242         } else {
1243             sattr.fvalue /= sattr2.fvalue;
1244         }
1245         return;
1246       case dbvmAbsReal:
1247         execute(expr->operand[0], iattr, sattr);
1248         if (sattr.fvalue < 0) {
1249             sattr.fvalue = -sattr.fvalue;
1250         }
1251         return;
1252       case dbvmPowerReal:
1253         execute(expr->operand[0], iattr, sattr);
1254         execute(expr->operand[1], iattr, sattr2);
1255         if (sattr.fvalue < 0) {
1256             iattr.cleanup();
1257             iattr.db->handleError(ArithmeticError,
1258                                   "Power operator returns complex result");
1259         } else if (sattr.fvalue == 0.0 && sattr2.fvalue < 0) {
1260             iattr.cleanup();
1261             iattr.db->handleError(ArithmeticError,
1262                                   "Raise zero to negative power");
1263         } else {
1264             sattr.fvalue = pow(sattr.fvalue, sattr2.fvalue);
1265         }
1266         return;
1267       case dbvmPowerRealInt:
1268         execute(expr->operand[0], iattr, sattr);
1269         execute(expr->operand[1], iattr, sattr2);
1270         if (sattr.fvalue == 0.0 && sattr2.ivalue < 0) {
1271             iattr.cleanup();
1272             iattr.db->handleError(ArithmeticError,
1273                                   "Raise zero to negative power");
1274         } else {
1275             sattr.fvalue = powerRealInt(sattr.fvalue, sattr2.ivalue);
1276         }
1277         return;
1278 
1279       case dbvmEqReal:
1280         execute(expr->operand[0], iattr, sattr);
1281         execute(expr->operand[1], iattr, sattr2);
1282         sattr.bvalue = sattr.fvalue == sattr2.fvalue;
1283         return;
1284       case dbvmNeReal:
1285         execute(expr->operand[0], iattr, sattr);
1286         execute(expr->operand[1], iattr, sattr2);
1287         sattr.bvalue = sattr.fvalue != sattr2.fvalue;
1288         return;
1289       case dbvmGtReal:
1290         execute(expr->operand[0], iattr, sattr);
1291         execute(expr->operand[1], iattr, sattr2);
1292         sattr.bvalue = sattr.fvalue > sattr2.fvalue;
1293         return;
1294       case dbvmGeReal:
1295         execute(expr->operand[0], iattr, sattr);
1296         execute(expr->operand[1], iattr, sattr2);
1297         sattr.bvalue = sattr.fvalue >= sattr2.fvalue;
1298         return;
1299       case dbvmLtReal:
1300         execute(expr->operand[0], iattr, sattr);
1301         execute(expr->operand[1], iattr, sattr2);
1302         sattr.bvalue = sattr.fvalue < sattr2.fvalue;
1303         return;
1304       case dbvmLeReal:
1305         execute(expr->operand[0], iattr, sattr);
1306         execute(expr->operand[1], iattr, sattr2);
1307         sattr.bvalue = sattr.fvalue <= sattr2.fvalue;
1308         return;
1309       case dbvmBetweenReal:
1310         execute(expr->operand[0], iattr, sattr);
1311         execute(expr->operand[1], iattr, sattr2);
1312         if (sattr.fvalue < sattr2.fvalue) {
1313             sattr.bvalue = false;
1314         } else {
1315             execute(expr->operand[2], iattr, sattr2);
1316             sattr.bvalue = sattr.fvalue <= sattr2.fvalue;
1317         }
1318         return;
1319 
1320      case dbvmEqBinary:
1321         execute(expr->operand[0], iattr, sattr);
1322         execute(expr->operand[1], iattr, sattr2);
1323         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) == 0;
1324         return;
1325       case dbvmNeBinary:
1326         execute(expr->operand[0], iattr, sattr);
1327         execute(expr->operand[1], iattr, sattr2);
1328         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) != 0;
1329         return;
1330       case dbvmGtBinary:
1331         execute(expr->operand[0], iattr, sattr);
1332         execute(expr->operand[1], iattr, sattr2);
1333         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) > 0;
1334         return;
1335       case dbvmGeBinary:
1336         execute(expr->operand[0], iattr, sattr);
1337         execute(expr->operand[1], iattr, sattr2);
1338         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) >= 0;
1339         return;
1340       case dbvmLtBinary:
1341         execute(expr->operand[0], iattr, sattr);
1342         execute(expr->operand[1], iattr, sattr2);
1343         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) < 0;
1344         return;
1345       case dbvmLeBinary:
1346         execute(expr->operand[0], iattr, sattr);
1347         execute(expr->operand[1], iattr, sattr2);
1348         sattr.bvalue = compareRawBinary(iattr, sattr, sattr2, expr->offs, expr->func.fptr) <= 0;
1349         return;
1350       case dbvmBetweenBinary:
1351         {
1352             execute(expr->operand[0], iattr, sattr);
1353             execute(expr->operand[1], iattr, sattr2);
1354             dbUDTComparator comparator = (dbUDTComparator)expr->func.fptr;
1355             if (comparator(sattr.raw, sattr2.raw, expr->offs) < 0) {
1356                 sattr.bvalue = false;
1357             } else {
1358                 iattr.free(sattr2);
1359                 execute(expr->operand[2], iattr, sattr2);
1360                 sattr.bvalue = comparator(sattr.raw, sattr2.raw, expr->offs) <= 0;
1361             }
1362             iattr.free(sattr2);
1363             iattr.free(sattr);
1364         }
1365         return;
1366 
1367       case dbvmIntToReference:
1368         execute(expr->operand[0], iattr, sattr);
1369         sattr.oid = (oid_t)sattr.ivalue;
1370         return;
1371 
1372       case dbvmIntToReal:
1373         execute(expr->operand[0], iattr, sattr);
1374         sattr.fvalue = (real8)sattr.ivalue;
1375         return;
1376       case dbvmRealToInt:
1377         execute(expr->operand[0], iattr, sattr);
1378         sattr.ivalue = (db_int8)sattr.fvalue;
1379         return;
1380 
1381       case dbvmIntToString:
1382         execute(expr->operand[0], iattr, sattr);
1383         convertIntToString(iattr, sattr);
1384         return;
1385       case dbvmRealToString:
1386         execute(expr->operand[0], iattr, sattr);
1387         convertRealToString(iattr, sattr);
1388         return;
1389       case dbvmStringConcat:
1390         execute(expr->operand[0], iattr, sattr);
1391         execute(expr->operand[1], iattr, sattr2);
1392         concatenateStrings(iattr, sattr, sattr2);
1393         return;
1394       case dbvmUpperString:
1395         execute(expr->operand[0], iattr, sattr);
1396         uppercaseString(iattr, sattr);
1397         return;
1398       case dbvmLowerString:
1399         execute(expr->operand[0], iattr, sattr);
1400         lowercaseString(iattr, sattr);
1401         return;
1402 
1403       case dbvmEqString:
1404         execute(expr->operand[0], iattr, sattr);
1405         execute(expr->operand[1], iattr, sattr2);
1406         sattr.bvalue = compareStringsForEquality(iattr, sattr, sattr2) == 0;
1407         return;
1408       case dbvmNeString:
1409         execute(expr->operand[0], iattr, sattr);
1410         execute(expr->operand[1], iattr, sattr2);
1411         sattr.bvalue = compareStringsForEquality(iattr, sattr, sattr2) != 0;
1412         return;
1413       case dbvmGtString:
1414         execute(expr->operand[0], iattr, sattr);
1415         execute(expr->operand[1], iattr, sattr2);
1416         sattr.bvalue = compareStrings(iattr, sattr, sattr2) > 0;
1417         return;
1418       case dbvmGeString:
1419         execute(expr->operand[0], iattr, sattr);
1420         execute(expr->operand[1], iattr, sattr2);
1421         sattr.bvalue = compareStrings(iattr, sattr, sattr2) >= 0;
1422         return;
1423       case dbvmLtString:
1424         execute(expr->operand[0], iattr, sattr);
1425         execute(expr->operand[1], iattr, sattr2);
1426         sattr.bvalue = compareStrings(iattr, sattr, sattr2) < 0;
1427         return;
1428       case dbvmLeString:
1429         execute(expr->operand[0], iattr, sattr);
1430         execute(expr->operand[1], iattr, sattr2);
1431         sattr.bvalue = compareStrings(iattr, sattr, sattr2) <= 0;
1432         return;
1433 #ifdef USE_REGEX
1434       case dbvmMatchString:
1435         execute(expr->regex.opd, iattr, sattr);
1436         sattr.bvalue = regexec(&expr->regex.re, (char*)sattr.array.base, 0, NULL, 0) == 0;
1437         iattr.free(sattr);
1438         return;
1439 #endif
1440       case dbvmLikeString:
1441         execute(expr->operand[0], iattr, sattr);
1442         execute(expr->operand[1], iattr, sattr2);
1443         sattr.bvalue = matchStrings(iattr, sattr, sattr2);
1444         return;
1445       case dbvmLikeEscapeString:
1446         execute(expr->operand[0], iattr, sattr);
1447         execute(expr->operand[1], iattr, sattr2);
1448         execute(expr->operand[2], iattr, sattr3);
1449         sattr.bvalue = matchStrings(iattr, sattr, sattr2, *sattr3.array.base);
1450         return;
1451       case dbvmBetweenString:
1452         execute(expr->operand[0], iattr, sattr);
1453         execute(expr->operand[1], iattr, sattr2);
1454 #ifdef USE_LOCALE_SETTINGS
1455         if (STRCOLL((char_t*)sattr.array.base, (char_t*)sattr2.array.base) < 0) {
1456             sattr.bvalue = false;
1457         } else {
1458             iattr.free(sattr2);
1459             execute(expr->operand[2], iattr, sattr2);
1460             sattr.bvalue = STRCOLL((char_t*)sattr.array.base, (char_t*)sattr2.array.base) <= 0;
1461         }
1462 #else
1463         if (STRCMP((char_t*)sattr.array.base, (char_t*)sattr2.array.base) < 0) {
1464             sattr.bvalue = false;
1465         } else {
1466             iattr.free(sattr2);
1467             execute(expr->operand[2], iattr, sattr2);
1468             sattr.bvalue = STRCMP((char_t*)sattr.array.base, (char_t*)sattr2.array.base) <= 0;
1469         }
1470 #endif
1471         iattr.free(sattr2);
1472         iattr.free(sattr);
1473         return;
1474 
1475       case dbvmEqBool:
1476         execute(expr->operand[0], iattr, sattr);
1477         execute(expr->operand[1], iattr, sattr2);
1478         sattr.bvalue = sattr.bvalue == sattr2.bvalue;
1479         return;
1480       case dbvmNeBool:
1481         execute(expr->operand[0], iattr, sattr);
1482         execute(expr->operand[1], iattr, sattr2);
1483         sattr.bvalue = sattr.bvalue != sattr2.bvalue;
1484         return;
1485 
1486       case dbvmEqReference:
1487         execute(expr->operand[0], iattr, sattr);
1488         execute(expr->operand[1], iattr, sattr2);
1489         sattr.bvalue = sattr.oid == sattr2.oid;
1490         return;
1491       case dbvmNeReference:
1492         execute(expr->operand[0], iattr, sattr);
1493         execute(expr->operand[1], iattr, sattr2);
1494         sattr.bvalue = sattr.oid != sattr2.oid;
1495         return;
1496 
1497       case dbvmDeref:
1498         execute(expr->operand[0], iattr, sattr);
1499         if (sattr.oid == 0) {
1500             iattr.cleanup();
1501             iattr.db->handleError(NullReferenceError);
1502         }
1503         iattr.load(sattr);
1504         return;
1505 
1506       case dbvmFuncArg2Bool:
1507         sattr.bvalue = (*(bool(__cdecl *)(dbUserFunctionArgument const&))expr->func.fptr)
1508             (dbUserFunctionArgument(expr, iattr, sattr, 0));
1509         iattr.free(sattr);
1510         return;
1511       case dbvmFuncArg2Int:
1512         sattr.ivalue = (*(db_int8(__cdecl *)(dbUserFunctionArgument const&))expr->func.fptr)
1513             (dbUserFunctionArgument(expr, iattr, sattr, 0));
1514         iattr.free(sattr);
1515         return;
1516       case dbvmFuncArg2Real:
1517         sattr.fvalue = (*(real8(__cdecl *)(dbUserFunctionArgument const&))expr->func.fptr)
1518             (dbUserFunctionArgument(expr, iattr, sattr, 0));
1519         iattr.free(sattr);
1520         return;
1521       case dbvmFuncArg2Str:
1522         tmp = (*(char*(__cdecl *)(dbUserFunctionArgument const&))expr->func.fptr)
1523             (dbUserFunctionArgument(expr, iattr, sattr, 0));
1524         iattr.free(sattr);
1525         sattr.array.size = (int)STRLEN((char_t*)tmp) + 1;
1526         sattr.array.base = tmp;
1527         iattr.makeDynamic(sattr, tmp);
1528         return;
1529       case dbvmFuncArgArg2Bool:
1530         sattr.bvalue = (*(bool(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1531             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1532              dbUserFunctionArgument(expr, iattr, sattr2, 1));
1533         iattr.free(sattr);
1534         iattr.free(sattr2);
1535         return;
1536       case dbvmFuncArgArg2Int:
1537         sattr.ivalue = (*(db_int8(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1538             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1539              dbUserFunctionArgument(expr, iattr, sattr2, 1));
1540         iattr.free(sattr);
1541         iattr.free(sattr2);
1542         return;
1543       case dbvmFuncArgArg2Real:
1544         sattr.fvalue = (*(real8(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1545             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1546              dbUserFunctionArgument(expr, iattr, sattr2, 1));
1547         iattr.free(sattr);
1548         iattr.free(sattr2);
1549         return;
1550       case dbvmFuncArgArg2Str:
1551         tmp = (char*)(*(char_t*(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1552             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1553              dbUserFunctionArgument(expr, iattr, sattr2, 1));
1554         iattr.free(sattr);
1555         iattr.free(sattr2);
1556         sattr.array.size = (int)STRLEN((char_t*)tmp) + 1;
1557         sattr.array.base = tmp;
1558         iattr.makeDynamic(sattr, tmp);
1559         return;
1560       case dbvmFuncArgArgArg2Bool:
1561         sattr.bvalue = (*(bool(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1562             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1563              dbUserFunctionArgument(expr, iattr, sattr2, 1),
1564              dbUserFunctionArgument(expr, iattr, sattr3, 2));
1565         iattr.free(sattr);
1566         iattr.free(sattr2);
1567         iattr.free(sattr3);
1568         return;
1569       case dbvmFuncArgArgArg2Int:
1570         sattr.ivalue = (*(db_int8(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1571             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1572              dbUserFunctionArgument(expr, iattr, sattr2, 1),
1573              dbUserFunctionArgument(expr, iattr, sattr3, 2));
1574         iattr.free(sattr);
1575         iattr.free(sattr2);
1576         iattr.free(sattr3);
1577         return;
1578       case dbvmFuncArgArgArg2Real:
1579         sattr.fvalue = (*(real8(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1580             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1581              dbUserFunctionArgument(expr, iattr, sattr2, 1),
1582              dbUserFunctionArgument(expr, iattr, sattr3, 2));
1583         iattr.free(sattr);
1584         iattr.free(sattr2);
1585         iattr.free(sattr3);
1586         return;
1587       case dbvmFuncArgArgArg2Str:
1588         tmp = (char*)(*(char_t*(__cdecl *)(dbUserFunctionArgument const&, dbUserFunctionArgument const&, dbUserFunctionArgument const&))expr->func.fptr)
1589             (dbUserFunctionArgument(expr, iattr, sattr, 0),
1590              dbUserFunctionArgument(expr, iattr, sattr2, 1),
1591              dbUserFunctionArgument(expr, iattr, sattr3, 2));
1592         iattr.free(sattr);
1593         iattr.free(sattr2);
1594         iattr.free(sattr3);
1595         sattr.array.size = (int)STRLEN((char_t*)tmp) + 1;
1596         sattr.array.base = tmp;
1597         iattr.makeDynamic(sattr, tmp);
1598         return;
1599 
1600 
1601       case dbvmFuncInt2Bool:
1602         execute(expr->func.arg[0], iattr, sattr);
1603         sattr.bvalue = (*(bool(__cdecl *)(db_int8))expr->func.fptr)(sattr.ivalue);
1604         return;
1605       case dbvmFuncReal2Bool:
1606         execute(expr->func.arg[0], iattr, sattr);
1607         sattr.bvalue = (*(bool(__cdecl *)(real8))expr->func.fptr)(sattr.fvalue);
1608         return;
1609       case dbvmFuncStr2Bool:
1610         execute(expr->func.arg[0], iattr, sattr);
1611         sattr.bvalue =
1612             (*(bool(__cdecl *)(char_t const*))expr->func.fptr)((char_t*)sattr.array.base);
1613         iattr.free(sattr);
1614         return;
1615       case dbvmFuncInt2Int:
1616         execute(expr->func.arg[0], iattr, sattr);
1617         sattr.ivalue = (*(db_int8(__cdecl *)(db_int8))expr->func.fptr)(sattr.ivalue);
1618         return;
1619       case dbvmFuncReal2Int:
1620         execute(expr->func.arg[0], iattr, sattr);
1621         sattr.ivalue = (*(db_int8(__cdecl *)(real8))expr->func.fptr)(sattr.fvalue);
1622         return;
1623       case dbvmFuncStr2Int:
1624         execute(expr->func.arg[0], iattr, sattr);
1625         sattr.ivalue =
1626             (*(db_int8(__cdecl *)(char_t const*))expr->func.fptr)((char_t*)sattr.array.base);
1627         iattr.free(sattr);
1628         return;
1629       case dbvmFuncInt2Real:
1630         execute(expr->func.arg[0], iattr, sattr);
1631         sattr.fvalue = (*(real8(__cdecl *)(db_int8))expr->func.fptr)(sattr.ivalue);
1632         return;
1633       case dbvmFuncReal2Real:
1634         execute(expr->func.arg[0], iattr, sattr);
1635         sattr.fvalue = (*(real8(__cdecl *)(real8))expr->func.fptr)(sattr.fvalue);
1636         return;
1637       case dbvmFuncStr2Real:
1638         execute(expr->func.arg[0], iattr, sattr);
1639         sattr.fvalue =
1640             (*(real8(__cdecl *)(char_t const*))expr->func.fptr)((char_t*)sattr.array.base);
1641         iattr.free(sattr);
1642         return;
1643       case dbvmFuncInt2Str:
1644         execute(expr->func.arg[0], iattr, sattr);
1645         sattr.array.base =
1646             (char*)(*(char_t*(__cdecl *)(db_int8))expr->func.fptr)(sattr.ivalue);
1647         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
1648         iattr.makeDynamic(sattr, sattr.array.base);
1649         return;
1650       case dbvmFuncReal2Str:
1651         execute(expr->func.arg[0], iattr, sattr);
1652         sattr.array.base =
1653             (char*)(*(char_t*(__cdecl *)(real8))expr->func.fptr)(sattr.fvalue);
1654         sattr.array.size = (int)STRLEN((char_t*)sattr.array.base) + 1;
1655         iattr.makeDynamic(sattr, sattr.array.base);
1656         return;
1657       case dbvmFuncStr2Str:
1658         execute(expr->func.arg[0], iattr, sattr);
1659         tmp = (char*)(*(char_t*(__cdecl *)(char_t const*))expr->func.fptr)((char_t*)sattr.array.base);
1660         iattr.free(sattr);
1661         sattr.array.size = (int)STRLEN((char_t*)tmp) + 1;
1662         sattr.array.base = tmp;
1663         iattr.makeDynamic(sattr, tmp);
1664         return;
1665 
1666       case dbvmInArrayBool:
1667         execute(expr->operand[0], iattr, sattr);
1668         execute(expr->operand[1], iattr, sattr2);
1669         searchArrayOfBool(iattr, sattr, sattr2);
1670         return;
1671       case dbvmInArrayInt1:
1672         execute(expr->operand[0], iattr, sattr);
1673         execute(expr->operand[1], iattr, sattr2);
1674         searchArrayOfInt1(iattr, sattr, sattr2);
1675         return;
1676       case dbvmInArrayInt2:
1677         execute(expr->operand[0], iattr, sattr);
1678         execute(expr->operand[1], iattr, sattr2);
1679         searchArrayOfInt2(iattr, sattr, sattr2);
1680         return;
1681       case dbvmInArrayInt4:
1682         execute(expr->operand[0], iattr, sattr);
1683         execute(expr->operand[1], iattr, sattr2);
1684         searchArrayOfInt4(iattr, sattr, sattr2);
1685         return;
1686       case dbvmInArrayInt8:
1687         execute(expr->operand[0], iattr, sattr);
1688         execute(expr->operand[1], iattr, sattr2);
1689         searchArrayOfInt8(iattr, sattr, sattr2);
1690         return;
1691       case dbvmInArrayReal4:
1692         execute(expr->operand[0], iattr, sattr);
1693         execute(expr->operand[1], iattr, sattr2);
1694         searchArrayOfReal4(iattr, sattr, sattr2);
1695         return;
1696       case dbvmInArrayReal8:
1697         execute(expr->operand[0], iattr, sattr);
1698         execute(expr->operand[1], iattr, sattr2);
1699         searchArrayOfReal8(iattr, sattr, sattr2);
1700         return;
1701       case dbvmInArrayString:
1702         execute(expr->operand[0], iattr, sattr);
1703         execute(expr->operand[1], iattr, sattr2);
1704         searchArrayOfString(iattr, sattr, sattr2);
1705         return;
1706       case dbvmInArrayReference:
1707         execute(expr->operand[0], iattr, sattr);
1708         execute(expr->operand[1], iattr, sattr2);
1709         searchArrayOfReference(iattr, sattr, sattr2);
1710         return;
1711       case dbvmInArrayRectangle:
1712         execute(expr->operand[0], iattr, sattr);
1713         execute(expr->operand[1], iattr, sattr2);
1714         searchArrayOfRectangle(iattr, sattr, sattr2);
1715         return;
1716       case dbvmInString:
1717         execute(expr->operand[0], iattr, sattr);
1718         execute(expr->operand[1], iattr, sattr2);
1719         searchInString(iattr, sattr, sattr2);
1720         return;
1721 
1722       case dbvmList:
1723         return;
1724 
1725       default:
1726         assert(false);
1727     }
1728 }
1729 
1730 
1731 char const* const dbDatabase::errorMessage[] =
1732 {
1733     "No error",
1734     "Query syntax error",
1735     "Arithmetic exception",
1736     "Index out of range",
1737     "Database open error",
1738     "File access error",
1739     "Out of memory",
1740     "Deadlock",
1741     "Null reference",
1742     "File limit exeeded",
1743     "Attempt to modify read-only database",
1744     "Unique constraint violation",
1745     "Inconsistent inverse reference",
1746     "Operation not supported",
1747     "Socket communication error",
1748     "Cursor error",
1749     "Access to deleted object",
1750     "Incompatible schema change",
1751     "Operation is rejected by transaction logger"
1752 };
1753 
handleError(dbErrorClass error,char const * msg,int arg)1754 void dbDatabase::handleError(dbErrorClass error, char const* msg, int arg)
1755 {
1756     if (errorHandler != NULL) {
1757         (*errorHandler)(error, msg, arg, errorHandlerContext);
1758     }
1759 #ifdef THROW_EXCEPTION_ON_ERROR
1760     if (error != NoError) {
1761         if (msg == NULL) {
1762             msg = errorMessage[error];
1763         }
1764         if (error == DatabaseOpenError || (error == InconsistentInverseReference && dbTableDescriptor::chain == NULL)) {
1765             fprintf(stderr, "%s\n", msg);
1766         } else {
1767             throw dbException(error, msg, arg);
1768         }
1769     }
1770 #else
1771     switch (error) {
1772       case QueryError:
1773         fprintf(stderr, "%s in position %d\n", msg, arg);
1774         return;
1775       case ArithmeticError:
1776         fprintf(stderr, "%s\n", msg);
1777         break;
1778       case IndexOutOfRangeError:
1779         fprintf(stderr, "Index %d is out of range\n", arg);
1780         break;
1781       case DatabaseOpenError:
1782         fprintf(stderr, "%s\n", msg);
1783         return;
1784       case FileError:
1785         {
1786           char_t buf[256];
1787           file->errorText(arg, buf, itemsof(buf));
1788           fprintf(stderr, "%s: ", msg);
1789           FPRINTF(stderr, STRLITERAL("%s\n"), buf);
1790         }
1791         break;
1792       case UniqueConstraintViolation:
1793         fprintf(stderr, "Unique constraint violation\n");
1794         return;
1795       case OutOfMemoryError:
1796         fprintf(stderr,"Not enough memory: out of memory\n");
1797         break;
1798       case NullReferenceError:
1799         fprintf(stderr, "Null object reference is accessed\n");
1800         break;
1801       case Deadlock:
1802         fprintf(stderr, "Deadlock is caused by upgrading "
1803                 "shared locks to exclusive");
1804         break;
1805       case FileLimitExeeded:
1806         fprintf(stderr, "Database file size limit exeeded");
1807         break;
1808       case DatabaseReadOnly:
1809         fprintf(stderr, "Attempt to modify readonly database");
1810         break;
1811       case InconsistentInverseReference:
1812         fprintf(stderr, "%s\n", msg);
1813         break;
1814       case RejectedByTransactionLogger:
1815         fprintf(stderr, "Operation is rejected by transaction logger\n");
1816         break;
1817       case NoError:
1818         break;
1819     }
1820     abort();
1821 #endif
1822 }
1823 
initializeMetaTable()1824 void dbDatabase::initializeMetaTable()
1825 {
1826     static struct {
1827         char_t const* name;
1828         int           type;
1829         int           size;
1830         int           offs;
1831     } metaTableFields[] = {
1832         { _T("name"), dbField::tpString, sizeof(dbVarying),
1833           offsetof(dbTable, name)},
1834         { _T("fields"), dbField::tpArray, sizeof(dbVarying),
1835           offsetof(dbTable, fields)},
1836         { _T("fields[]"), dbField::tpStructure, sizeof(dbField), 0},
1837         { _T("fields[].name"), dbField::tpString, sizeof(dbVarying),
1838           offsetof(dbField, name)},
1839         { _T("fields[].tableName"), dbField::tpString,sizeof(dbVarying),
1840           offsetof(dbField, tableName)},
1841         { _T("fields[].inverse"), dbField::tpString, sizeof(dbVarying),
1842           offsetof(dbField, inverse)},
1843 //        { _T("fields[].type"), dbField::tpInt4, 4, offsetof(dbField, type)},
1844         { _T("fields[].type"), dbField::tpInt4, 4, offsetof(dbField, offset)-4},
1845         { _T("fields[].offset"), dbField::tpInt4, 4, offsetof(dbField, offset)},
1846         { _T("fields[].size"), dbField::tpInt4, 4, offsetof(dbField, size)},
1847         { _T("fields[].hashTable"), dbField::tpReference, sizeof(oid_t),
1848           offsetof(dbField, hashTable)},
1849         { _T("fields[].bTree"), dbField::tpReference, sizeof(oid_t),
1850           offsetof(dbField, bTree)},
1851         { _T("fixedSize"), dbField::tpInt4, 4, offsetof(dbTable, fixedSize)},
1852         { _T("nRows"), dbField::tpInt4, 4, offsetof(dbTable, nRows)},
1853         { _T("nColumns"), dbField::tpInt4, 4, offsetof(dbTable, nColumns)},
1854         { _T("firstRow"), dbField::tpReference, sizeof(oid_t), offsetof(dbTable, firstRow)},
1855         { _T("lastRow"), dbField::tpReference, sizeof(oid_t), offsetof(dbTable, lastRow)}
1856 #ifdef AUTOINCREMENT_SUPPORT
1857         ,{ _T("count"), dbField::tpInt4, 4, offsetof(dbTable, count)}
1858 #endif
1859     };
1860 
1861     unsigned i;
1862     size_t varyingSize = (STRLEN(dbMetaTableName)+1)*sizeof(char_t);
1863     for (i = 0; i < itemsof(metaTableFields); i++) {
1864         varyingSize += (STRLEN(metaTableFields[i].name) + 3)*sizeof(char_t);
1865 
1866     }
1867     offs_t metaTableOffs = allocate((offs_t)(sizeof(dbTable)
1868                                     + sizeof(dbField)*itemsof(metaTableFields)
1869                                     + varyingSize));
1870     setPos(dbMetaTableId, metaTableOffs);
1871     dbTable* table = (dbTable*)pool.put(metaTableOffs);
1872     table->size = (nat4)(sizeof(dbTable) + sizeof(dbField)*itemsof(metaTableFields)
1873                 + varyingSize);
1874     table->next = table->prev = 0;
1875     int offs = sizeof(dbTable) + sizeof(dbField)*itemsof(metaTableFields);
1876     table->name.offs = offs;
1877     table->name.size = (nat4)((STRLEN(dbMetaTableName)+1)*sizeof(char_t));
1878     STRCPY((char_t*)((byte*)table + offs), dbMetaTableName);
1879     offs += table->name.size*sizeof(char_t);
1880     table->fields.offs = sizeof(dbTable);
1881     table->fields.size = itemsof(metaTableFields);
1882     table->fixedSize = sizeof(dbTable);
1883     table->nRows = 0;
1884     table->nColumns = 5;
1885     table->firstRow = 0;
1886     table->lastRow = 0;
1887 #ifdef AUTOINCREMENT_SUPPORT
1888     table->count = 0;
1889 #endif
1890 
1891     dbField* field = (dbField*)((byte*)table + table->fields.offs);
1892     offs -= sizeof(dbTable);
1893     for (i = 0; i < itemsof(metaTableFields); i++) {
1894         field->name.offs = offs;
1895         field->name.size = (nat4)STRLEN(metaTableFields[i].name) + 1;
1896         STRCPY((char_t*)((byte*)field + offs), metaTableFields[i].name);
1897         offs += field->name.size*sizeof(char_t);
1898 
1899         field->tableName.offs = offs;
1900         field->tableName.size = 1;
1901         *(char_t*)((byte*)field + offs) = '\0';
1902         offs += sizeof(char_t);
1903 
1904         field->inverse.offs = offs;
1905         field->inverse.size = 1;
1906         *(char_t*)((byte*)field + offs) = '\0';
1907         offs += sizeof(char_t);
1908 
1909         field->bTree = 0;
1910         field->hashTable = 0;
1911         field->flags = 0;
1912         field->type = metaTableFields[i].type;
1913         field->size = metaTableFields[i].size;
1914         field->offset = metaTableFields[i].offs;
1915         field += 1;
1916         offs -= sizeof(dbField);
1917     }
1918     pool.unfix(table);
1919 }
1920 
cleanupOnOpenError()1921 void dbDatabase::cleanupOnOpenError()
1922 {
1923     detach(DESTROY_CONTEXT);
1924 
1925     writeSem.close();
1926     readSem.close();
1927     upgradeSem.close();
1928     backupCompletedEvent.close();
1929 
1930     commitThreadSyncEvent.close();
1931     delayedCommitStartTimerEvent.close();
1932     delayedCommitStopTimerEvent.close();
1933     backupInitEvent.close();
1934 
1935     if (accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite) {
1936         dbDatabaseThreadContext* ctx = threadContext.get();
1937         if (ctx != NULL) {
1938             endTransaction(ctx);
1939         }
1940     }
1941     releaseFile();
1942 }
1943 
1944 
open(OpenParameters & params)1945 bool dbDatabase::open(OpenParameters& params)
1946 {
1947     accessType = params.accessType;
1948     pool.setPoolSize(params.poolSize);
1949     extensionQuantum = params.extensionQuantum;
1950     initIndexSize = params.initIndexSize;
1951     freeSpaceReuseThreshold = params.freeSpaceReuseThreshold;
1952     setConcurrency(params.nThreads);
1953     doNotReuseOidAfterClose = params.doNotReuseOidAfterClose;
1954     preserveExistedIndices = params.preserveExistedIndices;
1955     btreeUnderflowPercent = params.btreeUnderflowPercent;
1956 
1957     if (params.file != NULL) {
1958         return open(params.file, params.transactionCommitDelay, params.deleteFileOnClose);
1959     } else {
1960         return open(params.databaseName, params.transactionCommitDelay, params.openAttr);
1961     }
1962 }
1963 
1964 
open(char_t const * name,time_t transactionCommitDelay,int openAttr)1965 bool dbDatabase::open(char_t const* name, time_t transactionCommitDelay, int openAttr)
1966 {
1967     int rc;
1968     if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
1969         openAttr |= dbFile::read_only;
1970     }
1971     if (accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite) {
1972         openAttr |= dbFile::shared;
1973     }
1974     if (*name == '@') {
1975 #ifdef UNICODE
1976 #if defined(_WIN32)
1977         FILE* f = _wfopen(name+1, _T("r"));
1978 #else
1979         char buf[1024];
1980         wcstombs(buf, name+1, sizeof buf);
1981         FILE* f = fopen(buf, "r");
1982 #endif
1983 #else
1984         FILE* f = fopen(name+1, "r");
1985 #endif
1986         if (f == NULL) {
1987             handleError(DatabaseOpenError,
1988                         "Failed to open database configuration file");
1989             return false;
1990         }
1991         dbMultiFile::dbSegment segment[dbMaxFileSegments];
1992         const int maxFileNameLen = 1024;
1993         char_t fileName[maxFileNameLen];
1994         int i, n;
1995         db_int8 size;
1996         bool raid = false;
1997         size_t raidBlockSize = dbDefaultRaidBlockSize;
1998         for (i=0; (n=FSCANF(f, _T("%s") T_INT8_FORMAT,
1999                             fileName, &size)) >= 1; i++)
2000         {
2001             if (i == dbMaxFileSegments) {
2002                 while (--i >= 0) delete[] segment[i].name;
2003                 fclose(f);
2004                 handleError(DatabaseOpenError, "Too much segments");
2005                 return false;
2006             }
2007 
2008             if (n == 1) {
2009                 if (i == 0) {
2010                     raid = true;
2011                 } else if (!raid && segment[i-1].size == 0) {
2012                     while (--i >= 0) delete[] segment[i].name;
2013                     fclose(f);
2014                     handleError(DatabaseOpenError,
2015                                 "Segment size was not specified");
2016                     return false;
2017                 }
2018                 size = 0;
2019             } else if (size == 0 || raid) {
2020                 while (--i >= 0) delete[] segment[i].name;
2021                 fclose(f);
2022                 handleError(DatabaseOpenError, size == 0 ? "Invalid segment size"
2023                             : "segment size should not be specified for raid");
2024                 return false;
2025             }
2026 
2027             if (STRCMP(fileName, _T(".RaidBlockSize")) == 0) {
2028                 raidBlockSize = (size_t)size;
2029                 raid = true;
2030                 i -= 1;
2031                 continue;
2032             }
2033             segment[i].size = offs_t(size);
2034             char_t* suffix = STRCHR(fileName, '[');
2035             db_int8 offs = 0;
2036             if (suffix != NULL) {
2037                 *suffix = '\0';
2038                 SSCANF(suffix+1, T_INT8_FORMAT, &offs);
2039             }
2040             segment[i].name = new char_t[STRLEN(fileName) + 1];
2041             STRCPY(segment[i].name, fileName);
2042             segment[i].offs = offs_t(offs);
2043         }
2044         fclose(f);
2045         if (i == 0) {
2046             fclose(f);
2047             handleError(DatabaseOpenError,
2048                         "File should have at least one segment");
2049             return false;
2050         }
2051         if (i == 1 && raid) {
2052             raid = false;
2053         }
2054         dbMultiFile* mfile;
2055         if (raid) {
2056             mfile = new dbRaidFile(raidBlockSize);
2057         } else {
2058             mfile = new dbMultiFile();
2059         }
2060         rc = mfile->open(i, segment, openAttr);
2061         while (--i >= 0) delete[] segment[i].name;
2062         if (rc != dbFile::ok) {
2063             char_t msgbuf[64];
2064             mfile->errorText(rc, msgbuf, itemsof(msgbuf));
2065             TRACE_MSG((STRLITERAL("File open error: %s\n"), msgbuf));
2066             delete mfile;
2067             handleError(DatabaseOpenError, "Failed to create database file");
2068             return false;
2069         }
2070         return open(mfile, transactionCommitDelay, true);
2071     } else {
2072         dbOSFile* osfile = new dbOSFile();
2073         rc = osfile->open(name, openAttr);
2074         if (rc != dbFile::ok) {
2075             char_t msgbuf[64];
2076             osfile->errorText(rc, msgbuf, itemsof(msgbuf));
2077             TRACE_MSG((STRLITERAL("File open error: %s\n"), msgbuf));
2078             delete osfile;
2079             handleError(DatabaseOpenError, "Failed to create database file");
2080             return false;
2081         }
2082         return open(osfile,  transactionCommitDelay, true);
2083     }
2084 }
2085 
open(dbFile * file,time_t transactionCommitDelay,bool deleteFileOnClose)2086 bool dbDatabase::open(dbFile* file, time_t transactionCommitDelay, bool deleteFileOnClose)
2087 {
2088 
2089     int rc;
2090     this->file = file;
2091     deleteFile = deleteFileOnClose;
2092     forceCommitCount = 0;
2093     commitDelay = 0;
2094     commitTimeout = 0;
2095     commitTimerStarted = 0;
2096     backupFileName = NULL;
2097     backupPeriod = 0;
2098     opened = false;
2099     logger = NULL;
2100     writeSem.open();
2101     readSem.open();
2102     upgradeSem.open();
2103     backupCompletedEvent.open();
2104 
2105     commitThreadSyncEvent.open();
2106     delayedCommitStartTimerEvent.open();
2107     delayedCommitStopTimerEvent.open();
2108     backupInitEvent.open();
2109     backupFileName = NULL;
2110 
2111     batchList = NULL;
2112 
2113     allocatedSize = 0;
2114     deallocatedSize = 0;
2115 
2116     size_t indexSize = initIndexSize < dbFirstUserId
2117         ? size_t(dbFirstUserId) : initIndexSize;
2118     indexSize = DOALIGN(indexSize, dbHandlesPerPage);
2119 
2120     memset(tableHash, 0, sizeof tableHash);
2121     memset(dirtyPagesMap, 0, dbDirtyPageBitmapSize+4);
2122 
2123     for (int i = dbBitmapId + dbBitmapPages; --i >= 0;) {
2124         bitmapPageAvailableSpace[i] = INT_MAX;
2125     }
2126     currRBitmapPage = currPBitmapPage = dbBitmapId;
2127     currRBitmapOffs = currPBitmapOffs = 0;
2128     reservedChain = NULL;
2129     tables = NULL;
2130     modified = false;
2131     uncommittedChanges = false;
2132     concurrentTransId = 1;
2133     commitInProgress = false;
2134     threadContextList.reset();
2135     curr = 0;
2136     attach();
2137 
2138     memset(header, 0, sizeof(dbHeader));
2139 
2140     if (accessType == dbMulticlientReadOnly) {
2141         beginTransaction(dbSharedLock);
2142     } else if (accessType == dbMulticlientReadWrite) {
2143         assert(transactionCommitDelay == 0);
2144         beginTransaction(dbExclusiveLock);
2145     }
2146 
2147     rc = file->read(0, header, dbPageSize);
2148     if (rc != dbFile::ok && rc != dbFile::eof) {
2149         cleanupOnOpenError();
2150         handleError(DatabaseOpenError, "Failed to read file header");
2151         return false;
2152     }
2153     transactionId = header->transactionId;
2154     if ((unsigned)header->curr > 1) {
2155         cleanupOnOpenError();
2156         handleError(DatabaseOpenError,
2157                     "Database file was corrupted: invalid root index");
2158         return false;
2159     }
2160     if (!header->isInitialized()) {
2161         if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2162             cleanupOnOpenError();
2163             handleError(DatabaseOpenError, "Can not open uninitialized "
2164                         "file in read only mode");
2165             return false;
2166         }
2167         curr = header->curr = 0;
2168         offs_t used = dbPageSize;
2169         header->root[0].index = used;
2170         header->root[0].indexSize = (oid_t)indexSize;
2171         header->root[0].indexUsed = dbFirstUserId;
2172         header->root[0].freeList = 0;
2173         used += (offs_t)indexSize*sizeof(offs_t);
2174         header->root[1].index = used;
2175         header->root[1].indexSize = (oid_t)indexSize;
2176         header->root[1].indexUsed = dbFirstUserId;
2177         header->root[1].freeList = 0;
2178         used += (offs_t)indexSize*sizeof(offs_t);
2179 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
2180         header->root[0].sessionFreeList.head = header->root[0].sessionFreeList.tail = 0;
2181         header->root[1].sessionFreeList.head = header->root[1].sessionFreeList.tail = 0;
2182 #endif
2183 
2184         header->root[0].shadowIndex = header->root[1].index;
2185         header->root[1].shadowIndex = header->root[0].index;
2186         header->root[0].shadowIndexSize = (oid_t)indexSize;
2187         header->root[1].shadowIndexSize = (oid_t)indexSize;
2188 
2189         header->versionMajor = GIGABASE_MAJOR_VERSION;
2190         header->versionMinor = GIGABASE_MINOR_VERSION;
2191         header->mode = dbHeader::getCurrentMode();
2192         size_t bitmapPages =
2193             (size_t)((used + dbPageSize*(dbAllocationQuantum*8-1) - 1)
2194                      / (dbPageSize*(dbAllocationQuantum*8-1)));
2195         size_t bitmapSize = bitmapPages*dbPageSize;
2196         size_t usedBitmapSize = (int)((used + bitmapSize) / (dbAllocationQuantum*8));
2197         byte* bitmap = (byte*)dbOSFile::allocateBuffer(bitmapSize);
2198         memset(bitmap, 0xFF, usedBitmapSize);
2199         memset(bitmap + usedBitmapSize, 0, bitmapSize - usedBitmapSize);
2200         rc = file->write(used, bitmap, bitmapSize);
2201         dbOSFile::deallocateBuffer(bitmap);
2202         if (rc != dbFile::ok) {
2203             cleanupOnOpenError();
2204             handleError(DatabaseOpenError, "Failed to write to the file");
2205             return false;
2206         }
2207         size_t bitmapIndexSize =
2208             DOALIGN((offs_t)(dbBitmapId + dbBitmapPages)*sizeof(offs_t), (offs_t)dbPageSize);
2209         offs_t* index = (offs_t*)dbOSFile::allocateBuffer(bitmapIndexSize);
2210         index[dbInvalidId] = dbFreeHandleFlag;
2211         size_t i;
2212         for (i = 0; i < bitmapPages; i++) {
2213             index[dbBitmapId + i] = used | dbPageObjectFlag | dbModifiedFlag;
2214             used += dbPageSize;
2215         }
2216         header->root[0].bitmapEnd = dbBitmapId + (oid_t)i;
2217         header->root[1].bitmapEnd = dbBitmapId + (oid_t)i;
2218         while (i < dbBitmapPages) {
2219             index[dbBitmapId+i] = dbFreeHandleFlag;
2220             i += 1;
2221         }
2222         rc = file->write(header->root[1].index, index, bitmapIndexSize);
2223         dbOSFile::deallocateBuffer(index);
2224         if (rc != dbFile::ok) {
2225             cleanupOnOpenError();
2226             handleError(DatabaseOpenError, "Failed to write index to the file");
2227             return false;
2228         }
2229         header->root[0].size = used;
2230         header->root[1].size = used;
2231         committedIndexSize = 0;
2232         currIndexSize = dbFirstUserId;
2233         if (!pool.open(file, used)) {
2234             cleanupOnOpenError();
2235             handleError(DatabaseOpenError, "Failed to allocate page pool");
2236             return false;
2237         }
2238         if (dbFileExtensionQuantum != 0) {
2239             if (file->setSize(DOALIGN(used, dbFileExtensionQuantum)) != dbFile::ok) {
2240                 cleanupOnOpenError();
2241                 handleError(DatabaseOpenError, "Failed to set file size");
2242                 return false;
2243             }
2244         }
2245         initializeMetaTable();
2246         offs_t indexPage = header->root[1].index;
2247         offs_t lastIndexPage =
2248             indexPage + (offs_t)header->root[1].bitmapEnd*sizeof(offs_t);
2249         while (indexPage < lastIndexPage) {
2250             offs_t* p = (offs_t*)pool.put(indexPage);
2251             for (i = 0; i < dbHandlesPerPage; i++) {
2252                 p[i] &= ~dbModifiedFlag;
2253             }
2254             pool.unfix(p);
2255             indexPage += dbPageSize;
2256         }
2257         pool.copy(header->root[0].index, header->root[1].index,
2258                   (offs_t)currIndexSize*sizeof(offs_t));
2259         header->dirty = true;
2260         header->root[0].size = header->root[1].size;
2261         if (file->write(0, header, dbPageSize) != dbFile::ok) {
2262             pool.close();
2263             cleanupOnOpenError();
2264             handleError(DatabaseOpenError, "Failed to write to the file");
2265             return false;
2266         }
2267         pool.flush();
2268         header->initialized = true;
2269         if (file->write(0, header, dbPageSize) != dbFile::ok ||
2270             file->flush() != dbFile::ok)
2271         {
2272             pool.close();
2273             cleanupOnOpenError();
2274             handleError(DatabaseOpenError,
2275                         "Failed to complete file initialization");
2276             return false;
2277         }
2278     } else {
2279         if (!header->isCompatible()) {
2280             cleanupOnOpenError();
2281             handleError(DatabaseOpenError, "incompatible database mode");
2282             return false;
2283         }
2284         int curr = header->curr;
2285         this->curr = curr;
2286         if (header->root[curr].indexSize != header->root[curr].shadowIndexSize)
2287         {
2288             cleanupOnOpenError();
2289             handleError(DatabaseOpenError,
2290                         "Header of database file is corrupted");
2291             return false;
2292         }
2293 
2294         if (rc != dbFile::ok) {
2295             cleanupOnOpenError();
2296             handleError(DatabaseOpenError, "Failed to read object index");
2297             return false;
2298         }
2299         if (!pool.open(file, header->root[curr].size)) {
2300             cleanupOnOpenError();
2301             handleError(DatabaseOpenError, "Failed to allocate page pool");
2302             return false;
2303         }
2304         if (header->dirty) {
2305             TRACE_MSG((STRLITERAL("Database was not normally closed: start recovery\n")));
2306             if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
2307                 cleanupOnOpenError();
2308                 handleError(DatabaseOpenError,
2309                             "Can not open dirty file in read only mode");
2310                 return false;
2311             }
2312             header->root[1-curr].size = header->root[curr].size;
2313             header->root[1-curr].indexUsed = header->root[curr].indexUsed;
2314             header->root[1-curr].freeList = header->root[curr].freeList;
2315             header->root[1-curr].index = header->root[curr].shadowIndex;
2316             header->root[1-curr].indexSize =
2317                 header->root[curr].shadowIndexSize;
2318             header->root[1-curr].shadowIndex = header->root[curr].index;
2319             header->root[1-curr].shadowIndexSize =
2320                 header->root[curr].indexSize;
2321             header->root[1-curr].bitmapEnd = header->root[curr].bitmapEnd;
2322 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
2323             header->root[1-curr].sessionFreeList = header->root[curr].sessionFreeList;
2324 #endif
2325 
2326             pool.copy(header->root[1-curr].index, header->root[curr].index,
2327                       DOALIGN((offs_t)header->root[curr].indexUsed*sizeof(offs_t),
2328                               (offs_t)dbPageSize));
2329             restoreTablesConsistency();
2330             TRACE_MSG((STRLITERAL("Recovery completed\n")));
2331         }
2332     }
2333     if (!loadScheme()) {
2334         pool.close();
2335         cleanupOnOpenError();
2336         return false;
2337     }
2338     opened = true;
2339 
2340     if (transactionCommitDelay != 0) {
2341         dbCriticalSection cs(commitThreadSyncMutex);
2342         commitTimeout = commitDelay = transactionCommitDelay;
2343         commitThread.create((dbThread::thread_proc_t)delayedCommitProc, this);
2344         commitThreadSyncEvent.wait(commitThreadSyncMutex);
2345     }
2346 
2347     return true;
2348 }
2349 
scheduleBackup(char_t const * fileName,time_t period)2350 void dbDatabase::scheduleBackup(char_t const* fileName, time_t period)
2351 {
2352     if (backupFileName == NULL) {
2353         backupFileName = new char_t[STRLEN(fileName) + 1];
2354         STRCPY(backupFileName, fileName);
2355         backupPeriod = period;
2356         backupThread.create((dbThread::thread_proc_t)backupSchedulerProc, this);
2357     }
2358 }
2359 
backupScheduler()2360 void dbDatabase::backupScheduler()
2361 {
2362     backupThread.setPriority(dbThread::THR_PRI_LOW);
2363     attach();
2364     {
2365         dbCriticalSection cs(backupMutex);
2366         while (true) {
2367             if (!opened || backupFileName == NULL) {
2368                 break;
2369             }
2370             time_t timeout = backupPeriod;
2371             if (backupFileName[STRLEN(backupFileName)-1] != '?') {
2372 #ifdef _WINCE
2373                 WIN32_FIND_DATA lData;
2374                 HANDLE lFile = ::FindFirstFile(backupFileName, &lData);
2375                 FILETIME lATime;
2376                 if (::GetFileTime(lFile, 0l, &lATime, 0l) == TRUE)
2377                 {
2378                     ULARGE_INTEGER lNTime = *(ULARGE_INTEGER*)&lATime;
2379                     ULARGE_INTEGER sysTime;
2380                     SYSTEMTIME st;
2381                     GetSystemTime(&st);
2382                     SystemTimeToFileTime(&st, (LPFILETIME)&sysTime);
2383 
2384                     time_t howOld = sysTime.QuadPart - lNTime.QuadPart;
2385                     if (timeout < howOld) {
2386                         timeout = 0;
2387                     } else {
2388                         timeout -= howOld;
2389                     }
2390                 }
2391                 ::FindClose(lFile);
2392 #else
2393 #ifdef UNICODE
2394                 struct _stat st;
2395                 if (_wstat(backupFileName, &st) == 0)
2396 #else
2397 #if defined(_WIN32) && !defined(__MINGW32__) && !defined(__SYMBIAN32__)
2398                 struct _stat st;
2399                 if (_stat(backupFileName, &st) == 0)
2400 #else
2401                 struct stat st;
2402                 if (::stat(backupFileName, &st) == 0)
2403 #endif
2404 #endif
2405                 {
2406                     time_t howOld = time(NULL) - st.st_atime;
2407                     if (timeout < howOld) {
2408                         timeout = 0;
2409                     } else {
2410                         timeout -= howOld;
2411                     }
2412                 }
2413 #endif
2414             }
2415 
2416             backupInitEvent.wait(backupMutex, timeout);
2417 
2418             if (backupFileName != NULL) {
2419                 if (backupFileName[STRLEN(backupFileName)-1] == '?') {
2420                     char_t* fileName = new char_t[STRLEN(backupFileName) + 32];
2421 #ifdef _WINCE
2422                     SYSTEMTIME st;
2423                     GetSystemTime(&st);
2424                     SPRINTF(SPRINTF_BUFFER(fileName),
2425                             STRLITERAL("%.*s-%04d.%02d.%02d_%02d.%02d.%02d"),
2426                             (int)STRLEN(backupFileName)-1, backupFileName,
2427                             st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond);
2428 #else
2429                     time_t currTime = time(NULL);
2430                     struct tm* t = localtime(&currTime);
2431                     SPRINTF(SPRINTF_BUFFER(fileName),
2432                             STRLITERAL("%.*s-%04d.%02d.%02d_%02d.%02d.%02d"),
2433                             (int)STRLEN(backupFileName)-1, backupFileName,
2434                             t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
2435                             t->tm_hour, t->tm_min, t->tm_sec);
2436 #endif
2437                     backup(fileName, BCK_INCREMENTAL);
2438                     delete[] fileName;
2439                 } else {
2440                     char_t* newFileName = new char_t[STRLEN(backupFileName) + 5];
2441                     SPRINTF(SPRINTF_BUFFER(newFileName), STRLITERAL("%s.new"), backupFileName);
2442                     backup(newFileName, BCK_INCREMENTAL);
2443                     ::REMOVE_FILE(backupFileName);
2444                     ::RENAME_FILE(newFileName, backupFileName);
2445                     delete[] newFileName;
2446                 }
2447             } else {
2448                 break;
2449             }
2450         }
2451     }
2452     detach(DESTROY_CONTEXT);
2453 }
2454 
restoreTablesConsistency()2455 void dbDatabase::restoreTablesConsistency()
2456 {
2457     //
2458     // Restore consistency of table rows l2-list
2459     //
2460     dbTable* table = (dbTable*)get(dbMetaTableId);
2461     oid_t lastId = table->lastRow;
2462     oid_t tableId = table->firstRow;
2463     pool.unfix(table);
2464     if (lastId != 0) {
2465         dbRecord* record = (dbRecord*)get(lastId);
2466         if (record->next != 0) {
2467             pool.modify(record);
2468             record->next = 0;
2469         }
2470         pool.unfix(record);
2471     }
2472     while (tableId != 0) {
2473         table = (dbTable*)get(tableId);
2474         lastId = table->lastRow;
2475         tableId = table->next;
2476         pool.unfix(table);
2477         if (lastId != 0) {
2478             dbRecord* record = (dbRecord*)get(lastId);
2479             if (record->next != 0) {
2480                 pool.modify(record);
2481                 record->next = 0;
2482             }
2483             pool.unfix(record);
2484         }
2485     }
2486 }
2487 
setConcurrency(unsigned nThreads)2488 void dbDatabase::setConcurrency(unsigned nThreads)
2489 {
2490     if (nThreads == 0) { // autodetect number of processors
2491         nThreads = dbThread::numberOfProcessors();
2492     }
2493     if (nThreads > dbMaxParallelSearchThreads) {
2494         nThreads = dbMaxParallelSearchThreads;
2495     }
2496     parThreads = nThreads;
2497 }
2498 
2499 
loadScheme()2500 bool dbDatabase::loadScheme()
2501 {
2502     if (accessType != dbMulticlientReadOnly && accessType != dbMulticlientReadWrite) {
2503         beginTransaction(accessType != dbReadOnly ? dbUpdateLock : dbSharedLock);
2504     }
2505     dbGetTie tie;
2506     dbTable* metaTable = (dbTable*)get(dbMetaTableId);
2507     oid_t first = metaTable->firstRow;
2508     oid_t last = metaTable->lastRow;
2509     int nTables = metaTable->nRows;
2510     oid_t tableId = first;
2511     pool.unfix(metaTable);
2512 
2513     if (dbTableDescriptor::chain != NULL) {
2514         dbTableDescriptor *desc, *next;
2515         dbCriticalSection cs(dbTableDescriptor::getChainMutex());
2516         for (desc = dbTableDescriptor::chain; desc != NULL; desc = next) {
2517             next = desc->next;
2518             if (desc->db != NULL && desc->db != DETACHED_TABLE && desc->db != this) {
2519                 continue;
2520             }
2521             if (desc->db == DETACHED_TABLE) {
2522                 desc = desc->clone();
2523             }
2524             desc->db = this;
2525             dbFieldDescriptor* fd;
2526             for (fd = desc->firstField; fd != NULL; fd = fd->nextField) {
2527                 fd->bTree = 0;
2528                 fd->hashTable = 0;
2529                 fd->attr &= ~dbFieldDescriptor::Updated;
2530             }
2531             desc->nRows = 0;
2532             desc->firstRow = 0;
2533             desc->lastRow = 0;
2534 
2535             int n = nTables;
2536             while (--n >= 0) {
2537                 dbTable* table = (dbTable*)getRow(tie, tableId);
2538                 oid_t next = table->next;
2539                 if (STRCMP(desc->name, (char_t*)((byte*)table + table->name.offs)) == 0) {
2540                     if (!desc->equal(table, preserveExistedIndices)) {
2541                         beginTransaction(dbExclusiveLock);
2542                         modified = true;
2543                         if (table->nRows == 0) {
2544                             TRACE_MSG((STRLITERAL("Replace definition of table '%s'\n"), desc->name));
2545                             desc->match(table, true, preserveExistedIndices, true);
2546                             updateTableDescriptor(desc, tableId, table);
2547                         } else {
2548                             reformatTable(tableId, desc);
2549                         }
2550                     } else {
2551                         linkTable(desc, tableId);
2552                     }
2553                     desc->setFlags();
2554                     break;
2555                 }
2556                 if (tableId == last) {
2557                     tableId = first;
2558                 } else {
2559                     tableId = next;
2560                 }
2561             }
2562             if (n < 0) { // no match found
2563                 if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly ) {
2564                     handleError(DatabaseOpenError, "New table definition can not "
2565                                 "be added to read only database");
2566                     return false;
2567                 } else {
2568                     TRACE_MSG((STRLITERAL("Create new table '%s' in database\n"),
2569                                desc->name));
2570                     beginTransaction(dbExclusiveLock);
2571                     addNewTable(desc);
2572                     modified = true;
2573                 }
2574             }
2575             if (accessType != dbReadOnly && accessType != dbMulticlientReadOnly) {
2576                 addIndices(desc);
2577             }
2578         }
2579         for (desc = tables; desc != NULL; desc = desc->nextDbTable) {
2580             if (desc->cloneOf != NULL) {
2581                 for (dbFieldDescriptor *fd = desc->firstField; fd != NULL; fd = fd->nextField)
2582                 {
2583                     if (fd->refTable != NULL) {
2584                         fd->refTable = lookupTable(fd->refTable);
2585                     }
2586                 }
2587             }
2588             desc->checkRelationship();
2589         }
2590     }
2591     commit();
2592     return true;
2593 }
2594 
2595 
reformatTable(oid_t tableId,dbTableDescriptor * desc)2596 void dbDatabase::reformatTable(oid_t tableId, dbTableDescriptor* desc)
2597 {
2598     dbGetTie tie;
2599     dbTable* table = (dbTable*)getRow(tie, tableId);
2600 
2601     if (desc->match(table, confirmDeleteColumns, preserveExistedIndices, false)) {
2602         TRACE_MSG((STRLITERAL("New version of table '%s' is compatible with old one\n"),
2603                    desc->name));
2604         updateTableDescriptor(desc, tableId, table);
2605     } else {
2606         TRACE_MSG((STRLITERAL("Reformat table '%s'\n"), desc->name));
2607         oid_t oid = table->firstRow;
2608         updateTableDescriptor(desc, tableId, table);
2609         while (oid != 0) {
2610             dbGetTie getTie;
2611             dbPutTie putTie;
2612             byte* src = (byte*)getRow(getTie, oid);
2613             size_t size =
2614                 desc->columns->calculateNewRecordSize(src, desc->fixedSize);
2615             dbRecord* record = putRow(putTie, oid, size);
2616             byte* dst = (byte*)record;
2617             if (dst == src) {
2618                 dbSmallBuffer<char> buf(size);
2619                 dst = (byte*)buf.base();
2620                 desc->columns->convertRecord(dst, src, desc->fixedSize);
2621                 memcpy(record+1, dst+sizeof(dbRecord), size-sizeof(dbRecord));
2622             } else {
2623                 desc->columns->convertRecord(dst, src, desc->fixedSize);
2624             }
2625             oid = record->next;
2626         }
2627     }
2628 }
2629 
refreshTable(dbTableDescriptor * desc)2630 void dbDatabase::refreshTable(dbTableDescriptor* desc)
2631 {
2632     if (accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite) {
2633         dbCriticalSection cs(mutex);
2634         if (desc->transactionId != transactionId) {
2635             dbGetTie tie;
2636             dbTable* table = (dbTable*)getRow(tie, desc->tableId);
2637             desc->firstRow = table->firstRow;
2638             desc->lastRow = table->lastRow;
2639             desc->nRows = table->nRows;
2640             desc->transactionId = transactionId;
2641 
2642             dbField* field = (dbField*)((byte*)table + table->fields.offs);
2643             for (dbFieldDescriptor* fd = desc->firstField; fd != NULL; fd = fd->nextField) {
2644                 if (field[fd->fieldNo].bTree != fd->bTree) {
2645                     if (fd->bTree == 0) { //add index
2646                         fd->attr &= ~dbFieldDescriptor::Updated;
2647                         fd->nextIndexedField = fd->defTable->indexedFields;
2648                         fd->defTable->indexedFields = fd;
2649                         fd->indexType |= INDEXED;
2650                     } else { // drop index
2651                         fd->bTree = 0;
2652                         fd->indexType &= ~INDEXED;
2653 
2654                         dbFieldDescriptor** fpp = &fd->defTable->indexedFields;
2655                         while (*fpp != fd) {
2656                             fpp = &(*fpp)->nextIndexedField;
2657                         }
2658                         *fpp = fd->nextIndexedField;
2659                     }
2660                 }
2661             }
2662         }
2663     }
2664 }
2665 
deleteTable(dbTableDescriptor * desc)2666 void dbDatabase::deleteTable(dbTableDescriptor* desc)
2667 {
2668     beginTransaction(dbExclusiveLock);
2669     refreshTable(desc);
2670     modified = true;
2671     dbPutTie tie;
2672     dbTable* table = (dbTable*)putRow(tie, desc->tableId);
2673     oid_t rowId = desc->firstRow;
2674     assert(desc->firstRow == table->firstRow
2675            && desc->lastRow == table->lastRow);
2676     desc->firstRow = desc->lastRow = table->firstRow = table->lastRow = 0;
2677     desc->nRows = table->nRows = 0;
2678 
2679     while (rowId != 0) {
2680         dbRecord rec;
2681         getHeader(rec, rowId);
2682 
2683         removeInverseReferences(desc, rowId);
2684         offs_t pos = getPos(rowId);
2685         if (pos & dbModifiedFlag) {
2686             free(pos & ~dbFlagsMask, rec.size);
2687         } else {
2688             cloneBitmap(pos, rec.size);
2689         }
2690         freeId(rowId);
2691         rowId = rec.next;
2692     }
2693     dbFieldDescriptor* fd;
2694     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
2695         dbHashTable::purge(this, fd->hashTable);
2696     }
2697     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
2698         if (fd->type == dbField::tpRectangle) {
2699             dbRtree::purge(this, fd->bTree);
2700         } else {
2701             dbBtree::purge(this, fd->bTree);
2702         }
2703     }
2704 }
2705 
dropHashTable(dbFieldDescriptor * fd)2706 void dbDatabase::dropHashTable(dbFieldDescriptor* fd)
2707 {
2708     beginTransaction(dbExclusiveLock);
2709     modified = true;
2710     dbHashTable::drop(this, fd->hashTable);
2711     fd->hashTable = 0;
2712     fd->indexType &= ~HASHED;
2713 
2714     dbFieldDescriptor** fpp = &fd->defTable->hashedFields;
2715     while (*fpp != fd) {
2716         fpp = &(*fpp)->nextHashedField;
2717     }
2718     *fpp = fd->nextHashedField;
2719 
2720     dbPutTie tie;
2721     dbTable* table = (dbTable*)putRow(tie, fd->defTable->tableId);
2722     dbField* field = (dbField*)((byte*)table + table->fields.offs);
2723     field[fd->fieldNo].hashTable = 0;
2724 }
2725 
dropIndex(dbFieldDescriptor * fd)2726 void dbDatabase::dropIndex(dbFieldDescriptor* fd)
2727 {
2728     beginTransaction(dbExclusiveLock);
2729     modified = true;
2730     if (fd->type == dbField::tpRectangle) {
2731         dbRtree::drop(this, fd->bTree);
2732     } else {
2733         dbBtree::drop(this, fd->bTree);
2734     }
2735     fd->bTree = 0;
2736     fd->indexType &= ~INDEXED;
2737 
2738     dbFieldDescriptor** fpp = &fd->defTable->indexedFields;
2739     while (*fpp != fd) {
2740         fpp = &(*fpp)->nextIndexedField;
2741     }
2742     *fpp = fd->nextIndexedField;
2743 
2744     dbPutTie tie;
2745     dbTable* table = (dbTable*)putRow(tie, fd->defTable->tableId);
2746     dbField* field = (dbField*)((byte*)table + table->fields.offs);
2747     field[fd->fieldNo].bTree = 0;
2748 }
2749 
createHashTable(dbFieldDescriptor * fd)2750 void dbDatabase::createHashTable(dbFieldDescriptor* fd)
2751 {
2752     beginTransaction(dbExclusiveLock);
2753     modified = true;
2754     dbPutTie tie;
2755     dbTable* table = (dbTable*)putRow(tie, fd->defTable->tableId);
2756     int nRows = table->nRows;
2757     fd->hashTable = dbHashTable::allocate(this, 2*nRows);
2758     fd->attr &= ~dbFieldDescriptor::Updated;
2759     fd->nextHashedField = fd->defTable->hashedFields;
2760     fd->defTable->hashedFields = fd;
2761     fd->indexType |= HASHED;
2762     dbField* field = (dbField*)((byte*)table + table->fields.offs);
2763     field[fd->fieldNo].hashTable = fd->hashTable;
2764 
2765     oid_t oid = table->firstRow;
2766     while (oid != 0) {
2767         dbRecord rec;
2768         dbHashTable::insert(this, fd->hashTable, oid, fd->type, fd->dbsOffs,
2769                             nRows);
2770         getHeader(rec, oid);
2771         oid = rec.next;
2772     }
2773 }
2774 
2775 
createIndex(dbFieldDescriptor * fd)2776 void dbDatabase::createIndex(dbFieldDescriptor* fd)
2777 {
2778     beginTransaction(dbExclusiveLock);
2779     modified = true;
2780     dbPutTie tie;
2781     dbTable* table = (dbTable*)putRow(tie, fd->defTable->tableId);
2782     if (fd->type == dbField::tpRectangle) {
2783         fd->bTree = dbRtree::allocate(this);
2784     } else {
2785         int flags = 0;
2786         if (fd->indexType & CASE_INSENSITIVE) {
2787             flags |= dbBtree::FLAGS_CASE_INSENSITIVE;
2788         }
2789         if (fd->indexType & OPTIMIZE_DUPLICATES) {
2790             flags |= dbBtree::FLAGS_THICK;
2791         }
2792         if (fd->indexType & UNIQUE) {
2793             flags |= dbBtree::FLAGS_UNIQUE;
2794         }
2795         fd->bTree = dbBtree::allocate(this, fd->type, (int)fd->dbsSize, flags);
2796     }
2797     fd->attr &= ~dbFieldDescriptor::Updated;
2798     fd->nextIndexedField = fd->defTable->indexedFields;
2799     fd->defTable->indexedFields = fd;
2800     fd->indexType |= INDEXED;
2801     dbField* field = (dbField*)((byte*)table + table->fields.offs);
2802     field[fd->fieldNo].bTree = fd->bTree;
2803 
2804     oid_t oid = table->firstRow;
2805     while (oid != 0) {
2806         dbRecord rec;
2807         if (fd->type == dbField::tpRectangle) {
2808             dbRtree::insert(this, fd->bTree, oid, fd->dbsOffs);
2809         } else {
2810             if (!dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator)) {
2811                 handleError(UniqueConstraintViolation);
2812             }
2813         }
2814         getHeader(rec, oid);
2815         oid = rec.next;
2816     }
2817 }
2818 
dropTable(dbTableDescriptor * desc)2819 void dbDatabase::dropTable(dbTableDescriptor* desc)
2820 {
2821     deleteTable(desc);
2822     freeRow(dbMetaTableId, desc->tableId);
2823 
2824     dbFieldDescriptor* fd;
2825     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
2826         dbHashTable::drop(this, fd->hashTable);
2827     }
2828     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
2829         if (fd->type == dbField::tpRectangle) {
2830             dbRtree::drop(this, fd->bTree);
2831         } else {
2832             dbBtree::drop(this, fd->bTree);
2833         }
2834     }
2835 }
2836 
2837 #define NEW_INDEX 0x80000000u
2838 
2839 
addIndices(dbTableDescriptor * desc)2840 void dbDatabase::addIndices(dbTableDescriptor* desc)
2841 {
2842     dbFieldDescriptor* fd;
2843     oid_t tableId = desc->tableId;
2844     size_t nRows = desc->nRows;
2845     oid_t firstId = desc->firstRow;
2846     int nNewIndices = 0;
2847     int nDelIndices = 0;
2848     for (fd = desc->firstField; fd != NULL; fd = fd->nextField) {
2849         if ((fd->indexType & HASHED) && fd->type != dbField::tpStructure) {
2850             if (fd->hashTable == 0) {
2851                 beginTransaction(dbExclusiveLock);
2852                 fd->indexType |= NEW_INDEX;
2853                 fd->hashTable = dbHashTable::allocate(this, nRows);
2854                 nNewIndices += 1;
2855                 TRACE_MSG((STRLITERAL("Create hash table for field '%s'\n"), fd->name));
2856             }
2857         } else if (fd->hashTable != 0) {
2858             TRACE_MSG((STRLITERAL("Remove hash table for field '%s'\n"), fd->name));
2859             beginTransaction(dbExclusiveLock);
2860             nDelIndices += 1;
2861             fd->hashTable = 0;
2862         }
2863         if ((fd->indexType & INDEXED) && fd->type != dbField::tpStructure) {
2864             if (fd->bTree == 0) {
2865                 beginTransaction(dbExclusiveLock);
2866                 fd->indexType |= NEW_INDEX;
2867                 if (fd->type == dbField::tpRectangle) {
2868                     fd->bTree = dbRtree::allocate(this);
2869                 } else {
2870                     int flags = 0;
2871                     if (fd->indexType & CASE_INSENSITIVE) {
2872                         flags |= dbBtree::FLAGS_CASE_INSENSITIVE;
2873                     }
2874                     if (fd->indexType & OPTIMIZE_DUPLICATES) {
2875                         flags |= dbBtree::FLAGS_THICK;
2876                     }
2877                     if (fd->indexType & UNIQUE) {
2878                         flags |= dbBtree::FLAGS_UNIQUE;
2879                     }
2880                     fd->bTree = dbBtree::allocate(this, fd->type, (int)fd->dbsSize, flags);
2881                 }
2882                 nNewIndices += 1;
2883                 TRACE_MSG((STRLITERAL("Create index for field '%s'\n"), fd->name));
2884             }
2885         } else if (fd->bTree != 0) {
2886             nDelIndices += 1;
2887             beginTransaction(dbExclusiveLock);
2888             TRACE_MSG((STRLITERAL("Remove index for field '%s'\n"), fd->name));
2889             fd->bTree = 0;
2890         }
2891     }
2892     if (nNewIndices > 0) {
2893         dbRecord rec;
2894         modified = true;
2895         for (oid_t rowId = firstId; rowId != 0; rowId = rec.next) {
2896             for (fd = desc->hashedFields; fd != NULL; fd=fd->nextHashedField) {
2897                 if (fd->indexType & NEW_INDEX) {
2898                     dbHashTable::insert(this, fd->hashTable, rowId,
2899                                         fd->type, fd->dbsOffs, 2*nRows);
2900                 }
2901             }
2902             for (fd=desc->indexedFields; fd != NULL; fd=fd->nextIndexedField) {
2903                 if (fd->indexType & NEW_INDEX) {
2904                     if (fd->type == dbField::tpRectangle) {
2905                         dbRtree::insert(this, fd->bTree, rowId, fd->dbsOffs);
2906                     } else {
2907                         if (!dbBtree::insert(this, fd->bTree, rowId, fd->dbsOffs, fd->comparator)) {
2908                             handleError(UniqueConstraintViolation);
2909                         }
2910                     }
2911                 }
2912             }
2913             getHeader(rec, rowId);
2914         }
2915         for (fd = desc->firstField; fd != NULL; fd = fd->nextField) {
2916             fd->indexType &= ~NEW_INDEX;
2917         }
2918     }
2919     if (nNewIndices + nDelIndices != 0) {
2920         dbPutTie tie;
2921         dbTable* table = (dbTable*)putRow(tie, tableId);
2922         dbField* field = (dbField*)((byte*)table + table->fields.offs);
2923         for (fd = desc->firstField; fd != NULL; fd = fd->nextField) {
2924             if (field->hashTable != fd->hashTable) {
2925                 if (field->hashTable != 0) {
2926                     assert(fd->hashTable == 0);
2927                     modified = true;
2928                     dbHashTable::drop(this, field->hashTable);
2929                 }
2930                 field->hashTable = fd->hashTable;
2931             }
2932             if (field->bTree != fd->bTree) {
2933                 if (field->bTree != 0) {
2934                     assert(fd->bTree == 0);
2935                     modified = true;
2936                     if (field->type == dbField::tpRectangle) {
2937                         dbRtree::drop(this, field->bTree);
2938                     } else {
2939                         dbBtree::drop(this, field->bTree);
2940                     }
2941                 }
2942                 field->bTree = fd->bTree;
2943             }
2944             field += 1;
2945         }
2946     }
2947 }
2948 
2949 
updateTableDescriptor(dbTableDescriptor * desc,oid_t tableId,dbTable * table)2950 void dbDatabase::updateTableDescriptor(dbTableDescriptor* desc,
2951                                        oid_t tableId, dbTable* table)
2952 {
2953     dbFieldDescriptor* fd;
2954     size_t newSize = sizeof(dbTable) + desc->nFields*sizeof(dbField)
2955         + desc->totalNamesLength()*sizeof(char_t);
2956     linkTable(desc, tableId);
2957 
2958     int nFields = table->fields.size;
2959 #ifdef AUTOINCREMENT_SUPPORT
2960     desc->autoincrementCount = table->count;
2961 #endif
2962 
2963     dbField* field = (dbField*)((byte*)table + table->fields.offs);
2964 
2965     while (--nFields >= 0) {
2966         oid_t hashTableId = field->hashTable;
2967         oid_t bTreeId = field->bTree;
2968         if (hashTableId != 0) {
2969             for (fd = desc->hashedFields;
2970                  fd != NULL && fd->hashTable != hashTableId;
2971                  fd = fd->nextHashedField);
2972             if (fd == NULL && !preserveExistedIndices) {
2973                 dbHashTable::drop(this, hashTableId);
2974             }
2975         }
2976         if (bTreeId != 0) {
2977             for (fd = desc->indexedFields;
2978                  fd != NULL && fd->bTree != bTreeId;
2979                  fd = fd->nextIndexedField);
2980             if (fd == NULL && !preserveExistedIndices) {
2981                 if (field->type == dbField::tpRectangle) {
2982                     dbRtree::drop(this, field->bTree);
2983                 } else {
2984                     dbBtree::drop(this, bTreeId);
2985                 }
2986             }
2987         }
2988         field += 1;
2989     }
2990 
2991     dbPutTie tie;
2992     desc->storeInDatabase((dbTable*)putRow(tie, tableId, newSize));
2993 }
2994 
addNewTable(dbTableDescriptor * desc)2995 oid_t dbDatabase::addNewTable(dbTableDescriptor* desc)
2996 {
2997     oid_t tableId = allocateRow(dbMetaTableId,
2998                                 sizeof(dbTable) + desc->nFields*sizeof(dbField)
2999                                 + desc->totalNamesLength()*sizeof(char_t));
3000     linkTable(desc, tableId);
3001     dbPutTie tie;
3002     desc->storeInDatabase((dbTable*)putRow(tie, tableId));
3003     return tableId;
3004 }
3005 
3006 
3007 
close()3008 void dbDatabase::close()
3009 {
3010 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
3011     if (!doNotReuseOidAfterClose && header->root[1-curr].sessionFreeList.head != 0) {
3012         beginTransaction(dbExclusiveLock);
3013         modified = true;
3014         oid_t tail = header->root[1-curr].sessionFreeList.tail;
3015         dirtyPagesMap[size_t(tail / dbHandlesPerPage / 32)] |= 1 << (int(tail / dbHandlesPerPage) & 31);
3016         setPos(tail, (offs_t(header->root[1-curr].freeList) << dbFlagsBits) | dbFreeHandleFlag);
3017         header->root[1-curr].freeList = header->root[1-curr].sessionFreeList.head;
3018         header->root[1-curr].sessionFreeList.head = 0;
3019         header->root[1-curr].sessionFreeList.tail = 0;
3020     }
3021 #endif
3022     detach();
3023     if (backupFileName != NULL) {
3024         {
3025             dbCriticalSection cs(backupMutex);
3026             delete[] backupFileName;
3027             backupFileName = NULL;
3028             backupInitEvent.pulse();
3029         }
3030         backupThread.join();
3031     }
3032     if (commitDelay != 0) {
3033         {
3034             dbCriticalSection cs(delayedCommitStopTimerMutex);
3035             forceCommitCount += 1;
3036             delayedCommitStopTimerEvent.pulse();
3037         }
3038         {
3039             dbCriticalSection cs(delayedCommitStartTimerMutex);
3040             delayedCommitContext = NULL;
3041             delayedCommitStartTimerEvent.pulse();
3042         }
3043         commitDelay = 0;
3044         commitThread.join();
3045     }
3046     {
3047         dbCriticalSection cs(threadContextListMutex);
3048         while (!threadContextList.isEmpty()) {
3049             delete (dbDatabaseThreadContext*)threadContextList.next;
3050         }
3051     }
3052     commitThreadSyncEvent.close();
3053     delayedCommitStartTimerEvent.close();
3054     delayedCommitStopTimerEvent.close();
3055     backupInitEvent.close();
3056 
3057     opened = false;
3058     if (header->dirty) {
3059         int rc = file->write(0, header, dbPageSize);
3060         if (rc != dbFile::ok) {
3061             handleError(FileError, "Failed to write header to the disk", rc);
3062         }
3063         pool.flush();
3064         header->dirty = false;
3065         rc = file->write(0, header, dbPageSize);
3066         if (rc != dbFile::ok) {
3067             handleError(FileError, "Failed to write header to the disk", rc);
3068         }
3069         replicatePage(0, header);
3070     }
3071     dbTableDescriptor *desc, *next;
3072     for (desc = tables; desc != NULL; desc = next) {
3073         next = desc->nextDbTable;
3074         desc->tableId = 0;
3075         if (!desc->isStatic) {
3076             delete desc;
3077         } else if (!desc->fixedDatabase) {
3078             desc->db = NULL;
3079         }
3080     }
3081     pool.close();
3082     releaseFile();
3083     readSem.close();
3084     writeSem.close();
3085     upgradeSem.close();
3086     backupCompletedEvent.close();
3087 }
3088 
3089 
getVersion()3090 int  dbDatabase::getVersion()
3091 {
3092     return header->getVersion();
3093 }
3094 
3095 
attach()3096 void dbDatabase::attach()
3097 {
3098     if (threadContext.get() == NULL)
3099     {
3100         dbDatabaseThreadContext* ctx = new dbDatabaseThreadContext();
3101         {
3102             dbCriticalSection cs(threadContextListMutex);
3103             threadContextList.link(ctx);
3104         }
3105         threadContext.set(ctx);
3106     }
3107 }
3108 
attach(dbDatabaseThreadContext * ctx)3109 void dbDatabase::attach(dbDatabaseThreadContext* ctx)
3110 {
3111     threadContext.set(ctx);
3112 }
3113 
detach(int flags)3114 void dbDatabase::detach(int flags)
3115 {
3116     if (flags & COMMIT) {
3117         commit();
3118     } else {
3119         uncommittedChanges = true;
3120         precommit();
3121     }
3122     if (flags & DESTROY_CONTEXT) {
3123         dbDatabaseThreadContext* ctx = threadContext.get();
3124         if (commitDelay != 0) {
3125             dbCriticalSection cs(delayedCommitStopTimerMutex);
3126             if (delayedCommitContext == ctx) {
3127                 ctx->removeContext = true;
3128             } else {
3129                 dbCriticalSection cs(threadContextListMutex);
3130                 delete ctx;
3131             }
3132         } else {
3133             dbCriticalSection cs(threadContextListMutex);
3134             delete ctx;
3135 
3136         }
3137         threadContext.set(NULL);
3138     }
3139 }
3140 
existsInverseReference(dbExprNode * expr,int nExistsClauses)3141 bool dbDatabase::existsInverseReference(dbExprNode* expr, int nExistsClauses)
3142 {
3143     while (true) {
3144         switch (expr->cop) {
3145           case dbvmLoadSelfReference:
3146           case dbvmLoadSelfArray:
3147             return expr->ref.field->inverseRef != NULL;
3148           case dbvmLoadReference:
3149             if (expr->ref.field->attr & dbFieldDescriptor::ComponentOfArray) {
3150                 expr = expr->ref.base;
3151                 continue;
3152             }
3153             if (expr->ref.field->inverseRef == NULL && expr->ref.field->bTree == 0) {
3154                 return false;
3155             }
3156             expr = expr->ref.base;
3157             continue;
3158           case dbvmLoadArray:
3159             if (expr->ref.field->inverseRef == NULL) {
3160                 return false;
3161             }
3162             expr = expr->ref.base;
3163             continue;
3164           case dbvmGetAt:
3165             if (expr->operand[1]->cop != dbvmVariable
3166                 || expr->operand[1]->offs != --nExistsClauses)
3167             {
3168                 return false;
3169             }
3170             expr = expr->operand[0];
3171             continue;
3172           case dbvmDeref:
3173             expr = expr->operand[0];
3174             continue;
3175           default:
3176             return false;
3177         }
3178     }
3179 }
3180 
followInverseReference(dbExprNode * expr,dbExprNode * andExpr,dbAnyCursor * cursor,oid_t iref)3181 bool dbDatabase::followInverseReference(dbExprNode* expr, dbExprNode* andExpr,
3182                                         dbAnyCursor* cursor, oid_t iref)
3183 {
3184     dbGetTie tie;
3185     while (expr->cop == dbvmGetAt || expr->cop == dbvmDeref ||
3186            (expr->cop == dbvmLoadReference
3187             && (expr->ref.field->attr & dbFieldDescriptor::ComponentOfArray)))
3188     {
3189         expr = expr->operand[0];
3190     }
3191     dbTableDescriptor* table = cursor->table;
3192     dbFieldDescriptor* fd = expr->ref.field->inverseRef;
3193     if (fd == NULL) {
3194         dbAnyCursor tmpCursor(*expr->ref.field->defTable, dbCursorViewOnly, NULL);
3195         dbSearchContext sc;
3196         sc.ascent = true;
3197         sc.offs = expr->ref.field->dbsOffs;
3198         sc.cursor = &tmpCursor;
3199         sc.firstKey = sc.lastKey = (char_t*)&iref;
3200         sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3201         sc.tmpKeys = false;
3202         sc.prefixLength = 0;
3203         sc.spatialSearch = false;
3204         sc.arraySearch = false;
3205         sc.condition = NULL;
3206         applyIndex(expr->ref.field, sc);
3207         dbSelection::segment *first = &tmpCursor.selection.first, *curr = first;
3208         do {
3209             for (int i = 0, n = (int)curr->nRows; i < n; i++) {
3210                 oid_t oid = curr->rows[i];
3211                 if (!followInverseReference(expr->ref.base, andExpr, cursor, oid))
3212                 {
3213                     return false;
3214                 }
3215 
3216             }
3217         } while ((curr = curr->next) != first);
3218         return true;
3219     }
3220     if (fd->type == dbField::tpArray) {
3221         byte* rec = (byte*)getRow(tie, iref);
3222         dbVarying* arr = (dbVarying*)(rec + fd->dbsOffs);
3223         oid_t* refs = (oid_t*)(rec + arr->offs);
3224         if (expr->cop >= dbvmLoadSelfReference) {
3225             for (int n = arr->size; --n >= 0;) {
3226                 oid_t oid = *refs++;
3227                 if (oid != 0) {
3228                     if (andExpr == NULL || evaluateBoolean(andExpr, oid, table, cursor)) {
3229                         if (!cursor->add(oid)) {
3230                             return false;
3231                         }
3232                     }
3233                 }
3234             }
3235         } else {
3236             for (int n = arr->size; --n >= 0;) {
3237                 oid_t oid = *refs++;
3238                 if (oid != 0) {
3239                     if (!followInverseReference(expr->ref.base, andExpr,
3240                                                 cursor, oid))
3241                     {
3242                         return false;
3243                     }
3244                 }
3245             }
3246         }
3247     } else {
3248         assert(fd->type == dbField::tpReference);
3249         oid_t oid = *(oid_t*)((byte*)getRow(tie, iref) + fd->dbsOffs);
3250         if (oid != 0) {
3251             if (expr->cop >= dbvmLoadSelfReference) {
3252                 if (andExpr == NULL || evaluateBoolean(andExpr, oid, table, cursor)) {
3253                     if (!cursor->add(oid)) {
3254                         return false;
3255                     }
3256                 }
3257             } else {
3258                 if (!followInverseReference(expr->ref.base, andExpr,
3259                                             cursor, oid))
3260                 {
3261                     return false;
3262                 }
3263             }
3264         }
3265     }
3266     return true;
3267 }
3268 
3269 
applyIndex(dbFieldDescriptor * field,dbSearchContext & sc)3270 void dbDatabase::applyIndex(dbFieldDescriptor* field, dbSearchContext& sc)
3271 {
3272     sc.probes = 0;
3273     if (sc.arraySearch) {
3274         dbAnyArray* arr = sc.literal[0].a;
3275         switch (field->type) {
3276           case dbField::tpInt4:
3277           {
3278               db_int4* items = (db_int4*)arr->base();
3279               sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3280               sc.cursor->checkForDuplicates();
3281               for (int n = (int)arr->length(); --n >= 0; items++) {
3282                   sc.firstKey = sc.lastKey = (char_t*)items;
3283                   dbBtree::find(this, field->bTree, sc, field->comparator);
3284               }
3285               break;
3286           }
3287           case dbField::tpInt8:
3288           {
3289               db_int8* items = (db_int8*)arr->base();
3290               sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3291               sc.cursor->checkForDuplicates();
3292               for (int n = (int)arr->length(); --n >= 0; items++) {
3293                   sc.firstKey = sc.lastKey = (char_t*)items;
3294                   dbBtree::find(this, field->bTree, sc, field->comparator);
3295               }
3296               break;
3297           }
3298           case dbField::tpReference:
3299           {
3300               oid_t* items = (oid_t*)arr->base();
3301               sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3302               sc.cursor->checkForDuplicates();
3303               for (int n = (int)arr->length(); --n >= 0; items++) {
3304                   sc.firstKey = sc.lastKey = (char_t*)items;
3305                   dbBtree::find(this, field->bTree, sc, field->comparator);
3306               }
3307           }
3308         }
3309     } else {
3310         if (sc.spatialSearch) {
3311             dbRtree::find(this, field->bTree, sc);
3312         } else {
3313             dbBtree::find(this, field->bTree, sc, field->comparator);
3314         }
3315         if (sc.tmpKeys) {
3316             delete[] sc.firstKey;
3317             delete[] sc.lastKey;
3318         }
3319     }
3320     TRACE_MSG((STRLITERAL("Index search for field %s.%s: %d probes\n"),
3321                field->defTable->name, field->longName, sc.probes));
3322 }
3323 
existsIndexedReference(dbExprNode * ref)3324 bool dbDatabase::existsIndexedReference(dbExprNode* ref)
3325 {
3326     while (ref->cop == dbvmDeref) {
3327         ref = ref->operand[0];
3328         if ((ref->cop != dbvmLoadSelfReference && ref->cop != dbvmLoadReference) || ref->ref.field->bTree == 0) {
3329             return false;
3330         }
3331         if (ref->cop == dbvmLoadSelfReference) {
3332             return true;
3333         }
3334         ref = ref->operand[0];
3335     }
3336     return false;
3337 }
3338 
isIndicesApplicable(dbAnyCursor * cursor,dbExprNode * expr,bool & unique)3339 bool dbDatabase::isIndicesApplicable(dbAnyCursor* cursor, dbExprNode* expr, bool& unique)
3340 {
3341     int nExistsClauses = 0;
3342     bool leftUnique, rightUnique;
3343     if (expr->cop == dbvmAndBool || expr->cop == dbvmOrBool) {
3344         if (isIndicesApplicable(cursor, expr->operand[0], leftUnique) && isIndicesApplicable(cursor, expr->operand[1], rightUnique)) {
3345             unique = expr->cop == dbvmAndBool ? (leftUnique | rightUnique) : (leftUnique & rightUnique);
3346             return true;
3347         }
3348     }
3349     while (expr->cop == dbvmExists) {
3350         expr = expr->operand[0];
3351         nExistsClauses += 1;
3352     }
3353     if (dbExprNode::nodeOperands[expr->cop] < 2 && expr->cop != dbvmIsNull) {
3354         return false;
3355     }
3356     dbExprNode* loadExpr = expr->operand[0];
3357     bool ignoreCase = false;
3358     unsigned loadCop = loadExpr->cop;
3359     if (loadCop == dbvmLowerString ||loadCop == dbvmUpperString) {
3360         ignoreCase = true;
3361         loadExpr = loadExpr->operand[0];
3362         loadCop = loadExpr->cop;
3363     }
3364     if (loadCop - dbvmLoadSelfBool > dbvmLoadSelfRawBinary - dbvmLoadSelfBool
3365         && loadCop - dbvmLoadBool > dbvmLoadRawBinary - dbvmLoadBool)
3366     {
3367         return false;
3368     }
3369     dbFieldDescriptor* field = loadExpr->ref.field;
3370     if (field->bTree == 0) {
3371         return false;
3372     }
3373     if (ignoreCase && !(field->indexType & CASE_INSENSITIVE)) {
3374         return false;
3375     }
3376     unique = (field->indexType & UNIQUE) != 0;
3377     dbSearchContext sc;
3378     sc.cursor = cursor;
3379     sc.ascent = true;
3380     return isIndexApplicableToExpr(sc, expr)
3381         && (loadCop >= dbvmLoadSelfBool
3382             || existsInverseReference(loadExpr->ref.base, nExistsClauses)
3383             || existsIndexedReference(loadExpr->ref.base));
3384 }
3385 
3386 //
3387 // Try to execute query using indices.
3388 // This recursive functions tries to split execution of request in one or more
3389 // index search operations.
3390 // @param cursor result set
3391 // @param expr   selection criteria
3392 // @param query  executed query
3393 // @param indexedField (IN/OUT) indexed field used to perform index search
3394 // @param truncate (IN/OUT) flag used to indicate whether it is possible to stop search
3395 //        before testing all records (when query limit was specified)
3396 // @return true, if query was evaluated using indices and cursor contains valid selection, false otherwise
3397 //
applyIndices(dbAnyCursor * cursor,dbExprNode * expr,dbExprNode * topExpr,dbExprNode * filter,dbQuery & query,dbFieldDescriptor * & indexedField,bool & truncate,bool ascent)3398 bool dbDatabase::applyIndices(dbAnyCursor* cursor, dbExprNode* expr, dbExprNode* topExpr, dbExprNode* filter,
3399                               dbQuery& query, dbFieldDescriptor* &indexedField,
3400                               bool& truncate, bool ascent)
3401 {
3402     int nExistsClauses = 0;
3403     dbSearchContext sc;
3404     bool unique;
3405 
3406     if (expr->cop == dbvmAndBool) {
3407         if (filter == NULL) {
3408             if (!cursor->hasIncrementalHint()
3409                 && isIndicesApplicable(cursor, expr->operand[0], unique)
3410                 && !unique
3411                 && isIndicesApplicable(cursor, expr->operand[1], unique))
3412             {
3413                 size_t saveLimit = cursor->limit;
3414                 cursor->limit = dbDefaultSelectionLimit;
3415                 truncate = false;
3416                 bool succeed = applyIndices(cursor, expr->operand[0], expr, NULL, query, indexedField, truncate, true);
3417                 cursor->limit = saveLimit;
3418                 if (!succeed) {
3419                     return false;
3420                 }
3421                 if (cursor->selection.nRows != 0) {
3422                     dbAnyCursor tmpCursor(*cursor->table, dbCursorViewOnly, NULL);
3423                     tmpCursor.paramBase = cursor->paramBase;
3424                     if (!applyIndices(&tmpCursor, expr->operand[1], expr, NULL, query, indexedField, truncate, true)) {
3425                         return false;
3426                     }
3427                     TRACE_MSG((STRLITERAL("Indexed join\n")));
3428                     cursor->selection.merge(this, tmpCursor.selection);
3429 				}
3430                 indexedField = NULL;
3431                 return true;
3432             }
3433             return applyIndices(cursor, expr->operand[0], expr, expr->operand[1],
3434                                 query, indexedField, truncate, ascent)
3435                 || applyIndices(cursor, expr->operand[1], expr, expr->operand[0],
3436                                 query, indexedField, truncate, ascent);
3437         } else {
3438             return applyIndices(cursor, expr->operand[0], topExpr, topExpr,
3439                                 query, indexedField, truncate, ascent)
3440                 || applyIndices(cursor, expr->operand[1], topExpr, topExpr,
3441                                 query, indexedField, truncate, ascent);
3442         }
3443     } else if (expr->cop == dbvmOrBool) {
3444         if (/*filter == NULL && */
3445             !cursor->hasIncrementalHint()
3446             && isIndicesApplicable(cursor, expr->operand[0], unique)
3447             && isIndicesApplicable(cursor, expr->operand[1], unique))
3448         {
3449             cursor->checkForDuplicates();
3450             if (applyIndices(cursor, expr->operand[0], topExpr, filter, query, indexedField, truncate, true)
3451                 && (cursor->isLimitReached()
3452                     || applyIndices(cursor, expr->operand[1], topExpr, filter, query, indexedField, truncate, true)))
3453             {
3454                 indexedField = NULL;
3455                 return true;
3456             }
3457         }
3458         return false;
3459     }
3460     while (expr->cop == dbvmExists) {
3461         expr = expr->operand[0];
3462         nExistsClauses += 1;
3463     }
3464     if (dbExprNode::nodeOperands[expr->cop] < 2 && expr->cop != dbvmIsNull) {
3465         return false;
3466     }
3467     dbExprNode* loadExpr = expr->operand[0];
3468     unsigned loadCop = loadExpr->cop;
3469     bool ignoreCase = false;
3470     if (loadCop == dbvmLowerString ||loadCop == dbvmUpperString) {
3471         ignoreCase = true;
3472         loadExpr = loadExpr->operand[0];
3473         loadCop = loadExpr->cop;
3474     }
3475     if (loadCop - dbvmLoadSelfBool > dbvmLoadSelfRawBinary - dbvmLoadSelfBool
3476         && loadCop - dbvmLoadBool > dbvmLoadRawBinary - dbvmLoadBool)
3477     {
3478         return false;
3479     }
3480     dbFieldDescriptor* field = loadExpr->ref.field;
3481     if (field->bTree == 0) {
3482         return false;
3483     }
3484     if (ignoreCase && !(field->indexType & CASE_INSENSITIVE)) {
3485         return false;
3486     }
3487     sc.cursor = cursor;
3488     sc.ascent = true;
3489     if (loadCop >= dbvmLoadSelfBool) {
3490         if (isIndexApplicableToExpr(sc, expr)) {
3491             sc.condition = filter;
3492             sc.ascent = ascent;
3493             if (truncate) {
3494                 if (indexedField == NULL || indexedField == field) {
3495                     cursor->setStatementLimit(query);
3496                 } else {
3497                     truncate = false;
3498                 }
3499             } else if (cursor->hasIncrementalHint() && !sc.arraySearch
3500                        && (indexedField == NULL || indexedField == field))
3501             {
3502                 if (sc.spatialSearch) {
3503                     cursor->iterator = &cursor->rtreeIterator;
3504                     cursor->rtreeIterator.init(this, field->bTree, sc);
3505                 } else {
3506                     cursor->iterator = &cursor->btreeIterator;
3507                     cursor->btreeIterator.init(this, field->bTree, sc, field->comparator);
3508                 }
3509                 indexedField = field;
3510                 return true;
3511             }
3512             if (sc.prefixLength != 0) {
3513                 sc.ascent = true;
3514                 indexedField = ascent ? field : NULL;
3515             } else {
3516                 sc.ascent = ascent;
3517                 indexedField = field;
3518             }
3519             applyIndex(field, sc);
3520             return true;
3521         }
3522         return false;
3523     }
3524     truncate = false;
3525     if (existsInverseReference(loadExpr->ref.base, nExistsClauses))
3526     {
3527         dbAnyCursor tmpCursor(*field->defTable, dbCursorViewOnly, NULL);
3528         tmpCursor.paramBase = cursor->paramBase;
3529         if (isIndexApplicableToExpr(sc, expr)) {
3530             sc.cursor = &tmpCursor;
3531             applyIndex(field, sc);
3532             indexedField = field;
3533             expr = loadExpr->ref.base;
3534             cursor->checkForDuplicates();
3535             dbSelection::segment *first = &tmpCursor.selection.first, *curr = first;
3536             do {
3537                 for (int i = 0, n = (int)curr->nRows; i < n; i++) {
3538                     if (!followInverseReference(expr, filter,
3539                                                 cursor, curr->rows[i]))
3540                     {
3541                         break;
3542                     }
3543                 }
3544             } while ((curr = curr->next) != first);
3545             return true;
3546         }
3547     } else if (existsIndexedReference(loadExpr->ref.base)) {
3548         dbExprNode* ref = loadExpr->ref.base->operand[0];
3549         dbFieldDescriptor* refField = ref->ref.field;
3550         assert(refField->type == dbField::tpReference);
3551         dbAnyCursor tmpCursor[2];
3552         int currRefCursor = 0;
3553         tmpCursor[0].setTable(refField->refTable);
3554         tmpCursor[0].paramBase = cursor->paramBase;
3555         if (isIndexApplicableToExpr(sc, expr)) {
3556             oid_t oid;
3557             sc.cursor = &tmpCursor[0];
3558             applyIndex(field, sc);
3559             sc.offs = refField->dbsOffs;
3560             sc.cursor = cursor;
3561             sc.firstKey = sc.lastKey = (char_t*)&oid;
3562             sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3563             sc.tmpKeys = false;
3564             sc.prefixLength = 0;
3565             sc.spatialSearch = false;
3566             sc.arraySearch = false;
3567             sc.condition = NULL;
3568 
3569             indexedField = field;
3570             dbAnyCursor *srcCursor, *dstCursor;
3571             srcCursor = &tmpCursor[0];
3572 
3573             while (true) {
3574                 TRACE_MSG((STRLITERAL("Index merge for field %s.%s\n"),
3575                            refField->defTable->name, refField->longName));
3576                 sc.offs = refField->dbsOffs;
3577                 if (ref->cop == dbvmLoadSelfReference) {
3578                     dstCursor = cursor;
3579                     sc.condition = filter;
3580                 } else {
3581                     dstCursor = &tmpCursor[currRefCursor ^= 1];
3582                     dstCursor->setTable(refField->defTable);
3583                     dstCursor->reset();
3584                 }
3585                 sc.cursor = dstCursor;
3586                 dbSelection::segment *first = &srcCursor->selection.first, *curr = first;
3587                 do {
3588                     for (int i = 0, n = (int)curr->nRows; i < n; i++) {
3589                         oid = curr->rows[i];
3590                         applyIndex(refField, sc);
3591                     }
3592                 } while ((curr = curr->next) != first);
3593 
3594                 if (ref->cop == dbvmLoadSelfReference) {
3595                     break;
3596                 }
3597                 srcCursor = dstCursor;
3598                 ref = ref->operand[0]->operand[0];
3599                 refField = ref->ref.field;
3600                 assert(refField->type == dbField::tpReference);
3601             }
3602             return true;
3603         }
3604     }
3605     return false;
3606 }
3607 
3608 //
3609 // Checks whether expression can be calculated using indicies.
3610 // @param sc search context. If expression can be only prtly evaluated using indices (for example LIKE),
3611 // then original condition is stored in sc.condition, otherwise sc.condition is null
3612 // @param expr condition to be evaluated
3613 // @return true is expression can be calculated using index, false otherwise
3614 //
isIndexApplicableToExpr(dbSearchContext & sc,dbExprNode * expr)3615 bool dbDatabase::isIndexApplicableToExpr(dbSearchContext& sc, dbExprNode* expr)
3616 {
3617     int n = dbExprNode::nodeOperands[expr->cop];
3618     dbExprNode* loadExpr = expr->operand[0];
3619     unsigned loadCop = loadExpr->cop;
3620     if (loadCop == dbvmLowerString ||loadCop == dbvmUpperString) {
3621         loadExpr = loadExpr->operand[0];
3622         loadCop = loadExpr->cop;
3623     }
3624     dbFieldDescriptor* field = loadExpr->ref.field;
3625     size_t paramBase = (size_t)sc.cursor->paramBase;
3626     bool strop = false;
3627     char_t* s;
3628     sc.literal[0].i8 = 0;
3629     sc.literal[1].i8 = 0;
3630 
3631     for (int i = 0; i < n-1; i++) {
3632         bool  bval = false;
3633         db_int8  ival = 0;
3634         real8 fval = 0;
3635         oid_t oid  = 0;
3636         char_t* sval = NULL;
3637         rectangle* rect = NULL;
3638         void* raw = NULL;
3639         dbExprNode* opd = expr->operand[i+1];
3640         switch (opd->cop) {
3641           case dbvmLoadVarArrayOfOid:
3642           case dbvmLoadVarArrayOfInt4:
3643           case dbvmLoadVarArrayOfInt8:
3644             sc.literal[i].a = (dbAnyArray*)((char*)opd->var + paramBase);
3645             continue;
3646           case dbvmLoadVarArrayOfOidPtr:
3647           case dbvmLoadVarArrayOfInt4Ptr:
3648           case dbvmLoadVarArrayOfInt8Ptr:
3649             sc.literal[i].a = *(dbAnyArray**)((char*)opd->var + paramBase);
3650             continue;
3651           case dbvmLoadVarBool:
3652             bval = *(bool*)((char*)opd->var + paramBase);
3653             break;
3654           case dbvmLoadVarInt1:
3655             ival = *(int1*)((char*)opd->var + paramBase);
3656             break;
3657           case dbvmLoadVarInt2:
3658             ival = *(int2*)((char*)opd->var + paramBase);
3659             break;
3660           case dbvmLoadVarInt4:
3661             ival = *(int4*)((char*)opd->var + paramBase);
3662             break;
3663           case dbvmLoadVarInt8:
3664             ival = *(db_int8*)((char*)opd->var + paramBase);
3665             break;
3666           case dbvmLoadVarReference:
3667             oid = *(oid_t*)((char*)opd->var + paramBase);
3668             break;
3669           case dbvmLoadVarRectangle:
3670             rect = (rectangle*)((char*)opd->var + paramBase);
3671             break;
3672           case dbvmLoadVarRectanglePtr:
3673             rect = *(rectangle**)((char*)opd->var + paramBase);
3674             break;
3675           case dbvmLoadVarReal4:
3676             fval = *(real4*)((char*)opd->var + paramBase);
3677             break;
3678           case dbvmLoadVarReal8:
3679             fval = *(real8*)((char*)opd->var + paramBase);
3680             break;
3681           case dbvmLoadVarString:
3682             sval = (char_t*)((char*)opd->var + paramBase);
3683             strop = true;
3684             break;
3685           case dbvmLoadVarStringPtr:
3686             sval = *(char_t**)((char*)opd->var + paramBase);
3687             strop = true;
3688             break;
3689 #ifdef USE_MFC_STRING
3690           case dbvmLoadVarMfcString:
3691             sval = (char_t*)(MFC_STRING::PCXSTR)*(MFC_STRING*)((char*)opd->var + paramBase);
3692             strop = true;
3693             break;
3694 #endif
3695 #ifdef USE_STD_STRING
3696           case dbvmLoadVarStdString:
3697             sval = (char_t*)((STD_STRING*)((char*)opd->var + paramBase))->c_str();
3698             strop = true;
3699             break;
3700 #endif
3701           case dbvmLoadTrue:
3702             bval = true;
3703             break;
3704           case dbvmLoadFalse:
3705             bval = false;
3706             break;
3707           case dbvmLoadVarRawBinary:
3708             raw = (void*)((char*)opd->var + paramBase);
3709             break;
3710           case dbvmLoadVarRawBinaryPtr:
3711             raw = *(void**)((char*)opd->var + paramBase);
3712             break;
3713           case dbvmLoadIntConstant:
3714             ival = opd->ivalue;
3715             break;
3716           case dbvmLoadRealConstant:
3717             fval = opd->fvalue;
3718             break;
3719           case dbvmLoadStringConstant:
3720             sval = (char_t*)opd->svalue.str;
3721             strop = true;
3722             break;
3723           case dbvmLoadRectangleConstant:
3724             rect = &opd->rvalue;
3725             break;
3726           case dbvmLoadNull:
3727             oid = 0;
3728             break;
3729          default:
3730             return false;
3731         }
3732         switch (field->type) {
3733           case dbField::tpBool:
3734             sc.literal[i].b = bval;
3735             break;
3736           case dbField::tpInt1:
3737             sc.literal[i].i1 = (int1)ival;
3738             break;
3739           case dbField::tpInt2:
3740             sc.literal[i].i2 = (int2)ival;
3741             break;
3742           case dbField::tpInt4:
3743             sc.literal[i].i4 = (int4)ival;
3744             break;
3745           case dbField::tpInt8:
3746             sc.literal[i].i8 = ival;
3747             break;
3748           case dbField::tpReference:
3749             sc.literal[i].oid = oid;
3750             break;
3751           case dbField::tpRectangle:
3752             sc.literal[i].rect = rect;
3753             break;
3754           case dbField::tpReal4:
3755             sc.literal[i].f4 = (real4)fval;
3756             break;
3757           case dbField::tpReal8:
3758             sc.literal[i].f8 = fval;
3759             break;
3760           case dbField::tpString:
3761             sc.literal[i].s = sval;
3762             break;
3763           case dbField::tpRawBinary:
3764             sc.literal[i].raw = raw;
3765             break;
3766           default:
3767             assert(false);
3768         }
3769     }
3770     sc.db = this;
3771     sc.offs = field->dbsOffs;
3772     sc.tmpKeys = false;
3773     sc.prefixLength = 0;
3774     sc.condition = NULL;
3775     sc.spatialSearch = false;
3776     sc.arraySearch = false;
3777 
3778     switch (expr->cop) {
3779       case dbvmInArrayInt4:
3780         sc.arraySearch = true;
3781         return field->type == dbField::tpInt4;
3782       case dbvmInArrayInt8:
3783         sc.arraySearch = true;
3784         return field->type == dbField::tpInt8;
3785       case dbvmInArrayReference:
3786         sc.arraySearch = true;
3787         return field->type == dbField::tpReference;
3788 
3789       case dbvmEqRectangle:
3790       case dbvmLtRectangle:
3791       case dbvmLeRectangle:
3792       case dbvmGtRectangle:
3793       case dbvmGeRectangle:
3794         sc.firstKey = (char_t*)sc.literal[0].rect;
3795         sc.firstKeyInclusion = dbRtree::EQUAL + expr->cop - dbvmEqRectangle;
3796         sc.spatialSearch = true;
3797         break;
3798       case dbvmOverlapsRectangle:
3799         sc.firstKey = (char_t*)sc.literal[0].rect;
3800         sc.firstKeyInclusion = dbRtree::OVERLAPS;
3801         sc.spatialSearch = true;
3802         break;
3803 
3804       case dbvmIsNull:
3805       case dbvmEqReference:
3806       case dbvmEqInt:
3807       case dbvmEqBool:
3808       case dbvmEqReal:
3809       case dbvmEqString:
3810       case dbvmEqBinary:
3811         sc.firstKey = sc.lastKey =
3812             strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3813         sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3814         break;
3815       case dbvmGtInt:
3816       case dbvmGtReal:
3817       case dbvmGtString:
3818       case dbvmGtBinary:
3819         sc.firstKey = strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3820         sc.lastKey = NULL;
3821         sc.firstKeyInclusion = false;
3822         break;
3823       case dbvmGeInt:
3824       case dbvmGeReal:
3825       case dbvmGeString:
3826       case dbvmGeBinary:
3827         sc.firstKey = strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3828         sc.lastKey = NULL;
3829         sc.firstKeyInclusion = true;
3830         break;
3831       case dbvmLtInt:
3832       case dbvmLtReal:
3833       case dbvmLtString:
3834       case dbvmLtBinary:
3835         sc.firstKey = NULL;
3836         sc.lastKey = strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3837         sc.lastKeyInclusion = false;
3838         break;
3839       case dbvmLeInt:
3840       case dbvmLeReal:
3841       case dbvmLeString:
3842       case dbvmLeBinary:
3843         sc.firstKey = NULL;
3844         sc.lastKey = strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3845         sc.lastKeyInclusion = true;
3846         break;
3847       case dbvmBetweenInt:
3848       case dbvmBetweenReal:
3849       case dbvmBetweenString:
3850       case dbvmBetweenBinary:
3851         sc.firstKey = strop ? sc.literal[0].s : (char_t*)&sc.literal[0];
3852         sc.firstKeyInclusion = true;
3853         sc.lastKey = strop ? sc.literal[1].s : (char_t*)&sc.literal[1];
3854         sc.lastKeyInclusion = true;
3855         break;
3856       case dbvmLikeString:
3857       case dbvmLikeEscapeString:
3858         if ((s = findWildcard(sc.literal[0].s, sc.literal[1].s)) == NULL
3859             || (s[1] == '\0' || s != sc.literal[0].s))
3860         {
3861             if (s == NULL) {
3862                 sc.firstKey = sc.lastKey = sc.literal[0].s;
3863                 sc.firstKeyInclusion = sc.lastKeyInclusion = true;
3864             } else {
3865                 int len = (int)(s - sc.literal[0].s);
3866                 if (len == 0) {
3867                     if (*s != dbMatchAnySubstring) {
3868                         return false;
3869                     }
3870                     sc.firstKey = NULL;
3871                     sc.lastKey = NULL;
3872                 } else {
3873                     if (((s[0] != dbMatchAnySubstring || s[1] != '\0')
3874                          && loadCop != dbvmLoadSelfString))
3875                     {
3876                         return false;
3877                     }
3878                     char_t* firstKey = new char_t[len+1];
3879                     sc.firstKey = firstKey;
3880                     sc.lastKey = NULL;
3881                     memcpy(firstKey, sc.literal[0].s, len*sizeof(char_t));
3882                     firstKey[len] = '\0';
3883                     sc.firstKeyInclusion = true;
3884                     sc.prefixLength = len;
3885                     sc.tmpKeys = true;
3886                     if (s[0] != dbMatchAnySubstring || s[1] != '\0') {
3887                         // Records selected by index do not necessarily
3888                         // match the pattern, so include pattern matching in
3889                         // condition expression
3890                         sc.condition = expr;
3891                     }
3892                 }
3893             }
3894         } else {
3895             return false;
3896         }
3897         break;
3898       default:
3899         return false;
3900     }
3901     return true;
3902 }
3903 
3904 struct SearchThreadArgument {
3905     dbParallelQueryContext* ctx;
3906     int                     id;
3907 };
3908 
3909 
parallelSearch(void * arg)3910 static void thread_proc parallelSearch(void* arg)
3911 {
3912     SearchThreadArgument* sa = (SearchThreadArgument*)arg;
3913     sa->ctx->search(sa->id);
3914 }
3915 
3916 
traverse(dbAnyCursor * cursor,dbQuery & query)3917 void dbDatabase::traverse(dbAnyCursor* cursor, dbQuery& query)
3918 {
3919     const int defaultStackSize = 1024;
3920     oid_t buf[defaultStackSize];
3921     oid_t *stack = buf;
3922     int   stackSize = defaultStackSize;
3923     int   sp = 0, len;
3924     dbAnyArray* arr;
3925     oid_t oid, *refs;
3926     dbTableDescriptor* table = cursor->table;
3927 
3928     void* root = (void*)query.root;
3929 
3930     switch (query.startFrom) {
3931       case dbCompiledQuery::StartFromFirst:
3932         oid = table->firstRow;
3933         if (oid != 0) {
3934             stack[sp++] = oid;
3935         }
3936         break;
3937       case dbCompiledQuery::StartFromLast:
3938         oid = table->lastRow;
3939         if (oid != 0) {
3940             stack[sp++] = oid;
3941         }
3942         break;
3943       case dbCompiledQuery::StartFromRef:
3944         oid = *(oid_t*)root;
3945         if (oid != 0) {
3946             stack[sp++] = oid;
3947         }
3948         break;
3949       case dbCompiledQuery::StartFromArrayPtr:
3950         root = *(dbAnyArray**)root;
3951         // no break
3952       case dbCompiledQuery::StartFromArray:
3953         arr = (dbAnyArray*)root;
3954         len = (int)arr->length();
3955         if (len > stackSize) {
3956             stackSize = len;
3957             stack = new oid_t[stackSize];
3958         }
3959         refs = (oid_t*)arr->base();
3960         while (--len >= 0) {
3961             oid = refs[len];
3962             if (oid != 0) {
3963                 stack[sp++] = oid;
3964             }
3965         }
3966         break;
3967       default:
3968         assert(false);
3969     }
3970     cursor->checkForDuplicates();
3971     dbExprNode* condition = query.tree;
3972     dbFollowByNode* follow = query.follow;
3973     int iterType = query.iterType;
3974     dbGetTie tie;
3975 
3976     while (sp != 0) {
3977         oid_t curr = stack[--sp];
3978         if (condition->cop == dbvmVoid || evaluateBoolean(condition, curr, table, cursor)) {
3979             if (!cursor->add(curr)) {
3980                 break;
3981             }
3982         } else {
3983             cursor->mark(curr);
3984         }
3985         byte* record = (byte*)getRow(tie, curr);
3986         if (iterType & (dbCompiledQuery::TraverseForward|dbCompiledQuery::TraverseBackward)) {
3987             dbRecord rec;
3988             getHeader(rec, curr);
3989             if (iterType & dbCompiledQuery::TraverseForward) {
3990                 oid = rec.next;
3991                 if (oid != 0 && !cursor->isMarked(oid)) {
3992                     stack[sp++] = oid;
3993                 }
3994             }
3995             if (iterType & dbCompiledQuery::TraverseBackward) {
3996                 oid = rec.prev;
3997                 if (oid != 0 && !cursor->isMarked(oid)) {
3998                     stack[sp++] = oid;
3999                 }
4000             }
4001         }
4002         for (dbFollowByNode* fp = follow; fp != NULL; fp = fp->next) {
4003             dbFieldDescriptor* fd = fp->field;
4004             if (fd->type == dbField::tpArray) {
4005                 dbVarying* vp = (dbVarying*)(record + fd->dbsOffs);
4006                 len = vp->size;
4007                 if (sp + len > stackSize) {
4008                     int newSize = len > stackSize ? len*2 : stackSize*2;
4009                     oid_t* newStack = new oid_t[newSize];
4010                     memcpy(newStack, stack, stackSize*sizeof(oid_t));
4011                     stackSize = newSize;
4012                     if (stack != buf) {
4013                         delete[] stack;
4014                     }
4015                     stack = newStack;
4016                 }
4017                 refs = (oid_t*)(record + vp->offs);
4018                 while (--len >= 0) {
4019                     oid = refs[len];
4020                     if (oid != 0 && !cursor->isMarked(oid)) {
4021                         stack[sp++] = oid;
4022                     }
4023                 }
4024             } else {
4025                 assert(fd->type == dbField::tpReference);
4026                 if (sp == stackSize) {
4027                     int newSize = stackSize*2;
4028                     oid_t* newStack = new oid_t[newSize];
4029                     memcpy(newStack, stack, stackSize*sizeof(oid_t));
4030                     stackSize = newSize;
4031                     if (stack != buf) {
4032                         delete[] stack;
4033                     }
4034                     stack = newStack;
4035                 }
4036                 oid = *(oid_t*)(record + fd->dbsOffs);
4037                 if (oid != 0 && !cursor->isMarked(oid)) {
4038                     stack[sp++] = oid;
4039                 }
4040             }
4041         }
4042     }
4043     if (stack != buf) {
4044         delete[] stack;
4045     }
4046     if (query.order != NULL) {
4047         cursor->selection.sort(this, query.order);
4048     }
4049 }
4050 
4051 #ifdef PROFILE
4052 
4053 const size_t PROFILER_HASH_SIZE = 1013;
4054 const size_t PROFILER_MAX_QUERY_LENGTH = 1024;
4055 
4056 class Profiler {
4057     dbMutex mutex;
4058 
4059     struct QueryStat {
4060         QueryStat* next;
4061         char*      query;
4062         size_t     count;
4063         size_t     hashCode;
4064         time_t     maxTime;
4065         time_t     totalTime;
4066         bool       sequential;
4067 
~QueryStatProfiler::QueryStat4068         ~QueryStat() {
4069             delete[] query;
4070         }
4071     };
4072 
4073     QueryStat* table[PROFILER_HASH_SIZE];
4074     size_t nQueries;
4075 
4076 
orderByTime(void const * p1,void const * p2)4077     static int orderByTime(void const* p1, void const* p2) {
4078         QueryStat* q1 = *(QueryStat**)p1;
4079         QueryStat* q2 = *(QueryStat**)p2;
4080         return q1->totalTime < q2->totalTime ? -1
4081             : q1->totalTime > q2->totalTime ? 1 : q1->count < q2->count ? -1 : q1->count == q2->count ? 0 : 1;
4082     }
4083 
hashFunction(char const * s)4084     static size_t hashFunction(char const* s) {
4085         size_t h;
4086         for (h = 0; *s != '\0'; h = h*31 + (unsigned char)*s++);
4087         return h;
4088     }
4089 
4090   public:
add(char const * query,time_t elapsed,bool sequential)4091     void add(char const* query, time_t elapsed, bool sequential)
4092     {
4093         dbCriticalSection cs(mutex);
4094         size_t hash = hashFunction(query);
4095         size_t h = hash  % PROFILER_HASH_SIZE;
4096         QueryStat* s;
4097         for (s = table[h]; s != NULL; s = s->next)
4098         {
4099             if (s->hashCode == hash && strcmp(query, s->query) == 0) {
4100                 s->count += 1;
4101                 if (elapsed > s->maxTime) {
4102                     s->maxTime = elapsed;
4103                 }
4104                 s->totalTime += elapsed;
4105                 s->sequential |= sequential;
4106                 return;
4107             }
4108         }
4109         s = new QueryStat();
4110         s->query = new char[strlen(query) + 1];
4111         strcpy(s->query, query);
4112         s->next = table[h];
4113         table[h] = s;
4114         s->totalTime = elapsed;
4115         s->maxTime = elapsed;
4116         s->count = 1;
4117         s->hashCode = hash;
4118         s->sequential = sequential;
4119         nQueries += 1;
4120     }
4121 
4122     void dump(char const* filePath);
4123 
dumpToStream(FILE * f)4124     void dumpToStream(FILE* f)
4125     {
4126         dbCriticalSection cs(mutex);
4127         QueryStat** sa = new QueryStat*[nQueries];
4128         QueryStat** pp = sa;
4129         time_t total = 0;
4130         for (size_t i = 0; i < PROFILER_HASH_SIZE; i++) {
4131             for (QueryStat* sp = table[i]; sp != NULL; sp = sp->next) {
4132                 *pp++ = sp;
4133                 total += sp->totalTime;
4134             }
4135         }
4136         qsort(sa, nQueries, sizeof(QueryStat*), &orderByTime);
4137         fprintf(f, "S     Total      Count Maximum Average Percent Query\n");
4138         while (pp != sa) {
4139             QueryStat* s = *--pp;
4140             fprintf(f, "%c%10ld %10ld %7d %7d %6d%% %s\n",
4141                     s->sequential ? '!' : ' ',
4142                     (long)s->totalTime, (long)s->count, (int)s->maxTime, (s->count != 0 ? (int)(s->totalTime/s->count) : 0),
4143                     (total != 0 ? (int)(s->totalTime*100/total) : 0),
4144                     s->query
4145                 );
4146         }
4147         delete[] sa;
4148     }
4149 
~Profiler()4150     ~Profiler() {
4151         dumpToStream(stdout);
4152         for (size_t i = 0; i < PROFILER_HASH_SIZE; i++) {
4153             QueryStat* next;
4154             for (QueryStat* sp = table[i]; sp != NULL; sp = next) {
4155                 next = sp->next;
4156                 delete sp;
4157             }
4158         }
4159     }
4160 
getCurrentTimeMsec()4161     static unsigned getCurrentTimeMsec()
4162     {
4163 #if defined(_WIN32) && !defined(__SYMBIAN32__)
4164         return GetTickCount();
4165 #else
4166         struct timeval tv;
4167         gettimeofday(&tv, NULL);
4168         return (unsigned)(tv.tv_sec*1000 + tv.tv_usec / 1000);
4169 #endif
4170     }
4171 
4172     struct Measure
4173     {
4174         Profiler& profiler;
4175         dbAnyCursor* cursor;
4176         dbQuery*  query;
4177         unsigned  start;
4178         bool      sequential;
4179 
MeasureProfiler::Measure4180         Measure(Profiler& p, dbAnyCursor* c, dbQuery* q = NULL)
4181         : profiler(p), cursor(c), query(q), start(getCurrentTimeMsec()), sequential(false) {}
4182 
~MeasureProfiler::Measure4183         ~Measure() {
4184             char buf[PROFILER_MAX_QUERY_LENGTH];
4185             int n = sprintf(buf, "SELECT FROM %s", cursor->getTable()->getName());
4186             if (query != NULL) {
4187                 n += sprintf(buf + n, " WHERE ");
4188                 query->dump(buf + n);
4189             }
4190             profiler.add(buf, getCurrentTimeMsec() - start, sequential);
4191         }
4192     };
4193 };
4194 
dump(char const * filePath)4195 void Profiler::dump(char const* filePath)
4196 {
4197     FILE* f = fopen(filePath, "w");
4198     dumpToStream(f);
4199     fclose(f);
4200 }
4201 
4202 Profiler profiler;
4203 
profile(char const * file)4204 void dbDatabase::profile(char const* file)
4205 {
4206     profiler.dump(file);
4207 }
4208 
profile(FILE * file)4209 void dbDatabase::profile(FILE* file)
4210 {
4211     profiler.dumpToStream(file);
4212 }
4213 
4214 #endif
4215 
prepareQuery(dbAnyCursor * cursor,dbQuery & query)4216 bool dbDatabase::prepareQuery(dbAnyCursor* cursor, dbQuery& query)
4217 {
4218     if (cursor == NULL) {
4219         return false;
4220     }
4221     assert(opened);
4222     dbDatabaseThreadContext* ctx = threadContext.get();
4223     assert(ctx != NULL);
4224     {
4225         dbCriticalSection cs(query.mutex);
4226         query.mutexLocked = true;
4227         if (!query.compiled() || cursor->table != query.table || schemeVersion != query.schemeVersion) {
4228             query.schemeVersion = schemeVersion;
4229             if (!ctx->compiler.compile(cursor->table, query)) {
4230                 query.mutexLocked = false;
4231                 return false;
4232             }
4233         }
4234         query.mutexLocked = false;
4235         return true;
4236     }
4237 }
4238 
select(dbAnyCursor * cursor,dbQuery & query)4239 void dbDatabase::select(dbAnyCursor* cursor, dbQuery& query)
4240 {
4241     if (query.isEmpty()) {
4242         select(cursor);
4243         return;
4244     }
4245 #ifdef PROFILE
4246     Profiler::Measure measure(profiler, cursor, &query);
4247 #endif
4248     assert(opened);
4249     dbDatabaseThreadContext* ctx = threadContext.get();
4250     assert(ctx != NULL);
4251     {
4252         dbCriticalSection cs(query.mutex);
4253         query.mutexLocked = true;
4254         if (!query.compiled() || cursor->table != query.table || schemeVersion != query.schemeVersion) {
4255             query.schemeVersion = schemeVersion;
4256             if (!ctx->compiler.compile(cursor->table, query)) {
4257                 query.mutexLocked = false;
4258                 return;
4259             }
4260         }
4261         query.mutexLocked = false;
4262     }
4263 #if GIGABASE_DEBUG == DEBUG_TRACE
4264     char_t buf[4096];
4265     if (query.elements != NULL) {
4266         TRACE_MSG((STRLITERAL("Query:  select * from %s where %s\n"), query.table->name,  query.dump(buf)));
4267     } else {
4268         TRACE_MSG((STRLITERAL("Query:  select * from %s\n"), query.table->name));
4269     }
4270 #endif
4271     beginTransaction(cursor->type == dbCursorForUpdate ? dbUpdateLock : dbSharedLock);
4272     refreshTable(query.table);
4273 
4274     if (query.startFrom != dbCompiledQuery::StartFromAny) {
4275         if (query.limitSpecified && query.order == NULL) {
4276             cursor->setStatementLimit(query);
4277         }
4278         ctx->cursors.link(cursor);
4279         traverse(cursor, query);
4280         if (query.limitSpecified && query.order != NULL) {
4281             cursor->setStatementLimit(query);
4282             cursor->truncateSelection();
4283         }
4284         return;
4285     }
4286 
4287     dbExprNode* condition = query.tree;
4288     if (condition->cop == dbvmVoid && query.order == NULL && !query.limitSpecified) {
4289         // Empty select condition: select all records in the table
4290         select(cursor);
4291         return;
4292     }
4293     if (condition->cop == dbvmEqReference) {
4294         if (condition->operand[0]->cop == dbvmCurrent) {
4295             if (condition->operand[1]->cop == dbvmLoadVarReference) {
4296                 cursor->setCurrent(*(dbAnyReference*)((char*)condition->operand[1]->var + (size_t)cursor->paramBase));
4297                 return;
4298             } else if (condition->operand[1]->cop == dbvmIntToReference
4299                        && condition->operand[1]->operand[0]->cop == dbvmLoadIntConstant)
4300             {
4301                 oid_t oid = (oid_t)condition->operand[1]->operand[0]->ivalue;
4302                 cursor->setCurrent(*(dbAnyReference*)&oid);
4303                 return;
4304             }
4305         }
4306         if (condition->operand[1]->cop == dbvmCurrent) {
4307             if (condition->operand[0]->cop == dbvmLoadVarReference) {
4308                 cursor->setCurrent(*(dbAnyReference*)((char*)condition->operand[0]->var + (size_t)cursor->paramBase));
4309                 return;
4310             } else if (condition->operand[0]->cop == dbvmIntToReference
4311                        && condition->operand[0]->operand[0]->cop == dbvmLoadIntConstant)
4312             {
4313                 oid_t oid = (oid_t)condition->operand[0]->operand[0]->ivalue;
4314                 cursor->setCurrent(*(dbAnyReference*)&oid);
4315                 return;
4316             }
4317         }
4318     }
4319     ctx->cursors.link(cursor);
4320 
4321     dbFieldDescriptor* orderField = NULL;
4322     bool truncate = query.limitSpecified;
4323     bool ascent = true;
4324     if (query.order != NULL) {
4325         truncate = false;
4326         if (query.order->next == NULL) {
4327             orderField = query.order->getField();
4328             ascent = query.order->ascent;
4329             truncate = query.limitSpecified;
4330         }
4331     }
4332     dbFieldDescriptor* indexedField = orderField;
4333     if (applyIndices(cursor, condition, condition, NULL, query, indexedField, truncate, ascent)) {
4334         if (query.order != NULL) {
4335             if (indexedField == NULL || indexedField != orderField) {
4336                 cursor->selection.sort(this, query.order);
4337             }
4338         }
4339         if (query.limitSpecified && !truncate) {
4340             cursor->setStatementLimit(query);
4341             cursor->truncateSelection();
4342         }
4343         return;
4344     }
4345     if (query.order != NULL && query.order->next == NULL
4346         && query.order->field != NULL && query.order->field->type != dbField::tpRectangle
4347         && query.order->field->bTree != 0)
4348     {
4349         dbFieldDescriptor* field = query.order->field;
4350         TRACE_MSG((STRLITERAL("Use index for ordering records by field %s.%s\n"),
4351                    query.table->name, field->longName));
4352         if (!query.limitSpecified && cursor->hasIncrementalHint()) {
4353             dbSearchContext sc;
4354             sc.ascent = query.order->ascent;
4355             sc.cursor = cursor;
4356             sc.firstKey = sc.lastKey = NULL;
4357             sc.firstKeyInclusion = sc.lastKeyInclusion = false;
4358             sc.tmpKeys = false;
4359             sc.prefixLength = 0;
4360             sc.spatialSearch = false;
4361             sc.arraySearch = false;
4362             sc.condition = condition;
4363             cursor->iterator = &cursor->btreeIterator;
4364             cursor->btreeIterator.init(this, field->bTree, sc, NULL);
4365         } else {
4366             if (query.limitSpecified) {
4367                 cursor->setStatementLimit(query);
4368             }
4369             if (condition->cop == dbvmVoid) {
4370                 if (query.order->ascent) {
4371                     dbBtree::traverseForward(this, field->bTree, cursor);
4372                 } else {
4373                     dbBtree::traverseBackward(this, field->bTree, cursor);
4374                 }
4375             } else {
4376                 if (query.order->ascent) {
4377                     dbBtree::traverseForward(this, field->bTree, cursor, condition);
4378                 } else {
4379                     dbBtree::traverseBackward(this, field->bTree, cursor, condition);
4380                 }
4381             }
4382         }
4383         return;
4384     }
4385 
4386     if (query.limitSpecified && query.order == NULL) {
4387         cursor->setStatementLimit(query);
4388     } else if (cursor->hasIncrementalHint()) {
4389         cursor->iterator = &cursor->tableIterator;
4390         cursor->tableIterator.init(cursor, condition);
4391         return;
4392     }
4393 
4394 #ifdef PROFILE
4395     measure.sequential = true;
4396 #endif
4397     dbTableDescriptor* table = cursor->table;
4398     int n = parThreads-1;
4399     if (cursor->getNumberOfRecords() == 0
4400         && n > 0 && table->nRows >= dbParallelScanThreshold
4401         && cursor->limit >= dbDefaultSelectionLimit)
4402     {
4403         dbPooledThread* thread[dbMaxParallelSearchThreads];
4404         SearchThreadArgument sa[dbMaxParallelSearchThreads];
4405         dbParallelQueryContext par(this, table, &query, cursor);
4406         int i;
4407         for (i = 0; i < n; i++) {
4408             sa[i].id = i;
4409             sa[i].ctx = &par;
4410             thread[i] = threadPool.create((dbThread::thread_proc_t)parallelSearch, &sa[i]);
4411         }
4412         par.search(i);
4413         for (i = 0; i < n; i++) {
4414             threadPool.join(thread[i]);
4415         }
4416         if (query.order != NULL) {
4417             dbRecord* rec[dbMaxParallelSearchThreads];
4418             oid_t recOid[dbMaxParallelSearchThreads];
4419             dbGetTie  tie[dbMaxParallelSearchThreads];
4420             for (i = 0; i <= n; i++) {
4421                 if (par.selection[i].nRows != 0) {
4422                     rec[i] = getRow(tie[i], par.selection[i].first.rows[0]);
4423                     recOid[i] = par.selection[i].first.rows[0];
4424                 } else {
4425                     rec[i] = NULL;
4426                     recOid[i] = 0;
4427                 }
4428             }
4429             while (true) {
4430                 int min = -1;
4431                 for (i = 0; i <= n; i++) {
4432                     if (rec[i] != NULL
4433                         && (min < 0 || dbSelection::compare(recOid[i], rec[i], recOid[min], rec[min],
4434                                                             query.order) < 0))
4435                     {
4436                         min = i;
4437                     }
4438                 }
4439                 if (min < 0) {
4440                     return;
4441                 }
4442                 oid_t oid =
4443                     par.selection[min].curr->rows[par.selection[min].pos];
4444                 cursor->selection.add(oid);
4445                 par.selection[min].pos += 1;
4446                 if (par.selection[min].pos == par.selection[min].curr->nRows){
4447                     par.selection[min].pos = 0;
4448                     dbSelection::segment* next = par.selection[min].curr->next;
4449                     if (par.selection[min].curr != &par.selection[min].first) {
4450                         delete par.selection[min].curr;
4451                     } else {
4452                         par.selection[min].first.nRows = 0;
4453                     }
4454                     par.selection[min].curr = next;
4455                     if (next->nRows == 0) {
4456                         rec[min] = NULL;
4457                         recOid[min] = 0;
4458                         continue;
4459                     }
4460                 }
4461                 oid = par.selection[min].curr->rows[par.selection[min].pos];
4462                 rec[min] = getRow(tie[min], oid);
4463                 recOid[min] = oid;
4464             }
4465         } else {
4466             for (i = 0; i <= n; i++) {
4467                 if (par.selection[i].nRows != 0) {
4468                     dbSelection::segment* prev = par.selection[i].first.prev;
4469                     dbSelection::segment* next = par.selection[i].first.next;
4470                     if (cursor->selection.nRows == 0) {
4471                         prev->next = next->prev = &cursor->selection.first;
4472                         cursor->selection.first = par.selection[i].first;
4473                     } else if (cursor->selection.first.prev->nRows + par.selection[i].first.nRows <= cursor->selection.first.prev->maxRows) {
4474                         memcpy(cursor->selection.first.prev->rows + cursor->selection.first.prev->nRows, par.selection[i].first.rows,
4475                                par.selection[i].first.nRows*sizeof(oid_t));
4476                         cursor->selection.first.prev->nRows += par.selection[i].first.nRows;
4477                         prev->next = &cursor->selection.first;
4478                         next->prev = cursor->selection.first.prev;
4479                         cursor->selection.first.prev->next = par.selection[i].first.next;
4480                         cursor->selection.first.prev = par.selection[i].first.prev;
4481                     } else {
4482                         dbSelection::segment* s = new dbSelection::segment();
4483                         prev->next = &cursor->selection.first;
4484                         next->prev = s;
4485                         *s = par.selection[i].first;
4486                         s->prev = cursor->selection.first.prev;
4487                         cursor->selection.first.prev->next = s;
4488                         cursor->selection.first.prev = par.selection[i].first.prev;
4489                     }
4490                     cursor->selection.nRows += par.selection[i].nRows;
4491                     par.selection[i].first.prune();
4492                 }
4493             }
4494         }
4495     } else {
4496         oid_t oid = table->firstRow;
4497         if (!cursor->isLimitReached()) {
4498             while (oid != 0) {
4499                 dbRecord rec;
4500                 if (evaluateBoolean(condition, oid, table, cursor)) {
4501                     if (!cursor->add(oid)) {
4502                         break;
4503                     }
4504                 }
4505                 getHeader(rec, oid);
4506                 oid = rec.next;
4507             }
4508         }
4509         if (query.order != NULL) {
4510             cursor->selection.sort(this, query.order);
4511         }
4512     }
4513     if (query.limitSpecified && query.order != NULL) {
4514         cursor->setStatementLimit(query);
4515         cursor->truncateSelection();
4516     }
4517 }
4518 
select(dbAnyCursor * cursor)4519 void dbDatabase::select(dbAnyCursor* cursor)
4520 {
4521 #ifdef PROFILE
4522     Profiler::Measure measure(profiler, cursor);
4523 #endif
4524     assert(opened);
4525     beginTransaction(cursor->type == dbCursorForUpdate ? dbUpdateLock : dbSharedLock);
4526     refreshTable(cursor->table);
4527     cursor->firstId = cursor->table->firstRow;
4528     cursor->lastId = cursor->table->lastRow;
4529     cursor->selection.nRows = cursor->table->nRows;
4530     cursor->allRecords = true;
4531     threadContext.get()->cursors.link(cursor);
4532 }
4533 
4534 
lookupTable(dbTableDescriptor * origDesc)4535 dbTableDescriptor* dbDatabase::lookupTable(dbTableDescriptor* origDesc)
4536 {
4537     for (dbTableDescriptor* desc = tables; desc != NULL; desc = desc->nextDbTable) {
4538         if (desc == origDesc || desc->cloneOf == origDesc) {
4539             return desc;
4540         }
4541     }
4542     return NULL;
4543 }
4544 
remove(dbTableDescriptor * desc,oid_t delId)4545 void dbDatabase::remove(dbTableDescriptor* desc, oid_t delId)
4546 {
4547     modified = true;
4548     beginTransaction(dbExclusiveLock);
4549     dbTransactionLogger* tl = logger;
4550     if (tl != NULL) {
4551         if (!tl->remove(desc, delId)) {
4552             handleError(RejectedByTransactionLogger);
4553         }
4554         logger = NULL; // do not log cascade deletes
4555     }
4556     refreshTable(desc);
4557     if (inverseReferencesUpdate) {
4558         removeInverseReferences(desc, delId);
4559     }
4560 
4561     dbFieldDescriptor* fd;
4562     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField){
4563         dbHashTable::remove(this, fd->hashTable, delId, fd->type, fd->dbsOffs);
4564     }
4565     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4566         if (fd->type == dbField::tpRectangle) {
4567             dbRtree::remove(this, fd->bTree, delId, fd->dbsOffs);
4568         } else {
4569             dbBtree::remove(this, fd->bTree, delId, fd->dbsOffs, fd->comparator);
4570         }
4571     }
4572     freeRow(desc->tableId, delId, desc);
4573     updateCursors(delId, true);
4574     logger = tl;
4575 }
4576 
putRow(dbPutTie & tie,oid_t oid)4577 dbRecord* dbDatabase::putRow(dbPutTie& tie, oid_t oid) {
4578     offs_t pos = getPos(oid);
4579     int offs = (int)pos & (dbPageSize-1);
4580     byte* p = pool.get(pos - offs);
4581     dbRecord* rec = (dbRecord*)(p + (offs & ~dbFlagsMask));
4582     if (!(offs & dbModifiedFlag)) {
4583         dirtyPagesMap[size_t(oid/dbHandlesPerPage/32)]
4584             |= 1 << int(oid/dbHandlesPerPage & 31);
4585         cloneBitmap(pos & ~dbFlagsMask, rec->size);
4586         allocate(rec->size, oid);
4587         pos = getPos(oid);
4588     }
4589     tie.set(pool, oid, pos & ~dbFlagsMask, rec->size);
4590     pool.unfix(p);
4591     return (dbRecord*)tie.get();
4592 }
4593 
put(dbPutTie & tie,oid_t oid)4594 byte* dbDatabase::put(dbPutTie& tie, oid_t oid) {
4595     offs_t pos = getPos(oid);
4596     if (!(pos & dbModifiedFlag)) {
4597         assert(!commitInProgress);
4598         dirtyPagesMap[size_t(oid/dbHandlesPerPage/32)]
4599             |= 1 << int(oid/dbHandlesPerPage & 31);
4600         allocate(dbPageSize, oid);
4601         cloneBitmap(pos & ~dbFlagsMask, dbPageSize);
4602         pos = getPos(oid);
4603     }
4604     tie.set(pool, oid, pos & ~dbFlagsMask, dbPageSize);
4605     return tie.get();
4606 }
4607 
4608 
putRow(dbPutTie & tie,oid_t oid,size_t newSize)4609 dbRecord* dbDatabase::putRow(dbPutTie& tie, oid_t oid, size_t newSize)
4610 {
4611     offs_t pos = getPos(oid);
4612     int offs = (int)pos & (dbPageSize-1);
4613     byte* p = pool.get(pos - offs);
4614     dbRecord* rec = (dbRecord*)(p + (offs & ~dbFlagsMask));
4615     if (!(offs & dbModifiedFlag)) {
4616         dirtyPagesMap[size_t(oid/dbHandlesPerPage/32)]
4617             |= 1 << int(oid/dbHandlesPerPage & 31);
4618         cloneBitmap(pos, rec->size);
4619         pos = allocate((offs_t)newSize);
4620         setPos(oid, pos | dbModifiedFlag);
4621     } else {
4622         if (DOALIGN(rec->size, dbAllocationQuantum) < DOALIGN(newSize, dbAllocationQuantum)) {
4623             offs_t newPos = allocate((offs_t)newSize);
4624             cloneBitmap(pos & ~dbFlagsMask, rec->size);
4625             free(pos & ~dbFlagsMask, rec->size);
4626             pos = newPos;
4627             setPos(oid, pos | dbModifiedFlag);
4628         } else if (rec->size > newSize) {
4629             newSize = rec->size;
4630         }
4631     }
4632     tie.set(pool, oid, pos & ~dbFlagsMask, newSize);
4633     dbRecord* record = (dbRecord*)tie.get();
4634     record->next = rec->next;
4635     record->prev = rec->prev;
4636     record->size = (nat4)newSize;
4637     pool.unfix(p);
4638     return record;
4639 }
4640 
allocateRow(oid_t tableId,oid_t oid,size_t size,dbTableDescriptor * desc)4641 void dbDatabase::allocateRow(oid_t tableId, oid_t oid, size_t size,
4642                               dbTableDescriptor* desc)
4643 {
4644     dbPutTie rTie, tTie;
4645     offs_t pos = allocate((offs_t)size);
4646     //printf("Allocate object %s with OID=%x\n", desc != NULL ? desc->name : "???", oid);
4647     setPos(oid, pos | dbModifiedFlag);
4648     dbTable* table = (dbTable*)putRow(tTie, tableId);
4649     rTie.set(pool, oid, pos, size);
4650     dbRecord* record = (dbRecord*)rTie.get();
4651     memset(record, 0, size);
4652     record->size = (nat4)size;
4653     record->prev = table->lastRow;
4654     if (table->lastRow != 0) {
4655         //
4656         // Optimisation hack: avoid cloning of the last record.
4657         // Possible inconsistency in L2-list will be eliminated by recovery
4658         // procedure.
4659         //
4660         dbRecord* rec = (dbRecord*)put(table->lastRow);
4661         rec->next = oid;
4662         pool.unfix(rec);
4663     } else {
4664         table->firstRow = oid;
4665         if (desc != NULL) {
4666             desc->firstRow = oid;
4667         }
4668     }
4669     table->lastRow = oid;
4670     table->nRows += 1;
4671 #ifdef AUTOINCREMENT_SUPPORT
4672     table->count += 1;
4673 #endif
4674     if (desc != NULL) {
4675         desc->lastRow = oid;
4676         desc->nRows += 1;
4677 #ifdef AUTOINCREMENT_SUPPORT
4678         desc->autoincrementCount = table->count;
4679 #endif
4680         assert(table->nRows == desc->nRows);
4681     }
4682 }
4683 
4684 
freeRow(oid_t tableId,oid_t oid,dbTableDescriptor * desc)4685 void dbDatabase::freeRow(oid_t tableId, oid_t oid, dbTableDescriptor* desc)
4686 {
4687     dbPutTie tie;
4688     dbTable* table = (dbTable*)putRow(tie, tableId);
4689     dbRecord rec;
4690     getHeader(rec, oid);
4691     table->nRows -= 1;
4692     if (rec.prev == 0) {
4693         table->firstRow = rec.next;
4694     } else {
4695         dbPutTie tie;
4696         putRow(tie, rec.prev)->next = rec.next;
4697     }
4698     if (rec.next == 0) {
4699         table->lastRow = rec.prev;
4700     } else {
4701         dbPutTie tie;
4702         putRow(tie, rec.next)->prev = rec.prev;
4703     }
4704     offs_t pos = getPos(oid);
4705     if (pos & dbModifiedFlag) {
4706         free(pos & ~dbFlagsMask, rec.size);
4707     } else {
4708         cloneBitmap(pos, rec.size);
4709     }
4710     if (desc != NULL) {
4711         desc->nRows = table->nRows;
4712         desc->firstRow = table->firstRow;
4713         desc->lastRow = table->lastRow;
4714     }
4715     freeId(oid);
4716 }
4717 
freePage(oid_t oid)4718 void dbDatabase::freePage(oid_t oid)
4719 {
4720     offs_t pos = getPos(oid);
4721     if (pos & dbModifiedFlag) {
4722         free(pos & ~dbFlagsMask, dbPageSize);
4723     } else {
4724         cloneBitmap(pos & ~dbFlagsMask, dbPageSize);
4725     }
4726     freeId(oid);
4727 }
4728 
4729 
update(oid_t oid,dbTableDescriptor * desc,void const * record)4730 bool dbDatabase::update(oid_t oid, dbTableDescriptor* desc, void const* record)
4731 {
4732     beginTransaction(dbExclusiveLock);
4733     modified = true;
4734 
4735     size_t size = desc->columns->calculateRecordSize((byte*)record, desc->fixedSize);
4736 
4737     byte* src = (byte*)record;
4738     dbGetTie getTie;
4739     byte* old = (byte*)getRow(getTie, oid);
4740     desc->columns->markUpdatedFields(old, src);
4741 
4742     dbFieldDescriptor* fd;
4743     dbSmallBuffer<char> buf(size);
4744     byte* updated = (byte*)buf.base();
4745     desc->columns->storeRecordFields(updated, src, desc->fixedSize, dbFieldDescriptor::Update);
4746 
4747     if (logger != NULL) {
4748         ((dbRecord*)updated)->size = size;
4749         if (!logger->update(desc, oid, (dbRecord*)updated, record)) {
4750             return false;
4751         }
4752     }
4753 
4754     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4755         if (fd->attr & (dbFieldDescriptor::Updated)
4756             && (fd->indexType & UNIQUE) != 0
4757             && fd->type != dbField::tpRectangle)
4758         {
4759             dbBtree::remove(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
4760             if (!dbBtree::insert(this, fd->bTree, oid, updated, fd->dbsOffs, fd->comparator)) {
4761                 dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
4762                 for (dbFieldDescriptor* fdu = desc->indexedFields; fdu != fd; fdu = fdu->nextIndexedField) {
4763                     if ((fdu->attr & dbFieldDescriptor::Updated) != 0
4764                         && (fdu->indexType & UNIQUE) != 0
4765                         && fdu->type != dbField::tpRectangle)
4766                     {
4767                         dbBtree::remove(this, fdu->bTree, oid, updated, fdu->dbsOffs, fdu->comparator);
4768                         dbBtree::insert(this, fdu->bTree, oid, fdu->dbsOffs, fdu->comparator);
4769                     }
4770                 }
4771                 for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4772                     fd->attr &= ~dbFieldDescriptor::Updated;
4773                 }
4774                 for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
4775                     fd->attr &= ~dbFieldDescriptor::Updated;
4776                 }
4777                 return false;
4778             }
4779         }
4780     }
4781 
4782     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
4783         if (fd->attr & dbFieldDescriptor::Updated) {
4784             dbHashTable::remove(this, fd->hashTable, oid, fd->type,fd->dbsOffs);
4785         }
4786     }
4787     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4788         if (fd->attr & dbFieldDescriptor::Updated) {
4789             if (fd->type == dbField::tpRectangle) {
4790                 dbRtree::remove(this, fd->bTree, oid, fd->dbsOffs);
4791             } else if ((fd->indexType & UNIQUE) == 0) {
4792                 dbBtree::remove(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
4793             }
4794         }
4795     }
4796 
4797     if (inverseReferencesUpdate) {
4798         updatedRecordId = oid;
4799         for (fd = desc->inverseFields; fd != NULL; fd = fd->nextInverseField) {
4800             if (fd->type == dbField::tpArray) {
4801                 dbAnyArray* arr = (dbAnyArray*)(src + fd->appOffs);
4802                 int n = (int)arr->length();
4803                 oid_t* newrefs = (oid_t*)arr->base();
4804 
4805                 int m = ((dbVarying*)(old + fd->dbsOffs))->size;
4806                 int offs =  ((dbVarying*)(old + fd->dbsOffs))->offs;
4807                 int i, j, k;
4808 
4809                 if (fd->indexType & DB_FIELD_CASCADE_DELETE) {
4810                     for (i = 0, k = 0; i < m; i++) {
4811                         oid_t oldref = *(oid_t*)(old + offs);
4812                         offs += sizeof(oid_t);
4813                         for (j = i; j < n && newrefs[j] != oldref; j++);
4814                         if (j >= n) {
4815                             j = i < n ? i : n;
4816                             while (--j >= 0 && newrefs[j] != oldref);
4817                             if (j < 0) {
4818                                 k += 1;
4819                                 removeInverseReference(fd, oid, oldref);
4820                             }
4821                         }
4822                     }
4823                     if (n != m - k) {
4824                         oid_t* oldrefs = (oid_t*)(old + offs) - m;
4825                         for (i = 0; i < n; i++) {
4826                             for (j = 0; j < m && newrefs[i] != oldrefs[j]; j++);
4827                             if (j == m) {
4828                                 insertInverseReference(fd, oid, newrefs[i]);
4829                             }
4830                         }
4831                     }
4832                 } else {
4833                     k = n < m ? n : m;
4834                     for (i = 0; i < k; i++) {
4835                         oid_t oldref = *(oid_t*)(old + offs);
4836                         offs += sizeof(oid_t);
4837                         if (newrefs[i] != oldref) {
4838                             if (oldref != 0) {
4839                                 removeInverseReference(fd, oid, oldref);
4840                             }
4841                             if (newrefs[i] != 0) {
4842                                 insertInverseReference(fd, oid, newrefs[i]);
4843                             }
4844                         }
4845                     }
4846                     while (i < m) {
4847                         oid_t oldref = *(oid_t*)(old + offs);
4848                         offs += sizeof(oid_t);
4849                         if (oldref != 0) {
4850                             removeInverseReference(fd, oid, oldref);
4851                         }
4852                         i += 1;
4853                     }
4854                     while (i < n) {
4855                         if (newrefs[i] != 0) {
4856                             insertInverseReference(fd, oid, newrefs[i]);
4857                         }
4858                         i += 1;
4859                     }
4860                 }
4861             } else {
4862                 oid_t newref = *(oid_t*)(src + fd->appOffs);
4863                 oid_t oldref = *(oid_t*)(old + fd->dbsOffs);
4864                 if (newref != oldref) {
4865                     if (oldref != 0) {
4866                         removeInverseReference(fd, oid, oldref);
4867                     }
4868                     if (newref != 0 && !(fd->indexType & DB_BLOB_CASCADE_DELETE)) {
4869                         insertInverseReference(fd, oid, newref);
4870                     }
4871                 }
4872             }
4873         }
4874         updatedRecordId = 0;
4875     }
4876     {
4877         dbPutTie putTie(true);
4878         byte* dst = (byte*)putRow(putTie, oid, size);
4879         memcpy(dst+sizeof(dbRecord), updated+sizeof(dbRecord), size-sizeof(dbRecord));
4880     }
4881 
4882     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
4883         if (fd->attr & dbFieldDescriptor::Updated) {
4884             dbHashTable::insert(this, fd->hashTable, oid, fd->type,fd->dbsOffs,0);
4885         }
4886     }
4887     for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4888         if (fd->attr & dbFieldDescriptor::Updated) {
4889             fd->attr &= ~dbFieldDescriptor::Updated;
4890             if (fd->type == dbField::tpRectangle) {
4891                 dbRtree::insert(this, fd->bTree, oid, fd->dbsOffs);
4892             } else if ((fd->indexType & UNIQUE) == 0) {
4893                 dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
4894             }
4895         }
4896     }
4897     for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
4898         fd->attr &= ~dbFieldDescriptor::Updated;
4899     }
4900     updateCursors(oid);
4901     return true;
4902 }
4903 
4904 
insertRecord(dbTableDescriptor * desc,dbAnyReference * ref,void const * record,bool batch)4905 bool dbDatabase::insertRecord(dbTableDescriptor* desc, dbAnyReference* ref,
4906                               void const* record, bool batch)
4907 {
4908     assert(opened);
4909     beginTransaction(dbExclusiveLock);
4910     refreshTable(desc);
4911     modified = true;
4912     byte* src = (byte*)record;
4913     size_t size = desc->columns->calculateRecordSize(src, desc->fixedSize);
4914     dbFieldDescriptor* fd;
4915 
4916     oid_t oid = allocateRow(desc->tableId, size, desc);
4917     {
4918         dbPutTie tie;
4919         byte* dst = (byte*)putRow(tie, oid);
4920         desc->columns->storeRecordFields(dst, src, desc->fixedSize, dbFieldDescriptor::Insert);
4921         if (logger != NULL) {
4922             if (!logger->insert(desc, oid, (dbRecord*)dst, record)) {
4923                 freeRow(desc->tableId, oid, desc);
4924                 return false;
4925             }
4926         }
4927     }
4928 
4929     if (batch) {
4930         if (!desc->isInBatch) {
4931             desc->isInBatch = true;
4932             desc->nextBatch = batchList;
4933             batchList = desc;
4934             desc->batch.reset();
4935         }
4936         desc->batch.add(oid);
4937     } else {
4938         for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4939             if ((fd->indexType & UNIQUE) != 0 && fd->type != dbField::tpRectangle) {
4940                 if (!dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator)) {
4941                     for (dbFieldDescriptor* fdu = desc->indexedFields; fdu != fd; fdu = fdu->nextIndexedField) {
4942                         if ((fdu->indexType & UNIQUE) != 0 && fdu->type != dbField::tpRectangle) {
4943                             dbBtree::remove(this, fdu->bTree, oid, fdu->dbsOffs, fdu->comparator);
4944                         }
4945                     }
4946                     freeRow(desc->tableId, oid, desc);
4947                     return false;
4948                 }
4949             }
4950         }
4951 
4952         size_t nRows = desc->nRows;
4953         for (fd = desc->hashedFields; fd != NULL; fd = fd->nextHashedField) {
4954             dbHashTable::insert(this, fd->hashTable, oid, fd->type, fd->dbsOffs, nRows);
4955         }
4956         for (fd = desc->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
4957             if (fd->type == dbField::tpRectangle) {
4958                 dbRtree::insert(this, fd->bTree, oid, fd->dbsOffs);
4959             } else if ((fd->indexType & UNIQUE) == 0) {
4960                 dbBtree::insert(this, fd->bTree, oid, fd->dbsOffs, fd->comparator);
4961             }
4962         }
4963     }
4964 
4965     if (inverseReferencesUpdate) {
4966         for (fd = desc->inverseFields; fd != NULL; fd = fd->nextInverseField) {
4967             if (fd->type == dbField::tpArray) {
4968                 dbAnyArray* arr = (dbAnyArray*)(src + fd->appOffs);
4969                 int n = (int)arr->length();
4970                 oid_t* refs = (oid_t*)arr->base();
4971                 while (--n >= 0) {
4972                     if (refs[n] != 0) {
4973                         insertInverseReference(fd, oid, refs[n]);
4974                     }
4975                 }
4976             } else if (!(fd->indexType & DB_BLOB_CASCADE_DELETE)) {
4977                 oid_t ref = *(oid_t*)(src + fd->appOffs);
4978                 if (ref != 0) {
4979                     insertInverseReference(fd, oid, ref);
4980                 }
4981             }
4982         }
4983     }
4984     ref->oid = oid;
4985     return true;
4986 }
4987 
4988 
extend(offs_t size)4989 inline void dbDatabase::extend(offs_t size)
4990 {
4991     if (dbFileSizeLimit != 0 && size > dbFileSizeLimit) {
4992         handleError(FileLimitExeeded);
4993     }
4994     if (size > header->root[1-curr].size) {
4995         if (dbFileExtensionQuantum != 0
4996             && DOALIGN(size, dbFileExtensionQuantum)
4997                != DOALIGN(header->root[1-curr].size, dbFileExtensionQuantum))
4998         {
4999             if (file->setSize(DOALIGN(size, dbFileExtensionQuantum)) != dbFile::ok) {
5000                 handleError(FileError);
5001             }
5002         }
5003         header->root[1-curr].size = size;
5004     }
5005 }
5006 
used()5007 offs_t dbDatabase::used() {
5008     oid_t lastPage = header->root[1-curr].bitmapEnd;
5009     offs_t setbits = 0;
5010     for (oid_t page = dbBitmapId; page < lastPage; page++) {
5011         byte* p = get(page);
5012         for (size_t i = 0; i < dbPageSize; i++) {
5013             byte mask = p[i];
5014             while (mask != 0) {
5015                 if (mask & 1) {
5016                     setbits += 1;
5017                 }
5018                 mask >>= 1;
5019             }
5020         }
5021         pool.unfix(p);
5022     }
5023     return setbits*dbAllocationQuantum;
5024 }
5025 
5026 
wasReserved(offs_t pos,offs_t size)5027 inline bool dbDatabase::wasReserved(offs_t pos, offs_t size)
5028 {
5029     for (dbLocation* location = reservedChain; location != NULL; location = location->next) {
5030         if (pos - location->pos < location->size || location->pos - pos < size) {
5031             return true;
5032         }
5033     }
5034     return false;
5035 }
5036 
dbLocation(dbDatabase * dbs,offs_t locPos,offs_t locSize)5037 inline dbDatabase::dbLocation::dbLocation(dbDatabase* dbs, offs_t locPos, offs_t locSize)
5038   : pos(locPos), size(locSize), next(dbs->reservedChain), db(dbs)
5039 {
5040     db->reservedChain = this;
5041 }
5042 
~dbLocation()5043 inline dbDatabase::dbLocation::~dbLocation()
5044 {
5045     assert(db->reservedChain == this);
5046     db->reservedChain = next;
5047 }
5048 
createCluster(offs_t size)5049 void dbDatabase::createCluster(offs_t size)
5050 {
5051     offs_t addr = allocate(size, 0);
5052     free(addr, size);
5053     offs_t quantNo = addr / dbAllocationQuantum;
5054     currRBitmapPage = dbBitmapId + oid_t(quantNo / (dbPageSize*8));
5055     currRBitmapOffs = (size_t(quantNo) & (dbPageSize*8-1)) >> 3;
5056 }
5057 
ilog2(offs_t val)5058 inline int ilog2(offs_t val)
5059 {
5060     int    log;
5061     size_t pow;
5062     for (log = dbAllocationQuantumBits, pow = dbAllocationQuantum; pow <= val; pow <<= 1, log += 1);
5063     return log-1;
5064 }
5065 
getMemoryStatistic(dbMemoryStatistic & stat)5066 void dbDatabase::getMemoryStatistic(dbMemoryStatistic& stat)
5067 {
5068     stat.free = 0;
5069     stat.used = 0;
5070     stat.nHoles = 0;
5071     stat.minHoleSize = (offs_t)header->root[1-curr].size;
5072     stat.maxHoleSize = 0;
5073     for (int l = 0; l < dbDatabaseOffsetBits; l++) {
5074         stat.nHolesOfSize[l] = 0;
5075     }
5076     size_t holeSize = 0;
5077     oid_t lastPage = header->root[1-curr].bitmapEnd;
5078 
5079     for (oid_t i = dbBitmapId; i < lastPage; i++){
5080         register byte* bitmap = get(i);
5081         for (size_t j = 0; j < dbPageSize; j++) {
5082             unsigned mask = bitmap[j];
5083             int count = 0;
5084             while (mask != 0) {
5085                 while ((mask & 1) == 0) {
5086                     holeSize += 1;
5087                     mask >>= 1;
5088                     count += 1;
5089                 }
5090                 if (holeSize > 0) {
5091                     size_t size = holeSize << dbAllocationQuantumBits;
5092                     if (size > stat.maxHoleSize) {
5093                         stat.maxHoleSize = (offs_t)size;
5094                     }
5095                     if (size < stat.minHoleSize) {
5096                         stat.minHoleSize = (offs_t)size;
5097                     }
5098                     stat.nHolesOfSize[ilog2((offs_t)size)] += 1;
5099                     stat.free += (offs_t)size;
5100                     stat.nHoles += 1;
5101                     holeSize = 0;
5102                 }
5103                 while ((mask & 1) != 0) {
5104                     stat.used += dbAllocationQuantum;
5105                     count += 1;
5106                     mask >>= 1;
5107                 }
5108             }
5109             holeSize += 8 - count;
5110         }
5111         pool.unfix(bitmap);
5112     }
5113     if (holeSize > 0) {
5114         size_t size = holeSize << dbAllocationQuantumBits;
5115         if (size > stat.maxHoleSize) {
5116             stat.maxHoleSize = (offs_t)size;
5117         }
5118         if (size < stat.minHoleSize) {
5119             stat.minHoleSize = (offs_t)size;
5120         }
5121         stat.nHolesOfSize[ilog2((offs_t)size)] += 1;
5122         stat.free += (offs_t)size;
5123         stat.nHoles += 1;
5124     }
5125 }
5126 
allocate(offs_t size,oid_t oid)5127 offs_t dbDatabase::allocate(offs_t size, oid_t oid)
5128 {
5129     static byte const firstHoleSize [] = {
5130         8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5131         5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5132         6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5133         5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5134         7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5135         5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5136         6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5137         5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
5138     };
5139     static byte const lastHoleSize [] = {
5140         8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
5141         2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
5142         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
5143         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
5144         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
5145         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
5146         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
5147         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5148     };
5149     static byte const maxHoleSize [] = {
5150         8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
5151         5,4,3,3,2,2,2,2,3,2,2,2,2,2,2,2,4,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,
5152         6,5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
5153         5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
5154         7,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3,4,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,
5155         5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
5156         6,5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
5157         5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,0
5158     };
5159     static byte const maxHoleOffset [] = {
5160         0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,0,1,5,5,5,5,5,5,0,5,5,5,5,5,5,5,
5161         0,1,2,2,0,3,3,3,0,1,6,6,0,6,6,6,0,1,2,2,0,6,6,6,0,1,6,6,0,6,6,6,
5162         0,1,2,2,3,3,3,3,0,1,4,4,0,4,4,4,0,1,2,2,0,1,0,3,0,1,0,2,0,1,0,5,
5163         0,1,2,2,0,3,3,3,0,1,0,2,0,1,0,4,0,1,2,2,0,1,0,3,0,1,0,2,0,1,0,7,
5164         0,1,2,2,3,3,3,3,0,4,4,4,4,4,4,4,0,1,2,2,0,5,5,5,0,1,5,5,0,5,5,5,
5165         0,1,2,2,0,3,3,3,0,1,0,2,0,1,0,4,0,1,2,2,0,1,0,3,0,1,0,2,0,1,0,6,
5166         0,1,2,2,3,3,3,3,0,1,4,4,0,4,4,4,0,1,2,2,0,1,0,3,0,1,0,2,0,1,0,5,
5167         0,1,2,2,0,3,3,3,0,1,0,2,0,1,0,4,0,1,2,2,0,1,0,3,0,1,0,2,0,1,0,0
5168     };
5169 
5170     assert(size != 0);
5171 
5172     size = DOALIGN(size, (offs_t)dbAllocationQuantum);
5173     int objBitSize = (int)(size >> dbAllocationQuantumBits);
5174     offs_t pos;
5175     int holeBitSize = 0;
5176     register int alignment = (int)size & (dbPageSize-1);
5177     register size_t offs;
5178     const int pageBits = dbPageSize*8;
5179     oid_t firstPage, lastPage;
5180     int   holeBeforeFreePage  = 0;
5181     oid_t freeBitmapPage = 0;
5182     dbPutTie   tie;
5183     oid_t i;
5184     const size_t inc = dbPageSize/dbAllocationQuantum/8;
5185 
5186     setDirty();
5187 
5188     lastPage = header->root[1-curr].bitmapEnd;
5189     allocatedSize += size;
5190     if (alignment == 0) {
5191         firstPage = currPBitmapPage;
5192         offs = DOALIGN(currPBitmapOffs, inc);
5193     } else {
5194         firstPage = currRBitmapPage;
5195         offs = currRBitmapOffs;
5196     }
5197 
5198 
5199     while (true) {
5200         if (alignment == 0) {
5201             // allocate page object
5202             for (i = firstPage; i < lastPage; i++){
5203                 int spaceNeeded = objBitSize - holeBitSize < pageBits
5204                     ? objBitSize - holeBitSize : pageBits;
5205                 if (bitmapPageAvailableSpace[i] <= spaceNeeded) {
5206                     holeBitSize = 0;
5207                     offs = 0;
5208                     continue;
5209                 }
5210                 register byte* begin = get(i);
5211                 size_t startOffs = offs;
5212                 while (offs < dbPageSize) {
5213                     if (begin[offs++] != 0) {
5214                         offs = DOALIGN(offs, inc);
5215                         holeBitSize = 0;
5216                     } else if ((holeBitSize += 8) == objBitSize) {
5217                         pos = (offs_t)(((offs_t(i-dbBitmapId)*dbPageSize + offs)*8 - holeBitSize) << dbAllocationQuantumBits);
5218                         if (wasReserved(pos, size)) {
5219                             offs += objBitSize >> 3;
5220                             startOffs = offs = DOALIGN(offs, inc);
5221                             holeBitSize = 0;
5222                             continue;
5223                         }
5224                         dbLocation location(this, pos, size);
5225                         currPBitmapPage = i;
5226                         currPBitmapOffs = offs;
5227                         extend(pos + size);
5228                         if (oid != 0) {
5229                             offs_t prev = getPos(oid);
5230                             int marker = (int)prev & dbFlagsMask;
5231                             pool.copy(pos, prev - marker, size);
5232                             setPos(oid, pos | marker | dbModifiedFlag);
5233                         }
5234                         pool.unfix(begin);
5235                         begin = put(tie, i);
5236                         size_t holeBytes = holeBitSize >> 3;
5237                         if (holeBytes > offs) {
5238                             memset(begin, 0xFF, offs);
5239                             holeBytes -= offs;
5240                             begin = put(tie, --i);
5241                             offs = dbPageSize;
5242                         }
5243                         while (holeBytes > dbPageSize) {
5244                             memset(begin, 0xFF, dbPageSize);
5245                             holeBytes -= dbPageSize;
5246                             bitmapPageAvailableSpace[i] = 0;
5247                             begin = put(tie, --i);
5248                         }
5249                         memset(&begin[offs-holeBytes], 0xFF, holeBytes);
5250                         return pos;
5251                     }
5252                 }
5253                 if (startOffs == 0 && holeBitSize == 0
5254                     && spaceNeeded < bitmapPageAvailableSpace[i])
5255                 {
5256                     bitmapPageAvailableSpace[i] = spaceNeeded;
5257                 }
5258                 offs = 0;
5259                 pool.unfix(begin);
5260             }
5261         } else {
5262             for (i = firstPage; i < lastPage; i++){
5263                 int spaceNeeded = objBitSize - holeBitSize < pageBits
5264                     ? objBitSize - holeBitSize : pageBits;
5265                 if (bitmapPageAvailableSpace[i] <= spaceNeeded) {
5266                     holeBitSize = 0;
5267                     offs = 0;
5268                     continue;
5269                 }
5270                 register byte* begin = get(i);
5271                 size_t startOffs = offs;
5272 
5273                 while (offs < dbPageSize) {
5274                     int mask = begin[offs];
5275                     if (holeBitSize + firstHoleSize[mask] >= objBitSize) {
5276                         pos = (offs_t)(((offs_t(i-dbBitmapId)*dbPageSize + offs)*8 - holeBitSize) << dbAllocationQuantumBits);
5277                         if (wasReserved(pos, size)) {
5278                             startOffs = offs += (objBitSize + 7) >> 3;
5279                             holeBitSize = 0;
5280                             continue;
5281                         }
5282                         dbLocation location(this, pos, size);
5283                         currRBitmapPage = i;
5284                         currRBitmapOffs = offs;
5285                         extend(pos + size);
5286                         if (oid != 0) {
5287                             offs_t prev = getPos(oid);
5288                             int marker = (int)prev & dbFlagsMask;
5289                             pool.copy(pos, prev - marker, size);
5290                             setPos(oid, pos | marker | dbModifiedFlag);
5291                         }
5292                         pool.unfix(begin);
5293                         begin = put(tie, i);
5294                         begin[offs] |= (1 << (objBitSize - holeBitSize)) - 1;
5295                         if (holeBitSize != 0) {
5296                             if (size_t(holeBitSize) > offs*8) {
5297                                 memset(begin, 0xFF, offs);
5298                                 holeBitSize -= (int)(offs*8);
5299                                 begin = put(tie, --i);
5300                                 offs = dbPageSize;
5301                             }
5302                             while (holeBitSize > pageBits) {
5303                                 memset(begin, 0xFF, dbPageSize);
5304                                 holeBitSize -= pageBits;
5305                                 bitmapPageAvailableSpace[i] = 0;
5306                                 begin = put(tie, --i);
5307                             }
5308                             while ((holeBitSize -= 8) > 0) {
5309                                 begin[--offs] = 0xFF;
5310                             }
5311                             begin[offs-1] |= ~((1 << -holeBitSize) - 1);
5312                         }
5313                         return pos;
5314                     } else if (maxHoleSize[mask] >= objBitSize) {
5315                         int holeBitOffset = maxHoleOffset[mask];
5316                         pos = (offs_t)(((offs_t(i-dbBitmapId)*dbPageSize + offs)*8 +
5317 								       holeBitOffset) << dbAllocationQuantumBits);
5318                         if (wasReserved(pos, size)) {
5319                             startOffs = offs += (objBitSize + 7) >> 3;
5320                             holeBitSize = 0;
5321                             continue;
5322                         }
5323                         dbLocation location(this, pos, size);
5324                         currRBitmapPage = i;
5325                         currRBitmapOffs = offs;
5326                         extend(pos + size);
5327                         if (oid != 0) {
5328                             offs_t prev = getPos(oid);
5329                             int marker = (int)prev & dbFlagsMask;
5330                             pool.copy(pos, prev - marker, size);
5331                             setPos(oid, pos | marker | dbModifiedFlag);
5332                         }
5333                         pool.unfix(begin);
5334                         begin = put(tie, i);
5335                         begin[offs] |= ((1<<objBitSize) - 1) << holeBitOffset;
5336                         return pos;
5337                     }
5338                     offs += 1;
5339                     if (lastHoleSize[mask] == 8) {
5340                         holeBitSize += 8;
5341                     } else {
5342                         holeBitSize = lastHoleSize[mask];
5343                     }
5344                 }
5345                 if (startOffs == 0 && holeBitSize == 0
5346                     && spaceNeeded < bitmapPageAvailableSpace[i])
5347                 {
5348                     bitmapPageAvailableSpace[i] = spaceNeeded;
5349                 }
5350                 offs = 0;
5351                 pool.unfix(begin);
5352             }
5353         }
5354         if (firstPage == dbBitmapId) {
5355             if (freeBitmapPage > i) {
5356                 i = freeBitmapPage;
5357                 holeBitSize = holeBeforeFreePage;
5358             }
5359             if (i == dbBitmapId + dbBitmapPages) {
5360                 handleError(OutOfMemoryError, NULL);
5361             }
5362             offs_t extension = (size > extensionQuantum)
5363                              ? size : (offs_t)extensionQuantum;
5364             int morePages =
5365                 (int)((extension + dbPageSize*(dbAllocationQuantum*8-1) - 1)
5366                       / (dbPageSize*(dbAllocationQuantum*8-1)));
5367 
5368             if (size_t(i + morePages) > dbBitmapId + dbBitmapPages) {
5369                 morePages =
5370                     (int)((size + dbPageSize*(dbAllocationQuantum*8-1) - 1)
5371                           / (dbPageSize*(dbAllocationQuantum*8-1)));
5372                 if (size_t(i + morePages) > dbBitmapId + dbBitmapPages) {
5373                     handleError(OutOfMemoryError, NULL);
5374                 }
5375             }
5376             objBitSize -= holeBitSize;
5377             int skip = DOALIGN(objBitSize, dbPageSize/dbAllocationQuantum);
5378             pos = (offs_t(i-dbBitmapId)
5379                    << (dbPageBits+dbAllocationQuantumBits+3))
5380                 + ((offs_t)skip << dbAllocationQuantumBits);
5381             extend(pos + (offs_t)morePages*dbPageSize);
5382             size_t len = objBitSize >> 3;
5383             offs_t adr = pos;
5384             byte* p;
5385             while (len >= dbPageSize) {
5386                 p = pool.put(adr);
5387                 memset(p, 0xFF, dbPageSize);
5388                 pool.unfix(p);
5389                 adr += dbPageSize;
5390                 len -= dbPageSize;
5391             }
5392             p = pool.put(adr);
5393             memset(p, 0xFF, len);
5394             p[len] = (1 << (objBitSize&7))-1;
5395             pool.unfix(p);
5396             adr = pos + (skip>>3);
5397             len = morePages * (dbPageSize/dbAllocationQuantum/8);
5398             while (true) {
5399                 int off = (int)adr & (dbPageSize-1);
5400                 p = pool.put(adr - off);
5401                 if (dbPageSize - off >= len) {
5402                     memset(p + off, 0xFF, len);
5403                     pool.unfix(p);
5404                     break;
5405                 } else {
5406                     memset(p + off, 0xFF, dbPageSize - off);
5407                     pool.unfix(p);
5408                     adr += dbPageSize - off;
5409                     len -= dbPageSize - off;
5410                 }
5411             }
5412             oid_t j = i;
5413             while (--morePages >= 0) {
5414                 dirtyPagesMap[size_t(j/dbHandlesPerPage/32)]
5415                     |= 1 << int(j/dbHandlesPerPage & 31);
5416                 setPos(j++, pos | dbPageObjectFlag | dbModifiedFlag);
5417                 pos += dbPageSize;
5418             }
5419             freeBitmapPage = header->root[1-curr].bitmapEnd = j;
5420             j = i + objBitSize / pageBits;
5421             if (alignment != 0) {
5422                 currRBitmapPage = j;
5423                 currRBitmapOffs = 0;
5424             } else {
5425                 currPBitmapPage = j;
5426                 currPBitmapOffs = 0;
5427             }
5428             while (j > i) {
5429                 bitmapPageAvailableSpace[size_t(--j)] = 0;
5430             }
5431 
5432             pos = (offs_t(i-dbBitmapId)*dbPageSize*8 - holeBitSize)
5433                 << dbAllocationQuantumBits;
5434             if (oid != 0) {
5435                 offs_t prev = getPos(oid);
5436                 int marker = (int)prev & dbFlagsMask;
5437                 pool.copy(pos, prev - marker, size);
5438                 setPos(oid, pos | marker | dbModifiedFlag);
5439             }
5440             if (holeBitSize != 0) {
5441                 dbLocation location(this, pos, size);
5442                 while (holeBitSize > pageBits) {
5443                     holeBitSize -= pageBits;
5444                     byte* p = put(tie, --i);
5445                     memset(p, 0xFF, dbPageSize);
5446                     bitmapPageAvailableSpace[i] = 0;
5447                 }
5448                 byte* cur = (byte*)put(tie, --i) + dbPageSize;
5449                 while ((holeBitSize -= 8) > 0) {
5450                     *--cur = 0xFF;
5451                 }
5452                 *(cur-1) |= ~((1 << -holeBitSize) - 1);
5453             }
5454             return pos;
5455         }
5456         freeBitmapPage = i;
5457         holeBeforeFreePage = holeBitSize;
5458         holeBitSize = 0;
5459         lastPage = firstPage + 1;
5460         firstPage = dbBitmapId;
5461         offs = 0;
5462     }
5463 }
5464 
free(offs_t pos,offs_t size)5465 void dbDatabase::free(offs_t pos, offs_t size)
5466 {
5467     assert(pos != 0 && (pos & (dbAllocationQuantum-1)) == 0);
5468     dbPutTie tie;
5469     offs_t quantNo = pos / dbAllocationQuantum;
5470     int    objBitSize = (int)((size+dbAllocationQuantum-1) / dbAllocationQuantum);
5471     oid_t  pageId = dbBitmapId + oid_t(quantNo / (dbPageSize*8));
5472     size_t offs = (size_t(quantNo) & (dbPageSize*8-1)) >> 3;
5473     byte*  p = put(tie, pageId) + offs;
5474     int    bitOffs = int(quantNo) & 7;
5475 
5476     allocatedSize -= objBitSize*dbAllocationQuantum;
5477     if ((deallocatedSize += objBitSize*dbAllocationQuantum) >= freeSpaceReuseThreshold)
5478     {
5479         deallocatedSize = 0;
5480         currRBitmapPage = currPBitmapPage = dbBitmapId;
5481         currRBitmapOffs = currPBitmapOffs = 0;
5482     } else {
5483         if ((size_t(pos) & (dbPageSize-1)) == 0 && size >= dbPageSize) {
5484             if (pageId == currPBitmapPage && offs < currPBitmapOffs) {
5485                 currPBitmapOffs = offs;
5486             }
5487         } else {
5488             if (pageId == currRBitmapPage && offs < currRBitmapOffs) {
5489                 currRBitmapOffs = offs;
5490             }
5491         }
5492     }
5493 
5494     bitmapPageAvailableSpace[pageId] = INT_MAX;
5495 
5496     if (objBitSize > 8 - bitOffs) {
5497         objBitSize -= 8 - bitOffs;
5498         *p++ &= (1 << bitOffs) - 1;
5499         offs += 1;
5500         while (objBitSize + offs*8 > dbPageSize*8) {
5501             memset(p, 0, dbPageSize - offs);
5502             p = put(tie, ++pageId);
5503             bitmapPageAvailableSpace[pageId] = INT_MAX;
5504             objBitSize -= (int)((dbPageSize - offs)*8);
5505             offs = 0;
5506         }
5507         while ((objBitSize -= 8) > 0) {
5508             *p++ = 0;
5509         }
5510         *p &= ~((1 << (objBitSize + 8)) - 1);
5511     } else {
5512         *p &= ~(((1 << objBitSize) - 1) << bitOffs);
5513     }
5514 }
5515 
cloneBitmap(offs_t pos,offs_t size)5516 void dbDatabase::cloneBitmap(offs_t pos, offs_t size)
5517 {
5518     offs_t quantNo = pos / dbAllocationQuantum;
5519     int    objBitSize = (int)((size+dbAllocationQuantum-1) / dbAllocationQuantum);
5520     oid_t  pageId = dbBitmapId + oid_t(quantNo / (dbPageSize*8));
5521     size_t offs = (size_t(quantNo) & (dbPageSize*8-1)) >> 3;
5522     int    bitOffs = int(quantNo) & 7;
5523     oid_t  oid = pageId;
5524     pos = getPos(oid);
5525     if (!(pos & dbModifiedFlag)) {
5526         dirtyPagesMap[size_t(oid / dbHandlesPerPage / 32)]
5527             |= 1 << (int(oid / dbHandlesPerPage) & 31);
5528         allocate(dbPageSize, oid);
5529         cloneBitmap(pos & ~dbFlagsMask, dbPageSize);
5530     }
5531 
5532     if (objBitSize > 8 - bitOffs) {
5533         objBitSize -= 8 - bitOffs;
5534         offs += 1;
5535         while (objBitSize + offs*8 > dbPageSize*8) {
5536             oid = ++pageId;
5537             pos = getPos(oid);
5538             if (!(pos & dbModifiedFlag)) {
5539                 dirtyPagesMap[size_t(oid / dbHandlesPerPage / 32)]
5540                     |= 1 << (int(oid / dbHandlesPerPage) & 31);
5541                 allocate(dbPageSize, oid);
5542                 cloneBitmap(pos & ~dbFlagsMask, dbPageSize);
5543             }
5544             objBitSize -= (int)((dbPageSize - offs)*8);
5545             offs = 0;
5546         }
5547     }
5548 }
5549 
5550 
setDirty()5551 void dbDatabase::setDirty()
5552 {
5553     if (!header->dirty) {
5554         if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
5555             handleError(DatabaseReadOnly, "Attempt to modify readonly database");
5556         }
5557         header->dirty = true;
5558         int rc = file->write(0, header, dbPageSize);
5559         if (rc != dbFile::ok) {
5560             handleError(FileError, "Failed to write header to the disk", rc);
5561         }
5562         pool.flush();
5563     }
5564     modified = true;
5565 }
5566 
allocateId()5567 oid_t dbDatabase::allocateId()
5568 {
5569     setDirty();
5570 
5571     oid_t oid;
5572     int curr = 1-this->curr;
5573     if ((oid = header->root[curr].freeList) != 0) {
5574         header->root[curr].freeList = oid_t(getPos(oid) >> dbFlagsBits);
5575         dirtyPagesMap[size_t(oid / dbHandlesPerPage / 32)]
5576             |= 1 << (int(oid / dbHandlesPerPage) & 31);
5577         return oid;
5578     }
5579     if (currIndexSize + 1 > header->root[curr].indexSize) {
5580         oid_t oldIndexSize = header->root[curr].indexSize;
5581         oid_t newIndexSize = oldIndexSize << 1;
5582 
5583         if (newIndexSize < oldIndexSize) {
5584             newIndexSize = (oid_t)-1 & ~(dbHandlesPerPage-1);
5585             if (newIndexSize <= oldIndexSize) {
5586                 handleError(OutOfMemoryError, NULL);
5587             }
5588         }
5589         TRACE_MSG((STRLITERAL("Extend index size from %ld to %ld\n"),
5590                    oldIndexSize, newIndexSize));
5591         offs_t newIndex = allocate((offs_t)newIndexSize * sizeof(offs_t));
5592         offs_t oldIndex = header->root[curr].index;
5593         pool.copy(newIndex, oldIndex, (offs_t)currIndexSize*sizeof(offs_t));
5594         header->root[curr].index = newIndex;
5595         header->root[curr].indexSize = newIndexSize;
5596         free(oldIndex, (offs_t)oldIndexSize*sizeof(offs_t));
5597     }
5598     oid = currIndexSize;
5599     header->root[curr].indexUsed = ++currIndexSize;
5600     return oid;
5601 }
5602 
freeId(oid_t oid)5603 void dbDatabase::freeId(oid_t oid)
5604 {
5605     dirtyPagesMap[size_t(oid / dbHandlesPerPage / 32)]
5606         |= 1 << (int(oid / dbHandlesPerPage) & 31);
5607 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
5608     setPos(oid, (offs_t(header->root[1-curr].sessionFreeList.head) << dbFlagsBits)
5609            | dbFreeHandleFlag);
5610     header->root[1-curr].sessionFreeList.head = oid;
5611     if (header->root[1-curr].sessionFreeList.tail == 0) {
5612         header->root[1-curr].sessionFreeList.tail = oid;
5613     }
5614 #else
5615     setPos(oid, (offs_t(header->root[1-curr].freeList) << dbFlagsBits)
5616            | dbFreeHandleFlag);
5617     header->root[1-curr].freeList = oid;
5618 #endif
5619 }
5620 
5621 
wait(dbLockType type,dbMutex & mutex,dbDatabaseThreadContext * ctx)5622 inline void dbMonitor::wait(dbLockType type, dbMutex& mutex, dbDatabaseThreadContext* ctx)
5623 {
5624     if (lastPending == NULL) {
5625         lastPending = firstPending = ctx;
5626     } else {
5627         lastPending = lastPending->nextPending = ctx;
5628     }
5629     ctx->nextPending = NULL;
5630     ctx->pendingLock = type;
5631     ctx->event.reset();
5632     ctx->event.wait(mutex);
5633 }
5634 
beginTransaction(dbLockType type)5635 void dbDatabase::beginTransaction(dbLockType type)
5636 {
5637     dbDatabaseThreadContext* ctx = threadContext.get();
5638 
5639     if (accessType == dbMulticlientReadWrite && type == dbUpdateLock) {
5640         type = dbExclusiveLock;
5641     }
5642     if (ctx->holdLock < type) {
5643         if (commitDelay != 0) {
5644             dbCriticalSection cs(delayedCommitStopTimerMutex);
5645             forceCommitCount += 1;
5646             if (delayedCommitContext == ctx) {
5647                 // skip delayed transaction because this thread is starting new transaction
5648                 delayedCommitContext = NULL;
5649                 ctx->commitDelayed = false;
5650                 if (commitTimerStarted != 0) {
5651 #ifdef _WINCE
5652                     time_t elapsed = GetTickCount() - commitTimerStarted;
5653 #else
5654                     time_t elapsed = time(NULL) - commitTimerStarted;
5655 #endif
5656                     if (commitTimeout < elapsed) {
5657                         commitTimeout = 0;
5658                     } else {
5659                         commitTimeout -= elapsed;
5660                     }
5661                     delayedCommitStopTimerEvent.pulse();
5662                 }
5663             } else {
5664                 if (delayedCommitContext != NULL) {
5665                     // force transaction commit because another thread is going to begin transaction
5666                     delayedCommitStopTimerEvent.pulse();
5667                 }
5668             }
5669         }
5670         mutex.lock();
5671         if (type == dbExclusiveLock) {
5672             assert(accessType != dbMulticlientReadOnly);
5673             if (ctx->holdLock != dbNoLock) {
5674                 assert(accessType != dbMulticlientReadWrite); // lock upgrade is not possible in multiclient mode
5675                 assert(monitor.nWriters == 0);
5676                 if (monitor.nReaders != 1) {
5677                     monitor.nLockUpgrades += 1;
5678                     monitor.wait(dbExclusiveLock, mutex, ctx);
5679                     assert(monitor.nWriters == 1 && monitor.nReaders == 0
5680                            && monitor.accLock == dbExclusiveLock);
5681                 } else {
5682                     monitor.nWriters = 1;
5683                     monitor.nReaders = 0;
5684                     monitor.accLock = dbExclusiveLock;
5685                 }
5686             } else {
5687                 if (monitor.accLock != dbNoLock) {
5688                     monitor.wait(dbExclusiveLock, mutex, ctx);
5689                     assert(monitor.nWriters == 1 && monitor.nReaders == 0 && monitor.accLock == dbExclusiveLock);
5690                 } else {
5691                     assert(monitor.nWriters == 0 && monitor.nReaders == 0);
5692                     monitor.nWriters = 1;
5693                     monitor.accLock = dbExclusiveLock;
5694                 }
5695             }
5696             if (accessType == dbMulticlientReadWrite) {
5697                 //printf("Set exclusive lock\n");
5698                 file->lock(dbFile::lck_exclusive);
5699             }
5700         } else {
5701             if (monitor.accLock > dbSharedLock || monitor.lastPending != NULL) {
5702                 if (ctx->holdLock != dbNoLock) {
5703                     monitor.nLockUpgrades += 1;
5704                 }
5705                 monitor.wait(type, mutex, ctx);
5706                 assert(monitor.nWriters == 0 && monitor.nReaders > 0 && monitor.accLock >= type);
5707             } else {
5708                 monitor.accLock = type;
5709                 if (ctx->holdLock == dbNoLock) {
5710                     monitor.nReaders += 1;
5711                 }
5712             }
5713             if ((accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite)
5714                 && monitor.nReaders == 1)
5715             {
5716                 //printf("Set shared lock\n");
5717                 file->lock(dbFile::lck_shared);
5718             }
5719         }
5720         if (opened && ctx->holdLock == dbNoLock && (accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite)) {
5721             int rc = file->read(0, header, dbPageSize);
5722             if (rc != dbFile::ok) {
5723                 handleError(dbDatabase::FileError, "Failed to read root page", rc);
5724             }
5725             curr = header->curr;
5726             if (header->transactionId != transactionId) {
5727                 for (int i = dbBitmapId + dbBitmapPages; --i >= 0;) {
5728                     bitmapPageAvailableSpace[i] = INT_MAX;
5729                 }
5730                 pool.clear(header->root[1-curr].size);
5731                 transactionId = header->transactionId;
5732             }
5733         }
5734         mutex.unlock();
5735         ctx->holdLock = type;
5736     } else {
5737         return;
5738     }
5739     if (commitDelay != 0) {
5740         dbCriticalSection cs(delayedCommitStopTimerMutex);
5741         forceCommitCount -= 1;
5742     }
5743 
5744     mutex.lock();
5745     int curr = this->curr;
5746     currIndexSize = header->root[1-curr].indexUsed;
5747     committedIndexSize = header->root[curr].indexUsed;
5748     mutex.unlock();
5749     //assert(currIndexSize >= committedIndexSize);
5750 }
5751 
precommit()5752 void dbDatabase::precommit()
5753 {
5754     if (accessType == dbMulticlientReadWrite) {
5755         handleError(OperationNotSupported);
5756     }
5757     dbDatabaseThreadContext* ctx = threadContext.get();
5758     if (ctx != NULL && ctx->holdLock != dbNoLock) {
5759         ctx->concurrentId = concurrentTransId;
5760         endTransaction(ctx);
5761     }
5762 }
5763 
5764 
delayedCommit()5765 void dbDatabase::delayedCommit()
5766 {
5767     dbCriticalSection cs1(delayedCommitStartTimerMutex);
5768     {
5769         dbCriticalSection cs2(commitThreadSyncMutex);
5770         commitThreadSyncEvent.pulse();
5771     }
5772     while (true) {
5773         delayedCommitStartTimerEvent.wait(delayedCommitStartTimerMutex);
5774         if (delayedCommitContext == NULL) {
5775             return;
5776         }
5777         {
5778             dbCriticalSection cs2(delayedCommitStopTimerMutex);
5779             {
5780                 dbCriticalSection cs3(commitThreadSyncMutex);
5781                 commitThreadSyncEvent.pulse();
5782             }
5783             if (forceCommitCount == 0 && monitor.firstPending == NULL) {
5784                 // printf("Delayed transaction: %d\n", commitTimeout);
5785 #ifdef _WINCE
5786                 commitTimerStarted = GetTickCount();
5787 #else
5788                 commitTimerStarted = time(NULL);
5789 #endif
5790                 delayedCommitStopTimerEvent.wait(delayedCommitStopTimerMutex, commitTimeout);
5791             }
5792             // printf("forceCommitCount = %d, monitor.firstPending=%p\n", forceCommitCount, monitor.firstPending);
5793             dbDatabaseThreadContext* ctx = delayedCommitContext;
5794             if (ctx != NULL) {
5795                 commitTimeout = commitDelay;
5796                 delayedCommitContext = NULL;
5797                 threadContext.set(ctx);
5798                 commit(ctx);
5799                 ctx->commitDelayed = false;
5800                 if (ctx->removeContext) {
5801                     dbCriticalSection cs(threadContextListMutex);
5802                     delete ctx;
5803                 }
5804             }
5805         }
5806     }
5807 }
5808 
executeBatch()5809 void dbDatabase::executeBatch()
5810 {
5811     while (batchList != NULL) {
5812         dbTableDescriptor* table = batchList;
5813         dbOrderByNode orderBy;
5814         orderBy.next = NULL;
5815         orderBy.table = table;
5816         orderBy.expr = NULL;
5817         orderBy.ascent = true;
5818         for (dbFieldDescriptor* fd = table->indexedFields; fd != NULL; fd = fd->nextIndexedField) {
5819             if (fd->type == dbField::tpRectangle) {
5820                 dbSelection::segment const* first = &table->batch.first;
5821                 dbSelection::segment const* seg = first;
5822                 do {
5823                     for (int i = 0, n = (int)seg->nRows; i < n; i++) {
5824                         dbRtree::insert(this, fd->bTree, seg->rows[i], fd->dbsOffs);
5825                     }
5826                 } while ((seg = seg->next) != first);
5827             } else {
5828                 dbBtreePage::item ins;
5829                 dbSortResult sortResult;
5830                 orderBy.field = fd;
5831                 table->batch.sort(this, &orderBy, (fd->indexType & CASE_INSENSITIVE) != 0, &sortResult);
5832                 for (int i = 0, n = (int)table->batch.nRows; i < n; i++) {
5833                     ins.oid = sortResult.keys[i].oid;
5834                     ins.keyLen = (int)fd->dbsSize;
5835                     switch (fd->type) {
5836                       case dbField::tpBool:
5837                       case dbField::tpInt1:
5838                         ins.keyInt1 = (int1)sortResult.keys[i].u.intKey;
5839                         break;
5840                       case dbField::tpInt2:
5841                         ins.keyInt2 = (int2)sortResult.keys[i].u.intKey;
5842                         break;
5843                       case dbField::tpInt4:
5844                         ins.keyInt4 = (int4)sortResult.keys[i].u.intKey;
5845                         break;
5846                       case dbField::tpInt8:
5847                         ins.keyInt8 = (db_int8)sortResult.keys[i].u.longKey;
5848                         break;
5849                       case dbField::tpReference:
5850                         ins.keyOid = (oid_t)sortResult.keys[i].u.longKey;
5851                         break;
5852                       case dbField::tpReal4:
5853                         ins.keyReal4 = (real4)sortResult.keys[i].u.realKey;
5854                         break;
5855                       case dbField::tpReal8:
5856                         ins.keyReal8 = sortResult.keys[i].u.realKey;
5857                         break;
5858                       case dbField::tpString:
5859                         ins.keyLen = (int)STRLEN(sortResult.keys[i].u.strKey)+1;
5860                         STRCPY(ins.keyChar, sortResult.keys[i].u.strKey);
5861                         break;
5862                       case dbField::tpRawBinary:
5863                         memcpy(ins.keyChar, sortResult.keys[i].u.rawKey, ins.keyLen);
5864                         break;
5865                       default:
5866                         assert(false);
5867                     }
5868                     if (!dbBtree::insert(this, fd->bTree, ins, fd->comparator)) {
5869                         handleError(UniqueConstraintViolation);
5870                     }
5871                 }
5872             }
5873         }
5874         table->isInBatch = false;
5875         table->batch.reset();
5876         batchList = table->nextBatch;
5877     }
5878 }
5879 
commit()5880 void dbDatabase::commit()
5881 {
5882     dbDatabaseThreadContext* ctx = threadContext.get();
5883 
5884     if (ctx != NULL && !ctx->commitDelayed) {
5885         bool needToCommit;
5886         mutex.lock();
5887         needToCommit = modified && !commitInProgress && (uncommittedChanges || ctx->holdLock == dbExclusiveLock || ctx->concurrentId == concurrentTransId);
5888         mutex.unlock();
5889         if (needToCommit) {
5890             if (ctx->holdLock != dbExclusiveLock) {
5891                 beginTransaction(dbExclusiveLock);
5892             }
5893             if (commitDelay != 0) {
5894                 dbCriticalSection cs1(commitThreadSyncMutex);
5895                 if (monitor.firstPending == NULL) {
5896                     {
5897                         dbCriticalSection cs2(delayedCommitStartTimerMutex);
5898                         delayedCommitContext = ctx;
5899                         ctx->commitDelayed = true;
5900                         delayedCommitStartTimerEvent.pulse();
5901                     }
5902                     commitThreadSyncEvent.wait(commitThreadSyncMutex);
5903                     return;
5904                 }
5905             }
5906             commit(ctx);
5907         } else {
5908             if (ctx->holdLock != dbNoLock) {
5909                 endTransaction(ctx);
5910             }
5911         }
5912     }
5913 }
5914 
5915 
commit(dbDatabaseThreadContext * ctx)5916 void dbDatabase::commit(dbDatabaseThreadContext* ctx)
5917 {
5918     if (accessType == dbReadOnly || accessType == dbMulticlientReadOnly) {
5919         handleError(DatabaseReadOnly, "Attempt to modify readonly database");
5920     }
5921 
5922     executeBatch();
5923 
5924     if (logger != NULL) {
5925         if (!logger->commitPhase1()) {
5926             handleError(RejectedByTransactionLogger);
5927         }
5928     }
5929 
5930     //
5931     // Commit transaction
5932     //
5933     int rc;
5934     int curr = header->curr;
5935     oid_t i, n;
5936     int4* map = dirtyPagesMap;
5937     oid_t currIndexSize = this->currIndexSize;
5938     oid_t committedIndexSize = this->committedIndexSize;
5939     oid_t oldIndexSize = header->root[curr].indexSize;
5940     oid_t newIndexSize = header->root[1-curr].indexSize;
5941     oid_t nPages = committedIndexSize / dbHandlesPerPage;
5942     if (newIndexSize > oldIndexSize) {
5943         offs_t newIndex = allocate((offs_t)newIndexSize*sizeof(offs_t));
5944         header->root[1-curr].shadowIndex = newIndex;
5945         header->root[1-curr].shadowIndexSize = newIndexSize;
5946         cloneBitmap(header->root[curr].index, (offs_t)oldIndexSize*sizeof(offs_t));
5947         free(header->root[curr].index, (offs_t)oldIndexSize*sizeof(offs_t));
5948     }
5949     //
5950     // Enable read access to the database
5951     //
5952     mutex.lock();
5953 
5954     assert(!commitInProgress);
5955     commitInProgress = true;
5956     assert (ctx->holdLock == dbExclusiveLock);
5957     if (accessType != dbMulticlientReadWrite) {
5958         monitor.nWriters -= 1;
5959         monitor.nReaders += 1;
5960         monitor.accLock = dbSharedLock;
5961         ctx->holdLock = dbSharedLock;
5962 
5963         dbDatabaseThreadContext* pendingCtx;
5964         while ((pendingCtx = monitor.firstPending) != NULL
5965                && pendingCtx->pendingLock == dbSharedLock)
5966         {
5967             monitor.firstPending = pendingCtx->nextPending;
5968             if (pendingCtx == monitor.lastPending) {
5969                 monitor.lastPending = NULL;
5970             }
5971             pendingCtx->event.signal();
5972             pendingCtx->pendingLock = dbNoLock;
5973             monitor.nReaders += 1;
5974         }
5975     }
5976     mutex.unlock();
5977 
5978     for (i = 0; i < nPages; i++) {
5979         if (map[size_t(i >> 5)] & (1 << int(i & 31))) {
5980             offs_t* srcIndex =
5981                 (offs_t*)pool.get(header->root[1-curr].index + (offs_t)i*dbPageSize);
5982             offs_t* dstIndex =
5983                 (offs_t*)pool.get(header->root[curr].index + (offs_t)i*dbPageSize);
5984             for (size_t j = 0; j < dbHandlesPerPage; j++) {
5985                 offs_t pos = dstIndex[j];
5986                 if (srcIndex[j] != pos) {
5987                     if (!(pos & dbFreeHandleFlag)) {
5988                         if (pos & dbPageObjectFlag) {
5989                             free(pos & ~dbFlagsMask, dbPageSize);
5990                         } else {
5991                             int offs = (int)pos & (dbPageSize-1);
5992                             dbRecord* rec = (dbRecord*)
5993                                 (pool.get(pos-offs)+(offs & ~dbFlagsMask));
5994                             free(pos, rec->size);
5995                             pool.unfix(rec);
5996                         }
5997                     }
5998                 }
5999             }
6000             pool.unfix(srcIndex);
6001             pool.unfix(dstIndex);
6002         }
6003     }
6004     if ((committedIndexSize % dbHandlesPerPage) != 0
6005         && (map[size_t(i >> 5)] & (1 << int(i & 31))))
6006     {
6007         offs_t* srcIndex =
6008             (offs_t*)pool.get(header->root[1-curr].index + (offs_t)i*dbPageSize);
6009         offs_t* dstIndex =
6010             (offs_t*)pool.get(header->root[curr].index + (offs_t)i*dbPageSize);
6011         n = committedIndexSize % dbHandlesPerPage;
6012         do {
6013             offs_t pos = *dstIndex;
6014             if (*srcIndex != pos) {
6015                 if (!(pos & dbFreeHandleFlag)) {
6016                     if (pos & dbPageObjectFlag) {
6017                         free(pos & ~dbFlagsMask, dbPageSize);
6018                     } else {
6019                         int offs = (int)pos & (dbPageSize-1);
6020                         dbRecord* rec = (dbRecord*)
6021                             (pool.get(pos-offs) + (offs & ~dbFlagsMask));
6022                         free(pos, rec->size);
6023                         pool.unfix(rec);
6024                     }
6025                 }
6026             }
6027             dstIndex += 1;
6028             srcIndex += 1;
6029         } while (--n != 0);
6030 
6031         pool.unfix(srcIndex);
6032         pool.unfix(dstIndex);
6033     }
6034 
6035     for (i = 0; i <= nPages; i++) {
6036         if (map[size_t(i >> 5)] & (1 << int(i & 31))) {
6037             offs_t* p =
6038                 (offs_t*)pool.put(header->root[1-curr].index + (offs_t)i*dbPageSize);
6039             for (size_t j = 0; j < dbHandlesPerPage; j++) {
6040                 p[j] &= ~dbModifiedFlag;
6041             }
6042             pool.unfix(p);
6043         }
6044     }
6045     if (currIndexSize > committedIndexSize) {
6046         offs_t page = (header->root[1-curr].index
6047                        + (offs_t)committedIndexSize*sizeof(offs_t)) & ~((offs_t)dbPageSize-1);
6048         offs_t end = (header->root[1-curr].index + dbPageSize - 1
6049                       + (offs_t)currIndexSize*sizeof(offs_t)) & ~((offs_t)dbPageSize-1);
6050         while (page < end) {
6051             offs_t* p = (offs_t*)pool.put(page);
6052             for (size_t h = 0; h < dbHandlesPerPage; h++) {
6053                 p[h] &= ~dbModifiedFlag;
6054             }
6055             pool.unfix(p);
6056             page += dbPageSize;
6057         }
6058     }
6059 
6060     if ((rc = file->write(0, header, dbPageSize)) != dbFile::ok) {
6061         handleError(FileError, "Failed to write header", rc);
6062     }
6063 
6064     pool.flush();
6065     mutex.lock();
6066     while (monitor.backupInProgress) {
6067         backupCompletedEvent.wait(mutex);
6068     }
6069     assert(header->transactionId == transactionId);
6070     header->transactionId = ++transactionId;
6071     header->curr = curr ^= 1;
6072     mutex.unlock();
6073 
6074     if ((rc = file->write(0, header, dbPageSize)) != dbFile::ok ||
6075         (rc = file->flush()) != dbFile::ok)
6076     {
6077         handleError(FileError, "Failed to flush changes to the disk", rc);
6078     }
6079 
6080     replicatePage(0, header);
6081     header->root[1-curr].size = header->root[curr].size;
6082     header->root[1-curr].indexUsed = currIndexSize;
6083     header->root[1-curr].freeList  = header->root[curr].freeList;
6084     header->root[1-curr].bitmapEnd = header->root[curr].bitmapEnd;
6085 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
6086     header->root[1-curr].sessionFreeList = header->root[curr].sessionFreeList;
6087 #endif
6088 
6089     if (newIndexSize != oldIndexSize) {
6090         header->root[1-curr].index=header->root[curr].shadowIndex;
6091         header->root[1-curr].indexSize=header->root[curr].shadowIndexSize;
6092         header->root[1-curr].shadowIndex=header->root[curr].index;
6093         header->root[1-curr].shadowIndexSize=header->root[curr].indexSize;
6094         pool.copy(header->root[1-curr].index, header->root[curr].index,
6095                   (offs_t)currIndexSize*sizeof(offs_t));
6096         memset(map, 0, (size_t)(4*((currIndexSize+dbHandlesPerPage*32-1)
6097                                    / (dbHandlesPerPage*32))));
6098     } else {
6099         for (i = 0; i < nPages; i++) {
6100             if (map[size_t(i >> 5)] & (1 << int(i & 31))) {
6101                 map[size_t(i >> 5)] -= (1 << int(i & 31));
6102                 pool.copy(header->root[1-curr].index + (offs_t)i*dbPageSize,
6103                           header->root[curr].index + (offs_t)i*dbPageSize,
6104                           dbPageSize);
6105             }
6106         }
6107         if (currIndexSize > i*dbHandlesPerPage &&
6108             ((map[size_t(i >> 5)] & (1 << int(i & 31))) != 0
6109              || currIndexSize != committedIndexSize))
6110         {
6111             pool.copy(header->root[1-curr].index + (offs_t)i*dbPageSize,
6112                       header->root[curr].index + (offs_t)i*dbPageSize,
6113                       sizeof(offs_t)*(offs_t)currIndexSize - (offs_t)i*dbPageSize);
6114             memset(map + size_t(i>>5), 0,
6115                    size_t(((currIndexSize + dbHandlesPerPage*32 - 1)
6116                            / (dbHandlesPerPage*32) - (i>>5))*4));
6117         }
6118     }
6119     mutex.lock();
6120 
6121     if (accessType == dbMulticlientReadWrite) {
6122         pool.flush();
6123         header->dirty = false;
6124         if ((rc = file->write(0, header, dbPageSize)) != dbFile::ok) {
6125             handleError(FileError, "Failed to write header", rc);
6126         }
6127         for (dbTableDescriptor* desc = tables; desc != NULL; desc = desc->nextDbTable) {
6128             if (desc->transactionId == transactionId-1) {
6129                 desc->transactionId = transactionId;
6130             }
6131         }
6132     }
6133     this->curr = curr;
6134     modified = false;
6135     uncommittedChanges = false;
6136     commitInProgress = false;
6137     concurrentTransId += 1;
6138     mutex.unlock();
6139 
6140     if (logger != NULL) {
6141         logger->commitPhase2();
6142     }
6143     if (ctx->holdLock != dbNoLock) {
6144         endTransaction(ctx);
6145     }
6146 }
6147 
rollback()6148 void dbDatabase::rollback()
6149 {
6150     if (logger != NULL) {
6151         logger->rollback();
6152     }
6153     while (batchList != NULL) {
6154         dbTableDescriptor* table = batchList;
6155         table->isInBatch = false;
6156         table->batch.reset();
6157         batchList = table->nextBatch;
6158     }
6159 
6160     dbDatabaseThreadContext* ctx = threadContext.get();
6161     if (commitDelay != 0) {
6162         beginTransaction(dbExclusiveLock);
6163     }
6164     if (modified && (uncommittedChanges || ctx->holdLock == dbExclusiveLock || ctx->concurrentId == concurrentTransId))
6165     {
6166         if (ctx->holdLock != dbExclusiveLock) {
6167             beginTransaction(dbExclusiveLock);
6168         }
6169         int curr = header->curr;
6170         oid_t nPages =
6171             (committedIndexSize + dbHandlesPerPage - 1) / dbHandlesPerPage;
6172         int4 *map = dirtyPagesMap;
6173         if (header->root[1-curr].index != header->root[curr].shadowIndex) {
6174             pool.copy(header->root[curr].shadowIndex, header->root[curr].index,
6175                       (offs_t)dbPageSize*nPages);
6176         } else {
6177             for (oid_t i = 0; i < nPages; i++) {
6178                 if (map[size_t(i >> 5)] & (1 << int(i & 31))) {
6179                     pool.copy(header->root[curr].shadowIndex + (offs_t)i*dbPageSize,
6180                               header->root[curr].index + (offs_t)i*dbPageSize,
6181                               dbPageSize);
6182                 }
6183             }
6184         }
6185         memset(map, 0,
6186                size_t((currIndexSize+dbHandlesPerPage*32-1) / (dbHandlesPerPage*32))*4);
6187         header->root[1-curr].indexSize = header->root[curr].shadowIndexSize;
6188         header->root[1-curr].indexUsed = header->root[curr].indexUsed;
6189         header->root[1-curr].freeList  = header->root[curr].freeList;
6190         header->root[1-curr].index = header->root[curr].shadowIndex;
6191         header->root[1-curr].bitmapEnd = header->root[curr].bitmapEnd;
6192         header->root[1-curr].size = header->root[curr].size;
6193 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
6194         header->root[1-curr].sessionFreeList = header->root[curr].sessionFreeList;
6195 #endif
6196 
6197         currRBitmapPage = currPBitmapPage = dbBitmapId;
6198         currRBitmapOffs = currPBitmapOffs = 0;
6199 
6200         modified = false;
6201         uncommittedChanges = false;
6202         concurrentTransId += 1;
6203 
6204         restoreTablesConsistency();
6205         for (dbTableDescriptor* desc = tables; desc != NULL; desc = desc->nextDbTable)
6206         {
6207             dbTable* table = (dbTable*)get(desc->tableId);
6208             desc->firstRow = table->firstRow;
6209             desc->lastRow = table->lastRow;
6210             desc->nRows = table->nRows;
6211             pool.unfix(table);
6212         }
6213         if (accessType == dbMulticlientReadWrite) {
6214             pool.flush();
6215         }
6216     }
6217     endTransaction(ctx);
6218 }
6219 
updateCursors(oid_t oid,bool removed)6220 void dbDatabase::updateCursors(oid_t oid, bool removed)
6221 {
6222     dbDatabaseThreadContext* ctx = threadContext.get();
6223     if (ctx != NULL) {
6224         for (dbAnyCursor* cursor = (dbAnyCursor*)ctx->cursors.next;
6225              cursor != &ctx->cursors;
6226              cursor = (dbAnyCursor*)cursor->next)
6227         {
6228             if (cursor->currId == oid) {
6229                 if (removed) {
6230                     cursor->currId = 0;
6231                 } else if (cursor->record != NULL/* && !cursor->updateInProgress*/) {
6232                     cursor->fetch();
6233                 }
6234             }
6235         }
6236     }
6237 }
6238 
endTransaction(dbDatabaseThreadContext * ctx)6239 void dbDatabase::endTransaction(dbDatabaseThreadContext* ctx)
6240 {
6241     assert(ctx != NULL);
6242     if (!ctx->commitDelayed) {
6243         while (!ctx->cursors.isEmpty()) {
6244             ((dbAnyCursor*)ctx->cursors.next)->reset();
6245         }
6246     }
6247     if (ctx->holdLock != dbNoLock) {
6248         mutex.lock();
6249         if (ctx->holdLock == dbExclusiveLock) {
6250             monitor.nWriters -= 1;
6251             monitor.accLock = dbNoLock;
6252             assert(monitor.nWriters == 0 && monitor.nReaders == 0);
6253             if (accessType == dbMulticlientReadWrite) {
6254                 file->unlock();
6255                 //printf("Release lock\n");
6256             }
6257         } else {
6258             assert(monitor.nWriters == 0 && monitor.nReaders > 0);
6259             if (--monitor.nReaders == 0) {
6260                 monitor.accLock = dbNoLock;
6261                 if (accessType == dbMulticlientReadOnly || accessType == dbMulticlientReadWrite) {
6262                     file->unlock();
6263                     //printf("Release lock\n");
6264                 }
6265             } else if (ctx->holdLock == dbUpdateLock) {
6266                 monitor.accLock = dbSharedLock;
6267             }
6268         }
6269         ctx->holdLock = dbNoLock;
6270         if (monitor.nReaders == 1 && monitor.nLockUpgrades > 0) {
6271             // some thread having upgrade lock wants to upgrade it to exclusive
6272             dbDatabaseThreadContext **cpp = &monitor.firstPending, *prev = NULL;
6273             while ((ctx = *cpp)->holdLock == dbNoLock) {
6274                 prev = ctx;
6275                 cpp = &prev->nextPending;
6276             }
6277             *cpp = ctx->nextPending;
6278             if (ctx == monitor.lastPending) {
6279                 monitor.lastPending = prev;
6280             }
6281             monitor.nLockUpgrades -= 1;
6282             monitor.accLock = ctx->pendingLock;
6283             if (ctx->pendingLock == dbExclusiveLock) {
6284                 monitor.nWriters = 1;
6285                 monitor.nReaders = 0;
6286             }
6287             ctx->event.signal();
6288             ctx->pendingLock = dbNoLock;
6289         } else {
6290             while ((ctx = monitor.firstPending) != NULL) {
6291                 if (monitor.accLock == dbNoLock
6292                     || (monitor.accLock == dbSharedLock && ctx->pendingLock <= dbUpdateLock))
6293                 {
6294                     monitor.firstPending = ctx->nextPending;
6295                     if (ctx == monitor.lastPending) {
6296                         monitor.lastPending = NULL;
6297                     }
6298                     ctx->event.signal();
6299                     dbLockType lock = ctx->pendingLock;
6300                     ctx->pendingLock = dbNoLock;
6301                     if (lock == dbExclusiveLock) {
6302                         monitor.nWriters = 1;
6303                         monitor.nReaders = 0;
6304                         monitor.accLock = dbExclusiveLock;
6305                         break;
6306                     }
6307                     monitor.nReaders += 1;
6308                     if (lock == dbUpdateLock) {
6309                         monitor.accLock = dbUpdateLock;
6310                         break;
6311                     }
6312                     monitor.accLock = dbSharedLock;
6313                 } else {
6314                     break;
6315                 }
6316             }
6317         }
6318         mutex.unlock();
6319     }
6320 }
6321 
6322 
linkTable(dbTableDescriptor * table,oid_t tableId)6323 void dbDatabase::linkTable(dbTableDescriptor* table, oid_t tableId)
6324 {
6325     assert(((void)"Table can be used only in one database",
6326             table->tableId == 0));
6327     table->db = this;
6328     table->transactionId = transactionId;
6329     table->nextDbTable = tables;
6330     table->tableId = tableId;
6331     table->isInBatch = false;
6332     table->batch.reset();
6333     tables = table;
6334 
6335     size_t h = (size_t)table->name % dbTableHashSize;
6336     table->collisionChain = tableHash[h];
6337     tableHash[h] = table;
6338 }
6339 
unlinkTable(dbTableDescriptor * table)6340 void dbDatabase::unlinkTable(dbTableDescriptor* table)
6341 {
6342     dbTableDescriptor** tpp;
6343     for (tpp = &tables; *tpp != table; tpp = &(*tpp)->nextDbTable);
6344     *tpp = table->nextDbTable;
6345     table->tableId = 0;
6346     table->batch.reset();
6347 
6348     size_t h = (size_t)table->name % dbTableHashSize;
6349     for (tpp = &tableHash[h]; *tpp != table; tpp = &(*tpp)->collisionChain);
6350     *tpp = table->collisionChain;
6351 
6352     if (!table->fixedDatabase) {
6353         table->db = NULL;
6354     }
6355 }
6356 
findTable(char_t const * name)6357 dbTableDescriptor* dbDatabase::findTable(char_t const* name)
6358 {
6359     size_t h = (size_t)name % dbTableHashSize;
6360     for (dbTableDescriptor* desc = tableHash[h]; desc != NULL; desc = desc->collisionChain) {
6361         if (desc->name == name) {
6362             return desc;
6363         }
6364     }
6365     return NULL;
6366 }
6367 
findTableByName(char_t const * name)6368 dbTableDescriptor* dbDatabase::findTableByName(char_t const* name)
6369 {
6370     char_t* sym = (char_t*)name;
6371     dbSymbolTable::add(sym, tkn_ident);
6372     return findTable(sym);
6373 }
6374 
findTableByID(oid_t id)6375 dbTableDescriptor* dbDatabase::findTableByID(oid_t id)
6376 {
6377     for (dbTableDescriptor* desc = tables; desc != NULL; desc = desc->nextDbTable) {
6378         if (desc->tableId == id) {
6379             return desc;
6380         }
6381     }
6382     return NULL;
6383 }
6384 
insertInverseReference(dbFieldDescriptor * fd,oid_t inverseId,oid_t targetId)6385 void dbDatabase::insertInverseReference(dbFieldDescriptor* fd, oid_t inverseId,
6386                                         oid_t targetId)
6387 {
6388     {
6389         dbPutTie putTie(true);
6390         byte buf[1024];
6391         if (inverseId == targetId) {
6392             return;
6393         }
6394         fd = fd->inverseRef;
6395         //printf("Insert inverse reference from object %x too object %x field %s\n", targetId, inverseId, fd->name);
6396         if (fd->type == dbField::tpArray) {
6397             dbTableDescriptor* desc = fd->defTable;
6398             dbGetTie getTie;
6399             dbRecord* rec;
6400             size_t newSize = desc->fixedSize;
6401             if (desc->attr & dbFieldDescriptor::HasArrayOfArrayComponents) {
6402                 rec = getRow(getTie, targetId);
6403             } else {
6404                 rec = getRow(getTie, targetId, newSize);
6405             }
6406             dbVarying* arr = (dbVarying*)((byte*)rec + fd->dbsOffs);
6407             size_t arrSize = arr->size;
6408             size_t arrOffs = arr->offs;
6409             size_t lastOffs = desc->columns->sizeWithoutOneField(fd, (byte*)rec, newSize);
6410             size_t newArrOffs = DOALIGN(newSize, sizeof(oid_t));
6411             size_t oldSize = rec->size;
6412             newSize = newArrOffs + (arrSize + 1)*sizeof(oid_t);
6413             if (newSize > oldSize) {
6414                 newSize = newArrOffs + (arrSize+1)*sizeof(oid_t)*2;
6415             } else {
6416                 newSize = oldSize;
6417                 if (arrOffs == newArrOffs && newArrOffs > lastOffs) {
6418                     offs_t pos = getPos(targetId);
6419                     if (pos & dbModifiedFlag) {
6420                         pos -= dbModifiedFlag;
6421                         offs_t arrHdrPos = pos + fd->dbsOffs;
6422                         int offs = (int)arrHdrPos & (dbPageSize-1);
6423                         byte* p = pool.put(arrHdrPos-offs) + offs;
6424                         ((dbVarying*)p)->size += 1;
6425                         pool.unfix(p);
6426 
6427                         pos += (offs_t)(newArrOffs + arrSize*sizeof(oid_t));
6428                         offs = (int)pos & (dbPageSize-1);
6429                         p = pool.put(pos-offs) + offs;
6430                         *(oid_t*)p = inverseId;
6431                         pool.unfix(p);
6432                         updateCursors(targetId);
6433                         return;
6434                     }
6435                 }
6436             }
6437             if (!(desc->attr & dbFieldDescriptor::HasArrayOfArrayComponents)) {
6438                 rec = getRow(getTie, targetId);
6439             }
6440             byte* dst = (byte*)putRow(putTie, targetId, newSize);
6441             byte* src = (byte*)rec;
6442             byte* tmp = NULL;
6443 
6444             if (dst == src) {
6445                 if (oldSize > sizeof(buf)) {
6446                     src = tmp = dbMalloc(oldSize);
6447                 } else {
6448                     src = buf;
6449                 }
6450                 memcpy(src, rec, oldSize);
6451             }
6452             desc->columns->copyRecordExceptOneField(fd, dst, src, desc->fixedSize);
6453 
6454             arr = (dbVarying*)(dst + fd->dbsOffs);
6455             arr->size = (nat4)arrSize + 1;
6456             arr->offs = (int4)newArrOffs;
6457             memcpy(dst + newArrOffs, src + arrOffs, arrSize*sizeof(oid_t));
6458             *((oid_t*)(dst + newArrOffs) + arrSize) = inverseId;
6459             if (tmp != NULL) {
6460                 dbFree(tmp);
6461             }
6462         } else {
6463             if (fd->indexType & INDEXED) {
6464                 dbBtree::remove(this, fd->bTree, targetId, fd->dbsOffs, fd->comparator);
6465             }
6466             oid_t* rp = (oid_t*)((byte*)putRow(putTie, targetId) + fd->dbsOffs);
6467             oid_t oldRef = *(oid_t*)rp;
6468             if (oldRef != 0) {
6469                 removeInverseReference(fd, targetId, oldRef);
6470             }
6471             *rp = inverseId;
6472 
6473             if (fd->indexType & INDEXED) {
6474                 if (!dbBtree::insert(this, fd->bTree, targetId, fd->dbsOffs, fd->comparator)) {
6475                     handleError(UniqueConstraintViolation);
6476                 }
6477             }
6478         }
6479     }
6480     updateCursors(targetId);
6481 }
6482 
6483 
removeInverseReferences(dbTableDescriptor * desc,oid_t oid)6484 void dbDatabase::removeInverseReferences(dbTableDescriptor* desc, oid_t oid)
6485 {
6486     dbVisitedObject* chain = visitedChain;
6487     dbVisitedObject  vo(oid, chain);
6488     visitedChain = &vo;
6489 
6490     dbFieldDescriptor* fd;
6491     dbGetTie tie;
6492     offs_t pos = getPos(oid);
6493     assert(!(pos & (dbFreeHandleFlag|dbPageObjectFlag)));
6494     tie.set(pool, pos & ~dbFlagsMask);
6495     byte* rec = (byte*)tie.get();
6496 
6497     for (fd = desc->inverseFields; fd != NULL; fd = fd->nextInverseField) {
6498         if (fd->type == dbField::tpArray) {
6499             dbVarying* arr = (dbVarying*)(rec + fd->dbsOffs);
6500             int n = arr->size;
6501             int offs = arr->offs + n*sizeof(oid_t);
6502             while (--n >= 0) {
6503                 offs -= sizeof(oid_t);
6504                 oid_t ref = *(oid_t*)(rec + offs);
6505                 if (ref != 0) {
6506                     removeInverseReference(fd, oid, ref);
6507                 }
6508             }
6509         } else {
6510             oid_t ref = *(oid_t*)(rec + fd->dbsOffs);
6511             if (ref != 0) {
6512                 removeInverseReference(fd, oid, ref);
6513             }
6514         }
6515     }
6516     visitedChain = chain;
6517 }
6518 
6519 
removeInverseReference(dbFieldDescriptor * fd,oid_t inverseId,oid_t targetId)6520 void dbDatabase::removeInverseReference(dbFieldDescriptor* fd,
6521                                         oid_t inverseId,
6522                                         oid_t targetId)
6523 {
6524     if (fd->indexType & DB_BLOB_CASCADE_DELETE) {
6525         dbBlob blob(targetId);
6526         blob.free(*this);
6527         return;
6528     }
6529     if (inverseId == targetId || targetId == updatedRecordId || (getPos(targetId) & dbFreeHandleFlag) != 0)
6530     {
6531         return;
6532     }
6533     for (dbVisitedObject* vo = visitedChain; vo != NULL; vo = vo->next) {
6534         if (vo->oid == targetId) {
6535             return;
6536         }
6537     }
6538     dbPutTie tie(true);
6539     byte* rec = (byte*)putRow(tie, targetId);
6540 
6541     if ((fd->indexType & DB_FIELD_CASCADE_DELETE)
6542         && ((fd->inverseRef->type != dbField::tpArray) ||
6543             ((dbVarying*)(rec + fd->inverseRef->dbsOffs))->size <= 1))
6544     {
6545         tie.unset();
6546         remove(fd->inverseRef->defTable, targetId);
6547         return;
6548     }
6549 
6550     fd = fd->inverseRef;
6551     if (fd->type == dbField::tpArray) {
6552         dbVarying* arr = (dbVarying*)(rec + fd->dbsOffs);
6553         oid_t* p = (oid_t*)(rec + arr->offs);
6554         for (int n = arr->size, i = n; --i >= 0;) {
6555             if (p[i] == inverseId) {
6556                 while (++i < n) {
6557                     p[i-1] = p[i];
6558                 }
6559                 arr->size -= 1;
6560                 break;
6561             }
6562         }
6563     } else {
6564         if (*(oid_t*)(rec + fd->dbsOffs) == inverseId) {
6565             if (fd->indexType & INDEXED) {
6566                 dbBtree::remove(this, fd->bTree, targetId, fd->dbsOffs, fd->comparator);
6567             }
6568 
6569             *(oid_t*)(rec + fd->dbsOffs) = 0;
6570 
6571             if (fd->indexType & INDEXED) {
6572                 if (!dbBtree::insert(this, fd->bTree, targetId, fd->dbsOffs, fd->comparator)) {
6573                     handleError(UniqueConstraintViolation);
6574                 }
6575             }
6576         }
6577     }
6578     updateCursors(targetId);
6579 }
6580 
completeDescriptorsInitialization()6581 bool dbDatabase::completeDescriptorsInitialization()
6582 {
6583     bool result = true;
6584     for (dbTableDescriptor* desc = tables; desc != NULL; desc = desc->nextDbTable) {
6585         dbFieldDescriptor* fd;
6586         for (fd = desc->firstField; fd != NULL; fd = fd->nextField) {
6587             if (fd->refTableName != NULL) {
6588                 fd->refTable = findTable(fd->refTableName);
6589             }
6590         }
6591         result &= desc->checkRelationship();
6592     }
6593     return result;
6594 }
6595 
6596 
restore(char_t const * backupFileName,char_t const * databaseFileName)6597 bool dbDatabase::restore(char_t const* backupFileName,
6598                          char_t const* databaseFileName)
6599 {
6600     dbOSFile bck;
6601     dbOSFile dbf;
6602     int rc;
6603     assert(!opened);
6604     if (bck.open(backupFileName, dbFile::sequential|dbFile::read_only)
6605         != dbFile::ok)
6606     {
6607         TRACE_MSG((STRLITERAL("Failed to open backup file\n")));
6608         return false;
6609     }
6610     void* buf = dbOSFile::allocateBuffer(dbPageSize);
6611     if (*databaseFileName == '@') {
6612 #ifdef UNICODE
6613 #if defined(_WIN32)
6614         FILE* f = _wfopen(databaseFileName+1, _T("r"));
6615 #else
6616         char buf[1024];
6617         wcstombs(buf, databaseFileName+1, sizeof buf);
6618         FILE* f = fopen(buf, "r");
6619 #endif
6620 #else
6621         FILE* f = fopen(databaseFileName+1, "r");
6622 #endif
6623         if (f == NULL) {
6624             TRACE_MSG((STRLITERAL("Failed to open database configuration file\n")));
6625             dbOSFile::deallocateBuffer(buf);
6626             return false;
6627         }
6628         const int maxFileNameLen = 1024;
6629         char_t fileName[maxFileNameLen];
6630         int i, n;
6631         db_int8 size = 0;
6632         for (i=0; (n=FSCANF(f, _T("%s") T_INT8_FORMAT, fileName, &size)) >= 1; i++) {
6633             if ((rc = dbf.open(fileName, dbFile::truncate|dbFile::sequential))
6634                 != dbFile::ok)
6635             {
6636                 TRACE_MSG((STRLITERAL("Failed to open database segment %d '%s': %d\n"),
6637                            i, fileName, rc));
6638                 dbOSFile::deallocateBuffer(buf);
6639                 return false;
6640             }
6641             if (n == 2) {
6642                 while (size != 0) {
6643                     if ((rc = bck.read(buf, dbPageSize)) != dbFile::ok) {
6644                         TRACE_MSG((STRLITERAL("Failed to read page from backup: %d\n"),
6645                                    rc));
6646                         dbOSFile::deallocateBuffer(buf);
6647                         return false;
6648                     }
6649                     if ((rc = dbf.write(buf, dbPageSize)) != dbFile::ok) {
6650                         TRACE_MSG((STRLITERAL("Failed to write restored page: %d\n"), rc));
6651                         dbOSFile::deallocateBuffer(buf);
6652                         return false;
6653                     }
6654                     size -= 1;
6655                 }
6656             } else {
6657                 while ((rc = bck.read(buf, dbPageSize)) == dbFile::ok) {
6658                     if ((rc = dbf.write(buf, dbPageSize)) != dbFile::ok) {
6659                         TRACE_MSG((STRLITERAL("Failed to write restored page: %d\n"), rc));
6660                         dbOSFile::deallocateBuffer(buf);
6661                         return false;
6662                     }
6663                 }
6664                 if (rc != dbFile::eof) {
6665                     TRACE_MSG((STRLITERAL("Failed to read page from backup: %d\n"), rc));
6666                     dbOSFile::deallocateBuffer(buf);
6667                     return false;
6668                 }
6669             }
6670             dbf.close();
6671             size = 0;
6672         }
6673         fclose(f);
6674     } else {
6675         if ((rc = dbf.open(databaseFileName,
6676                            dbFile::sequential|dbFile::sequential))
6677             != dbFile::ok)
6678         {
6679             TRACE_MSG((STRLITERAL("Failed to open database file '%s': %d\n"),
6680                        databaseFileName, rc));
6681             dbOSFile::deallocateBuffer(buf);
6682             return false;
6683         }
6684         while ((rc = bck.read(buf, dbPageSize)) == dbFile::ok) {
6685             if ((rc = dbf.write(buf, dbPageSize)) != dbFile::ok) {
6686                 TRACE_MSG((STRLITERAL("Failed to write restored page: %d\n"), rc));
6687                 dbOSFile::deallocateBuffer(buf);
6688                 return false;
6689             }
6690         }
6691         if (rc != dbFile::eof) {
6692             TRACE_MSG((STRLITERAL("Failed to read page from backup: %d\n"), rc));
6693             dbOSFile::deallocateBuffer(buf);
6694             return false;
6695         }
6696         dbf.close();
6697     }
6698     bck.close();
6699     dbOSFile::deallocateBuffer(buf);
6700     return true;
6701 }
6702 
6703 struct dbObjectHdr {
6704     offs_t offs;
6705     oid_t  oid;
6706 };
6707 
compareOffs(void const * a,void const * b)6708 static int __cdecl compareOffs(void const* a, void const* b)
6709 {
6710     return ((dbObjectHdr*)a)->offs < ((dbObjectHdr*)b)->offs ? -1
6711         : ((dbObjectHdr*)a)->offs == ((dbObjectHdr*)b)->offs ? 0 : 1;
6712 }
6713 
backup(char_t const * fileName,int flags)6714 bool dbDatabase::backup(char_t const* fileName, int flags)
6715 {
6716     assert(opened);
6717     dbOSFile f;
6718     if (f.open(fileName, dbFile::sequential|dbFile::truncate) != dbFile::ok) {
6719         return false;
6720     }
6721     bool result = backup(&f, flags);
6722     f.close();
6723     return result;
6724 }
6725 
6726 
backup(dbOSFile * f,int flags)6727 bool dbDatabase::backup(dbOSFile* f, int flags)
6728 {
6729     int rc = dbFile::ok;
6730     mutex.lock();
6731     if (monitor.backupInProgress || dirtyPageBitmap != NULL) {
6732         mutex.unlock();
6733         return false; // no two concurrent backups are possible
6734     }
6735     backupCompletedEvent.reset();
6736     if (flags & BCK_INCREMENTAL) {
6737         if (flags & BCK_COMPACTIFY) {
6738             monitor.backupInProgress = true;
6739         } else {
6740             dirtyPageBitmapSize = (size_t)((header->root[curr].size  + dbPageSize - 1) / dbPageSize);
6741             dirtyPageBitmap = new int[(dirtyPageBitmapSize + 31) >> 5];
6742             memset(dirtyPageBitmap, 0, (dirtyPageBitmapSize + 31) >> 5);
6743         }
6744     } else {
6745         monitor.backupInProgress = true;
6746     }
6747     mutex.unlock();
6748 
6749     if ((flags & BCK_COMPACTIFY)
6750         && header->root[1-curr].indexUsed*sizeof(offs_t) == (size_t)header->root[1-curr].indexUsed*sizeof(offs_t))
6751     {
6752         oid_t   nObjects = (oid_t)header->root[1-curr].indexUsed;
6753         offs_t  indexOffs = header->root[1-curr].index;
6754         oid_t   i, j, k;
6755         oid_t   nUsedIndexPages = (nObjects + dbHandlesPerPage - 1) / dbHandlesPerPage;
6756         size_t  nIndexPages = (size_t)((header->root[1-curr].indexSize + dbHandlesPerPage - 1) / dbHandlesPerPage);
6757         offs_t  totalRecordsSize = 0;
6758         int     nPagedObjects = 0;
6759         offs_t* newIndex = new offs_t[nIndexPages*dbHandlesPerPage];
6760 
6761         memset(newIndex, 0, nIndexPages*dbPageSize);
6762         dbObjectHdr* oldIndex = new dbObjectHdr[(size_t)nObjects];
6763 
6764         for (i = 0, j = 0; i < nUsedIndexPages; i++) {
6765             offs_t* pg = (offs_t*)pool.get(indexOffs+i*dbPageSize);
6766             for (k = 0; k < dbHandlesPerPage && j < nObjects; k++, j++) {
6767                 offs_t offs = pg[k];
6768                 oldIndex[j].offs = offs;
6769                 oldIndex[j].oid = j;
6770                 if (!(offs & dbFreeHandleFlag)) {
6771                     if (offs & dbPageObjectFlag) {
6772                         nPagedObjects += 1;
6773                     } else {
6774                         dbRecord rec;
6775                         getHeader(rec, j);
6776                         totalRecordsSize += DOALIGN(rec.size, dbAllocationQuantum);
6777                     }
6778                 }
6779             }
6780             pool.unfix((byte*)pg);
6781         }
6782         assert(j == nObjects);
6783         byte page[dbPageSize];
6784         memset(page, 0, sizeof page);
6785         dbHeader* newHeader = (dbHeader*)page;
6786         newHeader->curr = 0;
6787         newHeader->dirty = 0;
6788         newHeader->initialized = true;
6789         newHeader->versionMajor = header->versionMajor;
6790         newHeader->versionMinor = header->versionMinor;
6791         newHeader->mode = header->mode;
6792         offs_t newFileSize = DOALIGN((offs_t)(nPagedObjects + nIndexPages*2 + 1)*dbPageSize + totalRecordsSize,
6793                                      (offs_t)dbPageSize);
6794         newHeader->root[0].size = newHeader->root[1].size = newFileSize;
6795         newHeader->root[0].index = newHeader->root[1].shadowIndex = (offs_t)dbPageSize;
6796         newHeader->root[0].shadowIndex = newHeader->root[1].index = (offs_t)(dbPageSize + nIndexPages*dbPageSize);
6797         newHeader->root[0].shadowIndexSize = newHeader->root[0].indexSize =
6798             newHeader->root[1].shadowIndexSize = newHeader->root[1].indexSize = (oid_t)(nIndexPages*dbHandlesPerPage);
6799         newHeader->root[0].indexUsed = newHeader->root[1].indexUsed = nObjects;
6800         newHeader->root[0].freeList = newHeader->root[1].freeList = header->root[1-curr].freeList;
6801         newHeader->root[0].bitmapEnd = newHeader->root[1].bitmapEnd = header->root[1-curr].bitmapEnd;
6802 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
6803         newHeader->root[0].sessionFreeList = newHeader->root[1].sessionFreeList = header->root[1-curr].sessionFreeList;
6804 #endif
6805         rc |= f->write(page, dbPageSize);
6806 
6807         offs_t pageOffs = (offs_t)(nIndexPages*2 + 1)*dbPageSize;
6808         offs_t recOffs = (offs_t)(nPagedObjects + nIndexPages*2 + 1)*dbPageSize;
6809         qsort(oldIndex, (size_t)nObjects, sizeof(dbObjectHdr), &compareOffs);
6810 
6811         for (i = 0; i < nObjects; i++) {
6812             offs_t oldOffs = oldIndex[i].offs;
6813             oid_t oid = oldIndex[i].oid;
6814             if (!(oldOffs & dbFreeHandleFlag)) {
6815                 if (oldOffs & dbPageObjectFlag) {
6816                     newIndex[oid] = pageOffs | dbPageObjectFlag;
6817                     pageOffs += dbPageSize;
6818                 } else {
6819                     newIndex[oid] = recOffs;
6820                     dbRecord rec;
6821                     getHeader(rec, oid);
6822                     recOffs += DOALIGN(rec.size, dbAllocationQuantum);
6823                 }
6824             } else {
6825                 newIndex[oid] = oldOffs;
6826             }
6827         }
6828         rc |= f->write(newIndex, nIndexPages*dbPageSize);
6829         rc |= f->write(newIndex, nIndexPages*dbPageSize);
6830 
6831         for (i = 0; i < nObjects; i++) {
6832             if ((oldIndex[i].offs & (dbFreeHandleFlag|dbPageObjectFlag)) == dbPageObjectFlag) {
6833                 if (oldIndex[i].oid < dbFirstUserId) {
6834                     offs_t mappedSpace = (oldIndex[i].oid - dbBitmapId)*dbPageSize*8*dbAllocationQuantum;
6835                     if (mappedSpace >= newFileSize) {
6836                         memset(page, 0, dbPageSize);
6837                     } else if (mappedSpace + dbPageSize*8*dbAllocationQuantum <= newFileSize) {
6838                         memset(page, 0xFF, dbPageSize);
6839                     } else {
6840                         int nBits = (int)((newFileSize - mappedSpace) >> dbAllocationQuantumBits);
6841                         memset(page, 0xFF, nBits >> 3);
6842                         page[nBits >> 3] = (1 << (nBits & 7)) - 1;
6843                         memset(page + (nBits >> 3) + 1, 0, dbPageSize - (nBits >> 3) - 1);
6844                     }
6845                     rc |= f->write(page, dbPageSize);
6846                 } else {
6847                     byte* pg = pool.get(oldIndex[i].offs & ~dbFlagsMask);
6848                     rc |= f->write(pg, dbPageSize);
6849                     pool.unfix(pg);
6850                 }
6851             }
6852         }
6853         for (i = 0; i < nObjects; i++) {
6854             if ((oldIndex[i].offs & (dbFreeHandleFlag|dbPageObjectFlag)) == 0) {
6855                 dbRecord rec;
6856                 getHeader(rec, oldIndex[i].oid);
6857                 offs_t offs = oldIndex[i].offs & ~dbFlagsMask;
6858                 size_t size = DOALIGN(rec.size, dbAllocationQuantum);
6859                 size_t pageOffs = (size_t)offs & (dbPageSize-1);
6860                 while (size != 0) {
6861                     byte* pg = pool.get(offs - (offs_t)pageOffs);
6862                     if (dbPageSize - pageOffs >= size) {
6863                         rc |= f->write(pg + pageOffs, size);
6864                         size = 0;
6865                     } else {
6866                         rc |= f->write(pg + pageOffs, dbPageSize - pageOffs);
6867                         size -= dbPageSize - pageOffs;
6868                         offs += (offs_t)(dbPageSize - pageOffs);
6869                         pageOffs = 0;
6870                     }
6871                     pool.unfix(pg);
6872                 }
6873             }
6874         }
6875         if (recOffs != newFileSize) {
6876             assert(newFileSize - recOffs < dbPageSize);
6877             size_t align = (size_t)(newFileSize - recOffs);
6878             memset(page, 0, align);
6879             rc |= f->write(page, align);
6880         }
6881         delete[] oldIndex;
6882         delete[] newIndex;
6883     } else if (flags & BCK_INCREMENTAL) { // end if compactify
6884         offs_t eof = (offs_t)dirtyPageBitmapSize*dbPageSize;
6885         rc = file->copy(f, 0, eof);
6886         beginTransaction(dbSharedLock);
6887         for (offs_t offs = 0; rc == dbFile::ok && offs < eof; offs += dbPageSize) {
6888             if (dirtyPageBitmap[(offs / dbPageSize) >> 5] & (1 << ((offs / dbPageSize) & 31))) {
6889                 rc = file->copy(f, offs, dbPageSize);
6890             }
6891         }
6892         if (rc == dbFile::ok && eof < header->root[curr].size) {
6893             rc = file->copy(f, eof, DOALIGN(header->root[curr].size - eof, dbPageSize));
6894         }
6895         if (!(flags & BCK_HOLD_LOCK)) {
6896             commit();
6897         }
6898     } else {
6899         offs_t eof = header->root[curr].size;
6900         rc = f->write(header, dbPageSize);
6901         for (offs_t pos = dbPageSize; rc == dbFile::ok && pos < eof;) {
6902             byte* p = pool.get(pos);
6903             rc = f->write(p, dbPageSize);
6904             pool.unfix(p);
6905             pos += dbPageSize;
6906         }
6907     }
6908     mutex.lock();
6909     backupCompletedEvent.signal();
6910     monitor.backupInProgress = false;
6911     delete[] dirtyPageBitmap;
6912     dirtyPageBitmap = NULL;
6913     mutex.unlock();
6914     return rc == dbFile::ok;
6915 }
6916 
6917 
getPoolSize(size_t poolSize)6918 inline size_t getPoolSize(size_t poolSize) {
6919     if (poolSize == 0) {
6920         char* env = getenv("DB_POOL_SIZE");
6921         return (env != NULL) ? atoi(env) : 0;
6922     }
6923     return poolSize;
6924 }
6925 
6926 
dbDatabase(dbAccessType type,size_t poolSize,size_t dbExtensionQuantum,size_t dbInitIndexSize,int nThreads,int appMode)6927 dbDatabase::dbDatabase(dbAccessType type, size_t poolSize,
6928                        size_t dbExtensionQuantum, size_t dbInitIndexSize,
6929                        int nThreads,
6930                        int appMode
6931 ) : accessType(type),
6932   extensionQuantum(dbExtensionQuantum),
6933   initIndexSize(dbInitIndexSize),
6934   pool(this, getPoolSize(poolSize))
6935 {
6936     int libMode = 0
6937 #ifdef NO_PTHREADS
6938                | dbHeader::MODE_NO_PTHREADS
6939 #endif
6940 #ifdef ALIGN_HEADER
6941                | dbHeader::MODE_ALIGN_HEADER;
6942 #endif
6943 #if dbDatabaseOffsetBits > 32
6944                | dbHeader::MODE_OFFS_64
6945 #endif
6946 #if dbDatabaseOidBits > 32
6947                | dbHeader::MODE_OID_64
6948 #endif
6949 #ifdef UNICODE
6950                | dbHeader::MODE_UNICODE
6951 #endif
6952 #ifdef AUTOINCREMENT_SUPPORT
6953                | dbHeader::MODE_AUTOINCREMENT
6954 #endif
6955 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
6956                | dbHeader::MODE_DO_NOT_REUSE_OID
6957 #endif
6958 
6959                      ;
6960     if (appMode != libMode) {
6961         fprintf(stderr, "Incompatibly between headers and library: %x vs. %x\n", appMode, libMode);
6962         exit(1);
6963     }
6964     forceCommitCount = 0;
6965     commitDelay = 0;
6966     commitTimeout = 0;
6967     commitTimerStarted = 0;
6968     backupFileName = NULL;
6969     backupPeriod = 0;
6970     freeSpaceReuseThreshold = (offs_t)dbExtensionQuantum;
6971 
6972     dirtyPagesMap = new int4[dbDirtyPageBitmapSize/4+1];
6973     bitmapPageAvailableSpace = new int[dbBitmapId + dbBitmapPages];
6974     setConcurrency(nThreads);
6975     tables = NULL;
6976     opened = false;
6977     header = (dbHeader*)dbOSFile::allocateBuffer(dbPageSize);
6978     updatedRecordId = 0;
6979     dbFileExtensionQuantum = 0;
6980     dbFileSizeLimit = 0;
6981     errorHandler = NULL;
6982     logger = NULL;
6983     confirmDeleteColumns = false;
6984     inverseReferencesUpdate = true;
6985     schemeVersion = 0;
6986     visitedChain = NULL;
6987     file = NULL;
6988     btreeBuf = new int[dbPageSize*2];
6989     doNotReuseOidAfterClose = false;
6990     preserveExistedIndices = false;
6991     dirtyPageBitmap = NULL;
6992     btreeUnderflowPercent = dbDefaultBtreeUndeflowPercent;
6993 }
6994 
~dbDatabase()6995 dbDatabase::~dbDatabase()
6996 {
6997     delete[] dirtyPagesMap;
6998     delete[] bitmapPageAvailableSpace;
6999     delete[] btreeBuf;
7000     delete[] dirtyPageBitmap;
7001     dbOSFile::deallocateBuffer(header);
7002 }
7003 
setTransactionLogger(dbTransactionLogger * logger)7004 void dbDatabase::setTransactionLogger(dbTransactionLogger* logger)
7005 {
7006     this->logger = logger;
7007 }
7008 
7009 
setErrorHandler(dbDatabase::dbErrorHandler newHandler,void * context)7010 dbDatabase::dbErrorHandler dbDatabase::setErrorHandler(dbDatabase::dbErrorHandler newHandler, void* context)
7011 {
7012     dbErrorHandler prevHandler = errorHandler;
7013     errorHandler = newHandler;
7014     errorHandlerContext = context;
7015     return prevHandler;
7016 }
7017 
replicatePage(offs_t pos,void *)7018 void dbDatabase::replicatePage(offs_t pos, void*)
7019 {
7020     if (dirtyPageBitmap != NULL && pos / dbPageSize < dirtyPageBitmapSize) {
7021         dirtyPageBitmap[(pos / dbPageSize) >> 5] |= 1 << ((pos / dbPageSize) & 31);
7022     }
7023 }
7024 
releaseFile()7025 void dbDatabase::releaseFile()
7026 {
7027     if (file != NULL) {
7028         file->close();
7029         if (deleteFile) {
7030             delete file;
7031         }
7032         file = NULL;
7033     }
7034 }
7035 
loadMetaTable()7036 dbTableDescriptor* dbDatabase::loadMetaTable()
7037 {
7038     dbGetTie tie;
7039     dbTable* table = (dbTable*)getRow(tie, dbMetaTableId);
7040     dbTableDescriptor* metatable = new dbTableDescriptor(table);
7041     linkTable(metatable, dbMetaTableId);
7042     oid_t tableId = table->firstRow;
7043     while (tableId != 0) {
7044         table = (dbTable*)getRow(tie, tableId);
7045         dbTableDescriptor* desc;
7046         for (desc = tables; desc != NULL && desc->tableId != tableId; desc = desc->nextDbTable);
7047         if (desc == NULL) {
7048             desc = new dbTableDescriptor(table);
7049             linkTable(desc, tableId);
7050             desc->setFlags();
7051         }
7052         tableId = table->next;
7053     }
7054     if (!completeDescriptorsInitialization()) {
7055         handleError(DatabaseOpenError, "Reference to undefined table");
7056     }
7057     return metatable;
7058 }
7059 
isCompatible()7060 bool dbHeader::isCompatible()
7061 {
7062     return getVersion() < 349 || getCurrentMode() == mode;
7063 }
7064 
getCurrentMode()7065 int dbHeader::getCurrentMode()
7066 {
7067     int mode = MODE_RECTANGLE_DIM * RECTANGLE_DIMENSION * sizeof(RECTANGLE_COORDINATE_TYPE);
7068 #ifdef ALIGN_HEADER
7069     mode |= MODE_ALIGN_HEADER;
7070 #endif
7071 #if dbDatabaseOffsetBits > 32
7072     mode |= MODE_OFFS_64;
7073 #endif
7074 #if dbDatabaseOidBits > 32
7075     mode |= MODE_OID_64;
7076 #endif
7077 #ifdef UNICODE
7078     mode |= MODE_UNICODE;
7079 #endif
7080 #ifdef AUTOINCREMENT_SUPPORT
7081     mode |= MODE_AUTOINCREMENT;
7082 #endif
7083 #ifdef DO_NOT_REUSE_OID_WITHIN_SESSION
7084     mode |= MODE_DO_NOT_REUSE_OID;
7085 #endif
7086     return mode;
7087 }
7088 
~dbTransactionLogger()7089 dbTransactionLogger::~dbTransactionLogger() {}
7090 
extend(size_t size)7091 inline char* dbFileTransactionLogger::extend(size_t size)
7092 {
7093     size = DOALIGN(size, 8);
7094     if (used + size > allocated) {
7095         size_t newSize = used + size > allocated*2 ? used + size : allocated*2;
7096         char* newBuf = new char[newSize];
7097         allocated = newSize;
7098         memcpy(newBuf, buf, used);
7099         delete[] buf;
7100         buf = newBuf;
7101     }
7102     char* dst = buf + used;
7103     used += size;
7104     return dst;
7105 }
7106 
7107 struct TransLogObjectHeader
7108 {
7109     int   kind;
7110     nat4  size;
7111     oid_t oid;
7112     oid_t table;
7113 };
7114 
7115 struct TransLogTransHeader
7116 {
7117     nat4 size;
7118     nat4 crc;
7119 };
7120 
append(OperationKind kind,dbTableDescriptor * table,oid_t oid,dbRecord const * body)7121 void dbFileTransactionLogger::append(OperationKind kind, dbTableDescriptor* table, oid_t oid, dbRecord const* body)
7122 {
7123     TransLogObjectHeader* hdr = (TransLogObjectHeader*)extend(sizeof(TransLogObjectHeader));
7124     hdr->kind = kind;
7125     hdr->oid = oid;
7126     hdr->table = table->getId();
7127     if (body != NULL) {
7128         hdr->size = body->size;
7129         memcpy(extend(body->size), body, body->size);
7130     }
7131 }
7132 
insert(dbTableDescriptor * table,oid_t oid,dbRecord const * dbsObj,void const * appObj)7133 bool dbFileTransactionLogger::insert(dbTableDescriptor* table, oid_t oid, dbRecord const* dbsObj, void const* appObj)
7134 {
7135     append(opInsert, table, oid, dbsObj);
7136     return true;
7137 }
7138 
update(dbTableDescriptor * table,oid_t oid,dbRecord const * dbsObj,void const * appObj)7139 bool dbFileTransactionLogger::update(dbTableDescriptor* table, oid_t oid, dbRecord const* dbsObj, void const* appObj)
7140 {
7141     append(opUpdate, table, oid, dbsObj);
7142     return true;
7143 }
7144 
remove(dbTableDescriptor * table,oid_t oid)7145 bool dbFileTransactionLogger::remove(dbTableDescriptor* table, oid_t oid)
7146 {
7147     append(opRemove, table, oid, NULL);
7148     return true;
7149 }
7150 
calculate_crc(void const * content,size_t content_length,nat4 crc)7151 inline nat4 calculate_crc(void const* content, size_t content_length, nat4 crc)
7152 {
7153     static const nat4 table [] = {
7154         0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
7155         0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
7156         0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
7157         0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
7158 
7159         0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
7160         0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
7161         0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
7162         0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
7163 
7164         0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
7165         0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
7166         0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
7167         0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
7168 
7169         0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
7170         0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
7171         0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
7172         0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
7173 
7174         0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
7175         0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
7176         0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
7177         0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
7178 
7179         0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
7180         0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
7181         0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
7182         0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
7183 
7184         0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
7185         0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
7186         0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
7187         0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
7188 
7189         0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
7190         0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
7191         0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
7192         0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
7193 
7194         0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
7195         0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
7196         0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
7197         0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
7198 
7199         0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
7200         0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
7201         0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
7202         0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
7203 
7204         0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
7205         0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
7206         0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
7207         0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
7208 
7209         0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
7210         0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
7211         0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
7212         0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
7213 
7214         0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
7215         0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
7216         0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
7217         0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
7218 
7219         0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
7220         0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
7221         0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
7222         0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
7223 
7224         0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
7225         0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
7226         0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
7227         0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
7228 
7229         0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
7230         0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
7231         0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
7232         0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
7233     };
7234 
7235     unsigned char* buffer = (unsigned char*)content;
7236 
7237     while (content_length-- != 0) {
7238           crc = (crc >> 8) ^ table[(crc & 0xFF) ^ *buffer++];
7239     }
7240     return crc;
7241 }
7242 
commitPhase1()7243 bool dbFileTransactionLogger::commitPhase1()
7244 {
7245     TransLogTransHeader* hdr = (TransLogTransHeader*)buf;
7246     hdr->size = used - sizeof(TransLogTransHeader);
7247     hdr->crc = crc ? calculate_crc(buf + sizeof(TransLogTransHeader), used - sizeof(TransLogTransHeader), ~0) : 0;
7248     bool rc = log.write(buf, used) == dbFile::ok;
7249     used = sizeof(TransLogTransHeader);
7250     return rc;
7251 }
7252 
commitPhase2()7253 void dbFileTransactionLogger::commitPhase2()
7254 {
7255     log.flush();
7256 }
7257 
rollback()7258 void dbFileTransactionLogger::rollback()
7259 {
7260     used = sizeof(TransLogTransHeader);
7261 }
7262 
dbFileTransactionLogger()7263 dbFileTransactionLogger::dbFileTransactionLogger()
7264 {
7265     allocated = dbPageSize;
7266     buf = new char[allocated];
7267     used = sizeof(TransLogTransHeader);
7268     crc = true;
7269 }
7270 
~dbFileTransactionLogger()7271 dbFileTransactionLogger::~dbFileTransactionLogger()
7272 {
7273     delete[] buf;
7274 }
7275 
open(char_t const * path,int flags,bool crc)7276 bool dbFileTransactionLogger::open(char_t const* path, int flags, bool crc)
7277 {
7278     this->crc = crc;
7279     used = sizeof(TransLogTransHeader);
7280     if (log.open(path, flags) == dbFile::ok) {
7281         if (!(flags & (dbFile::truncate|dbFile::read_only))) {
7282             log.seek(0, SEEK_END);
7283         }
7284         return true;
7285     }
7286     return false;
7287 }
7288 
close()7289 void dbFileTransactionLogger::close()
7290 {
7291     log.close();
7292 }
7293 
restore(dbDatabase & db,size_t & nTrans)7294 dbFileTransactionLogger::RestoreStatus dbFileTransactionLogger::restore(dbDatabase& db, size_t& nTrans)
7295 {
7296     TransLogTransHeader transHdr;
7297     dbSmallBuffer<char> transBuf;
7298     dbSmallBuffer<char> recBuf;
7299     nTrans = 0;
7300     log.seek(0, SEEK_SET);
7301     while (log.read(&transHdr, sizeof(TransLogTransHeader)) == dbFile::ok) {
7302         transBuf.put(transHdr.size);
7303         if (log.read(transBuf.base(), transHdr.size) != dbFile::ok) {
7304             return rsReadFailed;
7305         }
7306         if (crc) {
7307             if (transHdr.crc != calculate_crc(transBuf.base(), transHdr.size, ~0)) {
7308                 return rsCRCMismatch;
7309             }
7310         }
7311         char* cur = transBuf.base();
7312         char* end = cur + transHdr.size;
7313         while (cur < end) {
7314             TransLogObjectHeader& objHdr = *(TransLogObjectHeader*)cur;
7315             dbTableDescriptor* table = db.findTableByID(objHdr.table);
7316             if (table == NULL) {
7317                 return rsTableNotFound;
7318             }
7319             cur += sizeof(TransLogObjectHeader);
7320 
7321             if (objHdr.kind == opRemove) {
7322                 db.remove(table, objHdr.oid);
7323             } else {
7324                 recBuf.put(table->size());
7325                 memset(recBuf.base(), 0, table->size());
7326                 table->columns->fetchRecordFields((byte*)recBuf.base(), (byte*)cur);
7327                 cur += DOALIGN(objHdr.size, 8);
7328 
7329                 if (objHdr.kind == opInsert) {
7330                     dbAnyReference ref;
7331                     db.insertRecord(table, &ref, recBuf.base(), false);
7332                     if (ref.getOid() != objHdr.oid) {
7333                         return rsOIDMismatch;
7334                     }
7335                 } else {
7336                     db.update(objHdr.oid, table, recBuf.base());
7337                 }
7338             }
7339         }
7340         nTrans += 1;
7341     }
7342     return rsOK;
7343 }
7344 
7345 
7346 END_GIGABASE_NAMESPACE
7347 
7348 
7349 
7350 
7351 
7352