oqlselect.cc

00001 /* 
00002    EyeDB Object Database Management System
00003    Copyright (C) 1994-2008 SYSRA
00004    
00005    EyeDB is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009    
00010    EyeDB is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014    
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with this library; if not, write to the Free Software
00017    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA 
00018 */
00019 
00020 /*
00021    Author: Eric Viara <viara@sysra.com>
00022 */
00023 
00024 
00025 #include "oql_p.h"
00026 
00027 // -----------------------------------------------------------------------
00028 //
00029 // utility macros
00030 //
00031 // -----------------------------------------------------------------------
00032 
00033 namespace eyedb {
00034 
00035 #define oqmlIsPureComp(T) \
00036   ((T) == oqmlEQUAL || \
00037    (T) == oqmlDIFF || \
00038    (T) == oqmlINF || \
00039    (T) == oqmlINFEQ || \
00040    (T) == oqmlSUP || \
00041    (T) == oqmlSUPEQ || \
00042    (T) == oqmlREGCMP || \
00043    (T) == oqmlREGICMP || \
00044    (T) == oqmlREGDIFF || \
00045    (T) == oqmlREGIDIFF)
00046 
00047 #define oqmlIsComp(T) \
00048   ((T) == oqmlAND || (T) == oqmlLAND || \
00049    (T) == oqmlOR || (T) == oqmlLOR || \
00050    oqmlIsPureComp(T))
00051 
00052 static oqmlAtomType selectAtomType(oqmlATOM_SELECT);
00053 
00054 //#define TRACE
00055 
00056 #ifdef TRACE
00057 #define SELECT_TRACE(X) printf X
00058 #else
00059 #define SELECT_TRACE(X)
00060 #endif
00061 
00062 class SelectLog {
00063 
00064 public:
00065   enum Control {
00066     Off = 0,
00067     On,
00068     Detail
00069   };
00070 
00071   static Control oql_select_log_ctl;
00072   static oqmlAtom *oql_select_log;
00073   static const char oql_select_log_ctl_var[];
00074   static const char oql_select_log_var[];
00075   
00076   static oqmlStatus *error(oqmlNode *node) {
00077     return new oqmlStatus(node, "%s must be a string of "
00078                           "one of the values: on, off or detail",
00079                           oql_select_log_ctl_var);
00080   }
00081 
00082   static oqmlStatus *init(oqmlNode *node, oqmlContext *ctx) {
00083     if (ctx->getHiddenSelectContextCount())
00084       return oqmlSuccess;;
00085 
00086     oqmlAtom *x = 0;
00087     if (!ctx->getSymbol(oql_select_log_ctl_var, 0, &x) || !x) {
00088       oql_select_log_ctl = Off;
00089       oql_select_log = 0;
00090       return oqmlSuccess;;
00091     }
00092 
00093     if (!OQML_IS_STRING(x))
00094       return error(node);
00095 
00096     const char *val = OQML_ATOM_STRVAL(x);
00097     if (!strcasecmp(val, "off"))
00098       oql_select_log_ctl = Off;
00099     else if (!strcasecmp(val, "on"))
00100       oql_select_log_ctl = On;
00101     else if (!strcasecmp(val, "detail"))
00102       oql_select_log_ctl = Detail;
00103     else
00104       return error(node);
00105 
00106     if (oql_select_log_ctl != Off) {
00107       oql_select_log = new oqmlAtom_string("");
00108       ctx->setSymbol(oql_select_log_var, &oql_select_log->type, oql_select_log);
00109     }
00110     else
00111       oql_select_log = 0;
00112 
00113     return oqmlSuccess;
00114   }
00115 
00116   static void append(const std::string &str) {
00117     oql_select_log->as_string()->set((std::string(OQML_ATOM_STRVAL(oql_select_log)) + str).c_str());
00118   }
00119 };
00120 
00121 oqmlAtom *SelectLog::oql_select_log;
00122 SelectLog::Control SelectLog::oql_select_log_ctl = SelectLog::Off;
00123 const char SelectLog::oql_select_log_ctl_var[] = "oql$select_log_ctl";
00124 const char SelectLog::oql_select_log_var[] = "oql$select_log";
00125 
00126 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00127 //
00128 // oqmlAnd methods
00129 //
00130 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00131 
00132 oqmlAnd::oqmlAnd(oqmlNode * _qleft, oqmlNode * _qright) : oqmlNode(oqmlAND)
00133 {
00134   qleft = _qleft;
00135   qright = _qright;
00136 }
00137 
00138 oqmlAnd::~oqmlAnd()
00139 {
00140 }
00141 
00142 oqmlNode *
00143 oqmlAnd::optimize_realize(Database *db, oqmlContext *ctx, oqmlNode *ql,
00144                          int &_xc)
00145 {
00146   if (xc == 4)
00147     {
00148       _xc = 4;
00149       return ql;
00150     }
00151 
00152   oqmlNode **xql;
00153       
00154   oqmlTYPE t1 = qleft->getType();
00155 
00156   if (t1 == oqmlIDENT)
00157     {
00158       oqmlNode *tql = qleft;
00159       qleft = ql;
00160       return tql;
00161     }
00162 
00163   oqmlTYPE t2 = qright->getType();
00164 
00165   if (oqmlIsPureComp(t1))
00166     xql = &qleft;
00167   else if (oqmlIsPureComp(t2))
00168     xql = &qright;
00169   else
00170     return ql;
00171 
00172   if (((oqmlComp *)(*xql))->appearsMoreOftenThan((oqmlComp *)ql))
00173     {
00174       oqmlNode *tql = *xql;
00175       *xql = ql;
00176       return tql;
00177     }
00178 
00179   return ql;
00180 }
00181 
00182 void
00183 oqmlAnd::optimize(Database *db, oqmlContext *ctx)
00184 {
00185   oqmlAtomType at_left;
00186 
00187   qleft->evalType(db, ctx, &at_left);
00188 
00189   eval_type.type = oqmlATOM_OID;
00190   eval_type.cls = at_left.cls;
00191 }
00192 
00193 oqmlStatus *
00194 oqmlAnd::check(Database *db, oqmlContext *ctx)
00195 {
00196   oqmlAtomType at_left;
00197   oqmlAtomType at_right;
00198 
00199   qleft->evalType(db, ctx, &at_left);
00200   qright->evalType(db, ctx, &at_right);
00201 
00202   if (!xc) {
00203     if (at_left.type != oqmlATOM_BOOL || at_right.type != oqmlATOM_BOOL)
00204       return new oqmlStatus("and operator cannot deal with non boolean values");
00205     eval_type.type = oqmlATOM_BOOL;
00206     return oqmlSuccess;
00207   }
00208 
00209   if (at_left.type != oqmlATOM_OID)
00210     return new oqmlStatus("and operator: left identifier is not a class");
00211 
00212   if (at_right.type != oqmlATOM_OID)
00213     return new oqmlStatus("and operator: right identifier is not a class");
00214   
00215   if (!at_left.cls || !at_right.cls)
00216     return oqmlSuccess;
00217 
00218   Status status;
00219   Bool issuperclassof;
00220   
00221   status = at_left.cls->isSuperClassOf(at_right.cls, &issuperclassof);
00222   
00223   if (status)
00224     return new oqmlStatus(status);
00225       
00226   if (issuperclassof) {
00227     if (qleft->getType() == oqmlIDENT) {
00228       // swap
00229       oqmlNode *ql = qleft;
00230       qleft = qright;
00231       qright = ql;
00232     }
00233 
00234     optimize(db, ctx);
00235     return oqmlSuccess;
00236   }
00237   
00238   status = at_right.cls->isSuperClassOf(at_left.cls, &issuperclassof);
00239   
00240   if (status)
00241     return new oqmlStatus(status);
00242   
00243   if (issuperclassof)
00244     {
00245       optimize(db, ctx);
00246       return oqmlSuccess;
00247     }      
00248   
00249   xc = 4;
00250   eval_type.type = oqmlATOM_OID;
00251   eval_type.cls = db->getSchema()->getClass("object");
00252 
00253   return oqmlSuccess;
00254 }
00255 
00256 oqmlStatus *oqmlAnd::compile(Database *db, oqmlContext *ctx)
00257 {
00258   oqmlStatus *s;
00259   oqmlTYPE t1, t2;
00260 
00261   s = qleft->compile(db, ctx);
00262   if (s)
00263     return s;
00264 
00265   s = qright->compile(db, ctx);
00266   if (s)
00267     return s;
00268 
00269   t1 = qleft->getType();
00270   t2 = qright->getType();
00271 
00272   if (t1 == oqmlIDENT && t2 == oqmlIDENT)
00273     xc = 1;
00274   else if (oqmlIsComp(t1) && t2 == oqmlIDENT)
00275     xc = 2;
00276   else if (t1 == oqmlIDENT && oqmlIsComp(t2))
00277     {
00278       // swap
00279       oqmlNode *ql = qleft;
00280       qleft = qright;
00281       qright = ql;
00282       xc = 2;
00283     }
00284   else if (oqmlIsComp(t1) && oqmlIsComp(t2))
00285     xc = 3;
00286   else
00287     xc = 0;
00288 
00289   return check(db, ctx);
00290 }
00291 
00292 oqmlStatus *
00293 oqmlAnd::eval_0(Database *db, oqmlContext *ctx, oqmlAtomList **alist)
00294 {
00295   assert(0);
00296   oqmlStatus *s;
00297   oqmlAtomList *al_left, *al_right;
00298 
00299   s = qleft->eval(db, ctx, &al_left);
00300   if (s)
00301     return s;
00302 
00303   if (al_left->cnt != 1)
00304     return new oqmlStatus("and operator cannot deal with non atomic values");
00305 
00306   if (al_left->first->type.type != oqmlATOM_BOOL)
00307     return new oqmlStatus("and operator cannot deal with non boolean values");
00308 
00309   if (!((oqmlAtom_bool *)al_left->first)->b)
00310     {
00311       (*alist)->append(new oqmlAtom_bool(oqml_False));
00312       return oqmlSuccess;
00313     }
00314 
00315   s = qright->eval(db, ctx, &al_right);
00316   if (s)
00317     return s;
00318 
00319   if (al_right->cnt != 1)
00320     return new oqmlStatus("and operator cannot deal with non atomic values");
00321 
00322   if (al_right->first->type.type != oqmlATOM_BOOL)
00323     return new oqmlStatus("and operator cannot deal with non boolean values");
00324 
00325   (*alist)->append(new oqmlAtom_bool(((oqmlAtom_bool *)al_right->first)->b));
00326 
00327   return oqmlSuccess;
00328 }
00329 
00330 oqmlStatus *
00331 oqmlAnd::eval_1(Database *db, oqmlContext *ctx, oqmlAtomList **alist)
00332 {
00333   oqmlStatus *s;
00334   oqmlAtomList *and_ctx = ctx->getAndContext();
00335   unsigned int r_left, r_right;
00336 
00337   s = qleft->estimate(db, ctx, r_left);
00338   if (s) return s;
00339 
00340   if (r_left > 0)
00341     {
00342       s = qright->estimate(db, ctx, r_right);
00343       if (s) return s;
00344     }
00345   else
00346     r_right = oqml_ESTIM_MAX;
00347 
00348   oqmlAtomList *al_left, *al_right;
00349   if (r_left <= r_right)
00350     {
00351       s = qleft->eval(db, ctx, &al_left);
00352       if (s) return s;
00353 
00354       ctx->setAndContext(al_left);
00355 
00356       s = qright->eval(db, ctx, alist);
00357       if (s) return s;
00358 
00359       ctx->setAndContext(and_ctx);
00360       return oqmlSuccess;
00361     }
00362 
00363   s = qright->eval(db, ctx, &al_right);
00364   if (s) return s;
00365 
00366   ctx->setAndContext(al_right);
00367 
00368   s = qleft->eval(db, ctx, alist);
00369   if (s) return s;
00370 
00371   ctx->setAndContext(and_ctx);
00372   return oqmlSuccess;
00373 }
00374 
00375 oqmlStatus *oqmlAnd::eval(Database *db, oqmlContext *ctx, oqmlAtomList **alist, oqmlComp *, oqmlAtom *)
00376 {
00377   *alist = new oqmlAtomList();
00378 
00379   if (xc == 0)
00380     return eval_0(db, ctx, alist);
00381   if (xc == 4)
00382     return oqmlSuccess;
00383 
00384   return eval_1(db, ctx, alist);
00385 }
00386 
00387 void oqmlAnd::evalType(Database *db, oqmlContext *ctx, oqmlAtomType *at)
00388 {
00389   *at = eval_type;
00390 }
00391 
00392 oqmlBool oqmlAnd::isConstant() const
00393 {
00394   return oqml_False;
00395 }
00396 
00397 std::string
00398 oqmlAnd::toString(void) const
00399 {
00400   return oqml_binop_string(qleft, qright, " and ", is_statement);
00401 }
00402 
00403 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00404 //
00405 // oqmlOr methods
00406 //
00407 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00408 
00409 oqmlOr::oqmlOr(oqmlNode * _qleft, oqmlNode * _qright) : oqmlNode(oqmlOR)
00410 {
00411   qleft = _qleft;
00412   qright = _qright;
00413 }
00414 
00415 oqmlOr::~oqmlOr()
00416 {
00417   // delete qleft;
00418   // delete qright;
00419 }
00420 
00421 oqmlStatus *oqmlOr::compile(Database *db, oqmlContext *ctx)
00422 {
00423   oqmlStatus *s;
00424 
00425   // must eval types before evaluation
00426   s = qleft->compile(db, ctx);
00427   if (s)
00428     return s;
00429 
00430   s = qright->compile(db, ctx);
00431   if (s)
00432     return s;
00433 
00434   return oqmlSuccess;
00435 }
00436 
00437 oqmlStatus *oqmlOr::eval(Database *db, oqmlContext *ctx, oqmlAtomList **alist, oqmlComp *, oqmlAtom *)
00438 {
00439   oqmlStatus *s;
00440   oqmlAtomList *al_left, *al_right;
00441 
00442   oqmlAtomList *rlist = new oqmlAtomList();
00443   *alist = new oqmlAtomList(new oqmlAtom_list(rlist));
00444 
00445   s = qleft->eval(db, ctx, &al_left);
00446   if (s)
00447     return s;
00448 
00449   if (al_left->cnt == 1 && OQML_IS_COLL(al_left->first))
00450     rlist->append(OQML_ATOM_COLLVAL(al_left->first));
00451 
00452   s = qright->eval(db, ctx, &al_right);
00453   if (s)
00454     return s;
00455 
00456   if (al_right->cnt == 1 && OQML_IS_COLL(al_right->first))
00457     rlist->append(OQML_ATOM_COLLVAL(al_right->first));
00458 
00459   return oqmlSuccess;
00460 }
00461 
00462 void oqmlOr::evalType(Database *db, oqmlContext *ctx, oqmlAtomType *at)
00463 {
00464   oqmlAtomType at_left, at_right;
00465   oqmlStatus *s;
00466 
00467   qleft->evalType(db, ctx, &at_left);
00468 
00469   qright->evalType(db, ctx, &at_right);
00470 
00471   if (at_left.type == at_right.type)
00472     *at = at_left;
00473   else
00474     {
00475       at->type = oqmlATOM_UNKNOWN_TYPE;
00476       at->cls = 0;
00477       at->comp = oqml_False;
00478     }
00479 }
00480 
00481 oqmlBool oqmlOr::isConstant() const
00482 {
00483   return oqml_False;
00484 }
00485 
00486 std::string
00487 oqmlOr::toString(void) const
00488 {
00489   return oqml_binop_string(qleft, qright, " or ", is_statement);
00490 }
00491 
00492 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00493 //
00494 // oqmlDatabase methods
00495 //
00496 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00497 
00498 oqmlDatabase::oqmlDatabase(const char *_dbname, const char *_mode,
00499                            oqmlNode * _ql) : oqmlNode(oqmlDATABASE)
00500 {
00501   dbname = strdup(_dbname);
00502   mode = strdup(_mode);
00503   ql = _ql;
00504   cdb = 0;
00505 }
00506 
00507 oqmlDatabase::~oqmlDatabase()
00508 {
00509   free(dbname);
00510   free(mode);
00511 }
00512 
00513 oqmlStatus *oqmlDatabase::compile(Database *db, oqmlContext *ctx)
00514 {
00515   Database::OpenFlag open_flag;
00516 
00517   if (!strcasecmp(mode, "r"))
00518     open_flag = Database::DBRead;
00519   else if (!strcasecmp(mode, "sr"))
00520     open_flag = Database::DBSRead;
00521   else if (!strcasecmp(mode, "rw"))
00522     open_flag = Database::DBRW;
00523   else
00524     return new oqmlStatus(this, "unknown opening mode: %s", mode);
00525 
00526   if (!strcmp(dbname, db->getName()) && db->getOpenFlag() == open_flag)
00527     cdb = db;
00528   else
00529     {
00530       if (db->isLocal())
00531         return new oqmlStatus("cannot use multi database feature"
00532                               "in local opening mode");
00533 
00534       Status status = Database::open(db->getConnection(), dbname,
00535                                            db->getDBMDB(),
00536                                            db->getUser(),
00537                                            db->getPassword(),
00538                                            open_flag, 0, &cdb);
00539       
00540       if (status) return new oqmlStatus(status);
00541     }
00542 
00543   oqmlStatus *s;
00544 
00545   if (cdb != db) cdb->transactionBegin();
00546   s = ql->compile(cdb, ctx);
00547   if (cdb != db) cdb->transactionCommit();
00548 
00549   return s;
00550 }
00551 
00552 oqmlStatus *oqmlDatabase::eval(Database *db, oqmlContext *ctx,
00553                                oqmlAtomList **alist, oqmlComp *, oqmlAtom *)
00554 {
00555   oqmlStatus *s;
00556 
00557   if (cdb != db)
00558     cdb->transactionBegin();
00559 
00560   s = ql->eval(cdb, ctx, alist);
00561 
00562   if (cdb != db)
00563     cdb->transactionCommit();
00564 
00565   if (s)
00566     return s;
00567 
00568   return oqmlSuccess;
00569 }
00570 
00571 void oqmlDatabase::evalType(Database *db, oqmlContext *ctx, oqmlAtomType *at)
00572 {
00573   *at = eval_type;
00574 }
00575 
00576 oqmlBool oqmlDatabase::isConstant() const
00577 {
00578   return oqml_False;
00579 }
00580 
00581 std::string
00582 oqmlDatabase::toString(void) const
00583 {
00584   return std::string("(database ") + dbname + ")";
00585 }
00586 
00587 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00588 //
00589 // oqmlSelect methods
00590 //
00591 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00592 
00593 oqmlSelect::oqmlSelect(oqmlNode *_location,
00594                        oqmlBool _distinct,
00595                        oqmlBool _one,
00596                        oqmlNode *_projection,
00597                        oqml_IdentList *_ident_from_list,
00598                        oqmlNode *_where,
00599                        oqmlNode *_group,
00600                        oqmlNode *_having,
00601                        oqml_SelectOrder *_order) : oqmlNode(oqmlSELECT)
00602 {
00603   location = _location;
00604   distinct = _distinct;
00605   one = _one;
00606   projection = _projection;
00607   ident_from_list = _ident_from_list;
00608   where = _where;
00609   group = _group;
00610   having = _having;
00611   order = _order;
00612   list_order = 0;
00613   idents = 0;
00614   ident_cnt = 0;
00615   calledFromEval = oqml_False;
00616   databaseStatement = oqml_False;
00617   memset(&logged, 0, sizeof(logged));
00618 }
00619 
00620 oqmlSelect::~oqmlSelect()
00621 {
00622   delete ident_from_list;
00623   delete [] list_order;
00624   delete [] idents;
00625 }
00626 
00627 oqmlStatus *oqmlSelect::check_order_coll(oqmlNode *actual_projection)
00628 {
00629   oqml_List *projlist = ((oqmlColl *)actual_projection)->getList();
00630   if (!projlist)
00631     return new oqmlStatus(this, "order clause: '%s' not found in projection",
00632                           order->list->first->ql->toString().c_str());
00633 
00634   list_order = new int[order->list->cnt];
00635   oqml_Link *olink, *plink;
00636   int ocnt, pcnt;
00637   for (olink = order->list->first, ocnt = 0; olink; olink = olink->next, ocnt++)
00638     {
00639       oqmlBool ok = oqml_False;
00640       for (plink = projlist->first, pcnt = 0; plink; plink = plink->next, pcnt++)
00641         if (plink->ql->equals(olink->ql))
00642           {
00643             ok = oqml_True;
00644             list_order[ocnt] = pcnt;
00645             break;
00646           }
00647 
00648       if (!ok)
00649         return new oqmlStatus(this, "order clause: '%s' not found "
00650                               "in projection",
00651                               olink->ql->toString().c_str());
00652     }
00653 
00654   return oqmlSuccess;
00655 }
00656 
00657 oqmlStatus *oqmlSelect::check_order_simple()
00658 {
00659   for (oqml_Link *link = order->list->first; link; link = link->next)
00660     if (!projection->equals((link->ql)))
00661       return new oqmlStatus(this, "order clause: %s not found in projection",
00662                             link->ql->toString().c_str());
00663   return oqmlSuccess;
00664 }
00665 
00666 oqmlStatus *oqmlSelect::check_order()
00667 {
00668   if (!order)
00669     return oqmlSuccess;
00670 
00671   oqmlNode *actual_projection = projection;
00672   if (projection->getType() == oqmlCALL)
00673     actual_projection = ((oqmlCall *)projection)->getBuiltIn();
00674 
00675   if (actual_projection &&
00676       actual_projection->getType() == oqmlLISTCOLL ||
00677       actual_projection->getType() == oqmlBAGCOLL ||
00678       actual_projection->getType() == oqmlARRAYCOLL ||
00679       actual_projection->getType() == oqmlSETCOLL)
00680     return check_order_coll(actual_projection);
00681 
00682   return check_order_simple();
00683 }
00684 
00685 extern oqmlStatus *
00686 oqml_check_sort_type(oqmlNode *, oqmlATOMTYPE);
00687 
00688 oqmlStatus *
00689 oqmlSelect::realize_order(oqmlAtomList **alist)
00690 {
00691   if (!order || !*alist || !(*alist)->cnt || !OQML_IS_COLL((*alist)->first))
00692     return oqmlSuccess;
00693 
00694   oqmlAtomList *list = OQML_ATOM_COLLVAL((*alist)->first);
00695 
00696   if (!list->cnt) {
00697     *alist = new oqmlAtomList(new oqmlAtom_list(new oqmlAtomList())); // added the 25/05/01
00698     return oqmlSuccess;
00699   }
00700 
00701   oqmlStatus *s;
00702   if (!list_order)
00703     {
00704       oqmlAtomType &atom_type = list->first->type;
00705       s = oqml_check_sort_type(this, atom_type,
00706                                (std::string("cannot order using '") +
00707                                 order->list->first->ql->toString() + "'").c_str());
00708       if (s) return s;
00709       oqml_sort_simple(list, OQMLBOOL(!order->asc), atom_type.type, &list);
00710       *alist = new oqmlAtomList(new oqmlAtom_list(list));
00711       return oqmlSuccess;
00712     }
00713 
00714   // 2/2/00
00715   // warning: cette algo est faux dès qu'il y a plus d'un list order!
00716   oqml_Link *olink = order->list->first;
00717   for (int i = 0; i < order->list->cnt; i++, olink = olink->next)
00718     {
00719       oqmlAtomType &atom_type = OQML_ATOM_COLLVAL(list->first)->getAtom(list_order[i])->type;
00720       s = oqml_check_sort_type(this, atom_type,
00721                                (std::string("cannot order using '") +
00722                                 olink->ql->toString() + "'").c_str());
00723       if (s) return s;
00724       oqml_sort_list(list, OQMLBOOL(!order->asc), list_order[i],
00725                      atom_type.type, &list);
00726     }
00727 
00728   *alist = new oqmlAtomList(new oqmlAtom_list(list));
00729   return oqmlSuccess;
00730 }
00731 
00732 oqmlBool
00733 oqmlSelect::makeIdents()
00734 {
00735   if (!ident_from_list)
00736     return oqml_False;
00737 
00738   oqmlBool missingIdent = oqml_False;
00739 
00740   delete [] idents;
00741   idents = new oqml_IdentLink*[ident_from_list->cnt];
00742 
00743   oqml_IdentLink *l = ident_from_list->first;
00744 
00745   for (ident_cnt = 0; l; ident_cnt++)
00746     {
00747       idents[ident_cnt] = l;
00748       if (!l->ident)
00749         missingIdent = oqml_True;
00750       l = l->next;
00751     }
00752 
00753   return missingIdent;
00754 }
00755 
00756 oqmlStatus *
00757 oqmlSelect::processFromListRequalification(Database *db, oqmlContext *ctx)
00758 {
00759   for (int i = ident_cnt-1; i >= 0; i--)
00760     {
00761       if (!idents[i]->ident)
00762         continue;
00763 
00764       const char *left_ident = 0;
00765       if (idents[i]->ql->asIdent())
00766         left_ident = idents[i]->ql->asIdent()->getName();
00767       else if (idents[i]->ql->asDot())
00768         left_ident = idents[i]->ql->asDot()->getLeftIdent();
00769 
00770       if (left_ident)
00771         {
00772           for (int j = i-1; j >= 0; j--)
00773             if (idents[j]->ident && !strcmp(left_ident, idents[j]->ident))
00774               {
00775                 idents[i]->requalified = oqml_True;
00776                 break;
00777               }
00778 
00779           if (idents[i]->requalified)
00780             {
00781               oqmlBool done = oqml_False;
00782               oqmlStatus *s = where->requalify(db, ctx, idents[i]->ident,
00783                                                idents[i]->ql, done);
00784               if (s) return s;
00785             }
00786         }
00787     }
00788 
00789   return oqmlSuccess;
00790 }
00791 
00792 #define ERRFROM "from clause '%s': "
00793 
00794 std::string ident_gen()
00795 {
00796   static int i;
00797   return std::string("oql$x") + str_convert(i++);
00798 }
00799 
00800 oqmlStatus *
00801 oqmlSelect::processMissingIdentsRequalification(Database *db, oqmlContext *ctx)
00802 {
00803   for (int i = ident_cnt-1; i >= 0; i--)
00804     {
00805       if (idents[i]->ident)
00806         {
00807           if (idents[i]->ql->asIdent())
00808             idents[i]->cls = db->getSchema()->getClass(idents[i]->ql->asIdent()->getName());
00809           continue;
00810         }
00811 
00812       if (!idents[i]->ql->asIdent())
00813         return new oqmlStatus(this, ERRFROM "needs identifier",
00814                               idents[i]->ql->toString().c_str());
00815 
00816       const char *ident = idents[i]->ql->asIdent()->getName();
00817 
00818       for (int j = i-1; j >= 0; j--)
00819         if (idents[j]->ident && !strcmp(ident, idents[j]->ident))
00820           return new oqmlStatus(this, ERRFROM "needs identifier",
00821                                 idents[i]->ql->toString().c_str());
00822 
00823       idents[i]->cls = db->getSchema()->getClass(ident);
00824       if (!idents[i]->cls)
00825         return new oqmlStatus(this, ERRFROM "not a class",
00826                               idents[i]->ql->toString().c_str());
00827 
00828       //      idents[i]->ident = strdup(ident_gen());
00829       idents[i]->ident = strdup(ident);
00830       unsigned int attr_cnt;
00831       const Attribute **attrs = idents[i]->cls->getAttributes(attr_cnt);
00832       oqmlStatus *s;
00833       if (where)
00834         {
00835           s = where->requalify(db, ctx, attrs, attr_cnt, idents[i]->ident);
00836           if (s) return s;
00837         }
00838 
00839       if (order)
00840         for (oqml_Link *link = order->list->first; link; link = link->next)
00841           {
00842             if (link->ql->asIdent())
00843               s = requalify_node(db, ctx, link->ql, attrs, attr_cnt,
00844                                  idents[i]->ident);
00845             else
00846               s = link->ql->requalify(db, ctx, attrs, attr_cnt,
00847                                       idents[i]->ident);
00848             if (s) return s;
00849           }
00850 
00851       if (projection)
00852         {
00853           if (projection->asIdent())
00854             s = requalify_node(db, ctx, projection, attrs, attr_cnt, idents[i]->ident);
00855           else
00856             s = projection->requalify(db, ctx, attrs, attr_cnt, idents[i]->ident);
00857           if (s) return s;
00858         }
00859     }
00860 
00861   return oqmlSuccess;
00862 }
00863 
00864 static oqmlBool
00865 is_terminal(const Attribute *attr)
00866 {
00867   if (attr->isNative())
00868     return oqml_False;
00869 
00870   if (attr->isString())
00871     return oqml_True;
00872 
00873   if (attr->getTypeModifier().pdims != 1)
00874     return oqml_False;
00875     
00876   if (attr->isIndirect() || attr->getClass()->asBasicClass() ||
00877       attr->getClass()->asEnumClass())
00878     return oqml_True;
00879 
00880   return oqml_False;
00881 }
00882 
00883 oqmlStatus *
00884 oqmlSelect::processMissingProjRequalification(Database *db, oqmlContext *ctx)
00885 {
00886   oqml_IdentList *struct_list = new oqml_IdentList();
00887 
00888   for (int j = 0; j < ident_cnt; j++)
00889     {
00890       if (!idents[j]->cls)
00891         {
00892           delete struct_list;
00893           return new oqmlStatus(this, ERRFROM "not a class",
00894                                 idents[j]->ql->toString().c_str());
00895         }
00896 
00897       unsigned int attr_cnt;
00898       const Attribute **attrs = idents[j]->cls->getAttributes(attr_cnt);
00899       for (int k = 0; k < attr_cnt; k++)
00900         if (is_terminal(attrs[k]))
00901           struct_list->add(new oqmlIdent(attrs[k]->getName()),
00902                            new oqmlDot(new oqmlIdent(idents[j]->ident),
00903                                        new oqmlIdent(attrs[k]->getName()),
00904                                        oqml_False));
00905     }
00906 
00907   projection = new oqmlStruct(struct_list);
00908 
00909   if (isLocked()) projection->lock();
00910 
00911   return oqmlSuccess;
00912 }
00913 
00914 oqmlStatus *
00915 oqmlSelect::processRequalification_1(Database *db, oqmlContext *ctx)
00916 {
00917   oqmlBool missingIdent = makeIdents();
00918 
00919   if (missingIdent || !projection)
00920     {
00921       if (missingIdent && where && !where->mayBeRequalified())
00922         return new oqmlStatus(this, "this construct needs explicit "
00923                               "identifiers in the from clause");
00924 
00925       oqmlStatus *s = processMissingIdentsRequalification(db, ctx);
00926       if (s) return s;
00927     }
00928 
00929   if (!projection)
00930     {
00931       oqmlStatus *s = processMissingProjRequalification(db, ctx);
00932       if (s) return s;
00933     }
00934 
00935   return oqmlSuccess;
00936 }
00937 
00938 oqmlStatus *
00939 oqmlSelect::processRequalification_2(Database *db, oqmlContext *ctx)
00940 {
00941   if (where && where->mayBeRequalified())
00942     return processFromListRequalification(db, ctx);
00943 
00944   return oqmlSuccess;
00945 }
00946 
00947 oqmlStatus *
00948 oqmlSelect::compile(Database *db, oqmlContext *ctx)
00949 {
00950   oqmlStatus *s;
00951   if (!calledFromEval) {
00952     memset(&logged, 0, sizeof(logged));
00953     s = SelectLog::init(this, ctx);
00954     if (s) return s;
00955     return oqmlSuccess;
00956   }
00957 
00958   if (!db->isInTransaction())
00959     return new oqmlStatus(this, "must be done within the scope of "
00960                           "a transaction in database '%s'", db->getName());
00961 
00962   s = processRequalification_1(db, ctx);
00963   if (s) return s;
00964   
00965   if (!ident_from_list) {
00966     if (projection) {
00967       ctx->incrSelectContext(this);
00968       s = projection->compile(db, ctx);
00969       ctx->decrSelectContext();
00970       if (s) return s;
00971     }
00972     s = check_order();
00973     return s;
00974   }
00975     
00976   oqml_IdentLink *l;
00977     
00978   l = ident_from_list->first;
00979     
00980   while(l) {
00981     if (!l->ident)
00982       return new oqmlStatus(this, "identificator is missing in the from "
00983                             "clause: '%s'",
00984                             l->ql->toString().c_str());
00985     l = l->next;
00986   }
00987     
00988   l = ident_from_list->first;
00989   oqmlAtomType unknownType;
00990     
00991   s = oqmlSuccess;
00992 
00993   if (!s && !where) {
00994     ctx->incrSelectContext(this);
00995     while(l) {
00996       if (!s)
00997         s = l->ql->compile(db, ctx);
00998       ctx->pushSymbol(l->ident, &unknownType, 0, oqml_False);
00999       l = l->next;
01000     }
01001     
01002     if (!s) {
01003       int select_ctx_cnt = ctx->setSelectContextCount(0);
01004       s = projection->compile(db, ctx);
01005       ctx->setSelectContextCount(select_ctx_cnt);
01006     }
01007 
01008     if (!s)
01009       s = check_order();
01010 
01011     l = ident_from_list->first;
01012 
01013     while (l)   {
01014       ctx->popSymbol(l->ident, oqml_False);
01015       l = l->next;
01016     }
01017 
01018     ctx->decrSelectContext();
01019     return s;
01020   }
01021 
01022   ctx->incrSelectContext(this);
01023 
01024   while (l) {
01025     if (!s) s = l->ql->compile(db, ctx);
01026     ctx->pushSymbol(l->ident, &selectAtomType, 0, oqml_False);
01027     l = l->next;
01028   }
01029 
01030   ctx->decrSelectContext();
01031 
01032   if (!s && where) {
01033     int select_ctx_cnt = ctx->setSelectContextCount(0);
01034     // added 28/08/02
01035     ctx->incrHiddenSelectContext(this);
01036     s = where->compile(db, ctx);
01037     ctx->decrHiddenSelectContext();
01038     ctx->setSelectContextCount(select_ctx_cnt);
01039   }
01040 
01041   if (!s) {
01042     int select_ctx_cnt = ctx->setSelectContextCount(0);
01043     s = projection->compile(db, ctx);
01044     ctx->setSelectContextCount(select_ctx_cnt);
01045   }
01046 
01047   if (!s)
01048     s = check_order();
01049 
01050   l = ident_from_list->first;
01051 
01052   while (l) {
01053     ctx->popSymbol(l->ident, oqml_False);
01054     l = l->next;
01055   }
01056 
01057   return s;
01058 }
01059 
01060 oqmlStatus *
01061 optimize(Database *db, oqmlContext *ctx, oqmlAtom_select *atom,
01062          int n, oqml_IdentLink *idents[], oqmlAtomList *&list)
01063 {
01064   if (atom->list)
01065     {
01066       oqmlBool toEval = oqml_False;
01067       oqmlNode *node = atom->node;
01068 
01069       if (node)
01070         for (int i = 0; i < n; i++)
01071           if (node->hasIdent(idents[i]->ident)) {
01072             SELECT_TRACE(("optimize : node has ident %p\n", idents[i]->ident));
01073             toEval = oqml_True;
01074             break;
01075           }
01076 
01077       if (!toEval) {
01078         list = atom->list;
01079         return oqmlSuccess;
01080       }
01081     }
01082 
01083   if (!atom->node)
01084     return oqmlSuccess;
01085 
01086   oqmlStatus *s = atom->node->eval(db, ctx, &list);
01087   if (s) return s;
01088   atom->list = list;
01089   return oqmlSuccess;
01090 }
01091 
01092 oqmlBool
01093 checkIdent(oqml_IdentLink *idents[], int n, int cnt)
01094 {
01095   const char *ident = idents[n]->ident;
01096 
01097   for (int i = n+1; i < cnt; i++)
01098     if (idents[i]->ql->hasIdent(ident))
01099       return oqml_True;
01100 
01101   return oqml_False;
01102 }
01103 
01104 void stop_imm() { }
01105 
01106 static oqmlAtomList *
01107 oqml_make_intersect(oqmlAtomList *list1, oqmlAtom *a2)
01108 {
01109   oqmlAtomList *list = new oqmlAtomList();
01110 
01111   if (!a2 || !list1)
01112     return list;
01113 
01114   if (OQML_IS_COLL(a2))
01115     {
01116       oqmlAtomList *list2 = OQML_ATOM_COLLVAL(a2);
01117       oqmlAtom *a = list1->first;
01118 
01119       while (a)
01120         {
01121           oqmlAtom *next = a->next;
01122           if (list2->isIn(a))
01123             list->append(a->copy());
01124           a = next;
01125         }
01126     }
01127   else if (list1->isIn(a2))
01128     list->append(a2);
01129 
01130   /*
01131   printf("make_intersect between %s and %s is %s\n",
01132          list1->getString(), a2->getString(), list->getString());
01133          */
01134 
01135   return list;
01136 }
01137 
01138 oqmlAtom **
01139 begin_cpatoms(oqmlAtomList **cplists, int cpcnt)
01140 {
01141   if (!cpcnt)
01142     return 0;
01143 
01144   oqmlAtom **ratoms = new oqmlAtom *[cpcnt];
01145   for (int i = 0; i < cpcnt; i++) {
01146     oqmlAtomList *list = cplists[i];
01147     if (list && list->first && list->first->as_coll())
01148       list = OQML_ATOM_COLLVAL(list->first);
01149     ratoms[i] = (list ? list->first : 0);
01150   }
01151 
01152   return ratoms;
01153 }
01154 
01155 oqmlAtom **
01156 next_cpatoms(oqmlAtom **cpatoms, int cpcnt)
01157 {
01158   if (!cpcnt)
01159     return 0;
01160 
01161   oqmlAtom **ratoms = new oqmlAtom *[cpcnt];
01162   for (int i = 0; i < cpcnt; i++)
01163     ratoms[i] = cpatoms[i]->next;
01164 
01165   return ratoms;
01166 }
01167 
01168 #ifdef TRACE
01169 #define CP1_TRACE() \
01170   SELECT_TRACE(("EvalCartProdRealize(cpcnt %d, reducing #%d)\n", \
01171                 cpcnt, ident_cnt - n - 1)); \
01172   for (int i = 0; i < cpcnt; i++) \
01173     SELECT_TRACE(("EvalCartProdRealize(cpatoms[%d]: %s)\n", \
01174                  i, (const char *)cpatoms[i]->getString()))
01175 
01176 #define CP2_TRACE() \
01177   if (cpcnt_n) { \
01178     SELECT_TRACE(("\tcp count: %d\n", cpcnt_n)); \
01179     for (int i = 0; i < cpcnt_n; i++) \
01180       SELECT_TRACE(("\tcplist[%d]: %s\n", i, (const char *)cplists_n[i]->getString())); \
01181   }
01182 #else
01183 #define CP1_TRACE()
01184 #define CP2_TRACE()
01185 #endif
01186 
01187 oqmlStatus *
01188 oqmlSelect::evalCartProdRealize(Database *db, oqmlContext *ctx,
01189                                 oqmlAtomList *rlist, int n,
01190                                 const char *preval_ident,
01191                                 oqmlAtom **cpatoms, int cpcnt)
01192 {
01193   oqmlStatus *s = oqmlSuccess;
01194   oqmlAtomList *list = 0;
01195 
01196   oqmlAtomType at;
01197   oqmlAtom *atom = 0;
01198   const char *ident = idents[n]->ident;
01199   oqmlBool where_optim = oqml_False;
01200   int cpcnt_n = 0;
01201   oqmlAtomList **cplists_n = 0;
01202 
01203   SELECT_TRACE(("\nEvalCartProdRealize(n=%d, ident_cnt=%d) `%s'\n",
01204                 n, ident_cnt, (const char *)toString()));
01205 
01206   if (cpcnt) {
01207     CP1_TRACE();
01208     list = new oqmlAtomList(cpatoms[ident_cnt - n - 1]);
01209     where_optim = oqml_True;
01210   }
01211   else if (idents[n]->skipIdent || idents[n]->requalified) {
01212     if (n != ident_cnt - 1) {
01213       SELECT_TRACE(("\tBefore Requalified EvalCartProdRealize `%s'\n",
01214                     (const char *)toString()));
01215       oqmlStatus *s = evalCartProdRealize(db, ctx, rlist, n+1, preval_ident);
01216       SELECT_TRACE(("\tAfter Requalified EvalCartProdRealize `%s'\n",
01217                     (const char *)toString()));
01218       return s;
01219     }
01220     ctx->incrWhereContext();
01221     if (idents[n]->skipIdent)
01222       {
01223         ident = "oql$dummy";
01224         list = new oqmlAtomList(new oqmlAtom_nil());
01225       }
01226     else if (ctx->getSymbol(ident, &at, &atom) && atom->as_select() &&
01227              atom->as_select()->collatom)
01228       {
01229         oqmlAtomList *al;
01230         s = idents[n]->ql->eval(db, ctx, &al);
01231         if (s) return s;
01232         list = oqml_make_intersect(atom->as_select()->collatom->list,
01233                                    al ? al->first : 0);
01234         SELECT_TRACE(("\tMaking list from make_intersect `%s'\n",
01235                       (const char *)toString()));
01236       }
01237     else
01238       {
01239         SELECT_TRACE(("\tMaking list from idents[n]->ql->eval `%s' %s\n",
01240                       (const char *)toString(),
01241                       (const char*)idents[n]->ql->toString()));
01242         s = idents[n]->ql->eval(db, ctx, &list);
01243         if (s) return s;
01244       }
01245   }
01246   else {
01247     oqmlBool wasPopulated;
01248 
01249     if (ctx->getSymbol(ident, &at, &atom) && at.type == oqmlATOM_SELECT)
01250       wasPopulated = OQMLBOOL(atom->as_select()->list);
01251     else
01252       assert(0);
01253       
01254     ctx->incrWhereContext();
01255     if (n != ident_cnt - 1 && !wasPopulated &&
01256         !checkIdent(idents, n, ident_cnt)) {
01257       ctx->incrSelectContext(this);
01258       ctx->incrPrevalContext();
01259       SELECT_TRACE(("\tBefore Prevaling EvalCartProdRealize `%s'\n",
01260                     (const char *)toString()));
01261       s = evalCartProdRealize(db, ctx, rlist, n+1, ident);
01262       SELECT_TRACE(("\tAfter Prevaling EvalCartProdRealize `%s'\n",
01263                     (const char *)toString()));
01264       ctx->getSymbol(ident, &at, &atom);
01265       ctx->decrSelectContext();
01266       ctx->decrPrevalContext();
01267       if (s) return s;
01268     }
01269       
01270     where_optim = atom->as_select()->list ? oqml_True : oqml_False;
01271     cpcnt_n = atom->as_select()->cpcnt;
01272     cplists_n = atom->as_select()->cplists;
01273     CP2_TRACE();
01274 
01275     s = optimize(db, ctx, atom->as_select(), n, idents, list);
01276     SELECT_TRACE(("\tMaking list from optimize `%s' [where_optim %s]\n",
01277                   (const char *)toString(),
01278                   where_optim ? "true" : "false"));
01279 
01280     if (s) return s;
01281   }
01282 
01283   if (list && list->first && list->first->as_coll()) {
01284 #ifdef SYNC_GARB
01285     //oqmlAtomList *olist = list; 
01286 #endif
01287     list = OQML_ATOM_COLLVAL(list->first);
01288 #ifdef SYNC_GARB
01289     //olist->first = 0;
01290     //delete olist; // bad garbage !
01291 #endif
01292   }
01293 
01294   oqmlAtom *a = (list ? list->first : 0);
01295   oqmlAtom **cpatoms_n;
01296 
01297   if (cpcnt) {
01298     cpatoms_n = cpatoms;
01299     cpcnt_n = cpcnt;
01300   }
01301   else
01302     cpatoms_n = begin_cpatoms(cplists_n, cpcnt_n);
01303 
01304   oqmlBool cpatom_while = (!cpatoms && cpatoms_n) ? oqml_True : oqml_False;
01305 
01306   if (!a && n != ident_cnt - 1)
01307     a = new oqmlAtom_nil();
01308 
01309   SELECT_TRACE(("\tLoop start for %d items `%s'\n", list ? list->cnt : 0,
01310                 (const char *)toString()));
01311 
01312   if (SelectLog::oql_select_log_ctl != SelectLog::Off) {
01313     if (!logged[n] || SelectLog::oql_select_log_ctl == SelectLog::Detail) {
01314       SelectLog::append((std::string("QUERY : ") + toString() + "\nDEPTH : #" +
01315                          str_convert((long)n) + "\n").c_str());
01316       logged[n] = 1;
01317     }
01318     if (SelectLog::oql_select_log_ctl == SelectLog::Detail) {
01319       SelectLog::append((std::string("ITEM COUNT : ") +
01320                          str_convert((long)(list ? list->cnt : 0)) + "\n").c_str());
01321     }
01322   }
01323 
01324 #if 0
01325   printf("LOOP from 0 to %d\n", (list ? list->cnt : 0));
01326   int nn = 0;
01327 
01328   oqmlAtom *xxx = a;
01329   while (xxx) {
01330     oqmlAtom *next = xxx->next;
01331     printf("previous #%d -> %s %s\n", nn, a->getString(), (next ? next->getString() : "<NIL>"));
01332     nn++;
01333     xxx = next;
01334   }
01335 
01336   nn = 0;
01337 #endif
01338 
01339   while (a) {
01340     oqmlAtom *next = a->next;
01341     /*
01342     printf("#%d -> %s %s\n", nn, a->getString(), (next ? next->getString() : "<NIL>"));
01343     nn++;
01344     */
01345     oqmlAtom **cpatom_next = 0;
01346     if (cpatom_while)
01347       cpatom_next = next_cpatoms(cpatoms_n, cpcnt_n);
01348     OQML_CHECK_INTR();
01349     if (ident) {
01350       ctx->pushSymbol(ident, &a->type, a, oqml_False);
01351           
01352       SELECT_TRACE(("\tPushing Symbol %s -> %s\n", ident, a->getString()));
01353 
01354       if (n != ident_cnt - 1) {
01355         SELECT_TRACE(("\tBefore Calling EvalCartProdRealize "
01356                       "recursively(%s)\n", (const char *)toString()));
01357         s = evalCartProdRealize(db, ctx, rlist, n+1, preval_ident,
01358                                 cpatoms_n, cpcnt_n);
01359         SELECT_TRACE(("\tAfter Calling EvalCartProdRealize "
01360                       "recursively(%s)\n", (const char *)toString()));
01361       }
01362       else {
01363         oqmlAtomList *xlist = 0;
01364         if (where && ctx->isPrevalContext())
01365           {
01366             oqmlBool done;
01367             unsigned int cnt;
01368             SELECT_TRACE(("\tBefore PreEvaluatingSelect Where(%s)\n",
01369                           (const char *)where->toString()));
01370             ctx->pushCPAtom(this, a);
01371             s = where->preEvalSelect(db, ctx, preval_ident, done, cnt,
01372                                      oqml_False);
01373             ctx->popCPAtom(this);
01374             SELECT_TRACE(("\tAfter PreEvaluatingSelect Where(%s)\n",
01375                           (const char *)where->toString()));
01376           }
01377         else if (where && !(where_optim && where->asComp())) {
01378           // added the 4/07/01
01379           int select_ctx_cnt = ctx->setSelectContextCount(0);
01380           SELECT_TRACE(("\tBefore Evaluating Where(%s)\n",
01381                         (const char *)where->toString()));
01382           // added 28/08/02
01383           ctx->incrHiddenSelectContext(this);
01384           s = where->eval(db, ctx, &xlist);
01385           ctx->decrHiddenSelectContext();
01386           SELECT_TRACE(("\tAfter Evaluating Where(%s)\n",
01387                         (const char *)where->toString()));
01388 
01389           if (xlist && !xlist->first)
01390             SELECT_TRACE(("\tWhere(%s) returning false\n",
01391                           (const char *)where->toString()));
01392           if (!s && xlist->first) {
01393             if (!OQML_IS_BOOL(xlist->first)) {
01394               s = new oqmlStatus(this,
01395                                  "where condition: "
01396                                  "boolean expected, got %s",
01397                                  xlist->first->type.getString());
01398             }
01399             else if (OQML_ATOM_BOOLVAL(xlist->first)) {
01400               SELECT_TRACE(("\tWhere(%s) returning true\n",
01401                             (const char *)where->toString()));
01402               oqmlAtomList *ylist;
01403               s = projection->eval(db, ctx, &ylist);
01404               if (!s && ylist->first)
01405                 {
01406                   if (!distinct || !rlist->isIn(ylist->first)) {
01407                     SELECT_TRACE(("\tWhere(%s) appending %s\n",
01408                                   (const char *)where->toString(),
01409                                   (const char *)ylist->first->getString()));
01410                     rlist->append(ylist->first);
01411 #ifdef SYNC_GARB
01412                     if (ylist && !ylist->refcnt) {
01413                       ylist->first = 0;
01414                       delete ylist;
01415                     }
01416 #endif
01417                   }
01418                 }
01419             }
01420             else
01421               SELECT_TRACE(("\tWhere(%s) returning false\n",
01422                             (const char *)where->toString()));
01423           }
01424 
01425           // added the 4/07/01
01426           ctx->setSelectContextCount(select_ctx_cnt);
01427 #ifdef SYNC_GARB
01428           OQL_DELETE(xlist);
01429 #endif
01430         }
01431         else {
01432           // added the 4/07/01
01433           oqmlAtomList *ylist;
01434           int select_ctx_cnt = ctx->setSelectContextCount(0);
01435           s = projection->eval(db, ctx, &ylist);
01436           if (!s && ylist->first) {
01437             SELECT_TRACE(("\t%sappending %s\n",
01438                           "Unconditionally ",
01439                           (const char *)ylist->first->getString()));
01440             rlist->append(ylist->first);
01441 #ifdef SYNC_GARB
01442             if (ylist && !ylist->refcnt) {
01443               ylist->first = 0;
01444               delete ylist;
01445             }
01446 #endif
01447           }
01448           // added the 4/07/01
01449           ctx->setSelectContextCount(select_ctx_cnt);
01450         }
01451       }
01452 
01453       ctx->popSymbol(ident, oqml_False);
01454 
01455       if (s)
01456         break;
01457     }
01458 
01459     if (cpatom_while)
01460       delete [] cpatoms_n;
01461 
01462     cpatoms_n = cpatom_next;
01463     a = next;
01464   }
01465 
01466   if (cpatom_while)
01467     delete [] cpatoms_n;
01468 
01469   SELECT_TRACE(("\tLoop end `%s'\n", (const char *)toString()));
01470 
01471   ctx->decrWhereContext();
01472 #ifdef SYNC_GARB
01473   //delete list; // really ?
01474 #endif
01475   return s;
01476 }
01477 
01478 static oqmlContext *context;
01479 
01480 int identlink_cmp(const void *xi1, const void *xi2)
01481 {
01482   const oqml_IdentLink *i1 = *(const oqml_IdentLink **)xi1;
01483   const oqml_IdentLink *i2 = *(const oqml_IdentLink **)xi2;
01484   oqmlAtom *a1, *a2;
01485 
01486   if (context->getSymbol(i1->ident, 0, &a1) && a1 && a1->as_select() &&
01487       context->getSymbol(i2->ident, 0, &a2) && a2 && a2->as_select())
01488     {
01489       if (a1->as_select()->isPopulated())
01490         {
01491           if (a2->as_select()->isPopulated())
01492             return 0;
01493           return -1;
01494         }
01495       else if (a2->as_select()->isPopulated())
01496         return 1;
01497 
01498       if (a1->as_select()->indexed)
01499         {
01500           if (a2->as_select()->indexed)
01501             return 0;
01502           return -1;
01503         }
01504       else if (a2->as_select()->indexed)
01505         return 1;
01506     }
01507 
01508   return 0;
01509 }
01510 
01511 static void
01512 dump_idents(const char *msg, oqml_IdentLink *idents[], int n)
01513 {
01514   printf("%s: ", msg);
01515   for (int i = 0; i < n; i++)
01516     printf("ident[%d] = %s\n", i, idents[i]->ident);
01517 }
01518 
01519 oqmlBool
01520 oqmlSelect::usesFromIdent(oqmlNode *node)
01521 {
01522   if (!ident_from_list)
01523     return oqml_False;
01524 
01525   oqml_IdentLink *l = ident_from_list->first;
01526 
01527   while (l)
01528     {
01529       if (node->hasIdent(l->ident))
01530         return oqml_True;
01531       l = l->next;
01532     }
01533 
01534   return oqml_False;
01535 }
01536 
01537 oqmlStatus *
01538 oqmlSelect::evalCartProd(Database *db, oqmlContext *ctx,
01539                          oqmlAtomList **alist)
01540 {
01541   oqmlStatus *s = oqmlSuccess;
01542 
01543   if (ident_cnt > 1)
01544     {
01545       context = ctx;
01546       //dump_idents("before", idents, ident_cnt);
01547       qsort(idents, ident_cnt, sizeof(oqml_IdentLink *), identlink_cmp);
01548       //dump_idents("after", idents, ident_cnt);
01549     }
01550 
01551 #define OPT_DEAD_IDENTS
01552 
01553 #ifdef OPT_DEAD_IDENTS
01554   // Eliminate ``dead idents''
01555   //
01556   // WARNING: this test is not reliable as hasIdent() is not correctly
01557   // implemented for all classes!!!
01558   //
01559 
01560   // MUST BE IMPROVED: should check that idents[i] is not used in
01561   // any constructs of the ident_from_list except this.
01562   //
01563   for (int i = 0; i < ident_cnt; i++)
01564     if (!projection->hasIdent(idents[i]->ident) &&
01565         (!where || !where->hasIdent(idents[i]->ident)) &&
01566         !checkIdent(idents, i, ident_cnt))
01567       idents[i]->skipIdent = oqml_True;
01568 #endif
01569 
01570   oqmlAtomList *rlist = new oqmlAtomList();
01571 
01572   SELECT_TRACE(("\nEvalCartProd Start `%s'\n",
01573          (const char *)toString()));
01574 
01575   if (!s)
01576     s = evalCartProdRealize(db, ctx, rlist, 0);
01577 
01578   SELECT_TRACE(("EvalCartProd End `%s'\n",
01579          (const char *)toString()));
01580 
01581   if (s) return s;
01582 
01583   if (one)
01584     *alist = new oqmlAtomList(rlist->first);
01585   else
01586     {
01587       oqmlAtom_coll *list;
01588       if (distinct)
01589         list = new oqmlAtom_set(rlist, oqml_False);
01590       else
01591         list = new oqmlAtom_bag(rlist);
01592       
01593       //*alist = new oqmlAtomList(distinct ? list->suppressDoubles() : list);
01594       *alist = new oqmlAtomList(list);
01595     }
01596 
01597   //return realize_order(alist);
01598   return oqmlSuccess;
01599 }
01600 
01601 //#define NEW_ONEATOM
01602 
01603 #ifdef NEW_ONEATOM
01604 static oqmlBool
01605 xalist_make(oqmlBool one, oqmlAtomList *xalist, oqmlAtomList *alist)
01606 #else
01607 static oqmlBool
01608 xalist_make(oqmlContext *ctx, oqmlAtomList *xalist, oqmlAtomList *alist)
01609 #endif
01610 {
01611   if (!alist)
01612     return oqml_True;
01613 
01614   oqmlAtom *xa = xalist->first;
01615   oqmlAtom *a = alist->first;
01616 
01617   if (xa && OQML_IS_COLL(xa)) {
01618     if (OQML_IS_COLL(a))
01619       OQML_ATOM_COLLVAL(xa)->append(OQML_ATOM_COLLVAL(a));
01620     else
01621       OQML_ATOM_COLLVAL(xa)->append(a);
01622   }
01623   else if (a)
01624     xalist->append(a);
01625 
01626 #ifdef NEW_ONEATOM
01627   if (xalist->cnt && one)
01628     return oqml_False;
01629 #else
01630   if (xalist->cnt && ctx->isOneAtom())
01631     return oqml_False;
01632 #endif
01633 
01634   return oqml_True;
01635 }
01636 
01637 oqmlStatus *oqmlSelect::eval(Database *db, oqmlContext *ctx, oqmlAtomList **xalist, oqmlComp *, oqmlAtom *)
01638 {
01639   if (one && order)
01640     return new oqmlStatus(this,
01641                           "cannot use an order by clause within a select one");
01642 
01643   oqmlStatus *s;
01644 
01645   s = oqml_get_locations(db, ctx, location, dbs, dbs_cnt);
01646   if (s) return s;
01647 
01648   int where_ctx = ctx->setWhereContext(0);
01649   int preval_ctx = ctx->setPrevalContext(0);
01650 
01651   oqmlBool oldone = ctx->isOneAtom();
01652 #ifdef NEW_ONEATOM
01653   if (oldone) {
01654     printf("warning: a `select' expression should not be imbricated in a `select one' expression");
01655   }
01656 #else
01657   if (oldone)
01658     return new oqmlStatus(this, "a `select' expression cannot be imbricated in a `select one' expression");
01659 
01660   if (one && ident_from_list && ident_from_list->cnt > 1)
01661     return new oqmlStatus(this, "a join cannot be performed in a `select one' expression");
01662 #endif
01663 
01664   if (one) ctx->setOneAtom();
01665 
01666   SELECT_TRACE(("Eval(%s)\n", (const char *)toString()));
01667   *xalist = new oqmlAtomList(); // leaks
01668 
01669   for (int i = 0; i < dbs_cnt; i++) {
01670     oqmlAtomList *alist = 0;
01671     db = dbs[i];
01672     calledFromEval = oqml_True;
01673     s = compile(db, ctx);
01674     calledFromEval = oqml_False;
01675     if (s) return s;
01676     //db = newdb;
01677 
01678     if (!ident_from_list)
01679       {
01680         ctx->incrSelectContext(this);
01681         s = projection->eval(db, ctx, &alist);
01682         ctx->decrSelectContext();
01683 
01684         if (s) return s;
01685 
01686         if (distinct && alist->cnt && OQML_IS_COLL(alist->first))
01687           {
01688             oqmlAtom_set *list = new oqmlAtom_set
01689               (OQML_ATOM_COLLVAL(alist->first));
01690             //alist = new oqmlAtomList(list->suppressDoubles());
01691           }
01692         else if (one)
01693           {
01694             if (alist->cnt && OQML_IS_COLL(alist->first))
01695               alist = new oqmlAtomList(OQML_ATOM_COLLVAL(alist->first)->first);
01696             else
01697               alist = new oqmlAtomList(alist->first);
01698           }
01699 
01700         if (s) return s;
01701 
01702 #ifdef NEW_ONEATOM
01703         if (!xalist_make(one, *xalist, alist))
01704           break;
01705 #else
01706         if (!xalist_make(ctx, *xalist, alist))
01707           break;
01708 #endif
01709         continue;
01710       }
01711 
01712     s = oqmlSuccess;
01713     char *popIdent = 0;
01714 
01715     oqml_IdentLink *l = ident_from_list->first;
01716     while (l)
01717       {
01718         ctx->pushSymbol(l->ident, &selectAtomType,
01719                         new oqmlAtom_select(l->ql, l->ql), oqml_False); // leaks
01720         l = l->next;
01721       }
01722 
01723     s = processRequalification_2(db, ctx);
01724 
01725   /*
01726   IDB_LOG(IDB_LOG_OQL_EXEC,
01727           ("select requalified to '%s'\n", (const char *)toString()));
01728           */
01729 
01730   //printf("select is now '%s'\n", (const char *)toString());
01731 
01732     if (!s && where)
01733       {
01734         oqml_IdentLink *l = ident_from_list->first;
01735 
01736         while (l)
01737           {
01738             if (l->ql->getType() == oqmlIDENT)
01739               {
01740                 oqmlBool done;
01741                 ctx->incrSelectContext(this);
01742                 ctx->incrPrevalContext();
01743                 unsigned int cnt;
01744                 SELECT_TRACE(("Before Where PreEvalSelect `%s' %s\n",
01745                               (const char *)toString(),
01746                               (const char *)where->toString()));
01747                 s = where->preEvalSelect(db, ctx, l->ident, done, cnt);
01748                 SELECT_TRACE(("After Where PreEvalSelect `%s' %s\n",
01749                               (const char *)toString(),
01750                               (const char *)where->toString()));
01751                 ctx->decrPrevalContext();
01752                 popIdent = l->ident;
01753                 ctx->decrSelectContext();
01754                 if (s)
01755                   break;
01756                 oqmlAtom *atom_select = 0;
01757                 ctx->getSymbol(l->ident, 0, &atom_select);
01758                 if (atom_select->as_select()->list)
01759                   break;
01760               }
01761             l = l->next;
01762           }
01763       }
01764 
01765     if (!s) {
01766       s = evalCartProd(db, ctx, &alist);
01767       //if (!s) s = realize_order(&alist);
01768     }
01769 
01770     l = ident_from_list->first;
01771     while (l)
01772       {
01773         ctx->popSymbol(l->ident, oqml_False);
01774         l = l->next;
01775       }
01776 
01777     if (s) return s;
01778 #ifdef NEW_ONEATOM
01779     if (!xalist_make(one, *xalist, alist))
01780       break;
01781 #else
01782     if (!xalist_make(ctx, *xalist, alist))
01783       break;
01784 #endif
01785   }
01786 
01787 #ifdef NEW_ONEATOM
01788   ctx->setOneAtom(oqml_False);
01789 #else
01790   ctx->setOneAtom(oldone);
01791 #endif
01792   if (distinct) {
01793     oqmlAtom *xa = (*xalist)->first;
01794     if (xa && OQML_IS_COLL(xa))
01795       *xalist = new oqmlAtomList(new oqmlAtom_set(OQML_ATOM_COLLVAL(xa)));
01796   }
01797     
01798   s = realize_order(xalist);
01799   (void)ctx->setWhereContext(where_ctx);
01800   (void)ctx->setPrevalContext(preval_ctx);
01801   return s;
01802 }
01803 
01804 void oqmlSelect::evalType(Database *db, oqmlContext *ctx, oqmlAtomType *at)
01805 {
01806   *at = eval_type;
01807 }
01808 
01809 oqmlBool oqmlSelect::isConstant() const
01810 {
01811   return oqml_False;
01812 }
01813 
01814 std::string
01815 oqmlSelect::toString(void) const
01816 {
01817   std::string s = (is_statement ? std::string("") : std::string("(")) +
01818     (databaseStatement ? "context" : "select") + 
01819     (location ? std::string("<") + location->toString() + "> " :
01820      std::string(" ")) + (one ? "one " : "") + (distinct ? "distinct " : "") +
01821     (projection ? projection->toString() : std::string("*"));
01822   
01823   if (ident_from_list)
01824     {
01825       s += " from ";
01826       oqml_IdentLink *l = ident_from_list->first;
01827       for (int n = 0, c = 0; l; n++, l = l->next)
01828         //if (!l->requalified)
01829           {
01830             if (c) s += ",";
01831             if (l->ql)
01832               s += l->ql->toString();
01833             if (l->ident)
01834               s += std::string(" as ") + l->ident;
01835             c++;
01836           }
01837     }
01838 
01839   if (where)
01840     s += std::string(" where ") + where->toString();
01841 
01842   if (group)
01843     s += std::string(" group by ") + group->toString();
01844 
01845   if (having)
01846     s += std::string(" having ") + having->toString();
01847 
01848   if (order)
01849     {
01850       s += std::string(" order by ");
01851       s += order->list->toString();
01852       s += order->asc ? " asc" : " desc";
01853     }
01854 
01855   if (is_statement)
01856     return s + "; ";
01857 
01858   return s + ")";
01859 }
01860 
01861 void
01862 oqmlSelect::lock()
01863 {
01864   oqmlNode::lock();
01865   projection->lock();
01866   if (location) location->lock();
01867   if (where) where->lock();
01868   if (group) group->lock();
01869   if (having) having->lock();
01870   if (order) order->list->lock();
01871   if (!ident_from_list)
01872     return;
01873 
01874   oqml_IdentLink *l = ident_from_list->first;
01875   while (l)
01876     {
01877       l->ql->lock();
01878       l = l->next;
01879     }
01880 }
01881 
01882 void
01883 oqmlSelect::unlock()
01884 {
01885   oqmlNode::unlock();
01886   projection->unlock();
01887   if (location) location->unlock();
01888   if (where) where->unlock();
01889   if (group) group->unlock();
01890   if (having) having->unlock();
01891   if (order) order->list->unlock();
01892   if (!ident_from_list)
01893     return;
01894 
01895   oqml_IdentLink *l = ident_from_list->first;
01896   while (l)
01897     {
01898       l->ql->unlock();
01899       l = l->next;
01900     }
01901 }
01902 
01903 void
01904 oqml_capstring(char *str)
01905 {
01906   char c;
01907   while (c = *str)
01908     {
01909       if (c >= 'a' && c <= 'z')
01910         *str = c + 'A' - 'a';
01911       str++;
01912     }
01913 }
01914 }

Generated on Mon Dec 22 18:16:06 2008 for eyedb by  doxygen 1.5.3