ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ _currentSearch || null === $this->_currentSearch->getCriterias() ) { return $this; } $criterias = $this->_currentSearch->getCriterias(); $_REQUEST = $criterias + $_REQUEST; return $this; } /** * Getter for current search * * @return SavedSearches */ private function _getCurrentSearch() { return $this->_currentSearch; } /** * Public Constructor * * @param string $dbname Database name * @param array $savedSearchList List of saved searches * @param SavedSearches $currentSearch Current search id */ public function __construct( $dbname, $savedSearchList = array(), $currentSearch = null ) { $this->_db = $dbname; $this->_savedSearchList = $savedSearchList; $this->_currentSearch = $currentSearch; $this->_loadCriterias(); // Sets criteria parameters $this->_setSearchParams(); $this->_setCriteriaTablesAndColumns(); } /** * Sets search parameters * * @return void */ private function _setSearchParams() { $criteriaColumnCount = $this->_initializeCriteriasCount(); $this->_criteriaColumnInsert = PMA_ifSetOr( $_REQUEST['criteriaColumnInsert'], null, 'array' ); $this->_criteriaColumnDelete = PMA_ifSetOr( $_REQUEST['criteriaColumnDelete'], null, 'array' ); $this->_prev_criteria = isset($_REQUEST['prev_criteria']) ? $_REQUEST['prev_criteria'] : array(); $this->_criteria = isset($_REQUEST['criteria']) ? $_REQUEST['criteria'] : array_fill(0, $criteriaColumnCount, ''); $this->_criteriaRowInsert = isset($_REQUEST['criteriaRowInsert']) ? $_REQUEST['criteriaRowInsert'] : array_fill(0, $criteriaColumnCount, ''); $this->_criteriaRowDelete = isset($_REQUEST['criteriaRowDelete']) ? $_REQUEST['criteriaRowDelete'] : array_fill(0, $criteriaColumnCount, ''); $this->_criteriaAndOrRow = isset($_REQUEST['criteriaAndOrRow']) ? $_REQUEST['criteriaAndOrRow'] : array_fill(0, $criteriaColumnCount, ''); $this->_criteriaAndOrColumn = isset($_REQUEST['criteriaAndOrColumn']) ? $_REQUEST['criteriaAndOrColumn'] : array_fill(0, $criteriaColumnCount, ''); // sets minimum width $this->_form_column_width = 12; $this->_formColumns = array(); $this->_formSorts = array(); $this->_formShows = array(); $this->_formCriterions = array(); $this->_formAndOrRows = array(); $this->_formAndOrCols = array(); } /** * Sets criteria tables and columns * * @return void */ private function _setCriteriaTablesAndColumns() { // The tables list sent by a previously submitted form if (PMA_isValid($_REQUEST['TableList'], 'array')) { foreach ($_REQUEST['TableList'] as $each_table) { $this->_criteriaTables[$each_table] = ' selected="selected"'; } } // end if $all_tables = $GLOBALS['dbi']->query( 'SHOW TABLES FROM ' . Util::backquote($this->_db) . ';', null, DatabaseInterface::QUERY_STORE ); $all_tables_count = $GLOBALS['dbi']->numRows($all_tables); if (0 == $all_tables_count) { Message::error(__('No tables found in database.'))->display(); exit; } // The tables list gets from MySQL while (list($table) = $GLOBALS['dbi']->fetchRow($all_tables)) { $columns = $GLOBALS['dbi']->getColumns($this->_db, $table); if (empty($this->_criteriaTables[$table]) && ! empty($_REQUEST['TableList']) ) { $this->_criteriaTables[$table] = ''; } else { $this->_criteriaTables[$table] = ' selected="selected"'; } // end if // The fields list per selected tables if ($this->_criteriaTables[$table] == ' selected="selected"') { $each_table = Util::backquote($table); $this->_columnNames[] = $each_table . '.*'; foreach ($columns as $each_column) { $each_column = $each_table . '.' . Util::backquote($each_column['Field']); $this->_columnNames[] = $each_column; // increase the width if necessary $this->_form_column_width = max( mb_strlen($each_column), $this->_form_column_width ); } // end foreach } // end if } // end while $GLOBALS['dbi']->freeResult($all_tables); // sets the largest width found $this->_realwidth = $this->_form_column_width . 'ex'; } /** * Provides select options list containing column names * * @param integer $column_number Column Number (0,1,2) or more * @param string $selected Selected criteria column name * * @return string HTML for select options */ private function _showColumnSelectCell($column_number, $selected = '') { $html_output = ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; return $html_output; } /** * Provides select options list containing sort options (ASC/DESC) * * @param integer $column_number Column Number (0,1,2) or more * @param string $asc_selected Selected criteria 'Ascending' * @param string $desc_selected Selected criteria 'Descending' * * @return string HTML for select options */ private function _getSortSelectCell($column_number, $asc_selected = '', $desc_selected = '' ) { $html_output = ''; $html_output .= ''; $html_output .= ''; return $html_output; } /** * Provides select options list containing sort order * * @param integer $columnNumber Column Number (0,1,2) or more * @param integer $sortOrder Sort order * * @return string HTML for select options */ private function _getSortOrderSelectCell($columnNumber, $sortOrder) { $totalColumnCount = $this->_getNewColumnCount(); $html_output = ''; $html_output .= ''; $html_output .= ''; return $html_output; } /** * Returns the new column count after adding and removing columns as instructed * * @return int new column count */ private function _getNewColumnCount() { $totalColumnCount = $this->_criteria_column_count; if (! empty($this->_criteriaColumnInsert)) { $totalColumnCount += count($this->_criteriaColumnInsert); } if (! empty($this->_criteriaColumnDelete)) { $totalColumnCount -= count($this->_criteriaColumnDelete); } return $totalColumnCount; } /** * Provides search form's row containing column select options * * @return string HTML for search table's row */ private function _getColumnNamesRow() { $html_output = ''; $html_output .= '' . __('Column:') . ''; $new_column_count = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (isset($this->_criteriaColumnInsert[$column_index]) && $this->_criteriaColumnInsert[$column_index] == 'on' ) { $html_output .= $this->_showColumnSelectCell( $new_column_count ); $new_column_count++; } if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$column_index]) && $this->_criteriaColumnDelete[$column_index] == 'on' ) { continue; } $selected = ''; if (isset($_REQUEST['criteriaColumn'][$column_index])) { $selected = $_REQUEST['criteriaColumn'][$column_index]; $this->_formColumns[$new_column_count] = $_REQUEST['criteriaColumn'][$column_index]; } $html_output .= $this->_showColumnSelectCell( $new_column_count, $selected ); $new_column_count++; } // end for $this->_new_column_count = $new_column_count; $html_output .= ''; return $html_output; } /** * Provides search form's row containing column aliases * * @return string HTML for search table's row */ private function _getColumnAliasRow() { $html_output = ''; $html_output .= '' . __('Alias:') . ''; $new_column_count = 0; for ( $colInd = 0; $colInd < $this->_criteria_column_count; $colInd++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$colInd]) && $this->_criteriaColumnInsert[$colInd] == 'on' ) { $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$colInd]) && $this->_criteriaColumnDelete[$colInd] == 'on' ) { continue; } $tmp_alias = ''; if (! empty($_REQUEST['criteriaAlias'][$colInd])) { $tmp_alias = $this->_formAliases[$new_column_count] = $_REQUEST['criteriaAlias'][$colInd]; }// end if $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides search form's row containing sort(ASC/DESC) select options * * @return string HTML for search table's row */ private function _getSortRow() { $html_output = ''; $html_output .= '' . __('Sort:') . ''; $new_column_count = 0; for ( $colInd = 0; $colInd < $this->_criteria_column_count; $colInd++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$colInd]) && $this->_criteriaColumnInsert[$colInd] == 'on' ) { $html_output .= $this->_getSortSelectCell($new_column_count); $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$colInd]) && $this->_criteriaColumnDelete[$colInd] == 'on' ) { continue; } // If they have chosen all fields using the * selector, // then sorting is not available, Fix for Bug #570698 if (isset($_REQUEST['criteriaSort'][$colInd]) && isset($_REQUEST['criteriaColumn'][$colInd]) && mb_substr($_REQUEST['criteriaColumn'][$colInd], -2) == '.*' ) { $_REQUEST['criteriaSort'][$colInd] = ''; } //end if $asc_selected = ''; $desc_selected = ''; if (isset($_REQUEST['criteriaSort'][$colInd])) { $this->_formSorts[$new_column_count] = $_REQUEST['criteriaSort'][$colInd]; // Set asc_selected if ($_REQUEST['criteriaSort'][$colInd] == 'ASC') { $asc_selected = ' selected="selected"'; } // end if // Set desc selected if ($_REQUEST['criteriaSort'][$colInd] == 'DESC') { $desc_selected = ' selected="selected"'; } // end if } else { $this->_formSorts[$new_column_count] = ''; } $html_output .= $this->_getSortSelectCell( $new_column_count, $asc_selected, $desc_selected ); $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides search form's row containing sort order * * @return string HTML for search table's row */ private function _getSortOrder() { $html_output = ''; $html_output .= '' . __('Sort order:') . ''; $new_column_count = 0; for ( $colInd = 0; $colInd < $this->_criteria_column_count; $colInd++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$colInd]) && $this->_criteriaColumnInsert[$colInd] == 'on' ) { $html_output .= $this->_getSortOrderSelectCell( $new_column_count, null ); $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$colInd]) && $this->_criteriaColumnDelete[$colInd] == 'on' ) { continue; } $sortOrder = null; if (! empty($_REQUEST['criteriaSortOrder'][$colInd])) { $sortOrder = $this->_formSortOrders[$new_column_count] = $_REQUEST['criteriaSortOrder'][$colInd]; } $html_output .= $this->_getSortOrderSelectCell( $new_column_count, $sortOrder ); $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides search form's row containing SHOW checkboxes * * @return string HTML for search table's row */ private function _getShowRow() { $html_output = ''; $html_output .= '' . __('Show:') . ''; $new_column_count = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$column_index]) && $this->_criteriaColumnInsert[$column_index] == 'on' ) { $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$column_index]) && $this->_criteriaColumnDelete[$column_index] == 'on' ) { continue; } if (isset($_REQUEST['criteriaShow'][$column_index])) { $checked_options = ' checked="checked"'; $this->_formShows[$new_column_count] = $_REQUEST['criteriaShow'][$column_index]; } else { $checked_options = ''; } $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides search form's row containing criteria Inputboxes * * @return string HTML for search table's row */ private function _getCriteriaInputboxRow() { $html_output = ''; $html_output .= '' . __('Criteria:') . ''; $new_column_count = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$column_index]) && $this->_criteriaColumnInsert[$column_index] == 'on' ) { $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$column_index]) && $this->_criteriaColumnDelete[$column_index] == 'on' ) { continue; } if (isset($this->_criteria[$column_index])) { $tmp_criteria = $this->_criteria[$column_index]; } if ((empty($this->_prev_criteria) || ! isset($this->_prev_criteria[$column_index])) || $this->_prev_criteria[$column_index] != htmlspecialchars($tmp_criteria) ) { $this->_formCriterions[$new_column_count] = $tmp_criteria; } else { $this->_formCriterions[$new_column_count] = $this->_prev_criteria[$column_index]; } $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides footer options for adding/deleting row/columns * * @param string $type Whether row or column * * @return string HTML for footer options */ private function _getFootersOptions($type) { $html_output = '
'; $html_output .= (($type == 'row') ? __('Add/Delete criteria rows') : __('Add/Delete columns')); $html_output .= ':'; $html_output .= '
'; return $html_output; } /** * Provides search form table's footer options * * @return string HTML for table footer */ private function _getTableFooters() { $html_output = '
'; $html_output .= $this->_getFootersOptions("row"); $html_output .= $this->_getFootersOptions("column"); $html_output .= '
'; $html_output .= ''; $html_output .= '
'; $html_output .= '
'; return $html_output; } /** * Provides a select list of database tables * * @return string HTML for table select list */ private function _getTablesList() { $html_output = '
'; $html_output .= '
'; $html_output .= '' . __('Use Tables') . ''; // Build the options list for each table name $options = ''; $numTableListOptions = 0; foreach ($this->_criteriaTables as $key => $val) { $options .= ''; $numTableListOptions++; } $html_output .= ''; $html_output .= '
'; $html_output .= '
'; $html_output .= ''; $html_output .= '
'; $html_output .= '
'; return $html_output; } /** * Provides And/Or modification cell along with Insert/Delete options * (For modifying search form's table columns) * * @param integer $column_number Column Number (0,1,2) or more * @param array $selected Selected criteria column name * @param bool $last_column Whether this is the last column * * @return string HTML for modification cell */ private function _getAndOrColCell( $column_number, $selected = null, $last_column = false ) { $html_output = ''; if (! $last_column) { $html_output .= '' . __('Or:') . ''; $html_output .= ''; $html_output .= '  ' . __('And:') . ''; $html_output .= ''; } $html_output .= '
' . __('Ins'); $html_output .= ''; $html_output .= '  ' . __('Del'); $html_output .= ''; $html_output .= ''; return $html_output; } /** * Provides search form's row containing column modifications options * (For modifying search form's table columns) * * @return string HTML for search table's row */ private function _getModifyColumnsRow() { $html_output = ''; $html_output .= '' . __('Modify:') . ''; $new_column_count = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$column_index]) && $this->_criteriaColumnInsert[$column_index] == 'on' ) { $html_output .= $this->_getAndOrColCell($new_column_count); $new_column_count++; } // end if if (! empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$column_index]) && $this->_criteriaColumnDelete[$column_index] == 'on' ) { continue; } if (isset($this->_criteriaAndOrColumn[$column_index])) { $this->_formAndOrCols[$new_column_count] = $this->_criteriaAndOrColumn[$column_index]; } $checked_options = array(); if (isset($this->_criteriaAndOrColumn[$column_index]) && $this->_criteriaAndOrColumn[$column_index] == 'or' ) { $checked_options['or'] = ' checked="checked"'; $checked_options['and'] = ''; } else { $checked_options['and'] = ' checked="checked"'; $checked_options['or'] = ''; } $html_output .= $this->_getAndOrColCell( $new_column_count, $checked_options, ($column_index + 1 == $this->_criteria_column_count) ); $new_column_count++; } // end for $html_output .= ''; return $html_output; } /** * Provides Insert/Delete options for criteria inputbox * with AND/OR relationship modification options * * @param integer $row_index Number of criteria row * @param array $checked_options If checked * * @return string HTML */ private function _getInsDelAndOrCell($row_index, $checked_options) { $html_output = ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= '
'; $html_output .= '' . __('Ins:') . ''; $html_output .= ''; $html_output .= ''; $html_output .= '' . __('And:') . ''; $html_output .= ''; $html_output .= ''; $html_output .= '
'; $html_output .= '' . __('Del:') . ''; $html_output .= ''; $html_output .= ''; $html_output .= '' . __('Or:') . ''; $html_output .= ''; $html_output .= ''; $html_output .= '
'; $html_output .= ''; return $html_output; } /** * Provides rows for criteria inputbox Insert/Delete options * with AND/OR relationship modification options * * @param integer $new_row_index New row index if rows are added/deleted * * @return string HTML table rows */ private function _getInputboxRow($new_row_index) { $html_output = ''; $new_column_count = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (!empty($this->_criteriaColumnInsert) && isset($this->_criteriaColumnInsert[$column_index]) && $this->_criteriaColumnInsert[$column_index] == 'on' ) { $orFieldName = 'Or' . $new_row_index . '[' . $new_column_count . ']'; $html_output .= ''; $html_output .= ''; $html_output .= ''; $new_column_count++; } // end if if (!empty($this->_criteriaColumnDelete) && isset($this->_criteriaColumnDelete[$column_index]) && $this->_criteriaColumnDelete[$column_index] == 'on' ) { continue; } $or = 'Or' . $new_row_index; if (! empty($_REQUEST[$or]) && isset($_REQUEST[$or][$column_index])) { $tmp_or = $_REQUEST[$or][$column_index]; } else { $tmp_or = ''; } $html_output .= ''; $html_output .= ''; $html_output .= ''; if (!empty(${$or}) && isset(${$or}[$column_index])) { $GLOBALS[${'cur' . $or}][$new_column_count] = ${$or}[$column_index]; } $new_column_count++; } // end for return $html_output; } /** * Provides rows for criteria inputbox Insert/Delete options * with AND/OR relationship modification options * * @return string HTML table rows */ private function _getInsDelAndOrCriteriaRows() { $html_output = ''; $new_row_count = 0; $odd_row = true; $checked_options = array(); for ( $row_index = 0; $row_index <= $this->_criteria_row_count; $row_index++ ) { if (isset($this->_criteriaRowInsert[$row_index]) && $this->_criteriaRowInsert[$row_index] == 'on' ) { $checked_options['or'] = ' checked="checked"'; $checked_options['and'] = ''; $html_output .= ''; $html_output .= $this->_getInsDelAndOrCell( $new_row_count, $checked_options ); $html_output .= $this->_getInputboxRow( $new_row_count ); $new_row_count++; $html_output .= ''; $odd_row =! $odd_row; } // end if if (isset($this->_criteriaRowDelete[$row_index]) && $this->_criteriaRowDelete[$row_index] == 'on' ) { continue; } if (isset($this->_criteriaAndOrRow[$row_index])) { $this->_formAndOrRows[$new_row_count] = $this->_criteriaAndOrRow[$row_index]; } if (isset($this->_criteriaAndOrRow[$row_index]) && $this->_criteriaAndOrRow[$row_index] == 'and' ) { $checked_options['and'] = ' checked="checked"'; $checked_options['or'] = ''; } else { $checked_options['or'] = ' checked="checked"'; $checked_options['and'] = ''; } $html_output .= ''; $html_output .= $this->_getInsDelAndOrCell( $new_row_count, $checked_options ); $html_output .= $this->_getInputboxRow( $new_row_count ); $new_row_count++; $html_output .= ''; $odd_row =! $odd_row; } // end for $this->_new_row_count = $new_row_count; return $html_output; } /** * Provides SELECT clause for building SQL query * * @return string Select clause */ private function _getSelectClause() { $select_clause = ''; $select_clauses = array(); for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_formColumns[$column_index]) && isset($this->_formShows[$column_index]) && $this->_formShows[$column_index] == 'on' ) { $select = $this->_formColumns[$column_index]; if (! empty($this->_formAliases[$column_index])) { $select .= " AS " . Util::backquote($this->_formAliases[$column_index]); } $select_clauses[] = $select; } } // end for if (!empty($select_clauses)) { $select_clause = 'SELECT ' . htmlspecialchars(implode(", ", $select_clauses)) . "\n"; } return $select_clause; } /** * Provides WHERE clause for building SQL query * * @return string Where clause */ private function _getWhereClause() { $where_clause = ''; $criteria_cnt = 0; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_formColumns[$column_index]) && ! empty($this->_formCriterions[$column_index]) && $column_index && isset($last_where) && isset($this->_formAndOrCols) ) { $where_clause .= ' ' . mb_strtoupper($this->_formAndOrCols[$last_where]) . ' '; } if (! empty($this->_formColumns[$column_index]) && ! empty($this->_formCriterions[$column_index]) ) { $where_clause .= '(' . $this->_formColumns[$column_index] . ' ' . $this->_formCriterions[$column_index] . ')'; $last_where = $column_index; $criteria_cnt++; } } // end for if ($criteria_cnt > 1) { $where_clause = '(' . $where_clause . ')'; } // OR rows ${'cur' . $or}[$column_index] if (! isset($this->_formAndOrRows)) { $this->_formAndOrRows = array(); } for ( $row_index = 0; $row_index <= $this->_criteria_row_count; $row_index++ ) { $criteria_cnt = 0; $qry_orwhere = ''; $last_orwhere = ''; for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { if (! empty($this->_formColumns[$column_index]) && ! empty($_REQUEST['Or' . $row_index][$column_index]) && $column_index ) { $qry_orwhere .= ' ' . mb_strtoupper( $this->_formAndOrCols[$last_orwhere] ) . ' '; } if (! empty($this->_formColumns[$column_index]) && ! empty($_REQUEST['Or' . $row_index][$column_index]) ) { $qry_orwhere .= '(' . $this->_formColumns[$column_index] . ' ' . $_REQUEST['Or' . $row_index][$column_index] . ')'; $last_orwhere = $column_index; $criteria_cnt++; } } // end for if ($criteria_cnt > 1) { $qry_orwhere = '(' . $qry_orwhere . ')'; } if (! empty($qry_orwhere)) { $where_clause .= "\n" . mb_strtoupper( isset($this->_formAndOrRows[$row_index]) ? $this->_formAndOrRows[$row_index] . ' ' : '' ) . $qry_orwhere; } // end if } // end for if (! empty($where_clause) && $where_clause != '()') { $where_clause = 'WHERE ' . htmlspecialchars($where_clause) . "\n"; } // end if return $where_clause; } /** * Provides ORDER BY clause for building SQL query * * @return string Order By clause */ private function _getOrderByClause() { $orderby_clause = ''; $orderby_clauses = array(); // Create copy of instance variables $columns = $this->_formColumns; $sort = $this->_formSorts; $sortOrder = $this->_formSortOrders; if (!empty($sortOrder) && count($sortOrder) == count($sort) && count($sortOrder) == count($columns) ) { // Sort all three arrays based on sort order array_multisort($sortOrder, $sort, $columns); } for ( $column_index = 0; $column_index < $this->_criteria_column_count; $column_index++ ) { // if all columns are chosen with * selector, // then sorting isn't available // Fix for Bug #570698 if (empty($columns[$column_index]) && empty($sort[$column_index]) ) { continue; } if (mb_substr($columns[$column_index], -2) == '.*') { continue; } if (! empty($sort[$column_index])) { $orderby_clauses[] = $columns[$column_index] . ' ' . $sort[$column_index]; } } // end for if (!empty($orderby_clauses)) { $orderby_clause = 'ORDER BY ' . htmlspecialchars(implode(", ", $orderby_clauses)) . "\n"; } return $orderby_clause; } /** * Provides UNIQUE columns and INDEX columns present in criteria tables * * @param array $search_tables Tables involved in the search * @param array $search_columns Columns involved in the search * @param array $where_clause_columns Columns having criteria where clause * * @return array having UNIQUE and INDEX columns */ private function _getIndexes($search_tables, $search_columns, $where_clause_columns ) { $unique_columns = array(); $index_columns = array(); foreach ($search_tables as $table) { $indexes = $GLOBALS['dbi']->getTableIndexes($this->_db, $table); foreach ($indexes as $index) { $column = $table . '.' . $index['Column_name']; if (isset($search_columns[$column])) { if ($index['Non_unique'] == 0) { if (isset($where_clause_columns[$column])) { $unique_columns[$column] = 'Y'; } else { $unique_columns[$column] = 'N'; } } else { if (isset($where_clause_columns[$column])) { $index_columns[$column] = 'Y'; } else { $index_columns[$column] = 'N'; } } } } // end while (each index of a table) } // end while (each table) return array( 'unique' => $unique_columns, 'index' => $index_columns ); } /** * Provides UNIQUE columns and INDEX columns present in criteria tables * * @param array $search_tables Tables involved in the search * @param array $search_columns Columns involved in the search * @param array $where_clause_columns Columns having criteria where clause * * @return array having UNIQUE and INDEX columns */ private function _getLeftJoinColumnCandidates($search_tables, $search_columns, $where_clause_columns ) { $GLOBALS['dbi']->selectDb($this->_db); // Get unique columns and index columns $indexes = $this->_getIndexes( $search_tables, $search_columns, $where_clause_columns ); $unique_columns = $indexes['unique']; $index_columns = $indexes['index']; list($candidate_columns, $needsort) = $this->_getLeftJoinColumnCandidatesBest( $search_tables, $where_clause_columns, $unique_columns, $index_columns ); // If we came up with $unique_columns (very good) or $index_columns (still // good) as $candidate_columns we want to check if we have any 'Y' there // (that would mean that they were also found in the whereclauses // which would be great). if yes, we take only those if ($needsort != 1) { return $candidate_columns; } $very_good = array(); $still_good = array(); foreach ($candidate_columns as $column => $is_where) { $table = explode('.', $column); $table = $table[0]; if ($is_where == 'Y') { $very_good[$column] = $table; } else { $still_good[$column] = $table; } } if (count($very_good) > 0) { $candidate_columns = $very_good; // Candidates restricted in index+where } else { $candidate_columns = $still_good; // None of the candidates where in a where-clause } return $candidate_columns; } /** * Provides the main table to form the LEFT JOIN clause * * @param array $search_tables Tables involved in the search * @param array $search_columns Columns involved in the search * @param array $where_clause_columns Columns having criteria where clause * @param array $where_clause_tables Tables having criteria where clause * * @return string table name */ private function _getMasterTable($search_tables, $search_columns, $where_clause_columns, $where_clause_tables ) { if (count($where_clause_tables) == 1) { // If there is exactly one column that has a decent where-clause // we will just use this $master = key($where_clause_tables); return $master; } // Now let's find out which of the tables has an index // (When the control user is the same as the normal user // because he is using one of his databases as pmadb, // the last db selected is not always the one where we need to work) $candidate_columns = $this->_getLeftJoinColumnCandidates( $search_tables, $search_columns, $where_clause_columns ); // Generally, we need to display all the rows of foreign (referenced) // table, whether they have any matching row in child table or not. // So we select candidate tables which are foreign tables. $foreign_tables = array(); foreach ($candidate_columns as $one_table) { $foreigners = PMA_getForeigners($this->_db, $one_table); foreach ($foreigners as $key => $foreigner) { if ($key != 'foreign_keys_data') { if (in_array($foreigner['foreign_table'], $candidate_columns)) { $foreign_tables[$foreigner['foreign_table']] = $foreigner['foreign_table']; } continue; } foreach ($foreigner as $one_key) { if (in_array($one_key['ref_table_name'], $candidate_columns)) { $foreign_tables[$one_key['ref_table_name']] = $one_key['ref_table_name']; } } } } if (count($foreign_tables)) { $candidate_columns = $foreign_tables; } // If our array of candidates has more than one member we'll just // find the smallest table. // Of course the actual query would be faster if we check for // the Criteria which gives the smallest result set in its table, // but it would take too much time to check this if (!(count($candidate_columns) > 1)) { // Only one single candidate return reset($candidate_columns); } // Of course we only want to check each table once $checked_tables = $candidate_columns; $tsize = array(); $csize = array(); foreach ($candidate_columns as $table) { if ($checked_tables[$table] != 1) { $_table = new Table($table, $this->_db); $tsize[$table] = $_table->countRecords(); $checked_tables[$table] = 1; } $csize[$table] = $tsize[$table]; } // Return largest table return array_search(max($csize), $csize); } /** * Provides columns and tables that have valid where clause criteria * * @return array */ private function _getWhereClauseTablesAndColumns() { $where_clause_columns = array(); $where_clause_tables = array(); // Now we need all tables that we have in the where clause for ( $column_index = 0, $nb = count($this->_criteria); $column_index < $nb; $column_index++ ) { $current_table = explode('.', $_POST['criteriaColumn'][$column_index]); if (empty($current_table[0]) || empty($current_table[1])) { continue; } // end if $table = str_replace('`', '', $current_table[0]); $column = str_replace('`', '', $current_table[1]); $column = $table . '.' . $column; // Now we know that our array has the same numbers as $criteria // we can check which of our columns has a where clause if (! empty($this->_criteria[$column_index])) { if (mb_substr($this->_criteria[$column_index], 0, 1) == '=' || stristr($this->_criteria[$column_index], 'is') ) { $where_clause_columns[$column] = $column; $where_clause_tables[$table] = $table; } } // end if } // end for return array( 'where_clause_tables' => $where_clause_tables, 'where_clause_columns' => $where_clause_columns ); } /** * Provides FROM clause for building SQL query * * @param array $formColumns List of selected columns in the form * * @return string FROM clause */ private function _getFromClause($formColumns) { $from_clause = ''; if (empty($formColumns)) { return $from_clause; } // Initialize some variables $search_tables = $search_columns = array(); // We only start this if we have fields, otherwise it would be dumb foreach ($formColumns as $value) { $parts = explode('.', $value); if (! empty($parts[0]) && ! empty($parts[1])) { $table = str_replace('`', '', $parts[0]); $search_tables[$table] = $table; $search_columns[] = $table . '.' . str_replace( '`', '', $parts[1] ); } } // end while // Create LEFT JOINS out of Relations $from_clause = $this->_getJoinForFromClause( $search_tables, $search_columns ); // In case relations are not defined, just generate the FROM clause // from the list of tables, however we don't generate any JOIN if (empty($from_clause)) { // Create cartesian product $from_clause = implode( ", ", array_map('PMA\libraries\Util::backquote', $search_tables) ); } return $from_clause; } /** * Formulates the WHERE clause by JOINing tables * * @param array $searchTables Tables involved in the search * @param array $searchColumns Columns involved in the search * * @return string table name */ private function _getJoinForFromClause($searchTables, $searchColumns) { // $relations[master_table][foreign_table] => clause $relations = array(); // Fill $relations with inter table relationship data foreach ($searchTables as $oneTable) { $this->_loadRelationsForTable($relations, $oneTable); } // Get tables and columns with valid where clauses $validWhereClauses = $this->_getWhereClauseTablesAndColumns(); $whereClauseTables = $validWhereClauses['where_clause_tables']; $whereClauseColumns = $validWhereClauses['where_clause_columns']; // Get master table $master = $this->_getMasterTable( $searchTables, $searchColumns, $whereClauseColumns, $whereClauseTables ); // Will include master tables and all tables that can be combined into // a cluster by their relation $finalized = array(); if (mb_strlen($master) > 0) { // Add master tables $finalized[$master] = ''; } // Fill the $finalized array with JOIN clauses for each table $this->_fillJoinClauses($finalized, $relations, $searchTables); // JOIN clause $join = ''; // Tables that can not be combined with the table cluster // which includes master table $unfinalized = array_diff($searchTables, array_keys($finalized)); if (count($unfinalized) > 0) { // We need to look for intermediary tables to JOIN unfinalized tables // Heuristic to chose intermediary tables is to look for tables // having relationships with unfinalized tables foreach ($unfinalized as $oneTable) { $references = PMA_getChildReferences($this->_db, $oneTable); foreach ($references as $column => $columnReferences) { foreach ($columnReferences as $reference) { // Only from this schema if ($reference['table_schema'] != $this->_db) { continue; } $table = $reference['table_name']; $this->_loadRelationsForTable($relations, $table); // Make copies $tempFinalized = $finalized; $tempSearchTables = $searchTables; $tempSearchTables[] = $table; // Try joining with the added table $this->_fillJoinClauses( $tempFinalized, $relations, $tempSearchTables ); $tempUnfinalized = array_diff( $tempSearchTables, array_keys($tempFinalized) ); // Take greedy approach. // If the unfinalized count drops we keep the new table // and switch temporary varibles with the original ones if (count($tempUnfinalized) < count($unfinalized)) { $finalized = $tempFinalized; $searchTables = $tempSearchTables; } // We are done if no unfinalized tables anymore if (count($tempUnfinalized) == 0) { break 3; } } } } $unfinalized = array_diff($searchTables, array_keys($finalized)); // If there are still unfinalized tables if (count($unfinalized) > 0) { // Add these tables as cartesian product before joined tables $join .= implode( ', ', array_map('Util::backquote', $unfinalized) ); } } $first = true; // Add joined tables foreach ($finalized as $table => $clause) { if ($first) { if (! empty($join)) { $join .= ", "; } $join .= Util::backquote($table); $first = false; } else { $join .= "\n LEFT JOIN " . Util::backquote( $table ) . " ON " . $clause; } } return $join; } /** * Loads relations for a given table into the $relations array * * @param array &$relations array of relations * @param string $oneTable the table * * @return void */ private function _loadRelationsForTable(&$relations, $oneTable) { $relations[$oneTable] = array(); $foreigners = PMA_getForeigners($GLOBALS['db'], $oneTable); foreach ($foreigners as $field => $foreigner) { // Foreign keys data if ($field == 'foreign_keys_data') { foreach ($foreigner as $oneKey) { $clauses = array(); // There may be multiple column relations foreach ($oneKey['index_list'] as $index => $oneField) { $clauses[] = Util::backquote($oneTable) . "." . Util::backquote($oneField) . " = " . Util::backquote($oneKey['ref_table_name']) . "." . Util::backquote($oneKey['ref_index_list'][$index]); } // Combine multiple column relations with AND $relations[$oneTable][$oneKey['ref_table_name']] = implode(" AND ", $clauses); } } else { // Internal relations $relations[$oneTable][$foreigner['foreign_table']] = Util::backquote($oneTable) . "." . Util::backquote($field) . " = " . Util::backquote($foreigner['foreign_table']) . "." . Util::backquote($foreigner['foreign_field']); } } } /** * Fills the $finalized arrays with JOIN clauses for each of the tables * * @param array &$finalized JOIN clauses for each table * @param array $relations Relations among tables * @param array $searchTables Tables involved in the search * * @return void */ private function _fillJoinClauses(&$finalized, $relations, $searchTables) { while (true) { $added = false; foreach ($searchTables as $masterTable) { $foreignData = $relations[$masterTable]; foreach ($foreignData as $foreignTable => $clause) { if (! isset($finalized[$masterTable]) && isset($finalized[$foreignTable]) ) { $finalized[$masterTable] = $clause; $added = true; } elseif (! isset($finalized[$foreignTable]) && isset($finalized[$masterTable]) && in_array($foreignTable, $searchTables) ) { $finalized[$foreignTable] = $clause; $added = true; } if ($added) { // We are done if all tables are in $finalized if (count($finalized) == count($searchTables)) { return; } } } } // If no new tables were added during this iteration, break; if (! $added) { return; } } } /** * Provides the generated SQL query * * @param array $formColumns List of selected columns in the form * * @return string SQL query */ private function _getSQLQuery($formColumns) { $sql_query = ''; // get SELECT clause $sql_query .= $this->_getSelectClause(); // get FROM clause $from_clause = $this->_getFromClause($formColumns); if (! empty($from_clause)) { $sql_query .= 'FROM ' . htmlspecialchars($from_clause) . "\n"; } // get WHERE clause $sql_query .= $this->_getWhereClause(); // get ORDER BY clause $sql_query .= $this->_getOrderByClause(); return $sql_query; } /** * Provides the generated QBE form * * @return string QBE form */ public function getSelectionForm() { $html_output = '
'; $html_output .= '
'; if ($GLOBALS['cfgRelation']['savedsearcheswork']) { $html_output .= $this->_getSavedSearchesField(); } $html_output .= ''; // Get table's elements $html_output .= $this->_getColumnNamesRow(); $html_output .= $this->_getColumnAliasRow(); $html_output .= $this->_getShowRow(); $html_output .= $this->_getSortRow(); $html_output .= $this->_getSortOrder(); $html_output .= $this->_getCriteriaInputboxRow(); $html_output .= $this->_getInsDelAndOrCriteriaRows(); $html_output .= $this->_getModifyColumnsRow(); $html_output .= '
'; $this->_new_row_count--; $url_params = array(); $url_params['db'] = $this->_db; $url_params['criteriaColumnCount'] = $this->_new_column_count; $url_params['rows'] = $this->_new_row_count; $html_output .= PMA_URL_getHiddenInputs($url_params); $html_output .= '
'; // get footers $html_output .= $this->_getTableFooters(); // get tables select list $html_output .= $this->_getTablesList(); $html_output .= '
'; $html_output .= '
'; $html_output .= PMA_URL_getHiddenInputs(array('db' => $this->_db)); // get SQL query $html_output .= '
'; $html_output .= '
'; $html_output .= '' . sprintf( __('SQL query on database %s:'), Util::getDbLink($this->_db) ); $html_output .= ''; $text_dir = 'ltr'; $html_output .= ''; $html_output .= '
'; // displays form's footers $html_output .= '
'; $html_output .= ''; $html_output .= ''; $html_output .= '
'; $html_output .= '
'; $html_output .= '
'; return $html_output; } /** * Get fields to display * * @return string */ private function _getSavedSearchesField() { $html_output = __('Saved bookmarked search:'); $html_output .= ' '; $html_output .= ''; $html_output .= ''; $html_output .= ''; if (null !== $currentSearchId) { $html_output .= ''; $html_output .= ''; } return $html_output; } /** * Initialize _criteria_column_count * * @return int Previous number of columns */ private function _initializeCriteriasCount() { // sets column count $criteriaColumnCount = PMA_ifSetOr( $_REQUEST['criteriaColumnCount'], 3, 'numeric' ); $criteriaColumnAdd = PMA_ifSetOr( $_REQUEST['criteriaColumnAdd'], 0, 'numeric' ); $this->_criteria_column_count = max( $criteriaColumnCount + $criteriaColumnAdd, 0 ); // sets row count $rows = PMA_ifSetOr($_REQUEST['rows'], 0, 'numeric'); $criteriaRowAdd = PMA_ifSetOr($_REQUEST['criteriaRowAdd'], 0, 'numeric'); $this->_criteria_row_count = min( 100, max($rows + $criteriaRowAdd, 0) ); return $criteriaColumnCount; } /** * Get best * * @param array $search_tables Tables involved in the search * @param array $where_clause_columns Columns with where clause * @param array $unique_columns Unique columns * @param array $index_columns Indexed columns * * @return array */ private function _getLeftJoinColumnCandidatesBest( $search_tables, $where_clause_columns, $unique_columns, $index_columns ) { // now we want to find the best. if (isset($unique_columns) && count($unique_columns) > 0) { $candidate_columns = $unique_columns; $needsort = 1; return array($candidate_columns, $needsort); } elseif (isset($index_columns) && count($index_columns) > 0) { $candidate_columns = $index_columns; $needsort = 1; return array($candidate_columns, $needsort); } elseif (isset($where_clause_columns) && count($where_clause_columns) > 0) { $candidate_columns = $where_clause_columns; $needsort = 0; return array($candidate_columns, $needsort); } else { $candidate_columns = $search_tables; $needsort = 0; return array($candidate_columns, $needsort); } } }