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