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 = ∥
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