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